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

HttpTransact.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 #include "libts.h"
00025 
00026 #include <strings.h>
00027 #include <math.h>
00028 
00029 #include "HttpTransact.h"
00030 #include "HttpTransactHeaders.h"
00031 #include "HttpSM.h"
00032 #include "HttpCacheSM.h"        //Added to get the scope of HttpCacheSM object - YTS Team, yamsat
00033 #include "HttpDebugNames.h"
00034 #include "time.h"
00035 #include "ParseRules.h"
00036 #include "HTTP.h"
00037 #include "HdrUtils.h"
00038 #include "MimeTable.h"
00039 #include "logging/Log.h"
00040 #include "logging/LogUtils.h"
00041 #include "Error.h"
00042 #include "CacheControl.h"
00043 #include "ControlMatcher.h"
00044 #include "ReverseProxy.h"
00045 #include "HttpBodyFactory.h"
00046 #include "StatPages.h"
00047 #include "HttpClientSession.h"
00048 #include "I_Machine.h"
00049 
00050 static char range_type[] = "multipart/byteranges; boundary=RANGE_SEPARATOR";
00051 #define RANGE_NUMBERS_LENGTH 60
00052 
00053 #define HTTP_INCREMENT_TRANS_STAT(X) update_stat(s, X, 1);
00054 #define HTTP_SUM_TRANS_STAT(X,S) update_stat(s, X, (ink_statval_t) S);
00055 
00056 #define TRANSACT_REMEMBER(_s,_e,_d) \
00057 { \
00058     HttpSM *sm = (_s)->state_machine; \
00059     sm->history[sm->history_pos % HISTORY_SIZE].file  = __FILE__; \
00060     sm->history[sm->history_pos % HISTORY_SIZE].line  = __LINE__; \
00061     sm->history[sm->history_pos % HISTORY_SIZE].event = _e; \
00062     sm->history[sm->history_pos % HISTORY_SIZE].data = (void *)_d; \
00063     sm->history_pos += 1; \
00064 }
00065 
00066 #define DebugTxn(tag, ...) DebugSpecific((s->state_machine->debug_on), tag, __VA_ARGS__)
00067 
00068 extern HttpBodyFactory *body_factory;
00069 extern int cache_config_vary_on_user_agent;
00070 
00071 static const char local_host_ip_str[] = "127.0.0.1";
00072 
00073 
00074 // someday, reduce the amount of duplicate code between this
00075 // function and _process_xxx_connection_field_in_outgoing_header
00076 inline static HTTPKeepAlive
00077 is_header_keep_alive(const HTTPVersion & http_version, const HTTPVersion & request_http_version, MIMEField* con_hdr    /*, bool* unknown_tokens */)
00078 {
00079   enum
00080   {
00081     CON_TOKEN_NONE = 0,
00082     CON_TOKEN_KEEP_ALIVE,
00083     CON_TOKEN_CLOSE
00084   };
00085 
00086   int con_token = CON_TOKEN_NONE;
00087   HTTPKeepAlive keep_alive = HTTP_NO_KEEPALIVE;
00088   //    *unknown_tokens = false;
00089 
00090   if (con_hdr) {
00091     int val_len;
00092     const char *val;
00093 
00094     if (!con_hdr->has_dups()) { // try fastpath first
00095       val = con_hdr->value_get(&val_len);
00096       if (ptr_len_casecmp(val, val_len, "keep-alive", 10) == 0) {
00097         con_token = CON_TOKEN_KEEP_ALIVE;
00098       } else if (ptr_len_casecmp(val, val_len, "close", 5) == 0) {
00099         con_token = CON_TOKEN_CLOSE;
00100       }
00101     }
00102 
00103     if (con_token == CON_TOKEN_NONE) {
00104       HdrCsvIter iter;
00105 
00106       val = iter.get_first(con_hdr, &val_len);
00107 
00108       while (val) {
00109         if (ptr_len_casecmp(val, val_len, "keep-alive", 10) == 0) {
00110           con_token = CON_TOKEN_KEEP_ALIVE;
00111           /*
00112              if (!*unknown_tokens) {
00113              *unknown_tokens = (iter->get_next(&val_len) == NULL);
00114              } */
00115           break;
00116         } else if (ptr_len_casecmp(val, val_len, "close", 5) == 0) {
00117           con_token = CON_TOKEN_CLOSE;
00118           /*
00119              if (!*unknown_tokens) {
00120              *unknown_tokens = (iter->get_next(&val_len) == NULL);
00121              } */
00122           break;
00123         } else {
00124           //      *unknown_tokens = true;
00125         }
00126         val = iter.get_next(&val_len);
00127       }
00128     }
00129   }
00130 
00131   if (HTTPVersion(1, 0) == http_version) {
00132     keep_alive = (con_token == CON_TOKEN_KEEP_ALIVE) ? (HTTP_KEEPALIVE) : (HTTP_NO_KEEPALIVE);
00133   } else if (HTTPVersion(1, 1) == http_version) {
00134     // We deviate from the spec here.  If the we got a response where
00135     //   where there is no Connection header and the request 1.0 was
00136     //   1.0 don't treat this as keep-alive since Netscape-Enterprise/3.6 SP1
00137     //   server doesn't
00138     keep_alive = ((con_token == CON_TOKEN_KEEP_ALIVE) ||
00139                   (con_token == CON_TOKEN_NONE && HTTPVersion(1, 1) == request_http_version)) ? (HTTP_KEEPALIVE)
00140       : (HTTP_NO_KEEPALIVE);
00141   } else {
00142     keep_alive = HTTP_NO_KEEPALIVE;
00143   }
00144 
00145   return (keep_alive);
00146 }
00147 
00148 inline static bool
00149 is_request_conditional(HTTPHdr* header)
00150 {
00151   uint64_t mask = (MIME_PRESENCE_IF_UNMODIFIED_SINCE |
00152                  MIME_PRESENCE_IF_MODIFIED_SINCE | MIME_PRESENCE_IF_RANGE |
00153                  MIME_PRESENCE_IF_MATCH | MIME_PRESENCE_IF_NONE_MATCH);
00154   return (header->presence(mask) && (header->method_get_wksidx() == HTTP_WKSIDX_GET ||
00155                           header->method_get_wksidx() == HTTP_WKSIDX_HEAD));
00156 }
00157 
00158 static inline bool
00159 is_port_in_range(int port, HttpConfigPortRange *pr)
00160 {
00161   while (pr) {
00162     if (pr->low == -1) {
00163       return true;
00164     } else if ((pr->low <= port) && (pr->high >= port)) {
00165       return true;
00166     }
00167 
00168     pr = pr->next;
00169   }
00170 
00171   return false;
00172 }
00173 
00174 inline static void
00175 update_cache_control_information_from_config(HttpTransact::State* s)
00176 {
00177   getCacheControl(&s->cache_control, &s->request_data, s->txn_conf);
00178 
00179   s->cache_info.directives.does_config_permit_lookup &= (s->cache_control.never_cache == false);
00180   s->cache_info.directives.does_config_permit_storing &= (s->cache_control.never_cache == false);
00181 
00182   s->cache_info.directives.does_client_permit_storing =
00183     HttpTransact::does_client_request_permit_storing(&s->cache_control, &s->hdr_info.client_request);
00184 
00185   s->cache_info.directives.does_client_permit_lookup =
00186     HttpTransact::does_client_request_permit_cached_response(s->txn_conf, &s->cache_control,
00187                                                              &s->hdr_info.client_request, s->via_string);
00188 
00189   s->cache_info.directives.does_client_permit_dns_storing =
00190     HttpTransact::does_client_request_permit_dns_caching(&s->cache_control, &s->hdr_info.client_request);
00191 
00192   if (s->client_info.http_version == HTTPVersion(0, 9)) {
00193     s->cache_info.directives.does_client_permit_lookup = false;
00194     s->cache_info.directives.does_client_permit_storing = false;
00195   }
00196 
00197   // Less than 0 means it wasn't overridden, so leave it alone.
00198   if (s->cache_control.cache_responses_to_cookies >= 0)
00199     s->txn_conf->cache_responses_to_cookies = s->cache_control.cache_responses_to_cookies;
00200 }
00201 
00202 inline bool
00203 HttpTransact::is_server_negative_cached(State* s)
00204 {
00205   if (s->host_db_info.app.http_data.last_failure != 0 &&
00206       s->host_db_info.app.http_data.last_failure + s->txn_conf->down_server_timeout > s->client_request_time) {
00207     return true;
00208   } else {
00209     // Make sure some nasty clock skew has not happened
00210     //  Use the server timeout to set an upperbound as to how far in the
00211     //   future we should tolerate bogus last failure times.  This sets
00212     //   the upper bound to the time that we would ever consider a server
00213     //   down to 2*down_server_timeout
00214     if (s->client_request_time + s->txn_conf->down_server_timeout < s->host_db_info.app.http_data.last_failure) {
00215       s->host_db_info.app.http_data.last_failure = 0;
00216       ink_assert(!"extreme clock skew");
00217       return true;
00218     }
00219     return false;
00220   }
00221 }
00222 
00223 inline static void
00224 update_current_info(HttpTransact::CurrentInfo* into, HttpTransact::ConnectionAttributes* from,
00225                     HttpTransact::LookingUp_t who, int attempts)
00226 {
00227   into->request_to = who;
00228   into->server = from;
00229   into->attempts = attempts;
00230 }
00231 
00232 inline static void
00233 update_dns_info(HttpTransact::DNSLookupInfo* dns, HttpTransact::CurrentInfo* from, int attempts,
00234                 Arena* /* arena ATS_UNUSED */)
00235 {
00236   dns->looking_up = from->request_to;
00237   dns->lookup_name = from->server->name;
00238   dns->attempts = attempts;
00239 }
00240 
00241 inline static HTTPHdr *
00242 find_appropriate_cached_resp(HttpTransact::State* s)
00243 {
00244   HTTPHdr *c_resp = NULL;
00245 
00246   if (s->cache_info.object_store.valid()) {
00247     c_resp = s->cache_info.object_store.response_get();
00248     if (c_resp != NULL && c_resp->valid())
00249       return c_resp;
00250   }
00251 
00252   ink_assert(s->cache_info.object_read != NULL);
00253   return s->cache_info.object_read->response_get();
00254 }
00255 
00256 inline static bool
00257 is_negative_caching_appropriate(HttpTransact::State* s)
00258 {
00259   if (!s->txn_conf->negative_caching_enabled || !s->hdr_info.server_response.valid())
00260     return false;
00261 
00262   switch (s->hdr_info.server_response.status_get()) {
00263   case HTTP_STATUS_NO_CONTENT:
00264   case HTTP_STATUS_USE_PROXY:
00265   case HTTP_STATUS_BAD_REQUEST:
00266   case HTTP_STATUS_FORBIDDEN:
00267   case HTTP_STATUS_NOT_FOUND:
00268   case HTTP_STATUS_METHOD_NOT_ALLOWED:
00269   case HTTP_STATUS_REQUEST_URI_TOO_LONG:
00270   case HTTP_STATUS_INTERNAL_SERVER_ERROR:
00271   case HTTP_STATUS_NOT_IMPLEMENTED:
00272   case HTTP_STATUS_BAD_GATEWAY:
00273   case HTTP_STATUS_SERVICE_UNAVAILABLE:
00274   case HTTP_STATUS_GATEWAY_TIMEOUT:
00275     return true;
00276   default:
00277     break;
00278   }
00279 
00280   return false;
00281 }
00282 
00283 inline static HttpTransact::LookingUp_t
00284 find_server_and_update_current_info(HttpTransact::State* s)
00285 {
00286   URL *url = s->hdr_info.client_request.url_get();
00287   int host_len;
00288   const char *host = s->hdr_info.client_request.host_get(&host_len);
00289 
00290   if (ptr_len_cmp(host, host_len, local_host_ip_str, sizeof(local_host_ip_str) - 1) == 0) {
00291     // Do not forward requests to local_host onto a parent.
00292     // I just wanted to do this for cop heartbeats, someone else
00293     // wanted it for all requests to local_host.
00294     s->parent_result.r = PARENT_DIRECT;
00295   } else if (url->scheme_get_wksidx() == URL_WKSIDX_HTTPS) {
00296     // Do not forward HTTPS requests onto a parent.
00297     s->parent_result.r = PARENT_DIRECT;
00298   } else if (s->method == HTTP_WKSIDX_CONNECT && s->http_config_param->disable_ssl_parenting) {
00299     s->parent_result.r = PARENT_DIRECT;
00300   } else if (s->http_config_param->uncacheable_requests_bypass_parent &&
00301              s->http_config_param->no_dns_forward_to_parent == 0 &&
00302              !HttpTransact::is_request_cache_lookupable(s)) {
00303     // request not lookupable and cacheable, so bypass parent
00304     // Note that the configuration of the proxy as well as the request
00305     // itself affects the result of is_request_cache_lookupable();
00306     // we are assuming both child and parent have similar configuration
00307     // with respect to whether a request is cacheable or not.
00308     // For example, the cache_urls_that_look_dynamic variable.
00309     DebugTxn("http_trans", "request not cacheable, so bypass parent");
00310     s->parent_result.r = PARENT_DIRECT;
00311   } else {
00312     switch (s->parent_result.r) {
00313     case PARENT_UNDEFINED:
00314       s->parent_params->findParent(&s->request_data, &s->parent_result);
00315       break;
00316     case PARENT_SPECIFIED:
00317       s->parent_params->nextParent(&s->request_data, &s->parent_result);
00318 
00319       // Hack!
00320       // We already have a parent that failed, if we are now told
00321       //  to go the origin server, we can only obey this if we
00322       //  dns'ed the origin server
00323       if (s->parent_result.r == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 0) {
00324         ink_assert(!ats_is_ip(&s->server_info.addr));
00325         s->parent_result.r = PARENT_FAIL;
00326       }
00327       break;
00328     case PARENT_FAIL:
00329       // Check to see if should bypass the parent and go direct
00330       //   We can only do this if
00331       //   1) the parent was not set from API
00332       //   2) the config permits us
00333       //   3) the config permitted us to dns the origin server
00334       if (!s->parent_params->apiParentExists(&s->request_data) && s->parent_result.rec->bypass_ok() &&
00335           s->http_config_param->no_dns_forward_to_parent == 0) {
00336         s->parent_result.r = PARENT_DIRECT;
00337       }
00338       break;
00339     default:
00340       ink_assert(0);
00341       // FALL THROUGH
00342     case PARENT_DIRECT:
00343       //              // if we have already decided to go direct
00344       //              // dont bother calling nextParent.
00345       //              // do nothing here, guy.
00346       break;
00347     }
00348   }
00349 
00350   switch (s->parent_result.r) {
00351   case PARENT_SPECIFIED:
00352     s->parent_info.name = s->arena.str_store(s->parent_result.hostname, strlen(s->parent_result.hostname));
00353     s->parent_info.port = s->parent_result.port;
00354     update_current_info(&s->current, &s->parent_info, HttpTransact::PARENT_PROXY, (s->current.attempts)++);
00355     update_dns_info(&s->dns_info, &s->current, 0, &s->arena);
00356     ink_assert(s->dns_info.looking_up == HttpTransact::PARENT_PROXY);
00357     s->next_hop_scheme = URL_WKSIDX_HTTP;
00358 
00359     return HttpTransact::PARENT_PROXY;
00360   case PARENT_FAIL:
00361     // No more parents - need to return an error message
00362     s->current.request_to = HttpTransact::HOST_NONE;
00363     return HttpTransact::HOST_NONE;
00364 
00365   case PARENT_DIRECT:
00366     /* fall through */
00367   default:
00368     update_current_info(&s->current, &s->server_info, HttpTransact::ORIGIN_SERVER, (s->current.attempts)++);
00369     update_dns_info(&s->dns_info, &s->current, 0, &s->arena);
00370     ink_assert(s->dns_info.looking_up == HttpTransact::ORIGIN_SERVER);
00371     s->next_hop_scheme = s->scheme;
00372     return HttpTransact::ORIGIN_SERVER;
00373   }
00374 }
00375 
00376 inline static bool
00377 do_cookies_prevent_caching(int cookies_conf, HTTPHdr* request, HTTPHdr* response, HTTPHdr* cached_request = NULL)
00378 {
00379   enum CookiesConfig
00380   {
00381     COOKIES_CACHE_NONE = 0,     // do not cache any responses to cookies
00382     COOKIES_CACHE_ALL = 1,      // cache for any content-type (ignore cookies)
00383     COOKIES_CACHE_IMAGES = 2,   // cache only for image types
00384     COOKIES_CACHE_ALL_BUT_TEXT = 3,     // cache for all but text content-types
00385     COOKIES_CACHE_ALL_BUT_TEXT_EXT = 4  // cache for all but text content-types except with OS response
00386       // without "Set-Cookie" or with "Cache-Control: public"
00387   };
00388 
00389   const char *content_type = NULL;
00390   int str_len;
00391 
00392 #ifdef DEBUG
00393   ink_assert(request->type_get() == HTTP_TYPE_REQUEST);
00394   ink_assert(response->type_get() == HTTP_TYPE_RESPONSE);
00395   if (cached_request) {
00396     ink_assert(cached_request->type_get() == HTTP_TYPE_REQUEST);
00397   }
00398 #endif
00399 
00400   // Can cache all regardless of cookie header - just ignore all cookie headers
00401   if ((CookiesConfig) cookies_conf == COOKIES_CACHE_ALL) {
00402     return false;
00403   }
00404 
00405   // It is considered that Set-Cookie headers can be safely ignored
00406   // for non text content types if Cache-Control private is not set.
00407   // This enables a bigger hit rate, which currently outweighs the risk of
00408   // breaking origin servers that truly intend to set a cookie with other
00409   // objects such as images.
00410   // At this time, it is believed that only advertisers do this, and that
00411   // customers won't care about it.
00412 
00413   // If the response does not have a Set-Cookie header and
00414   // the response does not have a Cookie header and
00415   // the object is not cached or the request does not have a Cookie header
00416   // then cookies do not prevent caching.
00417   if (!response->presence(MIME_PRESENCE_SET_COOKIE) &&
00418       !request->presence(MIME_PRESENCE_COOKIE) && (cached_request == NULL
00419                                                    || !cached_request->presence(MIME_PRESENCE_COOKIE))) {
00420     return false;
00421   }
00422 
00423   // Do not cache if cookies option is COOKIES_CACHE_NONE
00424   // and a Cookie is detected
00425   if ((CookiesConfig) cookies_conf == COOKIES_CACHE_NONE) {
00426     return true;
00427   }
00428   // All other options depend on the Content-Type
00429   content_type = response->value_get(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE, &str_len);
00430 
00431   if ((CookiesConfig) cookies_conf == COOKIES_CACHE_IMAGES) {
00432     if (content_type && str_len >= 5 && memcmp(content_type, "image", 5) == 0) {
00433       // Images can be cached
00434       return false;
00435     }
00436     return true;                // do not cache if  COOKIES_CACHE_IMAGES && content_type != "image"
00437   }
00438   // COOKIES_CACHE_ALL_BUT_TEXT || COOKIES_CACHE_ALL_BUT_TEXT_EXT
00439   // Note: if the configuration is bad, we consider
00440   // COOKIES_CACHE_ALL_BUT_TEXT to be the default
00441 
00442   if (content_type && str_len >= 4 && memcmp(content_type, "text", 4) == 0) {   // content type  - "text"
00443     // Text objects cannot be cached unless the option is
00444     // COOKIES_CACHE_ALL_BUT_TEXT_EXT.
00445     // Furthermore, if there is a Set-Cookie header, then
00446     // Cache-Control must be set.
00447     if ((CookiesConfig) cookies_conf == COOKIES_CACHE_ALL_BUT_TEXT_EXT &&
00448         ((!response->presence(MIME_PRESENCE_SET_COOKIE)) || response->is_cache_control_set(HTTP_VALUE_PUBLIC))) {
00449       return false;
00450     }
00451     return true;
00452   }
00453   return false;                 // Non text objects can be cached
00454 }
00455 
00456 
00457 inline static bool
00458 does_method_require_cache_copy_deletion(const HttpConfigParams *http_config_param, const int method)
00459 {
00460   return ((method != HTTP_WKSIDX_GET) &&
00461           (method == HTTP_WKSIDX_DELETE || method == HTTP_WKSIDX_PURGE ||
00462            method == HTTP_WKSIDX_PUT ||
00463            (http_config_param->cache_post_method != 1 && method == HTTP_WKSIDX_POST)));
00464 }
00465 
00466 
00467 inline static
00468 HttpTransact::StateMachineAction_t
00469 how_to_open_connection(HttpTransact::State* s)
00470 {
00471   ink_assert(s->pending_work == NULL);
00472 
00473   // Originally we returned which type of server to open
00474   // Now, however, we may want to issue a cache
00475   // operation first in order to lock the cache
00476   // entry to prevent multiple origin server requests
00477   // for the same document.
00478   // The cache operation that we actually issue, of
00479   // course, depends on the specified "cache_action".
00480   // If there is no cache-action to be issued, just
00481   // connect to the server.
00482   switch (s->cache_info.action) {
00483   case HttpTransact::CACHE_PREPARE_TO_DELETE:
00484   case HttpTransact::CACHE_PREPARE_TO_UPDATE:
00485   case HttpTransact::CACHE_PREPARE_TO_WRITE:
00486     s->transact_return_point = HttpTransact::handle_cache_write_lock;
00487     return HttpTransact::SM_ACTION_CACHE_ISSUE_WRITE;
00488   default:
00489     // This covers:
00490     // CACHE_DO_UNDEFINED, CACHE_DO_NO_ACTION, CACHE_DO_DELETE,
00491     // CACHE_DO_LOOKUP, CACHE_DO_REPLACE, CACHE_DO_SERVE,
00492     // CACHE_DO_SERVE_AND_DELETE, CACHE_DO_SERVE_AND_UPDATE,
00493     // CACHE_DO_UPDATE, CACHE_DO_WRITE, TOTAL_CACHE_ACTION_TYPES
00494     break;
00495   }
00496 
00497   if (s->method == HTTP_WKSIDX_CONNECT && s->parent_result.r != PARENT_SPECIFIED) {
00498     s->cdn_saved_next_action = HttpTransact::SM_ACTION_ORIGIN_SERVER_RAW_OPEN;
00499   } else {
00500     s->cdn_saved_next_action = HttpTransact::SM_ACTION_ORIGIN_SERVER_OPEN;
00501   }
00502 
00503   // In the following, if url_remap_mode == 2 (URL_REMAP_FOR_OS)
00504   // then do remapping for requests to OS's.
00505   // Whether there is CDN remapping or not, goto SM_ACTION_DNS_LOOKUP;
00506   // after that, it'll goto ORIGIN_SERVER_(RAW_)OPEN as needed.
00507 
00508   if ((url_remap_mode == HttpTransact::URL_REMAP_FOR_OS) &&
00509       (s->current.request_to == HttpTransact::ORIGIN_SERVER) && !s->cdn_remap_complete) {
00510     DebugTxn("cdn", "*** START CDN Remapping *** CDN mode = %d", url_remap_mode);
00511 
00512     char *remap_redirect = NULL;
00513     int host_len;
00514     const char *host;
00515 
00516     // We need to copy the client request into the server request.  Why?  BUGBUG
00517     s->hdr_info.server_request.url_set(s->hdr_info.client_request.url_get());
00518 
00519     // TODO yeah, not sure everything here is correct with redirects
00520     // This probably doesn't work properly, since request_url_remap() is broken.
00521     if (request_url_remap(s, &s->hdr_info.server_request, &remap_redirect)) {
00522       ink_assert(!remap_redirect);      // should not redirect in this code
00523       HttpTransact::initialize_state_variables_for_origin_server(s, &s->hdr_info.server_request, true);
00524       DebugTxn("cdn", "Converting proxy request to server request");
00525       // Check whether a Host header field is missing from a 1.0 or 1.1 request.
00526       if (                      /*outgoing_version != HTTPVersion(0,9) && */
00527            !s->hdr_info.server_request.presence(MIME_PRESENCE_HOST)) {
00528         URL *url = s->hdr_info.server_request.url_get();
00529         host = url->host_get(&host_len);
00530         // Add a ':port' to the HOST header if the request is not going
00531         // to the default port.
00532         int port = url->port_get();
00533         if (port != url_canonicalize_port(URL_TYPE_HTTP, 0)) {
00534           char *buf = (char *)alloca(host_len + 15);
00535           memcpy(buf, host, host_len); 
00536           host_len += snprintf(buf + host_len, 15, ":%d", port);
00537           s->hdr_info.server_request.value_set(MIME_FIELD_HOST, MIME_LEN_HOST, buf, host_len);
00538         } else {
00539           s->hdr_info.server_request.value_set(MIME_FIELD_HOST, MIME_LEN_HOST, host, host_len);
00540         }
00541         ats_free(remap_redirect);  // This apparently shouldn't happen...
00542       }
00543       // Stripping out the host name from the URL
00544       if (s->current.server == &s->server_info && s->next_hop_scheme == URL_WKSIDX_HTTP) {
00545         DebugTxn("cdn", "Removing host name from URL");
00546         HttpTransactHeaders::remove_host_name_from_url(&s->hdr_info.server_request);
00547       }
00548     }                           // the URL was remapped
00549     if (is_debug_tag_set("cdn")) {
00550       char *d_url = s->hdr_info.server_request.url_get()->string_get(NULL);
00551       if (d_url)
00552         DebugTxn("cdn", "URL: %s", d_url);
00553       char *d_hst = (char *) s->hdr_info.server_request.value_get(MIME_FIELD_HOST, MIME_LEN_HOST, &host_len);
00554       if (d_hst)
00555         DebugTxn("cdn", "Host Hdr: %s", d_hst);
00556       ats_free(d_url);
00557     }
00558     s->cdn_remap_complete = true;       // It doesn't matter if there was an actual remap or not
00559     s->transact_return_point = HttpTransact::OSDNSLookup;
00560     ink_assert(s->next_action);
00561     ink_assert(s->cdn_saved_next_action);
00562     return HttpTransact::SM_ACTION_DNS_LOOKUP;
00563   }
00564 
00565   if (!s->already_downgraded) { //false unless downgraded previously (possibly due to HTTP 505)
00566     (&s->hdr_info.server_request)->version_set(HTTPVersion(1, 1));
00567     HttpTransactHeaders::convert_request(s->current.server->http_version, &s->hdr_info.server_request);
00568   }
00569 
00570   ink_assert(s->cdn_saved_next_action == HttpTransact::SM_ACTION_ORIGIN_SERVER_OPEN ||
00571                     s->cdn_saved_next_action == HttpTransact::SM_ACTION_ORIGIN_SERVER_RAW_OPEN);
00572   return s->cdn_saved_next_action;
00573 }
00574 
00575 
00576 /*****************************************************************************
00577  *****************************************************************************
00578  ****                                                                     ****
00579  ****                 HttpTransact State Machine Handlers                 ****
00580  ****                                                                     ****
00581  **** What follow from here on are the state machine handlers - the code  ****
00582  **** which is called from HttpSM::set_next_state to specify ****
00583  **** what action the state machine needs to execute next. These ftns     ****
00584  **** take as input just the state and set the next_action variable.      ****
00585  *****************************************************************************
00586  *****************************************************************************/
00587 void
00588 HttpTransact::BadRequest(State* s)
00589 {
00590   DebugTxn("http_trans", "[BadRequest]" "parser marked request bad");
00591   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
00592   build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid HTTP Request", "request#syntax_error", NULL);
00593   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL);
00594 }
00595 
00596 void
00597 HttpTransact::HandleBlindTunnel(State* s)
00598 {
00599   DebugTxn("http_trans", "[HttpTransact::HandleBlindTunnel]");
00600 
00601   // We've received a request on a port which we blind forward
00602   //  For logging purposes we create a fake request
00603   s->hdr_info.client_request.create(HTTP_TYPE_REQUEST);
00604   s->hdr_info.client_request.method_set(HTTP_METHOD_CONNECT, HTTP_LEN_CONNECT);
00605   URL u;
00606   s->hdr_info.client_request.url_create(&u);
00607   u.scheme_set(URL_SCHEME_TUNNEL, URL_LEN_TUNNEL);
00608   s->hdr_info.client_request.url_set(&u);
00609 
00610   // We set the version to 0.9 because once we know where we are going
00611   //   this blind ssl tunnel is indistinguishable from a "CONNECT 0.9"
00612   //   except for the need to suppression error messages
00613   HTTPVersion ver(0, 9);
00614   s->hdr_info.client_request.version_set(ver);
00615 
00616   char new_host[INET6_ADDRSTRLEN];
00617   ats_ip_ntop(s->state_machine->ua_session->get_netvc()->get_local_addr(), new_host, sizeof(new_host));
00618 
00619   s->hdr_info.client_request.url_get()->host_set(new_host, strlen(new_host));
00620   s->hdr_info.client_request.url_get()->port_set(s->state_machine->ua_session->get_netvc()->get_local_port());
00621 
00622   // Initialize the state vars necessary to sending error responses
00623   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
00624 
00625   if (is_debug_tag_set("http_trans")) {
00626     int host_len;
00627     const char *host = s->hdr_info.client_request.url_get()->host_get(&host_len);
00628     DebugTxn("http_trans", "[HandleBlindTunnel] destination set to %.*s:%d", host_len, host,
00629           s->hdr_info.client_request.url_get()->port_get());
00630   }
00631   // Now we need to run the url remapping engine to find the where
00632   //   this request really goes since we were sent was bound for
00633   //   machine we are running on
00634 
00635   // Do request_url_remap only if url_remap_mode != URL_REMAP_FOR_OS.
00636   bool url_remap_success = false;
00637   char *remap_redirect = NULL;
00638 
00639   if (s->transparent_passthrough) {
00640     url_remap_success = true;
00641   } else if (url_remap_mode == URL_REMAP_DEFAULT || url_remap_mode == URL_REMAP_ALL) {
00642     // TODO: take a look at this
00643     // This probably doesn't work properly, since request_url_remap() is broken.  
00644     url_remap_success = request_url_remap(s, &s->hdr_info.client_request, &remap_redirect);
00645   }
00646   // We must have mapping or we will self loop since the this
00647   //    request was addressed to us to begin with.  Remap directs
00648   //    are something used in the normal reverse proxy and if we
00649   //    get them here they indicate a very bad misconfiguration!
00650   if (url_remap_success == false || remap_redirect != NULL) {
00651     // The error message we send back will be suppressed so
00652     //  the only important thing in selecting the error is what
00653     //  status code it gets logged as
00654     build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Port Forwarding Error", "default", NULL);
00655 
00656     int host_len;
00657     const char *host = s->hdr_info.client_request.url_get()->host_get(&host_len);
00658 
00659     Log::error("Forwarded port error: request with destination %.*s:%d "
00660                "does not have a mapping", host_len, host, s->hdr_info.client_request.url_get()->port_get());
00661 
00662     TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL);
00663   }
00664   // Set the mode to tunnel so that we don't lookup the cache
00665   s->current.mode = TUNNELLING_PROXY;
00666 
00667   // Let the request work it's way through the code and
00668   //  we grab it again after the raw connection has been opened
00669   HandleRequest(s);
00670 }
00671 
00672 bool
00673 HttpTransact::perform_accept_encoding_filtering(State* s)
00674 {
00675   HttpUserAgent_RegxEntry *uae;
00676   HTTPHdr *client_request;
00677   MIMEField *accept_field;
00678   MIMEField *usragent_field;
00679   char tmp_ua_buf[1024], *c;
00680   char const *u_agent = NULL;
00681   int u_agent_len = 0;
00682   bool retcode = false;
00683   bool ua_match = false;
00684 
00685   client_request = &s->hdr_info.client_request;
00686 
00687   // Make sense to check Accept-Encoding if UserAgent is present (and matches)
00688   if ((usragent_field = client_request->field_find(MIME_FIELD_USER_AGENT, MIME_LEN_USER_AGENT)) != 0 &&
00689       (u_agent = usragent_field->value_get(&u_agent_len)) != 0 && u_agent_len > 0) {
00690     if (u_agent_len >= (int) sizeof(tmp_ua_buf))
00691       u_agent_len = (int) (sizeof(tmp_ua_buf) - 1);
00692     memcpy(tmp_ua_buf, u_agent, u_agent_len);
00693     tmp_ua_buf[u_agent_len] = '\0';
00694 
00695     // TODO: Do we really want to do these hardcoded checks still?
00696     // Check hardcoded case MSIE>6 & Mozilla>4
00697     if ((c = strstr(tmp_ua_buf, "MSIE")) != NULL) {
00698       if (c[5] >= '7' && c[5] <= '9')
00699         return false;           // Don't change anything for IE > 6
00700       ua_match = true;
00701     } else if (!strncasecmp(tmp_ua_buf, "mozilla", 7)) {
00702       if (tmp_ua_buf[8] >= '5' && tmp_ua_buf[8] <= '9')
00703         return false;           // Don't change anything for Mozilla > 4
00704       ua_match = true;
00705     }
00706 
00707     // Check custom filters
00708     if (!ua_match && HttpConfig::user_agent_list) {
00709       for (uae = HttpConfig::user_agent_list; uae && !ua_match; uae = uae->next) {
00710         switch (uae->stype) {
00711         case HttpUserAgent_RegxEntry::STRTYPE_SUBSTR_CASE:     /* .substring, .string */
00712           if (u_agent_len >= uae->user_agent_str_size &&
00713               !memcmp(tmp_ua_buf, uae->user_agent_str, uae->user_agent_str_size))
00714             ua_match = true;
00715           break;
00716         case HttpUserAgent_RegxEntry::STRTYPE_SUBSTR_NCASE:    /* .substring_ncase, .string_ncase */
00717           if (u_agent_len >= uae->user_agent_str_size &&
00718               !strncasecmp(uae->user_agent_str, tmp_ua_buf, uae->user_agent_str_size))
00719             ua_match = true;
00720           break;
00721         case HttpUserAgent_RegxEntry::STRTYPE_REGEXP:  /* .regexp POSIX regular expression */
00722           if (uae->regx_valid && !pcre_exec(uae->regx, NULL, tmp_ua_buf, u_agent_len, 0, 0, NULL, 0))
00723             ua_match = true;
00724           break;
00725         default:               /* unknown type in the structure - bad initialization - impossible bug! */
00726           /* I can use ink_error() here since we should shutdown TS immediately */
00727           ink_error
00728             ("[HttpTransact::perform_accept_encoding_filtering] - get unknown User-Agent string type - bad initialization");
00729         };
00730       }
00731     }
00732 
00733     /* If we have correct User-Agent header ....
00734        Just set Accept-Encoding: identity or .... do nothing because
00735        "If no Accept-Encoding field is present in a request, the server MAY assume that the client
00736        will accept any content coding. In this case, if "identity" is one of the available content-codings,
00737        then the server SHOULD use the "identity" content-coding, unless it has additional information that
00738        a different content-coding is meaningful to the client." */
00739     if (ua_match) {
00740       DebugTxn("http_trans", "HttpTransact::ModifyRequest, insert identity Accept-Encoding");
00741       accept_field = client_request->field_find(MIME_FIELD_ACCEPT_ENCODING, MIME_LEN_ACCEPT_ENCODING);
00742       if (!accept_field) {
00743         accept_field = client_request->field_create(MIME_FIELD_ACCEPT_ENCODING, MIME_LEN_ACCEPT_ENCODING);
00744         if (accept_field)
00745           client_request->field_attach(accept_field);
00746       }
00747       if (accept_field) {
00748         client_request->field_value_set(accept_field, HTTP_VALUE_IDENTITY, HTTP_LEN_IDENTITY);
00749       }
00750     }
00751     retcode = true;
00752   }                             // end of 'user-agent'
00753   return retcode;
00754 }
00755 
00756 void
00757 HttpTransact::StartRemapRequest(State* s)
00758 {
00759   
00760   if (s->api_skip_all_remapping) {
00761     Debug ("http_trans", "API request to skip remapping");
00762 
00763     if (s->is_upgrade_request && s->post_remap_upgrade_return_point) {
00764       TRANSACT_RETURN(SM_ACTION_POST_REMAP_SKIP, s->post_remap_upgrade_return_point);
00765     }
00766 
00767     TRANSACT_RETURN(SM_ACTION_POST_REMAP_SKIP, HttpTransact::HandleRequest);
00768   }
00769   
00770   DebugTxn("http_trans", "START HttpTransact::StartRemapRequest");
00771 
00772   /**
00773          * Check for URL remappings before checking request
00774          * validity or initializing state variables since
00775          * the remappings can insert or change the destination
00776          * host, port and protocol.
00777         **/
00778 
00779   HTTPHdr *incoming_request = &s->hdr_info.client_request;
00780   URL *url = incoming_request->url_get();
00781   int host_len, path_len;
00782   const char *host = url->host_get(&host_len);
00783   const char *path = url->path_get(&path_len);
00784   const int port = url->port_get();
00785 
00786   const char syntxt[] = "synthetic.txt";
00787 
00788   s->cop_test_page = (ptr_len_cmp(host, host_len, local_host_ip_str, sizeof(local_host_ip_str) - 1) == 0) &&
00789     (ptr_len_cmp(path, path_len, syntxt, sizeof(syntxt) - 1) == 0) &&
00790     port == s->http_config_param->autoconf_port &&
00791     s->method == HTTP_WKSIDX_GET &&
00792     s->orig_scheme == URL_WKSIDX_HTTP &&
00793     (!s->http_config_param->autoconf_localhost_only || ats_ip4_addr_cast(&s->client_info.addr.sa) == htonl(INADDR_LOOPBACK));
00794 
00795   //////////////////////////////////////////////////////////////////
00796   // FIX: this logic seems awfully convoluted and hard to follow; //
00797   //      seems like we could come up with a more elegant and     //
00798   //      comprehensible design that generalized things           //
00799   //////////////////////////////////////////////////////////////////
00800 
00801   /////////////////////////////////////////////////////////////////
00802   // run the remap url-rewriting engine:                         //
00803   //                                                             //
00804   // * the variable <url_remap_success> is set true if           //
00805   //   the url was rewritten                                     //
00806   //                                                             //
00807   // * the variable <remap_redirect> is set to non-NULL if there //
00808   //   is a URL provided that the proxy is supposed to redirect  //
00809   //   requesters of a particular URL to.                        //
00810   /////////////////////////////////////////////////////////////////
00811 
00812   if (is_debug_tag_set("http_chdr_describe") || is_debug_tag_set("http_trans")) {
00813     DebugTxn("http_trans", "Before Remapping:");
00814     obj_describe(s->hdr_info.client_request.m_http, 1);
00815   }
00816 
00817   if (url_remap_mode == URL_REMAP_DEFAULT || url_remap_mode == URL_REMAP_ALL) {
00818     if (s->http_config_param->referer_filter_enabled) {
00819       s->filter_mask = URL_REMAP_FILTER_REFERER;
00820       if (s->http_config_param->referer_format_redirect)
00821         s->filter_mask |= URL_REMAP_FILTER_REDIRECT_FMT;
00822     }
00823   }
00824 
00825   DebugTxn("http_trans", "END HttpTransact::StartRemapRequest");
00826   TRANSACT_RETURN(SM_ACTION_API_PRE_REMAP, HttpTransact::PerformRemap);
00827 }
00828 
00829 void HttpTransact::PerformRemap(State *s)
00830 {
00831   DebugTxn("http_trans","Inside PerformRemap");
00832   TRANSACT_RETURN(SM_ACTION_REMAP_REQUEST, HttpTransact::EndRemapRequest);
00833 }
00834 
00835 void
00836 HttpTransact::EndRemapRequest(State* s)
00837 {
00838   DebugTxn("http_trans", "START HttpTransact::EndRemapRequest");
00839 
00840   HTTPHdr *incoming_request = &s->hdr_info.client_request;
00841   int method = incoming_request->method_get_wksidx();
00842   int host_len;
00843   const char *host = incoming_request->host_get(&host_len);
00844   DebugTxn("http_trans","EndRemapRequest host is %.*s", host_len,host);
00845   
00846   ////////////////////////////////////////////////////////////////
00847   // if we got back a URL to redirect to, vector the user there //
00848   ////////////////////////////////////////////////////////////////
00849   if (s->remap_redirect != NULL) {
00850     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
00851     if (s->http_return_code == HTTP_STATUS_MOVED_PERMANENTLY) {
00852       build_error_response(s, HTTP_STATUS_MOVED_PERMANENTLY, "Redirect", "redirect#moved_permanently", NULL);
00853     } else {
00854       build_error_response(s, HTTP_STATUS_MOVED_TEMPORARILY, "Redirect", "redirect#moved_temporarily", NULL);
00855     }
00856     ats_free(s->remap_redirect);
00857     s->reverse_proxy = false;
00858     goto done;
00859   }
00860   /////////////////////////////////////////////////////
00861   // Quick HTTP filtering (primary key: http method) //
00862   /////////////////////////////////////////////////////
00863   process_quick_http_filter(s, method);
00864   /////////////////////////////////////////////////////////////////////////
00865   // We must close this connection if client_connection_enabled == false //
00866   /////////////////////////////////////////////////////////////////////////
00867   if (!s->client_connection_enabled) {
00868     build_error_response(s, HTTP_STATUS_FORBIDDEN, "Access Denied", "access#denied", NULL);
00869     s->reverse_proxy = false;
00870     goto done;
00871   }
00872   /////////////////////////////////////////////////////////////////
00873   // Check if remap plugin set HTTP return code and return body  //
00874   /////////////////////////////////////////////////////////////////
00875   if (s->http_return_code != HTTP_STATUS_NONE) {
00876     build_error_response(s, s->http_return_code, NULL, NULL,
00877                          s->internal_msg_buffer_size ? s->internal_msg_buffer : NULL);
00878     s->reverse_proxy = false;
00879     goto done;
00880   }
00881 
00882   ///////////////////////////////////////////////////////////////
00883   // if no mapping was found, handle the cases where:          //
00884   //                                                           //
00885   // (1) reverse proxy is on, and no URL host (server request) //
00886   // (2) no mappings are found, but mappings strictly required //
00887   ///////////////////////////////////////////////////////////////
00888 
00889   if (!s->url_remap_success) {
00890     /**
00891      * It's better to test redirect rules just after url_remap failed
00892      * Or those successfully remapped rules might be redirected
00893      **/
00894     if (handleIfRedirect(s)) {
00895       DebugTxn("http_trans", "END HttpTransact::RemapRequest");
00896       TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, NULL);
00897     }
00898 
00899     /////////////////////////////////////////////////////////
00900     // check for: (1) reverse proxy is on, and no URL host //
00901     /////////////////////////////////////////////////////////
00902     if (s->http_config_param->reverse_proxy_enabled
00903         && !s->client_info.is_transparent
00904         && !incoming_request->is_target_in_url()) {
00905       /////////////////////////////////////////////////////////
00906       // the url mapping failed, reverse proxy was enabled,
00907       // and the request contains no host:
00908       //
00909       // * if there is an explanatory redirect, send there.
00910       // * if there was no host, send "no host" error.
00911       // * if there was a host, say "not found".
00912       /////////////////////////////////////////////////////////
00913 
00914       char *redirect_url = s->http_config_param->reverse_proxy_no_host_redirect;
00915       int redirect_url_len = s->http_config_param->reverse_proxy_no_host_redirect_len;
00916 
00917       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
00918       if (redirect_url) {       /* there is a redirect url */
00919         build_error_response(s, HTTP_STATUS_MOVED_TEMPORARILY, "Redirect For Explanation", "request#no_host", NULL);
00920         s->hdr_info.client_response.value_set(MIME_FIELD_LOCATION, MIME_LEN_LOCATION, redirect_url, redirect_url_len);
00921       // socket when there is no host. Need to handle DNS failure elsewhere.
00922       } else if (host == NULL) {    /* no host */
00923         build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Host Header Required", "request#no_host", NULL);
00924       } else {
00925         build_error_response(s, HTTP_STATUS_NOT_FOUND, "Not Found on Accelerator", "urlrouting#no_mapping", NULL);
00926       }
00927       s->reverse_proxy = false;
00928       goto done;
00929     } else if (s->http_config_param->url_remap_required && !s->cop_test_page) {
00930       ///////////////////////////////////////////////////////
00931       // the url mapping failed, but mappings are strictly //
00932       // required (except for synthetic cop accesses), so  //
00933       // return an error message.                          //
00934       ///////////////////////////////////////////////////////
00935 
00936       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
00937       build_error_response(s, HTTP_STATUS_NOT_FOUND, "Not Found", "urlrouting#no_mapping", NULL);
00938 
00939       s->reverse_proxy = false;
00940       goto done;
00941     }
00942   } else {
00943     if (s->http_config_param->reverse_proxy_enabled) {
00944       s->req_flavor = REQ_FLAVOR_REVPROXY;
00945     }
00946   }
00947   s->reverse_proxy = true;
00948   s->server_info.is_transparent = s->state_machine->ua_session ? s->state_machine->ua_session->f_outbound_transparent : false;
00949 
00950 done:
00951   if (is_debug_tag_set("http_chdr_describe") || is_debug_tag_set("http_trans") || is_debug_tag_set("url_rewrite")) {
00952     DebugTxn("http_trans", "After Remapping:");
00953     obj_describe(s->hdr_info.client_request.m_http, 1);
00954   }
00955 
00956   /*
00957     if s->reverse_proxy == false, we can assume remapping failed in some way
00958       -however-
00959     If an API setup a tunnel to fake the origin or proxy's response we will 
00960     continue to handle the request (as this was likely the plugin author's intent)
00961     
00962     otherwise, 502/404 the request right now. /eric
00963   */
00964   if (!s->reverse_proxy && s->state_machine->plugin_tunnel_type == HTTP_NO_PLUGIN_TUNNEL) {
00965     // TS-2879: Let's initialize the state variables so the connection can be kept alive.
00966     initialize_state_variables_from_request(s, &s->hdr_info.client_request);
00967     DebugTxn("http_trans", "END HttpTransact::EndRemapRequest");
00968     HTTP_INCREMENT_TRANS_STAT(http_invalid_client_requests_stat);
00969     TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL);
00970   } else {
00971     s->hdr_info.client_response.clear(); //anything previously set is invalid from this point forward
00972     DebugTxn("http_trans", "END HttpTransact::EndRemapRequest");
00973 
00974     if (s->is_upgrade_request && s->post_remap_upgrade_return_point) {
00975       TRANSACT_RETURN(SM_ACTION_API_POST_REMAP, s->post_remap_upgrade_return_point);
00976     }
00977 
00978     TRANSACT_RETURN(SM_ACTION_API_POST_REMAP, HttpTransact::HandleRequest);
00979   }
00980 
00981   ink_assert(!"not reached");
00982 }
00983 
00984 bool HttpTransact::handle_upgrade_request(State *s) {
00985   // Quickest way to determine that this is defintely not an upgrade.
00986   /* RFC 6455 The method of the request MUST be GET, and the HTTP version MUST
00987         be at least 1.1. */
00988   if (!s->hdr_info.client_request.presence(MIME_PRESENCE_UPGRADE) ||
00989       !s->hdr_info.client_request.presence(MIME_PRESENCE_CONNECTION) ||
00990       s->method != HTTP_WKSIDX_GET ||
00991       s->hdr_info.client_request.version_get() < HTTPVersion(1, 1)) {
00992     return false;
00993   }
00994 
00995   MIMEField *upgrade_hdr = s->hdr_info.client_request.field_find(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
00996   MIMEField *connection_hdr = s->hdr_info.client_request.field_find(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
00997 
00998   StrList connection_hdr_vals;
00999   const char *upgrade_hdr_val = NULL;
01000   int upgrade_hdr_val_len = 0;
01001 
01002   if ( !upgrade_hdr ||
01003        !connection_hdr ||
01004        connection_hdr->value_get_comma_list(&connection_hdr_vals) == 0 ||
01005        (upgrade_hdr_val = upgrade_hdr->value_get(&upgrade_hdr_val_len)) == NULL) {
01006       DebugTxn("http_trans_upgrade", "Transaction wasn't a valid upgrade request, proceeding as a normal HTTP request.");
01007       return false;
01008   }
01009 
01010   /*
01011    * In order for this request to be treated as a normal upgrade request we must have a Connection: Upgrade header
01012    * and a Upgrade: header, with a non-empty value, otherwise we just assume it's not an Upgrade Request, after
01013    * we've verified that, we will try to match this upgrade to a known upgrade type such as Websockets.
01014    */
01015   bool connection_contains_upgrade = false;
01016   // Next, let's validate that the Connection header contains an Upgrade key
01017   for(int i = 0; i < connection_hdr_vals.count; ++i) {
01018     Str *val = connection_hdr_vals.get_idx(i);
01019     if (ptr_len_casecmp(val->str, val->len, MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE) == 0) {
01020       connection_contains_upgrade = true;
01021       break;
01022     }
01023   }
01024 
01025   if (!connection_contains_upgrade) {
01026     DebugTxn("http_trans_upgrade", "Transaction wasn't a valid upgrade request, proceeding as a normal HTTP request, missing Connection upgrade header.");
01027     return false;
01028   }
01029 
01030 
01031   // Mark this request as an upgrade request.
01032   s->is_upgrade_request = true;
01033 
01034   /*
01035      RFC 6455
01036      The request MUST contain an |Upgrade| header field whose value
01037         MUST include the "websocket" keyword.
01038      The request MUST contain a |Connection| header field whose value
01039         MUST include the "Upgrade" token. // Checked Above
01040      The request MUST include a header field with the name
01041         |Sec-WebSocket-Key|.
01042      The request MUST include a header field with the name
01043         |Sec-WebSocket-Version|.  The value of this header field MUST be
01044         13.
01045    */
01046   if (hdrtoken_tokenize(upgrade_hdr_val, upgrade_hdr_val_len, &s->upgrade_token_wks) >= 0) {
01047     if (s->upgrade_token_wks == MIME_VALUE_WEBSOCKET) {
01048       MIMEField *sec_websocket_key = s->hdr_info.client_request.field_find(MIME_FIELD_SEC_WEBSOCKET_KEY, MIME_LEN_SEC_WEBSOCKET_KEY);
01049       MIMEField *sec_websocket_ver = s->hdr_info.client_request.field_find(MIME_FIELD_SEC_WEBSOCKET_VERSION, MIME_LEN_SEC_WEBSOCKET_VERSION);
01050 
01051       if (sec_websocket_key &&
01052           sec_websocket_ver &&
01053           sec_websocket_ver->value_get_int() == 13) {
01054         DebugTxn("http_trans_upgrade", "Transaction wants upgrade to websockets");
01055         handle_websocket_upgrade_pre_remap(s);
01056         return true;
01057       } else {
01058         DebugTxn("http_trans_upgrade", "Unable to upgrade connection to websockets, invalid headers (RFC 6455).");
01059       }
01060     }
01061   } else {
01062     DebugTxn("http_trans_upgrade", "Transaction requested upgrade for unknown protocol: %s", upgrade_hdr_val);
01063   }
01064 
01065   build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid Upgrade Request", "request#syntax_error", NULL);
01066 
01067   // we want our modify_request method to just return while we fail out from here.
01068   // this seems like the preferred option because the user wanted to do an upgrade but sent a bad protocol.
01069   TRANSACT_RETURN_VAL(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL, true);
01070 }
01071 
01072 void
01073 HttpTransact::handle_websocket_upgrade_pre_remap(State *s) {
01074   DebugTxn("http_trans_websocket_upgrade_pre_remap", "Prepping transaction before remap.");
01075 
01076   /*
01077    * We will use this opportunity to set everything up so that during the remap stage we can deal with
01078    * ws:// and wss:// remap rules, and then we will take over again post remap.
01079    */
01080   s->is_websocket = true;
01081   s->post_remap_upgrade_return_point = HttpTransact::handle_websocket_upgrade_post_remap;
01082 
01083   /* let's modify the url scheme to be wss or ws, so remapping will happen as expected */
01084   URL *url = s->hdr_info.client_request.url_get();
01085   if (url->scheme_get_wksidx() == URL_WKSIDX_HTTP) {
01086     DebugTxn("http_trans_websocket_upgrade_pre_remap", "Changing scheme to WS for remapping.");
01087     url->scheme_set(URL_SCHEME_WS, URL_LEN_WS);
01088   } else if (url->scheme_get_wksidx() == URL_WKSIDX_HTTPS) {
01089     DebugTxn("http_trans_websocket_upgrade_pre_remap", "Changing scheme to WSS for remapping.");
01090     url->scheme_set(URL_SCHEME_WSS, URL_LEN_WSS);
01091   } else {
01092     DebugTxn("http_trans_websocket_upgrade_pre_remap", "Invalid scheme for websocket upgrade");
01093     build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid Upgrade Request", "request#syntax_error", NULL);
01094     TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL);
01095   }
01096 
01097   TRANSACT_RETURN(SM_ACTION_API_READ_REQUEST_HDR, HttpTransact::StartRemapRequest);
01098 }
01099 
01100 void
01101 HttpTransact::handle_websocket_upgrade_post_remap(State *s) {
01102   DebugTxn("http_trans_websocket_upgrade_post_remap", "Remap is complete, start websocket upgrade");
01103 
01104   TRANSACT_RETURN(SM_ACTION_API_POST_REMAP, HttpTransact::handle_websocket_connection);
01105 }
01106 
01107 void
01108 HttpTransact::handle_websocket_connection(State *s) {
01109   DebugTxn("http_trans_websocket", "START handle_websocket_connection");
01110 
01111   HandleRequest(s);
01112 }
01113 
01114 
01115 static bool mimefield_value_equal(MIMEField *field, const char *value, const int value_len)
01116 {
01117   if (field != NULL) {
01118     int field_value_len = 0;
01119     const char *field_value = field->value_get(&field_value_len);
01120     if (field_value != NULL) {
01121       if (field_value_len == value_len) {
01122         return !strncasecmp(field_value, value, value_len);
01123       }
01124     }
01125   }
01126   return false;
01127 }
01128 
01129 void
01130 HttpTransact::ModifyRequest(State* s)
01131 {
01132   int scheme, hostname_len;
01133   const char *hostname;
01134   HTTPHdr& request = s->hdr_info.client_request;
01135 
01136   DebugTxn("http_trans", "START HttpTransact::ModifyRequest");
01137 
01138   // Initialize the state vars necessary to sending error responses
01139   bootstrap_state_variables_from_request(s, &request);
01140 
01141   ////////////////////////////////////////////////
01142   // If there is no scheme default to http      //
01143   ////////////////////////////////////////////////
01144   URL *url = request.url_get();
01145 
01146   s->orig_scheme = (scheme = url->scheme_get_wksidx());
01147 
01148   s->method = request.method_get_wksidx();
01149   if (scheme < 0 && s->method != HTTP_WKSIDX_CONNECT) {
01150     if (s->client_info.port_attribute == HttpProxyPort::TRANSPORT_SSL) {
01151       url->scheme_set(URL_SCHEME_HTTPS, URL_LEN_HTTPS);
01152       s->orig_scheme = URL_WKSIDX_HTTPS;
01153     } else {
01154       url->scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP);
01155       s->orig_scheme = URL_WKSIDX_HTTP;
01156     }
01157   }
01158 
01159   if (s->method == HTTP_WKSIDX_CONNECT && !request.is_port_in_header())
01160     url->port_set(80);
01161 
01162   // Ugly - this must come after the call to url->scheme_set or
01163   // it can't get the scheme properly and the wrong data is cached.
01164   // The solution should be to move the scheme detecting logic in to
01165   // the header class, rather than doing it in a random bit of
01166   // external code.
01167   hostname = request.host_get(&hostname_len);
01168   if (!request.is_target_in_url()) {
01169     s->hdr_info.client_req_is_server_style = true;
01170   }
01171 
01172   // If the incoming request is proxy-style make sure the Host: header
01173   // matches the incoming request URL. The exception is if we have
01174   // Max-Forwards set to 0 in the request
01175   int max_forwards = -1;  // -1 is a valid value meaning that it didn't find the header
01176   if (request.presence(MIME_PRESENCE_MAX_FORWARDS)) {
01177     max_forwards = request.get_max_forwards();
01178   }
01179 
01180   if ((max_forwards != 0) && !s->hdr_info.client_req_is_server_style && s->method != HTTP_WKSIDX_CONNECT) {
01181     MIMEField *host_field = request.field_find(MIME_FIELD_HOST, MIME_LEN_HOST);
01182     int host_val_len = hostname_len;
01183     const char **host_val = &hostname;
01184     int port = url->port_get_raw();
01185     char *buf = NULL;
01186 
01187     // Form the host:port string if not a default port (e.g. 80)
01188     if (port > 0) {
01189       buf = (char *)alloca(host_val_len + 15);
01190       memcpy(buf, hostname, host_val_len);
01191       host_val_len += snprintf(buf + host_val_len, 15, ":%d", port);
01192       host_val = (const char**)(&buf);
01193     }
01194 
01195     if (mimefield_value_equal(host_field, *host_val, host_val_len) == false) {
01196       if (!host_field) { // Assure we have a Host field, before setting it
01197         host_field = request.field_create(MIME_FIELD_HOST, MIME_LEN_HOST);
01198         request.field_attach(host_field);
01199       }
01200       request.field_value_set(host_field, *host_val, host_val_len);
01201       request.mark_target_dirty();
01202     }
01203   }
01204 
01205   if (s->txn_conf->normalize_ae_gzip) {
01206     // if enabled, force Accept-Encoding header to gzip or no header
01207     MIMEField *ae_field = s->hdr_info.client_request.field_find(MIME_FIELD_ACCEPT_ENCODING, MIME_LEN_ACCEPT_ENCODING);
01208 
01209     if (ae_field) {
01210       if (HttpTransactCache::match_gzip(ae_field) == GZIP) {
01211         s->hdr_info.client_request.field_value_set(ae_field, "gzip", 4);
01212         DebugTxn("http_trans", "[ModifyRequest] normalized Accept-Encoding to gzip");
01213       } else {
01214         s->hdr_info.client_request.field_delete(ae_field);
01215         DebugTxn("http_trans", "[ModifyRequest] removed non-gzip Accept-Encoding");
01216       }
01217     }
01218   }
01219 
01220   /////////////////////////////////////////////////////////
01221   // Modify Accept-Encoding for several specific User-Agent
01222   /////////////////////////////////////////////////////////
01223   if (s->txn_conf->accept_encoding_filter_enabled) {
01224     perform_accept_encoding_filtering(s);
01225   }
01226 
01227   DebugTxn("http_trans", "END HttpTransact::ModifyRequest");
01228 
01229   DebugTxn("http_trans", "Checking if transaction wants to upgrade");
01230   if(handle_upgrade_request(s)) {
01231     // everything should be handled by the upgrade handler.
01232     DebugTxn("http_trans", "Transaction will be upgraded by the appropriate upgrade handler.");
01233     return;
01234   }
01235 
01236   TRANSACT_RETURN(SM_ACTION_API_READ_REQUEST_HDR, HttpTransact::StartRemapRequest);
01237 }
01238 
01239 // This function is supposed to figure out if this transaction is
01240 // susceptible to a redirection as specified by remap.config
01241 bool
01242 HttpTransact::handleIfRedirect(State *s)
01243 {
01244   int answer;
01245   URL redirect_url;
01246 
01247   answer = request_url_remap_redirect(&s->hdr_info.client_request, &redirect_url);
01248   if ((answer == PERMANENT_REDIRECT) || (answer == TEMPORARY_REDIRECT)) {
01249     int remap_redirect_len;
01250 
01251     s->remap_redirect = redirect_url.string_get(&s->arena, &remap_redirect_len);
01252     redirect_url.destroy();
01253     if (answer == TEMPORARY_REDIRECT) {
01254       if ((s->client_info).http_version.m_version == HTTP_VERSION(1, 1)) {
01255         build_error_response(s, HTTP_STATUS_TEMPORARY_REDIRECT, "Redirect", "redirect#moved_temporarily", NULL);
01256       } else {
01257         build_error_response(s, HTTP_STATUS_MOVED_TEMPORARILY, "Redirect", "redirect#moved_temporarily", NULL);
01258       }
01259     } else {
01260       build_error_response(s, HTTP_STATUS_MOVED_PERMANENTLY, "Redirect", "redirect#moved_permanently", NULL);
01261     }
01262     s->arena.str_free(s->remap_redirect);
01263     s->remap_redirect = NULL;
01264     return true;
01265   }
01266 
01267   return false;
01268 }
01269 
01270 void
01271 HttpTransact::HandleRequest(State* s)
01272 {
01273   DebugTxn("http_trans", "START HttpTransact::HandleRequest");
01274 
01275   ink_assert(!s->hdr_info.server_request.valid());
01276 
01277   HTTP_INCREMENT_TRANS_STAT(http_incoming_requests_stat);
01278 
01279   if (s->client_info.port_attribute == HttpProxyPort::TRANSPORT_SSL) {
01280     HTTP_INCREMENT_TRANS_STAT(https_incoming_requests_stat);
01281   }
01282 
01283   if (s->api_release_server_session == true) {
01284     s->api_release_server_session = false;
01285   }
01286   ///////////////////////////////////////////////
01287   // if request is bad, return error response  //
01288   ///////////////////////////////////////////////
01289 
01290   if (!(is_request_valid(s, &s->hdr_info.client_request))) {
01291     HTTP_INCREMENT_TRANS_STAT(http_invalid_client_requests_stat);
01292     DebugTxn("http_seq", "[HttpTransact::HandleRequest] request invalid.");
01293     s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
01294     //  s->next_action = HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
01295     return;
01296   }
01297   DebugTxn("http_seq", "[HttpTransact::HandleRequest] request valid.");
01298 
01299   if (is_debug_tag_set("http_chdr_describe")) {
01300     obj_describe(s->hdr_info.client_request.m_http, 1);
01301   }
01302 
01303   // at this point we are guaranteed that the request is good and acceptable.
01304   // initialize some state variables from the request (client version,
01305   // client keep-alive, cache action, etc.
01306   initialize_state_variables_from_request(s, &s->hdr_info.client_request);
01307 
01308   // Cache lookup or not will be decided later at DecideCacheLookup().
01309   // Before it's decided to do a cache lookup,
01310   // assume no cache lookup and using proxy (not tunneling)
01311   s->cache_info.action = CACHE_DO_NO_ACTION;
01312   s->current.mode = GENERIC_PROXY;
01313 
01314   // initialize the cache_control structure read from cache.config
01315   update_cache_control_information_from_config(s);
01316 
01317   // We still need to decide whether or not to do a cache lookup since
01318   // the scheduled update code depends on this info.
01319   if (is_request_cache_lookupable(s))
01320     s->cache_info.action = CACHE_DO_LOOKUP;
01321 
01322   // If the hostname is "$internal$" then this is a request for
01323   // internal proxy information.
01324   if (handle_internal_request(s, &s->hdr_info.client_request)) {
01325     TRANSACT_RETURN(SM_ACTION_INTERNAL_REQUEST, NULL);
01326   }
01327 
01328   // this needs to be called after initializing state variables from request
01329   // it adds the client-ip to the incoming client request.
01330 
01331   if (!s->cop_test_page)
01332     DUMP_HEADER("http_hdrs", &s->hdr_info.client_request, s->state_machine_id, "Incoming Request");
01333 
01334   if (s->state_machine->plugin_tunnel_type == HTTP_PLUGIN_AS_INTERCEPT) {
01335     setup_plugin_request_intercept(s);
01336     return;
01337   }
01338 
01339   // if ip in url or cop test page, not do srv lookup.
01340   if (s->srv_lookup) {
01341     if (s->cop_test_page)
01342       s->srv_lookup = false;
01343     else {
01344       IpEndpoint addr;
01345       ats_ip_pton(s->server_info.name, &addr);
01346       s->srv_lookup = !ats_is_ip(&addr);
01347     }
01348   }
01349 
01350   // if the request is a trace or options request, decrement the
01351   // max-forwards value. if the incoming max-forwards value was 0,
01352   // then we have to return a response to the client with the
01353   // appropriate action for trace/option. in this case this routine
01354   // is responsible for building the response.
01355   if (handle_trace_and_options_requests(s, &s->hdr_info.client_request)) {
01356     TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, NULL);
01357   }
01358 
01359   if (s->http_config_param->no_dns_forward_to_parent
01360     && s->scheme != URL_WKSIDX_HTTPS
01361     && strcmp(s->server_info.name, "127.0.0.1") != 0
01362   ) {
01363     // for HTTPS requests, we must go directly to the
01364     // origin server. Ignore the no_dns_just_forward_to_parent setting.
01365     // we need to see if the hostname is an
01366     //   ip address since the parent selection code result
01367     //   could change as a result of this ip address    
01368     IpEndpoint addr;
01369     ats_ip_pton(s->server_info.name, &addr);
01370     if (ats_is_ip(&addr)) {
01371       ats_ip_copy(&s->request_data.dest_ip, &addr);
01372     }
01373 
01374     if (s->parent_params->parentExists(&s->request_data)) {
01375       // If the proxy is behind and firewall and there is no
01376       //  DNS service available, we just want to forward the request
01377       //  the parent proxy.  In this case, we never find out the
01378       //  origin server's ip.  So just skip past OSDNS
01379       ats_ip_invalidate(&s->server_info.addr);
01380       StartAccessControl(s);
01381       return;
01382     } else if (s->http_config_param->no_origin_server_dns) {
01383       build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Next Hop Connection Failed", "connect#failed_connect", NULL);
01384 
01385       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL);
01386     }
01387   }
01388 
01389   // Added to skip the dns if the document is in the cache.
01390   // DNS is requested before cache lookup only if there are rules in cache.config , parent.config or
01391   // if the newly added varible doc_in_cache_skip_dns is not enabled
01392   if (s->dns_info.lookup_name[0] <= '9' &&
01393       s->dns_info.lookup_name[0] >= '0' &&
01394       (!s->state_machine->enable_redirection || !s->redirect_info.redirect_in_process) &&
01395       s->parent_params->ParentTable->hostMatch) {
01396     s->force_dns = 1;
01397   }
01398   //YTS Team, yamsat Plugin
01399   //Doing a Cache Lookup in case of a Redirection to ensure that
01400   //the newly requested object is not in the CACHE
01401   if (s->txn_conf->cache_http && s->redirect_info.redirect_in_process && s->state_machine->enable_redirection) {
01402     TRANSACT_RETURN(SM_ACTION_CACHE_LOOKUP, NULL);
01403   }
01404 
01405 
01406   if (s->force_dns) {
01407     TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, OSDNSLookup);   // After handling the request, DNS is done.
01408   } else {
01409     // After the requested is properly handled No need of requesting the DNS directly check the ACLs
01410     // if the request is authorized
01411     StartAccessControl(s);
01412   }
01413 }
01414 
01415 void
01416 HttpTransact::setup_plugin_request_intercept(State* s)
01417 {
01418   ink_assert(s->state_machine->plugin_tunnel != NULL);
01419 
01420   // Plugin is intercepting the request which means
01421   //  that we don't do dns, cache read or cache write
01422   //
01423   // We just want to write the request straight to the plugin
01424   if (s->cache_info.action != HttpTransact::CACHE_DO_NO_ACTION) {
01425     s->cache_info.action = HttpTransact::CACHE_DO_NO_ACTION;
01426     s->current.mode = TUNNELLING_PROXY;
01427     HTTP_INCREMENT_TRANS_STAT(http_tunnels_stat);
01428   }
01429   // Regardless of the protocol we're gatewaying to
01430   //   we see the scheme as http
01431   s->scheme = s->next_hop_scheme = URL_WKSIDX_HTTP;
01432 
01433   // Set up a "fake" server server entry
01434   update_current_info(&s->current, &s->server_info, HttpTransact::ORIGIN_SERVER, 0);
01435 
01436   // Also "fake" the info we'd normally get from
01437   //   hostDB
01438   s->server_info.http_version.set(1, 0);
01439   s->server_info.keep_alive = HTTP_NO_KEEPALIVE;
01440   s->host_db_info.app.http_data.http_version = HostDBApplicationInfo::HTTP_VERSION_10;
01441   s->host_db_info.app.http_data.pipeline_max = 1;
01442 
01443   // Build the request to the server
01444   build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->client_info.http_version);
01445 
01446   // We don't do keep alive over these impersonated
01447   //  NetVCs so nuke the connection header
01448   s->hdr_info.server_request.field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
01449 
01450   TRANSACT_RETURN(SM_ACTION_ORIGIN_SERVER_OPEN, NULL);
01451 }
01452 
01453 ////////////////////////////////////////////////////////////////////////
01454 // void HttpTransact::HandleApiErrorJump(State* s)
01455 //
01456 //   Called after an API function indicates it wished to send an
01457 //     error to the user agent
01458 ////////////////////////////////////////////////////////////////////////
01459 void
01460 HttpTransact::HandleApiErrorJump(State* s)
01461 {
01462   DebugTxn("http_trans", "[HttpTransact::HandleApiErrorJump]");
01463 
01464   // since the READ_REQUEST_HDR_HOOK is processed before
01465   //   we examine the request, returning TS_EVENT_ERROR will cause
01466   //   the protocol in the via string to be "?"  Set it here
01467   //   since we know it has to be http
01468   // For CONNECT method, next_hop_scheme is NULL
01469   if (s->next_hop_scheme < 0) {
01470     s->next_hop_scheme = URL_WKSIDX_HTTP;
01471   }
01472   // The client response may not be empty in the
01473   // case the txn was reenabled in error by a plugin from hook SEND_RESPONSE_HDR.
01474   // build_response doesn't clean the header. So clean it up before.
01475   // Do fields_clear() instead of clear() to prevent memory leak
01476   // and set the source to internal so chunking is handled correctly
01477   if (s->hdr_info.client_response.valid()) {
01478     s->hdr_info.client_response.fields_clear();
01479     s->source = SOURCE_INTERNAL;
01480   }
01481   
01482   /** 
01483     The API indicated an error. Lets use a >=400 error from the state (if one's set) or fallback to a 
01484     generic HTTP/1.X 500 INKApi Error
01485   **/
01486   if ( s->http_return_code && s->http_return_code >= HTTP_STATUS_BAD_REQUEST ) {
01487     const char *reason = http_hdr_reason_lookup(s->http_return_code);;
01488     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, s->http_return_code,
01489                    reason?reason:"Error" );
01490   }
01491   else {
01492     build_response(s, &s->hdr_info.client_response, s->client_info.http_version,
01493                    HTTP_STATUS_INTERNAL_SERVER_ERROR, "INKApi Error");
01494   }  
01495 
01496   TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, NULL);
01497   return;
01498 }
01499 
01500 ///////////////////////////////////////////////////////////////////////////////
01501 // Name       : PPDNSLookup
01502 // Description: called after DNS lookup of parent proxy name
01503 //
01504 // Details    :
01505 //
01506 // the configuration information gave us the name of the parent proxy
01507 // to send the request to. this function is called after the dns lookup
01508 // for that name. it may fail, in which case we look for the next parent
01509 // proxy to try and if none exist, then go to the origin server.
01510 // if the lookup succeeds, we open a connection to the parent proxy.
01511 //
01512 //
01513 // Possible Next States From Here:
01514 
01515 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
01516 // - HttpTransact::ORIGIN_SERVER_RAW_OPEN;
01517 // - HttpTransact::ORIGIN_SERVER_OPEN;
01518 //
01519 ///////////////////////////////////////////////////////////////////////////////
01520 void
01521 HttpTransact::PPDNSLookup(State* s)
01522 {
01523   ++s->dns_info.attempts;
01524 
01525   DebugTxn("http_trans", "[HttpTransact::PPDNSLookup] This was attempt %d", s->dns_info.attempts);
01526 
01527   ink_assert(s->dns_info.looking_up == PARENT_PROXY);
01528   if (!s->dns_info.lookup_success) {
01529     // DNS lookup of parent failed, find next parent or o.s.
01530     find_server_and_update_current_info(s);
01531     if (!ats_is_ip(&s->current.server->addr)) {
01532       if (s->current.request_to == PARENT_PROXY) {
01533         TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
01534       } else {
01535         // We could be out of parents here if all the parents
01536         // failed DNS lookup
01537         ink_assert(s->current.request_to == HOST_NONE);
01538         handle_parent_died(s);
01539       }
01540       return;
01541     }
01542   } else {
01543     // lookup succeeded, open connection to p.p.
01544     ats_ip_copy(&s->parent_info.addr, s->host_db_info.ip());
01545     get_ka_info_from_host_db(s, &s->parent_info, &s->client_info, &s->host_db_info);
01546     s->parent_info.dns_round_robin = s->host_db_info.round_robin;
01547 
01548     char addrbuf[INET6_ADDRSTRLEN];
01549     DebugTxn("http_trans", "[PPDNSLookup] DNS lookup for sm_id[%" PRId64"] successful IP: %s",
01550           s->state_machine->sm_id, ats_ip_ntop(&s->parent_info.addr.sa, addrbuf, sizeof(addrbuf)));
01551   }
01552 
01553   // Since this function can be called several times while retrying
01554   //  parents, check to see if we've already built our request
01555   if (!s->hdr_info.server_request.valid()) {
01556     build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
01557 
01558     // Take care of deferred (issue revalidate) work in building
01559     //   the request
01560     if (s->pending_work != NULL) {
01561       ink_assert(s->pending_work == issue_revalidate);
01562       (*s->pending_work) (s);
01563       s->pending_work = NULL;
01564     }
01565   }
01566   // what kind of a connection (raw, simple)
01567   s->next_action = how_to_open_connection(s);
01568 }
01569 
01570 ///////////////////////////////////////////////////////////////////////////////
01571 //
01572 // Name       : ReDNSRoundRobin
01573 // Description: Called after we fail to contact part of a round-robin
01574 //              robin server set and we found a another ip address.
01575 //
01576 // Details    :
01577 //
01578 //
01579 //
01580 // Possible Next States From Here:
01581 // - HttpTransact::ORIGIN_SERVER_RAW_OPEN;
01582 // - HttpTransact::ORIGIN_SERVER_OPEN;
01583 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
01584 //
01585 ///////////////////////////////////////////////////////////////////////////////
01586 void
01587 HttpTransact::ReDNSRoundRobin(State* s)
01588 {
01589   ink_assert(s->current.server == &s->server_info);
01590   ink_assert(s->current.server->had_connect_fail());
01591 
01592   if (s->dns_info.lookup_success) {
01593     // We using a new server now so clear the connection
01594     //  failure mark
01595     s->current.server->clear_connect_fail();
01596 
01597     // Our ReDNS of the server succeeded so update the necessary
01598     //  information and try again
01599     ats_ip_copy(&s->server_info.addr, s->host_db_info.ip());
01600     ats_ip_copy(&s->request_data.dest_ip, &s->server_info.addr);
01601     get_ka_info_from_host_db(s, &s->server_info, &s->client_info, &s->host_db_info);
01602     s->server_info.dns_round_robin = s->host_db_info.round_robin;
01603 
01604     char addrbuf[INET6_ADDRSTRLEN];
01605     DebugTxn("http_trans", "[ReDNSRoundRobin] DNS lookup for O.S. successful IP: %s",
01606           ats_ip_ntop(&s->server_info.addr.sa, addrbuf, sizeof(addrbuf)));
01607 
01608     s->next_action = how_to_open_connection(s);
01609   } else {
01610     // Our ReDNS failed so output the DNS failure error message
01611     build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Cannot find server.", "connect#dns_failed", NULL);
01612     s->cache_info.action = CACHE_DO_NO_ACTION;
01613     s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
01614     //  s->next_action = PROXY_INTERNAL_CACHE_NOOP;
01615   }
01616 
01617   return;
01618 }
01619 
01620 
01621 ///////////////////////////////////////////////////////////////////////////////
01622 // Name       : OSDNSLookup
01623 // Description: called after the DNS lookup of origin server name
01624 //
01625 // Details    :
01626 //
01627 // normally called after Start. may be called more than once, however,
01628 // if the dns lookup fails. this may be if the client does not specify
01629 // the full hostname (e.g. just cnn, instead of www.cnn.com), or because
01630 // it was not possible to resolve the name after several attempts.
01631 //
01632 // the next action depends. since this function is normally called after
01633 // a request has come in, which is valid and does not require an immediate
01634 // response, the next action may just be to open a connection to the
01635 // origin server, or a parent proxy, or the next action may be to do a
01636 // cache lookup, or in the event of an error, the next action may be to
01637 // send a response back to the client.
01638 //
01639 //
01640 // Possible Next States From Here:
01641 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
01642 // - HttpTransact::CACHE_LOOKUP;
01643 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
01644 // - HttpTransact::ORIGIN_SERVER_RAW_OPEN;
01645 // - HttpTransact::ORIGIN_SERVER_OPEN;
01646 //
01647 ///////////////////////////////////////////////////////////////////////////////
01648 void
01649 HttpTransact::OSDNSLookup(State* s)
01650 {
01651   static int max_dns_lookups = 3 + s->http_config_param->num_url_expansions;
01652   ++s->dns_info.attempts;
01653 
01654   DebugTxn("http_trans", "[HttpTransact::OSDNSLookup] This was attempt %d", s->dns_info.attempts);
01655 
01656   ink_assert(s->dns_info.looking_up == ORIGIN_SERVER);
01657 
01658   // detect whether we are about to self loop. the client may have
01659   // specified the proxy as the origin server (badness).
01660 
01661   // Check if this procedure is already done - YTS Team, yamsat
01662   if (!s->request_will_not_selfloop) {
01663     if (will_this_request_self_loop(s)) {
01664       DebugTxn("http_trans", "[OSDNSLookup] request will selfloop - bailing out");
01665       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
01666       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL);
01667     }
01668   }
01669 
01670   if (!s->dns_info.lookup_success) {
01671     // maybe the name can be expanded (e.g cnn -> www.cnn.com)
01672     HostNameExpansionError_t host_name_expansion = try_to_expand_host_name(s);
01673 
01674     switch (host_name_expansion) {
01675     case RETRY_EXPANDED_NAME:
01676       // expansion successful, do a dns lookup on expanded name
01677       HTTP_RELEASE_ASSERT(s->dns_info.attempts < max_dns_lookups);
01678       HTTP_RELEASE_ASSERT(s->http_config_param->enable_url_expandomatic);
01679       TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, OSDNSLookup);
01680       break;
01681     case EXPANSION_NOT_ALLOWED:
01682     case EXPANSION_FAILED:
01683     case DNS_ATTEMPTS_EXHAUSTED:
01684       if (DNSLookupInfo::OS_ADDR_TRY_HOSTDB == s->dns_info.os_addr_style) {
01685         // No HostDB data, just keep on with the CTA.
01686         s->dns_info.lookup_success = true;
01687         s->dns_info.os_addr_style = DNSLookupInfo::OS_ADDR_USE_CLIENT;
01688         DebugTxn("http_seq", "[HttpTransact::OSDNSLookup] DNS lookup unsuccessful reverting to force client target address use");
01689       } else {
01690         if (host_name_expansion == EXPANSION_NOT_ALLOWED) {
01691           // config file doesn't allow automatic expansion of host names
01692           HTTP_RELEASE_ASSERT(!(s->http_config_param->enable_url_expandomatic));
01693           DebugTxn("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup unsuccessful");
01694         } else if (host_name_expansion == EXPANSION_FAILED) {
01695           // not able to expand the hostname. dns lookup failed
01696           DebugTxn("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup unsuccessful");
01697         } else if (host_name_expansion == DNS_ATTEMPTS_EXHAUSTED) {
01698           // retry attempts exhausted --- can't find dns entry for this host name
01699           HTTP_RELEASE_ASSERT(s->dns_info.attempts >= max_dns_lookups);
01700           DebugTxn("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup unsuccessful");
01701         }
01702         // output the DNS failure error message
01703         SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
01704         build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Cannot find server.", "connect#dns_failed", NULL);
01705         // s->cache_info.action = CACHE_DO_NO_ACTION;
01706         TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL);
01707       }
01708       break;
01709     default:
01710       ink_assert(!("try_to_expand_hostname returned an unsupported code"));
01711       break;
01712     }
01713     return;
01714   }
01715   // ok, so the dns lookup succeeded
01716   ink_assert(s->dns_info.lookup_success);
01717   DebugTxn("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup successful");
01718 
01719   if (DNSLookupInfo::OS_ADDR_TRY_HOSTDB == s->dns_info.os_addr_style) {
01720     // We've backed off from a client supplied address and found some
01721     // HostDB addresses. We use those if they're different from the CTA.
01722     // In all cases we now commit to client or HostDB for our source.
01723     if (s->host_db_info.round_robin) {
01724       HostDBInfo* cta = s->host_db_info.rr()->select_next(&s->current.server->addr.sa);
01725       if (cta) {
01726         // found another addr, lock in host DB.
01727         s->host_db_info = *cta;
01728         s->dns_info.os_addr_style = DNSLookupInfo::OS_ADDR_USE_HOSTDB;
01729       } else {
01730         // nothing else there, continue with CTA.
01731         s->dns_info.os_addr_style = DNSLookupInfo::OS_ADDR_USE_CLIENT;
01732       }
01733     } else if (ats_ip_addr_eq(s->host_db_info.ip(), &s->server_info.addr.sa)) {
01734       s->dns_info.os_addr_style = DNSLookupInfo::OS_ADDR_USE_CLIENT;
01735     } else {
01736       s->dns_info.os_addr_style = DNSLookupInfo::OS_ADDR_USE_HOSTDB;
01737     }
01738   }
01739 
01740   // Check to see if can fullfill expect requests based on the cached
01741   // update some state variables with hostdb information that has
01742   // been provided.
01743   ats_ip_copy(&s->server_info.addr, s->host_db_info.ip());
01744   ats_ip_copy(&s->request_data.dest_ip, &s->server_info.addr);
01745   get_ka_info_from_host_db(s, &s->server_info, &s->client_info, &s->host_db_info);
01746   s->server_info.dns_round_robin = s->host_db_info.round_robin;
01747 
01748   char addrbuf[INET6_ADDRSTRLEN];
01749   DebugTxn("http_trans", "[OSDNSLookup] DNS lookup for O.S. successful "
01750         "IP: %s", ats_ip_ntop(&s->server_info.addr.sa, addrbuf, sizeof(addrbuf)));
01751 
01752   // so the dns lookup was a success, but the lookup succeeded on
01753   // a hostname which was expanded by the traffic server. we should
01754   // not automatically forward the request to this expanded hostname.
01755   // return a response to the client with the expanded host name
01756   // and a tasty little blurb explaining what happened.
01757 
01758   // if a DNS lookup succeeded on a user-defined
01759   // hostname expansion, forward the request to the expanded hostname.
01760   // On the other hand, if the lookup succeeded on a www.<hostname>.com
01761   // expansion, return a 302 response.
01762   // [amc] Also don't redirect if we backed off using HostDB instead of CTA.
01763   if (s->dns_info.attempts == max_dns_lookups && s->dns_info.looking_up == ORIGIN_SERVER && DNSLookupInfo::OS_ADDR_USE_CLIENT != s->dns_info.os_addr_style) {
01764     DebugTxn("http_trans", "[OSDNSLookup] DNS name resolution on expansion");
01765     DebugTxn("http_seq", "[OSDNSLookup] DNS name resolution on expansion - returning");
01766     build_redirect_response(s);
01767     // s->cache_info.action = CACHE_DO_NO_ACTION;
01768     TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, NULL);
01769   }
01770   // everything succeeded with the DNS lookup so do an API callout
01771   //   that allows for filtering.  We'll do traffic_server internal
01772   //   filtering after API filtering
01773 
01774 
01775   // After SM_ACTION_DNS_LOOKUP, goto the saved action/state ORIGIN_SERVER_(RAW_)OPEN.
01776   // Should we skip the StartAccessControl()? why?
01777 
01778   if (s->cdn_remap_complete) {
01779     DebugTxn("cdn", "This is a late DNS lookup.  We are going to the OS, " "not to HandleFiltering.");
01780     ink_assert(s->cdn_saved_next_action == SM_ACTION_ORIGIN_SERVER_OPEN || s->cdn_saved_next_action == SM_ACTION_ORIGIN_SERVER_RAW_OPEN);
01781     DebugTxn("cdn", "outgoing version -- (pre  conversion) %d", s->hdr_info.server_request.m_http->m_version);
01782     (&s->hdr_info.server_request)->version_set(HTTPVersion(1, 1));
01783     HttpTransactHeaders::convert_request(s->current.server->http_version, &s->hdr_info.server_request);
01784     DebugTxn("cdn", "outgoing version -- (post conversion) %d", s->hdr_info.server_request.m_http->m_version);
01785     TRANSACT_RETURN(s->cdn_saved_next_action, NULL);
01786   } else if (DNSLookupInfo::OS_ADDR_USE_CLIENT == s->dns_info.os_addr_style ||
01787              DNSLookupInfo::OS_ADDR_USE_HOSTDB == s->dns_info.os_addr_style) {
01788     // we've come back after already trying the server to get a better address
01789     // and finished with all backtracking - return to trying the server.
01790     TRANSACT_RETURN(how_to_open_connection(s), HttpTransact::HandleResponse);
01791   } else if (s->dns_info.lookup_name[0] <= '9' &&
01792              s->dns_info.lookup_name[0] >= '0' &&
01793              s->parent_params->ParentTable->hostMatch &&
01794              !s->http_config_param->no_dns_forward_to_parent) {
01795     // note, broken logic: ACC fudges the OR stmt to always be true,
01796     // 'AuthHttpAdapter' should do the rev-dns if needed, not here .
01797     TRANSACT_RETURN(SM_ACTION_DNS_REVERSE_LOOKUP, HttpTransact::StartAccessControl);
01798   } else {
01799     //(s->state_machine->authAdapter).StartLookup (s);
01800     // TRANSACT_RETURN(SM_ACTION_AUTH_LOOKUP, NULL);
01801 
01802     if (s->force_dns) {
01803       StartAccessControl(s);    // If skip_dns is enabled and no ip based rules in cache.config and parent.config
01804       // Access Control is called after DNS response
01805     } else {
01806       if ((s->cache_info.action == CACHE_DO_NO_ACTION) &&
01807           (((s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE) && !s->txn_conf->cache_range_write) ||
01808             s->range_setup == RANGE_NOT_SATISFIABLE || s->range_setup == RANGE_NOT_HANDLED))) {
01809         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HandleCacheOpenReadMiss);
01810       } else if (s->cache_lookup_result == HttpTransact::CACHE_LOOKUP_SKIPPED) {
01811         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, LookupSkipOpenServer);
01812         // DNS Lookup is done after LOOKUP Skipped  and after we get response
01813         // from the DNS we need to call LookupSkipOpenServer
01814       } else if (s->cache_lookup_result == CACHE_LOOKUP_HIT_FRESH ||
01815                  s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING ||
01816                  s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE) {
01817         //DNS lookup is done if the content is state need to call handle cache open read hit
01818         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HandleCacheOpenReadHit);
01819       } else if (s->cache_lookup_result == CACHE_LOOKUP_MISS || s->cache_info.action == CACHE_DO_NO_ACTION) {
01820         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HandleCacheOpenReadMiss);
01821         //DNS lookup is done if the lookup failed and need to call Handle Cache Open Read Miss
01822       } else {
01823         build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Invalid Cache Lookup result", "default", NULL);
01824         Log::error("HTTP: Invalid CACHE LOOKUP RESULT : %d", s->cache_lookup_result);
01825         TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL);
01826       }
01827     }
01828   }
01829 }
01830 
01831 void
01832 HttpTransact::StartAccessControl(State* s)
01833 {
01834   //if (s->cop_test_page  || (s->state_machine->authAdapter.disabled() == true)) {
01835     // Heartbeats should always be allowed.
01836     // s->content_control.access = ACCESS_ALLOW;
01837     HandleRequestAuthorized(s);
01838   //  return;
01839  // }
01840   // ua_session is NULL for scheduled updates.
01841   // Don't use req_flavor to do the test because if updated
01842   // urls are remapped, the req_flavor is changed to REV_PROXY.
01843   //if (s->state_machine->ua_session == NULL) {
01844     // Scheduled updates should always be allowed
01845    // return;
01846   //}
01847   // pass the access control logic to the ACC module.
01848   //(s->state_machine->authAdapter).StartLookup(s);
01849 }
01850 
01851 void
01852 HttpTransact::HandleRequestAuthorized(State* s)
01853 {
01854   //(s->state_machine->authAdapter).SetState(s);
01855   //(s->state_machine->authAdapter).UserAuthorized(NULL);
01856   //TRANSACT_RETURN(HTTP_API_OS_DNS, HandleFiltering);
01857    if (s->force_dns) {
01858     TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HttpTransact::DecideCacheLookup);
01859   } else {
01860     HttpTransact::DecideCacheLookup(s);
01861   }
01862 }
01863 
01864 void
01865 HttpTransact::HandleFiltering(State* s)
01866 {
01867   ink_release_assert(!"Fix-Me AUTH MERGE");
01868 
01869   if (s->method == HTTP_WKSIDX_PUSH && s->http_config_param->push_method_enabled == 0) {
01870     // config file says this request is not authorized.
01871     // send back error response to client.
01872     DebugTxn("http_trans", "[HandleFiltering] access denied.");
01873     DebugTxn("http_seq", "[HttpTransact::HandleFiltering] Access Denied.");
01874 
01875     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
01876     // adding a comment so that cvs recognizes that I added a space in the text below
01877     build_error_response(s, HTTP_STATUS_FORBIDDEN, "Access Denied", "access#denied", NULL);
01878     // s->cache_info.action = CACHE_DO_NO_ACTION;
01879     TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL);
01880   }
01881 
01882   DebugTxn("http_seq", "[HttpTransact::HandleFiltering] Request Authorized.");
01883   //////////////////////////////////////////////////////////////
01884   // ok, the config file says that the request is authorized. //
01885   //////////////////////////////////////////////////////////////
01886 
01887   // request is not black listed so now decided if we ought to
01888   //  lookup the cache
01889   DecideCacheLookup(s);
01890 }
01891 
01892 void
01893 HttpTransact::DecideCacheLookup(State* s)
01894 {
01895   // Check if a client request is lookupable.
01896   if (s->redirect_info.redirect_in_process || s->cop_test_page) {
01897     // for redirect, we want to skip cache lookup and write into
01898     // the cache directly with the URL before the redirect
01899     s->cache_info.action = CACHE_DO_NO_ACTION;
01900     s->current.mode = GENERIC_PROXY;
01901   } else {
01902     if (is_request_cache_lookupable(s)) {
01903       s->cache_info.action = CACHE_DO_LOOKUP;
01904       s->current.mode = GENERIC_PROXY;
01905     } else {
01906       s->cache_info.action = CACHE_DO_NO_ACTION;
01907       s->current.mode = TUNNELLING_PROXY;
01908       HTTP_INCREMENT_TRANS_STAT(http_tunnels_stat);
01909     }
01910   }
01911 
01912   if (service_transaction_in_proxy_only_mode(s)) {
01913     s->cache_info.action = CACHE_DO_NO_ACTION;
01914     s->current.mode = TUNNELLING_PROXY;
01915     HTTP_INCREMENT_TRANS_STAT(http_throttled_proxy_only_stat);
01916   }
01917   // at this point the request is ready to continue down the
01918   // traffic server path.
01919 
01920   // now decide whether the cache can even be looked up.
01921   if (s->cache_info.action == CACHE_DO_LOOKUP) {
01922     DebugTxn("http_trans", "[DecideCacheLookup] Will do cache lookup.");
01923     DebugTxn("http_seq", "[DecideCacheLookup] Will do cache lookup");
01924     ink_assert(s->current.mode != TUNNELLING_PROXY);
01925 
01926     if (s->cache_info.lookup_url == NULL) {
01927       HTTPHdr* incoming_request = &s->hdr_info.client_request;
01928 
01929       if (s->txn_conf->maintain_pristine_host_hdr) {
01930         s->cache_info.lookup_url_storage.create(NULL);
01931         s->cache_info.lookup_url_storage.copy(incoming_request->url_get());
01932         s->cache_info.lookup_url = &s->cache_info.lookup_url_storage;
01933         // if the target isn't in the URL, put it in the copy for
01934         // cache lookup.
01935         incoming_request->set_url_target_from_host_field(s->cache_info.lookup_url);
01936       } else {
01937         // make sure the target is in the URL.
01938         incoming_request->set_url_target_from_host_field();
01939         s->cache_info.lookup_url = incoming_request->url_get();
01940       }
01941 
01942       // *somebody* wants us to not hack the host header in a reverse proxy setup.
01943       // In addition, they want us to reverse proxy for 6000 servers, which vary
01944       // the stupid content on the Host header!!!!
01945       // We could a) have 6000 alts (barf, puke, vomit) or b) use the original
01946       // host header in the url before doing all cache actions (lookups, writes, etc.)
01947       if (s->txn_conf->maintain_pristine_host_hdr) {
01948         char const* host_hdr;
01949         char const* port_hdr;
01950         int host_len, port_len;
01951         // So, the host header will have the original host header.
01952         if (incoming_request->get_host_port_values(&host_hdr, &host_len, &port_hdr, &port_len)) {
01953           int port = 0;
01954           if (port_hdr) {
01955             s->cache_info.lookup_url->host_set(host_hdr, host_len);
01956             port = ink_atoi(port_hdr, port_len);
01957           } else {
01958             s->cache_info.lookup_url->host_set(host_hdr, host_len);
01959           }
01960           s->cache_info.lookup_url->port_set(port);
01961         }
01962       }
01963       ink_assert(s->cache_info.lookup_url->valid() == true);
01964     }
01965 
01966     TRANSACT_RETURN(SM_ACTION_CACHE_LOOKUP, NULL);
01967   } else {
01968     ink_assert(s->cache_info.action != CACHE_DO_LOOKUP && s->cache_info.action != CACHE_DO_SERVE);
01969 
01970     DebugTxn("http_trans", "[DecideCacheLookup] Will NOT do cache lookup.");
01971     DebugTxn("http_seq", "[DecideCacheLookup] Will NOT do cache lookup");
01972     // If this is a push request, we need send an error because
01973     //   since what ever was sent is not cachable
01974     if (s->method == HTTP_WKSIDX_PUSH) {
01975       HandlePushError(s, "Request Not Cachable");
01976       return;
01977     }
01978     // for redirect, we skipped cache lookup to do the automatic redirection
01979     if (s->redirect_info.redirect_in_process) {
01980       // without calling out the CACHE_LOOKUP_COMPLETE_HOOK
01981       s->cache_info.action = CACHE_DO_WRITE;
01982       LookupSkipOpenServer(s);
01983     } else {
01984       // calling out CACHE_LOOKUP_COMPLETE_HOOK even when the cache
01985       // lookup is skipped
01986       s->cache_lookup_result = HttpTransact::CACHE_LOOKUP_SKIPPED;
01987       if (s->force_dns) {
01988         TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, LookupSkipOpenServer);
01989       } else {
01990         // Returning to dns lookup as cache lookup is skipped
01991         TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, CallOSDNSLookup);
01992       }
01993     }
01994   }
01995 
01996   return;
01997 }
01998 
01999 void
02000 HttpTransact::LookupSkipOpenServer(State* s)
02001 {
02002   // cache will not be looked up. open a connection
02003   // to a parent proxy or to the origin server.
02004   find_server_and_update_current_info(s);
02005 
02006   if (s->current.request_to == PARENT_PROXY) {
02007     TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
02008   }
02009 
02010   ink_assert(s->current.request_to == ORIGIN_SERVER);
02011   // ink_assert(s->current.server->ip != 0);
02012 
02013   build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
02014 
02015   StateMachineAction_t next = how_to_open_connection(s);
02016   s->next_action = next;
02017   if (next == SM_ACTION_ORIGIN_SERVER_OPEN || next == SM_ACTION_ORIGIN_SERVER_RAW_OPEN) {
02018     TRANSACT_RETURN(next, HttpTransact::HandleResponse);
02019   }
02020 }
02021 
02022 
02023 //////////////////////////////////////////////////////////////////////////////
02024 // Name       : HandleCacheOpenReadPush
02025 // Description:
02026 //
02027 // Details    :
02028 //
02029 // Called on PUSH requests from HandleCacheOpenRead
02030 //////////////////////////////////////////////////////////////////////////////
02031 void
02032 HttpTransact::HandleCacheOpenReadPush(State* s, bool read_successful)
02033 {
02034   if (read_successful) {
02035     s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
02036   } else {
02037     s->cache_info.action = CACHE_PREPARE_TO_WRITE;
02038   }
02039 
02040   TRANSACT_RETURN(SM_ACTION_READ_PUSH_HDR, HandlePushResponseHdr);
02041 }
02042 
02043 //////////////////////////////////////////////////////////////////////////////
02044 // Name       : HandlePushResponseHdr
02045 // Description:
02046 //
02047 // Details    :
02048 //
02049 // Called after reading the response header on PUSH request
02050 //////////////////////////////////////////////////////////////////////////////
02051 void
02052 HttpTransact::HandlePushResponseHdr(State* s)
02053 {
02054   // Verify the pushed header wasn't longer than the content length
02055   int64_t body_bytes = s->hdr_info.request_content_length - s->state_machine->pushed_response_hdr_bytes;
02056   if (body_bytes < 0) {
02057     HandlePushError(s, "Bad Content Length");
02058     return;
02059   }
02060   // We need to create the request header storing in the cache
02061   s->hdr_info.server_request.create(HTTP_TYPE_REQUEST);
02062   s->hdr_info.server_request.copy(&s->hdr_info.client_request);
02063   s->hdr_info.server_request.method_set(HTTP_METHOD_GET, HTTP_LEN_GET);
02064   s->hdr_info.server_request.value_set("X-Inktomi-Source", 16, "http PUSH", 9);
02065 
02066   if (!s->cop_test_page)
02067     DUMP_HEADER("http_hdrs", &s->hdr_info.server_response, s->state_machine_id, "Pushed Response Header");
02068 
02069   if (!s->cop_test_page)
02070     DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Generated Request Header");
02071 
02072   s->response_received_time = s->request_sent_time = ink_cluster_time();
02073 
02074   if (is_response_cacheable(s, &s->hdr_info.server_request, &s->hdr_info.server_response)) {
02075     ink_assert(s->cache_info.action == CACHE_PREPARE_TO_WRITE || s->cache_info.action == CACHE_PREPARE_TO_UPDATE);
02076 
02077     TRANSACT_RETURN(SM_ACTION_CACHE_ISSUE_WRITE, HandlePushCacheWrite);
02078   } else {
02079     HandlePushError(s, "Response Not Cachable");
02080   }
02081 }
02082 
02083 //////////////////////////////////////////////////////////////////////////////
02084 // Name       : HandlePushCacheWrite
02085 // Description:
02086 //
02087 // Details    :
02088 //
02089 // Called after performing the cache write on a push request
02090 //////////////////////////////////////////////////////////////////////////////
02091 void
02092 HttpTransact::HandlePushCacheWrite(State* s)
02093 {
02094   switch (s->cache_info.write_lock_state) {
02095   case CACHE_WL_SUCCESS:
02096     // We were able to get the lock for the URL vector in the cache
02097     if (s->cache_info.action == CACHE_PREPARE_TO_WRITE) {
02098       s->cache_info.action = CACHE_DO_WRITE;
02099     } else if (s->cache_info.action == CACHE_PREPARE_TO_UPDATE) {
02100       s->cache_info.action = CACHE_DO_REPLACE;
02101     } else {
02102       ink_release_assert(0);
02103     }
02104     set_headers_for_cache_write(s, &s->cache_info.object_store, &s->hdr_info.server_request, &s->hdr_info.server_response);
02105 
02106     TRANSACT_RETURN(SM_ACTION_STORE_PUSH_BODY, NULL);
02107     break;
02108 
02109   case CACHE_WL_FAIL:
02110   case CACHE_WL_READ_RETRY:
02111     // No write lock, can not complete request so bail
02112     HandlePushError(s, "Cache Write Failed");
02113     break;
02114   case CACHE_WL_INIT:
02115   default:
02116     ink_release_assert(0);
02117   }
02118 }
02119 
02120 
02121 void
02122 HttpTransact::HandlePushTunnelSuccess(State* s)
02123 {
02124   ink_assert(s->cache_info.action == CACHE_DO_WRITE || s->cache_info.action == CACHE_DO_REPLACE);
02125 
02126   // FIX ME: check PUSH spec for status codes
02127   HTTPStatus resp_status = (s->cache_info.action == CACHE_DO_WRITE) ? HTTP_STATUS_CREATED : HTTP_STATUS_OK;
02128 
02129   build_response(s, &s->hdr_info.client_response, s->client_info.http_version, resp_status);
02130 
02131   TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, NULL);
02132 }
02133 
02134 
02135 void
02136 HttpTransact::HandlePushTunnelFailure(State* s)
02137 {
02138   HandlePushError(s, "Cache Error");
02139 }
02140 
02141 void
02142 HttpTransact::HandleBadPushRespHdr(State* s)
02143 {
02144   HandlePushError(s, "Malformed Pushed Response Header");
02145 }
02146 
02147 void
02148 HttpTransact::HandlePushError(State *s, const char *reason)
02149 {
02150   s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
02151 
02152   // Set half close flag to prevent TCP
02153   //   reset from the body still being transfered
02154   s->state_machine->set_ua_half_close_flag();
02155 
02156   build_error_response(s, HTTP_STATUS_BAD_REQUEST, reason, "default", NULL);
02157 }
02158 
02159 
02160 ///////////////////////////////////////////////////////////////////////////////
02161 // Name       : HandleCacheOpenRead
02162 // Description: the cache lookup succeeded - may have been a hit or a miss
02163 //
02164 // Details    :
02165 //
02166 // the cache lookup succeeded. first check if the lookup resulted in
02167 // a hit or a miss, if the lookup was for an http request.
02168 // This function just funnels the result into the appropriate
02169 // functions which handle these different cases.
02170 //
02171 //
02172 // Possible Next States From Here:
02173 //
02174 ///////////////////////////////////////////////////////////////////////////////
02175 void
02176 HttpTransact::HandleCacheOpenRead(State* s)
02177 {
02178   DebugTxn("http_trans", "[HttpTransact::HandleCacheOpenRead]");
02179 
02180   SET_VIA_STRING(VIA_DETAIL_CACHE_TYPE, VIA_DETAIL_CACHE);
02181 
02182   bool read_successful = true;
02183 
02184   if (s->cache_info.object_read == 0) {
02185     read_successful = false;
02186     //
02187     // If somebody else was writing the document, proceed just like it was
02188     // a normal cache miss, except don't try to write to the cache
02189     //
02190     if (s->cache_lookup_result == CACHE_LOOKUP_DOC_BUSY) {
02191       s->cache_lookup_result = CACHE_LOOKUP_MISS;
02192       s->cache_info.action = CACHE_DO_NO_ACTION;
02193     }
02194   } else {
02195     CacheHTTPInfo *obj = s->cache_info.object_read;
02196     if (obj->response_get()->type_get() == HTTP_TYPE_UNKNOWN) {
02197       read_successful = false;
02198     }
02199     if (obj->request_get()->type_get() == HTTP_TYPE_UNKNOWN) {
02200       read_successful = false;
02201     }
02202   }
02203 
02204   if (s->method == HTTP_WKSIDX_PUSH) {
02205     HandleCacheOpenReadPush(s, read_successful);
02206   } else if (read_successful == false) {
02207     // cache miss
02208     DebugTxn("http_trans", "CacheOpenRead -- miss");
02209     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_NOT_CACHED);
02210     //StartAccessControl(s);
02211     if (s->force_dns) {
02212       HandleCacheOpenReadMiss(s);
02213     } else {
02214       //Cache Lookup Unsuccessful ..calling dns lookup
02215       TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, OSDNSLookup);
02216     }
02217   }
02218   else {
02219     // cache hit
02220     DebugTxn("http_trans", "CacheOpenRead -- hit");
02221     TRANSACT_RETURN(SM_ACTION_API_READ_CACHE_HDR, HandleCacheOpenReadHitFreshness);
02222   }
02223 
02224   return;
02225 }
02226 
02227 
02228 ///////////////////////////////////////////////////////////////////////////////
02229 // Name       : issue_revalidate
02230 // Description:   Sets cache action and does various bookkeeping
02231 //
02232 // Details    :
02233 //
02234 // The Cache Lookup was hit but the document was stale so after
02235 //   calling build_request, we need setup up the cache action,
02236 //   set the via code, and possibly conditionalize the request
02237 // The paths that we take to get this code are:
02238 //   Directly from HandleOpenReadHit if we are going to the origin server
02239 //   After PPDNS if we are going to a parent proxy
02240 //
02241 //
02242 // Possible Next States From Here:
02243 // -
02244 //
02245 ///////////////////////////////////////////////////////////////////////////////
02246 void
02247 HttpTransact::issue_revalidate(State* s)
02248 {
02249   HTTPHdr *c_resp = find_appropriate_cached_resp(s);
02250   SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_STALE);
02251   ink_assert(GET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP) != ' ');
02252 
02253   if (s->www_auth_content == CACHE_AUTH_FRESH) {
02254     s->hdr_info.server_request.method_set(HTTP_METHOD_HEAD, HTTP_LEN_HEAD);
02255     // The document is fresh in cache and we just want to see if the
02256     // the client has the right credentials
02257     // this cache action is just to get us into the hcoofsr function
02258     s->cache_info.action = CACHE_DO_UPDATE;
02259     DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Proxy's Request (Conditionalized)");
02260     return;
02261   }
02262 
02263   if (s->cache_info.write_lock_state == CACHE_WL_INIT) {
02264     // We do a cache lookup for DELETE, PUT and POST requests as well.
02265     // We must, however, delete the cached copy after forwarding the
02266     // request to the server. is_cache_response_returnable will ensure
02267     // that we forward the request. We now specify what the cache
02268     // action should be when the response is received.
02269     if (does_method_require_cache_copy_deletion(s->http_config_param, s->method)) {
02270       s->cache_info.action = CACHE_PREPARE_TO_DELETE;
02271       DebugTxn("http_seq", "[HttpTransact::issue_revalidate] cache action: DELETE");
02272     } else {
02273       s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
02274       DebugTxn("http_seq", "[HttpTransact::issue_revalidate] cache action: UPDATE");
02275     }
02276   } else {
02277     // We've looped back around due to missing the write lock
02278     //  for the cache.  At this point we want to forget about the cache
02279     ink_assert(s->cache_info.write_lock_state == CACHE_WL_READ_RETRY);
02280     s->cache_info.action = CACHE_DO_NO_ACTION;
02281     return;
02282   }
02283 
02284   // if the document is cached, just send a conditional request to the server
02285 
02286   // So the request does not have preconditions. It can, however
02287   // be a simple GET request with a Pragma:no-cache. As on 8/28/98
02288   // we have fixed the whole Reload/Shift-Reload cached copy
02289   // corruption problem. This means that we can issue a conditional
02290   // request to the server only if the incoming request has a conditional
02291   // or the incoming request does NOT have a no-cache header.
02292   // In other words, if the incoming request is not conditional
02293   // but has a no-cache header we can not issue an IMS. check for
02294   // that case here.
02295   bool no_cache_in_request = false;
02296 
02297   if (s->hdr_info.client_request.is_pragma_no_cache_set() ||
02298       s->hdr_info.client_request.is_cache_control_set(HTTP_VALUE_NO_CACHE)) {
02299     DebugTxn("http_trans", "[issue_revalidate] no-cache header directive in request, folks");
02300     no_cache_in_request = true;
02301   }
02302 
02303   if ((!(s->hdr_info.client_request.presence(MIME_PRESENCE_IF_MODIFIED_SINCE))) &&
02304       (!(s->hdr_info.client_request.presence(MIME_PRESENCE_IF_NONE_MATCH))) &&
02305       (no_cache_in_request == true) &&
02306       (!s->txn_conf->cache_ims_on_client_no_cache) && (s->www_auth_content == CACHE_AUTH_NONE)) {
02307     DebugTxn("http_trans", "[issue_revalidate] Can not make this a conditional request. This is the force update of the cached copy case");
02308     // set cache action to update. response will be a 200 or error,
02309     // causing cached copy to be replaced (if 200).
02310     s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
02311     return;
02312   }
02313   // do not conditionalize if the cached response is not a 200
02314   switch (c_resp->status_get()) {
02315   case HTTP_STATUS_OK:         // 200
02316     // don't conditionalize if we are configured to repeat the clients
02317     //   conditionals
02318     if (s->txn_conf->cache_when_to_revalidate == 4)
02319       break;
02320     // ok, request is either a conditional or does not have a no-cache.
02321     //   (or is method that we don't conditionalize but lookup the
02322     //    cache on like DELETE)
02323     if (c_resp->get_last_modified() > 0 &&
02324         (s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_GET ||
02325                 s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_HEAD) && s->range_setup == RANGE_NONE) {
02326       // make this a conditional request
02327       int length;
02328       const char *str = c_resp->value_get(MIME_FIELD_LAST_MODIFIED, MIME_LEN_LAST_MODIFIED, &length);
02329 
02330       s->hdr_info.server_request.value_set(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE, str, length);
02331       if (!s->cop_test_page)
02332         DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Proxy's Request (Conditionalized)");
02333     }
02334     // if Etag exists, also add if-non-match header
02335     if (c_resp->presence(MIME_PRESENCE_ETAG) &&
02336                         (s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_GET ||
02337                          s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_HEAD)) {
02338       int length;
02339       const char *etag = c_resp->value_get(MIME_FIELD_ETAG, MIME_LEN_ETAG, &length);
02340       if ((length >= 2) && (etag[0] == 'W') && (etag[1] == '/')) {
02341         etag += 2;
02342         length -= 2;
02343       }
02344       s->hdr_info.server_request.value_set(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH, etag, length);
02345       if (!s->cop_test_page)
02346         DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Proxy's Request (Conditionalized)");
02347     }
02348     break;
02349   case HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION:      // 203
02350     /* fall through */
02351   case HTTP_STATUS_MULTIPLE_CHOICES:   // 300
02352     /* fall through */
02353   case HTTP_STATUS_MOVED_PERMANENTLY:  // 301
02354     /* fall through */
02355   case HTTP_STATUS_GONE:       // 410
02356     /* fall through */
02357   default:
02358     DebugTxn("http_trans", "[issue_revalidate] cached response is" "not a 200 response so no conditionalization.");
02359     s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
02360     break;
02361   case HTTP_STATUS_PARTIAL_CONTENT:
02362     ink_assert(!"unexpected status code");
02363     break;
02364   }
02365 }
02366 
02367 
02368 void
02369 HttpTransact::HandleCacheOpenReadHitFreshness(State* s)
02370 {
02371   CacheHTTPInfo *&obj = s->cache_info.object_read;
02372 
02373   ink_release_assert((s->request_sent_time == UNDEFINED_TIME) && (s->response_received_time == UNDEFINED_TIME));
02374   DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] Hit in cache");
02375 
02376   if (delete_all_document_alternates_and_return(s, true)) {
02377     DebugTxn("http_trans", "[HandleCacheOpenReadHitFreshness] Delete and return");
02378     s->cache_info.action = CACHE_DO_DELETE;
02379     s->next_action = HttpTransact::SM_ACTION_INTERNAL_CACHE_DELETE;
02380     return;
02381   }
02382 
02383   s->request_sent_time = obj->request_sent_time_get();
02384   s->response_received_time = obj->response_received_time_get();
02385 
02386   // There may be clock skew if one of the machines
02387   // went down and we do not have the correct delta
02388   // for it. this is just to deal with the effects
02389   // of the skew by setting minimum and maximum times
02390   // so that ages are not negative, etc.
02391   s->request_sent_time = min(s->client_request_time, s->request_sent_time);
02392   s->response_received_time = min(s->client_request_time, s->response_received_time);
02393 
02394   ink_assert(s->request_sent_time <= s->response_received_time);
02395 
02396   DebugTxn("http_trans", "[HandleCacheOpenReadHitFreshness] request_sent_time      : %" PRId64,
02397            (int64_t)s->request_sent_time);
02398   DebugTxn("http_trans", "[HandleCacheOpenReadHitFreshness] response_received_time : %" PRId64,
02399            (int64_t)s->response_received_time);
02400   // if the plugin has already decided the freshness, we don't need to
02401   // do it again
02402   if (s->cache_lookup_result == HttpTransact::CACHE_LOOKUP_NONE) {
02403     // is the document still fresh enough to be served back to
02404     // the client without revalidation?
02405     Freshness_t freshness = what_is_document_freshness(s, &s->hdr_info.client_request, obj->response_get());
02406     switch (freshness) {
02407     case FRESHNESS_FRESH:
02408       DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] " "Fresh copy");
02409       s->cache_lookup_result = HttpTransact::CACHE_LOOKUP_HIT_FRESH;
02410       break;
02411     case FRESHNESS_WARNING:
02412       DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] " "Heuristic-based Fresh copy");
02413       s->cache_lookup_result = HttpTransact::CACHE_LOOKUP_HIT_WARNING;
02414       break;
02415     case FRESHNESS_STALE:
02416       DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] " "Stale in cache");
02417       s->cache_lookup_result = HttpTransact::CACHE_LOOKUP_HIT_STALE;
02418       s->is_revalidation_necessary = true;      // to identify a revalidation occurrence
02419       break;
02420     default:
02421       ink_assert(!("what_is_document_freshness has returned unsupported code."));
02422       break;
02423     }
02424   }
02425 
02426   ink_assert(s->cache_lookup_result != HttpTransact::CACHE_LOOKUP_MISS);
02427   if (s->cache_lookup_result == HttpTransact::CACHE_LOOKUP_HIT_STALE)
02428     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_EXPIRED);
02429 
02430   if (!s->force_dns) {          // If DNS is not performed before
02431     if (need_to_revalidate(s)) {
02432       TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, CallOSDNSLookup); // content needs to be revalidated and we did not perform a dns ....calling DNS lookup
02433     } else {                    // document can be served can cache
02434       TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, HttpTransact::HandleCacheOpenReadHit);
02435     }
02436   } else {                      // we have done dns . Its up to HandleCacheOpenReadHit to decide to go OS or serve from cache
02437     TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, HttpTransact::HandleCacheOpenReadHit);
02438   }
02439 }
02440 
02441 ///////////////////////////////////////////////////////////////////////////////
02442 // Name       : CallOSDNSLookup
02443 // Description: Moves in SM_ACTION_DNS_LOOKUP state and sets the transact return to OSDNSLookup
02444 //
02445 // Details    :
02446 /////////////////////////////////////////////////////////////////////////////
02447 void
02448 HttpTransact::CallOSDNSLookup(State* s)
02449 {
02450   TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, OSDNSLookup);
02451 }
02452 
02453 ///////////////////////////////////////////////////////////////////////////////
02454 // Name       : need_to_revalidate
02455 // Description: Checks if a document which is in the cache needs to be revalidates
02456 //
02457 // Details    : Function calls AuthenticationNeeded and is_cache_response_returnable to determine
02458 //              if the cached document can be served
02459 /////////////////////////////////////////////////////////////////////////////
02460 bool
02461 HttpTransact::need_to_revalidate(State* s)
02462 {
02463   bool needs_revalidate, needs_authenticate = false;
02464   bool needs_cache_auth = false;
02465   CacheHTTPInfo *obj;
02466 
02467   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
02468     obj = &s->cache_info.object_store;
02469     ink_assert(obj->valid());
02470     if (!obj->valid())
02471       return true;
02472   } else
02473     obj = s->cache_info.object_read;
02474 
02475   // do we have to authenticate with the server before
02476   // sending back the cached response to the client?
02477   Authentication_t authentication_needed =
02478     AuthenticationNeeded(s->txn_conf, &s->hdr_info.client_request, obj->response_get());
02479 
02480   switch (authentication_needed) {
02481   case AUTHENTICATION_SUCCESS:
02482     DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication not needed");
02483     needs_authenticate = false;
02484     break;
02485   case AUTHENTICATION_MUST_REVALIDATE:
02486     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_METHOD);
02487     DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication needed");
02488     needs_authenticate = true;
02489     break;
02490   case AUTHENTICATION_MUST_PROXY:
02491     DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication needed");
02492     needs_authenticate = true;
02493     break;
02494   case AUTHENTICATION_CACHE_AUTH:
02495     DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication needed for cache_auth_content");
02496     needs_authenticate = false;
02497     needs_cache_auth = true;
02498     break;
02499   default:
02500     ink_assert(!("AuthenticationNeeded has returned unsupported code."));
02501     return true;
02502     break;
02503   }
02504 
02505   ink_assert(s->cache_lookup_result == CACHE_LOOKUP_HIT_FRESH ||
02506              s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING || s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE);
02507   if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE &&
02508       s->api_update_cached_object != HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
02509     needs_revalidate = true;
02510   } else
02511     needs_revalidate = false;
02512 
02513 
02514   bool
02515     send_revalidate = ((needs_authenticate == true) ||
02516                        (needs_revalidate == true) || (is_cache_response_returnable(s) == false));
02517   if (needs_cache_auth == true) {
02518     s->www_auth_content = send_revalidate ? CACHE_AUTH_STALE : CACHE_AUTH_FRESH;
02519     send_revalidate = true;
02520   }
02521   return send_revalidate;
02522 }
02523 
02524 ///////////////////////////////////////////////////////////////////////////////
02525 // Name       : HandleCacheOpenReadHit
02526 // Description: handle result of a cache hit
02527 //
02528 // Details    :
02529 //
02530 // Cache lookup succeeded and resulted in a cache hit. This means
02531 // that the Accept* and Etags fields also matched. The cache lookup
02532 // may have resulted in a vector of alternates (since lookup may
02533 // be based on a url). A different function (SelectFromAlternates)
02534 // goes through the alternates and finds the best match. That is
02535 // then returned to this function. The result may not be sent back
02536 // to the client, still, if the document is not fresh enough, or
02537 // does not have enough authorization, or if the client wants a
02538 // reload, etc. that decision will be made in this routine.
02539 //
02540 //
02541 // Possible Next States From Here:
02542 // - HttpTransact::PROXY_INTERNAL_CACHE_DELETE;
02543 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
02544 // - HttpTransact::ORIGIN_SERVER_OPEN;
02545 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
02546 // - HttpTransact::SERVE_FROM_CACHE;
02547 // - result of how_to_open_connection()
02548 //
02549 //
02550 // For Range requests, we will decide to do simple tunneling if one of the
02551 // following conditions hold:
02552 // - document stale
02553 // - cached response doesn't have Accept-Ranges and Content-Length
02554 //
02555 ///////////////////////////////////////////////////////////////////////////////
02556 void
02557 HttpTransact::HandleCacheOpenReadHit(State* s)
02558 {
02559   bool needs_revalidate, needs_authenticate = false;
02560   bool needs_cache_auth = false;
02561   bool server_up = true;
02562   CacheHTTPInfo *obj;
02563 
02564   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
02565     obj = &s->cache_info.object_store;
02566     ink_assert(obj->valid());
02567   } else
02568     obj = s->cache_info.object_read;
02569 
02570   // do we have to authenticate with the server before
02571   // sending back the cached response to the client?
02572   Authentication_t authentication_needed =
02573     AuthenticationNeeded(s->txn_conf, &s->hdr_info.client_request, obj->response_get());
02574 
02575   switch (authentication_needed) {
02576   case AUTHENTICATION_SUCCESS:
02577     DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication not needed");
02578     needs_authenticate = false;
02579     break;
02580   case AUTHENTICATION_MUST_REVALIDATE:
02581     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_METHOD);
02582     DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication needed");
02583     needs_authenticate = true;
02584     break;
02585   case AUTHENTICATION_MUST_PROXY:
02586     DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication needed");
02587     HandleCacheOpenReadMiss(s);
02588     return;
02589   case AUTHENTICATION_CACHE_AUTH:
02590     DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Authentication needed for cache_auth_content");
02591     needs_authenticate = false;
02592     needs_cache_auth = true;
02593     break;
02594   default:
02595     ink_assert(!("AuthenticationNeeded has returned unsupported code."));
02596     break;
02597   }
02598 
02599   ink_assert(s->cache_lookup_result == CACHE_LOOKUP_HIT_FRESH ||
02600              s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING ||
02601              s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE);
02602   if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE &&
02603       s->api_update_cached_object != HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
02604     needs_revalidate = true;
02605     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_EXPIRED);
02606   } else
02607     needs_revalidate = false;
02608 
02609   // the response may not be directly returnable to the client. there
02610   // are several reasons for this: config may force revalidation or
02611   // client may have forced a refresh by sending a Pragma:no-cache
02612   // or a Cache-Control:no-cache, or the client may have sent a
02613   // non-GET/HEAD request for a document that is cached. an example
02614   // of a situation for this is when a client sends a DELETE, PUT
02615   // or POST request for a url that is cached. except for DELETE,
02616   // we may actually want to update the cached copy with the contents
02617   // of the PUT/POST, but the easiest, safest and most robust solution
02618   // is to simply delete the cached copy (in order to maintain cache
02619   // consistency). this is particularly true if the server does not
02620   // accept or conditionally accepts the PUT/POST requests.
02621   // anyhow, this is an overloaded function and will return false
02622   // if the origin server still has to be looked up.
02623   bool response_returnable = is_cache_response_returnable(s);
02624 
02625   // do we need to revalidate. in other words if the response
02626   // has to be authorized, is stale or can not be returned, do
02627   // a revalidate.
02628   bool send_revalidate = ((needs_authenticate == true) || (needs_revalidate == true) || (response_returnable == false));
02629 
02630   if (needs_cache_auth == true) {
02631     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_EXPIRED);
02632     s->www_auth_content = send_revalidate ? CACHE_AUTH_STALE : CACHE_AUTH_FRESH;
02633     send_revalidate = true;
02634   }
02635 
02636   DebugTxn("http_trans", "CacheOpenRead --- needs_auth          = %d", needs_authenticate);
02637   DebugTxn("http_trans", "CacheOpenRead --- needs_revalidate    = %d", needs_revalidate);
02638   DebugTxn("http_trans", "CacheOpenRead --- response_returnable = %d", response_returnable);
02639   DebugTxn("http_trans", "CacheOpenRead --- needs_cache_auth    = %d", needs_cache_auth);
02640   DebugTxn("http_trans", "CacheOpenRead --- send_revalidate    = %d", send_revalidate);
02641   if (send_revalidate) {
02642     DebugTxn("http_trans", "CacheOpenRead --- HIT-STALE");
02643     s->dns_info.attempts = 0;
02644 
02645     DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Revalidate document with server");
02646 
02647     if (s->http_config_param->icp_enabled && icp_dynamic_enabled && s->http_config_param->stale_icp_enabled &&
02648         needs_authenticate == false && needs_cache_auth == false &&
02649         !s->hdr_info.client_request.is_pragma_no_cache_set() &&
02650         !s->hdr_info.client_request.is_cache_control_set(HTTP_VALUE_NO_CACHE)) {
02651       DebugTxn("http_trans", "[HandleCacheOpenReadHit] ICP is configured" " and no no-cache in request; checking ICP for a STALE hit");
02652 
02653       s->stale_icp_lookup = true;
02654 
02655       // we haven't done the ICP lookup yet. The following is to
02656       // fake an icp_info to cater for build_request's needs
02657       s->icp_info.http_version.set(1, 0);
02658       if (!s->txn_conf->keep_alive_enabled_out) {
02659         s->icp_info.keep_alive = HTTP_NO_KEEPALIVE;
02660       } else {
02661         s->icp_info.keep_alive = HTTP_KEEPALIVE;
02662       }
02663 
02664       update_current_info(&s->current, &s->icp_info, HttpTransact::ICP_SUGGESTED_HOST, 1);
02665     }
02666 
02667     if (s->stale_icp_lookup == false) {
02668       find_server_and_update_current_info(s);
02669 
02670       // We do not want to try to revalidate documents if we think
02671       //  the server is down due to the something report problem
02672       //
02673       // Note: we only want to skip origin servers because 1)
02674       //  parent proxies have their own negative caching
02675       //  scheme & 2) If we skip down parents, every page
02676       //  we serve is potentially stale
02677       //
02678       if (s->current.request_to == ORIGIN_SERVER &&
02679           is_server_negative_cached(s) && response_returnable == true &&
02680           is_stale_cache_response_returnable(s) == true) {
02681         server_up = false;
02682         update_current_info(&s->current, NULL, UNDEFINED_LOOKUP, 0);
02683         DebugTxn("http_trans", "CacheOpenReadHit - server_down, returning stale document");
02684       }
02685     }
02686 
02687     if (server_up || s->stale_icp_lookup) {
02688       if (!s->stale_icp_lookup && !ats_is_ip(&s->current.server->addr)) {
02689 //        ink_release_assert(s->current.request_to == PARENT_PROXY ||
02690 //                    s->http_config_param->no_dns_forward_to_parent != 0);
02691 
02692         // Set ourselves up to handle pending revalidate issues
02693         //  after the PP DNS lookup
02694         ink_assert(s->pending_work == NULL);
02695         s->pending_work = issue_revalidate;
02696 
02697         // We must be going a PARENT PROXY since so did
02698         //  origin server DNS lookup right after state Start
02699         //
02700         // If we end up here in the release case just fall
02701         //  through.  The request will fail because of the
02702         //  missing ip but we won't take down the system
02703         //
02704         if (s->current.request_to == PARENT_PROXY) {
02705           TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
02706         } else if (s->current.request_to == ORIGIN_SERVER) {
02707           TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, OSDNSLookup);
02708         } else {
02709           handle_parent_died(s);
02710           return;
02711         }
02712       }
02713 
02714       build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
02715 
02716       issue_revalidate(s);
02717 
02718       // this can not be anything but a simple origin server connection.
02719       // in other words, we would not have looked up the cache for a
02720       // connect request, so the next action can not be origin_server_raw_open.
02721       s->next_action = how_to_open_connection(s);
02722       if (s->stale_icp_lookup && s->next_action == SM_ACTION_ORIGIN_SERVER_OPEN) {
02723         s->next_action = SM_ACTION_ICP_QUERY;
02724       }
02725 
02726       ink_release_assert(s->next_action != SM_ACTION_ORIGIN_SERVER_RAW_OPEN);
02727       return;
02728     } else {                    // server is down but stale response is returnable
02729       SET_VIA_STRING(VIA_DETAIL_CACHE_TYPE, VIA_DETAIL_CACHE);
02730     }
02731   }
02732   // cache hit, document is fresh, does not authorization,
02733   // is valid, etc. etc. send it back to the client.
02734   //
02735   // the important thing to keep in mind is that if we are
02736   // here then we found a match in the cache and the document
02737   // is fresh and we have enough authorization for it to send
02738   // it back to the client without revalidating first with the
02739   // origin server. we are, therefore, allowed to behave as the
02740   // origin server. we can, therefore, make the claim that the
02741   // document has not been modified since or has not been unmodified
02742   // since the time requested by the client. this may not be
02743   // the case in reality, but since the document is fresh in
02744   // the cache, we can make the claim that this is the truth.
02745   //
02746   // so, any decision we make at this point can be made with authority.
02747   // realistically, if we can not make this claim, then there
02748   // is no reason to cache anything.
02749   //
02750   ink_assert((send_revalidate == true && server_up == false) || (send_revalidate == false && server_up == true));
02751 
02752   DebugTxn("http_trans", "CacheOpenRead --- HIT-FRESH");
02753   DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] " "Serve from cache");
02754 
02755   if (s->cache_info.is_ram_cache_hit) {
02756     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_RAM_CACHE_FRESH);
02757   } else {
02758     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_FRESH);
02759   }
02760 
02761   if (s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING) {
02762     build_response_from_cache(s, HTTP_WARNING_CODE_HERUISTIC_EXPIRATION);
02763   } else if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE) {
02764     ink_assert(server_up == false);
02765     build_response_from_cache(s, HTTP_WARNING_CODE_REVALIDATION_FAILED);
02766   } else {
02767     build_response_from_cache(s, HTTP_WARNING_CODE_NONE);
02768   }
02769 
02770   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
02771     s->saved_update_next_action = s->next_action;
02772     s->saved_update_cache_action = s->cache_info.action;
02773     s->next_action = SM_ACTION_CACHE_PREPARE_UPDATE;
02774   }
02775 }
02776 
02777 
02778 ///////////////////////////////////////////////////////////////////////////////
02779 // Name       : build_response_from_cache()
02780 // Description: build a client response from cached response and client request
02781 //
02782 // Input      : State, warning code to be inserted into the response header
02783 // Output     :
02784 //
02785 // Details    : This function is called if we decided to serve a client request
02786 //              using a cached response.
02787 //              It is called by handle_server_connection_not_open()
02788 //              and HandleCacheOpenReadHit().
02789 //
02790 ///////////////////////////////////////////////////////////////////////////////
02791 void
02792 HttpTransact::build_response_from_cache(State* s, HTTPWarningCode warning_code)
02793 {
02794   HTTPHdr *client_request = &s->hdr_info.client_request;
02795   HTTPHdr *cached_response = NULL;
02796   HTTPHdr *to_warn = &s->hdr_info.client_response;
02797   CacheHTTPInfo *obj;
02798 
02799   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
02800     obj = &s->cache_info.object_store;
02801     ink_assert(obj->valid());
02802   } else
02803     obj = s->cache_info.object_read;
02804   cached_response = obj->response_get();
02805 
02806   // If the client request is conditional, and the cached copy meets
02807   // the conditions, do not need to send back the full document,
02808   // just a NOT_MODIFIED response.
02809   // If the request is not conditional,
02810   // the function match_response_to_request_conditionals() returns
02811   // the code of the cached response, which means that we should send
02812   // back the full document.
02813   HTTPStatus client_response_code =
02814     HttpTransactCache::match_response_to_request_conditionals(client_request, cached_response);
02815 
02816   switch (client_response_code) {
02817   case HTTP_STATUS_NOT_MODIFIED:
02818     // A IMS or INM GET client request with conditions being met
02819     // by the cached response.  Send back a NOT MODIFIED response.
02820     DebugTxn("http_trans", "[build_response_from_cache] Not modified");
02821     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_HIT_CONDITIONAL);
02822 
02823     build_response(s, cached_response, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
02824     s->cache_info.action = CACHE_DO_NO_ACTION;
02825     s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
02826     break;
02827 
02828   case HTTP_STATUS_PRECONDITION_FAILED:
02829     // A conditional request with conditions not being met by the cached
02830     // response.  Send back a PRECONDITION FAILED response.
02831     DebugTxn("http_trans", "[build_response_from_cache] Precondition Failed");
02832     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_CONDITIONAL);
02833 
02834     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
02835     s->cache_info.action = CACHE_DO_NO_ACTION;
02836     s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
02837     break;
02838 
02839   case HTTP_STATUS_RANGE_NOT_SATISFIABLE:
02840     // Check if cached response supports Range. If it does, append
02841     // Range transformation plugin
02842     // A little misnomer. HTTP_STATUS_RANGE_NOT_SATISFIABLE
02843     // acutally means If-Range match fails here.
02844     // fall through
02845   default:
02846     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_HIT_SERVED);
02847     if (s->method == HTTP_WKSIDX_GET || s->api_resp_cacheable == true) {
02848       // send back the full document to the client.
02849       DebugTxn("http_trans", "[build_response_from_cache] Match! Serving full document.");
02850       s->cache_info.action = CACHE_DO_SERVE;
02851 
02852       // Check if cached response supports Range. If it does, append
02853       // Range transformation plugin
02854       // only if the cached response is a 200 OK
02855       if (client_response_code == HTTP_STATUS_OK && client_request->presence(MIME_PRESENCE_RANGE)) {
02856         s->state_machine->do_range_setup_if_necessary();
02857         if (s->range_setup == RANGE_NOT_SATISFIABLE) {
02858           build_error_response(s, HTTP_STATUS_RANGE_NOT_SATISFIABLE, "Requested Range Not Satisfiable", "default", NULL);
02859           s->cache_info.action = CACHE_DO_NO_ACTION;
02860           s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
02861           break;
02862         } else if (s->range_setup == RANGE_NOT_HANDLED) {
02863           // we switch to tunneling for Range requests if it is out of order.
02864           // In that case we fetch the entire source so it's OK to switch
02865           // this late.
02866           DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadHit] Out-of-order Range request - tunneling");
02867           s->cache_info.action = CACHE_DO_NO_ACTION;
02868           if (s->force_dns) {
02869             HandleCacheOpenReadMiss(s); // DNS is already completed no need of doing DNS
02870           } else {
02871             TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, OSDNSLookup);   // DNS not done before need to be done now as we are connecting to OS
02872           }
02873           return;
02874         }
02875       }
02876 
02877       if (s->state_machine->do_transform_open()) {
02878         set_header_for_transform(s, cached_response);
02879         to_warn = &s->hdr_info.transform_response;
02880       } else {
02881         build_response(s, cached_response, &s->hdr_info.client_response, s->client_info.http_version);
02882       }
02883       s->next_action = SM_ACTION_SERVE_FROM_CACHE;
02884     }
02885     // If the client request is a HEAD, then serve the header from cache.
02886     else if (s->method == HTTP_WKSIDX_HEAD) {
02887       DebugTxn("http_trans", "[build_response_from_cache] Match! Serving header only.");
02888 
02889       build_response(s, cached_response, &s->hdr_info.client_response, s->client_info.http_version);
02890       s->cache_info.action = CACHE_DO_NO_ACTION;
02891       s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
02892     } else {
02893       // We handled the request but it's not GET or HEAD (eg. DELETE),
02894       // and server is not reacheable: 502
02895       //
02896       DebugTxn("http_trans", "[build_response_from_cache] No match! Connection failed.");
02897       build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Connection Failed", "connect#failed_connect", NULL);
02898       s->cache_info.action = CACHE_DO_NO_ACTION;
02899       s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
02900       warning_code = HTTP_WARNING_CODE_NONE;
02901     }
02902     break;
02903   }
02904 
02905   // After building the client response, add the given warning if provided.
02906   if (warning_code != HTTP_WARNING_CODE_NONE) {
02907     delete_warning_value(to_warn, warning_code);
02908     HttpTransactHeaders::insert_warning_header(s->http_config_param, to_warn, warning_code);
02909   }
02910 }
02911 
02912 ///////////////////////////////////////////////////////////////////////////////
02913 // Name       : handle_cache_write_lock
02914 // Description:
02915 //
02916 // Details    :
02917 //
02918 //
02919 //
02920 // Possible Next States From Here:
02921 // - result of how_to_open_connection
02922 //
02923 ///////////////////////////////////////////////////////////////////////////////
02924 void
02925 HttpTransact::handle_cache_write_lock(State* s)
02926 {
02927   bool remove_ims = false;
02928 
02929   ink_assert(s->cache_info.action == CACHE_PREPARE_TO_DELETE ||
02930              s->cache_info.action == CACHE_PREPARE_TO_UPDATE || s->cache_info.action == CACHE_PREPARE_TO_WRITE);
02931 
02932   switch (s->cache_info.write_lock_state) {
02933   case CACHE_WL_SUCCESS:
02934     // We were able to get the lock for the URL vector in the cache
02935     SET_UNPREPARE_CACHE_ACTION(s->cache_info);
02936     break;
02937   case CACHE_WL_FAIL:
02938     // No write lock, ignore the cache and proxy only;
02939     // FIX: Should just serve from cache if this is a revalidate
02940     s->cache_info.action = CACHE_DO_NO_ACTION;
02941     s->cache_info.write_status = CACHE_WRITE_LOCK_MISS;
02942     remove_ims = true;
02943     break;
02944   case CACHE_WL_READ_RETRY:
02945     //  Write failed but retried and got a vector to read
02946     //  We need to clean up our state so that transact does
02947     //  not assert later on.  Then handle the open read hit
02948     //
02949     s->request_sent_time = UNDEFINED_TIME;
02950     s->response_received_time = UNDEFINED_TIME;
02951     s->cache_info.action = CACHE_DO_LOOKUP;
02952     remove_ims = true;
02953     SET_VIA_STRING(VIA_DETAIL_CACHE_TYPE, VIA_DETAIL_CACHE);
02954     break;
02955   case CACHE_WL_INIT:
02956   default:
02957     ink_release_assert(0);
02958     break;
02959   }
02960 
02961   // Since we've already built the server request and we can't get the write
02962   //  lock we need to remove the ims field from the request since we're
02963   //  ignoring the cache.  If their is a client ims field, copy that since
02964   //  we're tunneling response anyway
02965   if (remove_ims) {
02966     s->hdr_info.server_request.field_delete(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE);
02967     s->hdr_info.server_request.field_delete(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH);
02968     MIMEField *c_ims = s->hdr_info.client_request.field_find(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE);
02969     MIMEField *c_inm = s->hdr_info.client_request.field_find(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH);
02970 
02971     if (c_ims) {
02972       int len;
02973       const char *value = c_ims->value_get(&len);
02974       s->hdr_info.server_request.value_set(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE, value, len);
02975     }
02976     if (c_inm) {
02977       int len;
02978       const char *value = c_inm->value_get(&len);
02979       s->hdr_info.server_request.value_set(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH, value, len);
02980     }
02981   }
02982 
02983   if (s->cache_info.write_lock_state == CACHE_WL_READ_RETRY) {
02984     s->hdr_info.server_request.destroy();
02985     HandleCacheOpenReadHitFreshness(s);
02986   } else {
02987     StateMachineAction_t next;
02988     if (s->stale_icp_lookup == false) {
02989       next = how_to_open_connection(s);
02990       if (next == SM_ACTION_ORIGIN_SERVER_OPEN || next == SM_ACTION_ORIGIN_SERVER_RAW_OPEN) {
02991         s->next_action = next;
02992         TRANSACT_RETURN(next, NULL);
02993       } else {
02994         // hehe!
02995         s->next_action = next;
02996         ink_assert(s->next_action == SM_ACTION_DNS_LOOKUP);
02997         return;
02998       }
02999     } else {
03000       next = SM_ACTION_ICP_QUERY;
03001     }
03002 
03003     TRANSACT_RETURN(next, NULL);
03004   }
03005 }
03006 
03007 ///////////////////////////////////////////////////////////////////////////////
03008 // Name       : HandleCacheOpenReadMiss
03009 // Description: cache looked up, miss or hit, but needs authorization
03010 //
03011 // Details    :
03012 //
03013 //
03014 //
03015 // Possible Next States From Here:
03016 // - HttpTransact::ICP_QUERY;
03017 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
03018 // - HttpTransact::ORIGIN_SERVER_OPEN;
03019 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
03020 // - result of how_to_open_connection()
03021 //
03022 ///////////////////////////////////////////////////////////////////////////////
03023 void
03024 HttpTransact::HandleCacheOpenReadMiss(State* s)
03025 {
03026   DebugTxn("http_trans", "[HandleCacheOpenReadMiss] --- MISS");
03027   DebugTxn("http_seq", "[HttpTransact::HandleCacheOpenReadMiss] " "Miss in cache");
03028 
03029   if (delete_all_document_alternates_and_return(s, false)) {
03030     DebugTxn("http_trans", "[HandleCacheOpenReadMiss] Delete and return");
03031     s->cache_info.action = CACHE_DO_NO_ACTION;
03032     s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
03033     return;
03034   }
03035   // reinitialize some variables to reflect cache miss state.
03036   s->cache_info.object_read = NULL;
03037   s->request_sent_time = UNDEFINED_TIME;
03038   s->response_received_time = UNDEFINED_TIME;
03039   SET_VIA_STRING(VIA_CACHE_RESULT, VIA_CACHE_MISS);
03040   if (GET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP) == ' ') {
03041     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_NOT_CACHED);
03042   }
03043   // We do a cache lookup for DELETE and PUT requests as well.
03044   // We must, however, not cache the responses to these requests.
03045   if (does_method_require_cache_copy_deletion(s->http_config_param, s->method) && s->api_req_cacheable == false) {
03046     s->cache_info.action = CACHE_DO_NO_ACTION;
03047   } else if ((s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE) && !s->txn_conf->cache_range_write) ||
03048              s->range_setup == RANGE_NOT_SATISFIABLE || s->range_setup == RANGE_NOT_HANDLED) {
03049     s->cache_info.action = CACHE_DO_NO_ACTION;
03050   } else {
03051     s->cache_info.action = CACHE_PREPARE_TO_WRITE;
03052   }
03053 
03054   // We should not issue an ICP lookup if the request has a
03055   // no-cache header. First check if the request has a no
03056   // cache header. Then, if icp is enabled and the request
03057   // does not have a no-cache header, issue an icp lookup.
03058 
03059   // does the request have a no-cache?
03060   bool no_cache_in_request = false;
03061 
03062   if (s->hdr_info.client_request.is_pragma_no_cache_set() ||
03063       s->hdr_info.client_request.is_cache_control_set(HTTP_VALUE_NO_CACHE)) {
03064     no_cache_in_request = true;
03065   }
03066   // if ICP is enabled and above test indicates that request
03067   // does not have a no-cache, issue icp query to sibling cache.
03068   if (s->http_config_param->icp_enabled && icp_dynamic_enabled != 0 && (no_cache_in_request == false)) {
03069     DebugTxn("http_trans", "[HandleCacheOpenReadMiss] " "ICP is configured and no no-cache in request; checking ICP");
03070     s->next_action = SM_ACTION_ICP_QUERY;
03071     return;
03072   }
03073   ///////////////////////////////////////////////////////////////
03074   // a normal miss would try to fetch the document from the    //
03075   // origin server, unless the origin server isn't resolvable, //
03076   // but if "CacheControl: only-if-cached" is set, then we are //
03077   // supposed to send a 504 (GATEWAY TIMEOUT) response.        //
03078   ///////////////////////////////////////////////////////////////
03079 
03080   HTTPHdr *h = &s->hdr_info.client_request;
03081 
03082   if (!h->is_cache_control_set(HTTP_VALUE_ONLY_IF_CACHED)) {
03083     find_server_and_update_current_info(s);
03084     if (!ats_is_ip(&s->current.server->addr)) {
03085       ink_release_assert(s->current.request_to == PARENT_PROXY ||
03086                   s->http_config_param->no_dns_forward_to_parent != 0);
03087       if (s->current.request_to == PARENT_PROXY) {
03088         TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, HttpTransact::PPDNSLookup);
03089       } else {
03090         handle_parent_died(s);
03091         return;
03092       }
03093     }
03094     build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
03095 
03096     s->next_action = how_to_open_connection(s);
03097   } else {                      // miss, but only-if-cached is set
03098     build_error_response(s, HTTP_STATUS_GATEWAY_TIMEOUT, "Not Cached", "cache#not_in_cache", NULL);
03099     s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
03100   }
03101 
03102   return;
03103 }
03104 
03105 
03106 ///////////////////////////////////////////////////////////////////////////////
03107 // Name       : HandleICPLookup
03108 // Description:
03109 //
03110 // Details    :
03111 //
03112 //
03113 //
03114 // Possible Next States From Here:
03115 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
03116 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
03117 // - result of how_to_open_connection()
03118 //
03119 ///////////////////////////////////////////////////////////////////////////////
03120 void
03121 HttpTransact::HandleICPLookup(State* s)
03122 {
03123   SET_VIA_STRING(VIA_DETAIL_CACHE_TYPE, VIA_DETAIL_ICP);
03124   if (s->icp_lookup_success == true) {
03125     HTTP_INCREMENT_TRANS_STAT(http_icp_suggested_lookups_stat);
03126     DebugTxn("http_trans", "[HandleICPLookup] Success, sending request to icp suggested host.");
03127     ats_ip4_set(&s->icp_info.addr, s->icp_ip_result.sin_addr.s_addr);
03128     s->icp_info.port = ntohs(s->icp_ip_result.sin_port);
03129 
03130     // TODO in this case we should go to the miss case
03131     // just a little shy about using goto's, that's all.
03132     ink_release_assert(
03133         (s->icp_info.port != s->client_info.port) || 
03134         (ats_ip_addr_cmp(&s->icp_info.addr.sa, &Machine::instance()->ip.sa) != 0)
03135     );        
03136 
03137     // Since the ICPDNSLookup is not called, these two
03138     //   values are not initialized.
03139     // Force them to be initialized
03140     s->icp_info.http_version.set(1, 0);
03141     if (!s->txn_conf->keep_alive_enabled_out) {
03142       s->icp_info.keep_alive = HTTP_NO_KEEPALIVE;
03143     } else {
03144       s->icp_info.keep_alive = HTTP_KEEPALIVE;
03145     }
03146 
03147     s->icp_info.name = (char *) s->arena.alloc(17);
03148     unsigned char *p = (unsigned char *) &s->icp_ip_result.sin_addr.s_addr;
03149     snprintf(s->icp_info.name, 17, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
03150 
03151     update_current_info(&s->current, &s->icp_info, ICP_SUGGESTED_HOST, 1);
03152     s->next_hop_scheme = URL_WKSIDX_HTTP;
03153   } else {
03154     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_NOT_CACHED);
03155     DebugTxn("http_trans", "[HandleICPLookup] Failure, sending request to forward server.");
03156     s->parent_info.name = NULL;
03157     ink_zero(s->parent_info.addr);
03158 
03159     find_server_and_update_current_info(s);
03160     if (!ats_is_ip(&s->current.server->addr)) {
03161       if (s->current.request_to == PARENT_PROXY) {
03162         TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
03163       } else {
03164         ink_release_assert(0);
03165       }
03166       return;
03167     }
03168   }
03169   if (!s->stale_icp_lookup) {
03170     build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
03171   } else {
03172     ink_assert(s->hdr_info.server_request.valid());
03173     s->stale_icp_lookup = false;
03174   }
03175   s->next_action = how_to_open_connection(s);
03176 
03177   return;
03178 }
03179 
03180 
03181 ///////////////////////////////////////////////////////////////////////////////
03182 // Name       : OriginServerRawOpen
03183 // Description: called for ssl tunneling
03184 //
03185 // Details    :
03186 //
03187 // when the method is CONNECT, we open a raw connection to the origin
03188 // server. if the open succeeds, then do ssl tunneling from the client
03189 // to the host.
03190 //
03191 //
03192 // Possible Next States From Here:
03193 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
03194 // - HttpTransact::SSL_TUNNEL;
03195 //
03196 ///////////////////////////////////////////////////////////////////////////////
03197 void
03198 HttpTransact::OriginServerRawOpen(State* s)
03199 {
03200   DebugTxn("http_trans", "[HttpTransact::OriginServerRawOpen]");
03201 
03202   switch (s->current.state) {
03203   case STATE_UNDEFINED:
03204     /* fall through */
03205   case OPEN_RAW_ERROR:
03206     /* fall through */
03207   case CONNECTION_ERROR:
03208     /* fall through */
03209   case CONNECTION_CLOSED:
03210     /* fall through */
03211   case CONGEST_CONTROL_CONGESTED_ON_F:
03212     /* fall through */
03213   case CONGEST_CONTROL_CONGESTED_ON_M:
03214     handle_server_died(s);
03215 
03216     ink_assert(s->cache_info.action == CACHE_DO_NO_ACTION);
03217     s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
03218     break;
03219   case CONNECTION_ALIVE:
03220     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, HTTP_STATUS_OK);
03221 
03222     DebugTxn("http_trans", "[OriginServerRawOpen] connection alive. next action is ssl_tunnel");
03223     s->next_action = SM_ACTION_SSL_TUNNEL;
03224     break;
03225   default:
03226     ink_assert(!("s->current.state is set to something unsupported"));
03227     break;
03228   }
03229 
03230   return;
03231 }
03232 
03233 ///////////////////////////////////////////////////////////////////////////////
03234 // Name       : HandleResponse
03235 // Description: called from the state machine when a response is received
03236 //
03237 // Details    :
03238 //
03239 //   This is the entry into a coin-sorting machine. There are many different
03240 //   bins that the response can fall into. First, the response can be invalid
03241 //   if for example it is not a response, or not complete, or the connection
03242 //   was closed, etc. Then, the response can be from an icp-suggested-host,
03243 //   from a parent proxy or from the origin server. The next action to take
03244 //   differs for all three of these cases. Finally, good responses can either
03245 //   require a cache action, be it deletion, update, or writing or may just
03246 //   need to be tunnelled to the client. This latter case should be handled
03247 //   with as little processing as possible, since it should represent a fast
03248 //   path.
03249 //
03250 //
03251 // Possible Next States From Here:
03252 //
03253 //
03254 ///////////////////////////////////////////////////////////////////////////////
03255 void
03256 HttpTransact::HandleResponse(State* s)
03257 {
03258   DebugTxn("http_trans", "[HttpTransact::HandleResponse]");
03259   DebugTxn("http_seq", "[HttpTransact::HandleResponse] Response received");
03260 
03261   s->source = SOURCE_HTTP_ORIGIN_SERVER;
03262   s->response_received_time = ink_cluster_time();
03263   ink_assert(s->response_received_time >= s->request_sent_time);
03264   s->current.now = s->response_received_time;
03265 
03266   DebugTxn("http_trans", "[HandleResponse] response_received_time: %" PRId64, (int64_t)s->response_received_time);
03267   if (!s->cop_test_page)
03268     DUMP_HEADER("http_hdrs", &s->hdr_info.server_response, s->state_machine_id, "Incoming O.S. Response");
03269 
03270   HTTP_INCREMENT_TRANS_STAT(http_incoming_responses_stat);
03271 
03272   ink_release_assert(s->current.request_to != UNDEFINED_LOOKUP);
03273   if (s->cache_info.action != CACHE_DO_WRITE) {
03274     ink_release_assert(s->cache_info.action != CACHE_DO_LOOKUP);
03275     ink_release_assert(s->cache_info.action != CACHE_DO_SERVE);
03276     ink_release_assert(s->cache_info.action != CACHE_PREPARE_TO_DELETE);
03277     ink_release_assert(s->cache_info.action != CACHE_PREPARE_TO_UPDATE);
03278     ink_release_assert(s->cache_info.action != CACHE_PREPARE_TO_WRITE);
03279   }
03280 
03281   if (!is_response_valid(s, &s->hdr_info.server_response)) {
03282     DebugTxn("http_seq", "[HttpTransact::HandleResponse] Response not valid");
03283   } else {
03284     DebugTxn("http_seq", "[HttpTransact::HandleResponse] Response valid");
03285     initialize_state_variables_from_response(s, &s->hdr_info.server_response);
03286   }
03287 
03288   switch (s->current.request_to) {
03289   case ICP_SUGGESTED_HOST:
03290     handle_response_from_icp_suggested_host(s);
03291     break;
03292   case PARENT_PROXY:
03293     handle_response_from_parent(s);
03294     break;
03295   case ORIGIN_SERVER:
03296     handle_response_from_server(s);
03297     break;
03298   default:
03299     ink_assert(!("s->current.request_to is not ICP, P.P. or O.S. - hmmm."));
03300     break;
03301   }
03302 
03303   return;
03304 }
03305 
03306 ///////////////////////////////////////////////////////////////////////////////
03307 // Name       : HandleUpdateCachedObject
03308 // Description: called from the state machine when we are going to modify
03309 //              headers without any server contact.
03310 //
03311 // Details    : this function does very little. mainly to satisfy
03312 //              the call_transact_and_set_next format and not affect
03313 //              the performance of the non-invalidate operations, which
03314 //              are the majority
03315 //
03316 // Possible Next States From Here:
03317 //
03318 ///////////////////////////////////////////////////////////////////////////////
03319 void
03320 HttpTransact::HandleUpdateCachedObject(State* s)
03321 {
03322   if (s->cache_info.write_lock_state == HttpTransact::CACHE_WL_SUCCESS) {
03323     ink_assert(s->cache_info.object_store.valid());
03324     ink_assert(s->cache_info.object_store.response_get() != NULL);
03325     ink_assert(s->cache_info.object_read != NULL);
03326     ink_assert(s->cache_info.object_read->valid());
03327 
03328     if (!s->cache_info.object_store.request_get())
03329       s->cache_info.object_store.request_set(s->cache_info.object_read->request_get());
03330     s->request_sent_time = s->cache_info.object_read->request_sent_time_get();
03331     s->response_received_time = s->cache_info.object_read->response_received_time_get();
03332     if (s->api_update_cached_object == UPDATE_CACHED_OBJECT_CONTINUE) {
03333       TRANSACT_RETURN(SM_ACTION_CACHE_ISSUE_UPDATE, HttpTransact::HandleUpdateCachedObjectContinue);
03334     } else {
03335       TRANSACT_RETURN(SM_ACTION_CACHE_ISSUE_UPDATE, HttpTransact::HandleApiErrorJump);
03336     }
03337   } else if (s->api_update_cached_object == UPDATE_CACHED_OBJECT_CONTINUE) {
03338     // even failed to update, continue to serve from cache
03339     HandleUpdateCachedObjectContinue(s);
03340   } else {
03341     s->api_update_cached_object = UPDATE_CACHED_OBJECT_FAIL;
03342     HandleApiErrorJump(s);
03343   }
03344 }
03345 
03346 void
03347 HttpTransact::HandleUpdateCachedObjectContinue(State* s)
03348 {
03349   ink_assert(s->api_update_cached_object == UPDATE_CACHED_OBJECT_CONTINUE);
03350   s->cache_info.action = s->saved_update_cache_action;
03351   s->next_action = s->saved_update_next_action;
03352 }
03353 
03354 ///////////////////////////////////////////////////////////////////////////////
03355 // Name       : HandleStatPage
03356 // Description: called from the state machine when a response is received
03357 //
03358 // Details    :
03359 //
03360 //
03361 //
03362 // Possible Next States From Here:
03363 //
03364 ///////////////////////////////////////////////////////////////////////////////
03365 void
03366 HttpTransact::HandleStatPage(State* s)
03367 {
03368   HTTPStatus status;
03369 
03370   if (s->internal_msg_buffer) {
03371     status = HTTP_STATUS_OK;
03372   } else {
03373     status = HTTP_STATUS_BAD_REQUEST;
03374   }
03375 
03376   build_response(s, &s->hdr_info.client_response, s->client_info.http_version, status);
03377 
03378   ///////////////////////////
03379   // insert content-length //
03380   ///////////////////////////
03381   s->hdr_info.client_response.set_content_length(s->internal_msg_buffer_size);
03382 
03383   if (s->internal_msg_buffer_type) {
03384     int len = strlen(s->internal_msg_buffer_type);
03385 
03386     if (len > 0) {
03387       s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE,
03388                                             s->internal_msg_buffer_type, len);
03389     }
03390   } else {
03391     s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE,
03392                                           "text/plain", 9);
03393   }
03394 
03395   s->cache_info.action = CACHE_DO_NO_ACTION;
03396   s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
03397 }
03398 
03399 ///////////////////////////////////////////////////////////////////////////////
03400 // Name       : handle_response_from_icp_suggested_host
03401 // Description: response came from the host suggested by the icp lookup
03402 //
03403 // Details    :
03404 //
03405 //   If the response was bad (for whatever reason), may try to open a
03406 //   connection with a parent proxy, if there are any, else the request
03407 //   should be sent to the client.
03408 //   If the response is good, handle_forward_server_connection_open is
03409 //   called.
03410 //
03411 //
03412 // Possible Next States From Here:
03413 //
03414 ///////////////////////////////////////////////////////////////////////////////
03415 void
03416 HttpTransact::handle_response_from_icp_suggested_host(State* s)
03417 {
03418   DebugTxn("http_trans", "[handle_response_from_icp_suggested_host] (hrfish)");
03419   HTTP_RELEASE_ASSERT(s->current.server == &s->icp_info);
03420 
03421   s->icp_info.state = s->current.state;
03422   switch (s->current.state) {
03423   case CONNECTION_ALIVE:
03424     DebugTxn("http_trans", "[hrfish] connection alive");
03425     SET_VIA_STRING(VIA_DETAIL_ICP_CONNECT, VIA_DETAIL_ICP_SUCCESS);
03426     handle_forward_server_connection_open(s);
03427     break;
03428   default:
03429     DebugTxn("http_trans", "[hrfish] connection not alive");
03430     SET_VIA_STRING(VIA_DETAIL_ICP_CONNECT, VIA_DETAIL_ICP_FAILURE);
03431 
03432     // If the request is not retryable, bail
03433     if (is_request_retryable(s) == false) {
03434       handle_server_died(s);
03435       return;
03436     }
03437     // send request to parent proxy now if there is
03438     // one or else directly to the origin server.
03439     find_server_and_update_current_info(s);
03440     if (!ats_is_ip(&s->current.server->addr)) {
03441       if (s->current.request_to == PARENT_PROXY) {
03442         TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
03443       } else {
03444         ink_release_assert(0);
03445       }
03446       return;
03447     }
03448     ink_assert(&s->hdr_info.server_request);
03449     s->next_action = how_to_open_connection(s);
03450     if (s->current.server == &s->server_info && s->next_hop_scheme == URL_WKSIDX_HTTP) {
03451       HttpTransactHeaders::remove_host_name_from_url(&s->hdr_info.server_request);
03452     }
03453     break;
03454   }
03455 }
03456 
03457 
03458 ///////////////////////////////////////////////////////////////////////////////
03459 // Name       : handle_response_from_parent
03460 // Description: response came from a parent proxy
03461 //
03462 // Details    :
03463 //
03464 //   The configuration file can be used to specify more than one parent
03465 //   proxy. If a connection to one fails, another can be looked up. This
03466 //   function handles responses from parent proxies. If the response is
03467 //   bad the next parent proxy (if any) is looked up. If there are no more
03468 //   parent proxies that can be looked up, the response is sent to the
03469 //   origin server. If the response is good handle_forward_server_connection_open
03470 //   is called, as with handle_response_from_icp_suggested_host.
03471 //
03472 //
03473 // Possible Next States From Here:
03474 //
03475 ///////////////////////////////////////////////////////////////////////////////
03476 void
03477 HttpTransact::handle_response_from_parent(State* s)
03478 {
03479   DebugTxn("http_trans", "[handle_response_from_parent] (hrfp)");
03480   HTTP_RELEASE_ASSERT(s->current.server == &s->parent_info);
03481 
03482   s->parent_info.state = s->current.state;
03483   switch (s->current.state) {
03484   case CONNECTION_ALIVE:
03485     DebugTxn("http_trans", "[hrfp] connection alive");
03486     s->current.server->connect_result = 0;
03487     SET_VIA_STRING(VIA_DETAIL_PP_CONNECT, VIA_DETAIL_PP_SUCCESS);
03488     if (s->parent_result.retry) {
03489       s->parent_params->recordRetrySuccess(&s->parent_result);
03490     }
03491     handle_forward_server_connection_open(s);
03492     break;
03493   default:
03494     {
03495       LookingUp_t next_lookup = UNDEFINED_LOOKUP;
03496       DebugTxn("http_trans", "[hrfp] connection not alive");
03497       SET_VIA_STRING(VIA_DETAIL_PP_CONNECT, VIA_DETAIL_PP_FAILURE);
03498 
03499       ink_assert(s->hdr_info.server_request.valid());
03500 
03501       s->current.server->connect_result = ENOTCONN;
03502 
03503       char addrbuf[INET6_ADDRSTRLEN];
03504       DebugTxn("http_trans", "[%d] failed to connect to parent %s", s->current.attempts,
03505             ats_ip_ntop(&s->current.server->addr.sa, addrbuf, sizeof(addrbuf)));
03506 
03507       // If the request is not retryable, just give up!
03508       if (!is_request_retryable(s)) {
03509         s->parent_params->markParentDown(&s->parent_result);
03510         s->parent_result.r = PARENT_FAIL;
03511         handle_parent_died(s);
03512         return;
03513       }
03514 
03515       if (s->current.attempts < s->http_config_param->parent_connect_attempts) {
03516         s->current.attempts++;
03517 
03518         // Are we done with this particular parent?
03519         if ((s->current.attempts - 1) % s->http_config_param->per_parent_connect_attempts != 0) {
03520           // No we are not done with this parent so retry
03521           s->next_action = how_to_open_connection(s);
03522           DebugTxn("http_trans", "%s Retrying parent for attempt %d, max %" PRId64,
03523                 "[handle_response_from_parent]", s->current.attempts, s->http_config_param->per_parent_connect_attempts);
03524           return;
03525         } else {
03526           DebugTxn("http_trans", "%s %d per parent attempts exhausted",
03527                 "[handle_response_from_parent]", s->current.attempts);
03528 
03529           // Only mark the parent down if we failed to connect
03530           //  to the parent otherwise slow origin servers cause
03531           //  us to mark the parent down
03532           if (s->current.state == CONNECTION_ERROR) {
03533             s->parent_params->markParentDown(&s->parent_result);
03534           }
03535           // We are done so look for another parent if any
03536           next_lookup = find_server_and_update_current_info(s);
03537         }
03538       } else {
03539         // Done trying parents... fail over to origin server if that is
03540         //   appropriate
03541         DebugTxn("http_trans", "[handle_response_from_parent] Error. No more retries.");
03542         s->parent_params->markParentDown(&s->parent_result);
03543         s->parent_result.r = PARENT_FAIL;
03544         next_lookup = find_server_and_update_current_info(s);
03545       }
03546 
03547       // We have either tried to find a new parent or failed over to the
03548       //   origin server
03549       switch (next_lookup) {
03550       case PARENT_PROXY:
03551         ink_assert(s->current.request_to == PARENT_PROXY);
03552         TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
03553         break;
03554       case ORIGIN_SERVER:
03555         s->current.attempts = 0;
03556         s->next_action = how_to_open_connection(s);
03557         if (s->current.server == &s->server_info && s->next_hop_scheme == URL_WKSIDX_HTTP) {
03558           HttpTransactHeaders::remove_host_name_from_url(&s->hdr_info.server_request);
03559         }
03560         break;
03561       case HOST_NONE:
03562         handle_parent_died(s);
03563         break;
03564       default:
03565         // This handles:
03566         // UNDEFINED_LOOKUP, ICP_SUGGESTED_HOST,
03567         // INCOMING_ROUTER
03568         break;
03569       }
03570 
03571       break;
03572     }
03573   }
03574 }
03575 
03576 
03577 ///////////////////////////////////////////////////////////////////////////////
03578 // Name       : handle_response_from_server
03579 // Description: response is from the origin server
03580 //
03581 // Details    :
03582 //
03583 //   response from the origin server. one of three things can happen now.
03584 //   if the response is bad, then we can either retry (by first downgrading
03585 //   the request, maybe making it non-keepalive, etc.), or we can give up.
03586 //   the latter case is handled by handle_server_connection_not_open and
03587 //   sends an error response back to the client. if the response is good
03588 //   handle_forward_server_connection_open is called.
03589 //
03590 //
03591 // Possible Next States From Here:
03592 //
03593 ///////////////////////////////////////////////////////////////////////////////
03594 void
03595 HttpTransact::handle_response_from_server(State* s)
03596 {
03597   DebugTxn("http_trans", "[handle_response_from_server] (hrfs)");
03598   HTTP_RELEASE_ASSERT(s->current.server == &s->server_info);
03599   int max_connect_retries = 0;
03600 
03601   // plugin call
03602   s->server_info.state = s->current.state;
03603   if (s->fp_tsremap_os_response) {
03604     s->fp_tsremap_os_response(s->remap_plugin_instance, reinterpret_cast<TSHttpTxn>(s->state_machine), s->current.state);
03605   }
03606 
03607   switch (s->current.state) {
03608   case CONNECTION_ALIVE:
03609     DebugTxn("http_trans", "[hrfs] connection alive");
03610     SET_VIA_STRING(VIA_DETAIL_SERVER_CONNECT, VIA_DETAIL_SERVER_SUCCESS);
03611     s->current.server->clear_connect_fail();
03612     handle_forward_server_connection_open(s);
03613     break;
03614   case CONGEST_CONTROL_CONGESTED_ON_F:
03615   case CONGEST_CONTROL_CONGESTED_ON_M:
03616     DebugTxn("http_trans", "[handle_response_from_server] Error. congestion control -- congested.");
03617     SET_VIA_STRING(VIA_DETAIL_SERVER_CONNECT, VIA_DETAIL_SERVER_FAILURE);
03618     s->current.server->set_connect_fail(EUSERS); // too many users
03619     handle_server_connection_not_open(s);
03620     break;
03621   case OPEN_RAW_ERROR:
03622     /* fall through */
03623   case CONNECTION_ERROR:
03624     /* fall through */
03625   case STATE_UNDEFINED:
03626     /* fall through */
03627   case INACTIVE_TIMEOUT:
03628     /* fall through */
03629   case PARSE_ERROR:
03630     /* fall through */
03631   case CONNECTION_CLOSED:
03632     /* fall through */
03633   case BAD_INCOMING_RESPONSE:
03634     // Set to generic I/O error if not already set specifically.
03635     if (!s->current.server->had_connect_fail())
03636       s->current.server->set_connect_fail(EIO);
03637 
03638     if (is_server_negative_cached(s)) {
03639       max_connect_retries = s->txn_conf->connect_attempts_max_retries_dead_server;
03640     } else {
03641       // server not yet negative cached - use default number of retries
03642       max_connect_retries = s->txn_conf->connect_attempts_max_retries;
03643     }
03644     if (s->pCongestionEntry != NULL)
03645       max_connect_retries = s->pCongestionEntry->connect_retries();
03646 
03647     if (is_request_retryable(s) && s->current.attempts < max_connect_retries) {
03648       // If this is a round robin DNS entry & we're tried configured
03649       //    number of times, we should try another node
03650       if (DNSLookupInfo::OS_ADDR_TRY_CLIENT == s->dns_info.os_addr_style) {
03651         // attempt was based on client supplied server address. Try again
03652         // using HostDB.
03653         // Allow DNS attempt
03654         s->dns_info.lookup_success = false;
03655         // See if we can get data from HostDB for this.
03656         s->dns_info.os_addr_style = DNSLookupInfo::OS_ADDR_TRY_HOSTDB;
03657         // Force host resolution to have the same family as the client.
03658         // Because this is a transparent connection, we can't switch address
03659         // families - that is locked in by the client source address.
03660         s->state_machine->ua_session->host_res_style = ats_host_res_match(&s->current.server->addr.sa);
03661         TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, OSDNSLookup);
03662       } else if ((s->dns_info.srv_lookup_success || s->server_info.dns_round_robin) &&
03663                  (s->txn_conf->connect_attempts_rr_retries > 0) &&
03664                  (s->current.attempts % s->txn_conf->connect_attempts_rr_retries == 0)) {
03665         delete_server_rr_entry(s, max_connect_retries);
03666         return;
03667       } else {
03668         retry_server_connection_not_open(s, s->current.state, max_connect_retries);
03669         DebugTxn("http_trans", "[handle_response_from_server] Error. Retrying...");
03670         s->next_action = how_to_open_connection(s);
03671         return;
03672       }
03673     } else {
03674       DebugTxn("http_trans", "[handle_response_from_server] Error. No more retries.");
03675       SET_VIA_STRING(VIA_DETAIL_SERVER_CONNECT, VIA_DETAIL_SERVER_FAILURE);
03676       handle_server_connection_not_open(s);
03677     }
03678     break;
03679   case ACTIVE_TIMEOUT:
03680     DebugTxn("http_trans", "[hrfs] connection not alive");
03681     SET_VIA_STRING(VIA_DETAIL_SERVER_CONNECT, VIA_DETAIL_SERVER_FAILURE);
03682     s->current.server->set_connect_fail(ETIMEDOUT);
03683     handle_server_connection_not_open(s);
03684     break;
03685   default:
03686     ink_assert(!("s->current.state is set to something unsupported"));
03687     break;
03688   }
03689 
03690   return;
03691 }
03692 
03693 
03694 
03695 ///////////////////////////////////////////////////////////////////////////////
03696 // Name       : delete_server_rr_entry
03697 // Description:
03698 //
03699 // Details    :
03700 //
03701 //   connection to server failed mark down the server round robin entry
03702 //
03703 //
03704 // Possible Next States From Here:
03705 //
03706 ///////////////////////////////////////////////////////////////////////////////
03707 void
03708 HttpTransact::delete_server_rr_entry(State* s, int max_retries)
03709 {
03710   char addrbuf[INET6_ADDRSTRLEN];
03711   
03712   DebugTxn("http_trans", "[%d] failed to connect to %s", s->current.attempts,
03713         ats_ip_ntop(&s->current.server->addr.sa, addrbuf, sizeof(addrbuf)));
03714   DebugTxn("http_trans", "[delete_server_rr_entry] marking rr entry " "down and finding next one");
03715   ink_assert(s->current.server->had_connect_fail());
03716   ink_assert(s->current.request_to == ORIGIN_SERVER);
03717   ink_assert(s->current.server == &s->server_info);
03718   update_dns_info(&s->dns_info, &s->current, 0, &s->arena);
03719   s->current.attempts++;
03720   DebugTxn("http_trans", "[delete_server_rr_entry] attempts now: %d, max: %d", s->current.attempts, max_retries);
03721   TRANSACT_RETURN(SM_ACTION_ORIGIN_SERVER_RR_MARK_DOWN, ReDNSRoundRobin);
03722 }
03723 
03724 ///////////////////////////////////////////////////////////////////////////////
03725 // Name       : retry_server_connection_not_open
03726 // Description:
03727 //
03728 // Details    :
03729 //
03730 //   connection to server failed. retry.
03731 //
03732 //
03733 // Possible Next States From Here:
03734 //
03735 ///////////////////////////////////////////////////////////////////////////////
03736 void
03737 HttpTransact::retry_server_connection_not_open(State* s, ServerState_t conn_state, int max_retries)
03738 {
03739   ink_assert(s->current.state != CONNECTION_ALIVE);
03740   ink_assert(s->current.state != ACTIVE_TIMEOUT);
03741   ink_assert(s->current.attempts <= max_retries);
03742   ink_assert(s->current.server->had_connect_fail());
03743   char addrbuf[INET6_ADDRSTRLEN];
03744 
03745   char *url_string = s->hdr_info.client_request.url_string_get(&s->arena);
03746 
03747   DebugTxn("http_trans", "[%d] failed to connect [%d] to %s", s->current.attempts, conn_state,
03748         ats_ip_ntop(&s->current.server->addr.sa, addrbuf, sizeof(addrbuf)));
03749 
03750   //////////////////////////////////////////
03751   // on the first connect attempt failure //
03752   // record the failue                   //
03753   //////////////////////////////////////////
03754   if (0 == s->current.attempts)
03755     Log::error("CONNECT:[%d] could not connect [%s] to %s for '%s'",
03756      s->current.attempts,
03757      HttpDebugNames::get_server_state_name(conn_state),
03758      ats_ip_ntop(&s->current.server->addr.sa, addrbuf, sizeof(addrbuf)), url_string ? url_string : "<none>"
03759     );
03760 
03761   if (url_string) {
03762     s->arena.str_free(url_string);
03763   }
03764   //////////////////////////////////////////////
03765   // disable keep-alive for request and retry //
03766   //////////////////////////////////////////////
03767   s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
03768   s->current.attempts++;
03769 
03770   DebugTxn("http_trans", "[retry_server_connection_not_open] attempts now: %d, max: %d", s->current.attempts, max_retries);
03771 
03772   return;
03773 }
03774 
03775 ///////////////////////////////////////////////////////////////////////////////
03776 // Name       : handle_server_connection_not_open
03777 // Description:
03778 //
03779 // Details    :
03780 //
03781 //
03782 // Possible Next States From Here:
03783 //
03784 ///////////////////////////////////////////////////////////////////////////////
03785 void
03786 HttpTransact::handle_server_connection_not_open(State* s)
03787 {
03788   bool serve_from_cache = false;
03789 
03790   DebugTxn("http_trans", "[handle_server_connection_not_open] (hscno)");
03791   DebugTxn("http_seq", "[HttpTransact::handle_server_connection_not_open] ");
03792   ink_assert(s->current.state != CONNECTION_ALIVE);
03793   ink_assert(s->current.server->had_connect_fail());
03794 
03795   SET_VIA_STRING(VIA_SERVER_RESULT, VIA_SERVER_ERROR);
03796   HTTP_INCREMENT_TRANS_STAT(http_broken_server_connections_stat);
03797 
03798   // Fire off a hostdb update to mark the server as down
03799   s->state_machine->do_hostdb_update_if_necessary();
03800 
03801   switch (s->cache_info.action) {
03802   case CACHE_DO_UPDATE:
03803     serve_from_cache = is_stale_cache_response_returnable(s);
03804     break;
03805 
03806   case CACHE_PREPARE_TO_DELETE:
03807     /* fall through */
03808   case CACHE_PREPARE_TO_UPDATE:
03809     /* fall through */
03810   case CACHE_PREPARE_TO_WRITE:
03811     ink_release_assert(!"Why still preparing for cache action - " "we skipped a step somehow.");
03812     break;
03813 
03814   case CACHE_DO_LOOKUP:
03815     /* fall through */
03816   case CACHE_DO_SERVE:
03817     ink_assert(!("Why server response? Should have been a cache operation"));
03818     break;
03819 
03820   case CACHE_DO_DELETE:
03821     // decisions, decisions. what should we do here?
03822     // we could theoretically still delete the cached
03823     // copy or serve it back with a warning, or easier
03824     // just punt and biff the user. i say: biff the user.
03825     /* fall through */
03826   case CACHE_DO_UNDEFINED:
03827     /* fall through */
03828   case CACHE_DO_NO_ACTION:
03829     /* fall through */
03830   case CACHE_DO_WRITE:
03831     /* fall through */
03832   default:
03833     serve_from_cache = false;
03834     break;
03835   }
03836 
03837   if (serve_from_cache) {
03838     ink_assert(s->cache_info.object_read != NULL);
03839     ink_assert(s->cache_info.action == CACHE_DO_UPDATE);
03840     ink_assert(s->internal_msg_buffer == NULL);
03841 
03842     DebugTxn("http_trans", "[hscno] serving stale doc to client");
03843     build_response_from_cache(s, HTTP_WARNING_CODE_REVALIDATION_FAILED);
03844   } else {
03845     handle_server_died(s);
03846     s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
03847   }
03848 
03849   return;
03850 }
03851 
03852 
03853 ///////////////////////////////////////////////////////////////////////////////
03854 // Name       : handle_forward_server_connection_open
03855 // Description: connection to a forward server is open and good
03856 //
03857 // Details    :
03858 //
03859 //   "Forward server" includes the icp-suggested-host or the parent proxy
03860 //   or the origin server. This function first determines if the forward
03861 //   server uses HTTP 0.9, in which case it simply tunnels the response
03862 //   to the client. Else, it updates
03863 //
03864 //
03865 // Possible Next States From Here:
03866 //
03867 ///////////////////////////////////////////////////////////////////////////////
03868 void
03869 HttpTransact::handle_forward_server_connection_open(State* s)
03870 {
03871   DebugTxn("http_trans", "[handle_forward_server_connection_open] (hfsco)");
03872   DebugTxn("http_seq", "[HttpTransact::handle_server_connection_open] ");
03873   ink_release_assert(s->current.state == CONNECTION_ALIVE);
03874 
03875   if (s->hdr_info.server_response.version_get() == HTTPVersion(0, 9)) {
03876     DebugTxn("http_trans", "[hfsco] server sent 0.9 response, reading...");
03877     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, HTTP_STATUS_OK, "Connection Established");
03878 
03879     s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
03880     s->cache_info.action = CACHE_DO_NO_ACTION;
03881     s->next_action = SM_ACTION_SERVER_READ;
03882     return;
03883 
03884   }
03885   else if (s->hdr_info.server_response.version_get() == HTTPVersion(1, 0)) {
03886     if (s->current.server->http_version == HTTPVersion(0, 9)) {
03887       // update_hostdb_to_indicate_server_version_is_1_0
03888       s->updated_server_version = HostDBApplicationInfo::HTTP_VERSION_10;
03889     } else if (s->current.server->http_version == HTTPVersion(1, 1)) {
03890       // update_hostdb_to_indicate_server_version_is_1_0
03891       s->updated_server_version = HostDBApplicationInfo::HTTP_VERSION_10;
03892     } else {
03893       // dont update the hostdb. let us try again with what we currently think.
03894     }
03895   } else if (s->hdr_info.server_response.version_get() == HTTPVersion(1, 1)) {
03896     if (s->current.server->http_version == HTTPVersion(0, 9)) {
03897       // update_hostdb_to_indicate_server_version_is_1_1
03898       s->updated_server_version = HostDBApplicationInfo::HTTP_VERSION_11;
03899     } else if (s->current.server->http_version == HTTPVersion(1, 0)) {
03900       // update_hostdb_to_indicate_server_version_is_1_1
03901       s->updated_server_version = HostDBApplicationInfo::HTTP_VERSION_11;
03902     } else {
03903       // dont update the hostdb. let us try again with what we currently think.
03904     }
03905   } else {
03906     // dont update the hostdb. let us try again with what we currently think.
03907   }
03908 
03909   if (s->hdr_info.server_response.status_get() == HTTP_STATUS_CONTINUE) {
03910     handle_100_continue_response(s);
03911     return;
03912   }
03913 
03914   s->state_machine->do_hostdb_update_if_necessary();
03915 
03916   if (s->www_auth_content == CACHE_AUTH_FRESH) {
03917     // no update is needed - either to serve from cache if authorized,
03918     // or tunnnel the server response
03919     if (s->hdr_info.server_response.status_get() == HTTP_STATUS_OK) {
03920       // borrow a state variable used by the API function
03921       // this enable us to serve from cache without doing any updating
03922       s->api_server_response_ignore = true;
03923     }
03924     //s->cache_info.action = CACHE_PREPARE_TO_SERVE;
03925     // xing xing in the tunneling case, need to check when the cache_read_vc is closed, make sure the cache_read_vc is closed right away
03926   }
03927 
03928   CacheVConnection* cw_vc = s->state_machine->get_cache_sm().cache_write_vc;
03929 
03930   if (s->redirect_info.redirect_in_process && s->state_machine->enable_redirection) {
03931     if (s->cache_info.action == CACHE_DO_NO_ACTION) {
03932       switch (s->hdr_info.server_response.status_get())
03933       {
03934       case HTTP_STATUS_MULTIPLE_CHOICES:     //300
03935       case HTTP_STATUS_MOVED_PERMANENTLY:    //301
03936       case HTTP_STATUS_MOVED_TEMPORARILY:    //302
03937       case HTTP_STATUS_SEE_OTHER:            //303
03938       case HTTP_STATUS_USE_PROXY:            //305
03939       case HTTP_STATUS_TEMPORARY_REDIRECT:   //307
03940         break;
03941       default:
03942         DebugTxn("http_trans", "[hfsco] redirect in progress, non-3xx response, setting cache_do_write");
03943         if (cw_vc) s->cache_info.action = CACHE_DO_WRITE;
03944         break;
03945       }
03946     }
03947   }
03948 
03949   switch (s->cache_info.action) {
03950   case CACHE_DO_WRITE:
03951     /* fall through */
03952   case CACHE_DO_UPDATE:
03953     /* fall through */
03954   case CACHE_DO_DELETE:
03955     DebugTxn("http_trans", "[hfsco] cache action: %s", HttpDebugNames::get_cache_action_name(s->cache_info.action));
03956     handle_cache_operation_on_forward_server_response(s);
03957     break;
03958   case CACHE_PREPARE_TO_DELETE:
03959     /* fall through */
03960   case CACHE_PREPARE_TO_UPDATE:
03961     /* fall through */
03962   case CACHE_PREPARE_TO_WRITE:
03963     ink_release_assert(!"Why still preparing for cache action - we skipped a step somehow.");
03964     break;
03965   case CACHE_DO_LOOKUP:
03966     /* fall through */
03967   case CACHE_DO_SERVE:
03968     ink_assert(!("Why server response? Should have been a cache operation"));
03969     break;
03970   case CACHE_DO_UNDEFINED:
03971     /* fall through */
03972   case CACHE_DO_NO_ACTION:
03973     /* fall through */
03974   default:
03975     // Just tunnel?
03976     DebugTxn("http_trans", "[hfsco] cache action: %s", HttpDebugNames::get_cache_action_name(s->cache_info.action));
03977     handle_no_cache_operation_on_forward_server_response(s);
03978     break;
03979   }
03980 
03981   return;
03982 }
03983 
03984 // void HttpTransact::handle_100_continue_response(State* s)
03985 //
03986 //   We've received a 100 continue response.  Determine if
03987 //     we should just swallow the response 100 or forward it
03988 //     the client.  http-1.1-spec-rev-06 section 8.2.3
03989 //
03990 void
03991 HttpTransact::handle_100_continue_response(State* s)
03992 {
03993   bool forward_100 = false;
03994 
03995   HTTPVersion ver = s->hdr_info.client_request.version_get();
03996   if (ver == HTTPVersion(1, 1)) {
03997     forward_100 = true;
03998   } else if (ver == HTTPVersion(1, 0)) {
03999     if (s->hdr_info.client_request.value_get_int(MIME_FIELD_EXPECT, MIME_LEN_EXPECT) == 100) {
04000       forward_100 = true;
04001     }
04002   }
04003 
04004   if (forward_100) {
04005     // We just want to copy the server's response.  All
04006     //   the other build response functions insist on
04007     //   adding stuff
04008     build_response_copy(s, &s->hdr_info.server_response, &s->hdr_info.client_response, s->client_info.http_version);
04009     TRANSACT_RETURN(SM_ACTION_INTERNAL_100_RESPONSE, HandleResponse);
04010   } else {
04011     TRANSACT_RETURN(SM_ACTION_SERVER_PARSE_NEXT_HDR, HandleResponse);
04012   }
04013 }
04014 
04015 // void HttpTransact::build_response_copy
04016 //
04017 //   Build a response with minimal changes from the base response
04018 //
04019 void
04020 HttpTransact::build_response_copy(State* s, HTTPHdr* base_response,HTTPHdr* outgoing_response, HTTPVersion outgoing_version)
04021 {
04022   HttpTransactHeaders::copy_header_fields(base_response, outgoing_response, s->txn_conf->fwd_proxy_auth_to_parent,
04023                                           s->current.now);
04024   HttpTransactHeaders::convert_response(outgoing_version, outgoing_response);   // http version conversion
04025   HttpTransactHeaders::add_server_header_to_response(s->txn_conf, outgoing_response);
04026 
04027   if (!s->cop_test_page)
04028     DUMP_HEADER("http_hdrs", outgoing_response, s->state_machine_id, "Proxy's Response");
04029 }
04030 
04031 //////////////////////////////////////////////////////////////////////////
04032 //   IMS handling table                                                 //
04033 //       OS = Origin Server                                             //
04034 //       IMS = A GET request w/ an If-Modified-Since header             //
04035 //       LMs = Last modified state returned by server                   //
04036 //       D, D' are Last modified dates returned by the origin server    //
04037 //          and are later used for IMS                                  //
04038 //       D < D'                                                         //
04039 //                                                                      //
04040 //  +------------------------------------------------------------+      //
04041 //  | Client's | Cached    | Proxy's  | Response to client       |      //
04042 //  | Request  |  State    | Request  |  OS 200    |   OS 304    |      //
04043 //  |------------------------------------------------------------|      //
04044 //  |  GET     | Fresh     | N/A      |  N/A      |  N/A         |      //
04045 //  |------------------------------------------------------------|      //
04046 //  |  GET     | Stale, D' | IMS  D'  | 200, new  | 200, cached  |      //
04047 //  |------------------------------------------------------------|      //
04048 //  |  IMS D   | None      | GET      | 200, new *|  N/A         |      //
04049 //  |------------------------------------------------------------|      //
04050 //  |  IMS D   | Stale, D' | IMS D'   | 200, new  | Compare      |      //
04051 //  |---------------------------------------------| LMs & D'     |      //
04052 //  |  IMS D'  | Stale, D' | IMS D'   | 200, new  | If match, 304|      //
04053 //  |---------------------------------------------| If no match, |      //
04054 //  |  IMS D'  | Stale D   | IMS D    | 200, new *|  200, cached |      //
04055 //  +------------------------------------------------------------+      //
04056 //                                                                      //
04057 //  Note: * indicates a case that could be optimized to return          //
04058 //     304 to the client but currently is not                           //
04059 //                                                                      //
04060 //////////////////////////////////////////////////////////////////////////
04061 
04062 ///////////////////////////////////////////////////////////////////////////////
04063 // Name       : handle_cache_operation_on_forward_server_response
04064 // Description:
04065 //
04066 // Details    :
04067 //
04068 //
04069 //
04070 // Possible Next States From Here:
04071 //
04072 ///////////////////////////////////////////////////////////////////////////////
04073 void
04074 HttpTransact::handle_cache_operation_on_forward_server_response(State* s)
04075 {
04076   DebugTxn("http_trans", "[handle_cache_operation_on_forward_server_response] (hcoofsr)");
04077   DebugTxn("http_seq", "[handle_cache_operation_on_forward_server_response]");
04078 
04079   HTTPHdr *base_response = NULL;
04080   HTTPStatus server_response_code = HTTP_STATUS_NONE;
04081   HTTPStatus client_response_code = HTTP_STATUS_NONE;
04082   const char *warn_text = NULL;
04083   bool cacheable = false;
04084 
04085   cacheable = is_response_cacheable(s, &s->hdr_info.client_request, &s->hdr_info.server_response);
04086   DebugTxn("http_trans", "[hcoofsr] response %s cacheable", cacheable ? "is" : "is not");
04087 
04088   // set the correct next action, cache action, response code, and base response
04089 
04090   server_response_code = s->hdr_info.server_response.status_get();
04091   switch (server_response_code) {
04092   case HTTP_STATUS_NOT_MODIFIED:       // 304
04093     SET_VIA_STRING(VIA_SERVER_RESULT, VIA_SERVER_NOT_MODIFIED);
04094 
04095     // determine the correct cache action, next state, and response
04096     // precondition: s->cache_info.action should be one of the following
04097     // CACHE_DO_DELETE, or CACHE_DO_UPDATE; otherwise, it's an error.
04098     if (s->api_server_response_ignore && s->cache_info.action == CACHE_DO_UPDATE) {
04099       s->api_server_response_ignore = false;
04100       ink_assert(s->cache_info.object_read);
04101       base_response = s->cache_info.object_read->response_get();
04102       s->cache_info.action = CACHE_DO_SERVE;
04103       DebugTxn("http_trans", "[hcoofsr] not merging, cache action changed to: %s",
04104             HttpDebugNames::get_cache_action_name(s->cache_info.action));
04105       s->next_action = SM_ACTION_SERVE_FROM_CACHE;
04106       client_response_code = base_response->status_get();
04107     } else if ((s->cache_info.action == CACHE_DO_DELETE) || ((s->cache_info.action == CACHE_DO_UPDATE) && !cacheable)) {
04108       if (is_request_conditional(&s->hdr_info.client_request)) {
04109         client_response_code =
04110           HttpTransactCache::match_response_to_request_conditionals(&s->hdr_info.client_request,
04111                                                                     s->cache_info.object_read->response_get());
04112       } else {
04113         client_response_code = HTTP_STATUS_OK;
04114       }
04115 
04116       if (client_response_code != HTTP_STATUS_OK) {
04117         // we can just forward the not modified response
04118         // from the server and delete the cached copy
04119         base_response = &s->hdr_info.server_response;
04120         client_response_code = base_response->status_get();
04121         s->cache_info.action = CACHE_DO_DELETE;
04122         s->next_action = SM_ACTION_INTERNAL_CACHE_DELETE;
04123       } else {
04124         // We got screwed. The client did not send a conditional request,
04125         // but we had a cached copy which we revalidated. The server has
04126         // now told us to delete the cached copy and sent back a 304.
04127         // We need to send the cached copy to the client, then delete it.
04128         if (s->method == HTTP_WKSIDX_HEAD) {
04129           s->cache_info.action = CACHE_DO_DELETE;
04130           s->next_action = SM_ACTION_SERVER_READ;
04131         } else {
04132           s->cache_info.action = CACHE_DO_SERVE_AND_DELETE;
04133           s->next_action = SM_ACTION_SERVE_FROM_CACHE;
04134         }
04135         base_response = s->cache_info.object_read->response_get();
04136         client_response_code = base_response->status_get();
04137       }
04138 
04139     } else if (s->cache_info.action == CACHE_DO_UPDATE && is_request_conditional(&s->hdr_info.server_request)) {
04140       // CACHE_DO_UPDATE and server response is cacheable
04141       if (is_request_conditional(&s->hdr_info.client_request)) {
04142         if (s->txn_conf->cache_when_to_revalidate != 4)
04143           client_response_code =
04144             HttpTransactCache::match_response_to_request_conditionals(&s->hdr_info.client_request,
04145                                                                       s->cache_info.object_read->response_get());
04146         else
04147           client_response_code = server_response_code;
04148       } else {
04149         client_response_code = HTTP_STATUS_OK;
04150       }
04151 
04152       if (client_response_code != HTTP_STATUS_OK) {
04153         // delete the cached copy unless configured to always verify IMS
04154         if (s->txn_conf->cache_when_to_revalidate != 4) {
04155           s->cache_info.action = CACHE_DO_UPDATE;
04156           s->next_action = SM_ACTION_INTERNAL_CACHE_UPDATE_HEADERS;
04157           /* base_response will be set after updating headers below */
04158         } else {
04159           s->cache_info.action = CACHE_DO_NO_ACTION;
04160           s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
04161           base_response = &s->hdr_info.server_response;
04162         }
04163       } else {
04164         if (s->method == HTTP_WKSIDX_HEAD) {
04165           s->cache_info.action = CACHE_DO_UPDATE;
04166           s->next_action = SM_ACTION_SERVER_READ;
04167         } else {
04168           if (s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE)) {
04169             s->state_machine->do_range_setup_if_necessary();
04170             // Note that even if the Range request is not satisfiable, we
04171             // update and serve this cache. This will give a 200 response to
04172             // a bad client, but allows us to avoid pegging the origin (e.g. abuse).
04173           }
04174           s->cache_info.action = CACHE_DO_SERVE_AND_UPDATE;
04175           s->next_action = SM_ACTION_SERVE_FROM_CACHE;
04176         }
04177         /* base_response will be set after updating headers below */
04178       }
04179 
04180     } else {                    // cache action != CACHE_DO_DELETE and != CACHE_DO_UPDATE
04181 
04182       // bogus response from server. deal by tunnelling to client.
04183       // server should not have sent back a 304 because our request
04184       // should not have been an conditional.
04185       DebugTxn("http_trans", "[hcoofsr] 304 for non-conditional request");
04186       s->cache_info.action = CACHE_DO_NO_ACTION;
04187       s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
04188       client_response_code = s->hdr_info.server_response.status_get();
04189       base_response = &s->hdr_info.server_response;
04190 
04191       // since this is bad, insert warning header into client response
04192       // The only exception case is conditional client request,
04193       // cache miss, and client request being unlikely cacheable.
04194       // In this case, the server request is given the same
04195       // conditional headers as client request (see build_request()).
04196       // So an unexpected 304 might be received.
04197       // FIXME: check this case
04198       if (is_request_likely_cacheable(s, &s->hdr_info.client_request)) {
04199         warn_text = "Proxy received unexpected 304 response; " "content may be stale";
04200       }
04201     }
04202 
04203     break;
04204 
04205   case HTTP_STATUS_HTTPVER_NOT_SUPPORTED:      // 505
04206     {
04207       bool keep_alive = (s->current.server->keep_alive == HTTP_KEEPALIVE);
04208 
04209       s->next_action = how_to_open_connection(s);
04210 
04211       /* Downgrade the request level and retry */
04212       if (!HttpTransactHeaders::downgrade_request(&keep_alive, &s->hdr_info.server_request)) {
04213         build_error_response(s, HTTP_STATUS_HTTPVER_NOT_SUPPORTED, "HTTP Version Not Supported",
04214                              "response#bad_version", NULL);
04215         s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
04216         s->already_downgraded = true;
04217       } else {
04218         if (!keep_alive) {
04219           /* START Hack */
04220           (s->hdr_info.server_request).field_delete(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
04221           /* END   Hack */
04222         }
04223         s->already_downgraded = true;
04224         s->next_action = how_to_open_connection(s);
04225       }
04226     }
04227     return;
04228 
04229   default:
04230     DebugTxn("http_trans", "[hcoofsr] response code: %d", server_response_code);
04231     SET_VIA_STRING(VIA_SERVER_RESULT, VIA_SERVER_SERVED);
04232     SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVED);
04233 
04234 
04235     /* if we receive a 500, 502, 503 or 504 while revalidating
04236        a document, treat the response as a 304 and in effect revalidate the document for
04237        negative_revalidating_lifetime. (negative revalidating)
04238      */
04239 
04240     if ((server_response_code == HTTP_STATUS_INTERNAL_SERVER_ERROR ||
04241          server_response_code == HTTP_STATUS_GATEWAY_TIMEOUT ||
04242          server_response_code == HTTP_STATUS_BAD_GATEWAY ||
04243          server_response_code == HTTP_STATUS_SERVICE_UNAVAILABLE) &&
04244         s->cache_info.action == CACHE_DO_UPDATE &&
04245         s->txn_conf->negative_revalidating_enabled && is_stale_cache_response_returnable(s)) {
04246       DebugTxn("http_trans", "[hcoofsr] negative revalidating: revalidate stale object and serve from cache");
04247 
04248       s->cache_info.object_store.create();
04249       s->cache_info.object_store.request_set(&s->hdr_info.client_request);
04250       s->cache_info.object_store.response_set(s->cache_info.object_read->response_get());
04251       base_response = s->cache_info.object_store.response_get();
04252       time_t exp_time = s->txn_conf->negative_revalidating_lifetime + ink_cluster_time();
04253       base_response->set_expires(exp_time);
04254 
04255       SET_VIA_STRING(VIA_CACHE_FILL_ACTION, VIA_CACHE_UPDATED);
04256       HTTP_INCREMENT_TRANS_STAT(http_cache_updates_stat);
04257 
04258       // unset Cache-control: "need-revalidate-once" (if it's set)
04259       // This directive is used internally by T.S. to invalidate
04260       // documents so that an invalidated document needs to be
04261       // revalidated again.
04262       base_response->unset_cooked_cc_need_revalidate_once();
04263 
04264       if (is_request_conditional(&s->hdr_info.client_request) &&
04265           HttpTransactCache::match_response_to_request_conditionals(&s->hdr_info.client_request,
04266                                                                     s->cache_info.object_read->response_get()) == HTTP_STATUS_NOT_MODIFIED) {
04267         s->next_action = SM_ACTION_INTERNAL_CACHE_UPDATE_HEADERS;
04268         client_response_code = HTTP_STATUS_NOT_MODIFIED;
04269       } else {
04270         if (s->method == HTTP_WKSIDX_HEAD) {
04271           s->cache_info.action = CACHE_DO_UPDATE;
04272           s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
04273         } else {
04274           s->cache_info.action = CACHE_DO_SERVE_AND_UPDATE;
04275           s->next_action = SM_ACTION_SERVE_FROM_CACHE;
04276         }
04277 
04278         client_response_code = s->cache_info.object_read->response_get()->status_get();
04279       }
04280 
04281       ink_assert(base_response->valid());
04282 
04283       if (client_response_code == HTTP_STATUS_NOT_MODIFIED) {
04284         ink_assert(GET_VIA_STRING(VIA_CLIENT_REQUEST) != VIA_CLIENT_SIMPLE);
04285         SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_IMS);
04286         SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_NOT_MODIFIED);
04287       } else {
04288         SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVED);
04289       }
04290 
04291       ink_assert(client_response_code != HTTP_STATUS_NONE);
04292 
04293       if (s->next_action == SM_ACTION_SERVE_FROM_CACHE && s->state_machine->do_transform_open()) {
04294         set_header_for_transform(s, base_response);
04295       } else {
04296         build_response(s, base_response, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
04297       }
04298 
04299       return;
04300     }
04301 
04302     s->next_action = SM_ACTION_SERVER_READ;
04303     client_response_code = server_response_code;
04304     base_response = &s->hdr_info.server_response;
04305 
04306     s->negative_caching = is_negative_caching_appropriate(s);
04307 
04308     // determine the correct cache action given the original cache action,
04309     // cacheability of server response, and request method
04310     // precondition: s->cache_info.action is one of the following
04311     // CACHE_DO_UPDATE, CACHE_DO_WRITE, or CACHE_DO_DELETE
04312     if (s->api_server_response_no_store) {
04313       s->cache_info.action = CACHE_DO_NO_ACTION;
04314     } else if (s->api_server_response_ignore &&
04315                server_response_code == HTTP_STATUS_OK &&
04316                s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_HEAD) {
04317       s->api_server_response_ignore = false;
04318       ink_assert(s->cache_info.object_read);
04319       base_response = s->cache_info.object_read->response_get();
04320       s->cache_info.action = CACHE_DO_SERVE;
04321       DebugTxn("http_trans", "[hcoofsr] ignoring server response, "
04322             "cache action changed to: %s", HttpDebugNames::get_cache_action_name(s->cache_info.action));
04323       s->next_action = SM_ACTION_SERVE_FROM_CACHE;
04324       client_response_code = base_response->status_get();
04325     } else if (s->cache_info.action == CACHE_DO_UPDATE) {
04326       if (s->www_auth_content == CACHE_AUTH_FRESH) {
04327         s->cache_info.action = CACHE_DO_NO_ACTION;
04328       } else if (s->www_auth_content == CACHE_AUTH_STALE && server_response_code == HTTP_STATUS_UNAUTHORIZED) {
04329         s->cache_info.action = CACHE_DO_NO_ACTION;
04330       } else if (!cacheable) {
04331         s->cache_info.action = CACHE_DO_DELETE;
04332       } else if (s->method == HTTP_WKSIDX_HEAD) {
04333         s->cache_info.action = CACHE_DO_DELETE;
04334       } else {
04335         ink_assert(s->cache_info.object_read != 0);
04336         s->cache_info.action = CACHE_DO_REPLACE;
04337       }
04338 
04339     } else if (s->cache_info.action == CACHE_DO_WRITE) {
04340       if (!cacheable && !s->negative_caching) {
04341         s->cache_info.action = CACHE_DO_NO_ACTION;
04342       } else if (s->method == HTTP_WKSIDX_HEAD) {
04343         s->cache_info.action = CACHE_DO_NO_ACTION;
04344       } else {
04345         s->cache_info.action = CACHE_DO_WRITE;
04346       }
04347 
04348     } else if (s->cache_info.action == CACHE_DO_DELETE) {
04349       // do nothing
04350 
04351     } else {
04352       ink_assert(!("cache action inconsistent with current state"));
04353     }
04354     // postcondition: s->cache_info.action is one of the following
04355     // CACHE_DO_REPLACE, CACHE_DO_WRITE, CACHE_DO_DELETE, or
04356     // CACHE_DO_NO_ACTION
04357 
04358     // Check see if we ought to serve the client a 304 based on
04359     //   it's IMS date.  We may gotten a 200 back from the origin
04360     //   server if our (the proxies's) cached copy was out of date
04361     //   but the client's wasn't.  However, if the response is
04362     //   not cacheable we ought not issue a 304 to the client so
04363     //   make sure we are writing the document to the cache if
04364     //   before issuing a 304
04365     if (s->cache_info.action == CACHE_DO_WRITE ||
04366         s->cache_info.action == CACHE_DO_NO_ACTION || s->cache_info.action == CACHE_DO_REPLACE) {
04367       if (s->negative_caching) {
04368         HTTPHdr *resp;
04369         s->cache_info.object_store.create();
04370         s->cache_info.object_store.request_set(&s->hdr_info.client_request);
04371         s->cache_info.object_store.response_set(&s->hdr_info.server_response);
04372         resp = s->cache_info.object_store.response_get();
04373         if (!resp->presence(MIME_PRESENCE_EXPIRES)) {
04374           time_t exp_time = s->txn_conf->negative_caching_lifetime + ink_cluster_time();
04375 
04376           resp->set_expires(exp_time);
04377         }
04378       } else if (is_request_conditional(&s->hdr_info.client_request) && server_response_code == HTTP_STATUS_OK) {
04379         DebugTxn("http_trans", "[hcoofsr] conditional request, 200 " "response, send back 304 if possible");
04380         client_response_code =
04381           HttpTransactCache::match_response_to_request_conditionals(&s->hdr_info.client_request,
04382                                                                     &s->hdr_info.server_response);
04383 
04384         if ((client_response_code == HTTP_STATUS_NOT_MODIFIED) || (client_response_code == HTTP_STATUS_PRECONDITION_FAILED)) {
04385           switch (s->cache_info.action) {
04386           case CACHE_DO_WRITE:
04387           case CACHE_DO_REPLACE:
04388             s->next_action = SM_ACTION_INTERNAL_CACHE_WRITE;
04389             break;
04390           case CACHE_DO_DELETE:
04391             s->next_action = SM_ACTION_INTERNAL_CACHE_DELETE;
04392             break;
04393           default:
04394             s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
04395             break;
04396           }
04397         } else {
04398           SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVER_REVALIDATED);
04399         }
04400       }
04401     } else if (s->negative_caching) {
04402       s->negative_caching = false;
04403     }
04404 
04405     break;
04406   }
04407 
04408   // update stat, set via string, etc
04409 
04410   switch (s->cache_info.action) {
04411   case CACHE_DO_SERVE_AND_DELETE:
04412     // fall through
04413   case CACHE_DO_DELETE:
04414     DebugTxn("http_trans", "[hcoofsr] delete cached copy");
04415     SET_VIA_STRING(VIA_CACHE_FILL_ACTION, VIA_CACHE_DELETED);
04416     HTTP_INCREMENT_TRANS_STAT(http_cache_deletes_stat);
04417     break;
04418   case CACHE_DO_WRITE:
04419     DebugTxn("http_trans", "[hcoofsr] cache write");
04420     SET_VIA_STRING(VIA_CACHE_FILL_ACTION, VIA_CACHE_WRITTEN);
04421     HTTP_INCREMENT_TRANS_STAT(http_cache_writes_stat);
04422     break;
04423   case CACHE_DO_SERVE_AND_UPDATE:
04424     // fall through
04425   case CACHE_DO_UPDATE:
04426     // fall through
04427   case CACHE_DO_REPLACE:
04428     DebugTxn("http_trans", "[hcoofsr] cache update/replace");
04429     SET_VIA_STRING(VIA_CACHE_FILL_ACTION, VIA_CACHE_UPDATED);
04430     HTTP_INCREMENT_TRANS_STAT(http_cache_updates_stat);
04431     break;
04432   default:
04433     break;
04434   }
04435 
04436   if ((client_response_code == HTTP_STATUS_NOT_MODIFIED) && (s->cache_info.action != CACHE_DO_NO_ACTION)) {
04437     /* ink_assert(GET_VIA_STRING(VIA_CLIENT_REQUEST)
04438        != VIA_CLIENT_SIMPLE); */
04439     DebugTxn("http_trans", "[hcoofsr] Client request was conditional");
04440     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_IMS);
04441     SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_NOT_MODIFIED);
04442   } else {
04443     SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVED);
04444   }
04445 
04446   ink_assert(client_response_code != HTTP_STATUS_NONE);
04447 
04448   // The correct cache action, next action, and response code are set.
04449   // Do the real work below.
04450 
04451   // first update the cached object
04452   if ((s->cache_info.action == CACHE_DO_UPDATE) || (s->cache_info.action == CACHE_DO_SERVE_AND_UPDATE)) {
04453     DebugTxn("http_trans", "[hcoofsr] merge and update cached copy");
04454     merge_and_update_headers_for_cache_update(s);
04455     base_response = s->cache_info.object_store.response_get();
04456     // unset Cache-control: "need-revalidate-once" (if it's set)
04457     // This directive is used internally by T.S. to invalidate documents
04458     // so that an invalidated document needs to be revalidated again.
04459     base_response->unset_cooked_cc_need_revalidate_once();
04460     // unset warning revalidation failed header if it set
04461     // (potentially added by negative revalidating)
04462     delete_warning_value(base_response, HTTP_WARNING_CODE_REVALIDATION_FAILED);
04463   }
04464   ink_assert(base_response->valid());
04465 
04466   if ((s->cache_info.action == CACHE_DO_WRITE) || (s->cache_info.action == CACHE_DO_REPLACE)) {
04467     set_headers_for_cache_write(s, &s->cache_info.object_store, &s->hdr_info.server_request, &s->hdr_info.server_response);
04468   }
04469   // 304, 412, and 416 responses are handled here
04470   if ((client_response_code == HTTP_STATUS_NOT_MODIFIED) || (client_response_code == HTTP_STATUS_PRECONDITION_FAILED)) {
04471     // Because we are decoupling User-Agent validation from
04472     //  Traffic Server validation just build a regular 304
04473     //  if the exception of adding prepending the VIA
04474     //  header to show the revalidation path
04475     build_response(s, base_response, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
04476 
04477     // Copy over the response via field (if any) preserving
04478     //  the order of the fields
04479     MIMEField *resp_via = s->hdr_info.server_response.field_find(MIME_FIELD_VIA, MIME_LEN_VIA);
04480     if (resp_via) {
04481       MIMEField *our_via;
04482       our_via = s->hdr_info.client_response.field_find(MIME_FIELD_VIA, MIME_LEN_VIA);
04483       if (our_via == NULL) {
04484         our_via = s->hdr_info.client_response.field_create(MIME_FIELD_VIA, MIME_LEN_VIA);
04485         s->hdr_info.client_response.field_attach(our_via);
04486       }
04487       // HDR FIX ME - Mulitple appends are VERY slow
04488       while (resp_via) {
04489         int clen;
04490         const char *cfield = resp_via->value_get(&clen);
04491         s->hdr_info.client_response.field_value_append(our_via, cfield, clen, true);
04492         resp_via = resp_via->m_next_dup;
04493       }
04494     }
04495     // a warning text is added only in the case of a NOT MODIFIED response
04496     if (warn_text) {
04497       HttpTransactHeaders::insert_warning_header(s->http_config_param, &s->hdr_info.client_response,
04498                                                  HTTP_WARNING_CODE_MISC_WARNING, warn_text, strlen(warn_text));
04499     }
04500 
04501     if (!s->cop_test_page)
04502       DUMP_HEADER("http_hdrs", &s->hdr_info.client_response, s->state_machine_id, "Proxy's Response (Client Conditionals)");
04503     return;
04504   }
04505   // all other responses (not 304, 412, 416) are handled here
04506   else {
04507     if (((s->next_action == SM_ACTION_SERVE_FROM_CACHE) ||
04508          (s->next_action == SM_ACTION_SERVER_READ)) && s->state_machine->do_transform_open()) {
04509       set_header_for_transform(s, base_response);
04510     } else {
04511       build_response(s, base_response, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
04512     }
04513   }
04514 
04515   return;
04516 }
04517 
04518 
04519 ///////////////////////////////////////////////////////////////////////////////
04520 // Name       : handle_no_cache_operation_on_forward_server_response
04521 // Description:
04522 //
04523 // Details    :
04524 //
04525 //
04526 //
04527 // Possible Next States From Here:
04528 //
04529 ///////////////////////////////////////////////////////////////////////////////
04530 void
04531 HttpTransact::handle_no_cache_operation_on_forward_server_response(State* s)
04532 {
04533   DebugTxn("http_trans", "[handle_no_cache_operation_on_forward_server_response] (hncoofsr)");
04534   DebugTxn("http_seq", "[handle_no_cache_operation_on_forward_server_response]");
04535 
04536   bool keep_alive = s->current.server->keep_alive == HTTP_KEEPALIVE;
04537   const char *warn_text = NULL;
04538 
04539   switch (s->hdr_info.server_response.status_get()) {
04540   case HTTP_STATUS_OK:
04541     DebugTxn("http_trans", "[hncoofsr] server sent back 200");
04542     SET_VIA_STRING(VIA_SERVER_RESULT, VIA_SERVER_SERVED);
04543     SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVED);
04544     if (s->method == HTTP_WKSIDX_CONNECT) {
04545       DebugTxn("http_trans", "[hncoofsr] next action is SSL_TUNNEL");
04546       s->next_action = SM_ACTION_SSL_TUNNEL;
04547     } else {
04548       DebugTxn("http_trans", "[hncoofsr] next action will be OS_READ_CACHE_NOOP");
04549 
04550       ink_assert(s->cache_info.action == CACHE_DO_NO_ACTION);
04551       s->next_action = SM_ACTION_SERVER_READ;
04552     }
04553     if (s->state_machine->redirect_url == NULL) {
04554       s->state_machine->enable_redirection = false;
04555     }
04556     break;
04557   case HTTP_STATUS_NOT_MODIFIED:
04558     DebugTxn("http_trans", "[hncoofsr] server sent back 304. IMS from client?");
04559     SET_VIA_STRING(VIA_SERVER_RESULT, VIA_SERVER_NOT_MODIFIED);
04560     SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_NOT_MODIFIED);
04561 
04562     if (!is_request_conditional(&s->hdr_info.client_request)) {
04563       // bogus server response. not a conditional request
04564       // from the client and probably not a conditional
04565       // request from the proxy.
04566 
04567       // since this is bad, insert warning header into client response
04568       warn_text = "Proxy received unexpected 304 response; content may be stale";
04569     }
04570 
04571     ink_assert(s->cache_info.action == CACHE_DO_NO_ACTION);
04572     s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
04573     break;
04574   case HTTP_STATUS_HTTPVER_NOT_SUPPORTED:
04575     s->next_action = how_to_open_connection(s);
04576 
04577     /* Downgrade the request level and retry */
04578     if (!HttpTransactHeaders::downgrade_request(&keep_alive, &s->hdr_info.server_request)) {
04579       s->already_downgraded = true;
04580       build_error_response(s, HTTP_STATUS_HTTPVER_NOT_SUPPORTED, "HTTP Version Not Supported", "response#bad_version",
04581                            NULL);
04582       s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
04583     } else {
04584       s->already_downgraded = true;
04585       s->next_action = how_to_open_connection(s);
04586     }
04587     return;
04588   case HTTP_STATUS_PARTIAL_CONTENT:
04589     // If we get this back we should be just passing it through.
04590     ink_assert(s->cache_info.action == CACHE_DO_NO_ACTION);
04591     s->next_action = SM_ACTION_SERVER_READ;
04592     break;
04593   default:
04594     DebugTxn("http_trans", "[hncoofsr] server sent back something other than 100,304,200");
04595     /* Default behavior is to pass-through response to the client */
04596 
04597     ink_assert(s->cache_info.action == CACHE_DO_NO_ACTION);
04598     s->next_action = SM_ACTION_SERVER_READ;
04599     break;
04600   }
04601 
04602   HTTPHdr *to_warn;
04603   if (s->next_action == SM_ACTION_SERVER_READ && s->state_machine->do_transform_open()) {
04604     set_header_for_transform(s, &s->hdr_info.server_response);
04605     to_warn = &s->hdr_info.transform_response;
04606   } else {
04607     build_response(s, &s->hdr_info.server_response, &s->hdr_info.client_response, s->client_info.http_version);
04608     to_warn = &s->hdr_info.server_response;
04609   }
04610 
04611   if (warn_text) {
04612     HttpTransactHeaders::insert_warning_header(s->http_config_param, to_warn, HTTP_WARNING_CODE_MISC_WARNING,
04613                                                warn_text, strlen(warn_text));
04614   }
04615 
04616   return;
04617 }
04618 
04619 
04620 void
04621 HttpTransact::merge_and_update_headers_for_cache_update(State* s)
04622 {
04623   URL *s_url = NULL;
04624   // This is not used.
04625   // HTTPHdr * f_resp = NULL;
04626 
04627   if (!s->cache_info.object_store.valid()) {
04628     s->cache_info.object_store.create();
04629   }
04630 
04631   s->cache_info.object_store.request_set(&s->hdr_info.server_request);
04632 
04633   if (s->redirect_info.redirect_in_process)
04634     s_url = &s->redirect_info.original_url;
04635   else
04636     s_url = &s->cache_info.original_url;
04637   ink_assert(s_url != NULL);
04638 
04639   s->cache_info.object_store.request_get()->url_set(s_url->valid()? s_url : s->hdr_info.client_request.url_get());
04640 
04641   if (s->cache_info.object_store.request_get()->method_get_wksidx() == HTTP_WKSIDX_HEAD) {
04642     s->cache_info.object_store.request_get()->method_set(HTTP_METHOD_GET, HTTP_LEN_GET);
04643   }
04644 
04645   if (s->api_modifiable_cached_resp) {
04646     ink_assert(s->cache_info.object_store.response_get() != NULL && s->cache_info.object_store.response_get()->valid());
04647     s->api_modifiable_cached_resp = false;
04648   } else {
04649     s->cache_info.object_store.response_set(s->cache_info.object_read->response_get());
04650   }
04651 
04652   merge_response_header_with_cached_header(s->cache_info.object_store.response_get(), &s->hdr_info.server_response);
04653 
04654   // Some special processing for 304
04655   //
04656   if (s->hdr_info.server_response.status_get() == HTTP_STATUS_NOT_MODIFIED) {
04657     // Hack fix. If the server sends back
04658     // a 304 without a Date Header, use the current time
04659     // as the new Date value in the header to be cached.
04660     time_t date_value = s->hdr_info.server_response.get_date();
04661     HTTPHdr *cached_hdr = s->cache_info.object_store.response_get();
04662 
04663     if (date_value <= 0) {
04664       cached_hdr->set_date(s->request_sent_time);
04665       date_value = s->request_sent_time;
04666     }
04667     // If the cached response has an Age: we should update it
04668     // We could use calculate_document_age but my guess is it's overkill
04669     // Just use 'now' - 304's Date: + Age: (response's Age: if there)
04670     date_value = max(s->current.now - date_value, (ink_time_t)0);
04671     if (s->hdr_info.server_response.presence(MIME_PRESENCE_AGE)) {
04672       time_t new_age = s->hdr_info.server_response.get_age();
04673 
04674       if (new_age >= 0)
04675         cached_hdr->set_age(date_value + new_age);
04676       else
04677         cached_hdr->set_age(-1); // Overflow
04678     }
04679     delete_warning_value(cached_hdr, HTTP_WARNING_CODE_REVALIDATION_FAILED);
04680   }
04681 
04682   s->cache_info.object_store.request_get()->field_delete(MIME_FIELD_VIA, MIME_LEN_VIA);
04683 
04684 }
04685 
04686 void
04687 HttpTransact::handle_transform_cache_write(State* s)
04688 {
04689   ink_assert(s->cache_info.transform_action == CACHE_PREPARE_TO_WRITE);
04690 
04691   switch (s->cache_info.write_lock_state) {
04692   case CACHE_WL_SUCCESS:
04693     // We were able to get the lock for the URL vector in the cache
04694     s->cache_info.transform_action = CACHE_DO_WRITE;
04695     break;
04696   case CACHE_WL_FAIL:
04697     // No write lock, ignore the cache
04698     s->cache_info.transform_action = CACHE_DO_NO_ACTION;
04699     s->cache_info.transform_write_status = CACHE_WRITE_LOCK_MISS;
04700     break;
04701   default:
04702     ink_release_assert(0);
04703   }
04704 
04705   TRANSACT_RETURN(SM_ACTION_TRANSFORM_READ, NULL);
04706 }
04707 
04708 void
04709 HttpTransact::handle_transform_ready(State* s)
04710 {
04711   ink_assert(s->hdr_info.transform_response.valid() == true);
04712 
04713   s->pre_transform_source = s->source;
04714   s->source = SOURCE_TRANSFORM;
04715 
04716   if (!s->cop_test_page)
04717     DUMP_HEADER("http_hdrs", &s->hdr_info.transform_response, s->state_machine_id, "Header From Transform");
04718 
04719   build_response(s, &s->hdr_info.transform_response, &s->hdr_info.client_response, s->client_info.http_version);
04720 
04721   if (s->cache_info.action != CACHE_DO_NO_ACTION &&
04722       s->cache_info.action != CACHE_DO_DELETE && s->api_info.cache_transformed && !s->range_setup) {
04723     HTTPHdr *transform_store_request = 0;
04724     switch (s->pre_transform_source) {
04725     case SOURCE_CACHE:
04726       // If we are transforming from the cache, treat
04727       //  the transform as if it were virtual server
04728       //  use in the incoming request
04729       transform_store_request = &s->hdr_info.client_request;
04730       break;
04731     case SOURCE_HTTP_ORIGIN_SERVER:
04732       transform_store_request = &s->hdr_info.server_request;
04733       break;
04734     default:
04735       ink_release_assert(0);
04736     }
04737     ink_assert(transform_store_request->valid() == true);
04738     set_headers_for_cache_write(s, &s->cache_info.transform_store, transform_store_request, &s->hdr_info.transform_response);
04739 
04740     // For debugging
04741     if (is_action_tag_set("http_nullt")) {
04742       s->cache_info.transform_store.request_get()->value_set("InkXform", 8, "nullt", 5);
04743       s->cache_info.transform_store.response_get()->value_set("InkXform", 8, "nullt", 5);
04744     }
04745 
04746     s->cache_info.transform_action = CACHE_PREPARE_TO_WRITE;
04747     TRANSACT_RETURN(SM_ACTION_CACHE_ISSUE_WRITE_TRANSFORM, handle_transform_cache_write);
04748   } else {
04749     s->cache_info.transform_action = CACHE_DO_NO_ACTION;
04750     TRANSACT_RETURN(SM_ACTION_TRANSFORM_READ, NULL);
04751   }
04752 }
04753 
04754 void
04755 HttpTransact::set_header_for_transform(State* s, HTTPHdr* base_header)
04756 {
04757   s->hdr_info.transform_response.create(HTTP_TYPE_RESPONSE);
04758   s->hdr_info.transform_response.copy(base_header);
04759 
04760   // Nuke the content length since 1) the transform will probably
04761   //   change it.  2) it would only be valid for the first transform
04762   //   in the chain
04763   s->hdr_info.transform_response.field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
04764 
04765   if (!s->cop_test_page)
04766     DUMP_HEADER("http_hdrs", &s->hdr_info.transform_response, s->state_machine_id, "Header To Transform");
04767 }
04768 
04769 void
04770 HttpTransact::set_headers_for_cache_write(State* s, HTTPInfo* cache_info, HTTPHdr* request, HTTPHdr* response)
04771 {
04772   URL *temp_url;
04773   ink_assert(request->type_get() == HTTP_TYPE_REQUEST);
04774   ink_assert(response->type_get() == HTTP_TYPE_RESPONSE);
04775 
04776   if (!cache_info->valid()) {
04777     cache_info->create();
04778   }
04779 
04780   /* Store the requested URI */
04781   //  Nasty hack. The set calls for
04782   //  marshalled types current do handle something being
04783   //  set to itself.  Make the check here for that case.
04784   //  Why the request url is set before a copy made is
04785   //  quite beyond me.  Seems like a unsafe practice so
04786   //  FIX ME!
04787 
04788   // Logic added to restore the orignal URL for multiple cache lookup
04789   // and automatic redirection
04790   if (s->redirect_info.redirect_in_process) {
04791     temp_url = &s->redirect_info.original_url;
04792     ink_assert(temp_url->valid());
04793     request->url_set(temp_url);
04794   } else if ((temp_url = &(s->cache_info.original_url))->valid()) {
04795     request->url_set(temp_url);
04796   } else if (request != &s->hdr_info.client_request) {
04797     request->url_set(s->hdr_info.client_request.url_get());
04798   }
04799   cache_info->request_set(request);
04800   /* Why do we check the negative caching case? No one knows. This used to assert if the cache_info
04801      response wasn't already valid, which broke negative caching when a transform is active. Why it
04802      wasn't OK to pull in the @a response explicitly passed in isn't clear and looking at the call
04803      sites yields no insight. So the assert is removed and we keep the behavior that if the response
04804      in @a cache_info is already set, we don't override it.
04805   */
04806   if (!s->negative_caching || !cache_info->response_get()->valid())
04807     cache_info->response_set(response);
04808 
04809   if (s->api_server_request_body_set)
04810     cache_info->request_get()->method_set(HTTP_METHOD_GET, HTTP_LEN_GET);
04811 
04812   // Set-Cookie should not be put in the cache to prevent
04813   //  sending person A's cookie to person B
04814   cache_info->response_get()->field_delete(MIME_FIELD_SET_COOKIE, MIME_LEN_SET_COOKIE);
04815   cache_info->request_get()->field_delete(MIME_FIELD_VIA, MIME_LEN_VIA);
04816   // server 200 Ok for Range request
04817   cache_info->request_get()->field_delete(MIME_FIELD_RANGE, MIME_LEN_RANGE);
04818 
04819   // If we're ignoring auth, then we don't want to cache WWW-Auth
04820   //  headers
04821   if (s->txn_conf->cache_ignore_auth) {
04822     cache_info->response_get()->field_delete(MIME_FIELD_WWW_AUTHENTICATE, MIME_LEN_WWW_AUTHENTICATE);
04823   }
04824 
04825   //if (s->cache_control.cache_auth_content && s->www_auth_content != CACHE_AUTH_NONE) {
04826     // decided to cache authenticated content because of cache.config
04827     // add one marker to the content in cache
04828    // cache_info->response_get()->value_set("@WWW-Auth", 9, "true", 4);
04829   //}
04830   if (!s->cop_test_page)
04831     DUMP_HEADER("http_hdrs", cache_info->request_get(), s->state_machine_id, "Cached Request Hdr");
04832 }
04833 
04834 void
04835 HttpTransact::merge_response_header_with_cached_header(HTTPHdr* cached_header, HTTPHdr* response_header)
04836 {
04837   MIMEField *field;
04838   MIMEField *new_field;
04839   MIMEFieldIter fiter;
04840   const char *name;
04841   bool dups_seen = false;
04842 
04843 
04844   field = response_header->iter_get_first(&fiter);
04845 
04846   for (; field != NULL; field = response_header->iter_get_next(&fiter)) {
04847     int name_len;
04848     name = field->name_get(&name_len);
04849 
04850     ///////////////////////////
04851     // is hop-by-hop header? //
04852     ///////////////////////////
04853     if (HttpTransactHeaders::is_this_a_hop_by_hop_header(name)) {
04854       continue;
04855     }
04856     /////////////////////////////////////
04857     // dont cache content-length field //
04858     /////////////////////////////////////
04859     if (name == MIME_FIELD_CONTENT_LENGTH) {
04860       continue;
04861     }
04862     /////////////////////////////////////
04863     // dont cache Set-Cookie headers   //
04864     /////////////////////////////////////
04865     if (name == MIME_FIELD_SET_COOKIE) {
04866       continue;
04867     }
04868     /////////////////////////////////////////
04869     // dont overwrite the cached content   //
04870     //   type as this wreaks havoc with    //
04871     //   transformed content               //
04872     /////////////////////////////////////////
04873     if (name == MIME_FIELD_CONTENT_TYPE) {
04874       continue;
04875     }
04876     /////////////////////////////////////
04877     // dont delete warning.  a separate//
04878     //  functions merges the two in a  //
04879     //  complex manner                 //
04880     /////////////////////////////////////
04881     if (name == MIME_FIELD_WARNING) {
04882       continue;
04883     }
04884     // Copy all remaining headers with replacement
04885 
04886     // Duplicate header fields cause a bug problem
04887     //   since we need to duplicate with replacement.
04888     //   Without dups, we can just nuke what is already
04889     //   there in the cached header.  With dups, we
04890     //   can't do this because what is already there
04891     //   may be a dup we've already copied in.  If
04892     //   dups show up we look through the remaining
04893     //   header fields in the new reponse, nuke
04894     //   them in the cached response and then add in
04895     //   the remaining fields one by one from the
04896     //   response header
04897     //
04898     if (field->m_next_dup) {
04899       if (dups_seen == false) {
04900         MIMEField *dfield;
04901         // use a second iterator to delete the
04902         // remaining response headers in the cached response,
04903         // so that they will be added in the next iterations.
04904         MIMEFieldIter fiter2 = fiter;
04905         const char *dname = name;
04906         int dlen = name_len;
04907 
04908         while (dname) {
04909           cached_header->field_delete(dname, dlen);
04910           dfield = response_header->iter_get_next(&fiter2);
04911           if (dfield) {
04912             dname = dfield->name_get(&dlen);
04913           } else {
04914             dname = NULL;
04915           }
04916         }
04917         dups_seen = true;
04918       }
04919     }
04920 
04921     int value_len;
04922     const char *value = field->value_get(&value_len);
04923 
04924     if (dups_seen == false) {
04925       cached_header->value_set(name, name_len, value, value_len);
04926     } else {
04927       new_field = cached_header->field_create(name, name_len);
04928       cached_header->field_attach(new_field);
04929       cached_header->field_value_set(new_field, value, value_len);
04930     }
04931   }
04932 
04933   merge_warning_header(cached_header, response_header);
04934 
04935   Debug("http_hdr_space", "Merged response header with %d dead bytes", cached_header->m_heap->m_lost_string_space);
04936 }
04937 
04938 
04939 void
04940 HttpTransact::merge_warning_header(HTTPHdr* cached_header, HTTPHdr* response_header)
04941 {
04942   //  The plan:
04943   //
04944   //    1) The cached header has it's warning codes untouched
04945   //         since merge_response_header_with_cached_header()
04946   //         doesn't deal with warning headers.
04947   //    2) If there are 1xx warning codes in the cached
04948   //         header, they need to be removed.  Removal
04949   //         is difficult since the hdrs don't comma
04950   //         separate values, so build up a new header
04951   //         piecemal.  Very slow but shouldn't happen
04952   //         very often
04953   //    3) Since we keep the all the warning codes from
04954   //         the response header, append if to
04955   //         the cached header
04956   //
04957   MIMEField *c_warn = cached_header->field_find(MIME_FIELD_WARNING, MIME_LEN_WARNING);
04958   MIMEField *r_warn = response_header->field_find(MIME_FIELD_WARNING, MIME_LEN_WARNING);
04959   MIMEField *new_cwarn = NULL;
04960   int move_warn_len;
04961   const char *move_warn;
04962 
04963   // Loop over the cached warning header and transfer all non 1xx
04964   //   warning values to a new header
04965   if (c_warn) {
04966     HdrCsvIter csv;
04967 
04968     move_warn = csv.get_first(c_warn, &move_warn_len);
04969     while (move_warn) {
04970       int code = ink_atoi(move_warn, move_warn_len);
04971       if (code<100 || code> 199) {
04972         bool first_move;
04973         if (!new_cwarn) {
04974           new_cwarn = cached_header->field_create();
04975           first_move = true;
04976         } else {
04977           first_move = false;
04978         }
04979         cached_header->field_value_append(new_cwarn, move_warn, move_warn_len, !first_move);
04980       }
04981 
04982       move_warn = csv.get_next(&move_warn_len);
04983     }
04984 
04985     // At this point we can nuke the old warning headers
04986     cached_header->field_delete(MIME_FIELD_WARNING, MIME_LEN_WARNING);
04987 
04988     // Add in the new header if it has anything in it
04989     if (new_cwarn) {
04990       new_cwarn->name_set(cached_header->m_heap, cached_header->m_mime, MIME_FIELD_WARNING, MIME_LEN_WARNING);
04991       cached_header->field_attach(new_cwarn);
04992     }
04993   }
04994   // Loop over all the dups in the response warning header and append
04995   //  them one by one on to the cached warning header
04996   while (r_warn) {
04997     move_warn = r_warn->value_get(&move_warn_len);
04998 
04999     if (new_cwarn) {
05000       cached_header->field_value_append(new_cwarn, move_warn, move_warn_len, true);
05001     } else {
05002       new_cwarn = cached_header->field_create(MIME_FIELD_WARNING, MIME_LEN_WARNING);
05003       cached_header->field_attach(new_cwarn);
05004       cached_header->field_value_set(new_cwarn, move_warn, move_warn_len);
05005     }
05006 
05007     r_warn = r_warn->m_next_dup;
05008   }
05009 }
05010 
05011 void
05012 HttpTransact::get_ka_info_from_host_db(State *s, ConnectionAttributes *server_info,
05013                                        ConnectionAttributes * /* client_info ATS_UNUSED */, HostDBInfo *host_db_info)
05014 {
05015   ////////////////////////////////////////////////////////
05016   // Set the keep-alive and version flags for later use //
05017   // in request construction                            //
05018   // this is also used when opening a connection to     //
05019   // the origin server, and search_keepalive_to().      //
05020   ////////////////////////////////////////////////////////
05021 
05022   bool force_http11 = false;
05023   bool http11_if_hostdb = false;
05024 
05025   switch (s->txn_conf->send_http11_requests) {
05026   case HttpConfigParams::SEND_HTTP11_NEVER:
05027     // No need to do anything since above vars
05028     //   are defaulted false
05029     break;
05030   case HttpConfigParams::SEND_HTTP11_ALWAYS:
05031     force_http11 = true;
05032     break;
05033   case HttpConfigParams::SEND_HTTP11_UPGRADE_HOSTDB:
05034     http11_if_hostdb = true;
05035     break;
05036   default:
05037     ink_assert(0);
05038     // FALL THROUGH
05039   case HttpConfigParams::SEND_HTTP11_IF_REQUEST_11_AND_HOSTDB:
05040     if (s->hdr_info.client_request.version_get() == HTTPVersion(1, 1)) {
05041       http11_if_hostdb = true;
05042     }
05043     break;
05044   }
05045 
05046   if (force_http11 == true ||
05047       (http11_if_hostdb == true &&
05048        host_db_info->app.http_data.http_version == HostDBApplicationInfo::HTTP_VERSION_11)) {
05049     server_info->http_version.set(1, 1);
05050     server_info->keep_alive = HTTP_KEEPALIVE;
05051   } else if (host_db_info->app.http_data.http_version == HostDBApplicationInfo::HTTP_VERSION_10) {
05052     server_info->http_version.set(1, 0);
05053     server_info->keep_alive = HTTP_KEEPALIVE;
05054   } else if (host_db_info->app.http_data.http_version == HostDBApplicationInfo::HTTP_VERSION_09) {
05055     server_info->http_version.set(0, 9);
05056     server_info->keep_alive = HTTP_NO_KEEPALIVE;
05057   } else {
05058     //////////////////////////////////////////////
05059     // not set yet for this host. set defaults. //
05060     //////////////////////////////////////////////
05061     server_info->http_version.set(1, 0);
05062     server_info->keep_alive = HTTP_KEEPALIVE;
05063     host_db_info->app.http_data.http_version = HostDBApplicationInfo::HTTP_VERSION_10;
05064   }
05065 
05066   /////////////////////////////
05067   // origin server keep_alive //
05068   /////////////////////////////
05069   if (!s->txn_conf->keep_alive_enabled_out) {
05070     server_info->keep_alive = HTTP_NO_KEEPALIVE;
05071   }
05072 
05073   return;
05074 }
05075 
05076 void
05077 HttpTransact::add_client_ip_to_outgoing_request(State* s, HTTPHdr* request)
05078 {
05079   char ip_string[INET6_ADDRSTRLEN + 1] = {'\0'};
05080   size_t ip_string_size = 0;
05081 
05082   if (!ats_is_ip(&s->client_info.addr.sa))
05083     return;
05084 
05085   // Always prepare the IP string.
05086   if (ats_ip_ntop(&s->client_info.addr.sa, ip_string, sizeof(ip_string)) != NULL) {
05087     ip_string_size += strlen(ip_string);
05088   } else {
05089     // Failure, omg
05090     ip_string_size = 0;
05091     ip_string[0] = 0;
05092   }
05093 
05094   ////////////////////////////////////////////////////////////////
05095   // if we want client-ip headers, and there isn't one, add one //
05096   ////////////////////////////////////////////////////////////////
05097   if ((s->txn_conf->anonymize_insert_client_ip) && (!s->txn_conf->anonymize_remove_client_ip)) {
05098     bool client_ip_set = request->presence(MIME_PRESENCE_CLIENT_IP);
05099     DebugTxn("http_trans", "client_ip_set = %d", client_ip_set);
05100 
05101     if (!client_ip_set && ip_string_size > 0) {
05102       request->value_set(MIME_FIELD_CLIENT_IP, MIME_LEN_CLIENT_IP, ip_string, ip_string_size);
05103       DebugTxn("http_trans", "inserted request header 'Client-ip: %s'", ip_string);
05104     }
05105   }
05106 
05107   if (s->txn_conf->insert_squid_x_forwarded_for) {
05108     if (ip_string_size > 0) {
05109       MIMEField *x_for;
05110 
05111       if ((x_for = request->field_find(MIME_FIELD_X_FORWARDED_FOR, MIME_LEN_X_FORWARDED_FOR)) != 0) {
05112         // http://en.wikipedia.org/wiki/X-Forwarded-For
05113         // The X-Forwarded-For (XFF) HTTP header field is a de facto standard
05114         // for identifying the originating IP address of a client connecting
05115         // to a web server through an HTTP proxy or load balancer. This is a
05116         // non-RFC-standard request field which was introduced by the Squid
05117         // caching proxy server's developers.
05118         //   X-Forwarded-For: client1, proxy1, proxy2
05119         request->field_value_append(x_for, ip_string, ip_string_size, true);  // true => comma must be inserted
05120       } else {
05121         request->value_set(MIME_FIELD_X_FORWARDED_FOR, MIME_LEN_X_FORWARDED_FOR, ip_string, ip_string_size);
05122       }
05123       DebugTxn("http_trans", "[add_client_ip_to_outgoing_request] Appended connecting client's "
05124             "(%s) to the X-Forwards header", ip_string);
05125     }
05126   }
05127 }
05128 
05129 ///////////////////////////////////////////////////////////////////////////////
05130 // Name       : check_request_validity()
05131 // Description: checks to see if incoming request has necessary fields
05132 //
05133 // Input      : State, header (can we do this without the state?)
05134 // Output     : enum RequestError_t of the error type, if any
05135 //
05136 // Details    :
05137 //
05138 //
05139 ///////////////////////////////////////////////////////////////////////////////
05140 HttpTransact::RequestError_t HttpTransact::check_request_validity(State* s, HTTPHdr* incoming_hdr)
05141 {
05142   if (incoming_hdr == 0) {
05143     return NON_EXISTANT_REQUEST_HEADER;
05144   }
05145 
05146   if (!(HttpTransactHeaders::is_request_proxy_authorized(incoming_hdr))) {
05147     return FAILED_PROXY_AUTHORIZATION;
05148   }
05149 
05150   URL *incoming_url = incoming_hdr->url_get();
05151   int hostname_len;
05152   const char *hostname = incoming_hdr->host_get(&hostname_len);
05153 
05154   if (hostname == NULL) {
05155     return MISSING_HOST_FIELD;
05156   }
05157 
05158   if (hostname_len >= MAXDNAME || hostname_len <= 0 || memchr(hostname, '\0', hostname_len)) {
05159     return BAD_HTTP_HEADER_SYNTAX;
05160   }
05161 
05162   int scheme = incoming_url->scheme_get_wksidx();
05163   int method = incoming_hdr->method_get_wksidx();
05164 
05165   // Check for chunked encoding
05166   if (incoming_hdr->presence(MIME_PRESENCE_TRANSFER_ENCODING)) {
05167     MIMEField *field = incoming_hdr->field_find(MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING);
05168     HdrCsvIter enc_val_iter;
05169     int enc_val_len;
05170     const char *enc_value = enc_val_iter.get_first(field, &enc_val_len);
05171 
05172     while (enc_value) {
05173       const char *wks_value = hdrtoken_string_to_wks(enc_value, enc_val_len);
05174       if (wks_value == HTTP_VALUE_CHUNKED) {
05175           s->client_info.transfer_encoding = CHUNKED_ENCODING;
05176         break;
05177       }
05178         enc_value = enc_val_iter.get_next(&enc_val_len);
05179     }
05180   }
05181 
05182   /////////////////////////////////////////////////////
05183   // get request content length                      //
05184   // To avoid parsing content-length twice, we set   //
05185   // s->hdr_info.request_content_length here rather  //
05186   // than in initialize_state_variables_from_request //
05187   /////////////////////////////////////////////////////
05188   if (method != HTTP_WKSIDX_TRACE) {
05189     int64_t length = incoming_hdr->get_content_length();
05190     s->hdr_info.request_content_length = (length >= 0) ? length : HTTP_UNDEFINED_CL;    // content length less than zero is invalid
05191 
05192     DebugTxn("http_trans", "[init_stat_vars_from_req] set req cont length to %" PRId64,
05193           s->hdr_info.request_content_length);
05194 
05195   } else {
05196     s->hdr_info.request_content_length = 0;
05197   }
05198 
05199   if (!((scheme == URL_WKSIDX_HTTP) && (method == HTTP_WKSIDX_GET))) {
05200     if (scheme != URL_WKSIDX_HTTP && scheme != URL_WKSIDX_HTTPS &&
05201         method != HTTP_WKSIDX_CONNECT &&
05202         ((scheme == URL_WKSIDX_WS || scheme == URL_WKSIDX_WSS) && !s->is_websocket)) {
05203       if (scheme < 0) {
05204         return NO_REQUEST_SCHEME;
05205       } else {
05206         return SCHEME_NOT_SUPPORTED;
05207       }
05208     }
05209 
05210     if (!HttpTransactHeaders::is_this_method_supported(scheme, method)) {
05211       return METHOD_NOT_SUPPORTED;
05212     }
05213     if ((method == HTTP_WKSIDX_CONNECT) && !s->transparent_passthrough && (!is_port_in_range(incoming_hdr->url_get()->port_get(), s->http_config_param->connect_ports))) {
05214       return BAD_CONNECT_PORT;
05215     }
05216 
05217     // Require Content-Length/Transfer-Encoding for POST/PUSH/PUT
05218     if ((scheme == URL_WKSIDX_HTTP || scheme == URL_WKSIDX_HTTPS) &&
05219         (method == HTTP_WKSIDX_POST || method == HTTP_WKSIDX_PUSH || method == HTTP_WKSIDX_PUT) &&
05220         s->client_info.transfer_encoding != CHUNKED_ENCODING) {
05221       if ((s->txn_conf->post_check_content_length_enabled) &&
05222           !incoming_hdr->presence(MIME_PRESENCE_CONTENT_LENGTH)) {
05223         return NO_POST_CONTENT_LENGTH;
05224       }
05225       if (HTTP_UNDEFINED_CL == s->hdr_info.request_content_length) {
05226         return INVALID_POST_CONTENT_LENGTH;
05227       }
05228     }
05229   }
05230   // Check whether a Host header field is missing in the request.
05231   if (!incoming_hdr->presence(MIME_PRESENCE_HOST) && incoming_hdr->version_get() != HTTPVersion(0, 9)) {
05232     // Update the number of incoming 1.0 or 1.1 requests that do
05233     // not contain Host header fields.
05234     HTTP_INCREMENT_TRANS_STAT(http_missing_host_hdr_stat);
05235   }
05236   // Did the client send a "TE: identity;q=0"? We have to respond
05237   // with an error message because we only support identity
05238   // Transfer Encoding.
05239 
05240   if (incoming_hdr->presence(MIME_PRESENCE_TE)) {
05241     MIMEField *te_field = incoming_hdr->field_find(MIME_FIELD_TE, MIME_LEN_TE);
05242     HTTPValTE *te_val;
05243 
05244     if (te_field) {
05245       HdrCsvIter csv_iter;
05246       int te_raw_len;
05247       const char *te_raw = csv_iter.get_first(te_field, &te_raw_len);
05248 
05249       while (te_raw) {
05250         te_val = http_parse_te(te_raw, te_raw_len, &s->arena);
05251         if (te_val->encoding == HTTP_VALUE_IDENTITY) {
05252           if (te_val->qvalue <= 0.0) {
05253             s->arena.free(te_val, sizeof(HTTPValTE));
05254             return UNACCEPTABLE_TE_REQUIRED;
05255           }
05256         }
05257         s->arena.free(te_val, sizeof(HTTPValTE));
05258         te_raw = csv_iter.get_next(&te_raw_len);
05259       }
05260     }
05261   }
05262 
05263   return NO_REQUEST_HEADER_ERROR;
05264 }
05265 
05266 HttpTransact::ResponseError_t HttpTransact::check_response_validity(State* s, HTTPHdr* incoming_hdr)
05267 {
05268   ink_assert(s->next_hop_scheme == URL_WKSIDX_HTTP || s->next_hop_scheme == URL_WKSIDX_HTTPS);
05269 
05270   if (incoming_hdr == 0) {
05271     return NON_EXISTANT_RESPONSE_HEADER;
05272   }
05273 
05274   if (incoming_hdr->type_get() != HTTP_TYPE_RESPONSE) {
05275     return NOT_A_RESPONSE_HEADER;
05276   }
05277   // If the response is 0.9 then there is no status
05278   //   code or date
05279   if (did_forward_server_send_0_9_response(s) == true) {
05280     return NO_RESPONSE_HEADER_ERROR;
05281   }
05282 
05283   HTTPStatus incoming_status = incoming_hdr->status_get();
05284   if (!incoming_status) {
05285     return MISSING_STATUS_CODE;
05286   }
05287 
05288   if (incoming_status == HTTP_STATUS_INTERNAL_SERVER_ERROR) {
05289     return STATUS_CODE_SERVER_ERROR;
05290   }
05291 
05292   if (!incoming_hdr->presence(MIME_PRESENCE_DATE)) {
05293     incoming_hdr->set_date(s->current.now);
05294   }
05295 //     if (! incoming_hdr->get_reason_phrase()) {
05296 //      return MISSING_REASON_PHRASE;
05297 //     }
05298 
05299 #ifdef REALLY_NEED_TO_CHECK_DATE_VALIDITY
05300 
05301   if (incoming_hdr->presence(MIME_PRESENCE_DATE)) {
05302     time_t date_value = incoming_hdr->get_date();
05303     if (date_value <= 0) {
05304 // following lines commented out because of performance
05305 // concerns
05306 //          if (s->http_config_param->errors_log_error_pages) {
05307 //              const char *date_string =
05308 //                    incoming_hdr->value_get(MIME_FIELD_DATE);
05309 //              Log::error ("Incoming response has bogus date value: %d: %s",
05310 //                          date_value, date_string ? date_string : "(null)");
05311 //          }
05312 
05313       DebugTxn("http_trans", "[check_response_validity] Bogus date in response");
05314       return BOGUS_OR_NO_DATE_IN_RESPONSE;
05315     }
05316   } else {
05317     DebugTxn("http_trans", "[check_response_validity] No date in response");
05318     return BOGUS_OR_NO_DATE_IN_RESPONSE;
05319   }
05320 #endif
05321 
05322   return NO_RESPONSE_HEADER_ERROR;
05323 }
05324 
05325 bool
05326 HttpTransact::did_forward_server_send_0_9_response(State* s)
05327 {
05328   if (s->hdr_info.server_response.version_get() == HTTPVersion(0, 9)) {
05329     s->current.server->http_version.set(0, 9);
05330     return true;
05331   }
05332   return false;
05333 }
05334 
05335 bool
05336 HttpTransact::handle_internal_request(State* /* s ATS_UNUSED */, HTTPHdr* incoming_hdr)
05337 {
05338   URL *url;
05339 
05340   ink_assert(incoming_hdr->type_get() == HTTP_TYPE_REQUEST);
05341 
05342   if (incoming_hdr->method_get_wksidx() != HTTP_WKSIDX_GET) {
05343     return false;
05344   }
05345 
05346   url = incoming_hdr->url_get();
05347 
05348   int scheme = url->scheme_get_wksidx();
05349   if (scheme != URL_WKSIDX_HTTP && scheme != URL_WKSIDX_HTTPS) {
05350     return false;
05351   }
05352 
05353   if (!statPagesManager.is_stat_page(url)) {
05354     return false;
05355   }
05356 
05357   return true;
05358 }
05359 
05360 bool
05361 HttpTransact::handle_trace_and_options_requests(State* s, HTTPHdr* incoming_hdr)
05362 {
05363   ink_assert(incoming_hdr->type_get() == HTTP_TYPE_REQUEST);
05364 
05365   // This only applies to TRACE and OPTIONS
05366   if ((s->method != HTTP_WKSIDX_TRACE) && (s->method != HTTP_WKSIDX_OPTIONS))
05367     return false;
05368 
05369   // If there is no Max-Forwards request header, just return false.
05370   if (!incoming_hdr->presence(MIME_PRESENCE_MAX_FORWARDS)) {
05371     // Trace and Options requests should not be looked up in cache.
05372     // s->cache_info.action = CACHE_DO_NO_ACTION;
05373     s->current.mode = TUNNELLING_PROXY;
05374     HTTP_INCREMENT_TRANS_STAT(http_tunnels_stat);
05375     return false;
05376   }
05377 
05378   int max_forwards = incoming_hdr->get_max_forwards();
05379   if (max_forwards <= 0) {
05380     //////////////////////////////////////////////
05381     // if max-forward is 0 the request must not //
05382     // be forwarded to the origin server.       //
05383     //////////////////////////////////////////////
05384     DebugTxn("http_trans", "[handle_trace] max-forwards: 0, building response...");
05385     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
05386     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, HTTP_STATUS_OK);
05387 
05388     ////////////////////////////////////////
05389     // if method is trace we should write //
05390     // the request header as the body.    //
05391     ////////////////////////////////////////
05392     if (s->method == HTTP_WKSIDX_TRACE) {
05393       DebugTxn("http_trans", "[handle_trace] inserting request in body.");
05394       int req_length = incoming_hdr->length_get();
05395       HTTP_RELEASE_ASSERT(req_length > 0);
05396 
05397       s->internal_msg_buffer_index = 0;
05398       s->internal_msg_buffer_size = req_length * 2;
05399       s->free_internal_msg_buffer();
05400 
05401       if (s->internal_msg_buffer_size <= max_iobuffer_size) {
05402         s->internal_msg_buffer_fast_allocator_size = buffer_size_to_index(s->internal_msg_buffer_size);
05403         s->internal_msg_buffer = (char *) ioBufAllocator[s->internal_msg_buffer_fast_allocator_size].alloc_void();
05404       } else {
05405         s->internal_msg_buffer_fast_allocator_size = -1;
05406         s->internal_msg_buffer = (char *)ats_malloc(s->internal_msg_buffer_size);
05407       }
05408 
05409       // clear the stupid buffer
05410       memset(s->internal_msg_buffer, '\0', s->internal_msg_buffer_size);
05411 
05412       int offset = 0;
05413       int used = 0;
05414       int done;
05415       done = incoming_hdr->print(s->internal_msg_buffer, s->internal_msg_buffer_size, &used, &offset);
05416       HTTP_RELEASE_ASSERT(done);
05417       s->internal_msg_buffer_size = used;
05418 
05419       s->hdr_info.client_response.set_content_length(used);
05420     } else {
05421       // For OPTIONS request insert supported methods in ALLOW field
05422       DebugTxn("http_trans", "[handle_options] inserting methods in Allow.");
05423       HttpTransactHeaders::insert_supported_methods_in_response(&s->hdr_info.client_response, s->scheme);
05424 
05425     }
05426     return true;
05427   } else {                      /* max-forwards != 0 */
05428 
05429     if ((max_forwards <= 0) || (max_forwards > INT_MAX)) {
05430       Log::error("HTTP: snapping invalid max-forwards value %d to %d", max_forwards, INT_MAX);
05431       max_forwards = INT_MAX;
05432     }
05433 
05434     --max_forwards;
05435     DebugTxn("http_trans", "[handle_trace_options] Decrementing max_forwards to %d", max_forwards);
05436     incoming_hdr->set_max_forwards(max_forwards);
05437 
05438     // Trace and Options requests should not be looked up in cache.
05439     // s->cache_info.action = CACHE_DO_NO_ACTION;
05440     s->current.mode = TUNNELLING_PROXY;
05441     HTTP_INCREMENT_TRANS_STAT(http_tunnels_stat);
05442   }
05443 
05444   return false;
05445 }
05446 
05447 void
05448 HttpTransact::initialize_state_variables_for_origin_server(State* s, HTTPHdr* incoming_request, bool second_time)
05449 {
05450   if (s->server_info.name && !second_time) {
05451     ink_assert(s->server_info.port != 0);
05452   }
05453 
05454   int host_len;
05455   const char *host = incoming_request->host_get(&host_len);
05456   s->server_info.name = s->arena.str_store(host, host_len);
05457   s->server_info.port = incoming_request->port_get();
05458 
05459   if (second_time) {
05460     s->dns_info.attempts = 0;
05461     s->dns_info.lookup_name = s->server_info.name;
05462   }
05463 }
05464 
05465 void
05466 HttpTransact::bootstrap_state_variables_from_request(State* s, HTTPHdr* incoming_request)
05467 {
05468   s->current.now = s->client_request_time = ink_cluster_time();
05469   s->client_info.http_version = incoming_request->version_get();
05470 }
05471 
05472 void
05473 HttpTransact::initialize_state_variables_from_request(State* s, HTTPHdr* obsolete_incoming_request)
05474 {
05475   HTTPHdr* incoming_request = &s->hdr_info.client_request;
05476 
05477   // Temporary, until we're confident that the second argument is redundant.
05478   ink_assert(incoming_request == obsolete_incoming_request);
05479 
05480   int host_len;
05481   const char *host_name = incoming_request->host_get(&host_len);
05482 
05483   // check if the request is conditional (IMS or INM)
05484   if (incoming_request->presence(MIME_PRESENCE_IF_MODIFIED_SINCE | MIME_PRESENCE_IF_NONE_MATCH)) {
05485     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_IMS);
05486   } else {
05487     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_SIMPLE);
05488   }
05489 
05490   // Is the user agent Keep-Alive?
05491   //  If we are transparent or if the user-agent is following
05492   //  the 1.1 spec, we will see a "Connection" header to
05493   //  indicate a keep-alive.  However most user-agents including
05494   //  MSIE3.0, Netscape4.04 and Netscape3.01 send Proxy-Connection
05495   //  when they are configured to use a proxy.  Proxy-Connection
05496   //  is not in the spec but was added to prevent problems
05497   //  with a dumb proxy forwarding all headers (including "Connection")
05498   //  to the origin server and confusing it.  In cases of transparent
05499   //  deployments we use the Proxy-Connect hdr (to be as transparent
05500   //  as possible).
05501   MIMEField *pc = incoming_request->field_find(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
05502 
05503   // If we need to send a close header later check to see if it should be "Proxy-Connection"
05504   if (pc != NULL) {
05505     s->client_info.proxy_connect_hdr = true;
05506   }
05507 
05508   if (!s->txn_conf->keep_alive_enabled_in) {
05509     s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
05510   } else {
05511     // If there is a Proxy-Connection header use that, otherwise use the Connection header
05512     if (pc != NULL) {
05513       s->client_info.keep_alive = is_header_keep_alive(s->client_info.http_version, s->client_info.http_version, pc);
05514     } else {
05515       MIMEField *c = incoming_request->field_find(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
05516 
05517       s->client_info.keep_alive = is_header_keep_alive(s->client_info.http_version, s->client_info.http_version, c);
05518     }
05519   }
05520 
05521   if (s->client_info.keep_alive == HTTP_KEEPALIVE && s->client_info.http_version == HTTPVersion(1, 1)) {
05522     s->client_info.pipeline_possible = true;
05523   }
05524 
05525   if (!s->server_info.name || s->redirect_info.redirect_in_process) {
05526     s->server_info.name = s->arena.str_store(host_name, host_len);
05527     s->server_info.port = incoming_request->port_get();
05528   } else {
05529     ink_assert(s->server_info.port != 0);
05530   }
05531 
05532   s->next_hop_scheme = s->scheme = incoming_request->url_get()->scheme_get_wksidx();
05533 
05534   // With websockets we need to make an outgoing request
05535   // as http or https.
05536   // We switch back to HTTP or HTTPS for the next hop
05537   // I think this is required to properly establish outbound WSS connections,
05538   // you'll need to force the next hop to be https.
05539   if (s->is_websocket) {
05540     if (s->next_hop_scheme == URL_WKSIDX_WS) {
05541       DebugTxn("http_trans", "Switching WS next hop scheme to http.");
05542       s->next_hop_scheme = URL_WKSIDX_HTTP;
05543       s->scheme = URL_WKSIDX_HTTP;
05544       //s->request_data.hdr->url_get()->scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP);
05545     } else if (s->next_hop_scheme == URL_WKSIDX_WSS) {
05546       DebugTxn("http_trans", "Switching WSS next hop scheme to https.");
05547       s->next_hop_scheme = URL_WKSIDX_HTTPS;
05548       s->scheme = URL_WKSIDX_HTTPS;
05549       //s->request_data.hdr->url_get()->scheme_set(URL_SCHEME_HTTPS, URL_LEN_HTTPS);
05550     } else {
05551       Error("Scheme doesn't match websocket...!");
05552     }
05553 
05554     s->current.mode = GENERIC_PROXY;
05555     s->cache_info.action = CACHE_DO_NO_ACTION;
05556   }
05557 
05558   s->method = incoming_request->method_get_wksidx();
05559 
05560   if (s->method == HTTP_WKSIDX_GET) {
05561     HTTP_INCREMENT_TRANS_STAT(http_get_requests_stat);
05562   } else if (s->method == HTTP_WKSIDX_HEAD) {
05563     HTTP_INCREMENT_TRANS_STAT(http_head_requests_stat);
05564   } else if (s->method == HTTP_WKSIDX_POST) {
05565     HTTP_INCREMENT_TRANS_STAT(http_post_requests_stat);
05566   } else if (s->method == HTTP_WKSIDX_PUT) {
05567     HTTP_INCREMENT_TRANS_STAT(http_put_requests_stat);
05568   } else if (s->method == HTTP_WKSIDX_CONNECT) {
05569     HTTP_INCREMENT_TRANS_STAT(http_connect_requests_stat);
05570   } else if (s->method == HTTP_WKSIDX_DELETE) {
05571     HTTP_INCREMENT_TRANS_STAT(http_delete_requests_stat);
05572   } else if (s->method == HTTP_WKSIDX_PURGE) {
05573     HTTP_INCREMENT_TRANS_STAT(http_purge_requests_stat);
05574   } else if (s->method == HTTP_WKSIDX_TRACE) {
05575     HTTP_INCREMENT_TRANS_STAT(http_trace_requests_stat);
05576   } else if (s->method == HTTP_WKSIDX_PUSH) {
05577     HTTP_INCREMENT_TRANS_STAT(http_push_requests_stat);
05578   } else if (s->method == HTTP_WKSIDX_OPTIONS) {
05579     HTTP_INCREMENT_TRANS_STAT(http_options_requests_stat);
05580   } else if (s->method == HTTP_WKSIDX_TRACE) {
05581     HTTP_INCREMENT_TRANS_STAT(http_trace_requests_stat);
05582   } else {
05583     HTTP_INCREMENT_TRANS_STAT(http_extension_method_requests_stat);
05584     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_METHOD);
05585     s->squid_codes.log_code = SQUID_LOG_TCP_MISS;
05586     s->hdr_info.extension_method = true;
05587   }
05588 
05589   // if transfer encoding is chunked content length is undefined
05590   if (s->client_info.transfer_encoding == CHUNKED_ENCODING) {
05591     s->hdr_info.request_content_length = HTTP_UNDEFINED_CL;
05592   }
05593   s->request_data.hdr = &s->hdr_info.client_request;
05594 
05595   s->request_data.hostname_str = s->arena.str_store(host_name, host_len);
05596   ats_ip_copy(&s->request_data.src_ip, &s->client_info.addr);
05597   memset(&s->request_data.dest_ip, 0, sizeof(s->request_data.dest_ip));
05598   if (s->state_machine->ua_session) {
05599     s->request_data.incoming_port = s->state_machine->ua_session->get_netvc()->get_local_port();
05600   }
05601   s->request_data.xact_start = s->client_request_time;
05602   s->request_data.api_info = &s->api_info;
05603 
05604   /////////////////////////////////////////////
05605   // Do dns lookup for the host. We need     //
05606   // the expanded host for cache lookup, and //
05607   // the host ip for reverse proxy.          //
05608   /////////////////////////////////////////////
05609   s->dns_info.looking_up = ORIGIN_SERVER;
05610   s->dns_info.attempts = 0;
05611   s->dns_info.lookup_name = s->server_info.name;
05612 }
05613 
05614 void
05615 HttpTransact::initialize_state_variables_from_response(State* s, HTTPHdr* incoming_response)
05616 {
05617   /* check if the server permits caching */
05618   s->cache_info.directives.does_server_permit_storing =
05619     HttpTransactHeaders::does_server_allow_response_to_be_stored(&s->hdr_info.server_response);
05620 
05621   /*
05622    * A stupid moronic broken pathetic excuse
05623    *   for a server may send us a keep alive response even
05624    *   if we sent "Connection: close"  We need check the response
05625    *   header regardless of what we sent to the server
05626    */
05627   MIMEField *c_hdr;
05628   if ((s->current.request_to != ORIGIN_SERVER) &&
05629       (s->current.request_to == PARENT_PROXY ||
05630        s->current.request_to == ICP_SUGGESTED_HOST)) {
05631     c_hdr = s->hdr_info.server_response.field_find(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
05632 
05633     // If there is a Proxy-Connection header use that,
05634     //   otherwise use the Connection header
05635     if (c_hdr == NULL) {
05636       c_hdr = s->hdr_info.server_response.field_find(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
05637     }
05638   } else {
05639     c_hdr = s->hdr_info.server_response.field_find(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
05640   }
05641 
05642   s->current.server->keep_alive = is_header_keep_alive(s->hdr_info.server_response.version_get(),
05643                                                        s->hdr_info.server_request.version_get(), c_hdr);
05644 
05645   // Don't allow an upgrade request to Keep Alive
05646   if (s->is_upgrade_request) {
05647     s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
05648   }
05649 
05650   if (s->current.server->keep_alive == HTTP_KEEPALIVE) {
05651     if (!s->cop_test_page)
05652       DebugTxn("http_hdrs", "[initialize_state_variables_from_response]" "Server is keep-alive.");
05653   } else if (s->state_machine->ua_session && s->state_machine->ua_session->f_outbound_transparent && s->state_machine->t_state.http_config_param->use_client_source_port) {
05654     /* If we are reusing the client<->ATS 4-tuple for ATS<->server then if the server side is closed, we can't
05655        re-open it because the 4-tuple may still be in the processing of shutting down. So if the server isn't
05656        keep alive we must turn that off for the client as well.
05657     */
05658     s->state_machine->t_state.client_info.keep_alive = HTTP_NO_KEEPALIVE;
05659   }
05660 
05661 
05662   HTTPStatus status_code = incoming_response->status_get();
05663   if (is_response_body_precluded(status_code, s->method)) {
05664     s->hdr_info.response_content_length = 0;
05665     s->hdr_info.trust_response_cl = true;
05666   } else {
05667     // This code used to discriminate CL: headers when the origin disabled keep-alive.
05668     if (incoming_response->presence(MIME_PRESENCE_CONTENT_LENGTH)) {
05669       int64_t cl = incoming_response->get_content_length();
05670 
05671       s->hdr_info.response_content_length = (cl >= 0) ? cl : HTTP_UNDEFINED_CL;
05672       s->hdr_info.trust_response_cl = true;
05673     } else {
05674       s->hdr_info.response_content_length = HTTP_UNDEFINED_CL;
05675       s->hdr_info.trust_response_cl = false;
05676     }
05677   }
05678 
05679   if (incoming_response->presence(MIME_PRESENCE_TRANSFER_ENCODING)) {
05680     MIMEField *field = incoming_response->field_find(MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING);
05681 
05682     HdrCsvIter enc_val_iter;
05683     int enc_val_len;
05684     const char *enc_value = enc_val_iter.get_first(field, &enc_val_len);
05685 
05686     while (enc_value) {
05687       const char *wks_value = hdrtoken_string_to_wks(enc_value, enc_val_len);
05688 
05689       if (wks_value == HTTP_VALUE_CHUNKED) {
05690         if (!s->cop_test_page) {
05691           DebugTxn("http_hdrs", "[init_state_vars_from_resp] transfer encoding: chunked!");
05692         }
05693         s->current.server->transfer_encoding = CHUNKED_ENCODING;
05694 
05695         s->hdr_info.response_content_length = HTTP_UNDEFINED_CL;
05696         s->hdr_info.trust_response_cl = false;
05697 
05698         // OBJECTIVE: Since we are dechunking the request remove the
05699         //   chunked value If this is the only value, we need to remove
05700         //    the whole field.
05701         MIMEField *new_enc_field = NULL;
05702         HdrCsvIter new_enc_iter;
05703         int new_enc_len;
05704         const char *new_enc_val = new_enc_iter.get_first(field, &new_enc_len);
05705 
05706         // Loop over the all the values in existing Trans-enc header and
05707         //   copy the ones that aren't our chunked value to a new field
05708         while (new_enc_val) {
05709           const char *new_wks_value = hdrtoken_string_to_wks(new_enc_val, new_enc_len);
05710           if (new_wks_value != wks_value) {
05711             if (new_enc_field) {
05712               new_enc_field->value_append(incoming_response->m_heap, incoming_response->m_mime, new_enc_val, new_enc_len, true);
05713             } else {
05714               new_enc_field = incoming_response->field_create();
05715               incoming_response->field_value_set(new_enc_field, new_enc_val, new_enc_len);
05716             }
05717           }
05718 
05719           new_enc_val = new_enc_iter.get_next(&new_enc_len);
05720         }
05721 
05722         // We're done with the old field since we copied out everything
05723         //   we needed
05724         incoming_response->field_delete(field);
05725 
05726         // If there is a new field (ie: there was more than one
05727         //   transfer-encoding), insert it to the list
05728         if (new_enc_field) {
05729           new_enc_field->name_set(incoming_response->m_heap, incoming_response->m_mime, MIME_FIELD_TRANSFER_ENCODING,
05730                                   MIME_LEN_TRANSFER_ENCODING);
05731           incoming_response->field_attach(new_enc_field);
05732         }
05733 
05734         return;
05735       }                         //  if (enc_value == CHUNKED)
05736 
05737       enc_value = enc_val_iter.get_next(&enc_val_len);
05738     }
05739   }
05740 
05741   s->current.server->transfer_encoding = NO_TRANSFER_ENCODING;
05742 }
05743 
05744 
05745 bool
05746 HttpTransact::is_cache_response_returnable(State* s)
05747 {
05748   if (s->cache_control.never_cache) {
05749     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_CONFIG);
05750     return false;
05751   }
05752 
05753   if (!s->cache_info.directives.does_client_permit_lookup) {
05754     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_CLIENT);
05755     return false;
05756   }
05757 
05758   if (!HttpTransactHeaders::is_method_cacheable(s->http_config_param, s->method) && s->api_resp_cacheable == false) {
05759     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_NOT_ACCEPTABLE);
05760     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_METHOD);
05761     return false;
05762   }
05763   // If cookies in response and no TTL set, we do not cache the doc
05764   if ((s->cache_control.ttl_in_cache <= 0) &&
05765       do_cookies_prevent_caching((int) s->txn_conf->cache_responses_to_cookies,
05766                                  &s->hdr_info.client_request, s->cache_info.object_read->response_get(),
05767                                  s->cache_info.object_read->request_get())) {
05768     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_NOT_ACCEPTABLE);
05769     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_COOKIE);
05770     return false;
05771   }
05772 
05773   return true;
05774 }
05775 
05776 ///////////////////////////////////////////////////////////////////////////////
05777 // Name       : is_stale_cache_response_returnable()
05778 // Description: check if a stale cached response is returnable to a client
05779 //
05780 // Input      : State
05781 // Output     : true or false
05782 //
05783 // Details    :
05784 //
05785 ///////////////////////////////////////////////////////////////////////////////
05786 bool
05787 HttpTransact::is_stale_cache_response_returnable(State* s)
05788 {
05789   HTTPHdr *cached_response = s->cache_info.object_read->response_get();
05790 
05791   // First check if client allows cached response
05792   // Note does_client_permit_lookup was set to
05793   // does_client_Request_permit_cached_response()
05794   // in update_cache_control_information_from_config().
05795   if (!s->cache_info.directives.does_client_permit_lookup) {
05796     return false;
05797   }
05798   // Spec says that we can not serve a stale document with a
05799   //   "must-revalidate header"
05800   // How about "s-maxage" and "no-cache" directives?
05801   uint32_t cc_mask;
05802   cc_mask = (MIME_COOKED_MASK_CC_MUST_REVALIDATE |
05803              MIME_COOKED_MASK_CC_PROXY_REVALIDATE |
05804              MIME_COOKED_MASK_CC_NEED_REVALIDATE_ONCE |
05805              MIME_COOKED_MASK_CC_NO_CACHE | MIME_COOKED_MASK_CC_NO_STORE | MIME_COOKED_MASK_CC_S_MAXAGE);
05806   if ((cached_response->get_cooked_cc_mask() & cc_mask) || cached_response->is_pragma_no_cache_set()) {
05807     DebugTxn("http_trans", "[is_stale_cache_response_returnable] " "document headers prevent serving stale");
05808     return false;
05809   }
05810   // See how old the document really is.  We don't want create a
05811   //   stale content museum of documents that are no longer available
05812   time_t current_age = HttpTransactHeaders::calculate_document_age(s->cache_info.object_read->request_sent_time_get(),
05813                                                                    s->cache_info.object_read->response_received_time_get(),
05814                                                                    cached_response,
05815                                                                    cached_response->get_date(),
05816                                                                    s->current.now);
05817   // Negative age is overflow
05818   if ((current_age < 0) || (current_age > s->txn_conf->cache_max_stale_age)) {
05819     DebugTxn("http_trans", "[is_stale_cache_response_returnable] " "document age is too large %" PRId64,
05820              (int64_t)current_age);
05821     return false;
05822   }
05823   // If the stale document requires authorization, we can't return it either.
05824   Authentication_t auth_needed = AuthenticationNeeded(s->txn_conf, &s->hdr_info.client_request, cached_response);
05825 
05826   if (auth_needed != AUTHENTICATION_SUCCESS) {
05827     DebugTxn("http_trans", "[is_stale_cache_response_returnable] " "authorization prevent serving stale");
05828     return false;
05829   }
05830 
05831   DebugTxn("http_trans", "[is_stale_cache_response_returnable] can serve stale");
05832   return true;
05833 }
05834 
05835 
05836 bool
05837 HttpTransact::url_looks_dynamic(URL* url)
05838 {
05839   const char *p_start, *p, *t;
05840   static const char *asp = ".asp";
05841   const char *part;
05842   int part_length;
05843 
05844   if (url->scheme_get_wksidx() != URL_WKSIDX_HTTP && url->scheme_get_wksidx() != URL_WKSIDX_HTTPS) {
05845     return false;
05846   }
05847   ////////////////////////////////////////////////////////////
05848   // (1) If URL contains query stuff in it, call it dynamic //
05849   ////////////////////////////////////////////////////////////
05850 
05851   part = url->params_get(&part_length);
05852   if (part != NULL) {
05853     return true;
05854   }
05855   part = url->query_get(&part_length);
05856   if (part != NULL) {
05857     return true;
05858   }
05859   ///////////////////////////////////////////////
05860   // (2) If path ends in "asp" call it dynamic //
05861   ///////////////////////////////////////////////
05862 
05863   part = url->path_get(&part_length);
05864   if (part) {
05865     p = &part[part_length - 1];
05866     t = &asp[3];
05867 
05868     while (p != part) {
05869       if (ParseRules::ink_tolower(*p) == ParseRules::ink_tolower(*t)) {
05870         p -= 1;
05871         t -= 1;
05872         if (t == asp)
05873           return true;
05874       } else
05875         break;
05876     }
05877   }
05878   /////////////////////////////////////////////////////////////////
05879   // (3) If the path of the url contains "cgi", call it dynamic. //
05880   /////////////////////////////////////////////////////////////////
05881 
05882   if (part && part_length >= 3) {
05883     for (p_start = part; p_start <= &part[part_length - 3]; p_start++) {
05884       if (((p_start[0] == 'c') || (p_start[0] == 'C')) &&
05885           ((p_start[1] == 'g') || (p_start[1] == 'G')) && ((p_start[2] == 'i') || (p_start[2] == 'I'))) {
05886         return (true);
05887       }
05888     }
05889   }
05890 
05891   return (false);
05892 }
05893 
05894 
05895 ///////////////////////////////////////////////////////////////////////////////
05896 // Name       : is_request_cache_lookupable()
05897 // Description: check if a request should be looked up in cache
05898 //
05899 // Input      : State, request header
05900 // Output     : true or false
05901 //
05902 // Details    :
05903 //
05904 //
05905 ///////////////////////////////////////////////////////////////////////////////
05906 bool
05907 HttpTransact::is_request_cache_lookupable(State* s)
05908 {
05909   // ummm, someone has already decided that proxy should tunnel
05910   if (s->current.mode == TUNNELLING_PROXY) {
05911     return false;
05912   }
05913   // don't bother with remaining checks if we already did a cache lookup
05914   if (s->cache_info.lookup_count > 0) {
05915     return true;
05916   }
05917   // is cache turned on?
05918   if (!s->txn_conf->cache_http) {
05919     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_CACHE_OFF);
05920     return false;
05921   }
05922   // GET, HEAD, POST, DELETE, and PUT are all cache lookupable
05923   if (!HttpTransactHeaders::is_method_cache_lookupable(s->method) && s->api_req_cacheable == false) {
05924     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_METHOD);
05925     return false;
05926   }
05927   // don't cache page if URL "looks dynamic" and this filter is enabled
05928   // We can do the check in is_response_cacheable() or here.
05929   // It may be more efficient if we are not going to cache dynamic looking urls
05930   // (the default config?) since we don't even need to do cache lookup.
05931   // So for the time being, it'll be left here.
05932 
05933   // If url looks dynamic but a ttl is set, request is cache lookupable
05934   if ((!s->txn_conf->cache_urls_that_look_dynamic) &&
05935       url_looks_dynamic(s->hdr_info.client_request.url_get()) && (s->cache_control.ttl_in_cache <= 0)) {
05936     // We do not want to forward the request for a dynamic URL onto the
05937     // origin server if the value of the Max-Forwards header is zero.
05938     int max_forwards = -1;
05939     if (s->hdr_info.client_request.presence(MIME_PRESENCE_MAX_FORWARDS)) {
05940       MIMEField *max_forwards_f = s->hdr_info.client_request.field_find(MIME_FIELD_MAX_FORWARDS, MIME_LEN_MAX_FORWARDS);
05941 
05942       if (max_forwards_f)
05943         max_forwards = max_forwards_f->value_get_int();
05944     }
05945 
05946     if (max_forwards != 0) {
05947       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_URL);
05948       return false;
05949     }
05950   }
05951 
05952   // Don't look in cache if it's a RANGE request but the cache is not enabled for RANGE.
05953   if (!s->txn_conf->cache_range_lookup && s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE)) {
05954     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_HEADER_FIELD);
05955     return false;
05956   }
05957 
05958   // Even with "no-cache" directive, we want to do a cache lookup
05959   // because we need to update our cached copy.
05960   // Client request "no-cache" directive is handle elsewhere:
05961   // update_cache_control_information_from_config()
05962 
05963   return true;
05964 }
05965 
05966 
05967 ///////////////////////////////////////////////////////////////////////////////
05968 // Name       : response_cacheable_indicated_by_cc()
05969 // Description: check if a response is cacheable as indicated by Cache-Control
05970 //
05971 // Input      : Response header
05972 // Output     : -1, 0, or +1
05973 //
05974 // Details    :
05975 // (1) return -1 if cache control indicates response not cacheable,
05976 //     ie, with no-store, or private directives;
05977 // (2) return +1 if cache control indicates response cacheable
05978 //     ie, with public, max-age, s-maxage, must-revalidate, or proxy-revalidate;
05979 // (3) otherwise, return 0 if cache control does not indicate.
05980 //
05981 ///////////////////////////////////////////////////////////////////////////////
05982 int
05983 response_cacheable_indicated_by_cc(HTTPHdr* response)
05984 {
05985   uint32_t cc_mask;
05986   // the following directives imply not cacheable
05987   cc_mask = (MIME_COOKED_MASK_CC_NO_STORE | MIME_COOKED_MASK_CC_PRIVATE);
05988   if (response->get_cooked_cc_mask() & cc_mask) {
05989     return -1;
05990   }
05991   // the following directives imply cacheable
05992   cc_mask = (MIME_COOKED_MASK_CC_PUBLIC |
05993              MIME_COOKED_MASK_CC_MAX_AGE |
05994              MIME_COOKED_MASK_CC_S_MAXAGE | MIME_COOKED_MASK_CC_MUST_REVALIDATE | MIME_COOKED_MASK_CC_PROXY_REVALIDATE);
05995   if (response->get_cooked_cc_mask() & cc_mask) {
05996     return 1;
05997   }
05998   // otherwise, no indication
05999   return 0;
06000 }
06001 
06002 
06003 ///////////////////////////////////////////////////////////////////////////////
06004 // Name       : is_response_cacheable()
06005 // Description: check if a response is cacheable
06006 //
06007 // Input      : State, request header, response header
06008 // Output     : true or false
06009 //
06010 // Details    :
06011 //
06012 ///////////////////////////////////////////////////////////////////////////////
06013 bool
06014 HttpTransact::is_response_cacheable(State* s, HTTPHdr* request, HTTPHdr* response)
06015 {
06016   // If the use_client_target_addr is specified but the client
06017   // specified OS addr does not match any of trafficserver's looked up
06018   // host addresses, do not allow cache.  This may cause DNS cache poisoning
06019   // of other trafficserver clients. The flag is set in the 
06020   // process_host_db_info method
06021   if (!s->dns_info.lookup_validated
06022     && s->client_info.is_transparent) {
06023     DebugTxn("http_trans", "[is_response_cacheable] " "Lookup not validated.  Possible DNS cache poison.  Don't cache");
06024     return false;
06025   }
06026 
06027   // if method is not GET or HEAD, do not cache.
06028   // Note: POST is also cacheable with Expires or Cache-control.
06029   // but due to INKqa11567, we are not caching POST responses.
06030   // Basically, the problem is the resp for POST url1 req should not
06031   // be served to a GET url1 request, but we just match URL not method.
06032   int req_method = request->method_get_wksidx();
06033   if (!(HttpTransactHeaders::is_method_cacheable(s->http_config_param, req_method)) && s->api_req_cacheable == false) {
06034     DebugTxn("http_trans", "[is_response_cacheable] " "only GET, and some HEAD and POST are cachable");
06035     return false;
06036   }
06037   // DebugTxn("http_trans", "[is_response_cacheable] method is cacheable");
06038   // If the request was not looked up in the cache, the response
06039   // should not be cached (same subsequent requests will not be
06040   // looked up, either, so why cache this).
06041   if (!(is_request_cache_lookupable(s))) {
06042     DebugTxn("http_trans", "[is_response_cacheable] " "request is not cache lookupable, response is not cachable");
06043     return (false);
06044   }
06045   // already has a fresh copy in the cache
06046   if (s->range_setup == RANGE_NOT_HANDLED)
06047     return false;
06048 
06049   // Check whether the response is cachable based on its cookie
06050   // If there are cookies in response but a ttl is set, allow caching
06051   if ((s->cache_control.ttl_in_cache <= 0) &&
06052       do_cookies_prevent_caching((int) s->txn_conf->cache_responses_to_cookies, request, response)) {
06053     DebugTxn("http_trans", "[is_response_cacheable] " "response has uncachable cookies, response is not cachable");
06054     return (false);
06055   }
06056   // if server spits back a WWW-Authenticate
06057   if ((s->txn_conf->cache_ignore_auth) == 0 && response->presence(MIME_PRESENCE_WWW_AUTHENTICATE)) {
06058     DebugTxn("http_trans", "[is_response_cacheable] " "response has WWW-Authenticate, response is not cachable");
06059     return (false);
06060   }
06061   // does server explicitly forbid storing?
06062   // If OS forbids storing but a ttl is set, allow caching
06063   if (!s->cache_info.directives.does_server_permit_storing &&
06064       !s->cache_control.ignore_server_no_cache && (s->cache_control.ttl_in_cache <= 0)) {
06065     DebugTxn("http_trans", "[is_response_cacheable] server does not permit storing and config file does not "
06066           "indicate that server directive should be ignored");
06067     return (false);
06068   }
06069   // DebugTxn("http_trans", "[is_response_cacheable] server permits storing");
06070 
06071   // does config explicitly forbid storing?
06072   // ttl overides other config parameters
06073   if ((!s->cache_info.directives.does_config_permit_storing &&
06074        !s->cache_control.ignore_server_no_cache &&
06075        (s->cache_control.ttl_in_cache <= 0)) || (s->cache_control.never_cache)) {
06076     DebugTxn("http_trans", "[is_response_cacheable] config doesn't allow storing, and cache control does not "
06077           "say to ignore no-cache and does not specify never-cache or a ttl");
06078     return (false);
06079   }
06080   // DebugTxn("http_trans", "[is_response_cacheable] config permits storing");
06081 
06082   // does client explicitly forbid storing?
06083   if (!s->cache_info.directives.does_client_permit_storing && !s->cache_control.ignore_client_no_cache) {
06084     DebugTxn("http_trans", "[is_response_cacheable] client does not permit storing, "
06085           "and cache control does not say to ignore client no-cache");
06086     return (false);
06087   }
06088   DebugTxn("http_trans", "[is_response_cacheable] client permits storing");
06089 
06090   HTTPStatus response_code = response->status_get();
06091 
06092   // caching/not-caching based on required headers
06093   // only makes sense when the server sends back a
06094   // 200 and a document.
06095   if (response_code == HTTP_STATUS_OK) {
06096     // If a ttl is set: no header required for caching
06097     // otherwise: follow parameter http.cache.required_headers
06098     if (s->cache_control.ttl_in_cache <= 0) {
06099       uint32_t cc_mask = (MIME_COOKED_MASK_CC_MAX_AGE | MIME_COOKED_MASK_CC_S_MAXAGE);
06100       // server did not send expires header or last modified
06101       // and we are configured to not cache without them.
06102       switch (s->txn_conf->cache_required_headers) {
06103       case HttpConfigParams::CACHE_REQUIRED_HEADERS_NONE:
06104         DebugTxn("http_trans", "[is_response_cacheable] " "no response headers required");
06105         break;
06106 
06107       case HttpConfigParams::CACHE_REQUIRED_HEADERS_AT_LEAST_LAST_MODIFIED:
06108         if (!response->presence(MIME_PRESENCE_EXPIRES) && !(response->get_cooked_cc_mask() & cc_mask) &&
06109             !response->get_last_modified()) {
06110           DebugTxn("http_trans", "[is_response_cacheable] " "last_modified, expires, or max-age is required");
06111 
06112           s->squid_codes.hit_miss_code = ((response->get_date() == 0) ? (SQUID_MISS_HTTP_NO_DLE) : (SQUID_MISS_HTTP_NO_LE));
06113           return (false);
06114         }
06115         break;
06116 
06117       case HttpConfigParams::CACHE_REQUIRED_HEADERS_CACHE_CONTROL:
06118         if (!response->presence(MIME_PRESENCE_EXPIRES) && !(response->get_cooked_cc_mask() & cc_mask)) {
06119           DebugTxn("http_trans", "[is_response_cacheable] " "expires header or max-age is required");
06120           return (false);
06121         }
06122         break;
06123 
06124       default:
06125         break;
06126       }
06127     }
06128   }
06129   // do not cache partial content - Range response
06130   if (response_code == HTTP_STATUS_PARTIAL_CONTENT || response_code == HTTP_STATUS_RANGE_NOT_SATISFIABLE) {
06131     DebugTxn("http_trans", "[is_response_cacheable] " "response code %d - don't cache", response_code);
06132     return false;
06133   }
06134 
06135   // check if cache control overrides default cacheability
06136   int indicator;
06137   indicator = response_cacheable_indicated_by_cc(response);
06138   if (indicator > 0) {          // cacheable indicated by cache control header
06139     DebugTxn("http_trans", "[is_response_cacheable] YES by response cache control");
06140     // even if it is authenticated, this is cacheable based on regular rules
06141     s->www_auth_content = CACHE_AUTH_NONE;
06142     return true;
06143   } else if (indicator < 0) {   // not cacheable indicated by cache control header
06144 
06145     // If a ttl is set, allow caching even if response contains
06146     // Cache-Control headers to prevent caching
06147     if (s->cache_control.ttl_in_cache > 0) {
06148       DebugTxn("http_trans", "[is_response_cacheable] Cache-control header directives in response overridden by ttl in cache.config");
06149     } else if (!s->cache_control.ignore_server_no_cache) {
06150       DebugTxn("http_trans", "[is_response_cacheable] NO by response cache control");
06151       return false;
06152     }
06153   }
06154   // else no indication by cache control header
06155   // continue to determine cacheability
06156 
06157   // if client contains Authorization header,
06158   // only cache if response has proper Cache-Control
06159  // if (s->www_auth_content == CACHE_AUTH_FRESH) {
06160     // response to the HEAD request
06161   //  return false;
06162   //} else if (s->www_auth_content == CACHE_AUTH_TRUE ||
06163    //          (s->www_auth_content == CACHE_AUTH_NONE && request->presence(MIME_PRESENCE_AUTHORIZATION))) {
06164    // if (!s->cache_control.cache_auth_content || response_code != HTTP_STATUS_OK || req_method != HTTP_WKSIDX_GET)
06165     //  return false;
06166   //}
06167   // s->www_auth_content == CACHE_AUTH_STALE silently continues
06168 
06169   if (response->presence(MIME_PRESENCE_EXPIRES)) {
06170     DebugTxn("http_trans", "[is_response_cacheable] YES response w/ Expires");
06171     return true;
06172   }
06173   // if it's a 302 or 307 and no positive indicator from cache-control, reject
06174   if (response_code == HTTP_STATUS_MOVED_TEMPORARILY || response_code == HTTP_STATUS_TEMPORARY_REDIRECT) {
06175     DebugTxn("http_trans", "[is_response_cacheable] cache-control or expires header is required for 302");
06176     return false;
06177   }
06178   // if it's a POST request and no positive indicator from cache-control
06179   if (req_method == HTTP_WKSIDX_POST) {
06180     // allow caching for a POST requests w/o Expires but with a ttl
06181     if (s->cache_control.ttl_in_cache > 0) {
06182       DebugTxn("http_trans", "[is_response_cacheable] POST method with a TTL");
06183     } else {
06184       DebugTxn("http_trans", "[is_response_cacheable] NO POST w/o Expires or CC");
06185       return false;
06186     }
06187   }
06188   // the plugin may decide we don't want to cache the response
06189   if (s->api_server_response_no_store) {
06190     return (false);
06191   }
06192   // default cacheability
06193   if (!s->txn_conf->negative_caching_enabled) {
06194     if ((response_code == HTTP_STATUS_OK) ||
06195         (response_code == HTTP_STATUS_NOT_MODIFIED) ||
06196         (response_code == HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION) ||
06197         (response_code == HTTP_STATUS_MOVED_PERMANENTLY) ||
06198         (response_code == HTTP_STATUS_MULTIPLE_CHOICES) || (response_code == HTTP_STATUS_GONE)) {
06199       DebugTxn("http_trans", "[is_response_cacheable] YES by default ");
06200       return true;
06201     } else {
06202       DebugTxn("http_trans", "[is_response_cacheable] NO by default");
06203       return false;
06204     }
06205   }
06206   if (response_code == HTTP_STATUS_SEE_OTHER ||
06207       response_code == HTTP_STATUS_UNAUTHORIZED ||
06208       response_code == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED)
06209     return false;
06210   // let is_negative_caching_approriate decide what to do
06211   return true;
06212 /* Since we weren't caching response obtained with
06213    Authorization (the cache control stuff was commented out previously)
06214    I've moved this check to is_request_cache_lookupable().
06215    We should consider this matter further.  It is unclear
06216    how many sites actually add Cache-Control headers for Authorized content.
06217 
06218     // if client contains Authorization header, only cache if response
06219     // has proper Cache-Control flags, as in RFC2068, section 14.8.
06220     if (request->field_presence(MIME_PRESENCE_AUTHORIZATION)) {
06221 //         if (! (response->is_cache_control_set(HTTP_VALUE_MUST_REVALIDATE)) &&
06222 //             ! (response->is_cache_control_set(HTTP_VALUE_PROXY_REVALIDATE)) &&
06223 //             ! (response->is_cache_control_set(HTTP_VALUE_PUBLIC))) {
06224 
06225             DebugTxn("http_trans", "[is_response_cacheable] request has AUTHORIZATION - not cacheable");
06226             return(false);
06227 //         }
06228 //      else {
06229 //          DebugTxn("http_trans","[is_response_cacheable] request has AUTHORIZATION, "
06230 //                "but response has a cache-control that allows caching");
06231 //      }
06232     }
06233 */
06234 }
06235 
06236 bool
06237 HttpTransact::is_request_valid(State* s, HTTPHdr* incoming_request)
06238 {
06239   RequestError_t incoming_error;
06240   URL *url = NULL;
06241 
06242   if (incoming_request)
06243     url = incoming_request->url_get();
06244 
06245   incoming_error = check_request_validity(s, incoming_request);
06246   switch (incoming_error) {
06247   case NO_REQUEST_HEADER_ERROR:
06248     DebugTxn("http_trans", "[is_request_valid]" "no request header errors");
06249     break;
06250   case FAILED_PROXY_AUTHORIZATION:
06251     DebugTxn("http_trans", "[is_request_valid]" "failed proxy authorization");
06252     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
06253     build_error_response(s, HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required",
06254                          "access#proxy_auth_required", NULL);
06255     return false;
06256   case NON_EXISTANT_REQUEST_HEADER:
06257     /* fall through */
06258   case BAD_HTTP_HEADER_SYNTAX:
06259     {
06260       DebugTxn("http_trans", "[is_request_valid]" "non-existant/bad header");
06261       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
06262       build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid HTTP Request", "request#syntax_error", NULL);
06263       return false;
06264     }
06265 
06266   case MISSING_HOST_FIELD:
06267 
06268     ////////////////////////////////////////////////////////////////////
06269     // FIX: are we sure the following logic is right?  it seems that  //
06270     //      we shouldn't complain about the missing host header until //
06271     //      we know we really need one --- are we sure we need a host //
06272     //      header at this point?                                     //
06273     //                                                                //
06274     // FIX: also, let's clean up the transparency code to remove the  //
06275     //      SunOS conditionals --- we will be transparent on all      //
06276     //      platforms soon!  in fact, I really want a method that i   //
06277     //      can call for each transaction to say if the transaction   //
06278     //      is a forward proxy request, a transparent request, a      //
06279     //      reverse proxy request, etc --- the detail of how we       //
06280     //      determine the cases should be hidden behind the method.   //
06281     ////////////////////////////////////////////////////////////////////
06282 
06283     DebugTxn("http_trans", "[is_request_valid] missing host field");
06284     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
06285     if (s->http_config_param->reverse_proxy_enabled) {   // host header missing and reverse proxy on
06286       build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Host Header Required", "request#no_host", NULL);
06287     } else {
06288       // host header missing and reverse proxy off
06289       build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Host Required In Request", "request#no_host", NULL);
06290     }
06291 
06292     return false;
06293   case SCHEME_NOT_SUPPORTED:
06294   case NO_REQUEST_SCHEME:
06295     {
06296       DebugTxn("http_trans", "[is_request_valid] unsupported " "or missing request scheme");
06297       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
06298       build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Unsupported URL Scheme", "request#scheme_unsupported", NULL);
06299       return false;
06300     }
06301     /* fall through */
06302   case METHOD_NOT_SUPPORTED:
06303     DebugTxn("http_trans", "[is_request_valid]" "unsupported method");
06304     s->current.mode = TUNNELLING_PROXY;
06305     return true;
06306   case BAD_CONNECT_PORT:
06307     int port;
06308     port = url ? url->port_get() : 0;
06309     DebugTxn("http_trans", "[is_request_valid]" "%d is an invalid connect port", port);
06310     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
06311     build_error_response(s, HTTP_STATUS_FORBIDDEN, "Tunnel Forbidden", "access#connect_forbidden", NULL);
06312     return false;
06313   case NO_POST_CONTENT_LENGTH:
06314     {
06315       DebugTxn("http_trans", "[is_request_valid] post request without content length");
06316       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
06317       build_error_response(s, HTTP_STATUS_LENGTH_REQUIRED, "Content Length Required", "request#no_content_length", NULL);
06318       return false;
06319     }
06320   case UNACCEPTABLE_TE_REQUIRED:
06321     {
06322       DebugTxn("http_trans", "[is_request_valid] TE required is unacceptable.");
06323       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
06324       build_error_response(s, HTTP_STATUS_NOT_ACCEPTABLE, "Transcoding Not Available", "transcoding#unsupported", NULL);
06325       return false;
06326     }
06327   case INVALID_POST_CONTENT_LENGTH :
06328     {
06329       DebugTxn("http_trans", "[is_request_valid] post request with negative content length value");
06330       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
06331       build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid Content Length", "request#invalid_content_length", NULL);
06332       return false;
06333     }
06334   default:
06335     return true;
06336   }
06337 
06338   return true;
06339 }
06340 
06341 // bool HttpTransact::is_request_retryable
06342 //
06343 //   If we started a POST/PUT tunnel then we can
06344 //    not retry failed requests
06345 //
06346 bool
06347 HttpTransact::is_request_retryable(State* s)
06348 {
06349   if (s->hdr_info.request_body_start == true) {
06350     return false;
06351   }
06352 
06353   if (s->state_machine->plugin_tunnel_type != HTTP_NO_PLUGIN_TUNNEL) {
06354     // API can override
06355     if (s->state_machine->plugin_tunnel_type == HTTP_PLUGIN_AS_SERVER && s->api_info.retry_intercept_failures == true) {
06356       // This used to be an == comparison, which made no sense. Changed
06357       // to be an assignment, hoping the state is correct.
06358       s->state_machine->plugin_tunnel_type = HTTP_NO_PLUGIN_TUNNEL;
06359     } else {
06360       return false;
06361     }
06362   }
06363 
06364   return true;
06365 }
06366 
06367 bool
06368 HttpTransact::is_response_valid(State* s, HTTPHdr* incoming_response)
06369 {
06370   if (s->current.state != CONNECTION_ALIVE) {
06371     ink_assert((s->current.state == CONNECTION_ERROR) ||
06372                       (s->current.state == OPEN_RAW_ERROR) ||
06373                       (s->current.state == PARSE_ERROR) ||
06374                       (s->current.state == CONNECTION_CLOSED) ||
06375                       (s->current.state == INACTIVE_TIMEOUT) ||
06376                       (s->current.state == ACTIVE_TIMEOUT) ||
06377                       (s->current.state == CONGEST_CONTROL_CONGESTED_ON_M) ||
06378                       (s->current.state == CONGEST_CONTROL_CONGESTED_ON_F));
06379 
06380     s->hdr_info.response_error = CONNECTION_OPEN_FAILED;
06381     return false;
06382   }
06383 
06384   s->hdr_info.response_error = check_response_validity(s, incoming_response);
06385 
06386   switch (s->hdr_info.response_error) {
06387 #ifdef REALLY_NEED_TO_CHECK_DATE_VALIDITY
06388   case BOGUS_OR_NO_DATE_IN_RESPONSE:
06389     // We could modify the response to add the date, if need be.
06390 //          incoming_response->set_date(s->request_sent_time);
06391     return true;
06392 #endif
06393   case NO_RESPONSE_HEADER_ERROR:
06394     DebugTxn("http_trans", "[is_response_valid] No errors in response");
06395     return true;
06396 
06397   case MISSING_REASON_PHRASE:
06398     DebugTxn("http_trans", "[is_response_valid] Response Error: Missing reason phrase - allowing");
06399     return true;
06400 
06401   case STATUS_CODE_SERVER_ERROR:
06402     DebugTxn("http_trans", "[is_response_valid] Response Error: Origin Server returned 500 - allowing");
06403     return true;
06404 
06405   case CONNECTION_OPEN_FAILED:
06406     DebugTxn("http_trans", "[is_response_valid] Response Error: connection open failed");
06407     s->current.state = CONNECTION_ERROR;
06408     return false;
06409 
06410   case NON_EXISTANT_RESPONSE_HEADER:
06411     DebugTxn("http_trans", "[is_response_valid] Response Error: No response header");
06412     s->current.state = BAD_INCOMING_RESPONSE;
06413     return false;
06414 
06415   case NOT_A_RESPONSE_HEADER:
06416     DebugTxn("http_trans", "[is_response_valid] Response Error: Not a response header");
06417     s->current.state = BAD_INCOMING_RESPONSE;
06418     return false;
06419 
06420   case MISSING_STATUS_CODE:
06421     DebugTxn("http_trans", "[is_response_valid] Response Error: Missing status code");
06422     s->current.state = BAD_INCOMING_RESPONSE;
06423     return false;
06424 
06425   default:
06426     DebugTxn("http_trans", "[is_response_valid] Errors in response");
06427     s->current.state = BAD_INCOMING_RESPONSE;
06428     return false;
06429   }
06430 }
06431 
06432 ///////////////////////////////////////////////////////////////////////////////
06433 // Name       : service_transaction_in_proxy_only_mode
06434 // Description: uses some metric to force this transaction to be proxy-only
06435 //
06436 // Details    :
06437 //
06438 // Some metric may be employed to force the traffic server to enter
06439 // a proxy-only mode temporarily. This function is called to determine
06440 // if the current transaction should be proxy-only. The function is
06441 // called from initialize_state_variables_from_request and is used to
06442 // set s->current.mode to TUNNELLING_PROXY and just for safety to set
06443 // s->cache_info.action to CACHE_DO_NO_ACTION.
06444 //
06445 // Currently the function is just a placeholder and always returns false.
06446 //
06447 ///////////////////////////////////////////////////////////////////////////////
06448 bool
06449 HttpTransact::service_transaction_in_proxy_only_mode(State* /* s ATS_UNUSED */)
06450 {
06451   return false;
06452 }
06453 
06454 void
06455 HttpTransact::process_quick_http_filter(State* s, int method)
06456 {
06457   // connection already disabled by previous ACL filtering, don't modify it.
06458   if (!s->client_connection_enabled) {
06459     return;
06460   }
06461 
06462   if (s->state_machine->ua_session) {
06463     const AclRecord *acl_record = s->state_machine->ua_session->acl_record;
06464     bool deny_request = (acl_record == NULL);
06465     if (acl_record && (acl_record->_method_mask != AclRecord::ALL_METHOD_MASK)) {
06466       if (method != -1) {
06467         deny_request = !acl_record->isMethodAllowed(method);
06468       } else {
06469         int method_str_len;
06470         const char *method_str = s->hdr_info.client_request.method_get(&method_str_len);
06471         deny_request = !acl_record->isNonstandardMethodAllowed(std::string(method_str, method_str_len));
06472       }
06473     }
06474     if (deny_request) {
06475       if (is_debug_tag_set("ip-allow")) {
06476         ip_text_buffer ipb;
06477         Debug("ip-allow", "Quick filter denial on %s:%s with mask %x", ats_ip_ntop(&s->client_info.addr.sa, ipb, sizeof(ipb)), hdrtoken_index_to_wks(method), acl_record ? acl_record->_method_mask : 0x0);
06478       }
06479       s->client_connection_enabled = false;
06480     }
06481   }
06482 }
06483 
06484 
06485 HttpTransact::HostNameExpansionError_t HttpTransact::try_to_expand_host_name(State* s)
06486 {
06487   static int max_dns_lookups = 2 + s->http_config_param->num_url_expansions;
06488   static int last_expansion = max_dns_lookups - 2;
06489 
06490   HTTP_RELEASE_ASSERT(!s->dns_info.lookup_success);
06491 
06492   if (s->dns_info.looking_up == ORIGIN_SERVER) {
06493     ///////////////////////////////////////////////////
06494     // if resolving dns of the origin server failed, //
06495     // we try to expand hostname.                    //
06496     ///////////////////////////////////////////////////
06497     if (s->http_config_param->enable_url_expandomatic) {
06498       int attempts = s->dns_info.attempts;
06499       ink_assert(attempts >= 1 && attempts <= max_dns_lookups);
06500 
06501       if (attempts < max_dns_lookups) {
06502         // Try a URL expansion
06503         if (attempts <= last_expansion) {
06504           char *expansion = s->http_config_param->url_expansions[attempts - 1];
06505           int length = strlen(s->server_info.name) + strlen(expansion) + 1;
06506 
06507           s->dns_info.lookup_name = s->arena.str_alloc(length);
06508           ink_string_concatenate_strings_n(s->dns_info.lookup_name, length + 1, s->server_info.name, ".", expansion, NULL);
06509         } else {
06510           if (ParseRules::strchr(s->server_info.name, '.')) {
06511             // don't expand if contains '.'
06512             return (EXPANSION_FAILED);
06513           }
06514           // Try www.<server_name>.com
06515           int length = strlen(s->server_info.name) + 8;
06516 
06517           s->dns_info.lookup_name = s->arena.str_alloc(length);
06518           ink_string_concatenate_strings_n(s->dns_info.lookup_name, length + 1, "www.", s->server_info.name, ".com", NULL);
06519         }
06520         return (RETRY_EXPANDED_NAME);
06521       } else {
06522         return (DNS_ATTEMPTS_EXHAUSTED);
06523       }
06524     } else {
06525       return EXPANSION_NOT_ALLOWED;
06526     }
06527   } else {
06528     //////////////////////////////////////////////////////
06529     // we looked up dns of parent proxy, but it failed, //
06530     // try lookup of origin server name.                //
06531     //////////////////////////////////////////////////////
06532     ink_assert(s->dns_info.looking_up == PARENT_PROXY);
06533 
06534     s->dns_info.lookup_name = s->server_info.name;
06535     s->dns_info.looking_up = ORIGIN_SERVER;
06536     s->dns_info.attempts = 0;
06537 
06538     return RETRY_EXPANDED_NAME;
06539   }
06540 }
06541 
06542 bool
06543 HttpTransact::will_this_request_self_loop(State* s)
06544 {
06545   ////////////////////////////////////////
06546   // check if we are about to self loop //
06547   ////////////////////////////////////////
06548   if (s->dns_info.lookup_success) {
06549     if (ats_ip_addr_eq(s->host_db_info.ip(), &Machine::instance()->ip.sa)) {
06550       uint16_t host_port = s->hdr_info.client_request.url_get()->port_get();
06551       uint16_t local_port = ats_ip_port_host_order(&s->client_info.addr);
06552       if (host_port == local_port) {
06553         switch (s->dns_info.looking_up) {
06554         case ORIGIN_SERVER:
06555           DebugTxn("http_transact", "[will_this_request_self_loop] host ip and port same as local ip and port - bailing");
06556           break;
06557         case PARENT_PROXY:
06558           DebugTxn("http_transact", "[will_this_request_self_loop] "
06559                 "parent proxy ip and port same as local ip and port - bailing");
06560           break;
06561         default:
06562           DebugTxn("http_transact", "[will_this_request_self_loop] "
06563                 "unknown's ip and port same as local ip and port - bailing");
06564           break;
06565         }
06566         build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Cycle Detected", "request#cycle_detected", NULL);
06567         return true;
06568       }
06569     }
06570 
06571     // Now check for a loop using the Via string.
06572     // Since we insert our ip_address (in hex) into outgoing Via strings,
06573     // look for our_ip address in the request's via string.
06574     if (ats_is_ip(&Machine::instance()->ip)) {
06575       MIMEField *via_field = s->hdr_info.client_request.field_find(MIME_FIELD_VIA, MIME_LEN_VIA);
06576 
06577       while (via_field) {
06578         // No need to waste cycles comma separating the via values since we want to do a match anywhere in the
06579         // in the string.  We can just loop over the dup hdr fields
06580         int via_len;
06581         const char *via_string = via_field->value_get(&via_len);
06582 
06583         if (via_string && ptr_len_str(via_string, via_len, Machine::instance()->ip_hex_string)) {
06584           DebugTxn("http_transact", "[will_this_request_self_loop] Incoming via: %.*s has (%s[%s] (%s))", via_len, via_string,
06585                 s->http_config_param->proxy_hostname, Machine::instance()->ip_hex_string, s->http_config_param->proxy_request_via_string);
06586           build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Multi-Hop Cycle Detected", "request#cycle_detected", NULL);
06587           return true;
06588         }
06589 
06590         via_field = via_field->m_next_dup;
06591       }
06592     }
06593   }
06594   s->request_will_not_selfloop = true;
06595   return false;
06596 }
06597 
06598 /*
06599  * handle_content_length_header(...)
06600  *  Function handles the insertion of content length headers into
06601  * header. header CAN equal base.
06602  */
06603 void
06604 HttpTransact::handle_content_length_header(State* s, HTTPHdr* header, HTTPHdr* base)
06605 {
06606   int64_t cl = HTTP_UNDEFINED_CL;
06607   ink_assert(header->type_get() == HTTP_TYPE_RESPONSE);
06608   if (base->presence(MIME_PRESENCE_CONTENT_LENGTH)) {
06609     cl = base->get_content_length();
06610     if (cl >= 0) {
06611       // header->set_content_length(cl);
06612       ink_assert(header->get_content_length() == cl);
06613 
06614       switch (s->source) {
06615       case SOURCE_HTTP_ORIGIN_SERVER:
06616         // We made our decision about whether to trust the
06617         //   response content length in init_state_vars_from_response()
06618         if (s->range_setup != HttpTransact::RANGE_NOT_TRANSFORM_REQUESTED)
06619           break;
06620 
06621       case SOURCE_CACHE:
06622         // if we are doing a single Range: request, calculate the new
06623         // C-L: header
06624         if (s->range_setup == HttpTransact::RANGE_NOT_TRANSFORM_REQUESTED) {
06625           change_response_header_because_of_range_request(s,header);
06626           s->hdr_info.trust_response_cl = true;
06627         }
06628         ////////////////////////////////////////////////
06629         //  Make sure that the cache's object size    //
06630         //   agrees with the Content-Length           //
06631         //   Otherwise, set the state's machine view  //
06632         //   of c-l to undefined to turn off K-A      //
06633         ////////////////////////////////////////////////
06634         else if ((int64_t) s->cache_info.object_read->object_size_get() == cl) {
06635           s->hdr_info.trust_response_cl = true;
06636         } else {
06637           DebugTxn("http_trans", "Content Length header and cache object size mismatch." "Disabling keep-alive");
06638           s->hdr_info.trust_response_cl = false;
06639         }
06640         break;
06641 
06642       case SOURCE_TRANSFORM:
06643         if (s->range_setup == HttpTransact::RANGE_REQUESTED) {
06644           header->set_content_length(s->range_output_cl);
06645           s->hdr_info.trust_response_cl = true;
06646         } else if (s->hdr_info.transform_response_cl == HTTP_UNDEFINED_CL) {
06647           s->hdr_info.trust_response_cl = false;
06648         } else {
06649           s->hdr_info.trust_response_cl = true;
06650         }
06651         break;
06652 
06653       default:
06654         ink_release_assert(0);
06655         break;
06656       }
06657     } else {
06658       header->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
06659       s->hdr_info.trust_response_cl = false;
06660     }
06661     Debug("http_trans", "[handle_content_length_header] RESPONSE cont len in hdr is %" PRId64, header->get_content_length());
06662   } else {
06663     // No content length header
06664     if (s->source == SOURCE_CACHE) {
06665       // If there is no content-length header, we can
06666       //   insert one since the cache knows definately
06667       //   how long the object is unless we're in a
06668       //   read-while-write mode and object hasn't been
06669       //   written into a cache completely.
06670       cl = s->cache_info.object_read->object_size_get();
06671       if (cl == INT64_MAX) { //INT64_MAX cl in cache indicates rww in progress
06672         header->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
06673         s->hdr_info.trust_response_cl = false;
06674         s->hdr_info.request_content_length = HTTP_UNDEFINED_CL;
06675         ink_assert(s->range_setup == RANGE_NONE);
06676       }
06677       else if (s->range_setup == RANGE_NOT_TRANSFORM_REQUESTED) {
06678         // if we are doing a single Range: request, calculate the new
06679         // C-L: header
06680         change_response_header_because_of_range_request(s,header);
06681         s->hdr_info.trust_response_cl = true;
06682       }
06683       else {
06684         header->set_content_length(cl);
06685         s->hdr_info.trust_response_cl = true;
06686       }
06687     } else {
06688       // Check to see if there is no content length
06689       //  header because the response precludes a
06690       // body
06691       if (is_response_body_precluded(header->status_get(), s->method)) {
06692         // We want to be able to do keep-alive here since
06693         //   there can't be body so we don't have any
06694         //   issues about trusting the body length
06695         s->hdr_info.trust_response_cl = true;
06696       } else {
06697         s->hdr_info.trust_response_cl = false;
06698       }
06699       header->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
06700       ink_assert(s->range_setup != RANGE_NOT_TRANSFORM_REQUESTED);
06701     }
06702   }
06703   return;
06704 }                               /* End HttpTransact::handle_content_length_header */
06705 
06706 
06707 //////////////////////////////////////////////////////////////////////////////
06708 //
06709 //      void HttpTransact::handle_request_keep_alive_headers(
06710 //          State* s, bool ka_on, HTTPVersion ver, HTTPHdr *heads)
06711 //
06712 //      Removes keep alive headers from user-agent from <heads>
06713 //
06714 //      Adds the appropriate keep alive headers (if any) to <heads>
06715 //      for keep-alive state <ka_on>, and HTTP version <ver>.
06716 //
06717 //////////////////////////////////////////////////////////////////////////////
06718 void
06719 HttpTransact::handle_request_keep_alive_headers(State* s, HTTPVersion ver, HTTPHdr* heads)
06720 {
06721   enum KA_Action_t
06722   {
06723     KA_UNKNOWN,
06724     KA_DISABLED,
06725     KA_CLOSE,
06726     KA_CONNECTION
06727   };
06728 
06729   KA_Action_t ka_action = KA_UNKNOWN;
06730   bool upstream_ka = (s->current.server->keep_alive == HTTP_KEEPALIVE);
06731 
06732   ink_assert(heads->type_get() == HTTP_TYPE_REQUEST);
06733 
06734   // Check preconditions for Keep-Alive
06735   if (!upstream_ka) {
06736     ka_action = KA_DISABLED;
06737   } else if (HTTP_MAJOR(ver.m_version) == 0) {  /* No K-A for 0.9 apps */
06738     ka_action = KA_DISABLED;
06739   }
06740   // If preconditions are met, figure out what action to take
06741   if (ka_action == KA_UNKNOWN) {
06742     int method = heads->method_get_wksidx();
06743     if (method == HTTP_WKSIDX_GET ||
06744         method == HTTP_WKSIDX_HEAD ||
06745         method == HTTP_WKSIDX_OPTIONS ||
06746         method == HTTP_WKSIDX_PURGE || method == HTTP_WKSIDX_DELETE || method == HTTP_WKSIDX_TRACE) {
06747       // These methods do not need a content-length header
06748       ka_action = KA_CONNECTION;
06749     } else {
06750       // All remaining methods require a content length header
06751       if (heads->get_content_length() == -1) {
06752         ka_action = KA_CLOSE;
06753       } else {
06754         ka_action = KA_CONNECTION;
06755       }
06756     }
06757   }
06758 
06759   ink_assert(ka_action != KA_UNKNOWN);
06760 
06761   // Since connection headers are hop-to-hop, strip the
06762   //  the ones we received from the user-agent
06763   heads->field_delete(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
06764   heads->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
06765 
06766   if (!s->is_upgrade_request) {
06767     // Insert K-A headers as necessary
06768     switch (ka_action) {
06769     case KA_CONNECTION:
06770       ink_assert(s->current.server->keep_alive != HTTP_NO_KEEPALIVE);
06771       if (ver == HTTPVersion(1, 0)) {
06772         if (s->current.request_to == PARENT_PROXY ||
06773             s->current.request_to == ICP_SUGGESTED_HOST) {
06774           heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "keep-alive", 10);
06775         } else {
06776           heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "keep-alive", 10);
06777         }
06778       }
06779       // NOTE: if the version is 1.1 we don't need to do
06780       //  anything since keep-alive is assumed
06781       break;
06782     case KA_DISABLED:
06783     case KA_CLOSE:
06784       if (s->current.server->keep_alive != HTTP_NO_KEEPALIVE || (ver == HTTPVersion(1, 1))) {
06785         /* Had keep-alive */
06786         s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
06787         if (s->current.request_to == PARENT_PROXY ||
06788             s->current.request_to == ICP_SUGGESTED_HOST) {
06789           heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "close", 5);
06790         } else {
06791           heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "close", 5);
06792         }
06793       }
06794       // Note: if we are 1.1, we always need to send the close
06795       //  header since persistant connnections are the default
06796       break;
06797     case KA_UNKNOWN:
06798     default:
06799       ink_assert(0);
06800       break;
06801     }
06802   } else { /* websocket connection */
06803     s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
06804     s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
06805     heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
06806 
06807     if (s->is_websocket) {
06808       heads->value_set(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE, "websocket", 9);
06809     }
06810   }
06811 }                               /* End HttpTransact::handle_request_keep_alive_headers */
06812 
06813 //////////////////////////////////////////////////////////////////////////////
06814 //
06815 //      void HttpTransact::handle_response_keep_alive_headers(
06816 //          State* s, bool ka_on, HTTPVersion ver, HTTPHdr *heads)
06817 //
06818 //      Removes keep alive headers from origin server from <heads>
06819 //
06820 //      Adds the appropriate Transfer-Encoding: chunked header.
06821 //
06822 //      Adds the appropriate keep alive headers (if any) to <heads>
06823 //      for keep-alive state <ka_on>, and HTTP version <ver>.
06824 //
06825 //////////////////////////////////////////////////////////////////////////////
06826 void
06827 HttpTransact::handle_response_keep_alive_headers(State* s, HTTPVersion ver, HTTPHdr* heads)
06828 {
06829   enum KA_Action_t
06830   { KA_UNKNOWN, KA_DISABLED, KA_CLOSE, KA_CONNECTION };
06831   KA_Action_t ka_action = KA_UNKNOWN;
06832 
06833   ink_assert(heads->type_get() == HTTP_TYPE_RESPONSE);
06834 
06835   // Since connection headers are hop-to-hop, strip the
06836   //  the ones we received from upstream
06837   heads->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
06838   heads->field_delete(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
06839 
06840   // Handle the upgrade cases
06841   if (s->is_upgrade_request  &&
06842       heads->status_get() == HTTP_STATUS_SWITCHING_PROTOCOL &&
06843       s->source == SOURCE_HTTP_ORIGIN_SERVER) {
06844     s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
06845     if (s->is_websocket) {
06846       DebugTxn("http_trans", "transaction successfully upgraded to websockets.");
06847       //s->transparent_passthrough = true;
06848       heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
06849       heads->value_set(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE, "websocket", 9);
06850     }
06851 
06852     // We set this state so that we can jump to our blind forwarding state once
06853     // the response is sent to the client.
06854     s->did_upgrade_succeed = true;
06855     return;
06856   }
06857 
06858   int c_hdr_field_len;
06859   const char *c_hdr_field_str;
06860   if (s->client_info.proxy_connect_hdr) {
06861     c_hdr_field_str = MIME_FIELD_PROXY_CONNECTION;
06862     c_hdr_field_len = MIME_LEN_PROXY_CONNECTION;
06863   } else {
06864     c_hdr_field_str = MIME_FIELD_CONNECTION;
06865     c_hdr_field_len = MIME_LEN_CONNECTION;
06866   }
06867 
06868   // Check pre-conditions for keep-alive
06869   if (HTTP_MAJOR(ver.m_version) == 0) {  /* No K-A for 0.9 apps */
06870     ka_action = KA_DISABLED;
06871   }
06872   else if (heads->status_get() == HTTP_STATUS_NO_CONTENT &&
06873       ((s->source == SOURCE_HTTP_ORIGIN_SERVER && s->current.server->transfer_encoding != NO_TRANSFER_ENCODING)
06874        || heads->get_content_length() != 0)) {
06875     // some systems hang until the connection closes when receiving a 204 regardless of the K-A headers
06876     // close if there is any body response from the origin
06877     ka_action = KA_CLOSE;
06878   } else {
06879     // Determine if we are going to send either a server-generated or
06880     // proxy-generated chunked response to the client. If we cannot
06881     // trust the content-length, we may be able to chunk the response
06882     // to the client to keep the connection alive.
06883     // Insert a Transfer-Encoding header in the response if necessary.
06884 
06885     // check that the client is HTTP 1.1 and the conf allows chunking
06886     if (s->client_info.http_version == HTTPVersion(1, 1) && s->txn_conf->chunking_enabled == 1 &&
06887         // if we're not sending a body, don't set a chunked header regardless of server response
06888         !is_response_body_precluded(s->hdr_info.client_response.status_get(), s->method) &&
06889         // we do not need chunked encoding for internal error messages
06890         // that are sent to the client if the server response is not valid.
06891         (((s->source == SOURCE_HTTP_ORIGIN_SERVER || s->source == SOURCE_TRANSFORM) &&
06892           s->hdr_info.server_response.valid() &&
06893           // if we receive a 304, we will serve the client from the
06894           // cache and thus do not need chunked encoding.
06895           s->hdr_info.server_response.status_get() != HTTP_STATUS_NOT_MODIFIED &&
06896           (s->current.server->transfer_encoding == HttpTransact::CHUNKED_ENCODING ||
06897            // we can use chunked encoding if we cannot trust the content
06898            // length (e.g. no Content-Length and Connection:close in HTTP/1.1 responses)
06899            s->hdr_info.trust_response_cl == false)) ||
06900          // handle serve from cache (read-while-write) case
06901          (s->source == SOURCE_CACHE && s->hdr_info.trust_response_cl == false) ||
06902          //any transform will potentially alter the content length. try chunking if possible
06903          (s->source == SOURCE_TRANSFORM && s->hdr_info.trust_response_cl == false ))) {
06904       s->client_info.receive_chunked_response = true;
06905       heads->value_append(MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING, HTTP_VALUE_CHUNKED, HTTP_LEN_CHUNKED, true);
06906     } else {
06907       s->client_info.receive_chunked_response = false;
06908     }
06909 
06910     //make sure no content length header is send when transfer encoding is chunked
06911     if ( s->client_info.receive_chunked_response ) {
06912       s->hdr_info.trust_response_cl = false;
06913 
06914       // And delete the header if it's already been added...
06915       heads->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
06916     }
06917 
06918     // Close the connection if client_info is not keep-alive.
06919     // Otherwise, if we cannot trust the content length, we will close the connection
06920     // unless we are going to use chunked encoding or the client issued
06921     // a PUSH request
06922     if (s->client_info.keep_alive != HTTP_KEEPALIVE) {
06923        ka_action = KA_DISABLED;
06924     } else if (s->hdr_info.trust_response_cl == false &&
06925         !(s->client_info.receive_chunked_response == true ||
06926           (s->method == HTTP_WKSIDX_PUSH && s->client_info.keep_alive == HTTP_KEEPALIVE))) {
06927       ka_action = KA_CLOSE;
06928     } else {
06929       ka_action = KA_CONNECTION;
06930     }
06931   }
06932 
06933   // Insert K-A headers as necessary
06934   switch (ka_action) {
06935   case KA_CONNECTION:
06936     ink_assert(s->client_info.keep_alive != HTTP_NO_KEEPALIVE);
06937     // This is a hack, we send the keep-alive header for both 1.0
06938     // and 1.1, to be "compatible" with Akamai.
06939     // if (ver == HTTPVersion (1, 0)) {
06940     heads->value_set(c_hdr_field_str, c_hdr_field_len, "keep-alive", 10);
06941     // NOTE: if the version is 1.1 we don't need to do
06942     //  anything since keep-alive is assumed
06943     break;
06944   case KA_CLOSE:
06945   case KA_DISABLED:
06946     if (s->client_info.keep_alive != HTTP_NO_KEEPALIVE || (ver == HTTPVersion(1, 1))) {
06947       heads->value_set(c_hdr_field_str, c_hdr_field_len, "close", 5);
06948       s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
06949     }
06950     // Note: if we are 1.1, we always need to send the close
06951     //  header since persistant connnections are the default
06952     break;
06953   case KA_UNKNOWN:
06954   default:
06955     ink_assert(0);
06956     break;
06957   }
06958 }                               /* End HttpTransact::handle_response_keep_alive_headers */
06959 
06960 
06961 bool
06962 HttpTransact::delete_all_document_alternates_and_return(State* s, bool cache_hit)
06963 {
06964   if (cache_hit == true) {
06965     if (s->cache_info.is_ram_cache_hit) {
06966       SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_RAM_CACHE_FRESH);
06967     } else {
06968       SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_FRESH);
06969     }
06970   } else {
06971     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_NOT_CACHED);
06972   }
06973 
06974   if ((s->method != HTTP_WKSIDX_GET) && (s->method == HTTP_WKSIDX_DELETE || s->method == HTTP_WKSIDX_PURGE)) {
06975     bool valid_max_forwards;
06976     int max_forwards = -1;
06977     MIMEField *max_forwards_f = s->hdr_info.client_request.field_find(MIME_FIELD_MAX_FORWARDS, MIME_LEN_MAX_FORWARDS);
06978 
06979     // Check the max forwards value for DELETE
06980     if (max_forwards_f) {
06981       valid_max_forwards = true;
06982       max_forwards = max_forwards_f->value_get_int();
06983     } else {
06984       valid_max_forwards = false;
06985     }
06986 
06987     if (s->method == HTTP_WKSIDX_PURGE || (valid_max_forwards && max_forwards <= 0)) {
06988       DebugTxn("http_trans", "[delete_all_document_alternates_and_return] " "DELETE with Max-Forwards: %d", max_forwards);
06989 
06990       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
06991 
06992       // allow deletes to be pipelined
06993       //   We want to allow keep-alive so trust the response content
06994       //    length.  There really isn't one and the SM will add the
06995       //    zero content length when setting up the transfer
06996       s->hdr_info.trust_response_cl = true;
06997       build_response(s, &s->hdr_info.client_response, s->client_info.http_version,
06998                      (cache_hit == true) ? HTTP_STATUS_OK : HTTP_STATUS_NOT_FOUND);
06999 
07000       return true;
07001     } else {
07002       if (valid_max_forwards) {
07003         --max_forwards;
07004         DebugTxn("http_trans", "[delete_all_document_alternates_and_return] " "Decrementing max_forwards to %d", max_forwards);
07005         s->hdr_info.client_request.value_set_int(MIME_FIELD_MAX_FORWARDS, MIME_LEN_MAX_FORWARDS, max_forwards);
07006       }
07007     }
07008   }
07009 
07010   return false;
07011 }
07012 
07013 bool
07014 HttpTransact::does_client_request_permit_cached_response(const OverridableHttpConfigParams *p, CacheControlResult *c,
07015                                                          HTTPHdr *h, char *via_string)
07016 {
07017   ////////////////////////////////////////////////////////////////////////
07018   // If aren't ignoring client's cache directives, meet client's wishes //
07019   ////////////////////////////////////////////////////////////////////////
07020 
07021   if (!c->ignore_client_no_cache) {
07022     if (h->is_cache_control_set(HTTP_VALUE_NO_CACHE))
07023       return (false);
07024     if (h->is_pragma_no_cache_set()) {
07025       // if we are going to send out an ims anyway,
07026       // no need to flag this as a no-cache.
07027       if (!p->cache_ims_on_client_no_cache) {
07028         via_string[VIA_CLIENT_REQUEST] = VIA_CLIENT_NO_CACHE;
07029       }
07030       return (false);
07031     }
07032   }
07033 
07034   return (true);
07035 }
07036 
07037 bool
07038 HttpTransact::does_client_request_permit_dns_caching(CacheControlResult* c, HTTPHdr* h)
07039 {
07040   if (h->is_pragma_no_cache_set() && h->is_cache_control_set(HTTP_VALUE_NO_CACHE) && (!c->ignore_client_no_cache)) {
07041     return (false);
07042   }
07043   return (true);
07044 }
07045 
07046 bool
07047 HttpTransact::does_client_request_permit_storing(CacheControlResult* c, HTTPHdr* h)
07048 {
07049   ////////////////////////////////////////////////////////////////////////
07050   // If aren't ignoring client's cache directives, meet client's wishes //
07051   ////////////////////////////////////////////////////////////////////////
07052   if (!c->ignore_client_no_cache) {
07053     if (h->is_cache_control_set(HTTP_VALUE_NO_STORE))
07054       return (false);
07055   }
07056 
07057   return (true);
07058 }
07059 
07060 
07061 
07062 int
07063 HttpTransact::calculate_document_freshness_limit(State *s, HTTPHdr *response, time_t response_date, bool *heuristic)
07064 {
07065   bool expires_set, date_set, last_modified_set;
07066   time_t date_value, expires_value, last_modified_value;
07067   MgmtInt min_freshness_bounds, max_freshness_bounds;
07068   int freshness_limit = 0;
07069   uint32_t cc_mask = response->get_cooked_cc_mask();
07070 
07071   *heuristic = false;
07072 
07073   if (cc_mask & (MIME_COOKED_MASK_CC_S_MAXAGE | MIME_COOKED_MASK_CC_MAX_AGE)) {
07074     if (cc_mask & MIME_COOKED_MASK_CC_S_MAXAGE) {
07075       freshness_limit = (int) response->get_cooked_cc_s_maxage();
07076       DebugTxn("http_match", "calculate_document_freshness_limit --- s_max_age set, freshness_limit = %d", freshness_limit);
07077     } else if (cc_mask & MIME_COOKED_MASK_CC_MAX_AGE) {
07078       freshness_limit = (int) response->get_cooked_cc_max_age();
07079       DebugTxn("http_match", "calculate_document_freshness_limit --- max_age set, freshness_limit = %d", freshness_limit);
07080     }
07081     freshness_limit = min(max(0, freshness_limit), NUM_SECONDS_IN_ONE_YEAR);
07082   } else {
07083     date_set = last_modified_set = false;
07084 
07085     if (s->plugin_set_expire_time != UNDEFINED_TIME) {
07086       expires_set = true;
07087       expires_value = s->plugin_set_expire_time;
07088     } else {
07089       expires_set = (response->presence(MIME_PRESENCE_EXPIRES) != 0);
07090       expires_value = response->get_expires();
07091     }
07092 
07093     date_value = response_date;
07094     if (date_value > 0) {
07095       date_set = true;
07096     } else {
07097       date_value = s->request_sent_time;
07098       DebugTxn("http_match",
07099                "calculate_document_freshness_limit --- Expires header = %" PRId64 " no date, using sent time %" PRId64,
07100                (int64_t)expires_value, (int64_t)date_value);
07101     }
07102     ink_assert(date_value > 0);
07103 
07104     // Getting the cache_sm object
07105     HttpCacheSM & cache_sm = s->state_machine->get_cache_sm();
07106 
07107     //Bypassing if loop to set freshness_limit to heuristic value
07108     if (expires_set && !cache_sm.is_readwhilewrite_inprogress()) {
07109       if (expires_value == UNDEFINED_TIME || expires_value <= date_value) {
07110         expires_value = date_value;
07111         DebugTxn("http_match", "calculate_document_freshness_limit --- no expires, using date %" PRId64,
07112                  (int64_t)expires_value);
07113       }
07114       freshness_limit = (int) (expires_value - date_value);
07115 
07116       DebugTxn("http_match",
07117                "calculate_document_freshness_limit --- Expires: %" PRId64 ", Date: %" PRId64 ", freshness_limit = %d",
07118                (int64_t)expires_value, (int64_t)date_value, freshness_limit);
07119 
07120       freshness_limit = min(max(0, freshness_limit), NUM_SECONDS_IN_ONE_YEAR);
07121     } else {
07122       last_modified_value = 0;
07123       if (response->presence(MIME_PRESENCE_LAST_MODIFIED)) {
07124         last_modified_set = true;
07125         last_modified_value = response->get_last_modified();
07126         DebugTxn("http_match", "calculate_document_freshness_limit --- Last Modified header = %" PRId64,
07127                  (int64_t)last_modified_value);
07128 
07129         if (last_modified_value == UNDEFINED_TIME) {
07130           last_modified_set = false;
07131         } else if (last_modified_value > date_value) {
07132           last_modified_value = date_value;
07133           DebugTxn("http_match", "calculate_document_freshness_limit --- no last-modified, using sent time %" PRId64,
07134                    (int64_t)last_modified_value);
07135         }
07136       }
07137 
07138       *heuristic = true;
07139       if (date_set && last_modified_set) {
07140         MgmtFloat f = s->txn_conf->cache_heuristic_lm_factor;
07141         ink_assert((f >= 0.0) && (f <= 1.0));
07142         ink_time_t time_since_last_modify = date_value - last_modified_value;
07143         int h_freshness = (int) (time_since_last_modify * f);
07144         freshness_limit = max(h_freshness, 0);
07145         DebugTxn("http_match", "calculate_document_freshness_limit --- heuristic: date=%" PRId64 ", lm=%" PRId64
07146                  ", time_since_last_modify=%"  PRId64 ", f=%g, freshness_limit = %d",
07147                  (int64_t)date_value, (int64_t)last_modified_value, (int64_t)time_since_last_modify,
07148               f, freshness_limit);
07149       } else {
07150         freshness_limit = s->txn_conf->cache_heuristic_min_lifetime;
07151         DebugTxn("http_match", "calculate_document_freshness_limit --- heuristic: freshness_limit = %d", freshness_limit);
07152       }
07153     }
07154   }
07155 
07156   // The freshness limit must always fall within the min and max guaranteed bounds.
07157   min_freshness_bounds = max((MgmtInt)0, s->txn_conf->cache_guaranteed_min_lifetime);
07158   max_freshness_bounds = min((MgmtInt)NUM_SECONDS_IN_ONE_YEAR, s->txn_conf->cache_guaranteed_max_lifetime);
07159 
07160   // Heuristic freshness can be more strict.
07161   if (*heuristic) {
07162     min_freshness_bounds = max(min_freshness_bounds, s->txn_conf->cache_heuristic_min_lifetime);
07163     max_freshness_bounds = min(max_freshness_bounds, s->txn_conf->cache_heuristic_max_lifetime);
07164   }
07165   // Now clip the freshness limit.
07166   if (freshness_limit > max_freshness_bounds)
07167     freshness_limit = max_freshness_bounds;
07168   if (freshness_limit < min_freshness_bounds)
07169     freshness_limit = min_freshness_bounds;
07170 
07171   DebugTxn("http_match", "calculate_document_freshness_limit --- final freshness_limit = %d", freshness_limit);
07172 
07173   return (freshness_limit);
07174 }
07175 
07176 
07177 
07178 ////////////////////////////////////////////////////////////////////////////////////
07179 //  int HttpTransact::calculate_freshness_fuzz()
07180 //
07181 //    This function trys to revents many, many simulatenous revalidations in
07182 //     reverse proxy situations.  Statistically introduce a fuzz factor that
07183 //     brings revalidation forward for a small percentage of the requests/
07184 //     The hope is that is that the document early by a selected few, and
07185 //     the headers are updated in the cache before the regualr freshness
07186 //     limit is actually reached
07187 ////////////////////////////////////////////////////////////////////////////////////
07188 
07189 int
07190 HttpTransact::calculate_freshness_fuzz(State* s, int fresh_limit)
07191 {
07192   static double LOG_YEAR = log10((double)NUM_SECONDS_IN_ONE_YEAR);
07193   const uint32_t granularity = 1000;
07194   int result = 0;
07195 
07196   uint32_t random_num = this_ethread()->generator.random();
07197   uint32_t index = random_num % granularity;
07198   uint32_t range = (uint32_t) (granularity * s->txn_conf->freshness_fuzz_prob);
07199 
07200   if (index < range) {
07201     if (s->txn_conf->freshness_fuzz_min_time > 0) {
07202       // Complicated calculations to try to find a reasonable fuzz time between fuzz_min_time and fuzz_time
07203       int fresh_small = (int)rint((double)s->txn_conf->freshness_fuzz_min_time *
07204                                   pow(2, min((double)fresh_limit / (double)s->txn_conf->freshness_fuzz_time,
07205                                              sqrt((double)s->txn_conf->freshness_fuzz_time))));
07206       int fresh_large = max((int)s->txn_conf->freshness_fuzz_min_time,
07207                             (int)rint(s->txn_conf->freshness_fuzz_time *
07208                                       log10((double)(fresh_limit - s->txn_conf->freshness_fuzz_min_time) / LOG_YEAR)));
07209       result = min(fresh_small, fresh_large);
07210       DebugTxn("http_match", "calculate_freshness_fuzz using min/max --- freshness fuzz = %d", result);
07211     } else {
07212       result = s->txn_conf->freshness_fuzz_time;
07213       DebugTxn("http_match", "calculate_freshness_fuzz --- freshness fuzz = %d", result);
07214     }
07215   }
07216 
07217   return result;
07218 }
07219 
07220 //////////////////////////////////////////////////////////////////////////////
07221 //
07222 //
07223 //      This function takes the request and response headers for a cached
07224 //      object, and the current HTTP parameters, and decides if the object
07225 //      is still "fresh enough" to serve.  One of the following values
07226 //      is returned:
07227 //
07228 //          FRESHNESS_FRESH             Fresh enough, serve it
07229 //          FRESHNESS_WARNING           Stale but client says it's okay
07230 //          FRESHNESS_STALE             Too stale, don't use
07231 //
07232 //////////////////////////////////////////////////////////////////////////////
07233 HttpTransact::Freshness_t
07234 HttpTransact::what_is_document_freshness(State *s, HTTPHdr* client_request, HTTPHdr* cached_obj_response)
07235 {
07236   bool heuristic, do_revalidate = false;
07237   int age_limit;
07238   // These aren't used.
07239   //HTTPValCacheControl *cc;
07240   //const char *cc_val;
07241   int fresh_limit;
07242   ink_time_t current_age, response_date;;
07243   uint32_t cc_mask, cooked_cc_mask;
07244   uint32_t os_specifies_revalidate;
07245 
07246   //////////////////////////////////////////////////////
07247   // If config file has a ttl-in-cache field set,     //
07248   // it has priority over any other http headers and  //
07249   // other configuration parameters.                  //
07250   //////////////////////////////////////////////////////
07251   if (s->cache_control.ttl_in_cache > 0) {
07252     // what matters if ttl is set is not the age of the document
07253     // but for how long it has been stored in the cache (resident time)
07254     int resident_time = s->current.now - s->response_received_time;
07255 
07256     DebugTxn("http_match", "[..._document_freshness] ttl-in-cache = %d, resident time = %d", s->cache_control.ttl_in_cache,
07257           resident_time);
07258     if (resident_time > s->cache_control.ttl_in_cache) {
07259       return (FRESHNESS_STALE);
07260     } else {
07261       return (FRESHNESS_FRESH);
07262     }
07263   }
07264 
07265 
07266   cooked_cc_mask = cached_obj_response->get_cooked_cc_mask();
07267   os_specifies_revalidate = cooked_cc_mask &
07268     (MIME_COOKED_MASK_CC_MUST_REVALIDATE | MIME_COOKED_MASK_CC_PROXY_REVALIDATE);
07269   cc_mask = MIME_COOKED_MASK_CC_NEED_REVALIDATE_ONCE;
07270 
07271   // Check to see if the server forces revalidation
07272 
07273   if ((cooked_cc_mask & cc_mask) && s->cache_control.revalidate_after <= 0) {
07274     DebugTxn("http_match", "[what_is_document_freshness] document stale due to " "server must-revalidate");
07275     return FRESHNESS_STALE;
07276   }
07277 
07278   response_date = cached_obj_response->get_date();
07279   fresh_limit = calculate_document_freshness_limit(s, cached_obj_response, response_date, &heuristic);
07280   ink_assert(fresh_limit >= 0);
07281 
07282   // Fuzz the freshness to prevent too many revalidates to popular
07283   //  documents at the same time
07284   if (s->txn_conf->freshness_fuzz_time >= 0) {
07285     fresh_limit = fresh_limit - calculate_freshness_fuzz(s, fresh_limit);
07286     fresh_limit = max(0, fresh_limit);
07287     fresh_limit = min(NUM_SECONDS_IN_ONE_YEAR, fresh_limit);
07288   }
07289 
07290   current_age = HttpTransactHeaders::calculate_document_age(s->request_sent_time, s->response_received_time,
07291                                                             cached_obj_response, response_date, s->current.now);
07292 
07293   // Overflow ?
07294   if (current_age < 0)
07295     current_age = NUM_SECONDS_IN_ONE_YEAR;  // TODO: Should we make a new "max age" define?
07296   else
07297     current_age = min((time_t)NUM_SECONDS_IN_ONE_YEAR, current_age);
07298 
07299   DebugTxn("http_match", "[what_is_document_freshness] fresh_limit:  %d  current_age: %" PRId64,
07300            fresh_limit, (int64_t)current_age);
07301 
07302   /////////////////////////////////////////////////////////
07303   // did the admin override the expiration calculations? //
07304   // (used only for http).                               //
07305   /////////////////////////////////////////////////////////
07306   ink_assert(client_request == &s->hdr_info.client_request);
07307 
07308   if (s->txn_conf->cache_when_to_revalidate == 0) {
07309     ;
07310     // Compute how fresh below
07311   } else if (client_request->url_get()->scheme_get_wksidx() == URL_WKSIDX_HTTP) {
07312     switch (s->txn_conf->cache_when_to_revalidate) {
07313     case 1:                    // Stale if heuristic
07314       if (heuristic) {
07315         DebugTxn("http_match", "[what_is_document_freshness] config requires FRESHNESS_STALE because heuristic calculation");
07316         return (FRESHNESS_STALE);
07317       }
07318       break;
07319     case 2:                    // Always stale
07320       DebugTxn("http_match", "[what_is_document_freshness] config " "specifies always FRESHNESS_STALE");
07321       return (FRESHNESS_STALE);
07322     case 3:                    // Never stale
07323       DebugTxn("http_match", "[what_is_document_freshness] config " "specifies always FRESHNESS_FRESH");
07324       return (FRESHNESS_FRESH);
07325     case 4:                    // Stale if IMS
07326       if (client_request->presence(MIME_PRESENCE_IF_MODIFIED_SINCE)) {
07327         DebugTxn("http_match", "[what_is_document_freshness] config " "specifies FRESHNESS_STALE if IMS present");
07328         return (FRESHNESS_STALE);
07329       }
07330     default:                   // Bad config, ignore
07331       break;
07332     }
07333   }
07334   //////////////////////////////////////////////////////////////////////
07335   // the normal expiration policy allows serving a doc from cache if: //
07336   //     basic:          (current_age <= fresh_limit)                 //
07337   //                                                                  //
07338   // this can be modified by client Cache-Control headers:            //
07339   //     max-age:        (current_age <= max_age)                     //
07340   //     min-fresh:      (current_age <= fresh_limit - min_fresh)     //
07341   //     max-stale:      (current_age <= fresh_limit + max_stale)     //
07342   //////////////////////////////////////////////////////////////////////
07343   age_limit = fresh_limit;      // basic constraint
07344   DebugTxn("http_match", "[..._document_freshness] initial age limit: %d", age_limit);
07345 
07346   cooked_cc_mask = client_request->get_cooked_cc_mask();
07347   cc_mask = (MIME_COOKED_MASK_CC_MAX_STALE | MIME_COOKED_MASK_CC_MIN_FRESH | MIME_COOKED_MASK_CC_MAX_AGE);
07348   if (cooked_cc_mask & cc_mask) {
07349     /////////////////////////////////////////////////
07350     // if max-stale set, relax the freshness limit //
07351     /////////////////////////////////////////////////
07352     if (cooked_cc_mask & MIME_COOKED_MASK_CC_MAX_STALE) {
07353       if (os_specifies_revalidate) {
07354         DebugTxn("http_match", "[...document_freshness] OS specifies revalidation; "
07355               "ignoring client's max-stale request...");
07356       } else {
07357         int max_stale_val = client_request->get_cooked_cc_max_stale();
07358 
07359         if (max_stale_val != INT_MAX)
07360           age_limit += max_stale_val;
07361         else
07362           age_limit = max_stale_val;
07363         DebugTxn("http_match", "[..._document_freshness] max-stale set, age limit: %d", age_limit);
07364       }
07365     }
07366     /////////////////////////////////////////////////////
07367     // if min-fresh set, constrain the freshness limit //
07368     /////////////////////////////////////////////////////
07369     if (cooked_cc_mask & MIME_COOKED_MASK_CC_MIN_FRESH) {
07370       age_limit = min(age_limit, fresh_limit - client_request->get_cooked_cc_min_fresh());
07371       DebugTxn("http_match", "[..._document_freshness] min_fresh set, age limit: %d", age_limit);
07372     }
07373     ///////////////////////////////////////////////////
07374     // if max-age set, constrain the freshness limit //
07375     ///////////////////////////////////////////////////
07376     if (!s->cache_control.ignore_client_cc_max_age && (cooked_cc_mask & MIME_COOKED_MASK_CC_MAX_AGE)) {
07377       int age_val = client_request->get_cooked_cc_max_age();
07378       if (age_val == 0)
07379         do_revalidate = true;
07380       age_limit = min(age_limit, age_val);
07381       DebugTxn("http_match", "[..._document_freshness] min_fresh set, age limit: %d", age_limit);
07382     }
07383   }
07384   /////////////////////////////////////////////////////////
07385   // config file may have a "revalidate_after" field set //
07386   /////////////////////////////////////////////////////////
07387   // bug fix changed ">0" to ">=0"
07388   if (s->cache_control.revalidate_after >= 0) {
07389     // if we want the minimum of the already-computed age_limit and revalidate_after
07390 //      age_limit = mine(age_limit, s->cache_control.revalidate_after);
07391 
07392     // if instead the revalidate_after overrides all other variables
07393     age_limit = s->cache_control.revalidate_after;
07394 
07395     DebugTxn("http_match", "[..._document_freshness] revalidate_after set, age limit: %d", age_limit);
07396   }
07397 
07398   DebugTxn("http_match", "document_freshness --- current_age = %" PRId64, (int64_t)current_age);
07399   DebugTxn("http_match", "document_freshness --- age_limit   = %d", age_limit);
07400   DebugTxn("http_match", "document_freshness --- fresh_limit = %d", fresh_limit);
07401   DebugTxn("http_seq", "document_freshness --- current_age = %" PRId64, (int64_t)current_age);
07402   DebugTxn("http_seq", "document_freshness --- age_limit   = %d", age_limit);
07403   DebugTxn("http_seq", "document_freshness --- fresh_limit = %d", fresh_limit);
07404   ///////////////////////////////////////////
07405   // now, see if the age is "fresh enough" //
07406   ///////////////////////////////////////////
07407 
07408   if (do_revalidate || current_age > age_limit) { // client-modified limit
07409     DebugTxn("http_match", "[..._document_freshness] document needs revalidate/too old; "
07410             "returning FRESHNESS_STALE");
07411     return (FRESHNESS_STALE);
07412   } else if (current_age > fresh_limit) {  // original limit
07413     if (os_specifies_revalidate) {
07414       DebugTxn("http_match", "[..._document_freshness] document is stale and OS specifies revalidation; "
07415               "returning FRESHNESS_STALE");
07416       return (FRESHNESS_STALE);
07417     }
07418     DebugTxn("http_match", "[..._document_freshness] document is stale but no revalidation explicitly required; "
07419             "returning FRESHNESS_WARNING");
07420     return (FRESHNESS_WARNING);
07421   } else {
07422     DebugTxn("http_match", "[..._document_freshness] document is fresh; returning FRESHNESS_FRESH");
07423     return (FRESHNESS_FRESH);
07424   }
07425 }
07426 
07427 //////////////////////////////////////////////////////////////////////////////
07428 //
07429 //      HttpTransact::Authentication_t HttpTransact::AuthenticationNeeded(
07430 //          const OverridableHttpConfigParams *p,
07431 //          HTTPHdr *client_request,
07432 //          HTTPHdr *obj_response)
07433 //
07434 //      This function takes the current client request, and the headers
07435 //      from a potential response (e.g. from cache or proxy), and decides
07436 //      if the object needs to be authenticated with the origin server,
07437 //      before it can be sent to the client.
07438 //
07439 //      The return value describes the authentication process needed.  In
07440 //      this function, three results are possible:
07441 //
07442 //          AUTHENTICATION_SUCCESS              Can serve object directly
07443 //          AUTHENTICATION_MUST_REVALIDATE      Must revalidate with server
07444 //          AUTHENTICATION_MUST_PROXY           Must not serve object
07445 //
07446 //////////////////////////////////////////////////////////////////////////////
07447 
07448 HttpTransact::Authentication_t
07449 HttpTransact::AuthenticationNeeded(const OverridableHttpConfigParams* p, HTTPHdr* client_request, HTTPHdr* obj_response)
07450 {
07451   ///////////////////////////////////////////////////////////////////////
07452   // from RFC2068, sec 14.8, if a client request has the Authorization //
07453   // header set, we can't serve it unless the response is public, or   //
07454   // if it has a Cache-Control revalidate flag, and we do revalidate.  //
07455   ///////////////////////////////////////////////////////////////////////
07456 
07457   if ((p->cache_ignore_auth == 0) && client_request->presence(MIME_PRESENCE_AUTHORIZATION)) {
07458     if (obj_response->is_cache_control_set(HTTP_VALUE_MUST_REVALIDATE) ||
07459         obj_response->is_cache_control_set(HTTP_VALUE_PROXY_REVALIDATE)) {
07460       return AUTHENTICATION_MUST_REVALIDATE;
07461     } else if (obj_response->is_cache_control_set(HTTP_VALUE_PROXY_REVALIDATE)) {
07462       return AUTHENTICATION_MUST_REVALIDATE;
07463     } else if (obj_response->is_cache_control_set(HTTP_VALUE_PUBLIC)) {
07464       return AUTHENTICATION_SUCCESS;
07465     } else {
07466       if (obj_response->field_find("@WWW-Auth", 9) && client_request->method_get_wksidx() == HTTP_WKSIDX_GET)
07467         return AUTHENTICATION_CACHE_AUTH;
07468       return AUTHENTICATION_MUST_PROXY;
07469     }
07470   }
07471 
07472   if (obj_response->field_find("@WWW-Auth", 9) && client_request->method_get_wksidx() == HTTP_WKSIDX_GET)
07473     return AUTHENTICATION_CACHE_AUTH;
07474 
07475   return (AUTHENTICATION_SUCCESS);
07476 }
07477 
07478 void
07479 HttpTransact::handle_parent_died(State* s)
07480 {
07481   ink_assert(s->parent_result.r == PARENT_FAIL);
07482 
07483   build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Next Hop Connection Failed", "connect#failed_connect", NULL);
07484   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, NULL);
07485 }
07486 
07487 void
07488 HttpTransact::handle_server_died(State* s)
07489 {
07490   const char *reason = NULL;
07491   const char *body_type = "UNKNOWN";
07492   HTTPStatus status = HTTP_STATUS_BAD_GATEWAY;
07493 
07494   ////////////////////////////////////////////////////////
07495   // FIX: all the body types below need to be filled in //
07496   ////////////////////////////////////////////////////////
07497 
07498 
07499   //
07500   // congestion control
07501   //
07502   if (s->pCongestionEntry != NULL) {
07503     s->congestion_congested_or_failed = 1;
07504     if (s->current.state != CONGEST_CONTROL_CONGESTED_ON_F && s->current.state != CONGEST_CONTROL_CONGESTED_ON_M) {
07505       s->pCongestionEntry->failed_at(s->current.now);
07506     }
07507   }
07508 
07509   switch (s->current.state) {
07510   case CONNECTION_ALIVE:       /* died while alive for unknown reason */
07511     ink_release_assert(s->hdr_info.response_error != NO_RESPONSE_HEADER_ERROR);
07512     status = HTTP_STATUS_BAD_GATEWAY;
07513     reason = "Unknown Error";
07514     body_type = "response#bad_response";
07515     break;
07516   case CONNECTION_ERROR:
07517     status = HTTP_STATUS_BAD_GATEWAY;
07518     reason = (char *) get_error_string(s->cause_of_death_errno);
07519     body_type = "connect#failed_connect";
07520     break;
07521   case OPEN_RAW_ERROR:
07522     status = HTTP_STATUS_BAD_GATEWAY;
07523     reason = "Tunnel Connection Failed";
07524     body_type = "connect#failed_connect";
07525     break;
07526   case CONNECTION_CLOSED:
07527     status = HTTP_STATUS_BAD_GATEWAY;
07528     reason = "Server Hangup";
07529     body_type = "connect#hangup";
07530     break;
07531   case ACTIVE_TIMEOUT:
07532     if (s->api_txn_active_timeout_value != -1)
07533       DebugTxn("http_timeout", "Maximum active time of %d msec exceeded", s->api_txn_active_timeout_value);
07534     status = HTTP_STATUS_GATEWAY_TIMEOUT;
07535     reason = "Maximum Transaction Time Exceeded";
07536     body_type = "timeout#activity";
07537     break;
07538   case INACTIVE_TIMEOUT:
07539     if (s->api_txn_connect_timeout_value != -1)
07540       DebugTxn("http_timeout", "Maximum connect time of %d msec exceeded", s->api_txn_connect_timeout_value);
07541     status = HTTP_STATUS_GATEWAY_TIMEOUT;
07542     reason = "Connection Timed Out";
07543     body_type = "timeout#inactivity";
07544     break;
07545   case PARSE_ERROR:
07546   case BAD_INCOMING_RESPONSE:
07547     status = HTTP_STATUS_BAD_GATEWAY;
07548     reason = "Invalid HTTP Response";
07549     body_type = "response#bad_response";
07550     break;
07551   case CONGEST_CONTROL_CONGESTED_ON_F:
07552     status = HTTP_STATUS_SERVICE_UNAVAILABLE;
07553     reason = "Origin server congested";
07554     if (s->pCongestionEntry)
07555       body_type = s->pCongestionEntry->getErrorPage();
07556     else
07557       body_type = "congestion#retryAfter";
07558     s->hdr_info.response_error = TOTAL_RESPONSE_ERROR_TYPES;
07559     break;
07560   case CONGEST_CONTROL_CONGESTED_ON_M:
07561     status = HTTP_STATUS_SERVICE_UNAVAILABLE;
07562     reason = "Too many users";
07563     if (s->pCongestionEntry)
07564       body_type = s->pCongestionEntry->getErrorPage();
07565     else
07566       body_type = "congestion#retryAfter";
07567     s->hdr_info.response_error = TOTAL_RESPONSE_ERROR_TYPES;
07568     break;
07569   case STATE_UNDEFINED:
07570   case TRANSACTION_COMPLETE:
07571   default:                     /* unknown death */
07572     ink_release_assert(!"[handle_server_died] Unreasonable state - not dead, shouldn't be here");
07573     status = HTTP_STATUS_BAD_GATEWAY;
07574     reason = NULL;
07575     body_type = "response#bad_response";
07576     break;
07577   }
07578 
07579   if (s->pCongestionEntry && s->pCongestionEntry->F_congested() && status != HTTP_STATUS_SERVICE_UNAVAILABLE) {
07580     s->pCongestionEntry->stat_inc_F();
07581     CONGEST_SUM_GLOBAL_DYN_STAT(congested_on_F_stat, 1);
07582     status = HTTP_STATUS_SERVICE_UNAVAILABLE;
07583     reason = "Service Unavailable";
07584     body_type = s->pCongestionEntry->getErrorPage();
07585     s->hdr_info.response_error = TOTAL_RESPONSE_ERROR_TYPES;
07586   }
07587   ////////////////////////////////////////////////////////
07588   // FIX: comment stuff above and below here, not clear //
07589   ////////////////////////////////////////////////////////
07590 
07591   switch (s->hdr_info.response_error) {
07592   case NON_EXISTANT_RESPONSE_HEADER:
07593     status = HTTP_STATUS_BAD_GATEWAY;
07594     reason = "No Response Header From Server";
07595     body_type = "response#bad_response";
07596     break;
07597   case MISSING_REASON_PHRASE:
07598   case NO_RESPONSE_HEADER_ERROR:
07599   case NOT_A_RESPONSE_HEADER:
07600 #ifdef REALLY_NEED_TO_CHECK_DATE_VALIDITY
07601   case BOGUS_OR_NO_DATE_IN_RESPONSE:
07602 #endif
07603     status = HTTP_STATUS_BAD_GATEWAY;
07604     reason = "Malformed Server Response";
07605     body_type = "response#bad_response";
07606     break;
07607   case MISSING_STATUS_CODE:
07608     status = HTTP_STATUS_BAD_GATEWAY;
07609     reason = "Malformed Server Response Status";
07610     body_type = "response#bad_response";
07611     break;
07612   default:
07613     break;
07614   }
07615 
07616   if (reason == NULL) {
07617     status = HTTP_STATUS_BAD_GATEWAY;
07618     reason = "Server Connection Failed";
07619     body_type = "connect#failed_connect";
07620   }
07621 
07622   build_error_response(s, status, reason, body_type, NULL);
07623 
07624   return;
07625 }
07626 
07627 // return true if the response to the given request is likely cacheable
07628 // This function is called by build_request() to determine if the conditional
07629 // headers should be removed from server request.
07630 bool
07631 HttpTransact::is_request_likely_cacheable(State* s, HTTPHdr* request)
07632 {
07633   if ((s->method == HTTP_WKSIDX_GET || s->api_req_cacheable == true) &&
07634       !request->presence(MIME_PRESENCE_AUTHORIZATION) &&
07635       (!request->presence(MIME_PRESENCE_RANGE) || s->txn_conf->cache_range_write)) {
07636     return true;
07637   }
07638   return false;
07639 }
07640 
07641 void
07642 HttpTransact::build_request(State* s, HTTPHdr* base_request, HTTPHdr* outgoing_request, HTTPVersion outgoing_version)
07643 {
07644   // this part is to restore the original URL in case, multiple cache
07645   // lookups have happened - client request has been changed as the result
07646   //
07647   // notice that currently, based_request IS client_request
07648   if (base_request == &s->hdr_info.client_request) {
07649     if (s->redirect_info.redirect_in_process) {
07650       // this is for auto redirect
07651       URL *r_url = &s->redirect_info.redirect_url;
07652 
07653       ink_assert(r_url->valid());
07654       base_request->url_get()->copy(r_url);
07655     } else {
07656       // this is for multiple cache lookup
07657       URL *o_url = &s->cache_info.original_url;
07658 
07659       if (o_url->valid())
07660         base_request->url_get()->copy(o_url);
07661     }
07662   }
07663 
07664   HttpTransactHeaders::copy_header_fields(base_request, outgoing_request, s->txn_conf->fwd_proxy_auth_to_parent);
07665   add_client_ip_to_outgoing_request(s, outgoing_request);
07666   HttpTransactHeaders::remove_privacy_headers_from_request(s->http_config_param, s->txn_conf, outgoing_request);
07667   HttpTransactHeaders::add_global_user_agent_header_to_request(s->txn_conf, outgoing_request);
07668   handle_request_keep_alive_headers(s, outgoing_version, outgoing_request);
07669 
07670   // handle_conditional_headers appears to be obsolete.  Nothing happens
07671   // unelss s->cache_info.action == HttpTransact::CACHE_DO_UPDATE.  In that
07672   // case an assert will go off.  The functionality of this method
07673   // (e.g., setting the if-modfied-since header occurs in issue_revalidate
07674   //HttpTransactHeaders::handle_conditional_headers(&s->cache_info, outgoing_request);
07675 
07676   if (s->next_hop_scheme < 0)
07677     s->next_hop_scheme = URL_WKSIDX_HTTP;
07678   if (s->orig_scheme < 0)
07679     s->orig_scheme = URL_WKSIDX_HTTP;
07680 
07681   if (s->txn_conf->insert_request_via_string)
07682     HttpTransactHeaders::insert_via_header_in_request(s, outgoing_request);
07683 
07684   // We build 1.1 request header and then convert as necessary to
07685   //  the appropriate version in HttpTransact::build_request
07686   outgoing_request->version_set(HTTPVersion(1, 1));
07687 
07688   // Make sure our request version is defined
07689   ink_assert(outgoing_version != HTTPVersion(0, 0));
07690 
07691   // HttpTransactHeaders::convert_request(outgoing_version, outgoing_request); // commented out this idea
07692 
07693 
07694   // Check whether a Host header field is missing from a 1.0 or 1.1 request.
07695   if (outgoing_version != HTTPVersion(0, 9) && !outgoing_request->presence(MIME_PRESENCE_HOST)) {
07696     URL *url = outgoing_request->url_get();
07697     int host_len;
07698     const char *host = url->host_get(&host_len);
07699 
07700     // Add a ':port' to the HOST header if the request is not going
07701     // to the default port.
07702     int port = url->port_get();
07703     if (port != url_canonicalize_port(URL_TYPE_HTTP, 0)) {
07704       char *buf = (char *) alloca(host_len + 15);
07705       memcpy(buf, host, host_len);
07706       host_len += snprintf(buf + host_len, 15, ":%d", port);
07707       outgoing_request->value_set(MIME_FIELD_HOST, MIME_LEN_HOST, buf, host_len);
07708     } else {
07709       outgoing_request->value_set(MIME_FIELD_HOST, MIME_LEN_HOST, host, host_len);
07710     }
07711   }
07712 
07713   if (s->current.server == &s->server_info &&
07714       (s->next_hop_scheme == URL_WKSIDX_HTTP || s->next_hop_scheme == URL_WKSIDX_HTTPS ||
07715        s->next_hop_scheme == URL_WKSIDX_WS || s->next_hop_scheme == URL_WKSIDX_WSS)) {
07716     DebugTxn("http_trans", "[build_request] removing host name from url");
07717     HttpTransactHeaders::remove_host_name_from_url(outgoing_request);
07718   }
07719 
07720   // If we're going to a parent proxy, make sure we pass host and port
07721   // in the URL even if we didn't get them (e.g. transparent proxy)
07722   if (s->current.request_to == PARENT_PROXY &&
07723       !outgoing_request->is_target_in_url()) {
07724     DebugTxn("http_trans", "[build_request] adding target to URL for parent proxy");
07725 
07726     // No worry about HTTP/0.9 because we reject forward proxy requests that
07727     // don't have a host anywhere.
07728     outgoing_request->set_url_target_from_host_field();
07729   }
07730 
07731   // If the response is most likely not cacheable, eg, request with Authorization,
07732   // do we really want to remove conditional headers to get large 200 response?
07733   // Answer: NO.  Since if the response is most likely not cacheable,
07734   // we don't remove conditional headers so that for a non-200 response
07735   // from the O.S., we will save bandwidth between proxy and O.S.
07736   if (s->current.mode == GENERIC_PROXY) {
07737     if (is_request_likely_cacheable(s, base_request)) {
07738       if (s->txn_conf->cache_when_to_revalidate != 4) {
07739         DebugTxn("http_trans", "[build_request] " "request like cacheable and conditional headers removed");
07740         HttpTransactHeaders::remove_conditional_headers(outgoing_request);
07741       } else
07742         DebugTxn("http_trans", "[build_request] " "request like cacheable but keep conditional headers");
07743     } else {
07744       // In this case, we send a conditional request
07745       // instead of the normal non-conditional request.
07746       DebugTxn("http_trans", "[build_request] " "request not like cacheable and conditional headers not removed");
07747     }
07748   }
07749 
07750   if (s->http_config_param->send_100_continue_response) {
07751     HttpTransactHeaders::remove_100_continue_headers(s, outgoing_request);
07752     DebugTxn("http_trans", "[build_request] request expect 100-continue headers removed");
07753   }
07754 
07755   s->request_sent_time = ink_cluster_time();
07756   s->current.now = s->request_sent_time;
07757   // The assert is backwards in this case because request is being (re)sent.
07758   ink_assert(s->request_sent_time >= s->response_received_time);
07759 
07760 
07761   DebugTxn("http_trans", "[build_request] request_sent_time: %" PRId64, (int64_t)s->request_sent_time);
07762   if (!s->cop_test_page)
07763     DUMP_HEADER("http_hdrs", outgoing_request, s->state_machine_id, "Proxy's Request");
07764 
07765   HTTP_INCREMENT_TRANS_STAT(http_outgoing_requests_stat);
07766 }
07767 
07768 // build a (status_code) response based upon the given info
07769 
07770 
07771 void
07772 HttpTransact::build_response(State* s, HTTPHdr* base_response, HTTPHdr* outgoing_response, HTTPVersion outgoing_version)
07773 {
07774   build_response(s, base_response, outgoing_response, outgoing_version, HTTP_STATUS_NONE, NULL);
07775   return;
07776 }
07777 
07778 
07779 void
07780 HttpTransact::build_response(State* s, HTTPHdr* outgoing_response, HTTPVersion outgoing_version, HTTPStatus status_code,
07781                              const char *reason_phrase)
07782 {
07783   build_response(s, NULL, outgoing_response, outgoing_version, status_code, reason_phrase);
07784   return;
07785 }
07786 
07787 void
07788 HttpTransact::build_response(State* s, HTTPHdr* base_response, HTTPHdr* outgoing_response, HTTPVersion outgoing_version,
07789                              HTTPStatus status_code, const char *reason_phrase)
07790 {
07791   if (reason_phrase == NULL) {
07792     reason_phrase = http_hdr_reason_lookup(status_code);
07793   }
07794 
07795   if (base_response == NULL) {
07796     HttpTransactHeaders::build_base_response(outgoing_response, status_code, reason_phrase, strlen(reason_phrase), s->current.now);
07797   } else {
07798     if ((status_code == HTTP_STATUS_NONE) || (status_code == base_response->status_get())) {
07799       HttpTransactHeaders::copy_header_fields(base_response, outgoing_response, s->txn_conf->fwd_proxy_auth_to_parent);
07800 
07801       if (s->txn_conf->insert_age_in_response)
07802         HttpTransactHeaders::insert_time_and_age_headers_in_response(s->request_sent_time, s->response_received_time,
07803                                                                      s->current.now, base_response, outgoing_response);
07804 
07805       // Note: We need to handle the "Content-Length" header first here
07806       //  since handle_content_length_header()
07807       //  determines whether we accept origin server's content-length.
07808       //  We need to have made a decision regard the content-length
07809       //  before processing the keep_alive headers
07810       //
07811       handle_content_length_header(s, outgoing_response, base_response);
07812     } else
07813       switch (status_code) {
07814       case HTTP_STATUS_NOT_MODIFIED:
07815         HttpTransactHeaders::build_base_response(outgoing_response, status_code, reason_phrase, strlen(reason_phrase),
07816                                                  s->current.now);
07817 
07818         // According to RFC 2616, Section 10.3.5,
07819         // a 304 response MUST contain Date header,
07820         // Etag and/or Content-location header,
07821         // and Expires, Cache-control, and Vary
07822         // (if they might be changed).
07823         // Since a proxy doesn't know if a header differs from
07824         // a user agent's cached document or not, all are sent.
07825         {
07826           static const char *field_name[] = { MIME_FIELD_ETAG,
07827                                               MIME_FIELD_CONTENT_LOCATION,
07828                                               MIME_FIELD_EXPIRES,
07829                                               MIME_FIELD_CACHE_CONTROL,
07830                                               MIME_FIELD_VARY
07831           };
07832           static int field_len[] = { MIME_LEN_ETAG,
07833                                      MIME_LEN_CONTENT_LOCATION,
07834                                      MIME_LEN_EXPIRES,
07835                                      MIME_LEN_CACHE_CONTROL,
07836                                      MIME_LEN_VARY
07837           };
07838           static uint64_t field_presence[] = { MIME_PRESENCE_ETAG,
07839                                                MIME_PRESENCE_CONTENT_LOCATION,
07840                                                MIME_PRESENCE_EXPIRES,
07841                                                MIME_PRESENCE_CACHE_CONTROL,
07842                                                MIME_PRESENCE_VARY
07843           };
07844           MIMEField *field;
07845           int len;
07846           const char *value;
07847 
07848           for (size_t i = 0; i < sizeof(field_len) / sizeof(field_len[0]); i++) {
07849             if (base_response->presence(field_presence[i])) {
07850               field = base_response->field_find(field_name[i], field_len[i]);
07851               value = field->value_get(&len);
07852               outgoing_response->value_append(field_name[i], field_len[i], value, len, 0);
07853             }
07854           }
07855         }
07856         break;
07857 
07858       case HTTP_STATUS_PRECONDITION_FAILED:
07859         // fall through
07860       case HTTP_STATUS_RANGE_NOT_SATISFIABLE:
07861         HttpTransactHeaders::build_base_response(outgoing_response, status_code, reason_phrase,
07862                                                  strlen(reason_phrase), s->current.now);
07863         break;
07864       default:
07865         // ink_assert(!"unexpected status code in build_response()");
07866         break;
07867       }
07868   }
07869 
07870   // the following is done whether base_response == NULL or not
07871 
07872   // If the response is prohibited from containing a body,
07873   //  we know the content length is trustable for keep-alive
07874   if (is_response_body_precluded(status_code, s->method))
07875     s->hdr_info.trust_response_cl = true;
07876 
07877   handle_response_keep_alive_headers(s, outgoing_version, outgoing_response);
07878 
07879   if (s->next_hop_scheme < 0)
07880     s->next_hop_scheme = URL_WKSIDX_HTTP;
07881 
07882   // Add HSTS header (Strict-Transport-Security) if max-age is set and the request was https
07883   if (s->orig_scheme == URL_WKSIDX_HTTPS && s->txn_conf->proxy_response_hsts_max_age >= 0) {
07884     Debug("http_hdrs", "hsts max-age=%" PRId64, s->txn_conf->proxy_response_hsts_max_age);
07885     HttpTransactHeaders::insert_hsts_header_in_response(s, outgoing_response);
07886   }
07887 
07888   if (s->txn_conf->insert_response_via_string)
07889     HttpTransactHeaders::insert_via_header_in_response(s, outgoing_response);
07890 
07891   HttpTransactHeaders::convert_response(outgoing_version, outgoing_response);
07892 
07893   // process reverse mappings on the location header
07894   // TS-1364: do this regardless of response code
07895   response_url_remap(outgoing_response);
07896 
07897   if (s->http_config_param->enable_http_stats) {
07898     HttpTransactHeaders::generate_and_set_squid_codes(outgoing_response, s->via_string, &s->squid_codes);
07899   }
07900 
07901   HttpTransactHeaders::add_server_header_to_response(s->txn_conf, outgoing_response);
07902 
07903   // auth-response update
07904  // if (!s->state_machine->authAdapter.disabled()) {
07905   //  s->state_machine->authAdapter.UpdateResponseHeaders(outgoing_response);
07906  // }
07907 
07908   if (diags->on()) {
07909     if (base_response) {
07910       if (!s->cop_test_page)
07911         DUMP_HEADER("http_hdrs", base_response, s->state_machine_id, "Base Header for Building Response");
07912     }
07913     if (!s->cop_test_page)
07914       DUMP_HEADER("http_hdrs", outgoing_response, s->state_machine_id, "Proxy's Response 2");
07915   }
07916 
07917   return;
07918 }
07919 
07920 //////////////////////////////////////////////////////////////////////////////
07921 //
07922 //      void HttpTransact::build_error_response(
07923 //          State *s,
07924 //          HTTPStatus status_code,
07925 //          char *reason_phrase_or_null,
07926 //          char *error_body_type,
07927 //          char *format, ...)
07928 //
07929 //      This method sets the requires state for an error reply, including
07930 //      the error text, status code, reason phrase, and reply headers.  The
07931 //      caller calls the method with the HttpTransact::State <s>, the
07932 //      HTTP status code <status_code>, a user-specified reason phrase
07933 //      string (or NULL) <reason_phrase_or_null>, and a printf-like
07934 //      text format and arguments which are appended to the error text.
07935 //
07936 //      The <error_body_type> is the error message type, as specified by
07937 //      the HttpBodyFactory customized error page system.
07938 //
07939 //      If the descriptive text <format> is not NULL or "", it is also
07940 //      added to the error text body as descriptive text in the error body.
07941 //      If <reason_phrase_or_null> is NULL, the default HTTP reason phrase
07942 //      is used.  This routine DOES NOT check for buffer overflows.  The
07943 //      caller should keep the messages small to be sure the error text
07944 //      fits in the error buffer (ok, it's nasty, but at least I admit it!).
07945 //
07946 //////////////////////////////////////////////////////////////////////////////
07947 void
07948 HttpTransact::build_error_response(State *s, HTTPStatus status_code, const char *reason_phrase_or_null,
07949                                    const char *error_body_type, const char *format, ...)
07950 {
07951   va_list ap;
07952   const char *reason_phrase;
07953   char *url_string;
07954   char body_language[256], body_type[256];
07955 
07956   if (NULL == error_body_type) {
07957     error_body_type = "default";
07958   }
07959 
07960   ////////////////////////////////////////////////////////////
07961   // get the url --- remember this is dynamically allocated //
07962   ////////////////////////////////////////////////////////////
07963   if (s->hdr_info.client_request.valid()) {
07964     url_string = s->hdr_info.client_request.url_string_get(&s->arena);
07965   } else {
07966     url_string = NULL;
07967   }
07968 
07969   //////////////////////////////////////////////////////
07970   //  If there is a request body, we must disable     //
07971   //  keep-alive to prevent the body being read as    //
07972   //  the next header (unless we've already drained   //
07973   //  which we do for NTLM auth)                      //
07974   //////////////////////////////////////////////////////
07975   if (s->hdr_info.request_content_length != 0 &&
07976       s->state_machine->client_request_body_bytes < s->hdr_info.request_content_length) {
07977     s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
07978   } else {
07979     // We don't have a request body.  Since we are
07980     //  generating the error, we know we can trust
07981     //  the content-length
07982     s->hdr_info.trust_response_cl = true;
07983   }
07984 
07985   switch (status_code) {
07986   case HTTP_STATUS_BAD_REQUEST:
07987     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_ERROR);
07988     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_HEADER_SYNTAX);
07989     break;
07990   case HTTP_STATUS_BAD_GATEWAY:
07991     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_CONNECTION);
07992     break;
07993   case HTTP_STATUS_GATEWAY_TIMEOUT:
07994     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_TIMEOUT);
07995     break;
07996   case HTTP_STATUS_NOT_FOUND:
07997     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_SERVER);
07998     break;
07999   case HTTP_STATUS_FORBIDDEN:
08000     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_ERROR);
08001     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_FORBIDDEN);
08002     break;
08003   case HTTP_STATUS_HTTPVER_NOT_SUPPORTED:
08004     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_ERROR);
08005     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_SERVER);
08006     break;
08007   case HTTP_STATUS_INTERNAL_SERVER_ERROR:
08008     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_DNS_FAILURE);
08009     break;
08010   case HTTP_STATUS_MOVED_TEMPORARILY:
08011     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_SERVER);
08012     break;
08013   case HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED:
08014     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_ERROR);
08015     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_AUTHORIZATION);
08016     break;
08017   case HTTP_STATUS_UNAUTHORIZED:
08018     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_ERROR);
08019     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_AUTHORIZATION);
08020     break;
08021   default:
08022     break;
08023   }
08024 
08025   reason_phrase = (reason_phrase_or_null ? reason_phrase_or_null : (char *) (http_hdr_reason_lookup(status_code)));
08026   if (unlikely(!reason_phrase))
08027     reason_phrase = "Unknown HTTP Status";
08028 
08029   // set the source to internal so that chunking is handled correctly
08030   s->source = SOURCE_INTERNAL;
08031   build_response(s, &s->hdr_info.client_response, s->client_info.http_version, status_code, reason_phrase);
08032 
08033   if (status_code == HTTP_STATUS_SERVICE_UNAVAILABLE) {
08034     if (s->pCongestionEntry != NULL) {
08035       int ret_tmp;
08036       int retry_after = s->pCongestionEntry->client_retry_after();
08037 
08038       s->congestion_control_crat = retry_after;
08039       if (s->hdr_info.client_response.value_get(MIME_FIELD_RETRY_AFTER, MIME_LEN_RETRY_AFTER, &ret_tmp) == NULL)
08040         s->hdr_info.client_response.value_set_int(MIME_FIELD_RETRY_AFTER, MIME_LEN_RETRY_AFTER, retry_after);
08041     }
08042   }
08043   if (status_code == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED &&
08044       s->method == HTTP_WKSIDX_CONNECT && s->hdr_info.client_response.presence(MIME_PRESENCE_PROXY_CONNECTION)) {
08045     int has_ua_msie = 0;
08046     int user_agent_value_len, slen;
08047     const char *user_agent_value, *c, *e;
08048 
08049     user_agent_value = s->hdr_info.client_request.value_get(MIME_FIELD_USER_AGENT, MIME_LEN_USER_AGENT, &user_agent_value_len);
08050     if (user_agent_value && user_agent_value_len >= 4) {
08051       c = user_agent_value;
08052       e = c + user_agent_value_len - 4;
08053       while (1) {
08054         slen = (int) (e - c);
08055         c = (const char *) memchr(c, 'M', slen);
08056         if (c == NULL || (e - c) < 3)
08057           break;
08058         if ((c[1] == 'S') && (c[2] == 'I') && (c[3] == 'E')) {
08059           has_ua_msie = 1;
08060           break;
08061         }
08062         c++;
08063       }
08064     }
08065 
08066     if (has_ua_msie)
08067       s->hdr_info.client_response.value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "close", 5);
08068   }
08069   // Add a bunch of headers to make sure that caches between
08070   // the Traffic Server and the client do not cache the error
08071   // page.
08072   s->hdr_info.client_response.value_set(MIME_FIELD_CACHE_CONTROL, MIME_LEN_CACHE_CONTROL, "no-store", 8);
08073   // Make sure there are no Expires and Last-Modified headers.
08074   s->hdr_info.client_response.field_delete(MIME_FIELD_EXPIRES, MIME_LEN_EXPIRES);
08075   s->hdr_info.client_response.field_delete(MIME_FIELD_LAST_MODIFIED, MIME_LEN_LAST_MODIFIED);
08076 
08077   if ((status_code == HTTP_STATUS_TEMPORARY_REDIRECT ||
08078        status_code == HTTP_STATUS_MOVED_TEMPORARILY ||
08079        status_code == HTTP_STATUS_MOVED_PERMANENTLY) &&
08080       s->remap_redirect) {
08081     s->hdr_info.client_response.value_set(MIME_FIELD_LOCATION, MIME_LEN_LOCATION, s->remap_redirect, strlen(s->remap_redirect));
08082   }
08083 
08084 
08085 
08086   ////////////////////////////////////////////////////////////////////
08087   // create the error message using the "body factory", which will  //
08088   // build a customized error message if available, or generate the //
08089   // old style internal defaults otherwise --- the body factory     //
08090   // supports language targeting using the Accept-Language header   //
08091   ////////////////////////////////////////////////////////////////////
08092 
08093   int64_t len;
08094   char * new_msg;
08095 
08096   va_start(ap, format);
08097   new_msg = body_factory->fabricate_with_old_api(error_body_type, s, 8192,
08098                                                        &len,
08099                                                        body_language, sizeof(body_language),
08100                                                        body_type, sizeof(body_type),
08101                                                        format, ap);
08102   va_end(ap);
08103 
08104   // After the body factory is called, a new "body" is allocated, and we must replace it. It is
08105   // unfortunate that there's no way to avoid this fabrication even when there is no substitutions...
08106   s->free_internal_msg_buffer();
08107   s->internal_msg_buffer = new_msg;
08108   s->internal_msg_buffer_size = len;
08109   s->internal_msg_buffer_index = 0;
08110   s->internal_msg_buffer_fast_allocator_size = -1;
08111 
08112   s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE, body_type, strlen(body_type));
08113   s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_LANGUAGE, MIME_LEN_CONTENT_LANGUAGE, body_language,
08114                                         strlen(body_language));
08115 
08116   ////////////////////////////////////////
08117   // log a description in the error log //
08118   ////////////////////////////////////////
08119 
08120   if (s->current.state == CONNECTION_ERROR) {
08121     char *reason_buffer;
08122     int buf_len = sizeof(char) * (strlen(get_error_string(s->cause_of_death_errno)) + 50);
08123     reason_buffer = (char *) alloca(buf_len);
08124     snprintf(reason_buffer, buf_len, "Connect Error <%s/%d>", get_error_string(s->cause_of_death_errno), s->cause_of_death_errno);
08125     reason_phrase = reason_buffer;
08126   }
08127 
08128   if (s->http_config_param->errors_log_error_pages && status_code >= HTTP_STATUS_BAD_REQUEST) {
08129     char ip_string[INET6_ADDRSTRLEN];
08130 
08131     Log::error("RESPONSE: sent %s status %d (%s) for '%s'", 
08132         ats_ip_ntop(&s->client_info.addr.sa, ip_string, sizeof(ip_string)), 
08133         status_code, 
08134         reason_phrase,
08135         (url_string ? url_string : "<none>"));
08136   }
08137 
08138   if (url_string) {
08139     s->arena.str_free(url_string);
08140   }
08141 
08142   s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
08143   return;
08144 }
08145 
08146 void
08147 HttpTransact::build_redirect_response(State* s)
08148 {
08149   DebugTxn("http_redirect", "[HttpTransact::build_redirect_response]");
08150   URL *u;
08151   const char *old_host;
08152   int old_host_len;
08153   const char *new_url = NULL;
08154   int new_url_len;
08155   char *to_free = NULL;
08156   char body_language[256], body_type[256];
08157 
08158   HTTPStatus status_code = HTTP_STATUS_MOVED_TEMPORARILY;
08159   char *reason_phrase = (char *) (http_hdr_reason_lookup(status_code));
08160 
08161   build_response(s, &s->hdr_info.client_response, s->client_info.http_version, status_code, reason_phrase);
08162 
08163   //////////////////////////////////////////////////////////
08164   // figure out what new url should be.  this little hack //
08165   // inserts expanded hostname into old url in order to   //
08166   // get scheme information, then puts the old url back.  //
08167   //////////////////////////////////////////////////////////
08168   u = s->hdr_info.client_request.url_get();
08169   old_host = u->host_get(&old_host_len);
08170   u->host_set(s->dns_info.lookup_name, strlen(s->dns_info.lookup_name));
08171   new_url = to_free = u->string_get(&s->arena, &new_url_len);
08172   if (new_url == NULL) {
08173     new_url = "";
08174   }
08175   u->host_set(old_host, old_host_len);
08176 
08177   //////////////////////////
08178   // set redirect headers //
08179   //////////////////////////
08180   HTTPHdr *h = &s->hdr_info.client_response;
08181   if (s->txn_conf->insert_response_via_string) {
08182     const char pa[] = "Proxy-agent";
08183 
08184     h->value_append(pa, sizeof(pa) - 1, s->http_config_param->proxy_response_via_string,
08185                     s->http_config_param->proxy_response_via_string_len);
08186   }
08187   h->value_set(MIME_FIELD_LOCATION, MIME_LEN_LOCATION, new_url, new_url_len);
08188 
08189   //////////////////////////
08190   // set descriptive text //
08191   //////////////////////////
08192   s->internal_msg_buffer_index = 0;
08193   s->free_internal_msg_buffer();
08194   s->internal_msg_buffer_fast_allocator_size = -1;
08195   s->internal_msg_buffer = body_factory->fabricate_with_old_api_build_va("redirect#moved_temporarily", s, 8192,
08196                                                                          &s->internal_msg_buffer_size,
08197                                                                          body_language, sizeof(body_language),
08198                                                                          body_type, sizeof(body_type), 
08199                                                                          "%s <a href=\"%s\">%s</a>.  %s.",
08200                                                                          "The document you requested is now",
08201                                                                          new_url, new_url,
08202                                                                          "Please update your documents and bookmarks accordingly", NULL);
08203 
08204 
08205   h->set_content_length(s->internal_msg_buffer_size);
08206   h->value_set(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE, "text/html", 9);
08207 
08208   s->arena.str_free(to_free);
08209 }
08210 
08211 const char *
08212 HttpTransact::get_error_string(int erno)
08213 {
08214   if (erno >= 0) {
08215     return (strerror(erno));
08216   } else {
08217     switch (-erno) {
08218     case ENET_THROTTLING:
08219       return ("throttling");
08220     case ESOCK_DENIED:
08221       return ("socks error - denied");
08222     case ESOCK_TIMEOUT:
08223       return ("socks error - timeout");
08224     case ESOCK_NO_SOCK_SERVER_CONN:
08225       return ("socks error - no server connection");
08226 //              this assumes that the following case occurs
08227 //              when HttpSM.cc::state_origin_server_read_response
08228 //                 receives an HTTP_EVENT_EOS. (line 1729 in HttpSM.cc,
08229 //                 version 1.145.2.13.2.57)
08230     case UNKNOWN_INTERNAL_ERROR:
08231       return ("internal error - server connection terminated");
08232     default:
08233       return ("");
08234     }
08235   }
08236 }
08237 
08238 ink_time_t
08239 ink_cluster_time(void)
08240 {
08241   int highest_delta;
08242 
08243 #ifdef DEBUG
08244   ink_mutex_acquire(&http_time_lock);
08245   ink_time_t local_time = ink_get_hrtime() / HRTIME_SECOND;
08246   last_http_local_time = local_time;
08247   ink_mutex_release(&http_time_lock);
08248 #else
08249   ink_time_t local_time = ink_get_hrtime() / HRTIME_SECOND;
08250 #endif
08251 
08252   highest_delta = (int) HttpConfig::m_master.cluster_time_delta;
08253 //     highest_delta =
08254 //      lmgmt->record_data->readInteger("proxy.process.http.cluster_delta",
08255 //                                      &found);
08256 //     if (! found) {
08257 //      ink_assert(!"Highest delta config value not found!");
08258 //      highest_delta = 0L;
08259 //     }
08260 
08261   Debug("http_trans", "[ink_cluster_time] local: %" PRId64 ", highest_delta: %d, cluster: %" PRId64,
08262         (int64_t)local_time, highest_delta, (int64_t)(local_time + (ink_time_t) highest_delta));
08263 
08264   ink_assert(highest_delta >= 0);
08265 
08266   return local_time + (ink_time_t) highest_delta;
08267 }
08268 
08269 //
08270 // The stat functions
08271 //
08272 void
08273 HttpTransact::histogram_response_document_size(State* s, int64_t doc_size)
08274 {
08275   if (doc_size >= 0 && doc_size <= 100) {
08276     HTTP_INCREMENT_TRANS_STAT(http_response_document_size_100_stat);
08277   } else if (doc_size <= 1024) {
08278     HTTP_INCREMENT_TRANS_STAT(http_response_document_size_1K_stat);
08279   } else if (doc_size <= 3072) {
08280     HTTP_INCREMENT_TRANS_STAT(http_response_document_size_3K_stat);
08281   } else if (doc_size <= 5120) {
08282     HTTP_INCREMENT_TRANS_STAT(http_response_document_size_5K_stat);
08283   } else if (doc_size <= 10240) {
08284     HTTP_INCREMENT_TRANS_STAT(http_response_document_size_10K_stat);
08285   } else if (doc_size <= 1048576) {
08286     HTTP_INCREMENT_TRANS_STAT(http_response_document_size_1M_stat);
08287   } else {
08288     HTTP_INCREMENT_TRANS_STAT(http_response_document_size_inf_stat);
08289   }
08290   return;
08291 }
08292 
08293 void
08294 HttpTransact::histogram_request_document_size(State* s, int64_t doc_size)
08295 {
08296   if (doc_size >= 0 && doc_size <= 100) {
08297     HTTP_INCREMENT_TRANS_STAT(http_request_document_size_100_stat);
08298   } else if (doc_size <= 1024) {
08299     HTTP_INCREMENT_TRANS_STAT(http_request_document_size_1K_stat);
08300   } else if (doc_size <= 3072) {
08301     HTTP_INCREMENT_TRANS_STAT(http_request_document_size_3K_stat);
08302   } else if (doc_size <= 5120) {
08303     HTTP_INCREMENT_TRANS_STAT(http_request_document_size_5K_stat);
08304   } else if (doc_size <= 10240) {
08305     HTTP_INCREMENT_TRANS_STAT(http_request_document_size_10K_stat);
08306   } else if (doc_size <= 1048576) {
08307     HTTP_INCREMENT_TRANS_STAT(http_request_document_size_1M_stat);
08308   } else {
08309     HTTP_INCREMENT_TRANS_STAT(http_request_document_size_inf_stat);
08310   }
08311   return;
08312 }
08313 
08314 void
08315 HttpTransact::user_agent_connection_speed(State* s, ink_hrtime transfer_time, int64_t nbytes)
08316 {
08317   float bytes_per_hrtime = (transfer_time == 0) ? (nbytes) : ((float) nbytes / (float) (int64_t) transfer_time);
08318   int bytes_per_sec = (int) (bytes_per_hrtime * HRTIME_SECOND);
08319 
08320   if (bytes_per_sec <= 100) {
08321     HTTP_INCREMENT_TRANS_STAT(http_user_agent_speed_bytes_per_sec_100_stat);
08322   } else if (bytes_per_sec <= 1024) {
08323     HTTP_INCREMENT_TRANS_STAT(http_user_agent_speed_bytes_per_sec_1K_stat);
08324   } else if (bytes_per_sec <= 10240) {
08325     HTTP_INCREMENT_TRANS_STAT(http_user_agent_speed_bytes_per_sec_10K_stat);
08326   } else if (bytes_per_sec <= 102400) {
08327     HTTP_INCREMENT_TRANS_STAT(http_user_agent_speed_bytes_per_sec_100K_stat);
08328   } else if (bytes_per_sec <= 1048576) {
08329     HTTP_INCREMENT_TRANS_STAT(http_user_agent_speed_bytes_per_sec_1M_stat);
08330   } else if (bytes_per_sec <= 10485760) {
08331     HTTP_INCREMENT_TRANS_STAT(http_user_agent_speed_bytes_per_sec_10M_stat);
08332   } else {
08333     HTTP_INCREMENT_TRANS_STAT(http_user_agent_speed_bytes_per_sec_100M_stat);
08334   }
08335 
08336   return;
08337 }
08338 
08339 /*
08340  * added request_process_time stat for loadshedding foo
08341  */
08342 void
08343 HttpTransact::client_result_stat(State* s, ink_hrtime total_time, ink_hrtime request_process_time)
08344 {
08345   ClientTransactionResult_t client_transaction_result = CLIENT_TRANSACTION_RESULT_UNDEFINED;
08346 
08347   ///////////////////////////////////////////////////////
08348   // don't count errors we generated as hits or misses //
08349   ///////////////////////////////////////////////////////
08350   if ((s->source == SOURCE_INTERNAL) && (s->hdr_info.client_response.status_get() >= 400)) {
08351     client_transaction_result = CLIENT_TRANSACTION_RESULT_ERROR_OTHER;
08352   }
08353 
08354   switch (s->squid_codes.log_code) {
08355   case SQUID_LOG_ERR_CONNECT_FAIL:
08356     HTTP_INCREMENT_TRANS_STAT(http_cache_miss_cold_stat);
08357     client_transaction_result = CLIENT_TRANSACTION_RESULT_ERROR_CONNECT_FAIL;
08358     break;
08359 
08360   case SQUID_LOG_TCP_MEM_HIT:
08361     HTTP_INCREMENT_TRANS_STAT(http_cache_hit_mem_fresh_stat);
08362   case SQUID_LOG_TCP_HIT:
08363     // It's possible to have two stat's instead of one, if needed.
08364     HTTP_INCREMENT_TRANS_STAT(http_cache_hit_fresh_stat);
08365     client_transaction_result = CLIENT_TRANSACTION_RESULT_HIT_FRESH;
08366     break;
08367 
08368   case SQUID_LOG_TCP_REFRESH_HIT:
08369     HTTP_INCREMENT_TRANS_STAT(http_cache_hit_reval_stat);
08370     client_transaction_result = CLIENT_TRANSACTION_RESULT_HIT_REVALIDATED;
08371     break;
08372 
08373   case SQUID_LOG_TCP_IMS_HIT:
08374     HTTP_INCREMENT_TRANS_STAT(http_cache_hit_ims_stat);
08375     client_transaction_result = CLIENT_TRANSACTION_RESULT_HIT_FRESH;
08376     break;
08377 
08378   case SQUID_LOG_TCP_REF_FAIL_HIT:
08379     HTTP_INCREMENT_TRANS_STAT(http_cache_hit_stale_served_stat);
08380     client_transaction_result = CLIENT_TRANSACTION_RESULT_HIT_FRESH;
08381     break;
08382 
08383   case SQUID_LOG_TCP_MISS:
08384     if ((GET_VIA_STRING(VIA_CACHE_RESULT) == VIA_IN_CACHE_NOT_ACCEPTABLE)
08385         || (GET_VIA_STRING(VIA_CACHE_RESULT) == VIA_CACHE_MISS)) {
08386       HTTP_INCREMENT_TRANS_STAT(http_cache_miss_cold_stat);
08387       client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_COLD;
08388     } else {
08389       // FIX: what case is this for?  can it ever happen?
08390       HTTP_INCREMENT_TRANS_STAT(http_cache_miss_uncacheable_stat);
08391       client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_UNCACHABLE;
08392     }
08393     break;
08394 
08395   case SQUID_LOG_TCP_REFRESH_MISS:
08396     HTTP_INCREMENT_TRANS_STAT(http_cache_miss_changed_stat);
08397     client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_CHANGED;
08398     break;
08399 
08400   case SQUID_LOG_TCP_CLIENT_REFRESH:
08401     HTTP_INCREMENT_TRANS_STAT(http_cache_miss_client_no_cache_stat);
08402     client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_CLIENT_NO_CACHE;
08403     break;
08404 
08405   case SQUID_LOG_TCP_IMS_MISS:
08406     HTTP_INCREMENT_TRANS_STAT(http_cache_miss_ims_stat);
08407     client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_COLD;
08408     break;
08409 
08410   case SQUID_LOG_TCP_SWAPFAIL:
08411     HTTP_INCREMENT_TRANS_STAT(http_cache_read_error_stat);
08412     client_transaction_result = CLIENT_TRANSACTION_RESULT_HIT_FRESH;
08413     break;
08414 
08415   case SQUID_LOG_ERR_READ_TIMEOUT:
08416   case SQUID_LOG_TCP_DENIED:
08417     // No cache result due to error
08418     client_transaction_result = CLIENT_TRANSACTION_RESULT_ERROR_OTHER;
08419     break;
08420 
08421   default:
08422     // FIX: What is the conditional below doing?
08423 //          if (s->local_trans_stats[http_cache_lookups_stat].count == 1L)
08424 //              HTTP_INCREMENT_TRANS_STAT(http_cache_miss_cold_stat);
08425 
08426     // FIX: I suspect the following line should not be set here,
08427     //      because it overrides the error classification above.
08428     //      Commenting out.
08429     // client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_COLD;
08430 
08431     break;
08432   }
08433 
08434   //////////////////////////////////////////
08435   // don't count aborts as hits or misses //
08436   //////////////////////////////////////////
08437   if (s->client_info.abort == ABORTED) {
08438     client_transaction_result = CLIENT_TRANSACTION_RESULT_ERROR_ABORT;
08439   } else if (s->client_info.abort == MAYBE_ABORTED) {
08440     client_transaction_result = CLIENT_TRANSACTION_RESULT_ERROR_POSSIBLE_ABORT;
08441   }
08442   // Count the status codes, assuming the client didn't abort (i.e. there is an m_http)
08443   if ((s->source != SOURCE_NONE) && (s->client_info.abort == DIDNOT_ABORT)) {
08444     int status_code = s->hdr_info.client_response.status_get();
08445 
08446     switch(status_code) {
08447     case 100: HTTP_INCREMENT_TRANS_STAT(http_response_status_100_count_stat); break;
08448     case 101: HTTP_INCREMENT_TRANS_STAT(http_response_status_101_count_stat); break;
08449     case 200: HTTP_INCREMENT_TRANS_STAT(http_response_status_200_count_stat); break;
08450     case 201: HTTP_INCREMENT_TRANS_STAT(http_response_status_201_count_stat); break;
08451     case 202: HTTP_INCREMENT_TRANS_STAT(http_response_status_202_count_stat); break;
08452     case 203: HTTP_INCREMENT_TRANS_STAT(http_response_status_203_count_stat); break;
08453     case 204: HTTP_INCREMENT_TRANS_STAT(http_response_status_204_count_stat); break;
08454     case 205: HTTP_INCREMENT_TRANS_STAT(http_response_status_205_count_stat); break;
08455     case 206: HTTP_INCREMENT_TRANS_STAT(http_response_status_206_count_stat); break;
08456     case 300: HTTP_INCREMENT_TRANS_STAT(http_response_status_300_count_stat); break;
08457     case 301: HTTP_INCREMENT_TRANS_STAT(http_response_status_301_count_stat); break;
08458     case 302: HTTP_INCREMENT_TRANS_STAT(http_response_status_302_count_stat); break;
08459     case 303: HTTP_INCREMENT_TRANS_STAT(http_response_status_303_count_stat); break;
08460     case 304: HTTP_INCREMENT_TRANS_STAT(http_response_status_304_count_stat); break;
08461     case 305: HTTP_INCREMENT_TRANS_STAT(http_response_status_305_count_stat); break;
08462     case 307: HTTP_INCREMENT_TRANS_STAT(http_response_status_307_count_stat); break;
08463     case 400: HTTP_INCREMENT_TRANS_STAT(http_response_status_400_count_stat); break;
08464     case 401: HTTP_INCREMENT_TRANS_STAT(http_response_status_401_count_stat); break;
08465     case 402: HTTP_INCREMENT_TRANS_STAT(http_response_status_402_count_stat); break;
08466     case 403: HTTP_INCREMENT_TRANS_STAT(http_response_status_403_count_stat); break;
08467     case 404: HTTP_INCREMENT_TRANS_STAT(http_response_status_404_count_stat); break;
08468     case 405: HTTP_INCREMENT_TRANS_STAT(http_response_status_405_count_stat); break;
08469     case 406: HTTP_INCREMENT_TRANS_STAT(http_response_status_406_count_stat); break;
08470     case 407: HTTP_INCREMENT_TRANS_STAT(http_response_status_407_count_stat); break;
08471     case 408: HTTP_INCREMENT_TRANS_STAT(http_response_status_408_count_stat); break;
08472     case 409: HTTP_INCREMENT_TRANS_STAT(http_response_status_409_count_stat); break;
08473     case 410: HTTP_INCREMENT_TRANS_STAT(http_response_status_410_count_stat); break;
08474     case 411: HTTP_INCREMENT_TRANS_STAT(http_response_status_411_count_stat); break;
08475     case 412: HTTP_INCREMENT_TRANS_STAT(http_response_status_412_count_stat); break;
08476     case 413: HTTP_INCREMENT_TRANS_STAT(http_response_status_413_count_stat); break;
08477     case 414: HTTP_INCREMENT_TRANS_STAT(http_response_status_414_count_stat); break;
08478     case 415: HTTP_INCREMENT_TRANS_STAT(http_response_status_415_count_stat); break;
08479     case 416: HTTP_INCREMENT_TRANS_STAT(http_response_status_416_count_stat); break;
08480     case 500: HTTP_INCREMENT_TRANS_STAT(http_response_status_500_count_stat); break;
08481     case 501: HTTP_INCREMENT_TRANS_STAT(http_response_status_501_count_stat); break;
08482     case 502: HTTP_INCREMENT_TRANS_STAT(http_response_status_502_count_stat); break;
08483     case 503: HTTP_INCREMENT_TRANS_STAT(http_response_status_503_count_stat); break;
08484     case 504: HTTP_INCREMENT_TRANS_STAT(http_response_status_504_count_stat); break;
08485     case 505: HTTP_INCREMENT_TRANS_STAT(http_response_status_505_count_stat); break;
08486     default: break;
08487     }
08488     switch(status_code / 100) {
08489     case 1: HTTP_INCREMENT_TRANS_STAT(http_response_status_1xx_count_stat); break;
08490     case 2: HTTP_INCREMENT_TRANS_STAT(http_response_status_2xx_count_stat); break;
08491     case 3: HTTP_INCREMENT_TRANS_STAT(http_response_status_3xx_count_stat); break;
08492     case 4: HTTP_INCREMENT_TRANS_STAT(http_response_status_4xx_count_stat); break;
08493     case 5: HTTP_INCREMENT_TRANS_STAT(http_response_status_5xx_count_stat); break;
08494     default: break;
08495     }
08496   }
08497   
08498   // Increment the completed connection count
08499   HTTP_INCREMENT_TRANS_STAT(http_completed_requests_stat);
08500 
08501   // Set the stat now that we know what happend
08502   ink_hrtime total_msec = ink_hrtime_to_msec(total_time);
08503   ink_hrtime process_msec = ink_hrtime_to_msec(request_process_time);
08504   switch (client_transaction_result) {
08505   case CLIENT_TRANSACTION_RESULT_HIT_FRESH:
08506     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_hit_fresh_stat, total_msec);
08507     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_hit_fresh_process_stat, process_msec);
08508     break;
08509   case CLIENT_TRANSACTION_RESULT_HIT_REVALIDATED:
08510     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_hit_reval_stat, total_msec);
08511     break;
08512   case CLIENT_TRANSACTION_RESULT_MISS_COLD:
08513     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_miss_cold_stat, total_msec);
08514     break;
08515   case CLIENT_TRANSACTION_RESULT_MISS_CHANGED:
08516     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_miss_changed_stat, total_msec);
08517     break;
08518   case CLIENT_TRANSACTION_RESULT_MISS_CLIENT_NO_CACHE:
08519     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_miss_client_no_cache_stat, total_msec);
08520     break;
08521   case CLIENT_TRANSACTION_RESULT_MISS_UNCACHABLE:
08522     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_miss_uncacheable_stat, total_msec);
08523     break;
08524   case CLIENT_TRANSACTION_RESULT_ERROR_ABORT:
08525     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_errors_aborts_stat, total_msec);
08526     break;
08527   case CLIENT_TRANSACTION_RESULT_ERROR_POSSIBLE_ABORT:
08528     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_errors_possible_aborts_stat, total_msec);
08529     break;
08530   case CLIENT_TRANSACTION_RESULT_ERROR_CONNECT_FAIL:
08531     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_errors_connect_failed_stat, total_msec);
08532     break;
08533   case CLIENT_TRANSACTION_RESULT_ERROR_OTHER:
08534     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_errors_other_stat, total_msec);
08535     break;
08536   default:
08537     HTTP_SUM_TRANS_STAT(http_ua_msecs_counts_other_unclassified_stat, total_msec);
08538     // This can happen if a plugin manually sets the status code after an error.
08539     DebugTxn("http", "Unclassified statistic");
08540     break;
08541   }
08542 }
08543 
08544 void
08545 HttpTransact::origin_server_connection_speed(State* s, ink_hrtime transfer_time, int64_t nbytes)
08546 {
08547   float bytes_per_hrtime = (transfer_time == 0) ? (nbytes) : ((float) nbytes / (float) (int64_t) transfer_time);
08548   int bytes_per_sec = (int) (bytes_per_hrtime * HRTIME_SECOND);
08549 
08550   if (bytes_per_sec <= 100) {
08551     HTTP_INCREMENT_TRANS_STAT(http_origin_server_speed_bytes_per_sec_100_stat);
08552   } else if (bytes_per_sec <= 1024) {
08553     HTTP_INCREMENT_TRANS_STAT(http_origin_server_speed_bytes_per_sec_1K_stat);
08554   } else if (bytes_per_sec <= 10240) {
08555     HTTP_INCREMENT_TRANS_STAT(http_origin_server_speed_bytes_per_sec_10K_stat);
08556   } else if (bytes_per_sec <= 102400) {
08557     HTTP_INCREMENT_TRANS_STAT(http_origin_server_speed_bytes_per_sec_100K_stat);
08558   } else if (bytes_per_sec <= 1048576) {
08559     HTTP_INCREMENT_TRANS_STAT(http_origin_server_speed_bytes_per_sec_1M_stat);
08560   } else if (bytes_per_sec <= 10485760) {
08561     HTTP_INCREMENT_TRANS_STAT(http_origin_server_speed_bytes_per_sec_10M_stat);
08562   } else {
08563     HTTP_INCREMENT_TRANS_STAT(http_origin_server_speed_bytes_per_sec_100M_stat);
08564   }
08565 
08566   return;
08567 }
08568 
08569 void
08570 HttpTransact::update_size_and_time_stats(State* s, ink_hrtime total_time, ink_hrtime user_agent_write_time,
08571                                          ink_hrtime origin_server_read_time, int user_agent_request_header_size,
08572                                          int64_t user_agent_request_body_size, int user_agent_response_header_size,
08573                                          int64_t user_agent_response_body_size, int origin_server_request_header_size,
08574                                          int64_t origin_server_request_body_size, int origin_server_response_header_size,
08575                                          int64_t origin_server_response_body_size, int pushed_response_header_size,
08576                                          int64_t pushed_response_body_size)
08577 {
08578   int64_t user_agent_request_size = user_agent_request_header_size + user_agent_request_body_size;
08579   int64_t user_agent_response_size = user_agent_response_header_size + user_agent_response_body_size;
08580   int64_t user_agent_bytes = user_agent_request_size + user_agent_response_size;
08581 
08582   int64_t origin_server_request_size = origin_server_request_header_size + origin_server_request_body_size;
08583   int64_t origin_server_response_size = origin_server_response_header_size + origin_server_response_body_size;
08584   int64_t origin_server_bytes = origin_server_request_size + origin_server_response_size;
08585 
08586   // Background fill stats
08587   switch (s->state_machine->background_fill) {
08588   case BACKGROUND_FILL_COMPLETED:
08589     {
08590       int64_t bg_size = origin_server_response_body_size - user_agent_response_body_size;
08591       bg_size = max((int64_t)0, bg_size);
08592       HTTP_SUM_TRANS_STAT(http_background_fill_bytes_completed_stat, bg_size);
08593       break;
08594     }
08595   case BACKGROUND_FILL_ABORTED:
08596     {
08597       int64_t bg_size = origin_server_response_body_size - user_agent_response_body_size;
08598 
08599       if (bg_size < 0)
08600         bg_size = 0;
08601       HTTP_SUM_TRANS_STAT(http_background_fill_bytes_aborted_stat, bg_size);
08602       break;
08603     }
08604   case BACKGROUND_FILL_NONE:
08605     break;
08606   case BACKGROUND_FILL_STARTED:
08607   default:
08608     ink_assert(0);
08609   }
08610 
08611   // Bandwidth Savings
08612   switch (s->squid_codes.log_code) {
08613   case SQUID_LOG_TCP_HIT:
08614   case SQUID_LOG_TCP_MEM_HIT:
08615     // It's possible to have two stat's instead of one, if needed.
08616     HTTP_INCREMENT_TRANS_STAT(http_tcp_hit_count_stat);
08617     HTTP_SUM_TRANS_STAT(http_tcp_hit_user_agent_bytes_stat, user_agent_bytes);
08618     HTTP_SUM_TRANS_STAT(http_tcp_hit_origin_server_bytes_stat, origin_server_bytes);
08619     break;
08620   case SQUID_LOG_TCP_MISS:
08621     HTTP_INCREMENT_TRANS_STAT(http_tcp_miss_count_stat);
08622     HTTP_SUM_TRANS_STAT(http_tcp_miss_user_agent_bytes_stat, user_agent_bytes);
08623     HTTP_SUM_TRANS_STAT(http_tcp_miss_origin_server_bytes_stat, origin_server_bytes);
08624     break;
08625   case SQUID_LOG_TCP_EXPIRED_MISS:
08626     HTTP_INCREMENT_TRANS_STAT(http_tcp_expired_miss_count_stat);
08627     HTTP_SUM_TRANS_STAT(http_tcp_expired_miss_user_agent_bytes_stat, user_agent_bytes);
08628     HTTP_SUM_TRANS_STAT(http_tcp_expired_miss_origin_server_bytes_stat, origin_server_bytes);
08629     break;
08630   case SQUID_LOG_TCP_REFRESH_HIT:
08631     HTTP_INCREMENT_TRANS_STAT(http_tcp_refresh_hit_count_stat);
08632     HTTP_SUM_TRANS_STAT(http_tcp_refresh_hit_user_agent_bytes_stat, user_agent_bytes);
08633     HTTP_SUM_TRANS_STAT(http_tcp_refresh_hit_origin_server_bytes_stat, origin_server_bytes);
08634     break;
08635   case SQUID_LOG_TCP_REFRESH_MISS:
08636     HTTP_INCREMENT_TRANS_STAT(http_tcp_refresh_miss_count_stat);
08637     HTTP_SUM_TRANS_STAT(http_tcp_refresh_miss_user_agent_bytes_stat, user_agent_bytes);
08638     HTTP_SUM_TRANS_STAT(http_tcp_refresh_miss_origin_server_bytes_stat, origin_server_bytes);
08639     break;
08640   case SQUID_LOG_TCP_CLIENT_REFRESH:
08641     HTTP_INCREMENT_TRANS_STAT(http_tcp_client_refresh_count_stat);
08642     HTTP_SUM_TRANS_STAT(http_tcp_client_refresh_user_agent_bytes_stat, user_agent_bytes);
08643     HTTP_SUM_TRANS_STAT(http_tcp_client_refresh_origin_server_bytes_stat, origin_server_bytes);
08644     break;
08645   case SQUID_LOG_TCP_IMS_HIT:
08646     HTTP_INCREMENT_TRANS_STAT(http_tcp_ims_hit_count_stat);
08647     HTTP_SUM_TRANS_STAT(http_tcp_ims_hit_user_agent_bytes_stat, user_agent_bytes);
08648     HTTP_SUM_TRANS_STAT(http_tcp_ims_hit_origin_server_bytes_stat, origin_server_bytes);
08649     break;
08650   case SQUID_LOG_TCP_IMS_MISS:
08651     HTTP_INCREMENT_TRANS_STAT(http_tcp_ims_miss_count_stat);
08652     HTTP_SUM_TRANS_STAT(http_tcp_ims_miss_user_agent_bytes_stat, user_agent_bytes);
08653     HTTP_SUM_TRANS_STAT(http_tcp_ims_miss_origin_server_bytes_stat, origin_server_bytes);
08654     break;
08655   case SQUID_LOG_ERR_CLIENT_ABORT:
08656     HTTP_INCREMENT_TRANS_STAT(http_err_client_abort_count_stat);
08657     HTTP_SUM_TRANS_STAT(http_err_client_abort_user_agent_bytes_stat, user_agent_bytes);
08658     HTTP_SUM_TRANS_STAT(http_err_client_abort_origin_server_bytes_stat, origin_server_bytes);
08659     break;
08660   case SQUID_LOG_ERR_CONNECT_FAIL:
08661     HTTP_INCREMENT_TRANS_STAT(http_err_connect_fail_count_stat);
08662     HTTP_SUM_TRANS_STAT(http_err_connect_fail_user_agent_bytes_stat, user_agent_bytes);
08663     HTTP_SUM_TRANS_STAT(http_err_connect_fail_origin_server_bytes_stat, origin_server_bytes);
08664     break;
08665   default:
08666     HTTP_INCREMENT_TRANS_STAT(http_misc_count_stat);
08667     HTTP_SUM_TRANS_STAT(http_misc_user_agent_bytes_stat, user_agent_bytes);
08668     HTTP_SUM_TRANS_STAT(http_misc_origin_server_bytes_stat, origin_server_bytes);
08669     break;
08670   }
08671 
08672   // times
08673   HTTP_SUM_TRANS_STAT(http_total_transactions_time_stat, total_time);
08674 
08675   // sizes
08676   HTTP_SUM_TRANS_STAT(http_user_agent_request_header_total_size_stat, user_agent_request_header_size);
08677   HTTP_SUM_TRANS_STAT(http_user_agent_response_header_total_size_stat, user_agent_response_header_size);
08678   HTTP_SUM_TRANS_STAT(http_user_agent_request_document_total_size_stat, user_agent_request_body_size);
08679   HTTP_SUM_TRANS_STAT(http_user_agent_response_document_total_size_stat, user_agent_response_body_size);
08680 
08681   // proxy stats
08682   if (s->current.request_to == HttpTransact::PARENT_PROXY) {
08683     HTTP_SUM_TRANS_STAT(http_parent_proxy_request_total_bytes_stat,
08684                         origin_server_request_header_size + origin_server_request_body_size);
08685     HTTP_SUM_TRANS_STAT(http_parent_proxy_response_total_bytes_stat,
08686                         origin_server_response_header_size + origin_server_response_body_size);
08687     HTTP_SUM_TRANS_STAT(http_parent_proxy_transaction_time_stat, total_time);
08688   }
08689   // request header zero means the document was cached.
08690   // do not add to stats.
08691   if (origin_server_request_header_size > 0) {
08692     HTTP_SUM_TRANS_STAT(http_origin_server_request_header_total_size_stat, origin_server_request_header_size);
08693     HTTP_SUM_TRANS_STAT(http_origin_server_response_header_total_size_stat, origin_server_response_header_size);
08694     HTTP_SUM_TRANS_STAT(http_origin_server_request_document_total_size_stat, origin_server_request_body_size);
08695     HTTP_SUM_TRANS_STAT(http_origin_server_response_document_total_size_stat, origin_server_response_body_size);
08696   }
08697 
08698   if (s->method == HTTP_WKSIDX_PUSH) {
08699     HTTP_SUM_TRANS_STAT(http_pushed_response_header_total_size_stat, pushed_response_header_size);
08700     HTTP_SUM_TRANS_STAT(http_pushed_document_total_size_stat, pushed_response_body_size);
08701   }
08702 
08703   histogram_request_document_size(s, user_agent_request_body_size);
08704   histogram_response_document_size(s, user_agent_response_body_size);
08705 
08706   if (user_agent_write_time >= 0) {
08707     user_agent_connection_speed(s, user_agent_write_time, user_agent_response_size);
08708   }
08709 
08710   if (origin_server_request_header_size > 0 && origin_server_read_time > 0) {
08711     origin_server_connection_speed(s, origin_server_read_time, origin_server_response_size);
08712   }
08713 
08714   return;
08715 }
08716 
08717 
08718 // void HttpTransact::add_new_stat_block(State* s)
08719 //
08720 //   Adds a new stat block
08721 //
08722 void
08723 HttpTransact::add_new_stat_block(State* s)
08724 {
08725   // We keep the block around till the end of transaction
08726   //    We don't need explicitly deallocate it later since
08727   //    when the transaction is over, the arena will be destroyed
08728   ink_assert(s->current_stats->next_insert == StatBlockEntries);
08729   StatBlock *new_block = (StatBlock *) s->arena.alloc(sizeof(StatBlock));
08730   new_block->init();
08731   s->current_stats->next = new_block;
08732   s->current_stats = new_block;
08733   DebugTxn("http_trans", "Adding new large stat block");
08734 }
08735 
08736 
08737 void
08738 HttpTransact::delete_warning_value(HTTPHdr* to_warn, HTTPWarningCode warning_code)
08739 {
08740   int w_code = (int) warning_code;
08741   MIMEField *field = to_warn->field_find(MIME_FIELD_WARNING, MIME_LEN_WARNING);;
08742 
08743   // Loop over the values to see if we need to do anything
08744   if (field) {
08745     HdrCsvIter iter;
08746 
08747     int valid;
08748     int val_code;
08749 
08750     const char *value_str;
08751     int value_len;
08752 
08753     MIMEField *new_field = NULL;
08754     val_code = iter.get_first_int(field, &valid);
08755 
08756     while (valid) {
08757       if (val_code == w_code) {
08758         // Ok, found the value we're look to delete
08759         //  Look over and create a new field
08760         //  appending all elements that are not this
08761         //  value
08762         val_code = iter.get_first_int(field, &valid);
08763 
08764         while (valid) {
08765           if (val_code != warning_code) {
08766             value_str = iter.get_current(&value_len);
08767             if (new_field) {
08768               new_field->value_append(to_warn->m_heap, to_warn->m_mime, value_str, value_len, true);
08769             } else {
08770               new_field = to_warn->field_create();
08771               to_warn->field_value_set(new_field, value_str, value_len);
08772             }
08773 
08774           }
08775           val_code = iter.get_next_int(&valid);
08776         }
08777 
08778         to_warn->field_delete(MIME_FIELD_WARNING, MIME_LEN_WARNING);
08779         if (new_field) {
08780           new_field->name_set(to_warn->m_heap, to_warn->m_mime, MIME_FIELD_WARNING, MIME_LEN_WARNING);
08781           to_warn->field_attach(new_field);
08782         }
08783 
08784         return;
08785       }
08786 
08787       val_code = iter.get_next_int(&valid);
08788     }
08789   }
08790 }
08791 
08792 void
08793 HttpTransact::change_response_header_because_of_range_request(State *s, HTTPHdr * header)
08794 {
08795   MIMEField *field;
08796   char *reason_phrase;
08797 
08798   Debug("http_trans", "Partial content requested, re-calculating content-length");
08799 
08800   header->status_set(HTTP_STATUS_PARTIAL_CONTENT);
08801   reason_phrase = (char *) (http_hdr_reason_lookup(HTTP_STATUS_PARTIAL_CONTENT));
08802   header->reason_set(reason_phrase, strlen(reason_phrase));
08803 
08804   // set the right Content-Type for multiple entry Range
08805   if (s->num_range_fields > 1) {
08806     field = header->field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
08807 
08808     if (field != NULL)
08809       header->field_delete(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
08810 
08811     field = header->field_create(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
08812     field->value_append(header->m_heap, header->m_mime, range_type, sizeof(range_type) - 1);
08813 
08814     header->field_attach(field);
08815     // TODO: There's a known bug here where the Content-Length is not correct for multi-part
08816     // Range: requests.
08817     header->set_content_length(s->range_output_cl);
08818   } else {
08819     if (s->cache_info.object_read && s->cache_info.object_read->valid()) {
08820       // TODO: It's unclear under which conditions we need to update the Content-Range: header,
08821       // many times it's already set correctly before calling this. For now, always try do it
08822       // when we have the information for it available.
08823       // TODO: Also, it's unclear as to why object_read->valid() is not always true here.
08824       char numbers[RANGE_NUMBERS_LENGTH];
08825       header->field_delete(MIME_FIELD_CONTENT_RANGE, MIME_LEN_CONTENT_RANGE);
08826       field = header->field_create(MIME_FIELD_CONTENT_RANGE, MIME_LEN_CONTENT_RANGE);
08827       snprintf(numbers, sizeof(numbers), "bytes %" PRId64"-%" PRId64"/%" PRId64, s->ranges[0]._start, s->ranges[0]._end,
08828                s->cache_info.object_read->object_size_get());
08829       field->value_set(header->m_heap, header->m_mime, numbers, strlen(numbers));
08830       header->field_attach(field);
08831     }
08832     // Always update the Content-Length: header.
08833     header->set_content_length(s->range_output_cl);
08834   }
08835 }
08836 
08837 #if TS_HAS_TESTS
08838 void forceLinkRegressionHttpTransact();
08839 void forceLinkRegressionHttpTransactCaller() {
08840   forceLinkRegressionHttpTransact();
08841 }
08842 #endif

Generated by  doxygen 1.7.1