00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "Prefetch.h"
00027 #include "HdrUtils.h"
00028 #include "HttpCompat.h"
00029 #include <records/I_RecHttp.h>
00030 #include <ts/IpMapConf.h>
00031
00032 #ifdef PREFETCH
00033
00034 struct html_tag prefetch_allowable_html_tags[] = {
00035
00036
00037
00038 {"img", "src"},
00039 {"body", "background"},
00040 {"frame", "src"},
00041 {"fig", "src"},
00042 {"applet", "code"},
00043 {"script", "src"},
00044 {"embed", "src"},
00045 {"td", "background"},
00046 {"base", "href"},
00047 {"meta", "content"},
00048
00049 {"input", "src"},
00050 {"link", "href"},
00051 {NULL, NULL}
00052 };
00053
00054
00055
00056 struct html_tag prefetch_allowable_html_attrs[] = {
00057 {NULL, NULL},
00058 {NULL, NULL},
00059 {NULL, NULL},
00060 {NULL, NULL},
00061 {NULL, NULL},
00062 {NULL, NULL},
00063 {NULL, NULL},
00064 {NULL, NULL},
00065 {NULL, NULL},
00066 {NULL, NULL},
00067 {NULL, NULL},
00068 {"rel", "stylesheet"},
00069 {NULL, NULL}
00070 };
00071
00072 static const char *PREFETCH_FIELD_RECURSION;
00073 static int PREFETCH_FIELD_LEN_RECURSION;
00074
00075 PrefetchProcessor prefetchProcessor;
00076 KeepAliveConnTable *g_conn_table;
00077
00078 static int prefetch_udp_fd = 0;
00079 static int32_t udp_seq_no;
00080
00081 TSPrefetchBlastData const UDP_BLAST_DATA = { TS_PREFETCH_UDP_BLAST };
00082 TSPrefetchBlastData const TCP_BLAST_DATA = { TS_PREFETCH_TCP_BLAST };
00083
00084 #define PrefetchEstablishStaticConfigStringAlloc(_ix,_n) \
00085 REC_EstablishStaticConfigStringAlloc(_ix,_n); \
00086 REC_RegisterConfigUpdateFunc(_n, prefetch_config_cb, NULL)
00087
00088 #define PrefetchEstablishStaticConfigLongLong(_ix,_n) \
00089 REC_EstablishStaticConfigInteger(_ix,_n); \
00090 REC_RegisterConfigUpdateFunc(_n, prefetch_config_cb, NULL)
00091
00092 #define PrefetchEstablishStaticConfigFloat(_ix,_n) \
00093 REC_EstablishStaticConfigFloat(_ix,_n); \
00094 REC_RegisterConfigUpdateFunc(_n, prefetch_config_cb, NULL)
00095
00096 #define PrefetchEstablishStaticConfigByte(_ix,_n) \
00097 REC_EstablishStaticConfigByte(_ix,_n); \
00098 REC_RegisterConfigUpdateFunc(_n, prefetch_config_cb, NULL)
00099
00100 static inline uint32_t
00101 get_udp_seq_no()
00102 {
00103 return ink_atomic_increment(&udp_seq_no, 1);
00104 }
00105
00106 static inline void
00107 setup_udp_header(char *header, uint32_t seq_no, uint32_t pkt_no, bool last_pkt)
00108 {
00109 uint32_t *hdr = (uint32_t *) header;
00110 hdr[0] = 0;
00111 hdr[1] = htonl(seq_no);
00112 hdr[2] = htonl((last_pkt ? PRELOAD_UDP_LAST_PKT_FLAG : 0) | (pkt_no & PRELOAD_UDP_PKT_NUM_MASK));
00113 }
00114
00115 static inline void
00116 setup_object_header(char *header, int64_t size, bool url_promise)
00117 {
00118 uint32_t *hdr = (uint32_t *) header;
00119 hdr[0] = htonl(size);
00120 hdr[1] = 0;
00121 hdr[2] = (url_promise) ? htonl(PRELOAD_HDR_URL_PROMISE_FLAG) : 0;
00122 }
00123
00124
00125 inline const char *
00126 findDomainFromHost(const char *host, int host_len, bool & no_dot)
00127 {
00128 const char *h_cur = host + host_len - 1;
00129
00130 if (host_len > 4) {
00131
00132 h_cur = host + host_len - 4;
00133 if (*h_cur == '.') {
00134
00135 char c3 = *(h_cur + 1);
00136 char c1 = (c3 >= 'A' && c3 <= 'Z') ? (c3 + 'a' - 'A') : c3;
00137 c3 = *(h_cur + 2);
00138 char c2 = (c3 >= 'A' && c3 <= 'Z') ? (c3 + 'a' - 'A') : c3;
00139 c3 = *(h_cur + 3);
00140 if (c3 >= 'A' && c3 <= 'Z')
00141 c3 += 'a' - 'A';
00142
00143
00144 if ((c1 == 'c' && c2 == 'o' && c3 == 'm') ||
00145 (c1 == 'e' && c2 == 'd' && c3 == 'u') ||
00146 (c1 == 'n' && c2 == 'e' && c3 == 't') ||
00147 (c1 == 'o' && c2 == 'r' && c3 == 'g') ||
00148 (c1 == 'g' && c2 == 'o' && c3 == 'v') ||
00149 (c1 == 'm' && c2 == 'i' && c3 == 'l') || (c1 == 'i' && c2 == 'n' && c3 == 't')) {
00150 h_cur--;
00151
00152 while (h_cur != host) {
00153 if (*h_cur == '.')
00154 break;
00155 h_cur--;
00156 }
00157 if (h_cur != host) {
00158
00159 h_cur++;
00160 } else if (*h_cur == '.')
00161 return NULL;
00162
00163 return h_cur;
00164 }
00165 }
00166 }
00167
00168
00169 int num_dots = 0;
00170 while (h_cur != host) {
00171 if (*h_cur == '.') {
00172 num_dots++;
00173 if (num_dots == 3) {
00174 h_cur++;
00175 return h_cur;
00176 }
00177 }
00178 h_cur--;
00179 }
00180
00181 if (num_dots < 2 || *host == '.') {
00182 if (num_dots == 0)
00183 no_dot = true;
00184 return NULL;
00185 } else
00186 return h_cur;
00187 }
00188
00189 static int
00190 normalize_url(char *url, int *len)
00191 {
00192
00193
00194 char *p, *root, *end = url + len[0];
00195 int modified = 0;
00196
00197 enum
00198 {
00199 NONE,
00200 FIRST_DOT,
00201 SECOND_DOT,
00202 SLASH
00203 } state;
00204
00205 if (!(p = strstr(url, "://")))
00206 return -1;
00207 p += 3;
00208
00209
00210 root = (p = strchr(p, '/'));
00211
00212 if (!root)
00213 return 0;
00214
00215 state = SLASH;
00216
00217 while (++p <= end) {
00218
00219 switch (p[0]) {
00220
00221 case '\0':
00222 case '/':
00223 switch (state) {
00224
00225 case SLASH:
00226 if (p[0]) {
00227 modified = 1;
00228 p[0] = 0;
00229 }
00230 break;
00231
00232 case FIRST_DOT:
00233 modified = 1;
00234 p[0] = (p[-1] = 0);
00235 break;
00236
00237 case SECOND_DOT:{
00238 modified = 1;
00239 p[0] = (p[-1] = (p[-2] = 0));
00240
00241 char *dir = p - 3;
00242 while (dir[0] == 0 && dir > root)
00243 dir--;
00244
00245 ink_assert(dir[0] == '/');
00246 if (dir > root && dir[0] == '/') {
00247 do {
00248 dir[0] = 0;
00249 } while (*--dir != '/');
00250 }
00251 }
00252 break;
00253 default: ;
00254 };
00255
00256 state = SLASH;
00257 break;
00258
00259 case '.':
00260 switch (state) {
00261 case SLASH:
00262 state = FIRST_DOT;
00263 break;
00264 case FIRST_DOT:
00265 state = SECOND_DOT;
00266 break;
00267 default:
00268 state = NONE;
00269 }
00270 break;
00271
00272 default:
00273 state = NONE;
00274 }
00275 }
00276
00277 if (modified) {
00278
00279 p = ++root;
00280
00281 while (p < end) {
00282 if (p[0]) {
00283 *root++ = p[0];
00284 } else
00285 len[0]--;
00286 p++;
00287 }
00288 *root = 0;
00289 return 1;
00290 }
00291
00292 return 0;
00293 }
00294
00295 static PrefetchConfiguration *prefetch_config;
00296 ClassAllocator<PrefetchUrlEntry> prefetchUrlEntryAllocator("prefetchUrlEntryAllocator");
00297
00298 #define IS_STATUS_REDIRECT(status) (prefetch_config->redirection > 0 &&\
00299 (((status) == HTTP_STATUS_MOVED_PERMANENTLY) ||\
00300 ((status) == HTTP_STATUS_MOVED_TEMPORARILY) ||\
00301 ((status) == HTTP_STATUS_SEE_OTHER) ||\
00302 (((status) == HTTP_STATUS_TEMPORARY_REDIRECT))))
00303
00304 struct PrefetchConfigCont;
00305 typedef int (PrefetchConfigCont::*PrefetchConfigContHandler) (int, void *);
00306 struct PrefetchConfigCont:public Continuation
00307 {
00308 public:
00309 PrefetchConfigCont(ProxyMutex * m)
00310 : Continuation(m)
00311 {
00312 SET_HANDLER((PrefetchConfigContHandler) & PrefetchConfigCont::conf_update_handler);
00313 }
00314 int conf_update_handler(int event, void *edata);
00315 };
00316
00317 static Ptr<ProxyMutex> prefetch_reconfig_mutex;
00318
00319
00320 struct PrefetchConfigFreerCont;
00321 typedef int (PrefetchConfigFreerCont::*PrefetchConfigFreerContHandler) (int, void *);
00322
00323 struct PrefetchConfigFreerCont: public Continuation
00324 {
00325 PrefetchConfiguration *p;
00326 int freeEvent(int , Event * )
00327 {
00328 Debug("Prefetch", "Deleting old Prefetch config after change");
00329 delete p;
00330 delete this;
00331 return EVENT_DONE;
00332 }
00333 PrefetchConfigFreerCont(PrefetchConfiguration * ap):Continuation(new_ProxyMutex()), p(ap)
00334 {
00335 SET_HANDLER((PrefetchConfigFreerContHandler) & PrefetchConfigFreerCont::freeEvent);
00336 }
00337 };
00338
00339 int
00340 PrefetchConfigCont::conf_update_handler(int , void * )
00341 {
00342 Debug("Prefetch", "Handling Prefetch config change");
00343
00344 PrefetchConfiguration *new_prefetch_config = new PrefetchConfiguration;
00345 if (new_prefetch_config->readConfiguration() == 0) {
00346
00347 eventProcessor.schedule_in(new PrefetchConfigFreerCont(prefetch_config), PREFETCH_CONFIG_UPDATE_TIMEOUT, ET_TASK);
00348 ink_atomic_swap(&prefetch_config, new_prefetch_config);
00349 } else {
00350
00351 Debug("Prefetch", "New config in ERROR, keeping the old config");
00352 eventProcessor.schedule_in(new PrefetchConfigFreerCont(new_prefetch_config), PREFETCH_CONFIG_UPDATE_TIMEOUT, ET_TASK);
00353 }
00354
00355 delete this;
00356 return EVENT_DONE;
00357 }
00358
00359 static int
00360 prefetch_config_cb(const char * , RecDataT ,
00361 RecData , void * )
00362 {
00363 INK_MEMORY_BARRIER;
00364
00365 eventProcessor.schedule_in(new PrefetchConfigCont(prefetch_reconfig_mutex), HRTIME_SECONDS(1), ET_TASK);
00366 return 0;
00367 }
00368
00369 PrefetchTransform::PrefetchTransform(HttpSM *sm, HTTPHdr *resp)
00370 : INKVConnInternal(NULL, reinterpret_cast<TSMutex>((ProxyMutex*)sm->mutex)),
00371 m_output_buf(NULL), m_output_vio(NULL), m_sm(sm)
00372 {
00373 refcount_inc();
00374
00375 HTTPHdr *request = &sm->t_state.hdr_info.client_request;
00376 url = request->url_get()->string_get(NULL, NULL);
00377
00378 html_parser.Init(url, prefetch_config->html_tags_table, prefetch_config->html_attrs_table);
00379
00380 SET_HANDLER(&PrefetchTransform::handle_event);
00381
00382 Debug("PrefetchParser", "Created: transform for %s\n", url);
00383
00384 memset(&hash_table[0], 0, HASH_TABLE_LENGTH * sizeof(hash_table[0]));
00385
00386 udp_url_list = blasterUrlListAllocator.alloc();
00387 udp_url_list->init(UDP_BLAST_DATA, prefetch_config->url_buffer_timeout, prefetch_config->url_buffer_size);
00388 tcp_url_list = blasterUrlListAllocator.alloc();
00389 tcp_url_list->init(TCP_BLAST_DATA, prefetch_config->url_buffer_timeout, prefetch_config->url_buffer_size);
00390
00391
00392 host_start = request->url_get()->host_get(&host_len);
00393
00394 if (!host_start || !host_len)
00395 host_start = request->value_get(MIME_FIELD_HOST, MIME_LEN_HOST, &host_len);
00396
00397 no_dot_in_host = false;
00398 if (host_start && host_len) {
00399 domain_end = host_start + (host_len - 1);
00400 domain_start = findDomainFromHost(host_start, host_len, no_dot_in_host);
00401 } else
00402 domain_start = 0;
00403
00404
00405
00406 redirect(resp);
00407 }
00408
00409 PrefetchTransform::~PrefetchTransform()
00410 {
00411
00412 this_ethread()->schedule_imm_local(udp_url_list);
00413 this_ethread()->schedule_imm_local(tcp_url_list);
00414
00415 Debug("PrefetchParserURLs", "Unique URLs 0x%p (%s):\n", this, url);
00416 int nurls = 0;
00417 for (int i = 0; i < HASH_TABLE_LENGTH; i++) {
00418 PrefetchUrlEntry *e = hash_table[i];
00419 while (e) {
00420 Debug("PrefetchParserURLs", "(0x%p) %d: %s\n", this, i, e->url);
00421 nurls++;
00422 PrefetchUrlEntry *next = e->hash_link;
00423 e->free();
00424 e = next;
00425 }
00426 }
00427
00428 Debug("PrefetchParserURLs", "Number of embedded objects extracted for %s: %d\n", url, nurls);
00429
00430 if (m_output_buf)
00431 free_MIOBuffer(m_output_buf);
00432 ats_free(url);
00433 }
00434
00435 int
00436 PrefetchTransform::handle_event(int event, void *edata)
00437 {
00438 handle_event_count(event);
00439
00440 if (m_closed) {
00441 if (m_deletable) {
00442 Debug("PrefetchParser", "PrefetchTransform free(): %" PRId64"", m_output_vio ? m_output_vio->ndone : 0);
00443 if (m_output_buf) {
00444 free_MIOBuffer(m_output_buf);
00445 m_output_buf = 0;
00446 }
00447 Debug("Prefetch", "Freeing after closed %p", this);
00448 free();
00449 }
00450 } else {
00451 switch (event) {
00452 case VC_EVENT_ERROR:
00453 m_write_vio._cont->handleEvent(VC_EVENT_ERROR, &m_write_vio);
00454 break;
00455
00456 case VC_EVENT_WRITE_COMPLETE:
00457
00458 Debug("Prefetch", "got write_complete %p", this);
00459 ink_assert(m_output_vio == (VIO *) edata);
00460
00461 ink_assert(m_write_vio.ntodo() == 0);
00462
00463 m_output_vc->do_io_shutdown(IO_SHUTDOWN_WRITE);
00464 break;
00465
00466 case VC_EVENT_WRITE_READY:
00467 default:{
00468
00469 if (!m_output_vio) {
00470 m_output_buf = new_empty_MIOBuffer();
00471 m_output_reader = m_output_buf->alloc_reader();
00472 m_output_vio = m_output_vc->do_io_write(this, INT64_MAX, m_output_reader);
00473 }
00474
00475
00476
00477 if (m_write_vio.op == VIO::NONE) {
00478 m_output_vio->nbytes = m_write_vio.ndone;
00479 m_output_vio->reenable();
00480 return 0;
00481 }
00482
00483 ink_assert(m_output_vc != NULL);
00484
00485 MUTEX_TRY_LOCK(trylock, m_write_vio.mutex, this_ethread());
00486 if (!trylock) {
00487 retry(10);
00488 return 0;
00489 }
00490
00491 if (m_closed) {
00492 return 0;
00493 }
00494
00495 int64_t towrite = m_write_vio.ntodo();
00496
00497 if (towrite > 0) {
00498 IOBufferReader *buf_reader = m_write_vio.get_reader();
00499 int64_t avail = buf_reader->read_avail();
00500
00501 if (towrite > avail) {
00502 towrite = avail;
00503 }
00504
00505 if (towrite > 0) {
00506 Debug("PrefetchParser", "handle_event() " "writing %" PRId64" bytes to output", towrite);
00507
00508
00509
00510 m_output_buf->write(buf_reader, towrite);
00511
00512 parse_data(buf_reader);
00513
00514
00515 m_write_vio.ndone += towrite;
00516 }
00517 }
00518
00519 if (m_write_vio.ntodo() > 0) {
00520 if (towrite > 0) {
00521 m_output_vio->reenable();
00522 m_write_vio._cont->handleEvent(VC_EVENT_WRITE_READY, &m_write_vio);
00523 }
00524 } else {
00525 m_output_vio->nbytes = m_write_vio.ndone;
00526 m_output_vio->reenable();
00527 m_write_vio._cont->handleEvent(VC_EVENT_WRITE_COMPLETE, &m_write_vio);
00528 }
00529
00530 break;
00531 }
00532 }
00533 }
00534 return 0;
00535 }
00536
00537 int
00538 PrefetchTransform::redirect(HTTPHdr *resp)
00539 {
00540 HTTPHdr *req = NULL;
00541 int response_status = 0;
00542 char *req_url = NULL;
00543 char *redirect_url = NULL;
00544
00545
00546
00547
00548 if ((resp != NULL) && (resp->valid())) {
00549 response_status = resp->status_get();
00550
00551
00552
00553
00554
00555
00556 if (resp->presence(MIME_PRESENCE_LOCATION)) {
00557 int redirect_url_len = 0;
00558 const char *tmp_url = resp->value_get(MIME_FIELD_LOCATION, MIME_LEN_LOCATION, &redirect_url_len);
00559
00560 redirect_url = (char *)alloca(redirect_url_len + 1);
00561 ink_strlcpy(redirect_url, tmp_url, redirect_url_len + 1);
00562 Debug("PrefetchTransform", "redirect_url = %s\n", redirect_url);
00563 } else {
00564 response_status = -1;
00565 }
00566 } else {
00567 response_status = -1;
00568 }
00569
00570 if (IS_STATUS_REDIRECT(response_status)) {
00571 if (redirect_url) {
00572
00573 req = &m_sm->t_state.hdr_info.client_request;
00574 req_url = req->url_get()->string_get(NULL, NULL);
00575
00576 Debug("PrefetchTransform", "Received response status = %d\n", response_status);
00577 Debug("PrefetchTransform", "Redirect from request = %s\n", req_url);
00578
00579 int location_len = strlen(redirect_url);
00580 Debug("PrefetchTransform", "Redirect url to HTTP Hdr Location: \'%s\'\n", redirect_url);
00581 if (strncmp(redirect_url, req_url, location_len) == 0) {
00582 Debug("PrefetchTransform", "'%s' -> '%s' - Could be a loop. Discontinuing this path.\n", req_url, redirect_url);
00583 ats_free(req_url);
00584 return 0;
00585 }
00586
00587 PrefetchUrlEntry *entry = hash_add(redirect_url);
00588
00589 if (!entry) {
00590 Debug("PrefetchParserURLs", "Ignoring duplicate url '%s'", redirect_url);
00591 ats_free(req_url);
00592 return 0;
00593 }
00594
00595 Debug("PrefetchTransform", "Found embedded URL: %s", redirect_url);
00596 entry->req_ip = m_sm->t_state.client_info.addr;
00597
00598 PrefetchBlaster *blaster = prefetchBlasterAllocator.alloc();
00599 blaster->init(entry, &m_sm->t_state.hdr_info.client_request, this);
00600 ats_free(req_url);
00601 }
00602 }
00603 return 0;
00604 }
00605
00606 int
00607 PrefetchTransform::parse_data(IOBufferReader *reader)
00608 {
00609 char *url_start = NULL, *url_end = NULL;
00610
00611 while (html_parser.ParseHtml(reader, &url_start, &url_end)) {
00612
00613 PrefetchUrlEntry *entry = hash_add(url_start);
00614
00615 if (!entry) {
00616
00617 continue;
00618 }
00619
00620 ats_ip_copy(&entry->req_ip, &m_sm->t_state.client_info.addr);
00621
00622 PrefetchBlaster *blaster = prefetchBlasterAllocator.alloc();
00623 blaster->init(entry, &m_sm->t_state.hdr_info.client_request, this);
00624 }
00625
00626 return 0;
00627 }
00628
00629 PrefetchUrlEntry *
00630 PrefetchTransform::hash_add(char *s)
00631 {
00632 uint32_t index = 0;
00633 int str_len = strlen(s);
00634
00635 if (normalize_url(s, &str_len) > 0)
00636 Debug("PrefetchParserURLs", "Normalized URL: %s\n", s);
00637
00638
00639 INK_MD5 hash;
00640 MD5Context().hash_immediate(hash, s, str_len);
00641 index = hash.slice32(1) % HASH_TABLE_LENGTH;
00642
00643 PrefetchUrlEntry **e = &hash_table[index];
00644 for (; *e; e = &(*e)->hash_link)
00645 if (strcmp((*e)->url, s) == 0)
00646 return NULL;
00647
00648 *e = prefetchUrlEntryAllocator.alloc();
00649 (*e)->init(ats_strdup(s), hash);
00650
00651 return *e;
00652 }
00653
00654
00655 #define IS_RECURSIVE_PREFETCH(req_ip) \
00656 (prefetch_config->max_recursion > 0 && ats_is_ip_loopback(&req_ip))
00657
00658 static void
00659 check_n_attach_prefetch_transform(HttpSM *sm, HTTPHdr *resp, bool from_cache)
00660 {
00661 INKVConnInternal *prefetch_trans;
00662 ip_text_buffer client_ipb;
00663
00664 IpEndpoint client_ip = sm->t_state.client_info.addr;
00665
00666
00667 Debug("PrefetchParser", "Checking response for request from %s\n",
00668 ats_ip_ntop(&client_ip, client_ipb, sizeof(client_ipb))
00669 );
00670
00671 unsigned int rec_depth = 0;
00672 HTTPHdr *request = &sm->t_state.hdr_info.client_request;
00673
00674 if (IS_RECURSIVE_PREFETCH(client_ip)) {
00675 rec_depth = request->value_get_int(PREFETCH_FIELD_RECURSION, PREFETCH_FIELD_LEN_RECURSION);
00676 rec_depth++;
00677
00678 Debug("PrefetchTemp", "recursion: %d", rec_depth);
00679
00680 if (rec_depth > prefetch_config->max_recursion) {
00681 Debug("PrefetchParserRecursion", "Recursive parsing is not done "
00682 "since recursion depth(%d) is greater than max allowed (%d)", rec_depth, prefetch_config->max_recursion);
00683 return;
00684 }
00685 } else if (!prefetch_config->ip_map.contains(&client_ip)) {
00686 Debug("PrefetchParser", "client (%s) does not match any of the "
00687 "prefetch_children mentioned in configuration\n", client_ipb);
00688 return;
00689 }
00690
00691 if (prefetch_config->max_recursion > 0) {
00692 request->value_set_int(PREFETCH_FIELD_RECURSION, PREFETCH_FIELD_LEN_RECURSION, rec_depth);
00693 }
00694
00695 int c_type_len;
00696 const char *c_type = resp->value_get(MIME_FIELD_CONTENT_TYPE,
00697 MIME_LEN_CONTENT_TYPE, &c_type_len);
00698
00699 if ((c_type == NULL) || strncmp("text/html", c_type, 9) != 0) {
00700 Debug("PrefetchParserCT", "Content type is not text/html.. skipping\n");
00701 return;
00702 }
00703
00704
00705 c_type = resp->value_get(MIME_FIELD_CONTENT_ENCODING, MIME_LEN_CONTENT_ENCODING, &c_type_len);
00706 if (c_type) {
00707 char type[64];
00708 memcpy(type, c_type, c_type_len);
00709 type[c_type_len] = 0;
00710 Debug("PrefetchParserCT", "Content is encoded with %s .. skipping\n", type);
00711 return;
00712 }
00713
00714 Debug("PrefetchParserCT", "Content type is text/html\n");
00715
00716 if (prefetch_config->pre_parse_hook) {
00717 TSPrefetchInfo info;
00718
00719 HTTPHdr *req = &sm->t_state.hdr_info.client_request;
00720 info.request_buf = reinterpret_cast<TSMBuffer>(req);
00721 info.request_loc = reinterpret_cast<TSMLoc>(req->m_http);
00722 info.response_buf = reinterpret_cast<TSMBuffer>(resp);
00723 info.response_loc = reinterpret_cast<TSMLoc>(resp->m_http);
00724
00725 ats_ip_copy(ats_ip_sa_cast(&info.client_ip), &client_ip);
00726 info.embedded_url = 0;
00727 info.present_in_cache = from_cache;
00728 ink_zero(info.url_blast);
00729 ink_zero(info.url_response_blast);
00730
00731 info.object_buf = 0;
00732 info.object_buf_reader = 0;
00733 info.object_buf_status = TS_PREFETCH_OBJ_BUF_NOT_NEEDED;
00734
00735 int ret = (prefetch_config->pre_parse_hook) (TS_PREFETCH_PRE_PARSE_HOOK, &info);
00736 if (ret == TS_PREFETCH_DISCONTINUE)
00737 return;
00738 }
00739
00740 prefetch_trans = new PrefetchTransform(sm, resp);
00741
00742 if (prefetch_trans) {
00743 Debug("PrefetchParser", "Adding Prefetch Parser 0x%p\n", prefetch_trans);
00744 TSHttpTxnHookAdd(reinterpret_cast<TSHttpTxn>(sm), TS_HTTP_RESPONSE_TRANSFORM_HOOK, reinterpret_cast<TSCont>(prefetch_trans));
00745
00746 DUMP_HEADER("PrefetchParserHdrs", &sm->t_state.hdr_info.client_request, (int64_t)0,
00747 "Request Header given for Prefetch Parser");
00748 }
00749 }
00750
00751
00752 static int
00753 PrefetchPlugin(TSCont , TSEvent event, void *edata)
00754 {
00755 HttpSM *sm = (HttpSM *) edata;
00756 HTTPHdr *resp = 0;
00757 bool from_cache = false;
00758
00759 switch (event) {
00760
00761 case TS_EVENT_HTTP_CACHE_LOOKUP_COMPLETE:{
00762
00763 Debug("PrefetchPlugin", "Received TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK " "event (sm = 0x%p)\n", sm);
00764 int status;
00765 TSHttpTxnCacheLookupStatusGet((TSHttpTxn) sm, &status);
00766
00767 if (status == TS_CACHE_LOOKUP_HIT_FRESH) {
00768 Debug("PrefetchPlugin", "Cached object is fresh\n");
00769 resp = sm->t_state.cache_info.object_read->response_get();
00770 from_cache = true;
00771 } else {
00772 Debug("PrefetchPlugin", "Cache lookup did not succeed\n");
00773 }
00774
00775 break;
00776 }
00777 case TS_EVENT_HTTP_READ_RESPONSE_HDR:
00778 Debug("PrefetchPlugin", "Received TS_EVENT_HTTP_READ_RESPONSE_HDR " "event (sm = 0x%p)\n", sm);
00779 resp = &sm->t_state.hdr_info.server_response;
00780
00781 break;
00782
00783 default:
00784 Debug("PrefetchPlugin", "Error: Received unexpected event\n");
00785 return 0;
00786 }
00787
00788 if (resp && resp->valid())
00789 check_n_attach_prefetch_transform(sm, resp, from_cache);
00790
00791 TSHttpTxnReenable(reinterpret_cast<TSHttpTxn>(sm), TS_EVENT_HTTP_CONTINUE);
00792
00793
00794
00795 return 0;
00796 }
00797
00798 void
00799 PrefetchProcessor::start()
00800 {
00801
00802
00803 prefetch_reconfig_mutex = new_ProxyMutex();
00804 prefetch_config = new PrefetchConfiguration;
00805 RecRegisterConfigUpdateCb("proxy.config.prefetch.prefetch_enabled", prefetch_config_cb, NULL);
00806 RecRegisterConfigUpdateCb("proxy.config.http.server_port", prefetch_config_cb, NULL);
00807 RecRegisterConfigUpdateCb("proxy.config.prefetch.child_port", prefetch_config_cb, NULL);
00808 RecRegisterConfigUpdateCb("proxy.config.prefetch.url_buffer_size", prefetch_config_cb, NULL);
00809 RecRegisterConfigUpdateCb("proxy.config.prefetch.url_buffer_timeout", prefetch_config_cb, NULL);
00810 RecRegisterConfigUpdateCb("proxy.config.prefetch.keepalive_timeout", prefetch_config_cb, NULL);
00811 RecRegisterConfigUpdateCb("proxy.config.prefetch.push_cached_objects", prefetch_config_cb, NULL);
00812 RecRegisterConfigUpdateCb("proxy.config.prefetch.max_object_size", prefetch_config_cb, NULL);
00813 RecRegisterConfigUpdateCb("proxy.config.prefetch.max_recursion", prefetch_config_cb, NULL);
00814 RecRegisterConfigUpdateCb("proxy.config.prefetch.redirection", prefetch_config_cb, NULL);
00815 RecRegisterConfigUpdateCb("proxy.config.prefetch.default_url_proto", prefetch_config_cb, NULL);
00816 RecRegisterConfigUpdateCb("proxy.config.prefetch.default_url_proto", prefetch_config_cb, NULL);
00817 RecRegisterConfigUpdateCb("proxy.config.prefetch.config_file", prefetch_config_cb, NULL);
00818
00819 prefetch_config->readConfiguration();
00820
00821 if (prefetch_config->prefetch_enabled) {
00822
00823 PREFETCH_FIELD_RECURSION = "@InkPrefetch";
00824 PREFETCH_FIELD_LEN_RECURSION = strlen(PREFETCH_FIELD_RECURSION);
00825
00826
00827 g_conn_table = new KeepAliveConnTable;
00828 g_conn_table->init();
00829
00830 udp_seq_no = this_ethread()->generator.random();
00831
00832 prefetch_udp_fd = socketManager.socket(PF_INET, SOCK_DGRAM, 0);
00833
00834 TSCont contp = TSContCreate(PrefetchPlugin, NULL);
00835 TSHttpHookAdd(TS_HTTP_CACHE_LOOKUP_COMPLETE_HOOK, contp);
00836 TSHttpHookAdd(TS_HTTP_READ_RESPONSE_HDR_HOOK, contp);
00837
00838 Note("PrefetchProcessor: Started the prefetch processor\n");
00839 } else {
00840 Debug("PrefetchProcessor", "Prefetch processor is not started\n");
00841 }
00842
00843
00844 }
00845
00846
00847
00848 ClassAllocator<BlasterUrlList> blasterUrlListAllocator("blasterUrlList");
00849
00850 int
00851 BlasterUrlList::handleEvent(int event, void *data)
00852 {
00853 switch (event) {
00854
00855 case EVENT_INTERVAL:
00856 ink_assert(list_head);
00857 if (list_head) {
00858 invokeUrlBlaster();
00859 }
00860 action = NULL;
00861 break;
00862
00863 case EVENT_IMMEDIATE:
00864
00865
00866
00867
00868
00869 if (list_head) {
00870 action->cancel();
00871 action = NULL;
00872 invokeUrlBlaster();
00873 }
00874 free();
00875 break;
00876
00877 case PREFETCH_EVENT_SEND_URL:{
00878 PrefetchUrlEntry *entry = ((PrefetchUrlEntry *) data)->assign();
00879
00880 if (list_head) {
00881 action->cancel();
00882 action = NULL;
00883 if ((cur_len + entry->len) > mtu) {
00884 invokeUrlBlaster();
00885 }
00886 }
00887
00888 entry->blaster_link = list_head;
00889 list_head = entry;
00890 cur_len += entry->len;
00891
00892 if (cur_len >= mtu || timeout == 0) {
00893 invokeUrlBlaster();
00894 } else {
00895 action = this_ethread()->schedule_in(this, HRTIME_MSECONDS(timeout));
00896 }
00897
00898 break;
00899 }
00900
00901 default:
00902 ink_assert(!"not reached");
00903 }
00904
00905 return EVENT_DONE;
00906 }
00907
00908 ClassAllocator<PrefetchUrlBlaster> prefetchUrlBlasterAllocator("prefetchUrlBlaster");
00909
00910 inline void
00911 PrefetchUrlBlaster::free()
00912 {
00913 if (action)
00914 action->cancel();
00915
00916
00917 while (url_head) {
00918 PrefetchUrlEntry *next = url_head->blaster_link;
00919 this_ethread()->schedule_imm(url_head->resp_blaster);
00920 url_head->free();
00921 url_head = next;
00922 }
00923
00924 mutex.clear();
00925 prefetchUrlBlasterAllocator.free(this);
00926 }
00927
00928 void
00929 PrefetchUrlBlaster::writeBuffer(MIOBuffer *buf)
00930 {
00931
00932 PrefetchUrlEntry *entry = NULL;
00933
00934 while (url_head) {
00935
00936
00937 PrefetchUrlEntry *next = url_head->blaster_link;
00938 url_head->blaster_link = entry;
00939 entry = url_head;
00940 url_head = next;
00941 }
00942 url_head = entry;
00943
00944 int nurls = 0;
00945
00946 while (entry) {
00947 buf->write(entry->url, entry->len);
00948 entry = entry->blaster_link;
00949 nurls++;
00950 }
00951 Debug("PrefetchBlasterUrlList", "found %d urls in the list", nurls);
00952 return;
00953 }
00954
00955 int
00956 PrefetchUrlBlaster::udpUrlBlaster(int event, void *data)
00957 {
00958 switch (event) {
00959
00960 case SIMPLE_EVENT_EVENTS_START:{
00961 SET_HANDLER((EventHandler) (&PrefetchUrlBlaster::udpUrlBlaster));
00962
00963 MIOBuffer *buf = new_MIOBuffer();
00964 IOBufferReader *reader = buf->alloc_reader();
00965
00966 int udp_hdr_len = (TS_PREFETCH_TCP_BLAST == blast.type) ? 0 : PRELOAD_UDP_HEADER_LEN;
00967
00968 buf->fill(udp_hdr_len + PRELOAD_HEADER_LEN);
00969
00970 writeBuffer(buf);
00971
00972 if (TS_PREFETCH_TCP_BLAST == blast.type) {
00973 setup_object_header(reader->start(), reader->read_avail(), true);
00974 g_conn_table->append(url_head->child_ip, buf, reader);
00975 free();
00976 } else {
00977 IOBufferBlock *block = buf->get_current_block();
00978 ink_assert(reader->read_avail() == block->read_avail());
00979 setup_udp_header(block->start(), get_udp_seq_no(), 0, true);
00980 setup_object_header(block->start() + PRELOAD_UDP_HEADER_LEN, block->read_avail() - PRELOAD_UDP_HEADER_LEN, true);
00981
00982 IpEndpoint saddr;
00983 ats_ip_copy(&saddr, &url_head->url_multicast_ip) ||
00984 ats_ip_copy(&saddr, &url_head->child_ip);
00985 ats_ip_port_cast(&saddr.sa) = htons(prefetch_config->stuffer_port);
00986
00987 udpNet.sendto_re(this, NULL, prefetch_udp_fd, &saddr.sa, sizeof(saddr), block, block->read_avail());
00988 }
00989 break;
00990 }
00991
00992 case NET_EVENT_DATAGRAM_WRITE_ERROR:
00993 Debug("PrefetchBlaster", "Error in sending the url list on UDP (%p)", data);
00994 case NET_EVENT_DATAGRAM_WRITE_COMPLETE:
00995 free();
00996 break;
00997 }
00998 return EVENT_DONE;
00999 }
01000
01001 ClassAllocator<PrefetchBlaster> prefetchBlasterAllocator("PrefetchBlasterAllocator");
01002
01003 int
01004 PrefetchBlaster::init(PrefetchUrlEntry *entry, HTTPHdr *req_hdr, PrefetchTransform *p_trans)
01005 {
01006 mutex = new_ProxyMutex();
01007
01008
01009
01010
01011
01012 int url_len = strlen(entry->url);
01013
01014 request = new HTTPHdr;
01015 request->copy(req_hdr);
01016 url_clear(request->url_get()->m_url_impl);
01017
01018
01019
01020 request->field_delete(MIME_FIELD_HOST, MIME_LEN_HOST);
01021 request->field_delete(MIME_FIELD_IF_MATCH, MIME_LEN_IF_MATCH);
01022 request->field_delete(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE);
01023 request->field_delete(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH);
01024 request->field_delete(MIME_FIELD_IF_RANGE, MIME_LEN_IF_RANGE);
01025 request->field_delete(MIME_FIELD_IF_UNMODIFIED_SINCE, MIME_LEN_IF_UNMODIFIED_SINCE);
01026 request->field_delete(MIME_FIELD_CACHE_CONTROL, MIME_LEN_CACHE_CONTROL);
01027
01028 request->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
01029
01030 int temp;
01031 if (request->url_get()->parse(entry->url, url_len) != PARSE_DONE ||
01032 request->url_get()->scheme_get(&temp) != URL_SCHEME_HTTP) {
01033 Debug("PrefetchParserURLs", "URL parsing failed or scheme is not HTTP " "for %s", entry->url);
01034 free();
01035 return -1;
01036 }
01037
01038 request->method_set(HTTP_METHOD_GET, HTTP_LEN_GET);
01039
01040 request->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
01041 request->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "close", 5);
01042
01043
01044 if (request->field_find(MIME_FIELD_REFERER, MIME_LEN_REFERER)) {
01045 int topurl_len;
01046 char *topurl = req_hdr->url_get()->string_get(NULL, &topurl_len);
01047 if (topurl) {
01048 request->value_set(MIME_FIELD_REFERER, MIME_LEN_REFERER, topurl, topurl_len);
01049 ats_free(topurl);
01050 }
01051 }
01052
01053 if (request->field_find(MIME_FIELD_AUTHORIZATION, MIME_LEN_AUTHORIZATION)) {
01054 int host_len;
01055 bool delete_auth;
01056 const char *host_start = request->url_get()->host_get(&host_len);
01057
01058 if (host_start == NULL)
01059 delete_auth = true;
01060 else {
01061 const char *host_end = host_start + host_len - 1;
01062 int cmp_len = p_trans->domain_end - p_trans->domain_start + 1;
01063
01064 if (cmp_len <= 0 || host_len<cmp_len || (host_len> cmp_len && host_start[host_len - cmp_len - 1] != '.') ||
01065 strncasecmp(p_trans->domain_start, host_end - (cmp_len - 1), cmp_len) != 0) {
01066 delete_auth = true;
01067 } else
01068 delete_auth = false;
01069 }
01070
01071 if (delete_auth)
01072 request->field_delete(MIME_FIELD_AUTHORIZATION, MIME_LEN_AUTHORIZATION);
01073 }
01074
01075
01076 handleCookieHeaders(req_hdr,
01077 &p_trans->m_sm->t_state.hdr_info.server_response,
01078 p_trans->domain_start, p_trans->domain_end,
01079 p_trans->host_start, p_trans->host_len, p_trans->no_dot_in_host);
01080
01081
01082 int ip_len;
01083 const char *ip_str;
01084 if (IS_RECURSIVE_PREFETCH(entry->req_ip) &&
01085 (ip_str = request->value_get(MIME_FIELD_CLIENT_IP, MIME_LEN_CLIENT_IP, &ip_len))) {
01086 ip_text_buffer b;
01087
01088
01089 ink_strlcpy(b, ip_str, sizeof(b));
01090 ats_ip_pton(b, &entry->child_ip.sa);
01091 } else
01092 entry->child_ip = entry->req_ip;
01093
01094 DUMP_HEADER("PrefetchBlasterHdrs", request, (int64_t)0, "Request Header from Prefetch Blaster");
01095
01096 url_ent = entry->assign();
01097 transform = p_trans->assign();
01098
01099 buf = new_MIOBuffer();
01100 reader = buf->alloc_reader();
01101
01102 SET_HANDLER((EventHandler) (&PrefetchBlaster::handleEvent));
01103
01104 this_ethread()->schedule_imm(this);
01105
01106 return EVENT_DONE;
01107 }
01108
01109 void
01110 PrefetchBlaster::free()
01111 {
01112 if (serverVC)
01113 serverVC->do_io_close();
01114
01115 if (url_ent)
01116 url_ent->free();
01117 if (transform)
01118 transform->free();
01119
01120 if (buf)
01121 free_MIOBuffer(buf);
01122 if (io_block) {
01123 io_block->free();
01124 }
01125
01126 if (request) {
01127 request->destroy();
01128 delete request;
01129 }
01130
01131 mutex.clear();
01132 prefetchBlasterAllocator.free(this);
01133 }
01134
01135 bool
01136 isCookieUnique(HTTPHdr *req, const char *move_cookie, int move_cookie_len)
01137 {
01138
01139 MIMEField *o_cookie = req->field_find(MIME_FIELD_COOKIE, MIME_LEN_COOKIE);
01140 const char *a_raw;
01141 int a_raw_len;
01142 const char *iter_cookie;
01143 int iter_cookie_len;
01144 bool equalsign = false;
01145
01146 if ((a_raw = (const char *) memchr(move_cookie, '=', move_cookie_len)) != NULL) {
01147 int tmp_len = (int) (a_raw - move_cookie) + 1;
01148 if (tmp_len < move_cookie_len) {
01149 equalsign = true;
01150 move_cookie_len = tmp_len;
01151 }
01152 }
01153
01154 for (; o_cookie; o_cookie = o_cookie->m_next_dup) {
01155 a_raw = o_cookie->value_get(&a_raw_len);
01156 if (a_raw != NULL && a_raw_len > 0) {
01157 StrList a_param_list;
01158 Str *a_param;
01159
01160 HttpCompat::parse_tok_list(&a_param_list, 0, a_raw, a_raw_len, ';');
01161 for (a_param = a_param_list.head; a_param; a_param = a_param->next) {
01162 iter_cookie = a_param->str;
01163 iter_cookie_len = a_param->len;
01164
01165 if (equalsign) {
01166 if (iter_cookie_len > move_cookie_len && memcmp(iter_cookie, move_cookie, move_cookie_len) == 0) {
01167
01168 return false;
01169 }
01170 } else {
01171 if (iter_cookie_len == move_cookie_len && memcmp(iter_cookie, move_cookie, iter_cookie_len) == 0) {
01172
01173 return false;
01174 }
01175 }
01176 }
01177 }
01178 }
01179
01180 return true;
01181 }
01182
01183 inline void
01184 cookie_debug(const char *level, const char *value, int value_len)
01185 {
01186 if (is_debug_tag_set("PrefetchCookies")) {
01187 char *str = (char *)ats_malloc(value_len + 1);
01188 memcpy(str, value, value_len);
01189 str[value_len] = 0;
01190 Debug("PrefetchCookies", "Processing %s value: %s", level, str);
01191 ats_free(str);
01192 }
01193 }
01194
01195
01196 void
01197 PrefetchBlaster::handleCookieHeaders(HTTPHdr *req_hdr,
01198 HTTPHdr *resp_hdr,
01199 const char *domain_start,
01200 const char *domain_end, const char *thost_start, int thost_len, bool no_dot)
01201 {
01202 bool add_cookies = true;
01203 bool existing_req_cookies = request->valid() && request->presence(MIME_PRESENCE_COOKIE);
01204 bool existing_resp_cookies = resp_hdr->valid() && resp_hdr->presence(MIME_PRESENCE_SET_COOKIE);
01205 bool default_domain_match;
01206 const char *host_start;
01207 const char *host_end;
01208 int host_len, cmp_len;
01209
01210 if (!existing_req_cookies && !existing_resp_cookies)
01211 return;
01212
01213 if (!domain_start && (!thost_start || no_dot == false)) {
01214
01215 add_cookies = false;
01216 goto Lcheckcookie;
01217 }
01218
01219 host_start = request->url_get()->host_get(&host_len);
01220
01221 if (!host_start || !host_len)
01222 host_start = request->value_get(MIME_FIELD_HOST, MIME_LEN_HOST, &host_len);
01223
01224 if (!host_start && !host_len) {
01225 add_cookies = false;
01226 goto Lcheckcookie;
01227 }
01228
01229 host_end = host_start + host_len - 1;
01230 if (domain_start) {
01231 cmp_len = domain_end - domain_start + 1;
01232
01233 if (host_len<cmp_len || (host_len> cmp_len && host_start[host_len - cmp_len - 1] != '.') ||
01234 strncasecmp(domain_start, host_end - (cmp_len - 1), cmp_len) != 0) {
01235 add_cookies = false;
01236 goto Lcheckcookie;
01237 }
01238
01239 if (thost_len != host_len || strncasecmp(thost_start, host_start, host_len) != 0)
01240 default_domain_match = false;
01241 else
01242 default_domain_match = true;
01243 } else {
01244 if (host_len != thost_len || strncasecmp(thost_start, host_start, host_len) != 0) {
01245 add_cookies = false;
01246 goto Lcheckcookie;
01247 }
01248 default_domain_match = true;
01249 }
01250
01251 if (existing_resp_cookies) {
01252 const char *a_raw;
01253 int a_raw_len;
01254 const char *move_cookie;
01255 int move_cookie_len;
01256 MIMEField *s_cookie = NULL;
01257
01258 add_cookies = false;
01259
01260 request->field_delete(MIME_FIELD_COOKIE, MIME_LEN_COOKIE);
01261
01262
01263 s_cookie = resp_hdr->field_find(MIME_FIELD_SET_COOKIE, MIME_LEN_SET_COOKIE);
01264 for (; s_cookie; s_cookie = s_cookie->m_next_dup) {
01265 a_raw = s_cookie->value_get(&a_raw_len);
01266 MIMEField *new_cookie = NULL;
01267 StrList a_param_list;
01268 Str *a_param;
01269 bool first_move;
01270 bool domain_match;
01271
01272 cookie_debug("PrefetchCookies", a_raw, a_raw_len);
01273
01274 domain_match = default_domain_match;
01275 HttpCompat::parse_tok_list(&a_param_list, 0, a_raw, a_raw_len, ';');
01276 for (a_param = a_param_list.head; a_param; a_param = a_param->next) {
01277 move_cookie = a_param->str;
01278 move_cookie_len = a_param->len;
01279
01280 cookie_debug("Field", move_cookie, move_cookie_len);
01281
01282 if (!new_cookie) {
01283 new_cookie = request->field_create();
01284 first_move = true;
01285 } else
01286 first_move = false;
01287
01288 if (move_cookie_len > 7 && strncasecmp(move_cookie, "domain=", 7) == 0) {
01289
01290 const char *cookie_domain_start = move_cookie + 7;
01291 int cookie_domain_len = move_cookie_len - 7;
01292 const char *cookie_domain_end = (const char *) (move_cookie + move_cookie_len - 1);
01293
01294 if (*cookie_domain_start == '"') {
01295
01296 if (*cookie_domain_end == '"') {
01297 cookie_domain_start++;
01298 cookie_domain_end--;
01299 cookie_domain_len -= 2;
01300 if (cookie_domain_len <= 0)
01301 goto Lnotmatch;
01302 } else {
01303
01304 goto Lnotmatch;
01305 }
01306 }
01307
01308 while (*cookie_domain_end == '.' && cookie_domain_len > 0)
01309 cookie_domain_end--, cookie_domain_len--;
01310
01311 if (cookie_domain_len <= 0)
01312 goto Lnotmatch;
01313
01314
01315 int prefix_len = host_len - cookie_domain_len;
01316 if (host_len <= 0 || prefix_len < 0)
01317 goto Lnotmatch;
01318
01319 if (strncasecmp(host_start + prefix_len, cookie_domain_start, cookie_domain_len) != 0)
01320 goto Lnotmatch;
01321
01322
01323 if (prefix_len > 0 && memchr(host_start, '.', prefix_len))
01324 goto Lnotmatch;
01325
01326
01327
01328
01329 domain_match = true;
01330 continue;
01331 } else if (move_cookie_len > 5 && strncasecmp(move_cookie, "path=", 5) == 0) {
01332 const char *cookie_path_start = move_cookie + 5;
01333 int cookie_path_len = move_cookie_len - 5;
01334 const char *cookie_path_end = (const char *) (move_cookie + move_cookie_len - 1);
01335
01336 if (cookie_path_len <= 0)
01337 goto Lnotmatch;
01338
01339 if (*cookie_path_start == '/') {
01340 cookie_path_start++;
01341 cookie_path_len--;
01342 }
01343
01344 if (cookie_path_len == 0) {
01345
01346 continue;
01347 }
01348
01349 if (*cookie_path_end == '/') {
01350 cookie_path_end--;
01351 cookie_path_len--;
01352 }
01353
01354 if (cookie_path_len == 0) {
01355
01356 goto Lnotmatch;
01357 }
01358
01359
01360 int dest_path_len;
01361 const char *dest_path_start = request->url_get()->path_get(&dest_path_len);
01362
01363
01364 if (dest_path_start == NULL || dest_path_len == 0) {
01365 goto Lnotmatch;
01366 }
01367
01368 if (*dest_path_start == '/') {
01369 dest_path_start++;
01370 dest_path_len--;
01371 }
01372
01373 if (dest_path_len < cookie_path_len || strncasecmp(dest_path_start, cookie_path_start, cookie_path_len) != 0)
01374 goto Lnotmatch;
01375
01376
01377 } else if (move_cookie_len > 8 && strncasecmp(move_cookie, "expires=", 8) == 0) {
01378
01379 continue;
01380 } else {
01381
01382 request->field_value_append(new_cookie, move_cookie, move_cookie_len, !first_move, ';');
01383 }
01384 }
01385
01386 if (domain_match == false)
01387 goto Lnotmatch;
01388
01389 if (new_cookie) {
01390 add_cookies = true;
01391 new_cookie->name_set(request->m_heap, request->m_mime, MIME_FIELD_COOKIE, MIME_LEN_COOKIE);
01392 request->field_attach(new_cookie);
01393 }
01394 continue;
01395
01396 Lnotmatch:
01397 if (new_cookie) {
01398 new_cookie->name_set(request->m_heap, request->m_mime, MIME_FIELD_COOKIE, MIME_LEN_COOKIE);
01399 request->field_attach(new_cookie);
01400 request->field_delete(new_cookie);
01401 new_cookie = NULL;
01402 }
01403 }
01404
01405
01406 if (add_cookies && existing_req_cookies) {
01407 MIMEField *o_cookie = req_hdr->field_find(MIME_FIELD_COOKIE, MIME_LEN_COOKIE);
01408 const char *iter_cookie;
01409 int iter_cookie_len;
01410
01411 for (; o_cookie; o_cookie = o_cookie->m_next_dup) {
01412 MIMEField *n_cookie = NULL;
01413 a_raw = o_cookie->value_get(&a_raw_len);
01414 if (a_raw != NULL && a_raw_len > 0) {
01415 StrList a_param_list;
01416 Str *a_param;
01417 bool f_move;
01418
01419 HttpCompat::parse_tok_list(&a_param_list, 0, a_raw, a_raw_len, ';');
01420 for (a_param = a_param_list.head; a_param; a_param = a_param->next) {
01421 iter_cookie = a_param->str;
01422 iter_cookie_len = a_param->len;
01423
01424 if (isCookieUnique(request, iter_cookie, iter_cookie_len)) {
01425
01426 if (n_cookie == NULL) {
01427 n_cookie = request->field_create();
01428 f_move = true;
01429 } else
01430 f_move = false;
01431
01432 request->field_value_append(n_cookie, iter_cookie, iter_cookie_len, !f_move, ';');
01433 }
01434 }
01435
01436 if (n_cookie) {
01437 n_cookie->name_set(request->m_heap, request->m_mime, MIME_FIELD_COOKIE, MIME_LEN_COOKIE);
01438 request->field_attach(n_cookie);
01439 }
01440 }
01441 }
01442 }
01443
01444
01445
01446 add_cookies = add_cookies || existing_req_cookies;
01447 }
01448
01449 Lcheckcookie:
01450 if (add_cookies == false) {
01451
01452 request->field_delete(MIME_FIELD_COOKIE, MIME_LEN_COOKIE);
01453 }
01454
01455 DUMP_HEADER("PrefetchCookies", req_hdr, (int64_t)0,
01456 "Request Header for the top page used as the base for the new request with Cookies");
01457 DUMP_HEADER("PrefetchCookies", resp_hdr, (int64_t)0,
01458 "Response Header for the top page used as the base for the new request with Cookies");
01459 DUMP_HEADER("PrefetchCookies", request, (int64_t)0, "Request Header with Cookies generated by Prefetch Parser");
01460 }
01461
01462 int
01463 PrefetchBlaster::handleEvent(int event, void *data)
01464 {
01465
01466
01467
01468
01469
01470
01471 switch (event) {
01472
01473 case EVENT_IMMEDIATE:{
01474
01475
01476
01477
01478 initCacheLookupConfig();
01479 cacheProcessor.open_read(this, request->url_get(), false, request, &cache_lookup_config, 0);
01480
01481 break;
01482 }
01483
01484 case EVENT_INTERVAL:{
01485
01486 if (url_list) {
01487 MUTEX_TRY_LOCK(trylock, url_list->mutex, this_ethread());
01488 if (!trylock) {
01489 this_ethread()->schedule_in(this, HRTIME_MSECONDS(10));
01490 break;
01491 }
01492 url_ent->resp_blaster = this;
01493 url_list->handleEvent(PREFETCH_EVENT_SEND_URL, url_ent);
01494 }
01495
01496 if (serverVC) {
01497 SET_HANDLER((EventHandler) (&PrefetchBlaster::bufferObject));
01498 } else {
01499 SET_HANDLER((EventHandler) (&PrefetchBlaster::httpClient));
01500 }
01501
01502 transform->free();
01503 transform = NULL;
01504
01505 if (!url_list)
01506 this_ethread()->schedule_imm_local(this);
01507
01508
01509 break;
01510 }
01511
01512 case CACHE_EVENT_OPEN_READ:{
01513
01514
01515 Debug("PrefetchBlaster", "Cache lookup succeded for %s\n", url_ent->url);
01516
01517 serverVC = (VConnection *) data;
01518
01519 ((CacheVConnection *) data)->get_http_info(&cache_http_info);
01520
01521 invokeBlaster();
01522 break;
01523 }
01524 case CACHE_EVENT_OPEN_READ_FAILED:
01525
01526 Debug("PrefetchBlaster", "Cache lookup failed for %s\n", url_ent->url);
01527
01528 invokeBlaster();
01529 break;
01530
01531 default:
01532 ink_assert(!"not reached");
01533 free();
01534 }
01535
01536 return EVENT_DONE;
01537 }
01538
01539 static int
01540 copy_header(MIOBuffer *buf, HTTPHdr *hdr, const char *hdr_tail)
01541 {
01542
01543 int64_t done = 0;
01544 int64_t offset = 0;
01545
01546 while (!done) {
01547 int64_t block_len = buf->block_write_avail();
01548 int index = 0, temp = offset;
01549
01550 done = hdr->print(buf->end(), block_len, &index, &temp);
01551
01552 ink_assert(done || index == block_len);
01553
01554 offset += index;
01555
01556 if (!done) {
01557 buf->fill(index);
01558 buf->add_block();
01559 } else {
01560 ink_assert(index >= 2);
01561 if (hdr_tail && index >= 2) {
01562
01563 int len = strlen(hdr_tail);
01564 offset += len - 2;
01565 buf->fill(index - 2);
01566 buf->write(hdr_tail, len);
01567 } else
01568 buf->fill(index);
01569 }
01570 }
01571
01572 return offset;
01573 }
01574
01575 int
01576 PrefetchBlaster::httpClient(int event, void *data)
01577 {
01578
01579
01580
01581
01582 switch (event) {
01583
01584 case EVENT_IMMEDIATE:{
01585 IpEndpoint target;
01586 target.setToLoopback(AF_INET);
01587 target.port() = prefetch_config->local_http_server_port;
01588 netProcessor.connect_re(this, &target.sa);
01589 break;
01590 }
01591
01592 case NET_EVENT_OPEN:{
01593 serverVC = (VConnection *) data;
01594 buf->reset();
01595
01596 char *rec_header = 0;
01597 char hdr_buf[64];
01598
01599 if (request->field_find(PREFETCH_FIELD_RECURSION, PREFETCH_FIELD_LEN_RECURSION)) {
01600 snprintf(hdr_buf, sizeof(hdr_buf), "%s: %d\r\n\r\n", PREFETCH_FIELD_RECURSION,
01601 request->value_get_int(PREFETCH_FIELD_RECURSION, PREFETCH_FIELD_LEN_RECURSION));
01602 rec_header = hdr_buf;
01603 }
01604
01605 int len = copy_header(buf, request, rec_header);
01606
01607 serverVC->do_io_write(this, len, reader);
01608
01609 break;
01610 }
01611
01612 case NET_EVENT_OPEN_FAILED:
01613 Debug("PrefetchBlaster", "Open to local http port failed.. strange\n");
01614 free();
01615 break;
01616
01617 case VC_EVENT_WRITE_READY:
01618 break;
01619 case VC_EVENT_WRITE_COMPLETE:
01620 SET_HANDLER((EventHandler) (&PrefetchBlaster::bufferObject));
01621 bufferObject(EVENT_IMMEDIATE, NULL);
01622 break;
01623
01624 default:
01625 Debug("PrefetchBlaster", "Unexpected Event: %d(%s)\n", event, get_vc_event_name(event));
01626 case VC_EVENT_ERROR:
01627 case VC_EVENT_EOS:
01628 free();
01629 break;
01630 }
01631
01632 return EVENT_DONE;
01633 }
01634
01635 int
01636 PrefetchBlaster::bufferObject(int event, void * )
01637 {
01638 switch (event) {
01639
01640 case EVENT_INTERVAL:
01641 case EVENT_IMMEDIATE:{
01642 buf->reset();
01643 buf->water_mark = prefetch_config->max_object_size;
01644 buf->fill(PRELOAD_HEADER_LEN);
01645
01646 int64_t ntoread = INT64_MAX;
01647 copy_header(buf, request, NULL);
01648
01649 if (cache_http_info) {
01650 copy_header(buf, cache_http_info->response_get(), NULL);
01651 ntoread = cache_http_info->object_size_get();
01652 }
01653 serverVC->do_io_read(this, ntoread, buf);
01654 break;
01655 }
01656
01657 case VC_EVENT_READ_READY:
01658 if (buf->high_water()) {
01659
01660 Debug("PrefetchBlasterTemp", "The object is bigger than %" PRId64" bytes " "cancelling the url", buf->water_mark);
01661 buf->reset();
01662 buf->fill(PRELOAD_HEADER_LEN);
01663 buf->write("DEL ", 4);
01664 buf->write(url_ent->url, url_ent->len);
01665 blastObject(EVENT_IMMEDIATE, (void *) 1);
01666 }
01667 break;
01668
01669 default:
01670 Debug("PrefetchBlaster", "Error Event: %s\n", get_vc_event_name(event));
01671 case VC_EVENT_READ_COMPLETE:
01672 case VC_EVENT_EOS:
01673 blastObject(EVENT_IMMEDIATE, NULL);
01674 break;
01675 }
01676
01677 return EVENT_DONE;
01678 }
01679
01680
01681
01682
01683 int
01684 PrefetchBlaster::blastObject(int event, void *data)
01685 {
01686 switch (event) {
01687
01688 case EVENT_IMMEDIATE:
01689 serverVC->do_io_close();
01690 serverVC = 0;
01691
01692
01693
01694 bool obj_cancelled;
01695 obj_cancelled = (data == (void *) 1);
01696
01697 setup_object_header(reader->start(), reader->read_avail(), obj_cancelled);
01698
01699 if (url_ent->object_buf_status != TS_PREFETCH_OBJ_BUF_NOT_NEEDED &&
01700 prefetch_config->embedded_obj_hook && !obj_cancelled) {
01701 TSPrefetchInfo info;
01702 memset(&info, 0, sizeof(info));
01703
01704 info.embedded_url = url_ent->url;
01705 info.object_buf_status = url_ent->object_buf_status;
01706
01707 info.object_buf = TSIOBufferCreate();
01708 info.object_buf_reader = TSIOBufferReaderAlloc(info.object_buf);
01709
01710 ((MIOBuffer *) info.object_buf)->write(reader);
01711
01712 prefetch_config->embedded_obj_hook(TS_PREFETCH_EMBEDDED_OBJECT_HOOK, &info);
01713 }
01714
01715 if (url_ent->object_buf_status == TS_PREFETCH_OBJ_BUF_NEEDED) {
01716
01717 free();
01718 break;
01719 }
01720
01721 if (data_blast.type == TS_PREFETCH_TCP_BLAST) {
01722 g_conn_table->append(url_ent->child_ip, buf, reader);
01723 buf = 0;
01724 free();
01725 break;
01726 } else {
01727 SET_HANDLER((EventHandler) (&PrefetchBlaster::blastObject));
01728 *(int *) reader->start() = htonl(reader->read_avail());
01729
01730 io_block = ioBlockAllocator.alloc();
01731 io_block->alloc(BUFFER_SIZE_INDEX_32K);
01732
01733 seq_no = get_udp_seq_no();
01734
01735 }
01736
01737 case NET_EVENT_DATAGRAM_WRITE_COMPLETE:{
01738 io_block->reset();
01739 io_block->fill(PRELOAD_UDP_HEADER_LEN);
01740
01741 int64_t nread_avail = reader->read_avail();
01742
01743 if (nread_avail <= 0) {
01744 free();
01745 break;
01746 }
01747
01748 int64_t nwrite_avail = io_block->write_avail();
01749
01750 int64_t towrite = (nread_avail < nwrite_avail) ? nread_avail : nwrite_avail;
01751
01752 reader->read(io_block->end(), towrite);
01753 io_block->fill(towrite);
01754
01755 Debug("PrefetchBlaster", "UDP: sending data: pkt_no: %d last_pkt: %d"
01756 " url: %s", n_pkts_sent, (towrite >= nread_avail), url_ent->url);
01757
01758 setup_udp_header(io_block->start(), seq_no, n_pkts_sent++, (towrite >= nread_avail));
01759
01760 IpEndpoint saddr;
01761 ats_ip_copy(&saddr.sa,
01762 ats_is_ip(&url_ent->data_multicast_ip)
01763 ? &url_ent->data_multicast_ip.sa
01764 : &url_ent->child_ip.sa
01765 );
01766 ats_ip_port_cast(&saddr) = htons(prefetch_config->stuffer_port);
01767
01768
01769
01770
01771 udpNet.sendto_re(this, NULL, prefetch_udp_fd, &saddr.sa, sizeof(saddr), io_block, io_block->read_avail());
01772 }
01773 break;
01774
01775 case NET_EVENT_DATAGRAM_WRITE_ERROR:
01776 Debug("PrefetchBlaster", "error in sending the udp data %p", data);
01777
01778 default:
01779 ink_assert(!"unexpected event");
01780 }
01781 return EVENT_DONE;
01782 }
01783
01784 int
01785 PrefetchBlaster::invokeBlaster()
01786 {
01787 int ret = (cache_http_info && !prefetch_config->push_cached_objects)
01788 ? TS_PREFETCH_DISCONTINUE : TS_PREFETCH_CONTINUE;
01789
01790 TSPrefetchBlastData url_blast = prefetch_config->default_url_blast;
01791 data_blast = prefetch_config->default_data_blast;
01792
01793 if (prefetch_config->embedded_url_hook) {
01794
01795 TSPrefetchInfo info;
01796
01797 info.request_buf = reinterpret_cast<TSMBuffer>(request);
01798 info.request_loc = reinterpret_cast<TSMLoc>(request->m_http);
01799 info.response_buf = 0;
01800 info.response_loc = 0;
01801
01802 info.object_buf = 0;
01803 info.object_buf_reader = 0;
01804 info.object_buf_status = TS_PREFETCH_OBJ_BUF_NOT_NEEDED;
01805
01806 ats_ip_copy(ats_ip_sa_cast(&info.client_ip), &url_ent->child_ip);
01807 info.embedded_url = url_ent->url;
01808 info.present_in_cache = (cache_http_info != NULL);
01809 info.url_blast = url_blast;
01810 info.url_response_blast = data_blast;
01811
01812 ret = (*prefetch_config->embedded_url_hook)
01813 (TS_PREFETCH_EMBEDDED_URL_HOOK, &info);
01814
01815 url_blast = info.url_blast;
01816 data_blast = info.url_response_blast;
01817
01818 url_ent->object_buf_status = info.object_buf_status;
01819 }
01820
01821 if (ret == TS_PREFETCH_CONTINUE) {
01822
01823 if (TS_PREFETCH_MULTICAST_BLAST == url_blast.type)
01824 ats_ip_copy(&url_ent->url_multicast_ip, ats_ip_sa_cast(&url_blast.ip));
01825 if (TS_PREFETCH_MULTICAST_BLAST == data_blast.type)
01826 ats_ip_copy(&url_ent->data_multicast_ip, ats_ip_sa_cast(&data_blast.ip));
01827
01828 if (url_ent->object_buf_status != TS_PREFETCH_OBJ_BUF_NEEDED) {
01829 if (url_blast.type == TS_PREFETCH_TCP_BLAST)
01830 url_list = transform->tcp_url_list;
01831 else
01832 url_list = transform->udp_url_list;
01833 }
01834
01835
01836 if (prefetch_config->max_recursion > 0 && serverVC) {
01837 serverVC->do_io_close();
01838 serverVC = NULL;
01839 cache_http_info = 0;
01840 }
01841
01842
01843
01844
01845
01846
01847 handleEvent(EVENT_INTERVAL, NULL);
01848 } else {
01849 free();
01850 }
01851 return 0;
01852 }
01853
01854 void
01855 PrefetchBlaster::initCacheLookupConfig()
01856 {
01857
01858
01859 HttpConfigParams *http_config_params = HttpConfig::acquire();
01860 cache_lookup_config.cache_global_user_agent_header = http_config_params->oride.global_user_agent_header ? true : false;
01861 cache_lookup_config.cache_enable_default_vary_headers =
01862 http_config_params->cache_enable_default_vary_headers ? true : false;
01863 cache_lookup_config.cache_vary_default_text = http_config_params->cache_vary_default_text;
01864 cache_lookup_config.cache_vary_default_images = http_config_params->cache_vary_default_images;
01865 cache_lookup_config.cache_vary_default_other = http_config_params->cache_vary_default_other;
01866
01867 HttpConfig::release(http_config_params);
01868 }
01869
01870 static int
01871 config_read_proto(TSPrefetchBlastData &blast, const char *str)
01872 {
01873 if (strncasecmp(str, "udp", 3) == 0)
01874 blast.type = TS_PREFETCH_UDP_BLAST;
01875 else if (strncasecmp(str, "tcp", 3) == 0)
01876 blast.type = TS_PREFETCH_TCP_BLAST;
01877 else {
01878 if (strncasecmp("multicast:", str, 10) == 0) {
01879 if (0 != ats_ip_pton(str, ats_ip_sa_cast(&blast.ip))) {
01880 Error("PrefetchProcessor: Address specified for multicast does not seem to "
01881 "be of the form multicast:ip_addr (eg: multicast:224.0.0.1)");
01882 return 1;
01883 } else {
01884 ip_text_buffer ipb;
01885 blast.type = TS_PREFETCH_MULTICAST_BLAST;
01886 Debug("Prefetch", "Setting multicast address: %s\n", ats_ip_ntop(ats_ip_sa_cast(&blast.ip), ipb, sizeof(ipb)));
01887 }
01888 } else {
01889 Error("PrefetchProcessor: The protocol for Prefetch should of the form: " "tcp or udp or multicast:ip_address");
01890 return 1;
01891 }
01892 }
01893
01894 return 0;
01895 }
01896
01897 int
01898 PrefetchConfiguration::readConfiguration()
01899 {
01900 ats_scoped_str conf_path;
01901 int fd = -1;
01902
01903 local_http_server_port = stuffer_port = 0;
01904 prefetch_enabled = REC_ConfigReadInteger("proxy.config.prefetch.prefetch_enabled");
01905 if (prefetch_enabled <= 0) {
01906 prefetch_enabled = 0;
01907 return 0;
01908 }
01909
01910 local_http_server_port = HttpProxyPort::findHttp(AF_INET)->m_port;
01911 REC_ReadConfigInteger(stuffer_port, "proxy.config.prefetch.child_port");
01912 REC_ReadConfigInteger(url_buffer_size, "proxy.config.prefetch.url_buffer_size");
01913 REC_ReadConfigInteger(url_buffer_timeout, "proxy.config.prefetch.url_buffer_timeout");
01914 REC_ReadConfigInteger(keepalive_timeout, "proxy.config.prefetch.keepalive_timeout");
01915 if (keepalive_timeout <= 0)
01916 keepalive_timeout = 3600;
01917
01918 REC_ReadConfigInteger(push_cached_objects, "proxy.config.prefetch.push_cached_objects");
01919
01920 REC_ReadConfigInteger(max_object_size, "proxy.config.prefetch.max_object_size");
01921
01922 REC_ReadConfigInteger(max_recursion, "proxy.config.prefetch.max_recursion");
01923
01924 REC_ReadConfigInteger(redirection, "proxy.config.prefetch.redirection");
01925
01926 char *tstr = REC_ConfigReadString("proxy.config.prefetch.default_url_proto");
01927 if (config_read_proto(default_url_blast, tstr))
01928 goto Lerror;
01929
01930 tstr = REC_ConfigReadString("proxy.config.prefetch.default_data_proto");
01931 if (config_read_proto(default_data_blast, tstr))
01932 goto Lerror;
01933
01934
01935
01936
01937 conf_path = RecConfigReadConfigPath("proxy.config.prefetch.config_file");
01938 if (!conf_path) {
01939 Warning("PrefetchProcessor: No prefetch configuration file specified. Prefetch disabled\n");
01940 goto Lerror;
01941 }
01942
01943 fd = open(conf_path, O_RDONLY);
01944 if (fd < 0) {
01945 Error("PrefetchProcessor: Error, could not open '%s' disabling Prefetch\n", (const char *)conf_path);
01946 goto Lerror;
01947 }
01948
01949 char *temp_str;
01950 if ((temp_str = Load_IpMap_From_File(&ip_map, fd, "prefetch_children")) != 0) {
01951 Error("PrefetchProcessor: Error in reading ip_range from %s: %.256s\n", (const char *)conf_path, temp_str);
01952 ats_free(temp_str);
01953 goto Lerror;
01954 }
01955
01956 lseek(fd, 0, SEEK_SET);
01957 readHtmlTags(fd, &html_tags_table, &html_attrs_table);
01958 if (html_tags_table == NULL) {
01959 html_tags_table = &prefetch_allowable_html_tags[0];
01960 ink_assert(html_attrs_table == NULL);
01961 html_attrs_table = &prefetch_allowable_html_attrs[0];
01962 }
01963
01964 close(fd);
01965 return 0;
01966 Lerror:
01967 if (fd >= 0)
01968 close(fd);
01969 prefetch_enabled = 0;
01970 return -1;
01971 }
01972
01973 void
01974 PrefetchConfiguration::readHtmlTags(int fd, html_tag ** ptags, html_tag ** pattrs)
01975 {
01976 int ntags = 0;
01977 html_tag tags[256];
01978 html_tag attrs[256];
01979 bool attrs_exist = false;
01980 char buf[512], tag[64], attr[64], attr_tag[64], attr_attr[64];
01981 int num;
01982 int end_of_file = 0;
01983
01984 memset(attrs, 0, 256 * sizeof(html_tag));
01985 while (!end_of_file && ntags < 256) {
01986 char c;
01987 int ret, len = 0;
01988
01989 while (((ret = read(fd, &c, 1)) == 1) && (c != '\n'))
01990 if (len < 511)
01991 buf[len++] = c;
01992 buf[len] = 0;
01993 if (ret <= 0)
01994 end_of_file = 1;
01995
01996
01997
01998 if ((num = sscanf(buf, " html_tag %63s %63s %63s %63s", tag, attr, attr_tag, attr_attr)) >= 2) {
01999 Debug("Prefetch", "Read html_tag: %s %s\n", tag, attr);
02000 tags[ntags].tag = ats_strdup(tag);
02001 tags[ntags].attr = ats_strdup(attr);
02002 if (num >= 4) {
02003 if (!attrs_exist)
02004 attrs_exist = true;
02005 attrs[ntags].tag = ats_strdup(attr_tag);
02006 attrs[ntags].tag = ats_strdup(attr_attr);
02007 }
02008 ntags++;
02009 }
02010 }
02011
02012 if (ntags > 0) {
02013 html_tag *xtags = (html_tag *)ats_malloc((ntags + 3) * sizeof(html_tag));
02014
02015 memcpy(xtags, &tags[0], ntags * sizeof(tags[0]));
02016
02017 xtags[ntags].tag = "base";
02018 xtags[ntags].attr = "href";
02019 xtags[ntags + 1].tag = "meta";
02020 xtags[ntags + 1].attr = "content";
02021 xtags[ntags + 2].tag = xtags[ntags + 2].attr = NULL;
02022
02023 *ptags = xtags;
02024 if (attrs_exist) {
02025 html_tag *xattrs = (html_tag *)ats_malloc((ntags + 3) * sizeof(html_tag));
02026 memcpy(xattrs, &attrs[0], 256 * sizeof(html_tag));
02027 *pattrs = xattrs;
02028 } else
02029 *pattrs = NULL;
02030 return;
02031 }
02032
02033 *ptags = NULL;
02034 *pattrs = NULL;
02035 }
02036
02037
02038
02039 #define CONN_ARR_SIZE 256
02040 inline int
02041 KeepAliveConnTable::ip_hash(IpEndpoint const& ip)
02042 {
02043 return ats_ip_hash(&ip.sa) & (CONN_ARR_SIZE - 1);
02044 }
02045
02046 inline int
02047 KeepAliveConn::append(IOBufferReader *rdr)
02048 {
02049 int64_t size = rdr->read_avail();
02050
02051 nbytes_added += size;
02052
02053 buf->write(rdr);
02054 vio->reenable();
02055
02056 return 0;
02057 }
02058
02059 int
02060 KeepAliveConnTable::init()
02061 {
02062 arr = new conn_elem[CONN_ARR_SIZE];
02063
02064 for (int i = 0; i < CONN_ARR_SIZE; i++) {
02065 arr[i].conn = 0;
02066 arr[i].mutex = new_ProxyMutex();
02067 }
02068
02069 return 0;
02070 }
02071
02072 void
02073 KeepAliveConnTable::free()
02074 {
02075 for (int i = 0; i < CONN_ARR_SIZE; i++)
02076 arr[i].mutex.clear();
02077
02078 delete arr;
02079 delete this;
02080 }
02081
02082 ClassAllocator<KeepAliveLockHandler> prefetchLockHandlerAllocator("prefetchLockHandlerAllocator");
02083
02084 int
02085 KeepAliveConnTable::append(IpEndpoint const& ip, MIOBuffer *buf, IOBufferReader *reader)
02086 {
02087 int index = ip_hash(ip);
02088
02089 MUTEX_TRY_LOCK(trylock, arr[index].mutex, this_ethread());
02090 if (!trylock) {
02091
02092
02093
02094
02095
02096
02097
02098
02099 prefetchLockHandlerAllocator.alloc()->init(ip, buf, reader);
02100 return 1;
02101 }
02102
02103 KeepAliveConn **conn = &arr[index].conn;
02104
02105 while (*conn && ! ats_ip_addr_eq(&(*conn)->ip, &ip))
02106 conn = &(*conn)->next;
02107
02108 if (*conn) {
02109 (*conn)->append(reader);
02110 free_MIOBuffer(buf);
02111 } else {
02112 *conn = new KeepAliveConn;
02113 (*conn)->init(ip, buf, reader);
02114 }
02115
02116 return 0;
02117 }
02118
02119 int
02120 KeepAliveConn::init(IpEndpoint const& xip, MIOBuffer *xbuf, IOBufferReader *xreader)
02121 {
02122 mutex = g_conn_table->arr[KeepAliveConnTable::ip_hash(xip)].mutex;
02123
02124 ip = xip;
02125 buf = xbuf;
02126 reader = xreader;
02127
02128 childVC = 0;
02129 vio = 0;
02130 next = 0;
02131
02132 read_buf = new_MIOBuffer();
02133
02134 nbytes_added = reader->read_avail();
02135
02136 SET_HANDLER(&KeepAliveConn::handleEvent);
02137
02138
02139 netProcessor.connect_re(this, &ip.sa);
02140
02141 return 0;
02142 }
02143
02144 void
02145 KeepAliveConn::free()
02146 {
02147 if (childVC)
02148 childVC->do_io_close();
02149
02150 if (buf)
02151 free_MIOBuffer(buf);
02152 if (read_buf)
02153 free_MIOBuffer(read_buf);
02154
02155 KeepAliveConn *prev = 0;
02156 KeepAliveConn **head = &g_conn_table->arr[KeepAliveConnTable::ip_hash(ip)].conn;
02157
02158 KeepAliveConn *conn = *head;
02159 while (conn != this) {
02160 prev = conn;
02161 conn = conn->next;
02162 }
02163
02164 if (prev)
02165 prev->next = next;
02166 else
02167 *head = next;
02168
02169 mutex.clear();
02170 Debug("PrefetchKConn", "deleting a KeepAliveConn");
02171 delete this;
02172 }
02173
02174 int
02175 KeepAliveConn::handleEvent(int event, void *data)
02176 {
02177 ip_text_buffer ipb;
02178
02179 switch (event) {
02180
02181 case NET_EVENT_OPEN:
02182
02183 childVC = (NetVConnection *) data;
02184
02185 childVC->set_inactivity_timeout(HRTIME_SECONDS(prefetch_config->keepalive_timeout));
02186
02187 vio = childVC->do_io_write(this, INT64_MAX, reader);
02188
02189
02190 childVC->do_io_read(this, INT64_MAX, read_buf);
02191 break;
02192
02193 case NET_EVENT_OPEN_FAILED:
02194 Debug("PrefetchKeepAlive", "Connection to child %s failed\n", ats_ip_ntop(&ip.sa, ipb, sizeof(ipb)));
02195 free();
02196 break;
02197
02198 case VC_EVENT_WRITE_READY:
02199
02200
02201 break;
02202
02203 case VC_EVENT_INACTIVITY_TIMEOUT:
02204
02205
02206
02207 if (reader->read_avail())
02208 childVC->set_inactivity_timeout(HRTIME_SECONDS(prefetch_config->keepalive_timeout));
02209 else
02210 free();
02211 break;
02212
02213 case VC_EVENT_READ_COMPLETE:
02214 case VC_EVENT_READ_READY:
02215
02216
02217 case VC_EVENT_EOS:
02218 Debug("PrefetchKeepAlive", "the other side closed the connection");
02219 free();
02220 break;
02221
02222 case VC_EVENT_ERROR:
02223 Debug("PrefetchKeepAlive", "got VC_ERROR.. connection problem? " "(ip: %s)", ats_ip_ntop(&ip.sa, ipb, sizeof(ipb)));
02224 free();
02225 break;
02226
02227 default:
02228 ink_assert(!"not reached");
02229 free();
02230 }
02231
02232 return EVENT_DONE;
02233 }
02234
02235 int
02236 KeepAliveLockHandler::handleEvent(int event, void * )
02237 {
02238 if (event == EVENT_INTERVAL)
02239 g_conn_table->append(ip, buf, reader);
02240
02241 prefetchLockHandlerAllocator.free(this);
02242
02243 return EVENT_DONE;
02244 }
02245
02246
02247 int
02248 TSPrefetchHookSet(int hook_no, TSPrefetchHook hook)
02249 {
02250 switch (hook_no) {
02251
02252 case TS_PREFETCH_PRE_PARSE_HOOK:
02253 prefetch_config->pre_parse_hook = hook;
02254 return 0;
02255
02256 case TS_PREFETCH_EMBEDDED_URL_HOOK:
02257 prefetch_config->embedded_url_hook = hook;
02258 return 0;
02259
02260 case TS_PREFETCH_EMBEDDED_OBJECT_HOOK:
02261 prefetch_config->embedded_obj_hook = hook;
02262 return 0;
02263
02264 default:
02265 return -1;
02266 }
02267 }
02268
02269 #endif // PREFETCH