xref: /trafficserver/proxy/http/HttpTransact.cc (revision 504cc9fc)
1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #include "tscore/ink_platform.h"
25 
26 #include <strings.h>
27 #include <cmath>
28 
29 #include "HttpTransact.h"
30 #include "HttpTransactHeaders.h"
31 #include "HttpSM.h"
32 #include "HttpCacheSM.h" //Added to get the scope of HttpCacheSM object - YTS Team, yamsat
33 #include "HttpDebugNames.h"
34 #include <ctime>
35 #include "tscore/ParseRules.h"
36 #include "tscore/Filenames.h"
37 #include "HTTP.h"
38 #include "HdrUtils.h"
39 #include "logging/Log.h"
40 #include "logging/LogUtils.h"
41 #include "CacheControl.h"
42 #include "ControlMatcher.h"
43 #include "ReverseProxy.h"
44 #include "HttpBodyFactory.h"
45 #include "StatPages.h"
46 #include "../IPAllow.h"
47 #include "I_Machine.h"
48 
49 static char range_type[] = "multipart/byteranges; boundary=RANGE_SEPARATOR";
50 #define RANGE_NUMBERS_LENGTH 60
51 
52 #define TRANSACT_SETUP_RETURN(n, r) \
53   s->next_action           = n;     \
54   s->transact_return_point = r;     \
55   SpecificDebug((s->state_machine && s->state_machine->debug_on), "http_trans", "Next action %s; %s", #n, #r);
56 
57 #define TRANSACT_RETURN(n, r) \
58   TRANSACT_SETUP_RETURN(n, r) \
59   return;
60 
61 #define TRANSACT_RETURN_VAL(n, r, v) \
62   TRANSACT_SETUP_RETURN(n, r)        \
63   return v;
64 
65 #define SET_UNPREPARE_CACHE_ACTION(C)                               \
66   {                                                                 \
67     if (C.action == HttpTransact::CACHE_PREPARE_TO_DELETE) {        \
68       C.action = HttpTransact::CACHE_DO_DELETE;                     \
69     } else if (C.action == HttpTransact::CACHE_PREPARE_TO_UPDATE) { \
70       C.action = HttpTransact::CACHE_DO_UPDATE;                     \
71     } else {                                                        \
72       C.action = HttpTransact::CACHE_DO_WRITE;                      \
73     }                                                               \
74   }
75 
76 #define TxnDebug(tag, fmt, ...) \
77   SpecificDebug((s->state_machine->debug_on), tag, "[%" PRId64 "] " fmt, s->state_machine->sm_id, ##__VA_ARGS__)
78 
79 extern HttpBodyFactory *body_factory;
80 
81 // wrapper to choose between a remap next hop strategy or use parent.config
82 // remap next hop strategy is preferred
83 inline static bool
bypass_ok(HttpTransact::State * s)84 bypass_ok(HttpTransact::State *s)
85 {
86   bool r          = false;
87   url_mapping *mp = s->url_map.getMapping();
88 
89   if (mp && mp->strategy) {
90     // remap strategies do not support the TSHttpTxnParentProxySet API.
91     r = mp->strategy->go_direct;
92   } else if (s->parent_params) {
93     r = s->parent_result.bypass_ok();
94   }
95   return r;
96 }
97 
98 // wrapper to choose between a remap next hop strategy or use parent.config
99 // remap next hop strategy is preferred
100 inline static bool
is_api_result(HttpTransact::State * s)101 is_api_result(HttpTransact::State *s)
102 {
103   bool r          = false;
104   url_mapping *mp = s->url_map.getMapping();
105 
106   if (mp && mp->strategy) {
107     // remap strategies do not support the TSHttpTxnParentProxySet API.
108     r = false;
109   } else if (s->parent_params) {
110     r = s->parent_result.is_api_result();
111   }
112   return r;
113 }
114 
115 // wrapper to choose between a remap next hop strategy or use parent.config
116 // remap next hop strategy is preferred
117 inline static unsigned
max_retries(HttpTransact::State * s,ParentRetry_t method)118 max_retries(HttpTransact::State *s, ParentRetry_t method)
119 {
120   unsigned int r  = 0;
121   url_mapping *mp = s->url_map.getMapping();
122 
123   if (mp && mp->strategy) {
124     // remap strategies does not support unavailable_server_responses
125     if (method == PARENT_RETRY_SIMPLE) {
126       r = mp->strategy->max_simple_retries;
127     }
128   } else if (s->parent_params) {
129     r = s->parent_result.max_retries(method);
130   }
131   return r;
132 }
133 
134 // wrapper to choose between a remap next hop strategy or use parent.config
135 // remap next hop strategy is preferred
136 inline static uint32_t
numParents(HttpTransact::State * s)137 numParents(HttpTransact::State *s)
138 {
139   uint32_t r      = 0;
140   url_mapping *mp = s->url_map.getMapping();
141 
142   if (mp && mp->strategy) {
143     r = mp->strategy->num_parents;
144   } else if (s->parent_params) {
145     r = s->parent_params->numParents(&s->parent_result);
146   }
147   return r;
148 }
149 
150 // wrapper to choose between a remap next hop strategy or use parent.config
151 // remap next hop strategy is preferred
152 inline static bool
parent_is_proxy(HttpTransact::State * s)153 parent_is_proxy(HttpTransact::State *s)
154 {
155   bool r          = false;
156   url_mapping *mp = s->url_map.getMapping();
157 
158   if (mp && mp->strategy) {
159     r = mp->strategy->parent_is_proxy;
160   } else if (s->parent_params) {
161     r = s->parent_result.parent_is_proxy();
162   }
163   return r;
164 }
165 
166 // wrapper to choose between a remap next hop strategy or use parent.config
167 // remap next hop strategy is preferred
168 inline static bool
response_is_retryable(HttpTransact::State * s,HTTPStatus response_code)169 response_is_retryable(HttpTransact::State *s, HTTPStatus response_code)
170 {
171   bool r          = false;
172   url_mapping *mp = s->url_map.getMapping();
173 
174   if (mp && mp->strategy) {
175     if (mp->strategy->resp_codes.codes.size() > 0) {
176       r = mp->strategy->resp_codes.contains(response_code);
177     }
178   } else if (s->parent_params) {
179     r = s->parent_result.response_is_retryable(response_code);
180   }
181   return r;
182 }
183 
184 // wrapper to choose between a remap next hop strategy or use parent.config
185 // remap next hop strategy is preferred
186 inline static unsigned
retry_type(HttpTransact::State * s)187 retry_type(HttpTransact::State *s)
188 {
189   unsigned r      = PARENT_RETRY_NONE;
190   url_mapping *mp = s->url_map.getMapping();
191 
192   if (mp && mp->strategy) {
193     if (mp->strategy->resp_codes.codes.size() > 0) {
194       r = PARENT_RETRY_SIMPLE;
195     }
196   } else if (s->parent_params) {
197     r = s->parent_result.retry_type();
198   }
199   return r;
200 }
201 
202 // wrapper to choose between a remap next hop strategy or use parent.config
203 // remap next hop strategy is preferred
204 inline static void
findParent(HttpTransact::State * s)205 findParent(HttpTransact::State *s)
206 {
207   url_mapping *mp = s->url_map.getMapping();
208 
209   if (mp && mp->strategy) {
210     return mp->strategy->findNextHop(s->state_machine->sm_id, s->parent_result, s->request_data, s->txn_conf->parent_fail_threshold,
211                                      s->txn_conf->parent_retry_time);
212   } else if (s->parent_params) {
213     return s->parent_params->findParent(&s->request_data, &s->parent_result, s->txn_conf->parent_fail_threshold,
214                                         s->txn_conf->parent_retry_time);
215   }
216 }
217 
218 // wrapper to choose between a remap next hop strategy or use parent.config
219 // remap next hop strategy is preferred
220 inline static void
markParentDown(HttpTransact::State * s)221 markParentDown(HttpTransact::State *s)
222 {
223   url_mapping *mp = s->url_map.getMapping();
224 
225   if (mp && mp->strategy) {
226     return mp->strategy->markNextHopDown(s->state_machine->sm_id, s->parent_result, s->txn_conf->parent_fail_threshold,
227                                          s->txn_conf->parent_retry_time);
228   } else if (s->parent_params) {
229     return s->parent_params->markParentDown(&s->parent_result, s->txn_conf->parent_fail_threshold, s->txn_conf->parent_retry_time);
230   }
231 }
232 
233 // wrapper to choose between a remap next hop strategy or use parent.config
234 // remap next hop strategy is preferred
235 inline static void
markParentUp(HttpTransact::State * s)236 markParentUp(HttpTransact::State *s)
237 {
238   url_mapping *mp = s->url_map.getMapping();
239   if (mp && mp->strategy) {
240     return mp->strategy->markNextHopUp(s->state_machine->sm_id, s->parent_result);
241   } else if (s->parent_params) {
242     return s->parent_params->markParentUp(&s->parent_result);
243   }
244 }
245 
246 // wrapper to choose between a remap next hop strategy or use parent.config
247 // remap next hop strategy is preferred
248 inline static bool
parentExists(HttpTransact::State * s)249 parentExists(HttpTransact::State *s)
250 {
251   url_mapping *mp = s->url_map.getMapping();
252   if (mp && mp->strategy) {
253     return mp->strategy->nextHopExists(s->state_machine->sm_id);
254   } else if (s->parent_params) {
255     return s->parent_params->parentExists(&s->request_data);
256   }
257   return false;
258 }
259 
260 // wrapper to choose between a remap next hop strategy or use parent.config
261 // remap next hop strategy is preferred
262 inline static void
nextParent(HttpTransact::State * s)263 nextParent(HttpTransact::State *s)
264 {
265   url_mapping *mp = s->url_map.getMapping();
266   if (mp && mp->strategy) {
267     // NextHop only has a findNextHop() function.
268     return mp->strategy->findNextHop(s->state_machine->sm_id, s->parent_result, s->request_data, s->txn_conf->parent_fail_threshold,
269                                      s->txn_conf->parent_retry_time);
270   } else if (s->parent_params) {
271     return s->parent_params->nextParent(&s->request_data, &s->parent_result, s->txn_conf->parent_fail_threshold,
272                                         s->txn_conf->parent_retry_time);
273   }
274 }
275 
276 inline static bool
is_localhost(const char * name,int len)277 is_localhost(const char *name, int len)
278 {
279   static const char local[] = "127.0.0.1";
280   return (len == (sizeof(local) - 1)) && (memcmp(name, local, len) == 0);
281 }
282 
283 inline static void
simple_or_unavailable_server_retry(HttpTransact::State * s)284 simple_or_unavailable_server_retry(HttpTransact::State *s)
285 {
286   // server response.
287   HTTPStatus server_response = http_hdr_status_get(s->hdr_info.server_response.m_http);
288 
289   TxnDebug("http_trans", "[simple_or_unavailabe_server_retry] server_response = %d, simple_retry_attempts: %d, numParents:%d ",
290            server_response, s->current.simple_retry_attempts, numParents(s));
291 
292   // simple retry is enabled, 0x1
293   if ((retry_type(s) & PARENT_RETRY_SIMPLE) && s->current.simple_retry_attempts < max_retries(s, PARENT_RETRY_SIMPLE) &&
294       server_response == HTTP_STATUS_NOT_FOUND) {
295     TxnDebug("http_trans", "RECEIVED A SIMPLE RETRY RESPONSE");
296     if (s->current.simple_retry_attempts < numParents(s)) {
297       s->current.state      = HttpTransact::PARENT_RETRY;
298       s->current.retry_type = PARENT_RETRY_SIMPLE;
299       return;
300     } else {
301       TxnDebug("http_trans", "PARENT_RETRY_SIMPLE: retried all parents, send response to client.");
302       return;
303     }
304   }
305   // unavailable server retry is enabled 0x2
306   else if ((retry_type(s) & PARENT_RETRY_UNAVAILABLE_SERVER) &&
307            s->current.unavailable_server_retry_attempts < max_retries(s, PARENT_RETRY_UNAVAILABLE_SERVER) &&
308            response_is_retryable(s, server_response)) {
309     TxnDebug("parent_select", "RECEIVED A PARENT_RETRY_UNAVAILABLE_SERVER RESPONSE");
310     if (s->current.unavailable_server_retry_attempts < numParents(s)) {
311       s->current.state      = HttpTransact::PARENT_RETRY;
312       s->current.retry_type = PARENT_RETRY_UNAVAILABLE_SERVER;
313       return;
314     } else {
315       TxnDebug("http_trans", "PARENT_RETRY_UNAVAILABLE_SERVER: retried all parents, send error to client.");
316       return;
317     }
318   }
319 }
320 
321 inline static bool
is_request_conditional(HTTPHdr * header)322 is_request_conditional(HTTPHdr *header)
323 {
324   uint64_t mask = (MIME_PRESENCE_IF_UNMODIFIED_SINCE | MIME_PRESENCE_IF_MODIFIED_SINCE | MIME_PRESENCE_IF_RANGE |
325                    MIME_PRESENCE_IF_MATCH | MIME_PRESENCE_IF_NONE_MATCH);
326   return (header->presence(mask) &&
327           (header->method_get_wksidx() == HTTP_WKSIDX_GET || header->method_get_wksidx() == HTTP_WKSIDX_HEAD));
328 }
329 
330 static inline bool
is_port_in_range(int port,HttpConfigPortRange * pr)331 is_port_in_range(int port, HttpConfigPortRange *pr)
332 {
333   while (pr) {
334     if (pr->low == -1) {
335       return true;
336     } else if ((pr->low <= port) && (pr->high >= port)) {
337       return true;
338     }
339 
340     pr = pr->next;
341   }
342 
343   return false;
344 }
345 
346 inline static void
update_cache_control_information_from_config(HttpTransact::State * s)347 update_cache_control_information_from_config(HttpTransact::State *s)
348 {
349   getCacheControl(&s->cache_control, &s->request_data, s->txn_conf);
350 
351   s->cache_info.directives.does_config_permit_lookup &= (s->cache_control.never_cache == false);
352   s->cache_info.directives.does_config_permit_storing &= (s->cache_control.never_cache == false);
353 
354   s->cache_info.directives.does_client_permit_storing =
355     HttpTransact::does_client_request_permit_storing(&s->cache_control, &s->hdr_info.client_request);
356 
357   s->cache_info.directives.does_client_permit_lookup = HttpTransact::does_client_request_permit_cached_response(
358     s->txn_conf, &s->cache_control, &s->hdr_info.client_request, s->via_string);
359 
360   s->cache_info.directives.does_client_permit_dns_storing =
361     HttpTransact::does_client_request_permit_dns_caching(&s->cache_control, &s->hdr_info.client_request);
362 
363   if (s->client_info.http_version == HTTPVersion(0, 9)) {
364     s->cache_info.directives.does_client_permit_lookup  = false;
365     s->cache_info.directives.does_client_permit_storing = false;
366   }
367 
368   // Less than 0 means it wasn't overridden, so leave it alone.
369   if (s->cache_control.cache_responses_to_cookies >= 0) {
370     s->my_txn_conf().cache_responses_to_cookies = s->cache_control.cache_responses_to_cookies;
371   }
372 }
373 
374 inline bool
is_server_negative_cached(State * s)375 HttpTransact::is_server_negative_cached(State *s)
376 {
377   if (s->host_db_info.app.http_data.last_failure != 0 &&
378       s->host_db_info.app.http_data.last_failure + s->txn_conf->down_server_timeout > s->client_request_time) {
379     return true;
380   } else {
381     // Make sure some nasty clock skew has not happened
382     //  Use the server timeout to set an upperbound as to how far in the
383     //   future we should tolerate bogus last failure times.  This sets
384     //   the upper bound to the time that we would ever consider a server
385     //   down to 2*down_server_timeout
386     if (s->client_request_time + s->txn_conf->down_server_timeout < s->host_db_info.app.http_data.last_failure) {
387       s->host_db_info.app.http_data.last_failure = 0;
388       ink_assert(!"extreme clock skew");
389       return true;
390     }
391     return false;
392   }
393 }
394 
395 inline static void
update_current_info(HttpTransact::CurrentInfo * into,HttpTransact::ConnectionAttributes * from,HttpTransact::LookingUp_t who,int attempts)396 update_current_info(HttpTransact::CurrentInfo *into, HttpTransact::ConnectionAttributes *from, HttpTransact::LookingUp_t who,
397                     int attempts)
398 {
399   into->request_to = who;
400   into->server     = from;
401   into->attempts   = attempts;
402 }
403 
404 inline static void
update_dns_info(HttpTransact::DNSLookupInfo * dns,HttpTransact::CurrentInfo * from,int attempts,Arena *)405 update_dns_info(HttpTransact::DNSLookupInfo *dns, HttpTransact::CurrentInfo *from, int attempts, Arena * /* arena ATS_UNUSED */)
406 {
407   dns->looking_up  = from->request_to;
408   dns->lookup_name = from->server->name;
409   dns->attempts    = attempts;
410 }
411 
412 inline static HTTPHdr *
find_appropriate_cached_resp(HttpTransact::State * s)413 find_appropriate_cached_resp(HttpTransact::State *s)
414 {
415   HTTPHdr *c_resp = nullptr;
416 
417   if (s->cache_info.object_store.valid()) {
418     c_resp = s->cache_info.object_store.response_get();
419     if (c_resp != nullptr && c_resp->valid()) {
420       return c_resp;
421     }
422   }
423 
424   ink_assert(s->cache_info.object_read != nullptr);
425   return s->cache_info.object_read->response_get();
426 }
427 
428 int response_cacheable_indicated_by_cc(HTTPHdr *response);
429 
430 inline static bool
is_negative_caching_appropriate(HttpTransact::State * s)431 is_negative_caching_appropriate(HttpTransact::State *s)
432 {
433   if (!s->txn_conf->negative_caching_enabled || !s->hdr_info.server_response.valid()) {
434     return false;
435   }
436 
437   int status  = s->hdr_info.server_response.status_get();
438   auto params = s->http_config_param;
439   if (params->negative_caching_list[status]) {
440     TxnDebug("http_trans", "%d is eligible for negative caching", status);
441     return true;
442   } else {
443     TxnDebug("http_trans", "%d is NOT eligible for negative caching", status);
444     return false;
445   }
446 }
447 
448 inline static HttpTransact::LookingUp_t
find_server_and_update_current_info(HttpTransact::State * s)449 find_server_and_update_current_info(HttpTransact::State *s)
450 {
451   int host_len;
452   const char *host = s->hdr_info.client_request.host_get(&host_len);
453 
454   if (is_localhost(host, host_len)) {
455     // Do not forward requests to local_host onto a parent.
456     // I just wanted to do this for cop heartbeats, someone else
457     // wanted it for all requests to local_host.
458     TxnDebug("http_trans", "request is from localhost, so bypass parent");
459     s->parent_result.result = PARENT_DIRECT;
460   } else if (s->method == HTTP_WKSIDX_CONNECT && s->http_config_param->disable_ssl_parenting) {
461     if (s->parent_result.result == PARENT_SPECIFIED) {
462       nextParent(s);
463     } else {
464       findParent(s);
465     }
466     if (!s->parent_result.is_some() || is_api_result(s) || parent_is_proxy(s)) {
467       TxnDebug("http_trans", "request not cacheable, so bypass parent");
468       s->parent_result.result = PARENT_DIRECT;
469     }
470   } else if (s->txn_conf->uncacheable_requests_bypass_parent && s->http_config_param->no_dns_forward_to_parent == 0 &&
471              !HttpTransact::is_request_cache_lookupable(s)) {
472     // request not lookupable and cacheable, so bypass parent if the parent is not an origin server.
473     // Note that the configuration of the proxy as well as the request
474     // itself affects the result of is_request_cache_lookupable();
475     // we are assuming both child and parent have similar configuration
476     // with respect to whether a request is cacheable or not.
477     // For example, the cache_urls_that_look_dynamic variable.
478     if (s->parent_result.result == PARENT_SPECIFIED) {
479       nextParent(s);
480     } else {
481       findParent(s);
482     }
483     if (!s->parent_result.is_some() || is_api_result(s) || parent_is_proxy(s)) {
484       TxnDebug("http_trans", "request not cacheable, so bypass parent");
485       s->parent_result.result = PARENT_DIRECT;
486     }
487   } else {
488     switch (s->parent_result.result) {
489     case PARENT_UNDEFINED:
490       findParent(s);
491       break;
492     case PARENT_SPECIFIED:
493       nextParent(s);
494 
495       // Hack!
496       // We already have a parent that failed, if we are now told
497       //  to go the origin server, we can only obey this if we
498       //  dns'ed the origin server
499       if (s->parent_result.result == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 0) {
500         ink_assert(!s->server_info.dst_addr.isValid());
501         s->parent_result.result = PARENT_FAIL;
502       }
503       break;
504     case PARENT_FAIL:
505       // Check to see if should bypass the parent and go direct
506       //   We can only do this if
507       //   1) the config permitted us to dns the origin server
508       //   2) the config permits us
509       //   3) the parent was not set from API
510       if (s->http_config_param->no_dns_forward_to_parent == 0 && bypass_ok(s) && parent_is_proxy(s) &&
511           !s->parent_params->apiParentExists(&s->request_data)) {
512         s->parent_result.result = PARENT_DIRECT;
513       }
514       break;
515     default:
516       ink_assert(0);
517     // FALL THROUGH
518     case PARENT_DIRECT:
519       //              // if we have already decided to go direct
520       //              // dont bother calling nextParent.
521       //              // do nothing here, guy.
522       break;
523     }
524   }
525 
526   switch (s->parent_result.result) {
527   case PARENT_SPECIFIED:
528     s->parent_info.name = s->arena.str_store(s->parent_result.hostname, strlen(s->parent_result.hostname));
529     update_current_info(&s->current, &s->parent_info, HttpTransact::PARENT_PROXY, (s->current.attempts)++);
530     update_dns_info(&s->dns_info, &s->current, 0, &s->arena);
531     ink_assert(s->dns_info.looking_up == HttpTransact::PARENT_PROXY);
532     s->next_hop_scheme = URL_WKSIDX_HTTP;
533 
534     return HttpTransact::PARENT_PROXY;
535   case PARENT_FAIL:
536     // No more parents - need to return an error message
537     s->current.request_to = HttpTransact::HOST_NONE;
538     return HttpTransact::HOST_NONE;
539 
540   case PARENT_DIRECT:
541   /* fall through */
542   default:
543     update_current_info(&s->current, &s->server_info, HttpTransact::ORIGIN_SERVER, (s->current.attempts)++);
544     update_dns_info(&s->dns_info, &s->current, 0, &s->arena);
545     ink_assert(s->dns_info.looking_up == HttpTransact::ORIGIN_SERVER);
546     s->next_hop_scheme = s->scheme;
547     return HttpTransact::ORIGIN_SERVER;
548   }
549 }
550 
551 inline static bool
do_cookies_prevent_caching(int cookies_conf,HTTPHdr * request,HTTPHdr * response,HTTPHdr * cached_request=nullptr)552 do_cookies_prevent_caching(int cookies_conf, HTTPHdr *request, HTTPHdr *response, HTTPHdr *cached_request = nullptr)
553 {
554   enum CookiesConfig {
555     COOKIES_CACHE_NONE             = 0, // do not cache any responses to cookies
556     COOKIES_CACHE_ALL              = 1, // cache for any content-type (ignore cookies)
557     COOKIES_CACHE_IMAGES           = 2, // cache only for image types
558     COOKIES_CACHE_ALL_BUT_TEXT     = 3, // cache for all but text content-types
559     COOKIES_CACHE_ALL_BUT_TEXT_EXT = 4  // cache for all but text content-types except with OS response
560                                         // without "Set-Cookie" or with "Cache-Control: public"
561   };
562 
563   const char *content_type = nullptr;
564   int str_len;
565 
566 #ifdef DEBUG
567   ink_assert(request->type_get() == HTTP_TYPE_REQUEST);
568   ink_assert(response->type_get() == HTTP_TYPE_RESPONSE);
569   if (cached_request) {
570     ink_assert(cached_request->type_get() == HTTP_TYPE_REQUEST);
571   }
572 #endif
573 
574   // Can cache all regardless of cookie header - just ignore all cookie headers
575   if (static_cast<CookiesConfig>(cookies_conf) == COOKIES_CACHE_ALL) {
576     return false;
577   }
578 
579   // It is considered that Set-Cookie headers can be safely ignored
580   // for non text content types if Cache-Control private is not set.
581   // This enables a bigger hit rate, which currently outweighs the risk of
582   // breaking origin servers that truly intend to set a cookie with other
583   // objects such as images.
584   // At this time, it is believed that only advertisers do this, and that
585   // customers won't care about it.
586 
587   // If the response does not have a Set-Cookie header and
588   // the response does not have a Cookie header and
589   // the object is not cached or the request does not have a Cookie header
590   // then cookies do not prevent caching.
591   if (!response->presence(MIME_PRESENCE_SET_COOKIE) && !request->presence(MIME_PRESENCE_COOKIE) &&
592       (cached_request == nullptr || !cached_request->presence(MIME_PRESENCE_COOKIE))) {
593     return false;
594   }
595 
596   // Do not cache if cookies option is COOKIES_CACHE_NONE
597   // and a Cookie is detected
598   if (static_cast<CookiesConfig>(cookies_conf) == COOKIES_CACHE_NONE) {
599     return true;
600   }
601   // All other options depend on the Content-Type
602   content_type = response->value_get(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE, &str_len);
603 
604   if (static_cast<CookiesConfig>(cookies_conf) == COOKIES_CACHE_IMAGES) {
605     if (content_type && str_len >= 5 && memcmp(content_type, "image", 5) == 0) {
606       // Images can be cached
607       return false;
608     }
609     return true; // do not cache if  COOKIES_CACHE_IMAGES && content_type != "image"
610   }
611   // COOKIES_CACHE_ALL_BUT_TEXT || COOKIES_CACHE_ALL_BUT_TEXT_EXT
612   // Note: if the configuration is bad, we consider
613   // COOKIES_CACHE_ALL_BUT_TEXT to be the default
614 
615   if (content_type && str_len >= 4 && memcmp(content_type, "text", 4) == 0) { // content type  - "text"
616     // Text objects cannot be cached unless the option is
617     // COOKIES_CACHE_ALL_BUT_TEXT_EXT.
618     // Furthermore, if there is a Set-Cookie header, then
619     // Cache-Control must be set.
620     if (static_cast<CookiesConfig>(cookies_conf) == COOKIES_CACHE_ALL_BUT_TEXT_EXT &&
621         ((!response->presence(MIME_PRESENCE_SET_COOKIE)) || response->is_cache_control_set(HTTP_VALUE_PUBLIC))) {
622       return false;
623     }
624     return true;
625   }
626   return false; // Non text objects can be cached
627 }
628 
629 inline static bool
does_method_require_cache_copy_deletion(const HttpConfigParams * http_config_param,const int method)630 does_method_require_cache_copy_deletion(const HttpConfigParams *http_config_param, const int method)
631 {
632   return ((method != HTTP_WKSIDX_GET) &&
633           (method == HTTP_WKSIDX_DELETE || method == HTTP_WKSIDX_PURGE || method == HTTP_WKSIDX_PUT ||
634            (http_config_param->cache_post_method != 1 && method == HTTP_WKSIDX_POST)));
635 }
636 
637 inline static bool
does_method_effect_cache(int method)638 does_method_effect_cache(int method)
639 {
640   return ((method == HTTP_WKSIDX_GET || method == HTTP_WKSIDX_DELETE || method == HTTP_WKSIDX_PURGE || method == HTTP_WKSIDX_PUT ||
641            method == HTTP_WKSIDX_POST));
642 }
643 
644 inline static HttpTransact::StateMachineAction_t
how_to_open_connection(HttpTransact::State * s)645 how_to_open_connection(HttpTransact::State *s)
646 {
647   ink_assert((s->pending_work == nullptr) || (s->current.request_to == HttpTransact::PARENT_PROXY));
648 
649   // Originally we returned which type of server to open
650   // Now, however, we may want to issue a cache
651   // operation first in order to lock the cache
652   // entry to prevent multiple origin server requests
653   // for the same document.
654   // The cache operation that we actually issue, of
655   // course, depends on the specified "cache_action".
656   // If there is no cache-action to be issued, just
657   // connect to the server.
658   switch (s->cache_info.action) {
659   case HttpTransact::CACHE_PREPARE_TO_DELETE:
660   case HttpTransact::CACHE_PREPARE_TO_UPDATE:
661   case HttpTransact::CACHE_PREPARE_TO_WRITE:
662     s->transact_return_point = HttpTransact::handle_cache_write_lock;
663     return HttpTransact::SM_ACTION_CACHE_ISSUE_WRITE;
664   default:
665     // This covers:
666     // CACHE_DO_UNDEFINED, CACHE_DO_NO_ACTION, CACHE_DO_DELETE,
667     // CACHE_DO_LOOKUP, CACHE_DO_REPLACE, CACHE_DO_SERVE,
668     // CACHE_DO_SERVE_AND_DELETE, CACHE_DO_SERVE_AND_UPDATE,
669     // CACHE_DO_UPDATE, CACHE_DO_WRITE, TOTAL_CACHE_ACTION_TYPES
670     break;
671   }
672 
673   s->cdn_saved_next_action = HttpTransact::SM_ACTION_ORIGIN_SERVER_OPEN;
674 
675   // Setting up a direct CONNECT tunnel enters OriginServerRawOpen. We always do that if we
676   // are not forwarding CONNECT and are not going to a parent proxy.
677   if (s->method == HTTP_WKSIDX_CONNECT) {
678     if (s->txn_conf->forward_connect_method == 1 || s->parent_result.result == PARENT_SPECIFIED) {
679       s->cdn_saved_next_action = HttpTransact::SM_ACTION_ORIGIN_SERVER_OPEN;
680     } else {
681       s->cdn_saved_next_action = HttpTransact::SM_ACTION_ORIGIN_SERVER_RAW_OPEN;
682     }
683   }
684 
685   if (!s->already_downgraded) { // false unless downgraded previously (possibly due to HTTP 505)
686     (&s->hdr_info.server_request)->version_set(HTTPVersion(1, 1));
687     HttpTransactHeaders::convert_request(s->current.server->http_version, &s->hdr_info.server_request);
688   }
689 
690   ink_assert(s->cdn_saved_next_action == HttpTransact::SM_ACTION_ORIGIN_SERVER_OPEN ||
691              s->cdn_saved_next_action == HttpTransact::SM_ACTION_ORIGIN_SERVER_RAW_OPEN);
692   return s->cdn_saved_next_action;
693 }
694 
695 /*****************************************************************************
696  *****************************************************************************
697  ****                                                                     ****
698  ****                 HttpTransact State Machine Handlers                 ****
699  ****                                                                     ****
700  **** What follow from here on are the state machine handlers - the code  ****
701  **** which is called from HttpSM::set_next_state to specify              ****
702  **** what action the state machine needs to execute next. These ftns     ****
703  **** take as input just the state and set the next_action variable.      ****
704  *****************************************************************************
705  *****************************************************************************/
706 void
BadRequest(State * s)707 HttpTransact::BadRequest(State *s)
708 {
709   TxnDebug("http_trans", "[BadRequest]"
710                          "parser marked request bad");
711   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
712 
713   const char *body_factory_template = "request#syntax_error";
714   HTTPStatus status                 = HTTP_STATUS_BAD_REQUEST;
715   const char *reason                = "Invalid HTTP Request";
716 
717   switch (s->http_return_code) {
718   case HTTP_STATUS_REQUEST_URI_TOO_LONG:
719     body_factory_template = "request#uri_len_too_long";
720     status                = s->http_return_code;
721     reason                = "URI Too Long";
722     break;
723   default:
724     break;
725   }
726 
727   build_error_response(s, status, reason, body_factory_template);
728   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
729 }
730 
731 void
PostActiveTimeoutResponse(State * s)732 HttpTransact::PostActiveTimeoutResponse(State *s)
733 {
734   TxnDebug("http_trans", "[PostActiveTimeoutResponse]"
735                          "post active timeout");
736   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
737   build_error_response(s, HTTP_STATUS_REQUEST_TIMEOUT, "Active Timeout", "timeout#activity");
738   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
739 }
740 
741 void
PostInactiveTimeoutResponse(State * s)742 HttpTransact::PostInactiveTimeoutResponse(State *s)
743 {
744   TxnDebug("http_trans", "[PostInactiveTimeoutResponse]"
745                          "post inactive timeout");
746   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
747   build_error_response(s, HTTP_STATUS_REQUEST_TIMEOUT, "Inactive Timeout", "timeout#inactivity");
748   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
749 }
750 
751 void
Forbidden(State * s)752 HttpTransact::Forbidden(State *s)
753 {
754   TxnDebug("http_trans", "[Forbidden]"
755                          "IpAllow marked request forbidden");
756   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
757   build_error_response(s, HTTP_STATUS_FORBIDDEN, "Access Denied", "access#denied");
758   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
759 }
760 
761 void
TooEarly(State * s)762 HttpTransact::TooEarly(State *s)
763 {
764   TxnDebug("http_trans", "[TooEarly]"
765                          "Early Data method is not safe");
766   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
767   build_error_response(s, HTTP_STATUS_TOO_EARLY, "Too Early", "too#early");
768   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
769 }
770 
771 void
HandleBlindTunnel(State * s)772 HttpTransact::HandleBlindTunnel(State *s)
773 {
774   URL u;
775   // IpEndpoint dest_addr;
776   // ip_text_buffer new_host;
777 
778   TxnDebug("http_trans", "[HttpTransact::HandleBlindTunnel]");
779 
780   // We set the version to 0.9 because once we know where we are going
781   //   this blind ssl tunnel is indistinguishable from a "CONNECT 0.9"
782   //   except for the need to suppression error messages
783   HTTPVersion ver(0, 9);
784   s->hdr_info.client_request.version_set(ver);
785 
786   // Initialize the state vars necessary to sending error responses
787   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
788 
789   if (is_debug_tag_set("http_trans")) {
790     int host_len;
791     const char *host = s->hdr_info.client_request.url_get()->host_get(&host_len);
792     TxnDebug("http_trans", "[HandleBlindTunnel] destination set to %.*s:%d", host_len, host,
793              s->hdr_info.client_request.url_get()->port_get());
794   }
795 
796   // Set the mode to tunnel so that we don't lookup the cache
797   s->current.mode = TUNNELLING_PROXY;
798 
799   // Let the request work it's way through the code and
800   //  we grab it again after the raw connection has been opened
801   HandleRequest(s);
802 }
803 
804 void
StartRemapRequest(State * s)805 HttpTransact::StartRemapRequest(State *s)
806 {
807   if (s->api_skip_all_remapping) {
808     TxnDebug("http_trans", "API request to skip remapping");
809 
810     s->hdr_info.client_request.set_url_target_from_host_field();
811 
812     // Since we're not doing remap, we still have to allow for these overridable
813     // configurations to modify follow-redirect behavior. Someone could for example
814     // have set them in a plugin other than conf_remap running in a prior hook.
815     s->state_machine->enable_redirection = (s->txn_conf->number_of_redirections > 0);
816 
817     if (s->is_upgrade_request && s->post_remap_upgrade_return_point) {
818       TRANSACT_RETURN(SM_ACTION_POST_REMAP_SKIP, s->post_remap_upgrade_return_point);
819     }
820 
821     TRANSACT_RETURN(SM_ACTION_POST_REMAP_SKIP, HttpTransact::HandleRequest);
822   }
823 
824   TxnDebug("http_trans", "START HttpTransact::StartRemapRequest");
825 
826   //////////////////////////////////////////////////////////////////
827   // FIX: this logic seems awfully convoluted and hard to follow; //
828   //      seems like we could come up with a more elegant and     //
829   //      comprehensible design that generalized things           //
830   //////////////////////////////////////////////////////////////////
831 
832   /////////////////////////////////////////////////////////////////
833   // run the remap url-rewriting engine:                         //
834   //                                                             //
835   // * the variable <url_remap_success> is set true if           //
836   //   the url was rewritten                                     //
837   //                                                             //
838   // * the variable <remap_redirect> is set to non-NULL if there //
839   //   is a URL provided that the proxy is supposed to redirect  //
840   //   requesters of a particular URL to.                        //
841   /////////////////////////////////////////////////////////////////
842 
843   if (is_debug_tag_set("http_chdr_describe") || is_debug_tag_set("http_trans")) {
844     TxnDebug("http_trans", "Before Remapping:");
845     obj_describe(s->hdr_info.client_request.m_http, true);
846   }
847 
848   if (s->http_config_param->referer_filter_enabled) {
849     s->filter_mask = URL_REMAP_FILTER_REFERER;
850     if (s->http_config_param->referer_format_redirect) {
851       s->filter_mask |= URL_REMAP_FILTER_REDIRECT_FMT;
852     }
853   }
854 
855   TxnDebug("http_trans", "END HttpTransact::StartRemapRequest");
856 
857   TxnDebug("http_trans", "Checking if transaction wants to upgrade");
858   if (handle_upgrade_request(s)) {
859     // everything should be handled by the upgrade handler.
860     TxnDebug("http_trans", "Transaction will be upgraded by the appropriate upgrade handler.");
861     return;
862   }
863 
864   TRANSACT_RETURN(SM_ACTION_API_PRE_REMAP, HttpTransact::PerformRemap);
865 }
866 
867 void
PerformRemap(State * s)868 HttpTransact::PerformRemap(State *s)
869 {
870   TxnDebug("http_trans", "Inside PerformRemap");
871   TRANSACT_RETURN(SM_ACTION_REMAP_REQUEST, HttpTransact::EndRemapRequest);
872 }
873 
874 void
EndRemapRequest(State * s)875 HttpTransact::EndRemapRequest(State *s)
876 {
877   TxnDebug("http_trans", "START HttpTransact::EndRemapRequest");
878 
879   HTTPHdr *incoming_request = &s->hdr_info.client_request;
880   int method                = incoming_request->method_get_wksidx();
881   int host_len;
882   const char *host = incoming_request->host_get(&host_len);
883   TxnDebug("http_trans", "EndRemapRequest host is %.*s", host_len, host);
884 
885   // Setting enable_redirection according to HttpConfig (master or overridable). We
886   // defer this as late as possible, to allow plugins to modify the overridable
887   // configurations (e.g. conf_remap.so). We intentionally only modify this if
888   // the configuration says so.
889   s->state_machine->enable_redirection = (s->txn_conf->number_of_redirections > 0);
890 
891   ////////////////////////////////////////////////////////////////
892   // if we got back a URL to redirect to, vector the user there //
893   ////////////////////////////////////////////////////////////////
894   if (s->remap_redirect != nullptr) {
895     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
896     const char *error_body_type;
897     switch (s->http_return_code) {
898     case HTTP_STATUS_MOVED_PERMANENTLY:
899     case HTTP_STATUS_PERMANENT_REDIRECT:
900     case HTTP_STATUS_SEE_OTHER:
901     case HTTP_STATUS_USE_PROXY:
902       error_body_type = "redirect#moved_permanently";
903       break;
904     case HTTP_STATUS_MOVED_TEMPORARILY:
905     case HTTP_STATUS_TEMPORARY_REDIRECT:
906       error_body_type = "redirect#moved_temporarily";
907       break;
908     default:
909       if (HTTP_STATUS_NONE == s->http_return_code) {
910         s->http_return_code = HTTP_STATUS_MOVED_TEMPORARILY;
911         Warning("Changed status code from '0' to '%d'.", s->http_return_code);
912       } else {
913         Warning("Using invalid status code for redirect '%d'. Building a response for a temporary redirect.", s->http_return_code);
914       }
915       error_body_type = "redirect#moved_temporarily";
916     }
917     build_error_response(s, s->http_return_code, "Redirect", error_body_type);
918     ats_free(s->remap_redirect);
919     s->reverse_proxy = false;
920     goto done;
921   }
922   /////////////////////////////////////////////////////
923   // Quick HTTP filtering (primary key: http method) //
924   /////////////////////////////////////////////////////
925   process_quick_http_filter(s, method);
926   /////////////////////////////////////////////////////////////////////////
927   // We must close this connection if client_connection_enabled == false //
928   /////////////////////////////////////////////////////////////////////////
929   if (!s->client_connection_enabled) {
930     build_error_response(s, HTTP_STATUS_FORBIDDEN, "Access Denied", "access#denied");
931     s->reverse_proxy = false;
932     goto done;
933   }
934   /////////////////////////////////////////////////////////////////
935   // Check if remap plugin set HTTP return code and return body  //
936   /////////////////////////////////////////////////////////////////
937   if (s->http_return_code != HTTP_STATUS_NONE) {
938     build_error_response(s, s->http_return_code, nullptr, nullptr);
939     s->reverse_proxy = false;
940     goto done;
941   }
942 
943   ///////////////////////////////////////////////////////////////
944   // if no mapping was found, handle the cases where:          //
945   //                                                           //
946   // (1) reverse proxy is on, and no URL host (server request) //
947   // (2) no mappings are found, but mappings strictly required //
948   ///////////////////////////////////////////////////////////////
949 
950   if (!s->url_remap_success) {
951     /**
952      * It's better to test redirect rules just after url_remap failed
953      * Or those successfully remapped rules might be redirected
954      **/
955     if (handleIfRedirect(s)) {
956       TxnDebug("http_trans", "END HttpTransact::RemapRequest");
957       TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, nullptr);
958     }
959 
960     if (!s->http_config_param->url_remap_required && !incoming_request->is_target_in_url()) {
961       s->hdr_info.client_request.set_url_target_from_host_field();
962     }
963 
964     /////////////////////////////////////////////////////////
965     // check for: (1) reverse proxy is on, and no URL host //
966     /////////////////////////////////////////////////////////
967     if (s->http_config_param->reverse_proxy_enabled && !s->client_info.is_transparent && !incoming_request->is_target_in_url()) {
968       /////////////////////////////////////////////////////////
969       // the url mapping failed, reverse proxy was enabled,
970       // and the request contains no host:
971       //
972       // * if there is an explanatory redirect, send there.
973       // * if there was no host, send "no host" error.
974       // * if there was a host, say "not found".
975       /////////////////////////////////////////////////////////
976 
977       char *redirect_url   = s->http_config_param->reverse_proxy_no_host_redirect;
978       int redirect_url_len = s->http_config_param->reverse_proxy_no_host_redirect_len;
979 
980       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
981       if (redirect_url) { /* there is a redirect url */
982         build_error_response(s, HTTP_STATUS_MOVED_TEMPORARILY, "Redirect For Explanation", "request#no_host");
983         s->hdr_info.client_response.value_set(MIME_FIELD_LOCATION, MIME_LEN_LOCATION, redirect_url, redirect_url_len);
984         // socket when there is no host. Need to handle DNS failure elsewhere.
985       } else if (host == nullptr) { /* no host */
986         build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Host Header Required", "request#no_host");
987         s->squid_codes.log_code = SQUID_LOG_ERR_INVALID_URL;
988       } else {
989         build_error_response(s, HTTP_STATUS_NOT_FOUND, "Not Found on Accelerator", "urlrouting#no_mapping");
990         s->squid_codes.log_code = SQUID_LOG_ERR_INVALID_URL;
991       }
992       s->reverse_proxy = false;
993       goto done;
994     } else if (s->http_config_param->url_remap_required) {
995       ///////////////////////////////////////////////////////
996       // the url mapping failed, but mappings are strictly //
997       // required so return an error message.              //
998       ///////////////////////////////////////////////////////
999       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
1000       build_error_response(s, HTTP_STATUS_NOT_FOUND, "Not Found", "urlrouting#no_mapping");
1001       s->squid_codes.log_code = SQUID_LOG_ERR_INVALID_URL;
1002 
1003       s->reverse_proxy = false;
1004       goto done;
1005     }
1006   } else {
1007     if (s->http_config_param->reverse_proxy_enabled) {
1008       s->req_flavor = REQ_FLAVOR_REVPROXY;
1009     }
1010   }
1011   s->reverse_proxy              = true;
1012   s->server_info.is_transparent = s->state_machine->ua_txn ? s->state_machine->ua_txn->is_outbound_transparent() : false;
1013 
1014 done:
1015   // We now set the active-timeout again, since it might have been changed as part of the remap rules.
1016   if (s->state_machine->ua_txn) {
1017     s->state_machine->ua_txn->set_active_timeout(HRTIME_SECONDS(s->txn_conf->transaction_active_timeout_in));
1018   }
1019 
1020   if (is_debug_tag_set("http_chdr_describe") || is_debug_tag_set("http_trans") || is_debug_tag_set("url_rewrite")) {
1021     TxnDebug("http_trans", "After Remapping:");
1022     obj_describe(s->hdr_info.client_request.m_http, true);
1023   }
1024 
1025   /*
1026     if s->reverse_proxy == false, we can assume remapping failed in some way
1027       -however-
1028     If an API setup a tunnel to fake the origin or proxy's response we will
1029     continue to handle the request (as this was likely the plugin author's intent)
1030 
1031     otherwise, 502/404 the request right now. /eric
1032   */
1033   if (!s->reverse_proxy && s->state_machine->plugin_tunnel_type == HTTP_NO_PLUGIN_TUNNEL) {
1034     TxnDebug("http_trans", "END HttpTransact::EndRemapRequest");
1035     HTTP_INCREMENT_DYN_STAT(http_invalid_client_requests_stat);
1036     TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1037   } else {
1038     s->hdr_info.client_response.destroy(); // release the underlying memory.
1039     s->hdr_info.client_response.clear();   // clear the pointers.
1040     TxnDebug("http_trans", "END HttpTransact::EndRemapRequest");
1041 
1042     if (s->is_upgrade_request && s->post_remap_upgrade_return_point) {
1043       TRANSACT_RETURN(SM_ACTION_API_POST_REMAP, s->post_remap_upgrade_return_point);
1044     }
1045 
1046     TRANSACT_RETURN(SM_ACTION_API_POST_REMAP, HttpTransact::HandleRequest);
1047   }
1048 
1049   ink_assert(!"not reached");
1050 }
1051 
1052 bool
handle_upgrade_request(State * s)1053 HttpTransact::handle_upgrade_request(State *s)
1054 {
1055   HTTPHdr &request = s->hdr_info.client_request;
1056   s->method        = request.method_get_wksidx();
1057 
1058   // Quickest way to determine that this is defintely not an upgrade.
1059   /* RFC 6455 The method of the request MUST be GET, and the HTTP version MUST
1060         be at least 1.1. */
1061   if (!s->hdr_info.client_request.presence(MIME_PRESENCE_UPGRADE) ||
1062       !s->hdr_info.client_request.presence(MIME_PRESENCE_CONNECTION) || s->method != HTTP_WKSIDX_GET ||
1063       s->hdr_info.client_request.version_get() < HTTPVersion(1, 1)) {
1064     return false;
1065   }
1066 
1067   MIMEField *upgrade_hdr    = s->hdr_info.client_request.field_find(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
1068   MIMEField *connection_hdr = s->hdr_info.client_request.field_find(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
1069 
1070   StrList connection_hdr_vals;
1071   const char *upgrade_hdr_val = nullptr;
1072   int upgrade_hdr_val_len     = 0;
1073 
1074   if (!upgrade_hdr || !connection_hdr || connection_hdr->value_get_comma_list(&connection_hdr_vals) == 0 ||
1075       (upgrade_hdr_val = upgrade_hdr->value_get(&upgrade_hdr_val_len)) == nullptr) {
1076     TxnDebug("http_trans_upgrade", "Transaction wasn't a valid upgrade request, proceeding as a normal HTTP request.");
1077     return false;
1078   }
1079 
1080   /*
1081    * In order for this request to be treated as a normal upgrade request we must have a Connection: Upgrade header
1082    * and a Upgrade: header, with a non-empty value, otherwise we just assume it's not an Upgrade Request, after
1083    * we've verified that, we will try to match this upgrade to a known upgrade type such as Websockets.
1084    */
1085   bool connection_contains_upgrade = false;
1086   // Next, let's validate that the Connection header contains an Upgrade key
1087   for (int i = 0; i < connection_hdr_vals.count; ++i) {
1088     Str *val = connection_hdr_vals.get_idx(i);
1089     if (ptr_len_casecmp(val->str, val->len, MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE) == 0) {
1090       connection_contains_upgrade = true;
1091       break;
1092     }
1093   }
1094 
1095   if (!connection_contains_upgrade) {
1096     TxnDebug("http_trans_upgrade",
1097              "Transaction wasn't a valid upgrade request, proceeding as a normal HTTP request, missing Connection upgrade header.");
1098     return false;
1099   }
1100 
1101   // Mark this request as an upgrade request.
1102   s->is_upgrade_request = true;
1103 
1104   /*
1105      RFC 6455
1106      The request MUST contain an |Upgrade| header field whose value
1107         MUST include the "websocket" keyword.
1108      The request MUST contain a |Connection| header field whose value
1109         MUST include the "Upgrade" token. // Checked Above
1110      The request MUST include a header field with the name
1111         |Sec-WebSocket-Key|.
1112      The request MUST include a header field with the name
1113         |Sec-WebSocket-Version|.  The value of this header field MUST be
1114         13.
1115    */
1116   if (hdrtoken_tokenize(upgrade_hdr_val, upgrade_hdr_val_len, &s->upgrade_token_wks) >= 0) {
1117     if (s->upgrade_token_wks == MIME_VALUE_WEBSOCKET) {
1118       MIMEField *sec_websocket_key =
1119         s->hdr_info.client_request.field_find(MIME_FIELD_SEC_WEBSOCKET_KEY, MIME_LEN_SEC_WEBSOCKET_KEY);
1120       MIMEField *sec_websocket_ver =
1121         s->hdr_info.client_request.field_find(MIME_FIELD_SEC_WEBSOCKET_VERSION, MIME_LEN_SEC_WEBSOCKET_VERSION);
1122 
1123       if (sec_websocket_key && sec_websocket_ver && sec_websocket_ver->value_get_int() == 13) {
1124         TxnDebug("http_trans_upgrade", "Transaction wants upgrade to websockets");
1125         handle_websocket_upgrade_pre_remap(s);
1126         return true;
1127       } else {
1128         TxnDebug("http_trans_upgrade", "Unable to upgrade connection to websockets, invalid headers (RFC 6455).");
1129       }
1130     }
1131 
1132     // TODO accept h2c token to start HTTP/2 session after TS-3498 is fixed
1133   } else {
1134     TxnDebug("http_trans_upgrade", "Transaction requested upgrade for unknown protocol: %s", upgrade_hdr_val);
1135   }
1136 
1137   build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid Upgrade Request", "request#syntax_error");
1138 
1139   // we want our modify_request method to just return while we fail out from here.
1140   // this seems like the preferred option because the user wanted to do an upgrade but sent a bad protocol.
1141   TRANSACT_RETURN_VAL(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr, true);
1142 }
1143 
1144 void
handle_websocket_upgrade_pre_remap(State * s)1145 HttpTransact::handle_websocket_upgrade_pre_remap(State *s)
1146 {
1147   TxnDebug("http_trans_websocket_upgrade_pre_remap", "Prepping transaction before remap.");
1148 
1149   /*
1150    * We will use this opportunity to set everything up so that during the remap stage we can deal with
1151    * ws:// and wss:// remap rules, and then we will take over again post remap.
1152    */
1153   s->is_websocket                    = true;
1154   s->post_remap_upgrade_return_point = HttpTransact::handle_websocket_upgrade_post_remap;
1155 
1156   /* let's modify the url scheme to be wss or ws, so remapping will happen as expected */
1157   URL *url = s->hdr_info.client_request.url_get();
1158   if (url->scheme_get_wksidx() == URL_WKSIDX_HTTP) {
1159     TxnDebug("http_trans_websocket_upgrade_pre_remap", "Changing scheme to WS for remapping.");
1160     url->scheme_set(URL_SCHEME_WS, URL_LEN_WS);
1161   } else if (url->scheme_get_wksidx() == URL_WKSIDX_HTTPS) {
1162     TxnDebug("http_trans_websocket_upgrade_pre_remap", "Changing scheme to WSS for remapping.");
1163     url->scheme_set(URL_SCHEME_WSS, URL_LEN_WSS);
1164   } else {
1165     TxnDebug("http_trans_websocket_upgrade_pre_remap", "Invalid scheme for websocket upgrade");
1166     build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid Upgrade Request", "request#syntax_error");
1167     TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1168   }
1169 
1170   TRANSACT_RETURN(SM_ACTION_API_PRE_REMAP, HttpTransact::PerformRemap);
1171 }
1172 
1173 void
handle_websocket_upgrade_post_remap(State * s)1174 HttpTransact::handle_websocket_upgrade_post_remap(State *s)
1175 {
1176   TxnDebug("http_trans_websocket_upgrade_post_remap", "Remap is complete, start websocket upgrade");
1177 
1178   TRANSACT_RETURN(SM_ACTION_API_POST_REMAP, HttpTransact::handle_websocket_connection);
1179 }
1180 
1181 void
handle_websocket_connection(State * s)1182 HttpTransact::handle_websocket_connection(State *s)
1183 {
1184   TxnDebug("http_trans_websocket", "START handle_websocket_connection");
1185 
1186   HandleRequest(s);
1187 }
1188 
1189 static bool
mimefield_value_equal(MIMEField * field,const char * value,const int value_len)1190 mimefield_value_equal(MIMEField *field, const char *value, const int value_len)
1191 {
1192   int field_value_len     = 0;
1193   const char *field_value = field->value_get(&field_value_len);
1194 
1195   if (field_value != nullptr && field_value_len == value_len) {
1196     return !strncasecmp(field_value, value, value_len);
1197   }
1198 
1199   return false;
1200 }
1201 
1202 void
ModifyRequest(State * s)1203 HttpTransact::ModifyRequest(State *s)
1204 {
1205   int scheme, hostname_len;
1206   HTTPHdr &request              = s->hdr_info.client_request;
1207   static const int PORT_PADDING = 8;
1208 
1209   TxnDebug("http_trans", "START HttpTransact::ModifyRequest");
1210 
1211   // Initialize the state vars necessary to sending error responses
1212   bootstrap_state_variables_from_request(s, &request);
1213 
1214   ////////////////////////////////////////////////
1215   // If there is no scheme, default to http      //
1216   ////////////////////////////////////////////////
1217   URL *url = request.url_get();
1218 
1219   s->orig_scheme = (scheme = url->scheme_get_wksidx());
1220 
1221   s->method = request.method_get_wksidx();
1222   if (scheme < 0 && s->method != HTTP_WKSIDX_CONNECT) {
1223     if (s->client_info.port_attribute == HttpProxyPort::TRANSPORT_SSL) {
1224       url->scheme_set(URL_SCHEME_HTTPS, URL_LEN_HTTPS);
1225       s->orig_scheme = URL_WKSIDX_HTTPS;
1226     } else {
1227       url->scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP);
1228       s->orig_scheme = URL_WKSIDX_HTTP;
1229     }
1230   }
1231 
1232   if (s->method == HTTP_WKSIDX_CONNECT && !request.is_port_in_header()) {
1233     url->port_set(80);
1234   }
1235 
1236   // Ugly - this must come after the call to url->scheme_set or
1237   // it can't get the scheme properly and the wrong data is cached.
1238   // The solution should be to move the scheme detecting logic in to
1239   // the header class, rather than doing it in a random bit of
1240   // external code.
1241   const char *buf = request.host_get(&hostname_len);
1242   if (!request.is_target_in_url()) {
1243     s->hdr_info.client_req_is_server_style = true;
1244   }
1245   // Copy out buf to a hostname just in case its heap header memory is freed during coalescing
1246   // due to later HdrHeap operations
1247   char *hostname = static_cast<char *>(alloca(hostname_len + PORT_PADDING));
1248   memcpy(hostname, buf, hostname_len);
1249 
1250   // Make clang analyzer happy. hostname is non-null iff request.is_target_in_url().
1251   ink_assert(hostname || s->hdr_info.client_req_is_server_style);
1252 
1253   // If the incoming request is proxy-style make sure the Host: header
1254   // matches the incoming request URL. The exception is if we have
1255   // Max-Forwards set to 0 in the request
1256   int max_forwards = -1; // -1 is a valid value meaning that it didn't find the header
1257   if (request.presence(MIME_PRESENCE_MAX_FORWARDS)) {
1258     max_forwards = request.get_max_forwards();
1259   }
1260 
1261   if ((max_forwards != 0) && !s->hdr_info.client_req_is_server_style && s->method != HTTP_WKSIDX_CONNECT) {
1262     MIMEField *host_field = request.field_find(MIME_FIELD_HOST, MIME_LEN_HOST);
1263     in_port_t port        = url->port_get_raw();
1264 
1265     // Form the host:port string if not a default port (e.g. 80)
1266     // We allocated extra space for the port above
1267     if (port > 0) {
1268       hostname_len += snprintf(hostname + hostname_len, PORT_PADDING, ":%u", port);
1269     }
1270 
1271     // No host_field means not equal to host and will need to be set, so create it now.
1272     if (!host_field) {
1273       host_field = request.field_create(MIME_FIELD_HOST, MIME_LEN_HOST);
1274       request.field_attach(host_field);
1275     }
1276 
1277     if (mimefield_value_equal(host_field, hostname, hostname_len) == false) {
1278       request.field_value_set(host_field, hostname, hostname_len);
1279       request.mark_target_dirty();
1280     }
1281   }
1282 
1283   TxnDebug("http_trans", "END HttpTransact::ModifyRequest");
1284 
1285   TRANSACT_RETURN(SM_ACTION_API_READ_REQUEST_HDR, HttpTransact::StartRemapRequest);
1286 }
1287 
1288 // This function is supposed to figure out if this transaction is
1289 // susceptible to a redirection as specified by remap.config
1290 bool
handleIfRedirect(State * s)1291 HttpTransact::handleIfRedirect(State *s)
1292 {
1293   int answer;
1294   URL redirect_url;
1295 
1296   answer = request_url_remap_redirect(&s->hdr_info.client_request, &redirect_url, s->state_machine->m_remap);
1297   if ((answer == PERMANENT_REDIRECT) || (answer == TEMPORARY_REDIRECT)) {
1298     int remap_redirect_len;
1299 
1300     s->remap_redirect = redirect_url.string_get(&s->arena, &remap_redirect_len);
1301     redirect_url.destroy();
1302     if (answer == TEMPORARY_REDIRECT) {
1303       if ((s->client_info).http_version.m_version == HTTP_VERSION(1, 1)) {
1304         build_error_response(s, HTTP_STATUS_TEMPORARY_REDIRECT, "Redirect", "redirect#moved_temporarily");
1305       } else {
1306         build_error_response(s, HTTP_STATUS_MOVED_TEMPORARILY, "Redirect", "redirect#moved_temporarily");
1307       }
1308     } else {
1309       build_error_response(s, HTTP_STATUS_MOVED_PERMANENTLY, "Redirect", "redirect#moved_permanently");
1310     }
1311     s->arena.str_free(s->remap_redirect);
1312     s->remap_redirect = nullptr;
1313     return true;
1314   }
1315 
1316   return false;
1317 }
1318 
1319 void
HandleRequest(State * s)1320 HttpTransact::HandleRequest(State *s)
1321 {
1322   TxnDebug("http_trans", "START HttpTransact::HandleRequest");
1323 
1324   if (!s->state_machine->is_waiting_for_full_body && !s->state_machine->is_using_post_buffer) {
1325     ink_assert(!s->hdr_info.server_request.valid());
1326 
1327     HTTP_INCREMENT_DYN_STAT(http_incoming_requests_stat);
1328 
1329     if (s->client_info.port_attribute == HttpProxyPort::TRANSPORT_SSL) {
1330       HTTP_INCREMENT_DYN_STAT(https_incoming_requests_stat);
1331     }
1332 
1333     ///////////////////////////////////////////////
1334     // if request is bad, return error response  //
1335     ///////////////////////////////////////////////
1336 
1337     if (!(is_request_valid(s, &s->hdr_info.client_request))) {
1338       HTTP_INCREMENT_DYN_STAT(http_invalid_client_requests_stat);
1339       TxnDebug("http_seq", "[HttpTransact::HandleRequest] request invalid.");
1340       s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
1341       //  s->next_action = HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
1342       return;
1343     }
1344     TxnDebug("http_seq", "[HttpTransact::HandleRequest] request valid.");
1345 
1346     if (is_debug_tag_set("http_chdr_describe")) {
1347       obj_describe(s->hdr_info.client_request.m_http, true);
1348     }
1349     // at this point we are guaranteed that the request is good and acceptable.
1350     // initialize some state variables from the request (client version,
1351     // client keep-alive, cache action, etc.
1352     initialize_state_variables_from_request(s, &s->hdr_info.client_request);
1353     // The following chunk of code will limit the maximum number of websocket connections (TS-3659)
1354     if (s->is_upgrade_request && s->is_websocket && s->http_config_param->max_websocket_connections >= 0) {
1355       int64_t val = 0;
1356       HTTP_READ_DYN_SUM(http_websocket_current_active_client_connections_stat, val);
1357       if (val >= s->http_config_param->max_websocket_connections) {
1358         s->is_websocket = false; // unset to avoid screwing up stats.
1359         TxnDebug("http_trans", "Rejecting websocket connection because the limit has been exceeded");
1360         bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
1361         build_error_response(s, HTTP_STATUS_SERVICE_UNAVAILABLE, "WebSocket Connection Limit Exceeded", nullptr);
1362         TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1363       }
1364     }
1365 
1366     // The following code is configurable to allow a user to control the max post size (TS-3631)
1367     if (s->http_config_param->max_post_size > 0 && s->hdr_info.request_content_length > 0 &&
1368         s->hdr_info.request_content_length > s->http_config_param->max_post_size) {
1369       TxnDebug("http_trans", "Max post size %" PRId64 " Client tried to post a body that was too large.",
1370                s->http_config_param->max_post_size);
1371       HTTP_INCREMENT_DYN_STAT(http_post_body_too_large);
1372       bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
1373       build_error_response(s, HTTP_STATUS_REQUEST_ENTITY_TOO_LARGE, "Request Entity Too Large", "request#entity_too_large");
1374       s->squid_codes.log_code = SQUID_LOG_ERR_POST_ENTITY_TOO_LARGE;
1375       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1376     }
1377 
1378     // The following chunk of code allows you to disallow post w/ expect 100-continue (TS-3459)
1379     if (s->hdr_info.request_content_length && s->http_config_param->disallow_post_100_continue) {
1380       MIMEField *expect = s->hdr_info.client_request.field_find(MIME_FIELD_EXPECT, MIME_LEN_EXPECT);
1381 
1382       if (expect != nullptr) {
1383         const char *expect_hdr_val = nullptr;
1384         int expect_hdr_val_len     = 0;
1385         expect_hdr_val             = expect->value_get(&expect_hdr_val_len);
1386         if (ptr_len_casecmp(expect_hdr_val, expect_hdr_val_len, HTTP_VALUE_100_CONTINUE, HTTP_LEN_100_CONTINUE) == 0) {
1387           // Let's error out this request.
1388           TxnDebug("http_trans", "Client sent a post expect: 100-continue, sending 405.");
1389           HTTP_INCREMENT_DYN_STAT(disallowed_post_100_continue);
1390           build_error_response(s, HTTP_STATUS_METHOD_NOT_ALLOWED, "Method Not Allowed", "request#method_unsupported");
1391           TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1392         }
1393       }
1394     }
1395     if (s->txn_conf->request_buffer_enabled &&
1396         (s->hdr_info.request_content_length > 0 || s->client_info.transfer_encoding == CHUNKED_ENCODING)) {
1397       TRANSACT_RETURN(SM_ACTION_WAIT_FOR_FULL_BODY, nullptr);
1398     }
1399   }
1400 
1401   // Cache lookup or not will be decided later at DecideCacheLookup().
1402   // Before it's decided to do a cache lookup,
1403   // assume no cache lookup and using proxy (not tunneling)
1404   s->cache_info.action = CACHE_DO_NO_ACTION;
1405   s->current.mode      = GENERIC_PROXY;
1406 
1407   // initialize the cache_control structure read from cache.config
1408   update_cache_control_information_from_config(s);
1409 
1410   // We still need to decide whether or not to do a cache lookup since
1411   // the scheduled update code depends on this info.
1412   if (is_request_cache_lookupable(s)) {
1413     s->cache_info.action = CACHE_DO_LOOKUP;
1414   }
1415 
1416   // If the hostname is "$internal$" then this is a request for
1417   // internal proxy information.
1418   if (handle_internal_request(s, &s->hdr_info.client_request)) {
1419     TRANSACT_RETURN(SM_ACTION_INTERNAL_REQUEST, nullptr);
1420   }
1421 
1422   // this needs to be called after initializing state variables from request
1423   // it adds the client-ip to the incoming client request.
1424 
1425   DUMP_HEADER("http_hdrs", &s->hdr_info.client_request, s->state_machine_id, "Incoming Request");
1426 
1427   if (s->state_machine->plugin_tunnel_type == HTTP_PLUGIN_AS_INTERCEPT) {
1428     setup_plugin_request_intercept(s);
1429     return;
1430   }
1431 
1432   // if ip in url or cop test page, not do srv lookup.
1433   if (s->txn_conf->srv_enabled) {
1434     IpEndpoint addr;
1435     ats_ip_pton(s->server_info.name, &addr);
1436     s->my_txn_conf().srv_enabled = !ats_is_ip(&addr);
1437   }
1438 
1439   // if the request is a trace or options request, decrement the
1440   // max-forwards value. if the incoming max-forwards value was 0,
1441   // then we have to return a response to the client with the
1442   // appropriate action for trace/option. in this case this routine
1443   // is responsible for building the response.
1444   if (handle_trace_and_options_requests(s, &s->hdr_info.client_request)) {
1445     TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, nullptr);
1446   }
1447 
1448   if (s->http_config_param->no_dns_forward_to_parent && s->scheme != URL_WKSIDX_HTTPS &&
1449       strcmp(s->server_info.name, "127.0.0.1") != 0) {
1450     // for HTTPS requests, we must go directly to the
1451     // origin server. Ignore the no_dns_just_forward_to_parent setting.
1452     // we need to see if the hostname is an
1453     //   ip address since the parent selection code result
1454     //   could change as a result of this ip address
1455     IpEndpoint addr;
1456     ats_ip_pton(s->server_info.name, &addr);
1457     if (ats_is_ip(&addr)) {
1458       ats_ip_copy(&s->request_data.dest_ip, &addr);
1459     }
1460 
1461     if (parentExists(s)) {
1462       // If the proxy is behind and firewall and there is no
1463       //  DNS service available, we just want to forward the request
1464       //  the parent proxy.  In this case, we never find out the
1465       //  origin server's ip.  So just skip past OSDNS
1466       ats_ip_invalidate(&s->server_info.dst_addr);
1467       StartAccessControl(s);
1468       return;
1469     } else if (s->http_config_param->no_origin_server_dns) {
1470       build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Next Hop Connection Failed", "connect#failed_connect");
1471 
1472       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1473     }
1474   }
1475 
1476   // Added to skip the dns if the document is in the cache.
1477   // DNS is requested before cache lookup only if there are rules in cache.config , parent.config or
1478   // if the newly added varible doc_in_cache_skip_dns is not enabled
1479   if (s->dns_info.lookup_name[0] <= '9' && s->dns_info.lookup_name[0] >= '0' &&
1480       (!s->state_machine->enable_redirection || !s->redirect_info.redirect_in_process) &&
1481       s->parent_params->parent_table->hostMatch) {
1482     s->force_dns = true;
1483   }
1484   /* A redirect means we need to check some things again.
1485      If the cache is enabled then we need to check the new (redirected) request against the cache.
1486      If not, then we need to at least do DNS again to guarantee we are using the correct IP address
1487      (if the host changed). Note DNS comes after cache lookup so in both cases we do the DNS.
1488   */
1489   if (s->redirect_info.redirect_in_process && s->state_machine->enable_redirection) {
1490     if (s->txn_conf->cache_http) {
1491       TRANSACT_RETURN(SM_ACTION_CACHE_LOOKUP, nullptr);
1492     } else {
1493       return CallOSDNSLookup(s);
1494     }
1495   }
1496 
1497   if (s->force_dns) {
1498     return CallOSDNSLookup(s);
1499   } else {
1500     // After the requested is properly handled No need of requesting the DNS directly check the ACLs
1501     // if the request is authorized
1502     StartAccessControl(s);
1503   }
1504 }
1505 
1506 void
HandleRequestBufferDone(State * s)1507 HttpTransact::HandleRequestBufferDone(State *s)
1508 {
1509   TRANSACT_RETURN(SM_ACTION_REQUEST_BUFFER_READ_COMPLETE, HttpTransact::HandleRequest);
1510 }
1511 
1512 void
setup_plugin_request_intercept(State * s)1513 HttpTransact::setup_plugin_request_intercept(State *s)
1514 {
1515   ink_assert(s->state_machine->plugin_tunnel != nullptr);
1516 
1517   // Plugin is intercepting the request which means
1518   //  that we don't do dns, cache read or cache write
1519   //
1520   // We just want to write the request straight to the plugin
1521   if (s->cache_info.action != HttpTransact::CACHE_DO_NO_ACTION) {
1522     s->cache_info.action = HttpTransact::CACHE_DO_NO_ACTION;
1523     s->current.mode      = TUNNELLING_PROXY;
1524     HTTP_INCREMENT_DYN_STAT(http_tunnels_stat);
1525   }
1526   // Regardless of the protocol we're gatewaying to
1527   //   we see the scheme as http
1528   s->scheme = s->next_hop_scheme = URL_WKSIDX_HTTP;
1529 
1530   // Set up a "fake" server server entry
1531   update_current_info(&s->current, &s->server_info, HttpTransact::ORIGIN_SERVER, 0);
1532 
1533   // Also "fake" the info we'd normally get from
1534   //   hostDB
1535   s->server_info.http_version.set(1, 0);
1536   s->server_info.keep_alive                  = HTTP_NO_KEEPALIVE;
1537   s->host_db_info.app.http_data.http_version = HostDBApplicationInfo::HTTP_VERSION_10;
1538   s->server_info.dst_addr.setToAnyAddr(AF_INET);                                 // must set an address or we can't set the port.
1539   s->server_info.dst_addr.port() = htons(s->hdr_info.client_request.port_get()); // this is the info that matters.
1540 
1541   // Build the request to the server
1542   build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->client_info.http_version);
1543 
1544   // We don't do keep alive over these impersonated
1545   //  NetVCs so nuke the connection header
1546   s->hdr_info.server_request.field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
1547 
1548   TRANSACT_RETURN(SM_ACTION_ORIGIN_SERVER_OPEN, nullptr);
1549 }
1550 
1551 ////////////////////////////////////////////////////////////////////////
1552 // void HttpTransact::HandleApiErrorJump(State* s)
1553 //
1554 //   Called after an API function indicates it wished to send an
1555 //     error to the user agent
1556 ////////////////////////////////////////////////////////////////////////
1557 void
HandleApiErrorJump(State * s)1558 HttpTransact::HandleApiErrorJump(State *s)
1559 {
1560   TxnDebug("http_trans", "[HttpTransact::HandleApiErrorJump]");
1561 
1562   // since the READ_REQUEST_HDR_HOOK is processed before
1563   //   we examine the request, returning TS_EVENT_ERROR will cause
1564   //   the protocol in the via string to be "?"  Set it here
1565   //   since we know it has to be http
1566   // For CONNECT method, next_hop_scheme is NULL
1567   if (s->next_hop_scheme < 0) {
1568     s->next_hop_scheme = URL_WKSIDX_HTTP;
1569   }
1570   // The client response may not be empty in the
1571   // case the txn was reenabled in error by a plugin from hook SEND_RESPONSE_HDR.
1572   // build_response doesn't clean the header. So clean it up before.
1573   // Do fields_clear() instead of clear() to prevent memory leak
1574   if (s->hdr_info.client_response.valid()) {
1575     s->hdr_info.client_response.fields_clear();
1576   }
1577 
1578   // Set the source to internal so chunking is handled correctly
1579   s->source = SOURCE_INTERNAL;
1580 
1581   /**
1582     The API indicated an error. Lets use a >=400 error from the state (if one's set) or fallback to a
1583     generic HTTP/1.X 500 INKApi Error
1584   **/
1585   if (s->http_return_code && s->http_return_code >= HTTP_STATUS_BAD_REQUEST) {
1586     const char *reason = http_hdr_reason_lookup(s->http_return_code);
1587     ;
1588     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, s->http_return_code, reason ? reason : "Error");
1589   } else {
1590     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, HTTP_STATUS_INTERNAL_SERVER_ERROR, "INKApi Error");
1591   }
1592 
1593   TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, nullptr);
1594   return;
1595 }
1596 
1597 ///////////////////////////////////////////////////////////////////////////////
1598 // Name       : PPDNSLookup
1599 // Description: called after DNS lookup of parent proxy name
1600 //
1601 // Details    :
1602 //
1603 // the configuration information gave us the name of the parent proxy
1604 // to send the request to. this function is called after the dns lookup
1605 // for that name. it may fail, in which case we look for the next parent
1606 // proxy to try and if none exist, then go to the origin server.
1607 // if the lookup succeeds, we open a connection to the parent proxy.
1608 //
1609 //
1610 // Possible Next States From Here:
1611 
1612 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
1613 // - HttpTransact::ORIGIN_SERVER_RAW_OPEN;
1614 // - HttpTransact::ORIGIN_SERVER_OPEN;
1615 //
1616 ///////////////////////////////////////////////////////////////////////////////
1617 void
PPDNSLookup(State * s)1618 HttpTransact::PPDNSLookup(State *s)
1619 {
1620   ++s->dns_info.attempts;
1621 
1622   TxnDebug("http_trans", "[HttpTransact::PPDNSLookup] This was attempt %d", s->dns_info.attempts);
1623 
1624   ink_assert(s->dns_info.looking_up == PARENT_PROXY);
1625   if (!s->dns_info.lookup_success) {
1626     // Mark parent as down due to resolving failure
1627     HTTP_INCREMENT_DYN_STAT(http_total_parent_marked_down_count);
1628     markParentDown(s);
1629     // DNS lookup of parent failed, find next parent or o.s.
1630     if (find_server_and_update_current_info(s) == HttpTransact::HOST_NONE) {
1631       ink_assert(s->current.request_to == HOST_NONE);
1632       handle_parent_died(s);
1633       return;
1634     }
1635 
1636     if (!s->current.server->dst_addr.isValid()) {
1637       if (s->current.request_to == PARENT_PROXY) {
1638         TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
1639       } else if (s->parent_result.result == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 1) {
1640         // We ran out of parents but parent configuration allows us to go to Origin Server directly
1641         return CallOSDNSLookup(s);
1642       } else {
1643         // We could be out of parents here if all the parents failed DNS lookup
1644         ink_assert(s->current.request_to == HOST_NONE);
1645         handle_parent_died(s);
1646       }
1647       return;
1648     }
1649   } else {
1650     // lookup succeeded, open connection to p.p.
1651     ats_ip_copy(&s->parent_info.dst_addr, s->host_db_info.ip());
1652     s->parent_info.dst_addr.port() = htons(s->parent_result.port);
1653     get_ka_info_from_host_db(s, &s->parent_info, &s->client_info, &s->host_db_info);
1654 
1655     char addrbuf[INET6_ADDRSTRLEN];
1656     TxnDebug("http_trans", "[PPDNSLookup] DNS lookup for sm_id[%" PRId64 "] successful IP: %s", s->state_machine->sm_id,
1657              ats_ip_ntop(&s->parent_info.dst_addr.sa, addrbuf, sizeof(addrbuf)));
1658   }
1659 
1660   // Since this function can be called several times while retrying
1661   //  parents, check to see if we've already built our request
1662   if (!s->hdr_info.server_request.valid()) {
1663     build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
1664 
1665     // Take care of deferred (issue revalidate) work in building
1666     //   the request
1667     if (s->pending_work != nullptr) {
1668       ink_assert(s->pending_work == issue_revalidate);
1669       (*s->pending_work)(s);
1670       s->pending_work = nullptr;
1671     }
1672   }
1673   // what kind of a connection (raw, simple)
1674   s->next_action = how_to_open_connection(s);
1675 }
1676 
1677 ///////////////////////////////////////////////////////////////////////////////
1678 //
1679 // Name       : ReDNSRoundRobin
1680 // Description: Called after we fail to contact part of a round-robin
1681 //              robin server set and we found a another ip address.
1682 //
1683 // Details    :
1684 //
1685 //
1686 //
1687 // Possible Next States From Here:
1688 // - HttpTransact::ORIGIN_SERVER_RAW_OPEN;
1689 // - HttpTransact::ORIGIN_SERVER_OPEN;
1690 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
1691 //
1692 ///////////////////////////////////////////////////////////////////////////////
1693 void
ReDNSRoundRobin(State * s)1694 HttpTransact::ReDNSRoundRobin(State *s)
1695 {
1696   ink_assert(s->current.server == &s->server_info);
1697   ink_assert(s->current.server->had_connect_fail());
1698 
1699   if (s->dns_info.lookup_success) {
1700     // We using a new server now so clear the connection
1701     //  failure mark
1702     s->current.server->clear_connect_fail();
1703 
1704     // Our ReDNS of the server succeeded so update the necessary
1705     //  information and try again. Need to preserve the current port value if possible.
1706     in_port_t server_port = s->current.server->dst_addr.host_order_port();
1707     // Temporary check to make sure the port preservation can be depended upon. That should be the case
1708     // because we get here only after trying a connection. Remove for 6.2.
1709     ink_assert(s->current.server->dst_addr.isValid() && 0 != server_port);
1710 
1711     ats_ip_copy(&s->server_info.dst_addr, s->host_db_info.ip());
1712     s->server_info.dst_addr.port() = htons(server_port);
1713     ats_ip_copy(&s->request_data.dest_ip, &s->server_info.dst_addr);
1714     get_ka_info_from_host_db(s, &s->server_info, &s->client_info, &s->host_db_info);
1715 
1716     char addrbuf[INET6_ADDRSTRLEN];
1717     TxnDebug("http_trans", "[ReDNSRoundRobin] DNS lookup for O.S. successful IP: %s",
1718              ats_ip_ntop(&s->server_info.dst_addr.sa, addrbuf, sizeof(addrbuf)));
1719 
1720     s->next_action = how_to_open_connection(s);
1721   } else {
1722     // Our ReDNS failed so output the DNS failure error message
1723     build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Cannot find server.", "connect#dns_failed");
1724     s->cache_info.action = CACHE_DO_NO_ACTION;
1725     s->next_action       = SM_ACTION_SEND_ERROR_CACHE_NOOP;
1726     //  s->next_action = PROXY_INTERNAL_CACHE_NOOP;
1727   }
1728 
1729   return;
1730 }
1731 
1732 ///////////////////////////////////////////////////////////////////////////////
1733 // Name       : OSDNSLookup
1734 // Description: called after the DNS lookup of origin server name
1735 //
1736 // Details    :
1737 //
1738 // normally called after Start. may be called more than once, however,
1739 // if the dns lookup fails. this may be if the client does not specify
1740 // the full hostname (e.g. just cnn, instead of www.cnn.com), or because
1741 // it was not possible to resolve the name after several attempts.
1742 //
1743 // the next action depends. since this function is normally called after
1744 // a request has come in, which is valid and does not require an immediate
1745 // response, the next action may just be to open a connection to the
1746 // origin server, or a parent proxy, or the next action may be to do a
1747 // cache lookup, or in the event of an error, the next action may be to
1748 // send a response back to the client.
1749 //
1750 //
1751 // Possible Next States From Here:
1752 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
1753 // - HttpTransact::CACHE_LOOKUP;
1754 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
1755 // - HttpTransact::ORIGIN_SERVER_RAW_OPEN;
1756 // - HttpTransact::ORIGIN_SERVER_OPEN;
1757 //
1758 ///////////////////////////////////////////////////////////////////////////////
1759 void
OSDNSLookup(State * s)1760 HttpTransact::OSDNSLookup(State *s)
1761 {
1762   static const int max_dns_lookups = 3;
1763 
1764   ink_assert(s->dns_info.looking_up == ORIGIN_SERVER);
1765 
1766   TxnDebug("http_trans", "[HttpTransact::OSDNSLookup] This was attempt %d", s->dns_info.attempts);
1767   ++s->dns_info.attempts;
1768 
1769   // It's never valid to connect *to* INADDR_ANY, so let's reject the request now.
1770   if (ats_is_ip_any(s->host_db_info.ip())) {
1771     TxnDebug("http_trans", "[OSDNSLookup] Invalid request IP: INADDR_ANY");
1772     build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Bad Destination Address", "request#syntax_error");
1773     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
1774     TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1775   }
1776 
1777   // detect whether we are about to self loop. the client may have
1778   // specified the proxy as the origin server (badness).
1779   // Check if this procedure is already done - YTS Team, yamsat
1780   if (!s->request_will_not_selfloop) {
1781     if (will_this_request_self_loop(s)) {
1782       TxnDebug("http_trans", "[OSDNSLookup] request will selfloop - bailing out");
1783       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
1784       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1785     }
1786   }
1787 
1788   if (!s->dns_info.lookup_success) {
1789     // maybe the name can be expanded (e.g cnn -> www.cnn.com)
1790     HostNameExpansionError_t host_name_expansion = try_to_expand_host_name(s);
1791 
1792     switch (host_name_expansion) {
1793     case RETRY_EXPANDED_NAME:
1794       // expansion successful, do a dns lookup on expanded name
1795       HTTP_RELEASE_ASSERT(s->dns_info.attempts < max_dns_lookups);
1796       return CallOSDNSLookup(s);
1797       break;
1798     case EXPANSION_NOT_ALLOWED:
1799     case EXPANSION_FAILED:
1800     case DNS_ATTEMPTS_EXHAUSTED:
1801       if (DNSLookupInfo::OS_Addr::OS_ADDR_TRY_HOSTDB == s->dns_info.os_addr_style) {
1802         /*
1803          *  We tried to connect to client target address, failed and tried to use a different addr
1804          *  No HostDB data, just keep on with the CTA.
1805          */
1806         s->dns_info.lookup_success = true;
1807         s->dns_info.os_addr_style  = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT;
1808         TxnDebug("http_seq", "[HttpTransact::OSDNSLookup] DNS lookup unsuccessful, using client target address");
1809       } else {
1810         if (host_name_expansion == EXPANSION_NOT_ALLOWED) {
1811           // config file doesn't allow automatic expansion of host names
1812           TxnDebug("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup unsuccessful");
1813         } else if (host_name_expansion == EXPANSION_FAILED) {
1814           // not able to expand the hostname. dns lookup failed
1815           TxnDebug("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup unsuccessful");
1816         } else if (host_name_expansion == DNS_ATTEMPTS_EXHAUSTED) {
1817           // retry attempts exhausted --- can't find dns entry for this host name
1818           HTTP_RELEASE_ASSERT(s->dns_info.attempts >= max_dns_lookups);
1819           TxnDebug("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup unsuccessful");
1820         }
1821         // output the DNS failure error message
1822         SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
1823         build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Cannot find server.", "connect#dns_failed");
1824         // s->cache_info.action = CACHE_DO_NO_ACTION;
1825         TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1826       }
1827       break;
1828     default:
1829       ink_assert(!("try_to_expand_hostname returned an unsupported code"));
1830       break;
1831     }
1832     return;
1833   }
1834   // ok, so the dns lookup succeeded
1835   ink_assert(s->dns_info.lookup_success);
1836   TxnDebug("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup successful");
1837 
1838   if (DNSLookupInfo::OS_Addr::OS_ADDR_TRY_HOSTDB == s->dns_info.os_addr_style) {
1839     // We've backed off from a client supplied address and found some
1840     // HostDB addresses. We use those if they're different from the CTA.
1841     // In all cases we now commit to client or HostDB for our source.
1842     if (s->host_db_info.round_robin) {
1843       HostDBInfo *cta = s->host_db_info.rr()->select_next(&s->current.server->dst_addr.sa);
1844       if (cta) {
1845         // found another addr, lock in host DB.
1846         s->host_db_info           = *cta;
1847         s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB;
1848       } else {
1849         // nothing else there, continue with CTA.
1850         s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT;
1851       }
1852     } else if (ats_ip_addr_eq(s->host_db_info.ip(), &s->server_info.dst_addr.sa)) {
1853       s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT;
1854     } else {
1855       s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB;
1856     }
1857   }
1858 
1859   // Check to see if can fullfill expect requests based on the cached
1860   // update some state variables with hostdb information that has
1861   // been provided.
1862   ats_ip_copy(&s->server_info.dst_addr, s->host_db_info.ip());
1863   // If the SRV response has a port number, we should honor it. Otherwise we do the port defined in remap
1864   if (s->dns_info.srv_lookup_success) {
1865     s->server_info.dst_addr.port() = htons(s->dns_info.srv_port);
1866   } else if (!s->api_server_addr_set) {
1867     s->server_info.dst_addr.port() = htons(s->hdr_info.client_request.port_get()); // now we can set the port.
1868   }
1869   ats_ip_copy(&s->request_data.dest_ip, &s->server_info.dst_addr);
1870   get_ka_info_from_host_db(s, &s->server_info, &s->client_info, &s->host_db_info);
1871 
1872   char addrbuf[INET6_ADDRSTRLEN];
1873   TxnDebug("http_trans",
1874            "[OSDNSLookup] DNS lookup for O.S. successful "
1875            "IP: %s",
1876            ats_ip_ntop(&s->server_info.dst_addr.sa, addrbuf, sizeof(addrbuf)));
1877 
1878   if (s->redirect_info.redirect_in_process) {
1879     // If dns lookup was not successful, the code below will handle the error.
1880     RedirectEnabled::Action action = RedirectEnabled::Action::INVALID;
1881     if (true == Machine::instance()->is_self(s->host_db_info.ip())) {
1882       action = s->http_config_param->redirect_actions_self_action;
1883     } else {
1884       // Make sure the return value from contains is big enough for a void*.
1885       intptr_t x{intptr_t(RedirectEnabled::Action::INVALID)};
1886       ink_release_assert(s->http_config_param->redirect_actions_map != nullptr);
1887       ink_release_assert(s->http_config_param->redirect_actions_map->contains(s->host_db_info.ip(), reinterpret_cast<void **>(&x)));
1888       action = static_cast<RedirectEnabled::Action>(x);
1889     }
1890     switch (action) {
1891     case RedirectEnabled::Action::FOLLOW:
1892       TxnDebug("http_trans", "[OSDNSLookup] Invalid redirect address. Following");
1893       break;
1894     case RedirectEnabled::Action::REJECT:
1895       TxnDebug("http_trans", "[OSDNSLookup] Invalid redirect address. Rejecting.");
1896       build_error_response(s, HTTP_STATUS_FORBIDDEN, nullptr, "request#syntax_error");
1897       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
1898       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1899       break;
1900     case RedirectEnabled::Action::RETURN:
1901       TxnDebug("http_trans", "[OSDNSLookup] Configured to return on invalid redirect address.");
1902       // fall-through
1903     default:
1904       // Return this 3xx to the client as-is.
1905       TxnDebug("http_trans", "[OSDNSLookup] Invalid redirect address. Returning.");
1906       build_response_copy(s, &s->hdr_info.server_response, &s->hdr_info.client_response, s->client_info.http_version);
1907       TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, nullptr);
1908     }
1909   }
1910 
1911   // so the dns lookup was a success, but the lookup succeeded on
1912   // a hostname which was expanded by the traffic server. we should
1913   // not automatically forward the request to this expanded hostname.
1914   // return a response to the client with the expanded host name
1915   // and a tasty little blurb explaining what happened.
1916 
1917   // if a DNS lookup succeeded on a user-defined
1918   // hostname expansion, forward the request to the expanded hostname.
1919   // On the other hand, if the lookup succeeded on a www.<hostname>.com
1920   // expansion, return a 302 response.
1921   // [amc] Also don't redirect if we backed off using HostDB instead of CTA.
1922   if (s->dns_info.attempts == max_dns_lookups && s->dns_info.looking_up == ORIGIN_SERVER &&
1923       DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT != s->dns_info.os_addr_style) {
1924     TxnDebug("http_trans", "[OSDNSLookup] DNS name resolution on expansion");
1925     TxnDebug("http_seq", "[OSDNSLookup] DNS name resolution on expansion - returning");
1926     build_redirect_response(s);
1927     // s->cache_info.action = CACHE_DO_NO_ACTION;
1928     TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, nullptr);
1929   }
1930   // everything succeeded with the DNS lookup so do an API callout
1931   //   that allows for filtering.  We'll do traffic_server internal
1932   //   filtering after API filtering
1933 
1934   // After SM_ACTION_DNS_LOOKUP, goto the saved action/state ORIGIN_SERVER_(RAW_)OPEN.
1935   // Should we skip the StartAccessControl()? why?
1936 
1937   if (s->cdn_remap_complete) {
1938     TxnDebug("cdn", "This is a late DNS lookup.  We are going to the OS, "
1939                     "not to HandleFiltering.");
1940 
1941     ink_assert(s->cdn_saved_next_action == SM_ACTION_ORIGIN_SERVER_OPEN ||
1942                s->cdn_saved_next_action == SM_ACTION_ORIGIN_SERVER_RAW_OPEN);
1943     TxnDebug("cdn", "outgoing version -- (pre  conversion) %d", s->hdr_info.server_request.m_http->m_version);
1944     (&s->hdr_info.server_request)->version_set(HTTPVersion(1, 1));
1945     HttpTransactHeaders::convert_request(s->current.server->http_version, &s->hdr_info.server_request);
1946     TxnDebug("cdn", "outgoing version -- (post conversion) %d", s->hdr_info.server_request.m_http->m_version);
1947     TRANSACT_RETURN(s->cdn_saved_next_action, nullptr);
1948   } else if (DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT == s->dns_info.os_addr_style ||
1949              DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB == s->dns_info.os_addr_style) {
1950     // we've come back after already trying the server to get a better address
1951     // and finished with all backtracking - return to trying the server.
1952     TRANSACT_RETURN(how_to_open_connection(s), HttpTransact::HandleResponse);
1953   } else if (s->dns_info.lookup_name[0] <= '9' && s->dns_info.lookup_name[0] >= '0' && s->parent_params->parent_table->hostMatch &&
1954              !s->http_config_param->no_dns_forward_to_parent) {
1955     // note, broken logic: ACC fudges the OR stmt to always be true,
1956     // 'AuthHttpAdapter' should do the rev-dns if needed, not here .
1957     TRANSACT_RETURN(SM_ACTION_DNS_REVERSE_LOOKUP, HttpTransact::StartAccessControl);
1958   } else {
1959     if (s->force_dns) {
1960       StartAccessControl(s); // If skip_dns is enabled and no ip based rules in cache.config and parent.config
1961       // Access Control is called after DNS response
1962     } else {
1963       if ((s->cache_info.action == CACHE_DO_NO_ACTION) &&
1964           (((s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE) && !s->txn_conf->cache_range_write) ||
1965             s->range_setup == RANGE_NOT_SATISFIABLE || s->range_setup == RANGE_NOT_HANDLED))) {
1966         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HandleCacheOpenReadMiss);
1967       } else if (!s->txn_conf->cache_http || s->cache_lookup_result == HttpTransact::CACHE_LOOKUP_SKIPPED) {
1968         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, LookupSkipOpenServer);
1969         // DNS Lookup is done after LOOKUP Skipped  and after we get response
1970         // from the DNS we need to call LookupSkipOpenServer
1971       } else if (is_cache_hit(s->cache_lookup_result)) {
1972         // DNS lookup is done if the content is state need to call handle cache open read hit
1973         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HandleCacheOpenReadHit);
1974       } else if (s->cache_lookup_result == CACHE_LOOKUP_MISS || s->cache_info.action == CACHE_DO_NO_ACTION) {
1975         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HandleCacheOpenReadMiss);
1976         // DNS lookup is done if the lookup failed and need to call Handle Cache Open Read Miss
1977       } else {
1978         build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Invalid Cache Lookup result", "default");
1979         Log::error("HTTP: Invalid CACHE LOOKUP RESULT : %d", s->cache_lookup_result);
1980         TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1981       }
1982     }
1983   }
1984 }
1985 
1986 void
StartAccessControl(State * s)1987 HttpTransact::StartAccessControl(State *s)
1988 {
1989   HandleRequestAuthorized(s);
1990 }
1991 
1992 void
HandleRequestAuthorized(State * s)1993 HttpTransact::HandleRequestAuthorized(State *s)
1994 {
1995   if (s->force_dns) {
1996     TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HttpTransact::DecideCacheLookup);
1997   } else {
1998     HttpTransact::DecideCacheLookup(s);
1999   }
2000 }
2001 
2002 void
DecideCacheLookup(State * s)2003 HttpTransact::DecideCacheLookup(State *s)
2004 {
2005   // Check if a client request is lookupable.
2006   if (s->redirect_info.redirect_in_process) {
2007     // for redirect, we want to skip cache lookup and write into
2008     // the cache directly with the URL before the redirect
2009     s->cache_info.action = CACHE_DO_NO_ACTION;
2010     s->current.mode      = GENERIC_PROXY;
2011   } else {
2012     if (is_request_cache_lookupable(s) && !s->is_upgrade_request) {
2013       s->cache_info.action = CACHE_DO_LOOKUP;
2014       s->current.mode      = GENERIC_PROXY;
2015     } else {
2016       s->cache_info.action = CACHE_DO_NO_ACTION;
2017       s->current.mode      = TUNNELLING_PROXY;
2018       HTTP_INCREMENT_DYN_STAT(http_tunnels_stat);
2019     }
2020   }
2021 
2022   // at this point the request is ready to continue down the
2023   // traffic server path.
2024 
2025   // now decide whether the cache can even be looked up.
2026   if (s->cache_info.action == CACHE_DO_LOOKUP) {
2027     TxnDebug("http_trans", "[DecideCacheLookup] Will do cache lookup.");
2028     TxnDebug("http_seq", "[DecideCacheLookup] Will do cache lookup");
2029     ink_assert(s->current.mode != TUNNELLING_PROXY);
2030 
2031     if (s->cache_info.lookup_url == nullptr) {
2032       HTTPHdr *incoming_request = &s->hdr_info.client_request;
2033 
2034       if (s->txn_conf->maintain_pristine_host_hdr) {
2035         s->cache_info.lookup_url_storage.create(nullptr);
2036         s->cache_info.lookup_url_storage.copy(incoming_request->url_get());
2037         s->cache_info.lookup_url = &s->cache_info.lookup_url_storage;
2038         // if the target isn't in the URL, put it in the copy for
2039         // cache lookup.
2040         incoming_request->set_url_target_from_host_field(s->cache_info.lookup_url);
2041       } else {
2042         // make sure the target is in the URL.
2043         incoming_request->set_url_target_from_host_field();
2044         s->cache_info.lookup_url = incoming_request->url_get();
2045       }
2046 
2047       // *somebody* wants us to not hack the host header in a reverse proxy setup.
2048       // In addition, they want us to reverse proxy for 6000 servers, which vary
2049       // the stupid content on the Host header!!!!
2050       // We could a) have 6000 alts (barf, puke, vomit) or b) use the original
2051       // host header in the url before doing all cache actions (lookups, writes, etc.)
2052       if (s->txn_conf->maintain_pristine_host_hdr) {
2053         const char *host_hdr;
2054         const char *port_hdr;
2055         int host_len, port_len;
2056         // So, the host header will have the original host header.
2057         if (incoming_request->get_host_port_values(&host_hdr, &host_len, &port_hdr, &port_len)) {
2058           int port = 0;
2059           if (port_hdr) {
2060             s->cache_info.lookup_url->host_set(host_hdr, host_len);
2061             port = ink_atoi(port_hdr, port_len);
2062           } else {
2063             s->cache_info.lookup_url->host_set(host_hdr, host_len);
2064           }
2065           s->cache_info.lookup_url->port_set(port);
2066         }
2067       }
2068       ink_assert(s->cache_info.lookup_url->valid() == true);
2069     }
2070 
2071     TRANSACT_RETURN(SM_ACTION_CACHE_LOOKUP, nullptr);
2072   } else {
2073     ink_assert(s->cache_info.action != CACHE_DO_LOOKUP && s->cache_info.action != CACHE_DO_SERVE);
2074 
2075     TxnDebug("http_trans", "[DecideCacheLookup] Will NOT do cache lookup.");
2076     TxnDebug("http_seq", "[DecideCacheLookup] Will NOT do cache lookup");
2077     // If this is a push request, we need send an error because
2078     //   since what ever was sent is not cachable
2079     if (s->method == HTTP_WKSIDX_PUSH) {
2080       HandlePushError(s, "Request Not Cachable");
2081       return;
2082     }
2083     // for redirect, we skipped cache lookup to do the automatic redirection
2084     if (s->redirect_info.redirect_in_process) {
2085       // without calling out the CACHE_LOOKUP_COMPLETE_HOOK
2086       if (s->txn_conf->cache_http) {
2087         s->cache_info.action = CACHE_DO_WRITE;
2088       }
2089       LookupSkipOpenServer(s);
2090     } else {
2091       // calling out CACHE_LOOKUP_COMPLETE_HOOK even when the cache
2092       // lookup is skipped
2093       s->cache_lookup_result = HttpTransact::CACHE_LOOKUP_SKIPPED;
2094       if (s->force_dns) {
2095         TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, LookupSkipOpenServer);
2096       } else {
2097         // Returning to dns lookup as cache lookup is skipped
2098         TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, CallOSDNSLookup);
2099       }
2100     }
2101   }
2102 
2103   return;
2104 }
2105 
2106 void
LookupSkipOpenServer(State * s)2107 HttpTransact::LookupSkipOpenServer(State *s)
2108 {
2109   // cache will not be looked up. open a connection
2110   // to a parent proxy or to the origin server.
2111   find_server_and_update_current_info(s);
2112 
2113   if (s->current.request_to == PARENT_PROXY) {
2114     TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
2115   } else if (s->parent_result.result == PARENT_FAIL) {
2116     handle_parent_died(s);
2117     return;
2118   }
2119 
2120   ink_assert(s->current.request_to == ORIGIN_SERVER);
2121   // ink_assert(s->current.server->ip != 0);
2122 
2123   build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
2124 
2125   StateMachineAction_t next = how_to_open_connection(s);
2126   s->next_action            = next;
2127   if (next == SM_ACTION_ORIGIN_SERVER_OPEN || next == SM_ACTION_ORIGIN_SERVER_RAW_OPEN) {
2128     TRANSACT_RETURN(next, HttpTransact::HandleResponse);
2129   }
2130 }
2131 
2132 //////////////////////////////////////////////////////////////////////////////
2133 // Name       : HandleCacheOpenReadPush
2134 // Description:
2135 //
2136 // Details    :
2137 //
2138 // Called on PUSH requests from HandleCacheOpenRead
2139 //////////////////////////////////////////////////////////////////////////////
2140 void
HandleCacheOpenReadPush(State * s,bool read_successful)2141 HttpTransact::HandleCacheOpenReadPush(State *s, bool read_successful)
2142 {
2143   if (read_successful) {
2144     s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
2145   } else {
2146     s->cache_info.action = CACHE_PREPARE_TO_WRITE;
2147   }
2148 
2149   TRANSACT_RETURN(SM_ACTION_READ_PUSH_HDR, HandlePushResponseHdr);
2150 }
2151 
2152 //////////////////////////////////////////////////////////////////////////////
2153 // Name       : HandlePushResponseHdr
2154 // Description:
2155 //
2156 // Details    :
2157 //
2158 // Called after reading the response header on PUSH request
2159 //////////////////////////////////////////////////////////////////////////////
2160 void
HandlePushResponseHdr(State * s)2161 HttpTransact::HandlePushResponseHdr(State *s)
2162 {
2163   // Verify the pushed header wasn't longer than the content length
2164   int64_t body_bytes = s->hdr_info.request_content_length - s->state_machine->pushed_response_hdr_bytes;
2165   if (body_bytes < 0) {
2166     HandlePushError(s, "Bad Content Length");
2167     return;
2168   }
2169   // We need to create the request header storing in the cache
2170   s->hdr_info.server_request.create(HTTP_TYPE_REQUEST);
2171   s->hdr_info.server_request.copy(&s->hdr_info.client_request);
2172   s->hdr_info.server_request.method_set(HTTP_METHOD_GET, HTTP_LEN_GET);
2173   s->hdr_info.server_request.value_set("X-Inktomi-Source", 16, "http PUSH", 9);
2174 
2175   DUMP_HEADER("http_hdrs", &s->hdr_info.server_response, s->state_machine_id, "Pushed Response Header");
2176 
2177   DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Generated Request Header");
2178 
2179   s->response_received_time = s->request_sent_time = ink_local_time();
2180 
2181   if (is_response_cacheable(s, &s->hdr_info.server_request, &s->hdr_info.server_response)) {
2182     ink_assert(s->cache_info.action == CACHE_PREPARE_TO_WRITE || s->cache_info.action == CACHE_PREPARE_TO_UPDATE);
2183 
2184     TRANSACT_RETURN(SM_ACTION_CACHE_ISSUE_WRITE, HandlePushCacheWrite);
2185   } else {
2186     HandlePushError(s, "Response Not Cachable");
2187   }
2188 }
2189 
2190 //////////////////////////////////////////////////////////////////////////////
2191 // Name       : HandlePushCacheWrite
2192 // Description:
2193 //
2194 // Details    :
2195 //
2196 // Called after performing the cache write on a push request
2197 //////////////////////////////////////////////////////////////////////////////
2198 void
HandlePushCacheWrite(State * s)2199 HttpTransact::HandlePushCacheWrite(State *s)
2200 {
2201   switch (s->cache_info.write_lock_state) {
2202   case CACHE_WL_SUCCESS:
2203     // We were able to get the lock for the URL vector in the cache
2204     if (s->cache_info.action == CACHE_PREPARE_TO_WRITE) {
2205       s->cache_info.action = CACHE_DO_WRITE;
2206     } else if (s->cache_info.action == CACHE_PREPARE_TO_UPDATE) {
2207       s->cache_info.action = CACHE_DO_REPLACE;
2208     } else {
2209       ink_release_assert(0);
2210     }
2211     set_headers_for_cache_write(s, &s->cache_info.object_store, &s->hdr_info.server_request, &s->hdr_info.server_response);
2212 
2213     TRANSACT_RETURN(SM_ACTION_STORE_PUSH_BODY, nullptr);
2214     break;
2215 
2216   case CACHE_WL_FAIL:
2217   case CACHE_WL_READ_RETRY:
2218     // No write lock, can not complete request so bail
2219     HandlePushError(s, "Cache Write Failed");
2220     break;
2221   case CACHE_WL_INIT:
2222   default:
2223     ink_release_assert(0);
2224   }
2225 }
2226 
2227 void
HandlePushTunnelSuccess(State * s)2228 HttpTransact::HandlePushTunnelSuccess(State *s)
2229 {
2230   ink_assert(s->cache_info.action == CACHE_DO_WRITE || s->cache_info.action == CACHE_DO_REPLACE);
2231 
2232   // FIX ME: check PUSH spec for status codes
2233   HTTPStatus resp_status = (s->cache_info.action == CACHE_DO_WRITE) ? HTTP_STATUS_CREATED : HTTP_STATUS_OK;
2234 
2235   build_response(s, &s->hdr_info.client_response, s->client_info.http_version, resp_status);
2236 
2237   TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, nullptr);
2238 }
2239 
2240 void
HandlePushTunnelFailure(State * s)2241 HttpTransact::HandlePushTunnelFailure(State *s)
2242 {
2243   HandlePushError(s, "Cache Error");
2244 }
2245 
2246 void
HandleBadPushRespHdr(State * s)2247 HttpTransact::HandleBadPushRespHdr(State *s)
2248 {
2249   HandlePushError(s, "Malformed Pushed Response Header");
2250 }
2251 
2252 void
HandlePushError(State * s,const char * reason)2253 HttpTransact::HandlePushError(State *s, const char *reason)
2254 {
2255   s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
2256 
2257   // Set half close flag to prevent TCP
2258   //   reset from the body still being transfered
2259   s->state_machine->set_ua_half_close_flag();
2260 
2261   build_error_response(s, HTTP_STATUS_BAD_REQUEST, reason, "default");
2262 }
2263 
2264 ///////////////////////////////////////////////////////////////////////////////
2265 // Name       : HandleCacheOpenRead
2266 // Description: the cache lookup succeeded - may have been a hit or a miss
2267 //
2268 // Details    :
2269 //
2270 // the cache lookup succeeded. first check if the lookup resulted in
2271 // a hit or a miss, if the lookup was for an http request.
2272 // This function just funnels the result into the appropriate
2273 // functions which handle these different cases.
2274 //
2275 //
2276 // Possible Next States From Here:
2277 //
2278 ///////////////////////////////////////////////////////////////////////////////
2279 void
HandleCacheOpenRead(State * s)2280 HttpTransact::HandleCacheOpenRead(State *s)
2281 {
2282   TxnDebug("http_trans", "[HttpTransact::HandleCacheOpenRead]");
2283 
2284   SET_VIA_STRING(VIA_DETAIL_CACHE_TYPE, VIA_DETAIL_CACHE);
2285 
2286   bool read_successful = true;
2287 
2288   if (s->cache_info.object_read == nullptr) {
2289     read_successful = false;
2290     //
2291     // If somebody else was writing the document, proceed just like it was
2292     // a normal cache miss, except don't try to write to the cache
2293     //
2294     if (s->cache_lookup_result == CACHE_LOOKUP_DOC_BUSY) {
2295       s->cache_lookup_result = CACHE_LOOKUP_MISS;
2296       s->cache_info.action   = CACHE_DO_NO_ACTION;
2297     }
2298   } else {
2299     CacheHTTPInfo *obj = s->cache_info.object_read;
2300     if (obj->response_get()->type_get() == HTTP_TYPE_UNKNOWN) {
2301       read_successful = false;
2302     }
2303     if (obj->request_get()->type_get() == HTTP_TYPE_UNKNOWN) {
2304       read_successful = false;
2305     }
2306   }
2307 
2308   if (s->method == HTTP_WKSIDX_PUSH) {
2309     HandleCacheOpenReadPush(s, read_successful);
2310   } else if (read_successful == false) {
2311     // cache miss
2312     TxnDebug("http_trans", "CacheOpenRead -- miss");
2313     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_NOT_CACHED);
2314     // Perform DNS for the origin when it is required.
2315     // 1. If parent configuration does not allow to go to origin there is no need of performing DNS
2316     // 2. If parent satisfies the request there is no need to go to origin to perfrom DNS
2317     HandleCacheOpenReadMiss(s);
2318   } else {
2319     // cache hit
2320     TxnDebug("http_trans", "CacheOpenRead -- hit");
2321     TRANSACT_RETURN(SM_ACTION_API_READ_CACHE_HDR, HandleCacheOpenReadHitFreshness);
2322   }
2323 
2324   return;
2325 }
2326 
2327 ///////////////////////////////////////////////////////////////////////////////
2328 // Name       : issue_revalidate
2329 // Description:   Sets cache action and does various bookkeeping
2330 //
2331 // Details    :
2332 //
2333 // The Cache Lookup was hit but the document was stale so after
2334 //   calling build_request, we need setup up the cache action,
2335 //   set the via code, and possibly conditionalize the request
2336 // The paths that we take to get this code are:
2337 //   Directly from HandleOpenReadHit if we are going to the origin server
2338 //   After PPDNS if we are going to a parent proxy
2339 //
2340 //
2341 // Possible Next States From Here:
2342 // -
2343 //
2344 ///////////////////////////////////////////////////////////////////////////////
2345 void
issue_revalidate(State * s)2346 HttpTransact::issue_revalidate(State *s)
2347 {
2348   HTTPHdr *c_resp = find_appropriate_cached_resp(s);
2349   SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_STALE);
2350   ink_assert(GET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP) != ' ');
2351 
2352   if (s->www_auth_content == CACHE_AUTH_FRESH) {
2353     s->hdr_info.server_request.method_set(HTTP_METHOD_HEAD, HTTP_LEN_HEAD);
2354     // The document is fresh in cache and we just want to see if the
2355     // the client has the right credentials
2356     // this cache action is just to get us into the hcoofsr function
2357     s->cache_info.action = CACHE_DO_UPDATE;
2358     DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Proxy's Request (Conditionalized)");
2359     return;
2360   }
2361 
2362   if (s->cache_info.write_lock_state == CACHE_WL_INIT) {
2363     // We do a cache lookup for DELETE, PUT and POST requests as well.
2364     // We must, however, delete the cached copy after forwarding the
2365     // request to the server. is_cache_response_returnable will ensure
2366     // that we forward the request. We now specify what the cache
2367     // action should be when the response is received.
2368     if (does_method_require_cache_copy_deletion(s->http_config_param, s->method)) {
2369       s->cache_info.action = CACHE_PREPARE_TO_DELETE;
2370       TxnDebug("http_seq", "[HttpTransact::issue_revalidate] cache action: DELETE");
2371     } else {
2372       s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
2373       TxnDebug("http_seq", "[HttpTransact::issue_revalidate] cache action: UPDATE");
2374     }
2375   } else {
2376     // We've looped back around due to missing the write lock
2377     //  for the cache.  At this point we want to forget about the cache
2378     ink_assert(s->cache_info.write_lock_state == CACHE_WL_READ_RETRY);
2379     s->cache_info.action = CACHE_DO_NO_ACTION;
2380     return;
2381   }
2382 
2383   // if the document is cached, just send a conditional request to the server
2384 
2385   // So the request does not have preconditions. It can, however
2386   // be a simple GET request with a Pragma:no-cache. As on 8/28/98
2387   // we have fixed the whole Reload/Shift-Reload cached copy
2388   // corruption problem. This means that we can issue a conditional
2389   // request to the server only if the incoming request has a conditional
2390   // or the incoming request does NOT have a no-cache header.
2391   // In other words, if the incoming request is not conditional
2392   // but has a no-cache header we can not issue an IMS. check for
2393   // that case here.
2394   bool no_cache_in_request = false;
2395 
2396   if (s->hdr_info.client_request.is_pragma_no_cache_set() || s->hdr_info.client_request.is_cache_control_set(HTTP_VALUE_NO_CACHE)) {
2397     TxnDebug("http_trans", "[issue_revalidate] no-cache header directive in request, folks");
2398     no_cache_in_request = true;
2399   }
2400 
2401   if ((!(s->hdr_info.client_request.presence(MIME_PRESENCE_IF_MODIFIED_SINCE))) &&
2402       (!(s->hdr_info.client_request.presence(MIME_PRESENCE_IF_NONE_MATCH))) && (no_cache_in_request == true) &&
2403       (!s->txn_conf->cache_ims_on_client_no_cache) && (s->www_auth_content == CACHE_AUTH_NONE)) {
2404     TxnDebug("http_trans",
2405              "[issue_revalidate] Can not make this a conditional request. This is the force update of the cached copy case");
2406     // set cache action to update. response will be a 200 or error,
2407     // causing cached copy to be replaced (if 200).
2408     s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
2409     return;
2410   }
2411   // do not conditionalize if the cached response is not a 200
2412   switch (c_resp->status_get()) {
2413   case HTTP_STATUS_OK: // 200
2414     // don't conditionalize if we are configured to repeat the clients
2415     //   conditionals
2416     if (s->txn_conf->cache_when_to_revalidate == 4) {
2417       break;
2418     }
2419     // ok, request is either a conditional or does not have a no-cache.
2420     //   (or is method that we don't conditionalize but lookup the
2421     //    cache on like DELETE)
2422     if (c_resp->get_last_modified() > 0 &&
2423         (s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_GET ||
2424          s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_HEAD) &&
2425         s->range_setup == RANGE_NONE) {
2426       // make this a conditional request
2427       int length;
2428       const char *str = c_resp->value_get(MIME_FIELD_LAST_MODIFIED, MIME_LEN_LAST_MODIFIED, &length);
2429       if (str) {
2430         s->hdr_info.server_request.value_set(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE, str, length);
2431       }
2432       DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Proxy's Request (Conditionalized)");
2433     }
2434     // if Etag exists, also add if-non-match header
2435     if (c_resp->presence(MIME_PRESENCE_ETAG) && (s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_GET ||
2436                                                  s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_HEAD)) {
2437       int length       = 0;
2438       const char *etag = c_resp->value_get(MIME_FIELD_ETAG, MIME_LEN_ETAG, &length);
2439       if (nullptr != etag) {
2440         if ((length >= 2) && (etag[0] == 'W') && (etag[1] == '/')) {
2441           etag += 2;
2442           length -= 2;
2443         }
2444         s->hdr_info.server_request.value_set(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH, etag, length);
2445       }
2446       DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Proxy's Request (Conditionalized)");
2447     }
2448     break;
2449   case HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION: // 203
2450   /* fall through */
2451   case HTTP_STATUS_MULTIPLE_CHOICES: // 300
2452   /* fall through */
2453   case HTTP_STATUS_MOVED_PERMANENTLY: // 301
2454   /* fall through */
2455   case HTTP_STATUS_GONE: // 410
2456   /* fall through */
2457   default:
2458     TxnDebug("http_trans", "[issue_revalidate] cached response is"
2459                            "not a 200 response so no conditionalization.");
2460     s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
2461     break;
2462   case HTTP_STATUS_PARTIAL_CONTENT:
2463     ink_assert(!"unexpected status code");
2464     break;
2465   }
2466 }
2467 
2468 void
HandleCacheOpenReadHitFreshness(State * s)2469 HttpTransact::HandleCacheOpenReadHitFreshness(State *s)
2470 {
2471   CacheHTTPInfo *&obj = s->cache_info.object_read;
2472 
2473   ink_release_assert((s->request_sent_time == UNDEFINED_TIME) && (s->response_received_time == UNDEFINED_TIME));
2474   TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] Hit in cache");
2475 
2476   if (delete_all_document_alternates_and_return(s, true)) {
2477     TxnDebug("http_trans", "[HandleCacheOpenReadHitFreshness] Delete and return");
2478     s->cache_info.action = CACHE_DO_DELETE;
2479     s->next_action       = HttpTransact::SM_ACTION_INTERNAL_CACHE_DELETE;
2480     return;
2481   }
2482 
2483   s->request_sent_time      = obj->request_sent_time_get();
2484   s->response_received_time = obj->response_received_time_get();
2485 
2486   // There may be clock skew if one of the machines
2487   // went down and we do not have the correct delta
2488   // for it. this is just to deal with the effects
2489   // of the skew by setting minimum and maximum times
2490   // so that ages are not negative, etc.
2491   s->request_sent_time      = std::min(s->client_request_time, s->request_sent_time);
2492   s->response_received_time = std::min(s->client_request_time, s->response_received_time);
2493 
2494   ink_assert(s->request_sent_time <= s->response_received_time);
2495 
2496   TxnDebug("http_trans", "[HandleCacheOpenReadHitFreshness] request_sent_time      : %" PRId64, (int64_t)s->request_sent_time);
2497   TxnDebug("http_trans", "[HandleCacheOpenReadHitFreshness] response_received_time : %" PRId64, (int64_t)s->response_received_time);
2498   // if the plugin has already decided the freshness, we don't need to
2499   // do it again
2500   if (s->cache_lookup_result == HttpTransact::CACHE_LOOKUP_NONE) {
2501     // is the document still fresh enough to be served back to
2502     // the client without revalidation?
2503     Freshness_t freshness = what_is_document_freshness(s, &s->hdr_info.client_request, obj->response_get());
2504     switch (freshness) {
2505     case FRESHNESS_FRESH:
2506       TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] "
2507                            "Fresh copy");
2508       s->cache_lookup_result = HttpTransact::CACHE_LOOKUP_HIT_FRESH;
2509       break;
2510     case FRESHNESS_WARNING:
2511       TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] "
2512                            "Heuristic-based Fresh copy");
2513       s->cache_lookup_result = HttpTransact::CACHE_LOOKUP_HIT_WARNING;
2514       break;
2515     case FRESHNESS_STALE:
2516       TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] "
2517                            "Stale in cache");
2518       s->cache_lookup_result       = HttpTransact::CACHE_LOOKUP_HIT_STALE;
2519       s->is_revalidation_necessary = true; // to identify a revalidation occurrence
2520       break;
2521     default:
2522       ink_assert(!("what_is_document_freshness has returned unsupported code."));
2523       break;
2524     }
2525   }
2526 
2527   ink_assert(s->cache_lookup_result != HttpTransact::CACHE_LOOKUP_MISS);
2528   if (s->cache_lookup_result == HttpTransact::CACHE_LOOKUP_HIT_STALE) {
2529     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_EXPIRED);
2530     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_STALE);
2531   }
2532 
2533   if (!s->force_dns) { // If DNS is not performed before
2534     if (need_to_revalidate(s)) {
2535       TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE,
2536                       CallOSDNSLookup); // content needs to be revalidated and we did not perform a dns ....calling DNS lookup
2537     } else {                            // document can be served can cache
2538       TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, HttpTransact::HandleCacheOpenReadHit);
2539     }
2540   } else { // we have done dns . Its up to HandleCacheOpenReadHit to decide to go OS or serve from cache
2541     TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, HttpTransact::HandleCacheOpenReadHit);
2542   }
2543 }
2544 
2545 ///////////////////////////////////////////////////////////////////////////////
2546 // Name       : CallOSDNSLookup
2547 // Description: Moves in SM_ACTION_DNS_LOOKUP state and sets the transact return to OSDNSLookup
2548 //
2549 // Details    :
2550 /////////////////////////////////////////////////////////////////////////////
2551 void
CallOSDNSLookup(State * s)2552 HttpTransact::CallOSDNSLookup(State *s)
2553 {
2554   TxnDebug("http", "[HttpTransact::callos] %s ", s->server_info.name);
2555   HostStatus &pstatus = HostStatus::instance();
2556   HostStatRec *hst    = pstatus.getHostStatus(s->server_info.name);
2557   if (hst && hst->status == HostStatus_t::HOST_STATUS_DOWN) {
2558     TxnDebug("http", "[HttpTransact::callos] %d ", s->cache_lookup_result);
2559     s->current.state = OUTBOUND_CONGESTION;
2560     if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE || s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING ||
2561         s->cache_lookup_result == CACHE_LOOKUP_HIT_FRESH) {
2562       s->cache_info.action = CACHE_DO_SERVE;
2563     } else {
2564       s->cache_info.action = CACHE_DO_NO_ACTION;
2565     }
2566     handle_server_connection_not_open(s);
2567   } else {
2568     TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, OSDNSLookup);
2569   }
2570 }
2571 
2572 ///////////////////////////////////////////////////////////////////////////////
2573 // Name       : need_to_revalidate
2574 // Description: Checks if a document which is in the cache needs to be revalidates
2575 //
2576 // Details    : Function calls AuthenticationNeeded and is_cache_response_returnable to determine
2577 //              if the cached document can be served
2578 /////////////////////////////////////////////////////////////////////////////
2579 bool
need_to_revalidate(State * s)2580 HttpTransact::need_to_revalidate(State *s)
2581 {
2582   bool needs_revalidate, needs_authenticate = false;
2583   bool needs_cache_auth = false;
2584   CacheHTTPInfo *obj;
2585 
2586   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
2587     obj = &s->cache_info.object_store;
2588     ink_assert(obj->valid());
2589     if (!obj->valid()) {
2590       return true;
2591     }
2592   } else {
2593     obj = s->cache_info.object_read;
2594   }
2595 
2596   // do we have to authenticate with the server before
2597   // sending back the cached response to the client?
2598   Authentication_t authentication_needed = AuthenticationNeeded(s->txn_conf, &s->hdr_info.client_request, obj->response_get());
2599 
2600   switch (authentication_needed) {
2601   case AUTHENTICATION_SUCCESS:
2602     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2603                          "Authentication not needed");
2604     needs_authenticate = false;
2605     break;
2606   case AUTHENTICATION_MUST_REVALIDATE:
2607     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_METHOD);
2608     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2609                          "Authentication needed");
2610     needs_authenticate = true;
2611     break;
2612   case AUTHENTICATION_MUST_PROXY:
2613     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2614                          "Authentication needed");
2615     needs_authenticate = true;
2616     break;
2617   case AUTHENTICATION_CACHE_AUTH:
2618     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2619                          "Authentication needed for cache_auth_content");
2620     needs_authenticate = false;
2621     needs_cache_auth   = true;
2622     break;
2623   default:
2624     ink_assert(!("AuthenticationNeeded has returned unsupported code."));
2625     return true;
2626     break;
2627   }
2628 
2629   ink_assert(is_cache_hit(s->cache_lookup_result));
2630   if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE &&
2631       s->api_update_cached_object != HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
2632     needs_revalidate = true;
2633   } else {
2634     needs_revalidate = false;
2635   }
2636 
2637   bool send_revalidate = ((needs_authenticate == true) || (needs_revalidate == true) || (is_cache_response_returnable(s) == false));
2638   if (needs_cache_auth == true) {
2639     s->www_auth_content = send_revalidate ? CACHE_AUTH_STALE : CACHE_AUTH_FRESH;
2640     send_revalidate     = true;
2641   }
2642   return send_revalidate;
2643 }
2644 
2645 ///////////////////////////////////////////////////////////////////////////////
2646 // Name       : HandleCacheOpenReadHit
2647 // Description: handle result of a cache hit
2648 //
2649 // Details    :
2650 //
2651 // Cache lookup succeeded and resulted in a cache hit. This means
2652 // that the Accept* and Etags fields also matched. The cache lookup
2653 // may have resulted in a vector of alternates (since lookup may
2654 // be based on a url). A different function (SelectFromAlternates)
2655 // goes through the alternates and finds the best match. That is
2656 // then returned to this function. The result may not be sent back
2657 // to the client, still, if the document is not fresh enough, or
2658 // does not have enough authorization, or if the client wants a
2659 // reload, etc. that decision will be made in this routine.
2660 //
2661 //
2662 // Possible Next States From Here:
2663 // - HttpTransact::PROXY_INTERNAL_CACHE_DELETE;
2664 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
2665 // - HttpTransact::ORIGIN_SERVER_OPEN;
2666 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
2667 // - HttpTransact::SERVE_FROM_CACHE;
2668 // - result of how_to_open_connection()
2669 //
2670 //
2671 // For Range requests, we will decide to do simple tunneling if one of the
2672 // following conditions hold:
2673 // - document stale
2674 // - cached response doesn't have Accept-Ranges and Content-Length
2675 //
2676 ///////////////////////////////////////////////////////////////////////////////
2677 void
HandleCacheOpenReadHit(State * s)2678 HttpTransact::HandleCacheOpenReadHit(State *s)
2679 {
2680   bool needs_revalidate   = false;
2681   bool needs_authenticate = false;
2682   bool needs_cache_auth   = false;
2683   bool server_up          = true;
2684   CacheHTTPInfo *obj;
2685 
2686   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
2687     obj = &s->cache_info.object_store;
2688     ink_assert(obj->valid());
2689   } else {
2690     obj = s->cache_info.object_read;
2691   }
2692 
2693   // do we have to authenticate with the server before
2694   // sending back the cached response to the client?
2695   Authentication_t authentication_needed = AuthenticationNeeded(s->txn_conf, &s->hdr_info.client_request, obj->response_get());
2696 
2697   switch (authentication_needed) {
2698   case AUTHENTICATION_SUCCESS:
2699     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2700                          "Authentication not needed");
2701     needs_authenticate = false;
2702     break;
2703   case AUTHENTICATION_MUST_REVALIDATE:
2704     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_METHOD);
2705     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2706                          "Authentication needed");
2707     needs_authenticate = true;
2708     break;
2709   case AUTHENTICATION_MUST_PROXY:
2710     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2711                          "Authentication needed");
2712     HandleCacheOpenReadMiss(s);
2713     return;
2714   case AUTHENTICATION_CACHE_AUTH:
2715     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2716                          "Authentication needed for cache_auth_content");
2717     needs_authenticate = false;
2718     needs_cache_auth   = true;
2719     break;
2720   default:
2721     ink_assert(!("AuthenticationNeeded has returned unsupported code."));
2722     break;
2723   }
2724 
2725   ink_assert(is_cache_hit(s->cache_lookup_result));
2726 
2727   // We'll request a revalidation under one of these conditions:
2728   //
2729   // 1. Cache lookup is a hit, but the response is stale
2730   // 2. The cached object has a "Cache-Control: no-cache" header
2731   //       *and*
2732   //    proxy.config.http.cache.ignore_server_no_cache is set to 0 (i.e don't ignore no cache -- the default setting)
2733   //
2734   // But, we only do this if we're not in an API updating the cached object (see TSHttpTxnUpdateCachedObject)
2735   if ((((s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE) ||
2736         ((obj->response_get()->get_cooked_cc_mask() & MIME_COOKED_MASK_CC_NO_CACHE) && !s->cache_control.ignore_server_no_cache)) &&
2737        (s->api_update_cached_object != HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE))) {
2738     needs_revalidate = true;
2739     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_EXPIRED);
2740   }
2741 
2742   // the response may not be directly returnable to the client. there
2743   // are several reasons for this: config may force revalidation or
2744   // client may have forced a refresh by sending a Pragma:no-cache
2745   // or a Cache-Control:no-cache, or the client may have sent a
2746   // non-GET/HEAD request for a document that is cached. an example
2747   // of a situation for this is when a client sends a DELETE, PUT
2748   // or POST request for a url that is cached. except for DELETE,
2749   // we may actually want to update the cached copy with the contents
2750   // of the PUT/POST, but the easiest, safest and most robust solution
2751   // is to simply delete the cached copy (in order to maintain cache
2752   // consistency). this is particularly true if the server does not
2753   // accept or conditionally accepts the PUT/POST requests.
2754   // anyhow, this is an overloaded function and will return false
2755   // if the origin server still has to be looked up.
2756   bool response_returnable = is_cache_response_returnable(s);
2757 
2758   // do we need to revalidate. in other words if the response
2759   // has to be authorized, is stale or can not be returned, do
2760   // a revalidate.
2761   bool send_revalidate = (needs_authenticate || needs_revalidate || !response_returnable);
2762 
2763   if (needs_cache_auth == true) {
2764     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_EXPIRED);
2765     s->www_auth_content = send_revalidate ? CACHE_AUTH_STALE : CACHE_AUTH_FRESH;
2766     send_revalidate     = true;
2767   }
2768 
2769   TxnDebug("http_trans", "CacheOpenRead --- needs_auth          = %d", needs_authenticate);
2770   TxnDebug("http_trans", "CacheOpenRead --- needs_revalidate    = %d", needs_revalidate);
2771   TxnDebug("http_trans", "CacheOpenRead --- response_returnable = %d", response_returnable);
2772   TxnDebug("http_trans", "CacheOpenRead --- needs_cache_auth    = %d", needs_cache_auth);
2773   TxnDebug("http_trans", "CacheOpenRead --- send_revalidate     = %d", send_revalidate);
2774 
2775   if (send_revalidate) {
2776     TxnDebug("http_trans", "CacheOpenRead --- HIT-STALE");
2777     s->dns_info.attempts = 0;
2778 
2779     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2780                          "Revalidate document with server");
2781 
2782     find_server_and_update_current_info(s);
2783 
2784     // We do not want to try to revalidate documents if we think
2785     //  the server is down due to the something report problem
2786     //
2787     // Note: we only want to skip origin servers because 1)
2788     //  parent proxies have their own negative caching
2789     //  scheme & 2) If we skip down parents, every page
2790     //  we serve is potentially stale
2791     //
2792     if (s->current.request_to == ORIGIN_SERVER && is_server_negative_cached(s) && response_returnable == true &&
2793         is_stale_cache_response_returnable(s) == true) {
2794       server_up = false;
2795       update_current_info(&s->current, nullptr, UNDEFINED_LOOKUP, 0);
2796       TxnDebug("http_trans", "CacheOpenReadHit - server_down, returning stale document");
2797     }
2798     // a parent lookup could come back as PARENT_FAIL if in parent.config, go_direct == false and
2799     // there are no available parents (all down).
2800     else if (s->current.request_to == HOST_NONE && s->parent_result.result == PARENT_FAIL) {
2801       if (is_server_negative_cached(s) && response_returnable == true && is_stale_cache_response_returnable(s) == true) {
2802         server_up = false;
2803         update_current_info(&s->current, nullptr, UNDEFINED_LOOKUP, 0);
2804         TxnDebug("http_trans", "CacheOpenReadHit - server_down, returning stale document");
2805       } else {
2806         handle_parent_died(s);
2807         return;
2808       }
2809     }
2810 
2811     if (server_up) {
2812       // set a default version for the outgoing request
2813       HTTPVersion http_version;
2814 
2815       if (s->current.server != nullptr) {
2816         bool check_hostdb = get_ka_info_from_config(s, s->current.server);
2817         TxnDebug("http_trans", "CacheOpenReadHit - check_hostdb %d", check_hostdb);
2818         if (check_hostdb || !s->current.server->dst_addr.isValid()) {
2819           // We must be going a PARENT PROXY since so did
2820           //  origin server DNS lookup right after state Start
2821           //
2822           // If we end up here in the release case just fall
2823           //  through.  The request will fail because of the
2824           //  missing ip but we won't take down the system
2825           //
2826           if (s->current.request_to == PARENT_PROXY) {
2827             // Set ourselves up to handle pending revalidate issues
2828             //  after the PP DNS lookup
2829             ink_assert(s->pending_work == nullptr);
2830             s->pending_work = issue_revalidate;
2831 
2832             TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
2833           } else if (s->current.request_to == ORIGIN_SERVER) {
2834             return CallOSDNSLookup(s);
2835           } else {
2836             handle_parent_died(s);
2837             return;
2838           }
2839         }
2840         // override the default version with what the server has
2841         http_version = s->current.server->http_version;
2842       }
2843 
2844       TxnDebug("http_trans", "CacheOpenReadHit - version %d", http_version.m_version);
2845       build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, http_version);
2846 
2847       issue_revalidate(s);
2848 
2849       // this can not be anything but a simple origin server connection.
2850       // in other words, we would not have looked up the cache for a
2851       // connect request, so the next action can not be origin_server_raw_open.
2852       s->next_action = how_to_open_connection(s);
2853 
2854       ink_release_assert(s->next_action != SM_ACTION_ORIGIN_SERVER_RAW_OPEN);
2855       return;
2856     } else { // server is down but stale response is returnable
2857       SET_VIA_STRING(VIA_DETAIL_CACHE_TYPE, VIA_DETAIL_CACHE);
2858     }
2859   }
2860   // cache hit, document is fresh, does not authorization,
2861   // is valid, etc. etc. send it back to the client.
2862   //
2863   // the important thing to keep in mind is that if we are
2864   // here then we found a match in the cache and the document
2865   // is fresh and we have enough authorization for it to send
2866   // it back to the client without revalidating first with the
2867   // origin server. we are, therefore, allowed to behave as the
2868   // origin server. we can, therefore, make the claim that the
2869   // document has not been modified since or has not been unmodified
2870   // since the time requested by the client. this may not be
2871   // the case in reality, but since the document is fresh in
2872   // the cache, we can make the claim that this is the truth.
2873   //
2874   // so, any decision we make at this point can be made with authority.
2875   // realistically, if we can not make this claim, then there
2876   // is no reason to cache anything.
2877   //
2878   ink_assert((send_revalidate == true && server_up == false) || (send_revalidate == false && server_up == true));
2879 
2880   TxnDebug("http_trans", "CacheOpenRead --- HIT-FRESH");
2881   TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2882                        "Serve from cache");
2883 
2884   // ToDo: Should support other levels of cache hits here, but the cache does not support it (yet)
2885   if (SQUID_HIT_RAM == s->cache_info.hit_miss_code) {
2886     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_RAM_CACHE_FRESH);
2887   } else {
2888     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_FRESH);
2889   }
2890 
2891   if (s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING) {
2892     build_response_from_cache(s, HTTP_WARNING_CODE_HERUISTIC_EXPIRATION);
2893   } else if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE) {
2894     ink_assert(server_up == false);
2895     build_response_from_cache(s, HTTP_WARNING_CODE_REVALIDATION_FAILED);
2896   } else {
2897     build_response_from_cache(s, HTTP_WARNING_CODE_NONE);
2898   }
2899 
2900   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
2901     s->saved_update_next_action  = s->next_action;
2902     s->saved_update_cache_action = s->cache_info.action;
2903     s->next_action               = SM_ACTION_CACHE_PREPARE_UPDATE;
2904   }
2905 }
2906 
2907 ///////////////////////////////////////////////////////////////////////////////
2908 // Name       : build_response_from_cache()
2909 // Description: build a client response from cached response and client request
2910 //
2911 // Input      : State, warning code to be inserted into the response header
2912 // Output     :
2913 //
2914 // Details    : This function is called if we decided to serve a client request
2915 //              using a cached response.
2916 //              It is called by handle_server_connection_not_open()
2917 //              and HandleCacheOpenReadHit().
2918 //
2919 ///////////////////////////////////////////////////////////////////////////////
2920 void
build_response_from_cache(State * s,HTTPWarningCode warning_code)2921 HttpTransact::build_response_from_cache(State *s, HTTPWarningCode warning_code)
2922 {
2923   HTTPHdr *client_request  = &s->hdr_info.client_request;
2924   HTTPHdr *cached_response = nullptr;
2925   HTTPHdr *to_warn         = &s->hdr_info.client_response;
2926   CacheHTTPInfo *obj;
2927 
2928   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
2929     obj = &s->cache_info.object_store;
2930     ink_assert(obj->valid());
2931   } else {
2932     obj = s->cache_info.object_read;
2933   }
2934   cached_response = obj->response_get();
2935 
2936   // If the client request is conditional, and the cached copy meets
2937   // the conditions, do not need to send back the full document,
2938   // just a NOT_MODIFIED response.
2939   // If the request is not conditional,
2940   // the function match_response_to_request_conditionals() returns
2941   // the code of the cached response, which means that we should send
2942   // back the full document.
2943   HTTPStatus client_response_code =
2944     HttpTransactCache::match_response_to_request_conditionals(client_request, cached_response, s->response_received_time);
2945 
2946   switch (client_response_code) {
2947   case HTTP_STATUS_NOT_MODIFIED:
2948     // A IMS or INM GET client request with conditions being met
2949     // by the cached response.  Send back a NOT MODIFIED response.
2950     TxnDebug("http_trans", "[build_response_from_cache] Not modified");
2951     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_HIT_CONDITIONAL);
2952 
2953     build_response(s, cached_response, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
2954     s->cache_info.action = CACHE_DO_NO_ACTION;
2955     s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
2956     break;
2957 
2958   case HTTP_STATUS_PRECONDITION_FAILED:
2959     // A conditional request with conditions not being met by the cached
2960     // response.  Send back a PRECONDITION FAILED response.
2961     TxnDebug("http_trans", "[build_response_from_cache] Precondition Failed");
2962     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_CONDITIONAL);
2963 
2964     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
2965     s->cache_info.action = CACHE_DO_NO_ACTION;
2966     s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
2967     break;
2968 
2969   case HTTP_STATUS_RANGE_NOT_SATISFIABLE:
2970   // Check if cached response supports Range. If it does, append
2971   // Range transformation plugin
2972   // A little misnomer. HTTP_STATUS_RANGE_NOT_SATISFIABLE
2973   // acutally means If-Range match fails here.
2974   // fall through
2975   default:
2976     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_HIT_SERVED);
2977     if (s->method == HTTP_WKSIDX_GET || s->api_resp_cacheable == true) {
2978       // send back the full document to the client.
2979       TxnDebug("http_trans", "[build_response_from_cache] Match! Serving full document.");
2980       s->cache_info.action = CACHE_DO_SERVE;
2981 
2982       // Check if cached response supports Range. If it does, append
2983       // Range transformation plugin
2984       // only if the cached response is a 200 OK
2985       if (client_response_code == HTTP_STATUS_OK && client_request->presence(MIME_PRESENCE_RANGE)) {
2986         s->state_machine->do_range_setup_if_necessary();
2987         if (s->range_setup == RANGE_NOT_SATISFIABLE) {
2988           build_error_response(s, HTTP_STATUS_RANGE_NOT_SATISFIABLE, "Requested Range Not Satisfiable", "default");
2989           s->cache_info.action = CACHE_DO_NO_ACTION;
2990           s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
2991           break;
2992         } else if ((s->range_setup == RANGE_NOT_HANDLED) || !s->range_in_cache) {
2993           // we switch to tunneling for Range requests if it is out of order.
2994           // or if the range can't be satisfied from the cache
2995           // In that case we fetch the entire source so it's OK to switch
2996           // this late.
2997