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

HttpTransactHeaders.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 #include "libts.h"
00024 
00025 #include "HttpTransact.h"
00026 #include "HttpTransactHeaders.h"
00027 #include "HTTP.h"
00028 #include "HdrUtils.h"
00029 #include "HttpCompat.h"
00030 
00031 #include "I_Machine.h"
00032 
00033 bool
00034 HttpTransactHeaders::is_method_cacheable(const HttpConfigParams *http_config_param, const int method)
00035 {
00036   return (method == HTTP_WKSIDX_GET || method == HTTP_WKSIDX_HEAD ||
00037           (http_config_param->cache_post_method == 1 && method == HTTP_WKSIDX_POST));
00038 }
00039 
00040 
00041 bool
00042 HttpTransactHeaders::is_method_cache_lookupable(int method)
00043 {
00044   // responses to GET, HEAD, and POST are cacheable
00045   // URL's requested in DELETE and PUT are looked up to remove cached copies
00046   return (method == HTTP_WKSIDX_GET ||
00047           method == HTTP_WKSIDX_HEAD ||
00048           method == HTTP_WKSIDX_POST ||
00049           method == HTTP_WKSIDX_DELETE ||
00050           method == HTTP_WKSIDX_PUT || method == HTTP_WKSIDX_PURGE || method == HTTP_WKSIDX_PUSH);
00051 }
00052 
00053 
00054 bool
00055 HttpTransactHeaders::is_this_a_hop_by_hop_header(const char *field_name)
00056 {
00057   if (!hdrtoken_is_wks(field_name))
00058     return (false);
00059   if ((hdrtoken_wks_to_flags(field_name) & MIME_FLAGS_HOPBYHOP) && (field_name != MIME_FIELD_KEEP_ALIVE)) {
00060     return (true);
00061   } else {
00062     return (false);
00063   }
00064 }
00065 
00066 
00067 bool
00068 HttpTransactHeaders::is_this_method_supported(int the_scheme, int the_method)
00069 {
00070   if (the_method == HTTP_WKSIDX_CONNECT) {
00071     return true;
00072   } else if (the_scheme == URL_WKSIDX_HTTP || the_scheme == URL_WKSIDX_HTTPS) {
00073     return is_this_http_method_supported(the_method);
00074   } else if ((the_scheme == URL_WKSIDX_WS || the_scheme == URL_WKSIDX_WSS) &&
00075             the_method == HTTP_WKSIDX_GET) {
00076     return true;
00077   } else
00078     return false;
00079 }
00080 
00081 
00082 void
00083 HttpTransactHeaders::insert_supported_methods_in_response(HTTPHdr *response, int scheme)
00084 {
00085   int method_output_lengths[32];
00086   const char *methods[] = {
00087     HTTP_METHOD_CONNECT,
00088     HTTP_METHOD_DELETE,
00089     HTTP_METHOD_GET,
00090     HTTP_METHOD_HEAD,
00091     HTTP_METHOD_ICP_QUERY,
00092     HTTP_METHOD_OPTIONS,
00093     HTTP_METHOD_POST,
00094     HTTP_METHOD_PURGE,
00095     HTTP_METHOD_PUT,
00096     HTTP_METHOD_PUSH,
00097     HTTP_METHOD_TRACE,
00098   };
00099   char inline_buffer[64];
00100   char *alloced_buffer, *value_buffer;
00101 
00102   int nmethods = sizeof(methods) / sizeof(methods[0]);
00103   ink_assert(nmethods <= 32);
00104 
00105   char *p;
00106   int i, is_supported;
00107   size_t bytes = 0;
00108   int num_methods_supported = 0;
00109   MIMEField *field;
00110 
00111   // step 1: determine supported methods, count bytes & allocate
00112   for (i = 0; i < nmethods; i++) {
00113     const char *method_wks = methods[i];
00114     ink_assert(hdrtoken_is_wks(method_wks));
00115 
00116     is_supported = is_this_method_supported(scheme, hdrtoken_wks_to_index(method_wks));
00117 
00118     if (is_supported) {
00119       ++num_methods_supported;
00120       method_output_lengths[i] = hdrtoken_wks_to_length(method_wks);
00121       bytes += method_output_lengths[i];
00122       if (num_methods_supported > 1)
00123         bytes += 2;             // +2 if need leading ", "
00124     } else {
00125       method_output_lengths[i] = 0;
00126     }
00127   }
00128 
00129   // step 2: create Allow field if not present
00130   field = response->field_find(MIME_FIELD_ALLOW, MIME_LEN_ALLOW);
00131   if (!field) {
00132     field = response->field_create(MIME_FIELD_ALLOW, MIME_LEN_ALLOW);
00133     response->field_attach(field);
00134   }
00135   // step 3: get a big enough buffer
00136   if (bytes <= sizeof(inline_buffer)) {
00137     alloced_buffer = NULL;
00138     value_buffer = inline_buffer;
00139   } else {
00140     alloced_buffer = (char *)ats_malloc(bytes);
00141     value_buffer = alloced_buffer;
00142   }
00143 
00144   // step 4: build the value
00145   p = value_buffer;
00146   for (i = 0; i < nmethods; i++) {
00147     if (method_output_lengths[i]) {
00148       memcpy(p, methods[i], method_output_lengths[i]);
00149       p += method_output_lengths[i];
00150       if (num_methods_supported > 1) {
00151         *p++ = ',';
00152         *p++ = ' ';
00153       }
00154       --num_methods_supported;
00155     }
00156   }
00157 
00158   // FIXME: do we really want to append to old list, or replace old list?
00159 
00160   // step 5: attach new allow list to end of previous list
00161   field->value_append(response->m_heap, response->m_mime, value_buffer, bytes);
00162 
00163   // step 6: free up temp storage
00164   ats_free(alloced_buffer);
00165 }
00166 
00167 
00168 void
00169 HttpTransactHeaders::build_base_response(HTTPHdr *outgoing_response,
00170                                          HTTPStatus status,
00171                                          const char *reason_phrase, int reason_phrase_len, ink_time_t date)
00172 {
00173   if (!outgoing_response->valid()) {
00174     outgoing_response->create(HTTP_TYPE_RESPONSE);
00175   }
00176 
00177   ink_assert(outgoing_response->type_get() == HTTP_TYPE_RESPONSE);
00178 
00179   outgoing_response->version_set(HTTPVersion(1, 1));
00180   outgoing_response->status_set(status);
00181   outgoing_response->reason_set(reason_phrase, reason_phrase_len);
00182   outgoing_response->set_date(date);
00183 }
00184 
00185 
00186 ////////////////////////////////////////////////////////////////////////
00187 // Copy all non hop-by-hop header fields from src_hdr to new_hdr.
00188 // If header Date: is not present or invalid in src_hdr,
00189 // then the given date will be used.
00190 void
00191 HttpTransactHeaders::copy_header_fields(HTTPHdr *src_hdr,
00192                                         HTTPHdr *new_hdr, bool retain_proxy_auth_hdrs, ink_time_t date)
00193 {
00194   ink_assert(src_hdr->valid());
00195   ink_assert(!new_hdr->valid());
00196 
00197   MIMEField *field;
00198   MIMEFieldIter field_iter;
00199   bool date_hdr = false;
00200 
00201   // Start with an exact duplicate
00202   new_hdr->copy(src_hdr);
00203 
00204   // Nuke hop-by-hop headers
00205   //
00206   //    The hop-by-hop header fields are layed out by the spec
00207   //    with two adjustments
00208   //      1) we treat TE as hop-by-hop because spec implies
00209   //         that it is by declaring anyone who sends a TE must
00210   //         include TE in the connection header.  This in
00211   //         my opinion error prone and if the client doesn't follow the spec
00212   //         we'll have problems with the TE being forwarded to the server
00213   //         and us caching the transfer encoded documents and then
00214   //         serving it to a client that can not handle it
00215   //      2) Transfer enconding is copied.  If the transfer encoding
00216   //         is changed for example by dechunking, the transfer encoding
00217   //         should be modified when when the decision is made to dechunk it
00218 
00219   for (field = new_hdr->iter_get_first(&field_iter); field != NULL; field = new_hdr->iter_get_next(&field_iter)) {
00220     if (field->m_wks_idx == -1)
00221       continue;
00222 
00223     int field_flags = hdrtoken_index_to_flags(field->m_wks_idx);
00224 
00225     if (field_flags & MIME_FLAGS_HOPBYHOP) {
00226       // Delete header if not in special proxy_auth retention mode
00227       if ((!retain_proxy_auth_hdrs) || (!(field_flags & MIME_FLAGS_PROXYAUTH))) {
00228         new_hdr->field_delete(field);
00229       }
00230     } else if (field->m_wks_idx == MIME_WKSIDX_DATE) {
00231       date_hdr = true;
00232     }
00233   }
00234 
00235   // Set date hdr if not already set and valid value passed in
00236   if ((date_hdr == false) && (date > 0))
00237     new_hdr->set_date(date);
00238 }
00239 
00240 
00241 ////////////////////////////////////////////////////////////////////////
00242 // Just convert the outgoing request to the appropriate version
00243 void
00244 HttpTransactHeaders::convert_request(HTTPVersion outgoing_ver, HTTPHdr *outgoing_request)
00245 {
00246   if (outgoing_ver == HTTPVersion(1, 0)) {
00247     convert_to_1_0_request_header(outgoing_request);
00248   } else if (outgoing_ver == HTTPVersion(1, 1)) {
00249     convert_to_1_1_request_header(outgoing_request);
00250   } else if (outgoing_ver == HTTPVersion(0, 9)) {
00251     // Http 0.9 is a special case - do not bother copying over fields,
00252     // because they will all need to be removed anyway.
00253     convert_to_0_9_request_header(outgoing_request);
00254   } else {
00255     Debug("http_trans", "[HttpTransactHeaders::convert_request]" "Unsupported Version - passing through");
00256   }
00257 }
00258 
00259 ////////////////////////////////////////////////////////////////////////
00260 // Just convert the outgoing response to the appropriate version
00261 void
00262 HttpTransactHeaders::convert_response(HTTPVersion outgoing_ver, HTTPHdr *outgoing_response)
00263 {
00264   if (outgoing_ver == HTTPVersion(1, 0)) {
00265     convert_to_1_0_response_header(outgoing_response);
00266   } else if (outgoing_ver == HTTPVersion(1, 1)) {
00267     convert_to_1_1_response_header(outgoing_response);
00268   } else if (outgoing_ver == HTTPVersion(0, 9)) {
00269     // Http 0.9 is a special case - do not bother copying over fields,
00270     // because they will all need to be removed anyway.
00271     convert_to_0_9_response_header(outgoing_response);
00272   } else {
00273     Debug("http_trans", "[HttpTransactHeaders::convert_response]" "Unsupported Version - passing through");
00274   }
00275 }
00276 
00277 
00278 ////////////////////////////////////////////////////////////////////////
00279 // Take an existing outgoing request header and make it HTTP/0.9
00280 void
00281 HttpTransactHeaders::convert_to_0_9_request_header(HTTPHdr *outgoing_request)
00282 {
00283   // These are required
00284   ink_assert(outgoing_request->method_get_wksidx() == HTTP_WKSIDX_GET);
00285   ink_assert(outgoing_request->url_get()->valid());
00286 
00287   outgoing_request->version_set(HTTPVersion(0, 9));
00288 
00289   // HTTP/0.9 has no headers: nuke them all
00290   outgoing_request->fields_clear();
00291 }
00292 
00293 
00294 ////////////////////////////////////////////////////////////////////////
00295 // Take an existing outgoing request header and make it HTTP/1.0
00296 void
00297 HttpTransactHeaders::convert_to_1_0_request_header(HTTPHdr *outgoing_request)
00298 {
00299   // These are required
00300   ink_assert(outgoing_request->url_get()->valid());
00301 
00302   // Set HTTP version to 1.0
00303   outgoing_request->version_set(HTTPVersion(1, 0));
00304 
00305   // FIXME (P2): Need to change cache directives into pragma, cleanly
00306   //             Now, any Cache-Control hdr becomes Pragma: no-cache
00307 
00308   if (outgoing_request->presence(MIME_PRESENCE_CACHE_CONTROL) && !outgoing_request->is_pragma_no_cache_set()) {
00309     outgoing_request->value_append(MIME_FIELD_PRAGMA, MIME_LEN_PRAGMA, "no-cache", 8, true);
00310   }
00311   // We do not currently support chunked transfer encoding,
00312   // so specify that response should use identity transfer coding.
00313   //outgoing_request->value_insert(MIME_FIELD_TE, "identity;q=1.0");
00314   //outgoing_request->value_insert(MIME_FIELD_TE, "chunked;q=0.0");
00315 }
00316 
00317 
00318 ////////////////////////////////////////////////////////////////////////
00319 // Take an existing outgoing request header and make it HTTP/1.1
00320 void
00321 HttpTransactHeaders::convert_to_1_1_request_header(HTTPHdr *outgoing_request)
00322 {
00323 
00324   // These are required
00325   ink_assert(outgoing_request->url_get()->valid());
00326   ink_assert(outgoing_request->version_get() == HTTPVersion(1, 1));
00327 
00328   if (outgoing_request->get_cooked_pragma_no_cache() &&
00329       !(outgoing_request->get_cooked_cc_mask() & MIME_COOKED_MASK_CC_NO_CACHE)) {
00330     outgoing_request->value_append(MIME_FIELD_CACHE_CONTROL, MIME_LEN_CACHE_CONTROL, "no-cache", 8, true);
00331   }
00332   // We do not currently support chunked transfer encoding,
00333   // so specify that response should use identity transfer coding.
00334   //outgoing_request->value_insert(MIME_FIELD_TE, "identity;q=1.0");
00335   //outgoing_request->value_insert(MIME_FIELD_TE, "chunked;q=0.0");
00336 }
00337 
00338 
00339 ////////////////////////////////////////////////////////////////////////
00340 // Take an existing outgoing response header and make it HTTP/0.9
00341 void
00342 HttpTransactHeaders::convert_to_0_9_response_header(HTTPHdr * /* outgoing_response ATS_UNUSED */)
00343 {
00344   // Http 0.9 does not require a response header.
00345 
00346   // There used to be clear header here, but the state machine
00347   // does not write down the response header if the client is
00348   // 0.9. We need the header fields to make decisions about
00349   // the size of the response body, however.
00350   // There is therefore no need to clear the header.
00351 }
00352 
00353 
00354 ////////////////////////////////////////////////////////////////////////
00355 // Take an existing outgoing response header and make it HTTP/1.0
00356 void
00357 HttpTransactHeaders::convert_to_1_0_response_header(HTTPHdr *outgoing_response)
00358 {
00359 //     // These are required
00360 //     ink_assert(outgoing_response->status_get());
00361 //     ink_assert(outgoing_response->reason_get());
00362 
00363   // Set HTTP version to 1.0
00364   outgoing_response->version_set(HTTPVersion(1, 0));
00365 
00366   // Keep-Alive?
00367 
00368   // Cache-Control?
00369 }
00370 
00371 
00372 ////////////////////////////////////////////////////////////////////////
00373 // Take an existing outgoing response header and make it HTTP/1.1
00374 void
00375 HttpTransactHeaders::convert_to_1_1_response_header(HTTPHdr *outgoing_response)
00376 {
00377   // These are required
00378   ink_assert(outgoing_response->status_get());
00379 
00380   // Set HTTP version to 1.1
00381 //    ink_assert(outgoing_response->version_get() == HTTPVersion (1, 1));
00382   outgoing_response->version_set(HTTPVersion(1, 1));
00383 }
00384 
00385 
00386 ///////////////////////////////////////////////////////////////////////////////
00387 // Name       : calculate_document_age()
00388 // Description: returns age of document
00389 //
00390 // Input      :
00391 // Output     : ink_time_t age
00392 //
00393 // Details    :
00394 //   Algorithm is straight out of March 1998 1.1 specs, Section 13.2.3
00395 //
00396 ///////////////////////////////////////////////////////////////////////////////
00397 ink_time_t
00398 HttpTransactHeaders::calculate_document_age(ink_time_t request_time,
00399                                             ink_time_t response_time,
00400                                             HTTPHdr *base_response, ink_time_t base_response_date, ink_time_t now)
00401 {
00402   ink_time_t age_value = base_response->get_age();
00403   ink_time_t date_value = 0;
00404   ink_time_t apparent_age = 0;
00405   ink_time_t corrected_received_age = 0;
00406   ink_time_t response_delay = 0;
00407   ink_time_t corrected_initial_age = 0;
00408   ink_time_t current_age = 0;
00409   ink_time_t resident_time = 0;
00410   ink_time_t now_value = 0;
00411 
00412   ink_time_t tmp_value = 0;
00413 
00414   tmp_value = base_response_date;
00415   date_value = (tmp_value > 0) ? tmp_value : 0;
00416 
00417   // Deal with clock skew. Sigh.
00418   //
00419   // TODO solve this global clock problem
00420   now_value = max(now, response_time);
00421 
00422   ink_assert(response_time >= 0);
00423   ink_assert(request_time >= 0);
00424   ink_assert(response_time >= request_time);
00425   ink_assert(now_value >= response_time);
00426 
00427   if (date_value > 0) {
00428     apparent_age = max((time_t) 0, (response_time - date_value));
00429   }
00430   if (age_value < 0) {
00431     current_age = -1; // Overflow from Age: header
00432   } else {
00433     corrected_received_age = max(apparent_age, age_value);
00434     response_delay = response_time - request_time;
00435     corrected_initial_age = corrected_received_age + response_delay;
00436     resident_time = now_value - response_time;
00437     current_age = corrected_initial_age + resident_time;
00438   }
00439 
00440   Debug("http_age", "[calculate_document_age] age_value:              %" PRId64, (int64_t)age_value);
00441   Debug("http_age", "[calculate_document_age] date_value:             %" PRId64, (int64_t)date_value);
00442   Debug("http_age", "[calculate_document_age] response_time:          %" PRId64, (int64_t)response_time);
00443   Debug("http_age", "[calculate_document_age] now:                    %" PRId64, (int64_t)now);
00444   Debug("http_age", "[calculate_document_age] now (fixed):            %" PRId64, (int64_t)now_value);
00445   Debug("http_age", "[calculate_document_age] apparent_age:           %" PRId64, (int64_t)apparent_age);
00446   Debug("http_age", "[calculate_document_age] corrected_received_age: %" PRId64, (int64_t)corrected_received_age);
00447   Debug("http_age", "[calculate_document_age] response_delay:         %" PRId64, (int64_t)response_delay);
00448   Debug("http_age", "[calculate_document_age] corrected_initial_age:  %" PRId64, (int64_t)corrected_initial_age);
00449   Debug("http_age", "[calculate_document_age] resident_time:          %" PRId64, (int64_t)resident_time);
00450   Debug("http_age", "[calculate_document_age] current_age:            %" PRId64, (int64_t)current_age);
00451 
00452   return current_age;
00453 }
00454 
00455 
00456 bool
00457 HttpTransactHeaders::does_server_allow_response_to_be_stored(HTTPHdr *resp)
00458 {
00459   uint32_t cc_mask = (MIME_COOKED_MASK_CC_NO_CACHE | MIME_COOKED_MASK_CC_NO_STORE | MIME_COOKED_MASK_CC_PRIVATE);
00460 
00461   if ((resp->get_cooked_cc_mask() & cc_mask) || (resp->get_cooked_pragma_no_cache()))
00462     return false;
00463   else
00464     return true;
00465 }
00466 
00467 
00468 bool
00469 HttpTransactHeaders::downgrade_request(bool *origin_server_keep_alive, HTTPHdr *outgoing_request)
00470 {
00471   //HTTPVersion ver;
00472   /* First try turning keep_alive off */
00473   if (*origin_server_keep_alive) {
00474 
00475     *origin_server_keep_alive = false;
00476     //ver.set(outgoing_request->version_get());
00477 
00478   }
00479 
00480   if (outgoing_request->version_get() == HTTPVersion(1, 1)) {
00481 
00482     //ver.set (HTTPVersion (1, 0));
00483     convert_to_1_0_request_header(outgoing_request);
00484 
00485     // bz48199: only GET requests can be downgraded to HTTP/0.9
00486   } else if (outgoing_request->version_get() == HTTPVersion(1, 0) &&
00487              outgoing_request->method_get_wksidx() == HTTP_WKSIDX_GET) {
00488 
00489     //ver.set (HTTPVersion (0, 9));
00490     convert_to_0_9_request_header(outgoing_request);
00491 
00492   } else {
00493     return false;
00494   }
00495 
00496   return true;
00497 }
00498 
00499 void
00500 HttpTransactHeaders::generate_and_set_squid_codes(HTTPHdr *header,
00501                                                  char *via_string,
00502                                                  HttpTransact::SquidLogInfo *squid_codes)
00503 {
00504   SquidLogCode log_code;
00505   SquidHierarchyCode hier_code;
00506   SquidHitMissCode hit_miss_code;
00507 
00508   /////////////////////////////
00509   // First the Hit-Miss Code //
00510   /////////////////////////////
00511   if ((via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_HIT_CONDITIONAL) ||
00512       (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_MISS_CONDITIONAL) ||
00513       (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_HIT_SERVED)) {
00514     // its a cache hit.
00515     // INKqa10331
00516     hit_miss_code = SQUID_HIT_RESERVED;
00517   } else {
00518     int reason_len;
00519     const char *reason = header->reason_get(&reason_len);
00520     // INKqa10331
00521     if (reason != NULL && reason_len >= 24 && reason[0] == '!' && reason[1] == SQUID_HIT_RESERVED)
00522       hit_miss_code = SQUID_HIT_RESERVED;
00523     // its a miss in the cache. find out why.
00524     else if (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_MISS_EXPIRED) {
00525       hit_miss_code = SQUID_MISS_PRE_EXPIRED;
00526     } else if (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_MISS_CONFIG) {
00527       hit_miss_code = SQUID_MISS_NONE;
00528     } else if (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_MISS_CLIENT) {
00529       hit_miss_code = SQUID_MISS_PRAGMA_NOCACHE;
00530     } else if (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_MISS_METHOD) {
00531       hit_miss_code = SQUID_MISS_HTTP_NON_CACHE;
00532     } else if (via_string[VIA_CLIENT_REQUEST] == VIA_CLIENT_ERROR) {
00533       hit_miss_code = SQUID_MISS_ERROR;
00534     } else if (via_string[VIA_CLIENT_REQUEST] == VIA_CLIENT_NO_CACHE) {
00535       hit_miss_code = SQUID_MISS_PRAGMA_NOCACHE;
00536     } else {
00537       hit_miss_code = SQUID_MISS_NONE;
00538     }
00539   }
00540 
00541   //////////////////////
00542   // Now the Log Code //
00543   //////////////////////
00544   if (via_string[VIA_CLIENT_REQUEST] == VIA_CLIENT_NO_CACHE) {
00545     log_code = SQUID_LOG_TCP_CLIENT_REFRESH;
00546   }
00547 
00548   else {
00549     if (via_string[VIA_CLIENT_REQUEST] == VIA_CLIENT_IMS) {
00550       if ((via_string[VIA_CACHE_RESULT] == VIA_IN_CACHE_FRESH) ||
00551           (via_string[VIA_CACHE_RESULT] == VIA_IN_RAM_CACHE_FRESH)) {
00552         log_code = SQUID_LOG_TCP_IMS_HIT;
00553       } else {
00554         if (via_string[VIA_SERVER_RESULT] == VIA_SERVER_NOT_MODIFIED) {
00555           log_code = SQUID_LOG_TCP_REFRESH_HIT;
00556         } else {
00557           log_code = SQUID_LOG_TCP_IMS_MISS;
00558         }
00559       }
00560     }
00561 
00562     else {
00563       if (via_string[VIA_CACHE_RESULT] == VIA_IN_CACHE_STALE) {
00564         if (via_string[VIA_SERVER_RESULT] == VIA_SERVER_NOT_MODIFIED) {
00565           log_code = SQUID_LOG_TCP_REFRESH_HIT;
00566         } else {
00567           if (via_string[VIA_SERVER_RESULT] == VIA_SERVER_ERROR) {
00568             log_code = SQUID_LOG_TCP_REF_FAIL_HIT;
00569           } else {
00570             log_code = SQUID_LOG_TCP_REFRESH_MISS;
00571           }
00572         }
00573       } else {
00574         if (via_string[VIA_CACHE_RESULT] == VIA_IN_CACHE_FRESH) {
00575           log_code = SQUID_LOG_TCP_HIT;
00576         } else if (via_string[VIA_CACHE_RESULT] == VIA_IN_RAM_CACHE_FRESH) {
00577           log_code = SQUID_LOG_TCP_MEM_HIT;
00578         } else {
00579           log_code = SQUID_LOG_TCP_MISS;
00580         }
00581       }
00582     }
00583   }
00584 
00585   ////////////////////////
00586   // The Hierarchy Code //
00587   ////////////////////////
00588   if ((via_string[VIA_CACHE_RESULT] == VIA_IN_CACHE_FRESH) || (via_string[VIA_CACHE_RESULT] == VIA_IN_RAM_CACHE_FRESH)) {
00589     hier_code = SQUID_HIER_NONE;
00590   } else if (via_string[VIA_DETAIL_ICP_CONNECT] == VIA_DETAIL_ICP_SUCCESS) {
00591     hier_code = SQUID_HIER_SIBLING_HIT;
00592   } else if (via_string[VIA_DETAIL_PP_CONNECT] == VIA_DETAIL_PP_SUCCESS) {
00593     hier_code = SQUID_HIER_PARENT_HIT;
00594   } else if (via_string[VIA_DETAIL_CACHE_TYPE] == VIA_DETAIL_PARENT) {
00595     hier_code = SQUID_HIER_DEFAULT_PARENT;
00596   } else if (via_string[VIA_DETAIL_TUNNEL] == VIA_DETAIL_TUNNEL_NO_FORWARD) {
00597     hier_code = SQUID_HIER_NONE;
00598   } else {
00599     hier_code = SQUID_HIER_DIRECT;
00600   }
00601 
00602   // Errors may override the other codes, so check the via string error codes last
00603   switch (via_string[VIA_ERROR_TYPE]) {
00604   case VIA_ERROR_AUTHORIZATION:
00605     // TODO decide which one?
00606     // log_code = SQUID_LOG_TCP_DENIED;
00607     log_code = SQUID_LOG_ERR_PROXY_DENIED;
00608     break;
00609   case VIA_ERROR_CONNECTION:
00610     if (log_code == SQUID_LOG_TCP_MISS) {
00611       log_code = SQUID_LOG_ERR_CONNECT_FAIL;
00612     }
00613     break;
00614   case VIA_ERROR_DNS_FAILURE:
00615     log_code = SQUID_LOG_ERR_DNS_FAIL;
00616     hier_code = SQUID_HIER_NONE;
00617     break;
00618   case VIA_ERROR_FORBIDDEN:
00619     log_code = SQUID_LOG_ERR_PROXY_DENIED;
00620     break;
00621   case VIA_ERROR_HEADER_SYNTAX:
00622     log_code = SQUID_LOG_ERR_INVALID_REQ;
00623     hier_code = SQUID_HIER_NONE;
00624     break;
00625   case VIA_ERROR_SERVER:
00626     if (log_code == SQUID_LOG_TCP_MISS || log_code == SQUID_LOG_TCP_IMS_MISS) {
00627       log_code = SQUID_LOG_ERR_CONNECT_FAIL;
00628     }
00629     break;
00630   case VIA_ERROR_TIMEOUT:
00631     if (log_code == SQUID_LOG_TCP_MISS || log_code == SQUID_LOG_TCP_IMS_MISS) {
00632       log_code = SQUID_LOG_ERR_READ_TIMEOUT;
00633     }
00634     if (hier_code == SQUID_HIER_SIBLING_HIT) {
00635       hier_code = SQUID_HIER_TIMEOUT_SIBLING_HIT;
00636     } else if (hier_code == SQUID_HIER_PARENT_HIT) {
00637       hier_code = SQUID_HIER_TIMEOUT_PARENT_HIT;
00638     } else {
00639       hier_code = SQUID_HIER_TIMEOUT_DIRECT;
00640     }
00641     break;
00642   case VIA_ERROR_CACHE_READ:
00643     log_code = SQUID_LOG_TCP_SWAPFAIL;
00644     hier_code = SQUID_HIER_NONE;
00645     break;
00646   default:
00647     break;
00648   }
00649 
00650   Debug("http_trans",
00651         "[Squid code generation] Hit/Miss: %d, Log: %d, Hier: %d",
00652         hit_miss_code, log_code, hier_code);
00653   squid_codes->log_code = log_code;
00654   squid_codes->hier_code = hier_code;
00655   squid_codes->hit_miss_code = hit_miss_code;
00656 }
00657 
00658 #include "HttpDebugNames.h"
00659 
00660 void
00661 HttpTransactHeaders::insert_warning_header(HttpConfigParams *http_config_param, HTTPHdr *header, HTTPWarningCode code,
00662                                            const char *warn_text, int warn_text_len)
00663 {
00664   int bufsize, len;
00665 
00666   // + 23 for 20 possible digits of warning code (long long max
00667   //  digits) & 2 spaces & the string terminator
00668   bufsize = http_config_param->proxy_response_via_string_len + 23;
00669   if (warn_text != NULL)
00670     bufsize += warn_text_len;
00671   else
00672     warn_text_len = 0; // Make sure it's really zero
00673 
00674   char *warning_text = (char *)alloca(bufsize);
00675 
00676   len = snprintf(warning_text, bufsize, "%3d %s %.*s", code, http_config_param->proxy_response_via_string, warn_text_len, warn_text);
00677   header->value_set(MIME_FIELD_WARNING, MIME_LEN_WARNING, warning_text, len);
00678 }
00679 
00680 
00681 void
00682 HttpTransactHeaders::insert_time_and_age_headers_in_response(ink_time_t request_sent_time,
00683                                                              ink_time_t response_received_time,
00684                                                              ink_time_t now, HTTPHdr *base, HTTPHdr *outgoing)
00685 {
00686   ink_time_t date = base->get_date();
00687   ink_time_t current_age = calculate_document_age(request_sent_time, response_received_time, base, date, now);
00688 
00689   outgoing->set_age(current_age); // set_age() deals with overflow properly, so pass it along
00690 
00691   // FIX: should handle missing date when response is received, not here.
00692   //      See INKqa09852.
00693   if (date <= 0)
00694     outgoing->set_date(now);
00695 }
00696 
00697 
00698 void
00699 HttpTransactHeaders::insert_server_header_in_response(const char *server_tag, int server_tag_size, HTTPHdr *h)
00700 {
00701   if (likely(server_tag && server_tag_size > 0 && h)) {
00702     h->set_server(server_tag, server_tag_size);
00703   }
00704 }
00705 
00706 
00707 ///////////////////////////////////////////////////////////////////////////////
00708 // Name       : insert_via_header_in_request
00709 // Description: takes in existing via_string and inserts it in header
00710 //
00711 // Input      :
00712 // Output     :
00713 //
00714 // Details    :
00715 //
00716 // [u<client-stuff> l<cache-lookup-stuff> o<server-stuff> f<cache-fill-stuff> p<proxy-stuff>]
00717 //
00718 //      client stuff
00719 //              I       IMS
00720 //              N       no-cache
00721 //              A       accept headers
00722 //              C       cookie
00723 //
00724 //      cache lookup stuff
00725 //              M       miss
00726 //              A       in cache, not acceptable
00727 //              S       in cache, stale
00728 //              H       in cache, fresh
00729 //
00730 //      server stuff
00731 //              N       not-modified
00732 //              S       served
00733 //
00734 //      cache fill stuff
00735 //              F       filled into cache
00736 //              U       updated cache
00737 //
00738 //      proxy stuff
00739 //              N       not-modified
00740 //              S       served
00741 //              R       origin server revalidated
00742 //
00743 // For example:
00744 //
00745 //      [u lH o f pS]      cache hit
00746 //      [u lM oS fF pS]    cache miss
00747 //      [uN l oS f pS]     no-cache origin server fetch
00748 //
00749 //
00750 ///////////////////////////////////////////////////////////////////////////////
00751 void
00752 HttpTransactHeaders::insert_via_header_in_request(HttpTransact::State *s, HTTPHdr *header)
00753 {
00754   char new_via_string[1024]; // 512-bytes for hostname+via string, 512-bytes for the debug info
00755   char *via_string = new_via_string;
00756 
00757   if ((s->http_config_param->proxy_hostname_len + s->http_config_param->proxy_request_via_string_len) > 512) {
00758     header->value_append(MIME_FIELD_VIA, MIME_LEN_VIA, "TrafficServer", 13, true);
00759     return;
00760   }
00761 
00762   char *incoming_via = s->via_string;
00763   int scheme = s->orig_scheme;
00764   ink_assert(scheme >= 0);
00765 
00766   int scheme_len = hdrtoken_index_to_length(scheme);
00767   int32_t hversion = header->version_get().m_version;
00768 
00769   memcpy(via_string, hdrtoken_index_to_wks(scheme), scheme_len);
00770   via_string += scheme_len;
00771 
00772   // Common case (I hope?)
00773   if ((HTTP_MAJOR(hversion) == 1) && HTTP_MINOR(hversion) == 1) {
00774     memcpy(via_string, "/1.1 ", 5);
00775     via_string += 5;
00776   } else {
00777     *via_string++ = '/';
00778     *via_string++ = '0' + HTTP_MAJOR(hversion);
00779     *via_string++ = '.';
00780     *via_string++ = '0' + HTTP_MINOR(hversion);
00781     *via_string++ = ' ';
00782   }
00783   via_string += nstrcpy(via_string, s->http_config_param->proxy_hostname);
00784 
00785   *via_string++ = '[';
00786   /* I thought we should use the transaction local outgoing IP address but
00787      that makes cycle detection (which is the point) unrealiable. We must
00788      use the same value every time to be sure.
00789   */
00790   memcpy(via_string, Machine::instance()->ip_hex_string, Machine::instance()->ip_hex_string_len);
00791   via_string += Machine::instance()->ip_hex_string_len;
00792   *via_string++ = ']';
00793   *via_string++ = ' ';
00794   *via_string++ = '(';
00795 
00796   memcpy(via_string, s->http_config_param->proxy_request_via_string, s->http_config_param->proxy_request_via_string_len);
00797   via_string += s->http_config_param->proxy_request_via_string_len;
00798 
00799   if (s->txn_conf->insert_request_via_string > 1) {
00800     *via_string++ = ' ';
00801     *via_string++ = '[';
00802 
00803     // incoming_via can be max MAX_VIA_INDICES+1 long (i.e. around 25 or so)
00804     if (s->txn_conf->insert_request_via_string > 2) { // Highest verbosity
00805       via_string += nstrcpy(via_string, incoming_via);
00806     } else {
00807       memcpy(via_string, incoming_via + VIA_CLIENT, VIA_SERVER - VIA_CLIENT);
00808       via_string += VIA_SERVER - VIA_CLIENT;
00809     }
00810     *via_string++ = ']';
00811   }
00812 
00813   *via_string++ = ')';
00814   *via_string = 0;
00815 
00816   ink_assert((size_t)(via_string - new_via_string) < (sizeof(new_via_string) - 1));
00817   header->value_append(MIME_FIELD_VIA, MIME_LEN_VIA, new_via_string, via_string - new_via_string, true);
00818 }
00819 
00820 void
00821 HttpTransactHeaders::insert_hsts_header_in_response(HttpTransact::State *s, HTTPHdr *header)
00822 {
00823   char new_hsts_string[64];
00824   char *hsts_string = new_hsts_string;
00825   const char include_subdomains[] = "; includeSubDomains";
00826 
00827   // add max-age
00828   int length = snprintf(new_hsts_string, sizeof(new_hsts_string), "max-age=%" PRId64, s->txn_conf->proxy_response_hsts_max_age);
00829 
00830   // add include subdomain if set
00831   if (s->txn_conf->proxy_response_hsts_include_subdomains) {
00832     hsts_string += length;
00833     memcpy(hsts_string, include_subdomains, sizeof(include_subdomains));
00834     length += sizeof(include_subdomains);
00835   }
00836 
00837   header->value_set(MIME_FIELD_STRICT_TRANSPORT_SECURITY, MIME_LEN_STRICT_TRANSPORT_SECURITY, new_hsts_string, length);
00838 }
00839 
00840 void
00841 HttpTransactHeaders::insert_via_header_in_response(HttpTransact::State *s, HTTPHdr *header)
00842 {
00843   char new_via_string[1024]; // 512-bytes for hostname+via string, 512-bytes for the debug info
00844   char *via_string = new_via_string;
00845 
00846   if ((s->http_config_param->proxy_hostname_len + s->http_config_param->proxy_response_via_string_len) > 512) {
00847     header->value_append(MIME_FIELD_VIA, MIME_LEN_VIA, "TrafficServer", 13, true);
00848     return;
00849   }
00850 
00851   char *incoming_via =  s->via_string;
00852   int scheme = s->next_hop_scheme;
00853 
00854   ink_assert(scheme >= 0);
00855   int scheme_len = hdrtoken_index_to_length(scheme);
00856   int32_t hversion = header->version_get().m_version;
00857 
00858   memcpy(via_string, hdrtoken_index_to_wks(scheme), scheme_len);
00859   via_string += scheme_len;
00860 
00861   // Common case (I hope?)
00862   if ((HTTP_MAJOR(hversion) == 1) && HTTP_MINOR(hversion) == 1) {
00863     memcpy(via_string, "/1.1 ", 5);
00864     via_string += 5;
00865   } else {
00866     *via_string++ = '/';
00867     *via_string++ = '0' + HTTP_MAJOR(hversion);
00868     *via_string++ = '.';
00869     *via_string++ = '0' + HTTP_MINOR(hversion);
00870     *via_string++ = ' ';
00871   }
00872   via_string += nstrcpy(via_string, s->http_config_param->proxy_hostname);
00873   *via_string++ = ' ';
00874   *via_string++ = '(';
00875 
00876   memcpy(via_string, s->http_config_param->proxy_response_via_string, s->http_config_param->proxy_response_via_string_len);
00877   via_string += s->http_config_param->proxy_response_via_string_len;
00878 
00879   if (s->txn_conf->insert_response_via_string > 1) {
00880     *via_string++ = ' ';
00881     *via_string++ = '[';
00882 
00883     // incoming_via can be max MAX_VIA_INDICES+1 long (i.e. around 25 or so)
00884     if (s->txn_conf->insert_response_via_string > 2) { // Highest verbosity
00885       via_string += nstrcpy(via_string, incoming_via);
00886     } else {
00887       memcpy(via_string, incoming_via + VIA_CACHE, VIA_PROXY - VIA_CACHE);
00888       via_string += VIA_PROXY - VIA_CACHE;
00889     }
00890     *via_string++ = ']';
00891   }
00892 
00893   *via_string++ = ')';
00894   *via_string = 0;
00895 
00896   ink_assert((size_t)(via_string - new_via_string) < (sizeof(new_via_string) - 1));
00897   header->value_append(MIME_FIELD_VIA, MIME_LEN_VIA, new_via_string, via_string - new_via_string, true);
00898 }
00899 
00900 
00901 ///////////////////////////////////////////////////////////////////////////////
00902 // Name: insert_basic_realm_in_proxy_authenticate
00903 // Description: insert Basic realm into Proxy-Authenticate based on
00904 //              configuration
00905 //  fix for INKqa09089
00906 ///////////////////////////////////////////////////////////////////////////////
00907 void
00908 HttpTransactHeaders::insert_basic_realm_in_proxy_authenticate(const char *realm, HTTPHdr *header, bool bRevPrxy)
00909 {
00910   char new_basic_realm[128];
00911   char *basic_realm;
00912 
00913   basic_realm = new_basic_realm;
00914   basic_realm += nstrcpy(basic_realm, "Basic realm=\"");
00915   basic_realm += nstrcpy(basic_realm, (char *) realm);
00916   *basic_realm++ = '"';
00917   *basic_realm = 0;
00918 
00919   MIMEField *auth;
00920   if (false == bRevPrxy) {
00921     auth = header->field_create(MIME_FIELD_PROXY_AUTHENTICATE, MIME_LEN_PROXY_AUTHENTICATE);
00922   } else {
00923     auth = header->field_create(MIME_FIELD_WWW_AUTHENTICATE, MIME_LEN_WWW_AUTHENTICATE);
00924   }
00925 
00926   header->field_value_set(auth, new_basic_realm, strlen(new_basic_realm));
00927   header->field_attach(auth);
00928 }
00929 
00930 
00931 void
00932 HttpTransactHeaders::remove_conditional_headers(HTTPHdr *outgoing)
00933 {
00934   if (outgoing->presence(MIME_PRESENCE_IF_MODIFIED_SINCE | MIME_PRESENCE_IF_UNMODIFIED_SINCE |
00935                          MIME_PRESENCE_IF_MATCH | MIME_PRESENCE_IF_NONE_MATCH)) {
00936 
00937     outgoing->field_delete(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE);
00938     outgoing->field_delete(MIME_FIELD_IF_UNMODIFIED_SINCE, MIME_LEN_IF_UNMODIFIED_SINCE);
00939     outgoing->field_delete(MIME_FIELD_IF_MATCH, MIME_LEN_IF_MATCH);
00940     outgoing->field_delete(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH);
00941   }
00942   // TODO: how about RANGE and IF_RANGE?
00943 }
00944 
00945 void
00946 HttpTransactHeaders::remove_100_continue_headers(HttpTransact::State *s, HTTPHdr *outgoing)
00947 {
00948   int len = 0;
00949   const char *expect = s->hdr_info.client_request.value_get(MIME_FIELD_EXPECT, MIME_LEN_EXPECT, &len);
00950 
00951   if ((len == HTTP_LEN_100_CONTINUE) && (strncasecmp(expect, HTTP_VALUE_100_CONTINUE, HTTP_LEN_100_CONTINUE) == 0)) {
00952     outgoing->field_delete(MIME_FIELD_EXPECT, MIME_LEN_EXPECT);
00953   }
00954 }
00955 
00956 
00957 
00958 ////////////////////////////////////////////////////////////////////////
00959 // Deal with lame-o servers by removing the host name from the url.
00960 void
00961 HttpTransactHeaders::remove_host_name_from_url(HTTPHdr *outgoing_request)
00962 {
00963   URL *outgoing_url = outgoing_request->url_get();
00964   outgoing_url->nuke_proxy_stuff();
00965 }
00966 
00967 
00968 void
00969 HttpTransactHeaders::add_global_user_agent_header_to_request(OverridableHttpConfigParams *http_txn_conf, HTTPHdr *header)
00970 {
00971   if (http_txn_conf->global_user_agent_header) {
00972     MIMEField *ua_field;
00973 
00974     Debug("http_trans", "Adding User-Agent: %s", http_txn_conf->global_user_agent_header);
00975     if ((ua_field = header->field_find(MIME_FIELD_USER_AGENT, MIME_LEN_USER_AGENT)) == NULL) {
00976       if (likely((ua_field = header->field_create(MIME_FIELD_USER_AGENT, MIME_LEN_USER_AGENT)) != NULL))
00977         header->field_attach(ua_field);
00978     }
00979     // This will remove any old string (free it), and set our User-Agent.
00980     if (likely(ua_field))
00981       header->field_value_set(ua_field, http_txn_conf->global_user_agent_header,
00982                               http_txn_conf->global_user_agent_header_size);
00983   }
00984 }
00985 
00986 
00987 void
00988 HttpTransactHeaders::add_server_header_to_response(OverridableHttpConfigParams *http_txn_conf, HTTPHdr *header)
00989 {
00990   if (http_txn_conf->proxy_response_server_enabled && http_txn_conf->proxy_response_server_string) {
00991     MIMEField *ua_field;
00992     bool do_add = true;
00993 
00994     if ((ua_field = header->field_find(MIME_FIELD_SERVER, MIME_LEN_SERVER)) == NULL) {
00995       if (likely((ua_field = header->field_create(MIME_FIELD_SERVER, MIME_LEN_SERVER)) != NULL))
00996         header->field_attach(ua_field);
00997     } else {
00998       // There was an existing header from Origin, so only add if setting allows to overwrite.
00999       do_add = (1 == http_txn_conf->proxy_response_server_enabled);
01000     }
01001 
01002     // This will remove any old string (free it), and set our Server header.
01003     if (do_add && likely(ua_field)) {
01004       Debug("http_trans", "Adding Server: %s", http_txn_conf->proxy_response_server_string);
01005       header->field_value_set(ua_field, http_txn_conf->proxy_response_server_string, http_txn_conf->proxy_response_server_string_len);
01006     }
01007   }
01008 }
01009 
01010 
01011 void
01012 HttpTransactHeaders::remove_privacy_headers_from_request(HttpConfigParams *http_config_param,
01013                                                          OverridableHttpConfigParams *http_txn_conf, HTTPHdr *header)
01014 {
01015   if (!header)
01016     return;
01017 
01018   // From
01019   if (http_txn_conf->anonymize_remove_from) {
01020     Debug("anon", "removing 'From' headers");
01021     header->field_delete(MIME_FIELD_FROM, MIME_LEN_FROM);
01022   }
01023   // Referer
01024   if (http_txn_conf->anonymize_remove_referer) {
01025     Debug("anon", "removing 'Referer' headers");
01026     header->field_delete(MIME_FIELD_REFERER, MIME_LEN_REFERER);
01027   }
01028   // User-Agent
01029   if (http_txn_conf->anonymize_remove_user_agent) {
01030     Debug("anon", "removing 'User-agent' headers");
01031     header->field_delete(MIME_FIELD_USER_AGENT, MIME_LEN_USER_AGENT);
01032   }
01033   // Cookie
01034   if (http_txn_conf->anonymize_remove_cookie) {
01035     Debug("anon", "removing 'Cookie' headers");
01036     header->field_delete(MIME_FIELD_COOKIE, MIME_LEN_COOKIE);
01037   }
01038   // Client-ip
01039   if (http_txn_conf->anonymize_remove_client_ip) {
01040     Debug("anon", "removing 'Client-ip' headers");
01041     header->field_delete(MIME_FIELD_CLIENT_IP, MIME_LEN_CLIENT_IP);
01042   }
01043   /////////////////////////////////////////////
01044   // remove any other user specified headers //
01045   /////////////////////////////////////////////
01046 
01047   // FIXME: we shouldn't parse this list every time, only when the
01048   // FIXME: config file changes.
01049   if (http_config_param->anonymize_other_header_list) {
01050     Str *field;
01051     StrList anon_list(false);
01052     const char *anon_string;
01053 
01054     anon_string = http_config_param->anonymize_other_header_list;
01055     Debug("anon", "removing other headers (%s)", anon_string);
01056     HttpCompat::parse_comma_list(&anon_list, anon_string);
01057     for (field = anon_list.head; field != NULL; field = field->next) {
01058       Debug("anon", "removing '%s' headers", field->str);
01059       header->field_delete(field->str, field->len);
01060     }
01061   }
01062 }

Generated by  doxygen 1.7.1