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