• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • File List
  • Globals

Prefetch.cc

Go to the documentation of this file.
00001 /** @file
00002 
00003   A brief file description
00004 
00005   @section license License
00006 
00007   Licensed to the Apache Software Foundation (ASF) under one
00008   or more contributor license agreements.  See the NOTICE file
00009   distributed with this work for additional information
00010   regarding copyright ownership.  The ASF licenses this file
00011   to you under the Apache License, Version 2.0 (the
00012   "License"); you may not use this file except in compliance
00013   with the License.  You may obtain a copy of the License at
00014 
00015       http://www.apache.org/licenses/LICENSE-2.0
00016 
00017   Unless required by applicable law or agreed to in writing, software
00018   distributed under the License is distributed on an "AS IS" BASIS,
00019   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00020   See the License for the specific language governing permissions and
00021   limitations under the License.
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   //All embedded objects (fetched by the browser without requiring a click)
00036   //should be here
00037   //{ "a", "href"},   /* NOT USED */
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"},             // special handling
00047   {"meta", "content"},          // special handling
00048   //{ "area", "href"},  //used for testing parser
00049   {"input", "src"},
00050   {"link", "href"},
00051   {NULL, NULL}
00052 };
00053 
00054 // this attribute table is hard coded. It has to be the same size as
00055 // the prefetch_allowable_html_tags table
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"},        // We want to prefetch the .css files that are common; make sure this matches {"link", "href"}
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;                   //we are not pinning
00121   hdr[2] = (url_promise) ? htonl(PRELOAD_HDR_URL_PROMISE_FLAG) : 0;
00122 }
00123 
00124 // Raghu's info about domain extraction
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     // checking for .com .edu .net .org .gov .mil .int
00132     h_cur = host + host_len - 4;
00133     if (*h_cur == '.') {
00134       // convert to lower case
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       // there is a high posibility that the postfix is one of the seven
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           // found a '.'
00159           h_cur++;
00160         } else if (*h_cur == '.')
00161           return NULL;
00162 
00163         return h_cur;
00164       }
00165     }
00166   }
00167   // for non-top level domains, require the first char is not '.' and
00168   // two '.' minimum, e.g. abc.va.us
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   /* returns > 0 if the url is modified */
00193 
00194   char *p, *root, *end = url + len[0];
00195   int modified = 0;             // most of the time we don't modify the url.
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   //get to the first slash:
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:{        // "/dir/../" or "/../" => "/"
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:                 /* NONE */ ;
00254       };                        /* end of switch (state) */
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     //ok, now remove all the 0s in between
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 /** Used to free old PrefetchConfiguration data. */
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 ATS_UNUSED */, Event * /* e ATS_UNUSED */)
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 /* event ATS_UNUSED */, void * /* edata ATS_UNUSED */)
00341 {
00342   Debug("Prefetch", "Handling Prefetch config change");
00343 
00344   PrefetchConfiguration *new_prefetch_config = new PrefetchConfiguration;
00345   if (new_prefetch_config->readConfiguration() == 0) {
00346     // switch the prefetch_config
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     // new config construct error, we should not use the new config
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 * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */,
00361                    RecData /* data ATS_UNUSED */, void * /* cookie ATS_UNUSED */)
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   //extract domain
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   // Check for redirection and redirect get the redirect URL before parsing the
00405   // body of the redirect.
00406   redirect(resp);
00407 }
00408 
00409 PrefetchTransform::~PrefetchTransform()
00410 {
00411   //inform the lists that there no more urls left.
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         // If the write vio is null, it means it doesn't want
00475         // to get anymore event (WRITE_READY or WRITE_COMPLETE)
00476         // It also means we're done reading
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             //Debug("PrefetchParser", "Read avail before = %d\n", avail);
00509 
00510             m_output_buf->write(buf_reader, towrite);
00511 
00512             parse_data(buf_reader);
00513 
00514             //buf_reader->consume (towrite);
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   /* Check for responses validity. If the response is valid, determine the status of the response.
00546      We need to find out if there was a redirection (301, 302, 303, 307).
00547    */
00548   if ((resp != NULL) && (resp->valid())) {
00549     response_status = resp->status_get();
00550 
00551     /* OK, so we got the response. Now if the response is a redirect we have to check if we also
00552        got a Location: header. This indicates the new location where our object is located.
00553        If refirect_url was not found, letz falter back to just a recursion. Since
00554        we might find the url in the body.
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       //Debug("PrefetchParserURLs", "Duplicate URL: %s", url_start);
00617       continue;
00618     }
00619     //Debug("PrefetchParserURLs", "Found embedded URL: %s", url_start);
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   // we depend on this to setup @a client_ipb for all subsequent Debug().
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   /* skip if it is encoded */
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   //now insert the parser
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 /* contp ATS_UNUSED */, 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   //Debug("PrefetchPlugin", "Returning after check_n_attach_prefetch_transform()\n");
00794 
00795   return 0;
00796 }
00797 
00798 void
00799 PrefetchProcessor::start()
00800 {
00801   // we need to create the config and register all config callbacks
00802   // first.
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     //hdrtoken_wks_to_length(PREFETCH_FIELD_RECURSION);
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 //Blaster
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        PrefetchTransform informed us not to expect any more URLs
00866        This is used so that we dont wait for timeout when the mtu has not filled
00867        but theren't any URLs left in the page.
00868      */
00869     if (list_head) {
00870       action->cancel();
00871       action = NULL;
00872       invokeUrlBlaster();
00873     }
00874     free();                     // we need to call free because PrefetchTransform does not.
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;  //will be reversed before sending
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   //free the list;
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   //reverse the list:
00932   PrefetchUrlEntry *entry = NULL;
00933   //int total_len = 0;
00934   while (url_head) {
00935     //total_len += url_head->len;
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   //write it:
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   //extract host and the path
01009   // by this time, the url is sufficiently error checked..
01010   // we will just use sscanf to parse it:
01011   //int host_pos=-1, path_pos=-1;
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);    /* BugID: INKqa11148 */
01017   //request->url_get()->clear();
01018 
01019   // INKqa12871
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   // BZ 50540
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   // INKqa12871
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] != '.') ||    //nbc.com != cnbc.com
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   //Should we remove any cookies? Probably yes
01075   //We should probably add a referer header.
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   // FIXME? ip_len is pretty useless here.
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     //this is a recursive prefetch. get child ip address from
01088     //Client-IP header
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();    //refcount
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   // another double for loop for multiple Cookie headers
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             // INKqa11823 id=new to replace id=old
01168             return false;
01169           }
01170         } else {
01171           if (iter_cookie_len == move_cookie_len && memcmp(iter_cookie, move_cookie, iter_cookie_len) == 0) {
01172             // dupliate - do not add
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 // resp_hdr is the server response for the top page
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     // mising domain name information
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] != '.') ||      //nbc.com != cnbc.com
01234         strncasecmp(domain_start, host_end - (cmp_len - 1), cmp_len) != 0) {
01235       add_cookies = false;
01236       goto Lcheckcookie;
01237     }
01238     // Netscape Cookie spec says the default domain is the host name
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     // delete the old Cookie first - INKqa11823
01260     request->field_delete(MIME_FIELD_COOKIE, MIME_LEN_COOKIE);
01261     // for Set-Cookie it is not comma separated, each a_value contains
01262     // the value for one Set-Cookie header
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           // the Set-cookie header specify the domain name
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             // domain=".amazon.com" style
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               // invalid fomat, missing trailing quote
01304               goto Lnotmatch;
01305             }
01306           }
01307           // remove trailing .
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           // matching domain based on RFC2109
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           // make sure that the prefix doesn't contain a '.'
01323           if (prefix_len > 0 && memchr(host_start, '.', prefix_len))
01324             goto Lnotmatch;
01325 
01326           // Ok, when we get here, it should be a real match as far as
01327           //        domain is concerned.
01328           // possibly overwrite the default domain matching result
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             // a match - "/"
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             // invalid format "//"
01356             goto Lnotmatch;
01357           }
01358           // matching path based on RFC2109
01359 
01360           int dest_path_len;
01361           const char *dest_path_start = request->url_get()->path_get(&dest_path_len);
01362 
01363           // BZ 49734
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           // when we get here the path is a match
01377         } else if (move_cookie_len > 8 && strncasecmp(move_cookie, "expires=", 8) == 0) {
01378           // ignore expires directive for the time being
01379           continue;
01380         } else {
01381           // append the value to the request Cookie header
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     // INKqa11823 - now add the old Cookies back based on the new cookies
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               // this is a unique cookie attribute, ready to add
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     // add_cookies now means whether new Cookie headers are created
01444     // from the Set-Cookie headers
01445     // now also check the existing Cookie headers from the req_hdr
01446     add_cookies = add_cookies || existing_req_cookies;
01447   }
01448 
01449 Lcheckcookie:
01450   if (add_cookies == false) {
01451     // delete the cookie header, if there is any at all
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      This one first decides if we need to send the url or not.
01467      If necessary, send the url ( Right now, just connect on TCP
01468      connection and send the data)
01469    */
01470 
01471   switch (event) {
01472 
01473   case EVENT_IMMEDIATE:{
01474       //Here, we need to decide if we need to prefetch based on whether it
01475       //is in the cache or not.
01476 
01477       //if (cache_lookup_necessary) do:
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       // Otherwise, just wait till PrefetchUrlBlaster signals us.
01508 
01509       break;
01510     }
01511 
01512   case CACHE_EVENT_OPEN_READ:{
01513       //action = NULL;
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     //action = NULL;
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   //copy the http header into to the buffer
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         /*This is a hack to be able to send headers beginning with @ */
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      This one makes an http connection on the local host and sends the request
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 * /* data ATS_UNUSED */)
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       //right now we don't handle DEL events on the child
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 /* following sturcture and masks should be the same in StufferUdpReceiver.cc
01681    on microTS */
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     // (data == (void*)1) implies we are not sending the object because
01693     // it is too large. Instead we will send "DEL" msg for the promise
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       //we need not send this to the child
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       //fall through
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       //saddr.sin_addr.s_addr = htonl((209<<24)|(131<<16)|(60<<8)|243);
01769       //saddr.sin_addr.s_addr = htonl((209<<24)|(131<<16)|(48<<8)|52);
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     //if recursion is enabled, go through local host even for cached
01835     //objects
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        if (data_proto == TCP_BLAST)
01844        data_blaster = (EventHandler)(&PrefetchBlaster::tcpDataBlaster);
01845        else data_blaster = (EventHandler)(&PrefetchBlaster::udpDataBlaster);
01846      */
01847     handleEvent(EVENT_INTERVAL, NULL);
01848   } else {
01849     free();
01850   }
01851   return 0;
01852 }
01853 
01854 void
01855 PrefetchBlaster::initCacheLookupConfig()
01856 {
01857   //The look up parameters are intialized in the same as it is done
01858   //in HttpSM::init(). Any changes there should come in here.
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 {                        // this is a multicast address:
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   //pre_parse_hook = 0;
01935   //embedded_url_hook = 0;
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     //read the line
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     // length(63) specified in sscanf, no need to worry about string overflow
01997     // coverity[secure_coding]
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     //the following two are always added
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 /* Keep Alive stuff */
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     /* This lock fails quite often. This can be expected because,
02092        multiple threads try to append their buffer all the the same
02093        time to the same connection. Other thread holds it for a long
02094        time when it is doing network IO 'n stuff. This is one more
02095        reason why URL messages should be sent by UDP. We will avoid
02096        appending small messages here and those URL message reach the
02097        child much faster */
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;     //change to fast allocator?
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();   //we should give minimum size possible
02133 
02134   nbytes_added = reader->read_avail();
02135 
02136   SET_HANDLER(&KeepAliveConn::handleEvent);
02137 
02138   //we are already under lock
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     //this read lets us disconnect when the other side closes
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     //Debug("PrefetchTemp", "ndone = %d", vio->ndone);
02200 
02201     break;
02202 
02203   case VC_EVENT_INACTIVITY_TIMEOUT:
02204     //Debug("PrefetchTemp", "%d sec timeout expired for %d.%d.%d.%d",
02205     //prefetch_config->keepalive_timeout, IPSTRARGS(ip));
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     /*Right now we dont expect any response from the child.
02216        Read event implies POLLHUP */
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 * /* data ATS_UNUSED */)
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 /* API */
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

Generated by  doxygen 1.7.1