00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 #include "libts.h"
00025 
00026 #include "HttpTransact.h"
00027 #include "HttpTransactHeaders.h"
00028 #include "HttpTransactCache.h"
00029 #include "time.h"
00030 #include "HTTP.h"
00031 #include "HttpCompat.h"
00032 #include "Error.h"
00033 #include "InkErrno.h"
00034 
00035 ClassAllocator<CacheLookupHttpConfig> CacheLookupHttpConfigAllocator("CacheLookupHttpConfigAllocator");
00036 
00037 CacheLookupHttpConfig global_cache_lookup_config;
00038 
00039 
00040 
00041 
00042 
00043 
00044 inline static const char *
00045 find_etag(const char *raw_tag_field, int raw_tag_field_len, int *length)
00046 {
00047   const char *quote;
00048   int etag_length = 0;
00049   const char *etag_start = raw_tag_field;
00050   const char *etag_end = raw_tag_field + raw_tag_field_len;
00051 
00052   if ((raw_tag_field_len >= 2) && (etag_start[0] == 'W' && etag_start[1] == '/')) {
00053     etag_start += 2;
00054   }
00055 
00056   etag_length = etag_end - etag_start;
00057 
00058   if ((etag_start < etag_end) && (*etag_start == '"')) {
00059     ++etag_start;
00060     --etag_length;
00061     quote = (const char *) memchr(etag_start, '"', etag_length);
00062     if (quote)
00063       etag_length = quote - etag_start;
00064   }
00065   *length = etag_length;
00066   return etag_start;
00067 }
00068 
00069 
00070 
00071 
00072 
00073 
00074 inline static bool
00075 do_strings_match_strongly(const char *raw_tag_field,
00076                           int raw_tag_field_len, const char *comma_sep_tag_list, int comma_sep_tag_list_len)
00077 {
00078   StrList tag_list;
00079   const char *etag_start;
00080   int n, etag_length;
00081 
00082 
00083   
00084   if ((raw_tag_field_len >= 2) && (raw_tag_field[0] == 'W' && raw_tag_field[1] == '/')) {
00085     return false;
00086   }
00087   
00088   etag_start = find_etag(raw_tag_field, raw_tag_field_len, &etag_length);
00089 
00090   
00091   HttpCompat::parse_comma_list(&tag_list, comma_sep_tag_list, comma_sep_tag_list_len);
00092 
00093   
00094   for (Str * tag = tag_list.head; tag; tag = tag->next) {
00095     
00096     if ((tag->len == 1) && (tag->str[0] == '*'))
00097       return true;
00098 
00099     n = 0;
00100 
00101     if (((int) (tag->len - n) == etag_length) && (strncmp(etag_start, tag->str + n, etag_length) == 0)) {
00102       return true;
00103     }
00104   }
00105 
00106   return false;
00107 }
00108 
00109 
00110 
00111 
00112 
00113 
00114 inline static bool
00115 do_strings_match_weakly(const char *raw_tag_field,
00116                         int raw_tag_field_len, const char *comma_sep_tag_list, int comma_sep_tag_list_len)
00117 {
00118   StrList tag_list;
00119   const char *etag_start;
00120   const char *cur_tag;
00121   int etag_length, cur_tag_len;
00122 
00123   
00124   etag_start = find_etag(raw_tag_field, raw_tag_field_len, &etag_length);
00125 
00126   
00127   HttpCompat::parse_comma_list(&tag_list, comma_sep_tag_list, comma_sep_tag_list_len);
00128 
00129   for (Str * tag = tag_list.head; tag; tag = tag->next) {
00130     
00131     if ((tag->len == 1) && (tag->str[0] == '*'))
00132       return true;
00133 
00134     
00135     
00136     cur_tag = find_etag(tag->str, tag->len, &cur_tag_len);
00137     if ((cur_tag_len == etag_length) && (strncmp(cur_tag, etag_start, cur_tag_len) == 0))
00138       return true;
00139   }
00140   return false;
00141 }
00142 
00143 inline static bool
00144 is_asterisk(char *s)
00145 {
00146   return ((s[0] == '*') && (s[1] == NUL));
00147 }
00148 
00149 inline static bool
00150 is_empty(char *s)
00151 {
00152   return (s[0] == NUL);
00153 }
00154 
00155 
00156 
00157 
00158 
00159 
00160 
00161 
00162 
00163 
00164 
00165 
00166 
00167 int
00168 HttpTransactCache::SelectFromAlternates(CacheHTTPInfoVector * cache_vector,
00169                                         HTTPHdr * client_request, CacheLookupHttpConfig * http_config_params)
00170 {
00171   time_t current_age, best_age = NUM_SECONDS_IN_ONE_YEAR;
00172   time_t t_now = 0;
00173   int best_index = -1;
00174   float best_Q = -1.0;
00175   float unacceptable_Q = 0.0;
00176 
00177   int alt_count = cache_vector->count();
00178   if (alt_count == 0) {
00179     return -1;
00180   }
00181 
00182 
00183   Debug("http_match", "[SelectFromAlternates] # alternates = %d", alt_count);
00184   Debug("http_seq", "[SelectFromAlternates] %d alternates for this cached doc", alt_count);
00185   if (diags->on("http_alts")) {
00186     ACQUIRE_PRINT_LOCK()
00187       fprintf(stderr, "[alts] There are %d alternates for this request header.\n", alt_count);
00188     RELEASE_PRINT_LOCK()
00189   }
00190   
00191   if (http_config_params == &global_cache_lookup_config)
00192     return 0;
00193 
00194   if (!client_request->valid()) {
00195     return 0;
00196   }
00197   
00198   
00199   if (!client_request->valid()) {
00200     return 0;
00201   }
00202 
00203   for (int i = 0; i < alt_count; i++) {
00204     float Q;
00205     CacheHTTPInfo *obj = cache_vector->get(i);
00206     HTTPHdr *cached_request = obj->request_get();
00207     HTTPHdr *cached_response = obj->response_get();
00208 
00209     if (!(obj->object_key_get() == zero_key)) {
00210       ink_assert(cached_request->valid());
00211       ink_assert(cached_response->valid());
00212 
00213       Q = calculate_quality_of_match(http_config_params, client_request, cached_request, cached_response);
00214 
00215       if (alt_count > 1) {
00216         if (t_now == 0)
00217           t_now = ink_cluster_time();
00218         current_age = HttpTransactHeaders::calculate_document_age(obj->request_sent_time_get(),
00219                                                                   obj->response_received_time_get(),
00220                                                                   cached_response, cached_response->get_date(), t_now);
00221         
00222         if (current_age < 0)
00223           current_age = NUM_SECONDS_IN_ONE_YEAR; 
00224       } else {
00225         current_age = (time_t) 0;
00226       }
00227 
00228       if (diags->on("http_alts")) {
00229         fprintf(stderr, "[alts] ---- alternate #%d (Q = %g) has these request/response hdrs:\n", i + 1, Q);
00230         char b[4096];
00231         int used, tmp, offset;
00232         int done;
00233 
00234         offset = 0;
00235         do {
00236           used = 0;
00237           tmp = offset;
00238           done = cached_request->print(b, sizeof(b) - 1, &used, &tmp);
00239           offset += used;
00240           b[used] = '\0';
00241           fprintf(stderr, "%s", b);
00242         } while (!done);
00243 
00244         offset = 0;
00245         do {
00246           used = 0;
00247           tmp = offset;
00248           done = cached_response->print(b, sizeof(b) - 1, &used, &tmp);
00249           offset += used;
00250           b[used] = '\0';
00251           fprintf(stderr, "%s", b);
00252         } while (!done);
00253       }
00254 
00255       if ((Q > best_Q) || ((Q == best_Q) && (current_age <= best_age))) {
00256         best_Q = Q;
00257         best_age = current_age;
00258         best_index = i;
00259       }
00260     }
00261   }
00262   Debug("http_seq", "[SelectFromAlternates] Chosen alternate # %d", best_index);
00263   if (diags->on("http_alts")) {
00264     ACQUIRE_PRINT_LOCK()
00265       fprintf(stderr, "[alts] and the winner is alternate number %d\n", best_index + 1);
00266     RELEASE_PRINT_LOCK()
00267   }
00268 
00269   if ((best_index != -1) && (best_Q > unacceptable_Q)) {
00270     return best_index;
00271   } else {
00272     return -1;
00273   }
00274 }
00275 
00276 
00277 
00278 
00279 
00280 
00281 
00282 
00283 
00284 
00285 
00286 
00287 
00288 
00289 
00290 
00291 
00292 
00293 
00294 
00295 
00296 
00297 
00298 float
00299 HttpTransactCache::calculate_quality_of_match(CacheLookupHttpConfig * http_config_param,
00300                                               HTTPHdr * client_request,
00301                                               HTTPHdr * obj_client_request,
00302                                               HTTPHdr * obj_origin_server_response)
00303 {
00304   
00305   if (client_request->method_get_wksidx() == HTTP_WKSIDX_PURGE)
00306     return (float)1.0;
00307 
00308   
00309   float q[4], Q;
00310   MIMEField *accept_field;
00311   MIMEField *cached_accept_field;
00312   MIMEField *content_field;
00313 
00314   
00315   
00316   
00317   
00318   
00319   
00320   
00321   
00322   
00323   
00324   unsigned int vary_skip_mask = obj_origin_server_response->presence(MIME_PRESENCE_VARY) ? 1 : 3;
00325 
00326   
00327   q[1] = (q[2] = (q[3] = -2.0));
00328 
00329   
00330   content_field = obj_origin_server_response->field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
00331 
00332   
00333   if (http_config_param->ignore_accept_mismatch & vary_skip_mask) {
00334     
00335     q[0] = 1.0;
00336   } else {
00337     accept_field = client_request->field_find(MIME_FIELD_ACCEPT, MIME_LEN_ACCEPT);
00338 
00339     
00340     if (content_field == NULL || accept_field == NULL) {
00341       q[0] = 1.0; 
00342     } else {
00343       q[0] = calculate_quality_of_accept_match(accept_field, content_field);
00344     }
00345   }
00346 
00347   if (q[0] >= 0.0) {
00348     
00349     if (http_config_param->ignore_accept_charset_mismatch & vary_skip_mask) {
00350       
00351       q[1] = 1.0;
00352     } else {
00353       accept_field = client_request->field_find(MIME_FIELD_ACCEPT_CHARSET, MIME_LEN_ACCEPT_CHARSET);
00354       cached_accept_field = obj_client_request->field_find(MIME_FIELD_ACCEPT_CHARSET, MIME_LEN_ACCEPT_CHARSET);
00355 
00356       
00357       if (accept_field == NULL && cached_accept_field == NULL) {
00358         Debug("http_alternate", "Exact match for ACCEPT CHARSET (not in request nor cache)");
00359         q[1] = 1.001; 
00360       } else {
00361         q[1] = calculate_quality_of_accept_charset_match(accept_field, content_field, cached_accept_field);
00362       }
00363     }
00364 
00365     if (q[1] >= 0.0) {
00366       
00367       if (http_config_param->ignore_accept_encoding_mismatch & vary_skip_mask) {
00368         
00369         q[2] = 1.0;
00370       } else {
00371         accept_field = client_request->field_find(MIME_FIELD_ACCEPT_ENCODING, MIME_LEN_ACCEPT_ENCODING);
00372         content_field = obj_origin_server_response->field_find(MIME_FIELD_CONTENT_ENCODING, MIME_LEN_CONTENT_ENCODING);
00373         cached_accept_field = obj_client_request->field_find(MIME_FIELD_ACCEPT_ENCODING, MIME_LEN_ACCEPT_ENCODING);
00374 
00375         
00376         if (accept_field == NULL && cached_accept_field == NULL) {
00377           Debug("http_alternate", "Exact match for ACCEPT ENCODING (not in request nor cache)");
00378           q[2] = 1.001; 
00379         } else {
00380           q[2] = calculate_quality_of_accept_encoding_match(accept_field, content_field, cached_accept_field);
00381         }
00382       }
00383 
00384       if (q[2] >= 0.0) {
00385         
00386         if (http_config_param->ignore_accept_language_mismatch & vary_skip_mask) {
00387           
00388           q[3] = 1.0;
00389         } else {
00390           accept_field = client_request->field_find(MIME_FIELD_ACCEPT_LANGUAGE, MIME_LEN_ACCEPT_LANGUAGE);
00391           content_field = obj_origin_server_response->field_find(MIME_FIELD_CONTENT_LANGUAGE, MIME_LEN_CONTENT_LANGUAGE);
00392           cached_accept_field = obj_client_request->field_find(MIME_FIELD_ACCEPT_LANGUAGE, MIME_LEN_ACCEPT_LANGUAGE);
00393 
00394           
00395           if (accept_field == NULL && cached_accept_field == NULL) {
00396             Debug("http_alternate", "Exact match for ACCEPT LANGUAGE (not in request nor cache)");
00397             q[3] = 1.001; 
00398           } else {
00399             q[3] = calculate_quality_of_accept_language_match(accept_field, content_field, cached_accept_field);
00400           }
00401         }
00402       }
00403     }
00404   }
00405 
00406   
00407   Q = ((q[0] < 0) || (q[1] < 0) || (q[2] < 0) || (q[3] < 0)) ? -1.0 : q[0] * q[1] * q[2] * q[3];
00408 
00409   Debug("http_match", "    CalcQualityOfMatch: Accept match = %g", q[0]);
00410   Debug("http_seq", "    CalcQualityOfMatch: Accept match = %g", q[0]);
00411   Debug("http_alternate", "Content-Type and Accept %f", q[0]);
00412 
00413   Debug("http_match", "    CalcQualityOfMatch: AcceptCharset match = %g", q[1]);
00414   Debug("http_seq", "    CalcQualityOfMatch: AcceptCharset match = %g", q[1]);
00415   Debug("http_alternate", "Content-Type and Accept-Charset %f", q[1]);
00416 
00417   Debug("http_match", "    CalcQualityOfMatch: AcceptEncoding match = %g", q[2]);
00418   Debug("http_seq", "    CalcQualityOfMatch: AcceptEncoding match = %g", q[2]);
00419   Debug("http_alternate", "Content-Encoding and Accept-Encoding %f", q[2]);
00420 
00421   Debug("http_match", "    CalcQualityOfMatch: AcceptLanguage match = %g", q[3]);
00422   Debug("http_seq", "    CalcQualityOfMatch: AcceptLanguage match = %g", q[3]);
00423   Debug("http_alternate", "Content-Language and Accept-Language %f", q[3]);
00424 
00425   Debug("http_alternate", "Mult's Quality Factor: %f", Q);
00426   Debug("http_alternate", "----------End of Alternate----------");
00427 
00428   int force_alt = 0;
00429 
00430   if (Q > 0.0) {
00431     APIHook *hook;
00432     HttpAltInfo info;
00433     float qvalue;
00434 
00435     hook = http_global_hooks->get(TS_HTTP_SELECT_ALT_HOOK);
00436     if (hook) {
00437       info.m_client_req.copy_shallow(client_request);
00438       info.m_cached_req.copy_shallow(obj_client_request);
00439       info.m_cached_resp.copy_shallow(obj_origin_server_response);
00440       qvalue = 1.0;
00441 
00442       while (hook) {
00443         info.m_qvalue = 1.0;
00444         hook->invoke(TS_EVENT_HTTP_SELECT_ALT, &info);
00445         hook = hook->m_link.next;
00446         if (info.m_qvalue < 0.0) {
00447           info.m_qvalue = 0.0;
00448         } else if (info.m_qvalue > 1.0) {
00449           if (info.m_qvalue == FLT_MAX)
00450             force_alt = 1;
00451           info.m_qvalue = 1.0;
00452         }
00453         qvalue *= info.m_qvalue;
00454       }
00455       Q *= qvalue;
00456 
00457       
00458       
00459       info.m_client_req.clear();
00460       info.m_cached_req.clear();
00461       info.m_cached_resp.clear();
00462     }
00463   }
00464 
00465   if (Q >= 0.0 && !force_alt ) {                 
00466     
00467     Variability_t variability = CalcVariability(http_config_param, client_request,
00468                                                 obj_client_request, obj_origin_server_response);
00469 
00470     if (variability != VARIABILITY_NONE) {
00471       Q = -1.0;
00472     }
00473 
00474     Debug("http_match", "    CalcQualityOfMatch: CalcVariability says variability = %d",
00475           (variability != VARIABILITY_NONE));
00476     Debug("http_seq", "    CalcQualityOfMatch: CalcVariability says variability = %d",
00477           (variability != VARIABILITY_NONE));
00478     Debug("http_match", "    CalcQualityOfMatch: Returning final Q = %g", Q);
00479     Debug("http_seq", "    CalcQualityOfMatch: Returning final Q = %g", Q);
00480   }
00481 
00482   return Q;
00483 }
00484 
00485 
00486 
00487 
00488 
00489 
00490 
00491 
00492 
00493 
00494 
00495 
00496 
00497 
00498 
00499 
00500 
00501 
00502 
00503 
00504 static inline bool
00505 do_content_types_match(char *type1, char *subtype1, char *type2, char *subtype2)
00506 {
00507   return ((is_asterisk(type1) ||
00508            is_empty(type1) ||
00509            (strcasecmp(type1, type2) == 0)) &&
00510           (is_asterisk(subtype1) || is_empty(subtype1) || (strcasecmp(subtype1, subtype2) == 0)));
00511 }
00512 
00513 float
00514 HttpTransactCache::calculate_quality_of_accept_match(MIMEField * accept_field, MIMEField * content_field)
00515 {
00516   float q = -1.0;
00517   const char *c_raw, *a_raw;
00518   int c_raw_len, a_raw_len;
00519   char c_type[32], c_subtype[32];
00520   Str *a_value;
00521   StrList c_param_list, a_values_list;
00522   bool wildcard_type_present = false;
00523   bool wildcard_subtype_present = false;
00524   float wildcard_type_q = 1.0;
00525   float wildcard_subtype_q = 1.0;
00526 
00527   ink_assert((accept_field != NULL) && (content_field != NULL));
00528 
00529   
00530   
00531   
00532   
00533   
00534   
00535 
00536   c_raw = content_field->value_get(&c_raw_len);
00537   HttpCompat::parse_semicolon_list(&c_param_list, c_raw, c_raw_len);
00538   Str *c_param = c_param_list.head;
00539 
00540   if (!c_param) {
00541     return (1.0);
00542   }
00543   
00544   HttpCompat::parse_mime_type(c_param->str, c_type, c_subtype, sizeof(c_type), sizeof(c_subtype));
00545 
00546   
00547   
00548   accept_field->value_get_comma_list(&a_values_list);
00549 
00550   for (a_value = a_values_list.head; a_value; a_value = a_value->next) {
00551     
00552     a_raw = a_value->str;
00553     a_raw_len = a_value->len;
00554 
00555     
00556     StrList a_param_list;
00557     HttpCompat::parse_semicolon_list(&a_param_list, a_raw, a_raw_len);
00558 
00559     
00560     Str *a_param = a_param_list.head;
00561     if (!a_param)
00562       continue;
00563 
00564     
00565     char a_type[32], a_subtype[32];
00566     HttpCompat::parse_mime_type(a_param->str, a_type, a_subtype, sizeof(a_type), sizeof(a_subtype));
00567 
00568 
00569 
00570 
00571     
00572     if (is_asterisk(a_type)) {
00573       wildcard_type_present = true;
00574       wildcard_type_q = HttpCompat::find_Q_param_in_strlist(&a_param_list);
00575     } else if (is_asterisk(a_subtype) && (strcasecmp(a_type, c_type) == 0)) {
00576       wildcard_subtype_present = true;
00577       wildcard_subtype_q = HttpCompat::find_Q_param_in_strlist(&a_param_list);
00578     } else {
00579 
00580       
00581       if (do_content_types_match(a_type, a_subtype, c_type, c_subtype)) {
00582 
00583         float tq;
00584         tq = HttpCompat::find_Q_param_in_strlist(&a_param_list);
00585         q = (tq > q ? tq : q);
00586       }
00587     }
00588   }
00589 
00590   
00591   
00592   
00593   
00594 
00595   
00596   if ((q == -1.0) && (wildcard_subtype_present == true)) {
00597     q = wildcard_subtype_q;
00598   }
00599   
00600   if ((q == -1.0) && (wildcard_type_present == true)) {
00601     q = wildcard_type_q;
00602   }
00603   return (q);
00604 }
00605 
00606 
00607 
00608 
00609 
00610 
00611 
00612 
00613 
00614 
00615 
00616 
00617 
00618 
00619 
00620 
00621 
00622 
00623 static inline bool
00624 does_charset_match(char *charset1, char *charset2)
00625 {
00626   return (is_asterisk(charset1) || is_empty(charset1) || (strcasecmp(charset1, charset2) == 0));
00627 }
00628 
00629 
00630 float
00631 HttpTransactCache::calculate_quality_of_accept_charset_match(MIMEField * accept_field,
00632                                                              MIMEField * content_field, MIMEField * cached_accept_field)
00633 {
00634   float q = -1.0;
00635   const char *c_raw, *a_raw, *ca_raw;
00636   int c_raw_len, a_raw_len, ca_raw_len;
00637   StrList a_values_list;
00638   Str *a_value;
00639   char c_charset[128];
00640   char *a_charset;
00641   int a_charset_len;
00642   const char *default_charset = "utf-8";
00643   bool wildcard_present = false;
00644   float wildcard_q = 1.0;
00645 
00646   
00647   if (accept_field && cached_accept_field) {
00648     a_raw = accept_field->value_get(&a_raw_len);
00649     ca_raw = cached_accept_field->value_get(&ca_raw_len);
00650     if (a_raw && ca_raw && a_raw_len == ca_raw_len && !strncmp(a_raw, ca_raw, a_raw_len)) {
00651       Debug("http_alternate", "Exact match for ACCEPT CHARSET");
00652       return (float) 1.001;     
00653     }
00654   }
00655   
00656   
00657   if (accept_field == NULL || content_field == NULL) {
00658     return (float) 1.0;
00659   }
00660   
00661   c_raw = content_field->value_get(&c_raw_len);
00662   if (!HttpCompat::lookup_param_in_semicolon_string(c_raw, c_raw_len, "charset", c_charset, sizeof(c_charset) - 1)) {
00663     ink_strlcpy(c_charset, default_charset, sizeof(c_charset));
00664   }
00665   
00666   
00667   accept_field->value_get_comma_list(&a_values_list);
00668 
00669   for (a_value = a_values_list.head; a_value; a_value = a_value->next) {
00670     
00671     a_raw = a_value->str;
00672     a_raw_len = a_value->len;
00673 
00674     
00675     StrList a_param_list(true); 
00676     HttpCompat::parse_semicolon_list(&a_param_list, a_raw, a_raw_len);
00677 
00678     if (a_param_list.head) {
00679       a_charset = (char *) a_param_list.head->str;
00680       a_charset_len = a_param_list.head->len;
00681     } else
00682       continue;
00683 
00684 
00685 
00686 
00687     
00688     if ((a_charset_len == 1) && (a_charset[0] == '*')) {
00689       wildcard_present = true;
00690       wildcard_q = HttpCompat::find_Q_param_in_strlist(&a_param_list);
00691     } else {
00692       
00693       if (does_charset_match(a_charset, c_charset)) {
00694         float tq;
00695         tq = HttpCompat::find_Q_param_in_strlist(&a_param_list);
00696         q = (tq > q ? tq : q);
00697       }
00698     }
00699   }
00700 
00701   
00702   if ((q == -1.0) && (wildcard_present == true)) {
00703     q = wildcard_q;
00704   }
00705   
00706   if ((q == -1) && (strcasecmp(c_charset, default_charset) == 0)) {
00707     q = 1.0;
00708   }
00709   return (q);
00710 }
00711 
00712 
00713 
00714 
00715 
00716 
00717 
00718 
00719 
00720 
00721 
00722 
00723 
00724 
00725 
00726 
00727 
00728 
00729 
00730 
00731 
00732 
00733 
00734 
00735 
00736 
00737 
00738 
00739 
00740 
00741 
00742 
00743 
00744 
00745 static inline bool
00746 does_encoding_match(char *enc1, const char *enc2)
00747 {
00748   if (is_asterisk(enc1) || ((strcasecmp(enc1, enc2)) == 0))
00749     return true;
00750 
00751   
00752   
00753   if ((!strcasecmp(enc1, "gzip") && !strcasecmp(enc2, "x-gzip")) ||
00754       (!strcasecmp(enc1, "x-gzip") && !strcasecmp(enc2, "gzip")) ||
00755       (!strcasecmp(enc1, "compress") && !strcasecmp(enc2, "x-compress")) ||
00756       (!strcasecmp(enc1, "x-compress") && !strcasecmp(enc2, "compress"))
00757     ) {
00758     return true;
00759   }
00760 
00761   return false;
00762 }
00763 
00764 ContentEncoding
00765 HttpTransactCache::match_gzip(MIMEField * accept_field)
00766 {
00767   Str *a_value;
00768   const char *a_raw;
00769   StrList a_values_list;
00770   if (!accept_field) {
00771     return NO_GZIP;
00772   }
00773   
00774   accept_field->value_get_comma_list(&a_values_list);
00775 
00776   for (a_value = a_values_list.head; a_value; a_value = a_value->next) {
00777     char *a_encoding = NULL;
00778     StrList a_param_list;
00779     a_raw = a_value->str;
00780     HttpCompat::parse_semicolon_list(&a_param_list, a_raw);
00781     if (a_param_list.head)
00782       a_encoding = (char *) a_param_list.head->str;
00783     else
00784       continue;
00785     float q;
00786     q = HttpCompat::find_Q_param_in_strlist(&a_param_list);
00787     if (q != 0 && does_encoding_match(a_encoding, "gzip")) {
00788       return GZIP;
00789     }
00790   }
00791   return NO_GZIP;
00792 }
00793 
00794 
00795 static inline bool
00796 match_accept_content_encoding(const char *c_raw,
00797                               MIMEField * accept_field, bool * wildcard_present, float *wildcard_q, float *q)
00798 {
00799   Str *a_value;
00800   const char *a_raw;
00801   StrList a_values_list;
00802 
00803   if (!accept_field) {
00804     return false;
00805   }
00806   
00807   
00808   accept_field->value_get_comma_list(&a_values_list);
00809 
00810   for (a_value = a_values_list.head; a_value; a_value = a_value->next) {
00811     char *a_encoding = NULL;
00812     StrList a_param_list;
00813 
00814     
00815     a_raw = a_value->str;
00816 
00817     
00818     HttpCompat::parse_semicolon_list(&a_param_list, a_raw);
00819     if (a_param_list.head)
00820       a_encoding = (char *) a_param_list.head->str;
00821     else
00822       continue;
00823 
00824     if (is_asterisk(a_encoding)) {
00825       *wildcard_present = true;
00826       *wildcard_q = HttpCompat::find_Q_param_in_strlist(&a_param_list);
00827       return true;
00828     } else if (does_encoding_match(a_encoding, c_raw)) {
00829       
00830       float tq;
00831       tq = HttpCompat::find_Q_param_in_strlist(&a_param_list);
00832       *q = (tq > *q ? tq : *q);
00833 
00834       return true;
00835     } else {
00836       
00837     }
00838   }
00839   return false;
00840 }
00841 
00842 float
00843 HttpTransactCache::calculate_quality_of_accept_encoding_match(MIMEField * accept_field,
00844                                                               MIMEField * content_field,
00845                                                               MIMEField * cached_accept_field)
00846 {
00847 
00848   float q = -1.0;
00849   bool is_identity_encoding = false;
00850   const char *c_encoding;
00851   int c_encoding_len;
00852   bool wildcard_present = false;
00853   float wildcard_q = 1.0;
00854   StrList c_values_list;
00855   Str *c_value;
00856   const char *a_raw, *ca_raw;
00857   int a_raw_len, ca_raw_len;
00858 
00859 
00860   
00861   if (accept_field && cached_accept_field) {
00862     a_raw = accept_field->value_get(&a_raw_len);
00863     ca_raw = cached_accept_field->value_get(&ca_raw_len);
00864     if (a_raw && ca_raw && a_raw_len == ca_raw_len && !strncmp(a_raw, ca_raw, a_raw_len)) {
00865       Debug("http_alternate", "Exact match for ACCEPT ENCODING");
00866       return (float) 1.001;     
00867     }
00868   }
00869   
00870   
00871   if (accept_field == NULL && content_field == NULL) {
00872     return (float) 1.0;
00873   }
00874   
00875   if (!content_field) {
00876     Debug("http_match", "[calculate_quality_accept_encoding_match]: " "response hdr does not have content-encoding.");
00877     is_identity_encoding = true;
00878   } else {
00879     
00880     content_field->value_get_comma_list(&c_values_list);
00881 
00882     content_field->value_get(&c_encoding_len);
00883     if (c_encoding_len == 0) {
00884       is_identity_encoding = true;
00885     } else {
00886       
00887       for (c_value = c_values_list.head; c_value; c_value = c_value->next) {
00888         c_encoding = c_value->str;
00889         c_encoding_len = c_value->len;
00890         if ((c_encoding_len >= 8) && (strncasecmp(c_encoding, "identity", 8) == 0)) {
00891           is_identity_encoding = true;
00892           break;
00893         }
00894       }
00895     }
00896   }
00897 
00898 
00899   
00900   
00901   
00902   
00903   
00904   
00905   
00906   
00907 
00908   if (!accept_field) {
00909     if (is_identity_encoding) {
00910       if (!cached_accept_field) {
00911         return ((float) 1.0);
00912       } else {
00913         return ((float) 0.001);
00914       }
00915     } else {
00916       return ((float) -1.0);
00917     }
00918   }
00919 
00920   
00921   
00922   
00923   if (!content_field) {
00924     if (!match_accept_content_encoding("identity",
00925                                        accept_field, &wildcard_present, &wildcard_q, &q)) {
00926 
00927       
00928       if (match_gzip(accept_field) == GZIP && match_gzip(cached_accept_field) == GZIP) {
00929         return (float) 1.0;
00930       }
00931       goto encoding_wildcard;
00932     }
00933     
00934 
00935   } else {
00936     
00937     
00938     
00939     
00940     
00941     
00942     
00943     
00944     
00945     float combined_q = 1.0;
00946     for (c_value = c_values_list.head; c_value; c_value = c_value->next) {
00947       float this_q = -1.0;
00948       if (!match_accept_content_encoding(c_value->str,
00949                                          accept_field, &wildcard_present, &wildcard_q, &this_q)) {
00950         goto encoding_wildcard;
00951       }
00952       combined_q *= this_q;
00953     }
00954     q = combined_q;
00955   }
00956 
00957 encoding_wildcard:
00958   
00959   if ((q == -1.0) && (wildcard_present == true)) {
00960     q = wildcard_q;
00961   }
00962 
00963   
00964   
00965   
00966 
00967   if ((q == -1.0) && is_identity_encoding) {
00968     if (match_gzip(accept_field) == GZIP) {
00969       if (match_gzip(cached_accept_field) == GZIP) {
00970         return (float) 1.0;
00971       } else {
00972         
00973         return (float) -1.0;
00974       }
00975     } else if (cached_accept_field && match_gzip(cached_accept_field) != GZIP) {
00976       return (float) 0.001;
00977     } else {
00978       return (float) -1.0;
00979     }
00980   }
00981 
00982   return (q);
00983 }
00984 
00985 
00986 
00987 
00988 
00989 
00990 
00991 
00992 
00993 
00994 
00995 
00996 
00997 
00998 
00999 static inline bool
01000 does_language_range_match(const char *range1, const char *range2)
01001 {
01002   while (*range1 && *range2 && (ParseRules::ink_tolower(*range1) == ParseRules::ink_tolower(*range2))) {
01003     range1 += 1;
01004     range2 += 1;
01005   }
01006 
01007   
01008   if ((((*range1 == NUL) && (*range2 == NUL)) || ((*range1 == NUL) && (*range2 == '-')))) {
01009     return true;
01010   }
01011 
01012   return false;
01013 }
01014 
01015 static inline bool
01016 match_accept_content_language(const char *c_raw,
01017                               MIMEField * accept_field,
01018                               bool * wildcard_present,
01019                               float *wildcard_q, float *q, int *a_range_length)
01020 {
01021   const char *a_raw;
01022   int a_raw_len;
01023   StrList a_values_list;
01024   Str *a_value;
01025 
01026   ink_assert(accept_field != NULL);
01027 
01028   
01029   
01030   accept_field->value_get_comma_list(&a_values_list);
01031 
01032   for (a_value = a_values_list.head; a_value; a_value = a_value->next) {
01033     a_raw = a_value->str;
01034     a_raw_len = a_value->len;
01035 
01036     char *a_range;
01037     StrList a_param_list;
01038 
01039     HttpCompat::parse_semicolon_list(&a_param_list, a_raw, a_raw_len);
01040     float tq = HttpCompat::find_Q_param_in_strlist(&a_param_list);
01041 
01042 
01043     
01044     
01045     
01046     
01047     
01048 
01049     if (a_param_list.head) {
01050       a_range = (char *) a_param_list.head->str;
01051       *a_range_length = a_param_list.head->len;
01052     } else {
01053       continue;
01054     }
01055 
01056     if (is_asterisk(a_range)) {
01057       *wildcard_present = true;
01058       *wildcard_q = HttpCompat::find_Q_param_in_strlist(&a_param_list);
01059       return true;
01060     } else if (does_language_range_match(a_range, c_raw)) {
01061       *q = tq;
01062 
01063 
01064 
01065 
01066 
01067       return true;
01068     } else {
01069     }
01070   }
01071 
01072   return false;
01073 }
01074 
01075 
01076 
01077 
01078 
01079 
01080 
01081 float
01082 HttpTransactCache::calculate_quality_of_accept_language_match(MIMEField * accept_field,
01083                                                               MIMEField * content_field,
01084                                                               MIMEField * cached_accept_field)
01085 {
01086   float q = -1.0;
01087   int a_range_length;
01088   bool wildcard_present = false;
01089   float wildcard_q = 1.0;
01090   float min_q = 1.0;
01091   bool match_found = false;
01092   StrList c_values_list;
01093   Str *c_value;
01094   const char *c_raw, *a_raw, *ca_raw;
01095   int a_raw_len, ca_raw_len;
01096 
01097   
01098   if (accept_field && cached_accept_field) {
01099     a_raw = accept_field->value_get(&a_raw_len);
01100     ca_raw = cached_accept_field->value_get(&ca_raw_len);
01101     if (a_raw && ca_raw && a_raw_len == ca_raw_len && !strncmp(a_raw, ca_raw, a_raw_len)) {
01102       Debug("http_alternate", "Exact match for ACCEPT LANGUAGE");
01103       return (float) 1.001;     
01104     }
01105   }
01106 
01107   if (!accept_field) {
01108     return (1.0);
01109   }
01110   
01111   
01112   
01113 
01114   if (!content_field) {
01115     if (match_accept_content_language("identity",
01116                                       accept_field,
01117                                       &wildcard_present, &wildcard_q, &q, &a_range_length)) {
01118       goto language_wildcard;
01119     }
01120     Debug("http_match", "[calculate_quality_accept_language_match]: " "response hdr does not have content-language.");
01121     return (1.0);
01122   }
01123 
01124   
01125   
01126   content_field->value_get_comma_list(&c_values_list);
01127   for (c_value = c_values_list.head; c_value; c_value = c_value->next) {
01128     c_raw = c_value->str;
01129 
01130     
01131     if (match_accept_content_language(c_raw,
01132                                       accept_field,
01133                                       &wildcard_present, &wildcard_q, &q, &a_range_length)) {
01134       min_q = (min_q < q ? min_q : q);
01135       match_found = true;
01136     }
01137   }
01138   if (match_found) {
01139     q = min_q;
01140   } else {
01141     q = -1.0;
01142   }
01143 
01144 language_wildcard:
01145   
01146   if ((q == -1.0) && (wildcard_present == true)) {
01147     q = wildcard_q;
01148   }
01149   return (q);
01150 }
01151 
01152 
01153 
01154 
01155 
01156 
01157 
01158 
01159 
01160 
01161 Variability_t
01162 HttpTransactCache::CalcVariability(CacheLookupHttpConfig * http_config_params, HTTPHdr * client_request,
01163                                    HTTPHdr * obj_client_request, HTTPHdr * obj_origin_server_response)
01164 {
01165   ink_assert(http_config_params != NULL);
01166   ink_assert(client_request != NULL);
01167   ink_assert(obj_client_request != NULL);
01168   ink_assert(obj_origin_server_response != NULL);
01169 
01170   Variability_t variability = VARIABILITY_NONE;
01171   if (http_config_params->cache_enable_default_vary_headers ||
01172       obj_origin_server_response->presence(MIME_PRESENCE_VARY)) {
01173 
01174     
01175     
01176     
01177     
01178 
01179     StrList vary_list;
01180     int num_vary_values = obj_origin_server_response->value_get_comma_list(MIME_FIELD_VARY, MIME_LEN_VARY, &vary_list);
01181 
01182     if (num_vary_values <= 0) {   
01183       const char *vary_values = NULL;
01184       const char *content_type;
01185       int content_type_len;
01186       char type[32], subtype[32];
01187 
01188       content_type = obj_origin_server_response->value_get(MIME_FIELD_CONTENT_TYPE,
01189                                                            MIME_LEN_CONTENT_TYPE, &content_type_len);
01190 
01191       if (content_type) {
01192         HttpCompat::parse_mime_type_with_len(content_type, content_type_len, type, subtype, sizeof(type),
01193                                              sizeof(subtype));
01194       } else {
01195         type[0] = '\0';
01196         subtype[0] = '\0';
01197       }
01198 
01199       Debug("http_match", "      type = '%s', subtype = '%s'", type, subtype);
01200 
01201       if (http_config_params->cache_enable_default_vary_headers) {
01202         if (strcasecmp(type, "text") == 0) {
01203           Debug("http_match", "      Using default text vary headers");
01204           vary_values = http_config_params->cache_vary_default_text;
01205         } else if (strcasecmp(type, "image") == 0) {
01206           Debug("http_match", "      Using default image vary headers");
01207           vary_values = http_config_params->cache_vary_default_images;
01208         } else {
01209           Debug("http_match", "      Using default other vary headers");
01210           vary_values = http_config_params->cache_vary_default_other;
01211         }
01212       }
01213       
01214       HttpCompat::parse_comma_list(&vary_list, (vary_values ? vary_values : ""));
01215     }
01216 
01217     if (is_debug_tag_set("http_match") && (vary_list.head)) {
01218       Debug("http_match", "Vary list of %d elements", vary_list.count);
01219       vary_list.dump(stderr);
01220     }
01221 
01222     
01223     for (Str *field = vary_list.head; field != NULL; field = field->next) {
01224       if (field->len == 0)
01225         continue;
01226 
01227 
01228       
01229       
01230       
01231 
01232 
01233       Debug("http_match", "Vary: %s", field->str);
01234       if (((field->str[0] == '*') && (field->str[1] == NUL))) {
01235         Debug("http_match", "Wildcard variability --- object not served from cache\n");
01236         variability = VARIABILITY_ALL;
01237         break;
01238       }
01239 
01240       
01241       
01242       
01243 
01244       if (http_config_params->cache_global_user_agent_header &&
01245           !strcasecmp((char *) field->str, "User-Agent"))
01246         continue;
01247 
01248       
01249       
01250       if (http_config_params->ignore_accept_encoding_mismatch && 
01251           !strcasecmp((char *) field->str, "Accept-Encoding"))
01252         continue;
01253 
01254 
01255       
01256       
01257       
01258       
01259       
01260       
01261       
01262       
01263       
01264       
01265       
01266       
01267 
01268 
01269       ink_assert(strlen(field->str) == field->len);
01270 
01271       char *field_name_str = (char *) hdrtoken_string_to_wks(field->str, field->len);
01272       if (field_name_str == NULL)
01273         field_name_str = (char *) field->str;
01274 
01275       MIMEField *cached_hdr_field = obj_client_request->field_find(field_name_str, field->len);
01276       MIMEField *current_hdr_field = client_request->field_find(field_name_str, field->len);
01277 
01278       
01279       if (!HttpCompat::do_header_values_rfc2068_14_43_match(cached_hdr_field, current_hdr_field)) {
01280         variability = VARIABILITY_SOME;
01281         break;
01282       }
01283     }
01284   }
01285 
01286   return variability;
01287 }
01288 
01289 
01290 
01291 
01292 
01293 
01294 
01295 
01296 
01297 
01298 
01299 
01300 
01301 
01302 
01303 
01304 
01305 
01306 
01307 
01308 
01309 HTTPStatus
01310 HttpTransactCache::match_response_to_request_conditionals(HTTPHdr * request, HTTPHdr * response)
01311 {
01312   HTTPStatus response_code = HTTP_STATUS_NONE;
01313 
01314   ink_assert(response->status_get() != HTTP_STATUS_NOT_MODIFIED);
01315   ink_assert(response->status_get() != HTTP_STATUS_PRECONDITION_FAILED);
01316   ink_assert(response->status_get() != HTTP_STATUS_RANGE_NOT_SATISFIABLE);
01317 
01318 
01319   if (!(request->presence(MIME_PRESENCE_IF_MODIFIED_SINCE |
01320                           MIME_PRESENCE_IF_NONE_MATCH |
01321                           MIME_PRESENCE_IF_UNMODIFIED_SINCE | MIME_PRESENCE_IF_MATCH | MIME_PRESENCE_RANGE))) {
01322     return response->status_get();
01323   }
01324   
01325 
01326   
01327   if (request->presence(MIME_PRESENCE_IF_MODIFIED_SINCE)) {
01328     
01329     ink_time_t lm_value = response->get_last_modified();
01330 
01331     
01332     if ((lm_value == 0) || (request->get_if_modified_since() < lm_value)) {
01333       return response->status_get();
01334     } else {
01335       
01336       response_code = HTTP_STATUS_NOT_MODIFIED;
01337 
01338       if (!request->presence(MIME_PRESENCE_IF_NONE_MATCH)) {
01339         return response_code;
01340       }
01341     }
01342   }
01343 
01344   
01345   if (request->presence(MIME_PRESENCE_IF_NONE_MATCH)) {
01346     int raw_etags_len, comma_sep_tag_list_len;
01347     const char *raw_etags = response->value_get(MIME_FIELD_ETAG, MIME_LEN_ETAG, &raw_etags_len);
01348     const char *comma_sep_tag_list = NULL;
01349 
01350     if (raw_etags) {
01351       comma_sep_tag_list = request->value_get(MIME_FIELD_IF_NONE_MATCH,
01352                                               MIME_LEN_IF_NONE_MATCH, &comma_sep_tag_list_len);
01353     } else {
01354       
01355       
01356       goto L1;
01357     }
01358 
01359     if (!comma_sep_tag_list) {
01360       comma_sep_tag_list = "";
01361       comma_sep_tag_list_len = 0;
01362     }
01363 
01364     if (!raw_etags) {
01365       raw_etags = "";
01366       raw_etags_len = 0;
01367     }
01368 
01369     
01370     
01371     
01372 
01373     if (do_strings_match_weakly(raw_etags, raw_etags_len, comma_sep_tag_list, comma_sep_tag_list_len)) {
01374       
01375       return HTTP_STATUS_NOT_MODIFIED;
01376     } else {
01377       return response->status_get();
01378     }
01379   }
01380 
01381 L1:
01382   
01383   
01384   if (response_code != HTTP_STATUS_NONE) {
01385     return response_code;
01386   }
01387 
01388   
01389   
01390   
01391 
01392   
01393   if (request->presence(MIME_PRESENCE_IF_UNMODIFIED_SINCE)) {
01394     
01395     ink_time_t lm_value = response->get_last_modified();
01396 
01397     
01398     if ((request->get_if_unmodified_since() < lm_value) || (lm_value == 0)) {
01399       return HTTP_STATUS_PRECONDITION_FAILED;
01400     } else {
01401       
01402       response_code = response->status_get();
01403     }
01404   }
01405 
01406   
01407   if (request->presence(MIME_PRESENCE_IF_MATCH)) {
01408     int raw_etags_len, comma_sep_tag_list_len;
01409     const char *raw_etags = response->value_get(MIME_FIELD_ETAG, MIME_LEN_ETAG, &raw_etags_len);
01410     const char *comma_sep_tag_list = NULL;
01411 
01412     if (raw_etags) {
01413       comma_sep_tag_list = request->value_get(MIME_FIELD_IF_MATCH, MIME_LEN_IF_MATCH, &comma_sep_tag_list_len);
01414     }
01415 
01416     if (!comma_sep_tag_list) {
01417       comma_sep_tag_list = "";
01418       comma_sep_tag_list_len = 0;
01419     }
01420 
01421     if (!raw_etags) {
01422       raw_etags = "";
01423       raw_etags_len = 0;
01424     }
01425 
01426     if (do_strings_match_strongly(raw_etags, raw_etags_len, comma_sep_tag_list, comma_sep_tag_list_len)) {
01427       
01428       
01429       return response->status_get();
01430     } else {
01431       return HTTP_STATUS_PRECONDITION_FAILED;
01432     }
01433   }
01434   
01435   
01436   if (response_code != HTTP_STATUS_NONE) {
01437     return response_code;
01438   }
01439 
01440   
01441   
01442   
01443   if (request->presence(MIME_PRESENCE_RANGE) && request->presence(MIME_PRESENCE_IF_RANGE)) {
01444     int raw_len, comma_sep_list_len;
01445 
01446     const char *if_value = request->value_get(MIME_FIELD_IF_RANGE,
01447                                               MIME_LEN_IF_RANGE,
01448                                               &comma_sep_list_len);
01449 
01450     
01451     if (!if_value || if_value[0] == '"' || (comma_sep_list_len > 1 && if_value[1] == '/')) {
01452       if (!if_value) {
01453         if_value = "";
01454         comma_sep_list_len = 0;
01455       }
01456 
01457       const char *raw_etags = response->value_get(MIME_FIELD_ETAG, MIME_LEN_ETAG, &raw_len);
01458 
01459       if (!raw_etags) {
01460         raw_etags = "";
01461         raw_len = 0;
01462       }
01463 
01464       if (do_strings_match_strongly(raw_etags, raw_len, if_value, comma_sep_list_len)) {
01465         return response->status_get();
01466       } else {
01467         return HTTP_STATUS_RANGE_NOT_SATISFIABLE;
01468       }
01469     }
01470     
01471     else {
01472       
01473       ink_time_t lm_value = response->get_last_modified();
01474 
01475       
01476       if ((request->get_if_range_date() < lm_value) || (lm_value == 0)) {
01477         return HTTP_STATUS_RANGE_NOT_SATISFIABLE;
01478       } else {
01479         return response->status_get();
01480       }
01481     }
01482   }
01483 
01484   return response->status_get();
01485 }
01486 
01487 
01488 
01489 
01490 
01491 int
01492 CacheLookupHttpConfig::marshal_length()
01493 {
01494   int len = (int) sizeof(int32_t);
01495   len += (cache_vary_default_text ? strlen(cache_vary_default_text) + 1 : 1);
01496   len += (cache_vary_default_images ? strlen(cache_vary_default_images) + 1 : 1);
01497   len += (cache_vary_default_other ? strlen(cache_vary_default_other) + 1 : 1);
01498   return len;
01499 }
01500 
01501 int
01502 CacheLookupHttpConfig::marshal(char *buf, int length)
01503 {
01504   int32_t i32_tmp;
01505   char *p = buf;
01506   int len;
01507 
01508   if ((length -= sizeof(int32_t)) < 0)
01509     return -1;
01510 
01511   i32_tmp = (int32_t) cache_enable_default_vary_headers;
01512   memcpy(p, &i32_tmp, sizeof(int32_t));
01513   p += sizeof(int32_t);
01514 
01515   len = (cache_vary_default_text ? strlen(cache_vary_default_text) + 1 : 1);
01516   if ((length -= len) < 0)
01517     return -1;
01518   ink_strlcpy(p, (cache_vary_default_text ? cache_vary_default_text : ""), length);
01519   p += len;
01520 
01521   len = (cache_vary_default_images ? strlen(cache_vary_default_images) + 1 : 1);
01522   if ((length -= len) < 0)
01523     return -1;
01524   ink_strlcpy(p, (cache_vary_default_images ? cache_vary_default_images : ""), length);
01525   p += len;
01526 
01527   len = (cache_vary_default_other ? strlen(cache_vary_default_other) + 1 : 1);
01528   if ((length -= len) < 0)
01529     return -1;
01530   ink_strlcpy(p, (cache_vary_default_other ? cache_vary_default_other : ""), length);
01531   p += len;
01532 
01533   return (p - buf);
01534 }
01535 
01536 int
01537 CacheLookupHttpConfig::unmarshal(Arena * arena, const char *buf, int buflen)
01538 {
01539   const char *p = buf;
01540   int length = buflen;
01541   int len;
01542   int32_t i32_tmp;
01543 
01544   if ((length -= sizeof(int32_t)) < 0)
01545     return -1;
01546 
01547   memcpy(&i32_tmp, p, sizeof(int32_t));
01548   cache_enable_default_vary_headers = (bool) i32_tmp;
01549   p += sizeof(int32_t);
01550 
01551   len = strlen(p) + 1;
01552   if ((length -= len) < 0)
01553     return -1;
01554   cache_vary_default_text = arena->str_store(((len == 2) ? "" : p), len - 1);
01555   p += len;
01556 
01557   len = strlen(p) + 1;
01558   if ((length -= len) < 0)
01559     return -1;
01560   cache_vary_default_images = arena->str_store(((len == 2) ? "" : p), len - 1);
01561   p += len;
01562 
01563   len = strlen(p) + 1;
01564   if ((length -= len) < 0)
01565     return -1;
01566   cache_vary_default_other = arena->str_store(((len == 2) ? "" : p), len - 1);
01567   p += len;
01568 
01569   return (p - buf);
01570 }