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 "ts/nexthop.h"
25 #include "tscore/ink_platform.h"
26 
27 #include <strings.h>
28 #include <cmath>
29 
30 #include "HttpTransact.h"
31 #include "HttpTransactHeaders.h"
32 #include "HttpSM.h"
33 #include "HttpCacheSM.h" //Added to get the scope of HttpCacheSM object - YTS Team, yamsat
34 #include "HttpDebugNames.h"
35 #include <ctime>
36 #include "tscore/ParseRules.h"
37 #include "tscore/Filenames.h"
38 #include "tscore/bwf_std_format.h"
39 #include "HTTP.h"
40 #include "HdrUtils.h"
41 #include "logging/Log.h"
42 #include "logging/LogUtils.h"
43 #include "CacheControl.h"
44 #include "ControlMatcher.h"
45 #include "ReverseProxy.h"
46 #include "HttpBodyFactory.h"
47 #include "StatPages.h"
48 #include "../IPAllow.h"
49 #include "I_Machine.h"
50 
51 // Support ip_resolve override.
__anonf2ce92800102(const void *data) 52 const MgmtConverter HttpTransact::HOST_RES_CONV{[](const void *data) -> std::string_view {
53                                                   const HostResData *host_res_data = static_cast<const HostResData *>(data);
54                                                   return host_res_data->conf_value;
55                                                 },
56                                                 [](void *data, std::string_view src) -> void {
57                                                   HostResData *res_data = static_cast<HostResData *>(data);
58                                                   parse_host_res_preference(src.data(), res_data->order);
59                                                 }};
60 
61 static char range_type[] = "multipart/byteranges; boundary=RANGE_SEPARATOR";
62 #define RANGE_NUMBERS_LENGTH 60
63 
64 #define TRANSACT_SETUP_RETURN(n, r) \
65   s->next_action           = n;     \
66   s->transact_return_point = r;     \
67   SpecificDebug((s->state_machine && s->state_machine->debug_on), "http_trans", "Next action %s; %s", #n, #r);
68 
69 #define TRANSACT_RETURN(n, r) \
70   TRANSACT_SETUP_RETURN(n, r) \
71   return;
72 
73 #define TRANSACT_RETURN_VAL(n, r, v) \
74   TRANSACT_SETUP_RETURN(n, r)        \
75   return v;
76 
77 #define SET_UNPREPARE_CACHE_ACTION(C)                               \
78   {                                                                 \
79     if (C.action == HttpTransact::CACHE_PREPARE_TO_DELETE) {        \
80       C.action = HttpTransact::CACHE_DO_DELETE;                     \
81     } else if (C.action == HttpTransact::CACHE_PREPARE_TO_UPDATE) { \
82       C.action = HttpTransact::CACHE_DO_UPDATE;                     \
83     } else {                                                        \
84       C.action = HttpTransact::CACHE_DO_WRITE;                      \
85     }                                                               \
86   }
87 
88 #define TxnDebug(tag, fmt, ...) \
89   SpecificDebug((s->state_machine->debug_on), tag, "[%" PRId64 "] " fmt, s->state_machine->sm_id, ##__VA_ARGS__)
90 
91 extern HttpBodyFactory *body_factory;
92 
93 // Handy typedef for short (single line) message generation.
94 using lbw = ts::LocalBufferWriter<256>;
95 
96 // wrapper to choose between a remap next hop strategy or use parent.config
97 // remap next hop strategy is preferred
98 inline static bool
bypass_ok(HttpTransact::State * s)99 bypass_ok(HttpTransact::State *s)
100 {
101   bool r          = false;
102   url_mapping *mp = s->url_map.getMapping();
103 
104   if (mp && mp->strategy) {
105     // remap strategies do not support the TSHttpTxnParentProxySet API.
106     r = mp->strategy->go_direct;
107   } else if (s->parent_params) {
108     r = s->parent_result.bypass_ok();
109   }
110   return r;
111 }
112 
113 // wrapper to choose between a remap next hop strategy or use parent.config
114 // remap next hop strategy is preferred
115 inline static bool
is_api_result(HttpTransact::State * s)116 is_api_result(HttpTransact::State *s)
117 {
118   bool r          = false;
119   url_mapping *mp = s->url_map.getMapping();
120 
121   if (mp && mp->strategy) {
122     // remap strategies do not support the TSHttpTxnParentProxySet API.
123     r = false;
124   } else if (s->parent_params) {
125     r = s->parent_result.is_api_result();
126   }
127   return r;
128 }
129 
130 // wrapper to get the max_retries.
131 // Does NOT check the strategy; if strategy exists, strategy->responseIsRetryable should be called instead.
132 inline static unsigned
max_retries(HttpTransact::State * s,ParentRetry_t method)133 max_retries(HttpTransact::State *s, ParentRetry_t method)
134 {
135   if (s->parent_params) {
136     return s->parent_result.max_retries(method);
137   }
138   return 0;
139 }
140 
141 // wrapper to get the numParents.
142 // Does NOT check the strategy; if strategy exists, strategy->responseIsRetryable should be called instead.
143 inline static uint32_t
numParents(HttpTransact::State * s)144 numParents(HttpTransact::State *s)
145 {
146   if (s->parent_params) {
147     return s->parent_params->numParents(&s->parent_result);
148   }
149   return 0;
150 }
151 
152 // wrapper to choose between a remap next hop strategy or use parent.config
153 // remap next hop strategy is preferred
154 inline static bool
parent_is_proxy(HttpTransact::State * s)155 parent_is_proxy(HttpTransact::State *s)
156 {
157   bool r          = false;
158   url_mapping *mp = s->url_map.getMapping();
159 
160   if (mp && mp->strategy) {
161     r = mp->strategy->parent_is_proxy;
162   } else if (s->parent_params) {
163     r = s->parent_result.parent_is_proxy();
164   }
165   return r;
166 }
167 
168 // wrapper to get the parent.config retry type.
169 // Does NOT check the strategy; if strategy exists, strategy->responseIsRetryable should be called instead.
170 inline static unsigned
retry_type(HttpTransact::State * s)171 retry_type(HttpTransact::State *s)
172 {
173   if (s->parent_params) {
174     return s->parent_result.retry_type();
175   }
176   return PARENT_RETRY_NONE;
177 }
178 
179 // wrapper to choose between a remap next hop strategy or use parent.config
180 // remap next hop strategy is preferred
181 inline static void
findParent(HttpTransact::State * s)182 findParent(HttpTransact::State *s)
183 {
184   url_mapping *mp = s->url_map.getMapping();
185 
186   if (mp && mp->strategy) {
187     return mp->strategy->findNextHop(reinterpret_cast<TSHttpTxn>(s->state_machine));
188   } else if (s->parent_params) {
189     return s->parent_params->findParent(&s->request_data, &s->parent_result, s->txn_conf->parent_fail_threshold,
190                                         s->txn_conf->parent_retry_time);
191   }
192 }
193 
194 // wrapper to choose between a remap next hop strategy or use parent.config
195 // remap next hop strategy is preferred
196 inline static void
markParentDown(HttpTransact::State * s)197 markParentDown(HttpTransact::State *s)
198 {
199   HTTP_INCREMENT_DYN_STAT(http_total_parent_marked_down_count);
200   url_mapping *mp = s->url_map.getMapping();
201 
202   if (mp && mp->strategy) {
203     return mp->strategy->markNextHop(reinterpret_cast<TSHttpTxn>(s->state_machine), s->parent_result.hostname,
204                                      s->parent_result.port, NH_MARK_DOWN);
205   } else if (s->parent_params) {
206     return s->parent_params->markParentDown(&s->parent_result, s->txn_conf->parent_fail_threshold, s->txn_conf->parent_retry_time);
207   }
208 }
209 
210 // wrapper to choose between a remap next hop strategy or use parent.config
211 // remap next hop strategy is preferred
212 inline static void
markParentUp(HttpTransact::State * s)213 markParentUp(HttpTransact::State *s)
214 {
215   url_mapping *mp = s->url_map.getMapping();
216   if (mp && mp->strategy) {
217     return mp->strategy->markNextHop(reinterpret_cast<TSHttpTxn>(s->state_machine), s->parent_result.hostname,
218                                      s->parent_result.port, NH_MARK_UP);
219   } else if (s->parent_params) {
220     return s->parent_params->markParentUp(&s->parent_result);
221   }
222 }
223 
224 // wrapper to choose between a remap next hop strategy or use parent.config
225 // remap next hop strategy is preferred
226 inline static bool
parentExists(HttpTransact::State * s)227 parentExists(HttpTransact::State *s)
228 {
229   url_mapping *mp = s->url_map.getMapping();
230   if (mp && mp->strategy) {
231     return mp->strategy->nextHopExists(reinterpret_cast<TSHttpTxn>(s->state_machine));
232   } else if (s->parent_params) {
233     return s->parent_params->parentExists(&s->request_data);
234   }
235   return false;
236 }
237 
238 // wrapper to choose between a remap next hop strategy or use parent.config
239 // remap next hop strategy is preferred
240 inline static void
nextParent(HttpTransact::State * s)241 nextParent(HttpTransact::State *s)
242 {
243   TxnDebug("parent_down", "sm_id[%" PRId64 "] connection to parent %s failed, conn_state: %s, request to origin: %s",
244            s->state_machine->sm_id, s->parent_result.hostname, HttpDebugNames::get_server_state_name(s->current.state),
245            s->request_data.get_host());
246   url_mapping *mp = s->url_map.getMapping();
247   if (mp && mp->strategy) {
248     // NextHop only has a findNextHop() function.
249     return mp->strategy->findNextHop(reinterpret_cast<TSHttpTxn>(s->state_machine));
250   } else if (s->parent_params) {
251     return s->parent_params->nextParent(&s->request_data, &s->parent_result, s->txn_conf->parent_fail_threshold,
252                                         s->txn_conf->parent_retry_time);
253   }
254 }
255 
256 inline static bool
is_localhost(const char * name,int len)257 is_localhost(const char *name, int len)
258 {
259   static const char local[] = "127.0.0.1";
260   return (len == (sizeof(local) - 1)) && (memcmp(name, local, len) == 0);
261 }
262 
263 inline static bool
is_response_simple_code(HTTPStatus response_code)264 is_response_simple_code(HTTPStatus response_code)
265 {
266   if (static_cast<unsigned int>(response_code) < 400 || static_cast<unsigned int>(response_code) > 499) {
267     return false;
268   }
269 
270   return true;
271 }
272 
273 inline static bool
is_response_unavailable_code(HTTPStatus response_code)274 is_response_unavailable_code(HTTPStatus response_code)
275 {
276   if (static_cast<unsigned int>(response_code) < 500 || static_cast<unsigned int>(response_code) > 599) {
277     return false;
278   }
279 
280   return true;
281 }
282 
283 bool
is_response_valid(State * s,HTTPHdr * incoming_response)284 HttpTransact::is_response_valid(State *s, HTTPHdr *incoming_response)
285 {
286   if (s->current.state != CONNECTION_ALIVE) {
287     ink_assert((s->current.state == CONNECTION_ERROR) || (s->current.state == OPEN_RAW_ERROR) ||
288                (s->current.state == PARSE_ERROR) || (s->current.state == CONNECTION_CLOSED) ||
289                (s->current.state == INACTIVE_TIMEOUT) || (s->current.state == ACTIVE_TIMEOUT) ||
290                s->current.state == OUTBOUND_CONGESTION);
291 
292     s->hdr_info.response_error = CONNECTION_OPEN_FAILED;
293     return false;
294   }
295 
296   s->hdr_info.response_error = check_response_validity(s, incoming_response);
297 
298   switch (s->hdr_info.response_error) {
299 #ifdef REALLY_NEED_TO_CHECK_DATE_VALIDITY
300   case BOGUS_OR_NO_DATE_IN_RESPONSE:
301     // We could modify the response to add the date, if need be.
302     //          incoming_response->set_date(s->request_sent_time);
303     return true;
304 #endif
305   case NO_RESPONSE_HEADER_ERROR:
306     TxnDebug("http_trans", "[is_response_valid] No errors in response");
307     return true;
308 
309   case MISSING_REASON_PHRASE:
310     TxnDebug("http_trans", "[is_response_valid] Response Error: Missing reason phrase - allowing");
311     return true;
312 
313   case STATUS_CODE_SERVER_ERROR:
314     TxnDebug("http_trans", "[is_response_valid] Response Error: Origin Server returned 500 - allowing");
315     return true;
316 
317   case CONNECTION_OPEN_FAILED:
318     TxnDebug("http_trans", "[is_response_valid] Response Error: connection open failed");
319     s->current.state = CONNECTION_ERROR;
320     return false;
321 
322   case NON_EXISTANT_RESPONSE_HEADER:
323     TxnDebug("http_trans", "[is_response_valid] Response Error: No response header");
324     s->current.state = BAD_INCOMING_RESPONSE;
325     return false;
326 
327   case NOT_A_RESPONSE_HEADER:
328     TxnDebug("http_trans", "[is_response_valid] Response Error: Not a response header");
329     s->current.state = BAD_INCOMING_RESPONSE;
330     return false;
331 
332   case MISSING_STATUS_CODE:
333     TxnDebug("http_trans", "[is_response_valid] Response Error: Missing status code");
334     s->current.state = BAD_INCOMING_RESPONSE;
335     return false;
336 
337   default:
338     TxnDebug("http_trans", "[is_response_valid] Errors in response");
339     s->current.state = BAD_INCOMING_RESPONSE;
340     return false;
341   }
342 }
343 
344 inline static ParentRetry_t
response_is_retryable(HttpTransact::State * s,HTTPStatus response_code)345 response_is_retryable(HttpTransact::State *s, HTTPStatus response_code)
346 {
347   if (!HttpTransact::is_response_valid(s, &s->hdr_info.server_response) || s->current.request_to != HttpTransact::PARENT_PROXY) {
348     return PARENT_RETRY_NONE;
349   }
350 
351   const url_mapping *mp = s->url_map.getMapping();
352   if (mp && mp->strategy) {
353     if (mp->strategy->responseIsRetryable(s->current.simple_retry_attempts, response_code)) {
354       if (mp->strategy->onFailureMarkParentDown(response_code)) {
355         return PARENT_RETRY_UNAVAILABLE_SERVER;
356       } else {
357         return PARENT_RETRY_SIMPLE;
358       }
359     } else {
360       return PARENT_RETRY_NONE;
361     }
362   }
363 
364   if (s->parent_params && !s->parent_result.response_is_retryable(response_code)) {
365     return PARENT_RETRY_NONE;
366   }
367 
368   const unsigned int s_retry_type  = retry_type(s);
369   const HTTPStatus server_response = http_hdr_status_get(s->hdr_info.server_response.m_http);
370   if ((s_retry_type & PARENT_RETRY_SIMPLE) && is_response_simple_code(server_response) &&
371       s->current.simple_retry_attempts < max_retries(s, PARENT_RETRY_SIMPLE)) {
372     if (s->current.simple_retry_attempts < numParents(s)) {
373       return PARENT_RETRY_SIMPLE;
374     }
375     return PARENT_RETRY_NONE;
376   }
377   if ((s_retry_type & PARENT_RETRY_UNAVAILABLE_SERVER) && is_response_unavailable_code(server_response) &&
378       s->current.unavailable_server_retry_attempts < max_retries(s, PARENT_RETRY_UNAVAILABLE_SERVER)) {
379     if (s->current.unavailable_server_retry_attempts < numParents(s)) {
380       return PARENT_RETRY_UNAVAILABLE_SERVER;
381     }
382     return PARENT_RETRY_NONE;
383   }
384   return PARENT_RETRY_NONE;
385 }
386 
387 inline static void
simple_or_unavailable_server_retry(HttpTransact::State * s)388 simple_or_unavailable_server_retry(HttpTransact::State *s)
389 {
390   if (!HttpTransact::is_response_valid(s, &s->hdr_info.server_response)) {
391     return; // must return now if the response isn't valid, before calling http_hdr_status_get on uninitialized data
392   }
393 
394   HTTPStatus server_response = http_hdr_status_get(s->hdr_info.server_response.m_http);
395   switch (response_is_retryable(s, server_response)) {
396   case PARENT_RETRY_SIMPLE:
397     s->current.state      = HttpTransact::PARENT_RETRY;
398     s->current.retry_type = PARENT_RETRY_SIMPLE;
399     break;
400   case PARENT_RETRY_UNAVAILABLE_SERVER:
401     s->current.state      = HttpTransact::PARENT_RETRY;
402     s->current.retry_type = PARENT_RETRY_UNAVAILABLE_SERVER;
403     break;
404   case PARENT_RETRY_BOTH:
405     ink_assert(!"response_is_retryable should return an exact retry type, never both");
406     break;
407   case PARENT_RETRY_NONE:
408     break; // no retry
409   }
410 }
411 
412 inline static bool
is_request_conditional(HTTPHdr * header)413 is_request_conditional(HTTPHdr *header)
414 {
415   uint64_t mask = (MIME_PRESENCE_IF_UNMODIFIED_SINCE | MIME_PRESENCE_IF_MODIFIED_SINCE | MIME_PRESENCE_IF_RANGE |
416                    MIME_PRESENCE_IF_MATCH | MIME_PRESENCE_IF_NONE_MATCH);
417   return (header->presence(mask) &&
418           (header->method_get_wksidx() == HTTP_WKSIDX_GET || header->method_get_wksidx() == HTTP_WKSIDX_HEAD));
419 }
420 
421 static inline bool
is_port_in_range(int port,HttpConfigPortRange * pr)422 is_port_in_range(int port, HttpConfigPortRange *pr)
423 {
424   while (pr) {
425     if (pr->low == -1) {
426       return true;
427     } else if ((pr->low <= port) && (pr->high >= port)) {
428       return true;
429     }
430 
431     pr = pr->next;
432   }
433 
434   return false;
435 }
436 
437 inline static void
update_cache_control_information_from_config(HttpTransact::State * s)438 update_cache_control_information_from_config(HttpTransact::State *s)
439 {
440   getCacheControl(&s->cache_control, &s->request_data, s->txn_conf);
441 
442   s->cache_info.directives.does_config_permit_lookup &= (s->cache_control.never_cache == false);
443   s->cache_info.directives.does_config_permit_storing &= (s->cache_control.never_cache == false);
444 
445   s->cache_info.directives.does_client_permit_storing =
446     HttpTransact::does_client_request_permit_storing(&s->cache_control, &s->hdr_info.client_request);
447 
448   s->cache_info.directives.does_client_permit_lookup = HttpTransact::does_client_request_permit_cached_response(
449     s->txn_conf, &s->cache_control, &s->hdr_info.client_request, s->via_string);
450 
451   s->cache_info.directives.does_client_permit_dns_storing =
452     HttpTransact::does_client_request_permit_dns_caching(&s->cache_control, &s->hdr_info.client_request);
453 
454   if (s->client_info.http_version == HTTP_0_9) {
455     s->cache_info.directives.does_client_permit_lookup  = false;
456     s->cache_info.directives.does_client_permit_storing = false;
457   }
458 
459   // Less than 0 means it wasn't overridden, so leave it alone.
460   if (s->cache_control.cache_responses_to_cookies >= 0) {
461     s->my_txn_conf().cache_responses_to_cookies = s->cache_control.cache_responses_to_cookies;
462   }
463 }
464 
465 bool
is_server_negative_cached(State * s)466 HttpTransact::is_server_negative_cached(State *s)
467 {
468   if (s->host_db_info.app.http_data.last_failure != 0 &&
469       s->host_db_info.app.http_data.last_failure + s->txn_conf->down_server_timeout > s->client_request_time) {
470     return true;
471   } else {
472     // Make sure some nasty clock skew has not happened
473     //  Use the server timeout to set an upperbound as to how far in the
474     //   future we should tolerate bogus last failure times.  This sets
475     //   the upper bound to the time that we would ever consider a server
476     //   down to 2*down_server_timeout
477     if (s->client_request_time + s->txn_conf->down_server_timeout < s->host_db_info.app.http_data.last_failure) {
478       s->host_db_info.app.http_data.last_failure = 0;
479       s->host_db_info.app.http_data.fail_count   = 0;
480       ink_assert(!"extreme clock skew");
481       return true;
482     }
483     return false;
484   }
485 }
486 
487 inline static void
update_current_info(HttpTransact::CurrentInfo * into,HttpTransact::ConnectionAttributes * from,HttpTransact::LookingUp_t who,int attempts)488 update_current_info(HttpTransact::CurrentInfo *into, HttpTransact::ConnectionAttributes *from, HttpTransact::LookingUp_t who,
489                     int attempts)
490 {
491   into->request_to = who;
492   into->server     = from;
493   into->attempts   = attempts;
494 }
495 
496 inline static void
update_dns_info(HttpTransact::DNSLookupInfo * dns,HttpTransact::CurrentInfo * from)497 update_dns_info(HttpTransact::DNSLookupInfo *dns, HttpTransact::CurrentInfo *from)
498 {
499   dns->looking_up  = from->request_to;
500   dns->lookup_name = from->server->name;
501 }
502 
503 inline static HTTPHdr *
find_appropriate_cached_resp(HttpTransact::State * s)504 find_appropriate_cached_resp(HttpTransact::State *s)
505 {
506   HTTPHdr *c_resp = nullptr;
507 
508   if (s->cache_info.object_store.valid()) {
509     c_resp = s->cache_info.object_store.response_get();
510     if (c_resp != nullptr && c_resp->valid()) {
511       return c_resp;
512     }
513   }
514 
515   ink_assert(s->cache_info.object_read != nullptr);
516   return s->cache_info.object_read->response_get();
517 }
518 
519 int response_cacheable_indicated_by_cc(HTTPHdr *response);
520 
521 inline static bool
is_negative_caching_appropriate(HttpTransact::State * s)522 is_negative_caching_appropriate(HttpTransact::State *s)
523 {
524   if (!s->txn_conf->negative_caching_enabled || !s->hdr_info.server_response.valid()) {
525     return false;
526   }
527 
528   int status  = s->hdr_info.server_response.status_get();
529   auto params = s->http_config_param;
530   if (params->negative_caching_list[status]) {
531     TxnDebug("http_trans", "%d is eligible for negative caching", status);
532     return true;
533   } else {
534     TxnDebug("http_trans", "%d is NOT eligible for negative caching", status);
535     return false;
536   }
537 }
538 
539 inline static HttpTransact::LookingUp_t
find_server_and_update_current_info(HttpTransact::State * s)540 find_server_and_update_current_info(HttpTransact::State *s)
541 {
542   int host_len;
543   const char *host = s->hdr_info.client_request.host_get(&host_len);
544 
545   if (is_localhost(host, host_len)) {
546     // Do not forward requests to local_host onto a parent.
547     // I just wanted to do this for cop heartbeats, someone else
548     // wanted it for all requests to local_host.
549     TxnDebug("http_trans", "request is from localhost, so bypass parent");
550     s->parent_result.result = PARENT_DIRECT;
551   } else if (s->method == HTTP_WKSIDX_CONNECT && s->http_config_param->disable_ssl_parenting) {
552     if (s->parent_result.result == PARENT_SPECIFIED) {
553       nextParent(s);
554     } else {
555       findParent(s);
556     }
557     if (!s->parent_result.is_some() || is_api_result(s) || parent_is_proxy(s)) {
558       TxnDebug("http_trans", "request not cacheable, so bypass parent");
559       s->parent_result.result = PARENT_DIRECT;
560     }
561   } else if (s->txn_conf->uncacheable_requests_bypass_parent && s->http_config_param->no_dns_forward_to_parent == 0 &&
562              !HttpTransact::is_request_cache_lookupable(s)) {
563     // request not lookupable and cacheable, so bypass parent if the parent is not an origin server.
564     // Note that the configuration of the proxy as well as the request
565     // itself affects the result of is_request_cache_lookupable();
566     // we are assuming both child and parent have similar configuration
567     // with respect to whether a request is cacheable or not.
568     // For example, the cache_urls_that_look_dynamic variable.
569     if (s->parent_result.result == PARENT_SPECIFIED) {
570       nextParent(s);
571     } else {
572       findParent(s);
573     }
574     if (!s->parent_result.is_some() || is_api_result(s) || parent_is_proxy(s)) {
575       TxnDebug("http_trans", "request not cacheable, so bypass parent");
576       s->parent_result.result = PARENT_DIRECT;
577     }
578   } else {
579     switch (s->parent_result.result) {
580     case PARENT_UNDEFINED:
581       findParent(s);
582       break;
583     case PARENT_SPECIFIED:
584       nextParent(s);
585 
586       // Hack!
587       // We already have a parent that failed, if we are now told
588       //  to go the origin server, we can only obey this if we
589       //  dns'ed the origin server
590       if (s->parent_result.result == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 0) {
591         ink_assert(!s->server_info.dst_addr.isValid());
592         s->parent_result.result = PARENT_FAIL;
593       }
594       break;
595     case PARENT_FAIL:
596       // Check to see if should bypass the parent and go direct
597       //   We can only do this if
598       //   1) the config permitted us to dns the origin server
599       //   2) the config permits us
600       //   3) the parent was not set from API
601       if (s->http_config_param->no_dns_forward_to_parent == 0 && bypass_ok(s) && parent_is_proxy(s) &&
602           !s->parent_params->apiParentExists(&s->request_data)) {
603         s->parent_result.result = PARENT_DIRECT;
604       }
605       break;
606     default:
607       ink_assert(0);
608     // FALL THROUGH
609     case PARENT_DIRECT:
610       //              // if we have already decided to go direct
611       //              // dont bother calling nextParent.
612       //              // do nothing here, guy.
613       break;
614     }
615   }
616 
617   switch (s->parent_result.result) {
618   case PARENT_SPECIFIED:
619     s->parent_info.name = s->arena.str_store(s->parent_result.hostname, strlen(s->parent_result.hostname));
620     update_current_info(&s->current, &s->parent_info, HttpTransact::PARENT_PROXY, (s->current.attempts)++);
621     update_dns_info(&s->dns_info, &s->current);
622     ink_assert(s->dns_info.looking_up == HttpTransact::PARENT_PROXY);
623     s->next_hop_scheme = URL_WKSIDX_HTTP;
624 
625     return HttpTransact::PARENT_PROXY;
626   case PARENT_FAIL:
627     // No more parents - need to return an error message
628     s->current.request_to = HttpTransact::HOST_NONE;
629     return HttpTransact::HOST_NONE;
630 
631   case PARENT_DIRECT:
632     // if the configuration does not allow the origin to be dns'd
633     // we're unable to go direct to the origin.
634     if (s->http_config_param->no_dns_forward_to_parent) {
635       Warning("no available parents and the config proxy.config.http.no_dns_just_forward_to_parent, prevents origin lookups.");
636       s->parent_result.result = PARENT_FAIL;
637       return HttpTransact::HOST_NONE;
638     }
639   /* fall through */
640   default:
641     update_current_info(&s->current, &s->server_info, HttpTransact::ORIGIN_SERVER, (s->current.attempts)++);
642     update_dns_info(&s->dns_info, &s->current);
643     ink_assert(s->dns_info.looking_up == HttpTransact::ORIGIN_SERVER);
644     s->next_hop_scheme = s->scheme;
645     return HttpTransact::ORIGIN_SERVER;
646   }
647 }
648 
649 inline static bool
do_cookies_prevent_caching(int cookies_conf,HTTPHdr * request,HTTPHdr * response,HTTPHdr * cached_request=nullptr)650 do_cookies_prevent_caching(int cookies_conf, HTTPHdr *request, HTTPHdr *response, HTTPHdr *cached_request = nullptr)
651 {
652   enum CookiesConfig {
653     COOKIES_CACHE_NONE             = 0, // do not cache any responses to cookies
654     COOKIES_CACHE_ALL              = 1, // cache for any content-type (ignore cookies)
655     COOKIES_CACHE_IMAGES           = 2, // cache only for image types
656     COOKIES_CACHE_ALL_BUT_TEXT     = 3, // cache for all but text content-types
657     COOKIES_CACHE_ALL_BUT_TEXT_EXT = 4  // cache for all but text content-types except with OS response
658                                         // without "Set-Cookie" or with "Cache-Control: public"
659   };
660 
661   const char *content_type = nullptr;
662   int str_len;
663 
664 #ifdef DEBUG
665   ink_assert(request->type_get() == HTTP_TYPE_REQUEST);
666   ink_assert(response->type_get() == HTTP_TYPE_RESPONSE);
667   if (cached_request) {
668     ink_assert(cached_request->type_get() == HTTP_TYPE_REQUEST);
669   }
670 #endif
671 
672   // Can cache all regardless of cookie header - just ignore all cookie headers
673   if (static_cast<CookiesConfig>(cookies_conf) == COOKIES_CACHE_ALL) {
674     return false;
675   }
676 
677   // It is considered that Set-Cookie headers can be safely ignored
678   // for non text content types if Cache-Control private is not set.
679   // This enables a bigger hit rate, which currently outweighs the risk of
680   // breaking origin servers that truly intend to set a cookie with other
681   // objects such as images.
682   // At this time, it is believed that only advertisers do this, and that
683   // customers won't care about it.
684 
685   // If the response does not have a Set-Cookie header and
686   // the response does not have a Cookie header and
687   // the object is not cached or the request does not have a Cookie header
688   // then cookies do not prevent caching.
689   if (!response->presence(MIME_PRESENCE_SET_COOKIE) && !request->presence(MIME_PRESENCE_COOKIE) &&
690       (cached_request == nullptr || !cached_request->presence(MIME_PRESENCE_COOKIE))) {
691     return false;
692   }
693 
694   // Do not cache if cookies option is COOKIES_CACHE_NONE
695   // and a Cookie is detected
696   if (static_cast<CookiesConfig>(cookies_conf) == COOKIES_CACHE_NONE) {
697     return true;
698   }
699   // All other options depend on the Content-Type
700   content_type = response->value_get(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE, &str_len);
701 
702   if (static_cast<CookiesConfig>(cookies_conf) == COOKIES_CACHE_IMAGES) {
703     if (content_type && str_len >= 5 && memcmp(content_type, "image", 5) == 0) {
704       // Images can be cached
705       return false;
706     }
707     return true; // do not cache if  COOKIES_CACHE_IMAGES && content_type != "image"
708   }
709   // COOKIES_CACHE_ALL_BUT_TEXT || COOKIES_CACHE_ALL_BUT_TEXT_EXT
710   // Note: if the configuration is bad, we consider
711   // COOKIES_CACHE_ALL_BUT_TEXT to be the default
712 
713   if (content_type && str_len >= 4 && memcmp(content_type, "text", 4) == 0) { // content type  - "text"
714     // Text objects cannot be cached unless the option is
715     // COOKIES_CACHE_ALL_BUT_TEXT_EXT.
716     // Furthermore, if there is a Set-Cookie header, then
717     // Cache-Control must be set.
718     if (static_cast<CookiesConfig>(cookies_conf) == COOKIES_CACHE_ALL_BUT_TEXT_EXT &&
719         ((!response->presence(MIME_PRESENCE_SET_COOKIE)) || response->is_cache_control_set(HTTP_VALUE_PUBLIC))) {
720       return false;
721     }
722     return true;
723   }
724   return false; // Non text objects can be cached
725 }
726 
727 inline static bool
does_method_require_cache_copy_deletion(const HttpConfigParams * http_config_param,const int method)728 does_method_require_cache_copy_deletion(const HttpConfigParams *http_config_param, const int method)
729 {
730   return ((method != HTTP_WKSIDX_GET) &&
731           (method == HTTP_WKSIDX_DELETE || method == HTTP_WKSIDX_PURGE || method == HTTP_WKSIDX_PUT ||
732            (http_config_param->cache_post_method != 1 && method == HTTP_WKSIDX_POST)));
733 }
734 
735 inline static bool
does_method_effect_cache(int method)736 does_method_effect_cache(int method)
737 {
738   return ((method == HTTP_WKSIDX_GET || method == HTTP_WKSIDX_DELETE || method == HTTP_WKSIDX_PURGE || method == HTTP_WKSIDX_PUT ||
739            method == HTTP_WKSIDX_POST));
740 }
741 
742 inline static HttpTransact::StateMachineAction_t
how_to_open_connection(HttpTransact::State * s)743 how_to_open_connection(HttpTransact::State *s)
744 {
745   ink_assert((s->pending_work == nullptr) || (s->current.request_to == HttpTransact::PARENT_PROXY));
746 
747   // Originally we returned which type of server to open
748   // Now, however, we may want to issue a cache
749   // operation first in order to lock the cache
750   // entry to prevent multiple origin server requests
751   // for the same document.
752   // The cache operation that we actually issue, of
753   // course, depends on the specified "cache_action".
754   // If there is no cache-action to be issued, just
755   // connect to the server.
756   switch (s->cache_info.action) {
757   case HttpTransact::CACHE_PREPARE_TO_DELETE:
758   case HttpTransact::CACHE_PREPARE_TO_UPDATE:
759   case HttpTransact::CACHE_PREPARE_TO_WRITE:
760     s->transact_return_point = HttpTransact::handle_cache_write_lock;
761     return HttpTransact::SM_ACTION_CACHE_ISSUE_WRITE;
762   default:
763     // This covers:
764     // CACHE_DO_UNDEFINED, CACHE_DO_NO_ACTION, CACHE_DO_DELETE,
765     // CACHE_DO_LOOKUP, CACHE_DO_REPLACE, CACHE_DO_SERVE,
766     // CACHE_DO_SERVE_AND_DELETE, CACHE_DO_SERVE_AND_UPDATE,
767     // CACHE_DO_UPDATE, CACHE_DO_WRITE, TOTAL_CACHE_ACTION_TYPES
768     break;
769   }
770 
771   HttpTransact::StateMachineAction_t connect_next_action = HttpTransact::SM_ACTION_ORIGIN_SERVER_OPEN;
772 
773   // Setting up a direct CONNECT tunnel enters OriginServerRawOpen. We always do that if we
774   // are not forwarding CONNECT and are not going to a parent proxy.
775   if (s->method == HTTP_WKSIDX_CONNECT) {
776     if (s->txn_conf->forward_connect_method != 1 && s->parent_result.result != PARENT_SPECIFIED) {
777       connect_next_action = HttpTransact::SM_ACTION_ORIGIN_SERVER_RAW_OPEN;
778     }
779   }
780 
781   if (!s->already_downgraded) { // false unless downgraded previously (possibly due to HTTP 505)
782     (&s->hdr_info.server_request)->version_set(HTTP_1_1);
783     HttpTransactHeaders::convert_request(s->current.server->http_version, &s->hdr_info.server_request);
784   }
785 
786   return connect_next_action;
787 }
788 
789 /*****************************************************************************
790  *****************************************************************************
791  ****                                                                     ****
792  ****                 HttpTransact State Machine Handlers                 ****
793  ****                                                                     ****
794  **** What follow from here on are the state machine handlers - the code  ****
795  **** which is called from HttpSM::set_next_state to specify              ****
796  **** what action the state machine needs to execute next. These ftns     ****
797  **** take as input just the state and set the next_action variable.      ****
798  *****************************************************************************
799  *****************************************************************************/
800 void
BadRequest(State * s)801 HttpTransact::BadRequest(State *s)
802 {
803   TxnDebug("http_trans", "[BadRequest]"
804                          "parser marked request bad");
805   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
806 
807   const char *body_factory_template = "request#syntax_error";
808   HTTPStatus status                 = HTTP_STATUS_BAD_REQUEST;
809   const char *reason                = "Invalid HTTP Request";
810 
811   switch (s->http_return_code) {
812   case HTTP_STATUS_REQUEST_URI_TOO_LONG:
813     body_factory_template = "request#uri_len_too_long";
814     status                = s->http_return_code;
815     reason                = "URI Too Long";
816     break;
817   case HTTP_STATUS_NOT_IMPLEMENTED:
818     status                = s->http_return_code;
819     reason                = "Field not implemented";
820     body_factory_template = "transcoding#unsupported";
821     break;
822   case HTTP_STATUS_HTTPVER_NOT_SUPPORTED:
823     status = s->http_return_code;
824     reason = "Unsupported HTTP Version";
825   default:
826     break;
827   }
828 
829   build_error_response(s, status, reason, body_factory_template);
830   s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
831   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
832 }
833 
834 void
PostActiveTimeoutResponse(State * s)835 HttpTransact::PostActiveTimeoutResponse(State *s)
836 {
837   TxnDebug("http_trans", "[PostActiveTimeoutResponse]"
838                          "post active timeout");
839   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
840   build_error_response(s, HTTP_STATUS_REQUEST_TIMEOUT, "Active Timeout", "timeout#activity");
841   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
842 }
843 
844 void
PostInactiveTimeoutResponse(State * s)845 HttpTransact::PostInactiveTimeoutResponse(State *s)
846 {
847   TxnDebug("http_trans", "[PostInactiveTimeoutResponse]"
848                          "post inactive timeout");
849   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
850   build_error_response(s, HTTP_STATUS_REQUEST_TIMEOUT, "Inactive Timeout", "timeout#inactivity");
851   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
852 }
853 
854 void
Forbidden(State * s)855 HttpTransact::Forbidden(State *s)
856 {
857   TxnDebug("http_trans", "[Forbidden]"
858                          "IpAllow marked request forbidden");
859   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
860   build_error_response(s, HTTP_STATUS_FORBIDDEN, "Access Denied", "access#denied");
861   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
862 }
863 
864 void
SelfLoop(State * s)865 HttpTransact::SelfLoop(State *s)
866 {
867   TxnDebug("http_trans", "[Loop]"
868                          "Request will selfloop.");
869   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
870   build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Direct self loop detected", "request#cycle_detected");
871   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
872 }
873 
874 void
TooEarly(State * s)875 HttpTransact::TooEarly(State *s)
876 {
877   TxnDebug("http_trans", "[TooEarly]"
878                          "Early Data method is not safe");
879   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
880   build_error_response(s, HTTP_STATUS_TOO_EARLY, "Too Early", "too#early");
881   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
882 }
883 
884 void
OriginDead(State * s)885 HttpTransact::OriginDead(State *s)
886 {
887   TxnDebug("http_trans", "origin server is marked down");
888   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
889   build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Origin Server Marked Down", "connect#failed_connect");
890   HTTP_INCREMENT_DYN_STAT(http_dead_server_no_requests);
891   char *url_str = s->hdr_info.client_request.url_string_get(&s->arena);
892   int host_len;
893   const char *host_name_ptr = s->unmapped_url.host_get(&host_len);
894   std::string_view host_name{host_name_ptr, size_t(host_len)};
895   TxnDebug("dead_server", "%s",
896            lbw()
897              .clip(1)
898              .print("CONNECT: dead server no request to {} for host='{}' url='{}'", s->current.server->dst_addr, host_name,
899                     ts::bwf::FirstOf(url_str, "<none>"))
900              .extend(1)
901              .write('\0')
902              .data());
903   s->arena.str_free(url_str);
904 
905   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
906 }
907 
908 void
HandleBlindTunnel(State * s)909 HttpTransact::HandleBlindTunnel(State *s)
910 {
911   URL u;
912   // IpEndpoint dest_addr;
913   // ip_text_buffer new_host;
914 
915   TxnDebug("http_trans", "[HttpTransact::HandleBlindTunnel]");
916 
917   // We set the version to 0.9 because once we know where we are going
918   //   this blind ssl tunnel is indistinguishable from a "CONNECT 0.9"
919   //   except for the need to suppression error messages
920   HTTPVersion ver(0, 9);
921   s->hdr_info.client_request.version_set(ver);
922 
923   // Initialize the state vars necessary to sending error responses
924   bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
925 
926   if (is_debug_tag_set("http_trans")) {
927     int host_len;
928     const char *host = s->hdr_info.client_request.url_get()->host_get(&host_len);
929     TxnDebug("http_trans", "[HandleBlindTunnel] destination set to %.*s:%d", host_len, host,
930              s->hdr_info.client_request.url_get()->port_get());
931   }
932 
933   // Set the mode to tunnel so that we don't lookup the cache
934   s->current.mode = TUNNELLING_PROXY;
935 
936   // Let the request work it's way through the code and
937   //  we grab it again after the raw connection has been opened
938   HandleRequest(s);
939 }
940 
941 void
StartRemapRequest(State * s)942 HttpTransact::StartRemapRequest(State *s)
943 {
944   if (s->api_skip_all_remapping) {
945     TxnDebug("http_trans", "API request to skip remapping");
946 
947     s->hdr_info.client_request.set_url_target_from_host_field();
948 
949     // Since we're not doing remap, we still have to allow for these overridable
950     // configurations to modify follow-redirect behavior. Someone could for example
951     // have set them in a plugin other than conf_remap running in a prior hook.
952     s->state_machine->enable_redirection = (s->txn_conf->number_of_redirections > 0);
953 
954     if (s->is_upgrade_request && s->post_remap_upgrade_return_point) {
955       TRANSACT_RETURN(SM_ACTION_POST_REMAP_SKIP, s->post_remap_upgrade_return_point);
956     }
957 
958     TRANSACT_RETURN(SM_ACTION_POST_REMAP_SKIP, HttpTransact::HandleRequest);
959   }
960 
961   TxnDebug("http_trans", "START HttpTransact::StartRemapRequest");
962 
963   //////////////////////////////////////////////////////////////////
964   // FIX: this logic seems awfully convoluted and hard to follow; //
965   //      seems like we could come up with a more elegant and     //
966   //      comprehensible design that generalized things           //
967   //////////////////////////////////////////////////////////////////
968 
969   /////////////////////////////////////////////////////////////////
970   // run the remap url-rewriting engine:                         //
971   //                                                             //
972   // * the variable <url_remap_success> is set true if           //
973   //   the url was rewritten                                     //
974   //                                                             //
975   // * the variable <remap_redirect> is set to non-NULL if there //
976   //   is a URL provided that the proxy is supposed to redirect  //
977   //   requesters of a particular URL to.                        //
978   /////////////////////////////////////////////////////////////////
979 
980   if (is_debug_tag_set("http_chdr_describe") || is_debug_tag_set("http_trans")) {
981     TxnDebug("http_trans", "Before Remapping:");
982     obj_describe(s->hdr_info.client_request.m_http, true);
983   }
984   DUMP_HEADER("http_hdrs", &s->hdr_info.client_request, s->state_machine_id, "Incoming Request");
985 
986   if (s->http_config_param->referer_filter_enabled) {
987     s->filter_mask = URL_REMAP_FILTER_REFERER;
988     if (s->http_config_param->referer_format_redirect) {
989       s->filter_mask |= URL_REMAP_FILTER_REDIRECT_FMT;
990     }
991   }
992 
993   TxnDebug("http_trans", "END HttpTransact::StartRemapRequest");
994 
995   TxnDebug("http_trans", "Checking if transaction wants to upgrade");
996   if (handle_upgrade_request(s)) {
997     // everything should be handled by the upgrade handler.
998     TxnDebug("http_trans", "Transaction will be upgraded by the appropriate upgrade handler.");
999     return;
1000   }
1001 
1002   TRANSACT_RETURN(SM_ACTION_API_PRE_REMAP, HttpTransact::PerformRemap);
1003 }
1004 
1005 void
PerformRemap(State * s)1006 HttpTransact::PerformRemap(State *s)
1007 {
1008   TxnDebug("http_trans", "Inside PerformRemap");
1009   TRANSACT_RETURN(SM_ACTION_REMAP_REQUEST, HttpTransact::EndRemapRequest);
1010 }
1011 
1012 void
EndRemapRequest(State * s)1013 HttpTransact::EndRemapRequest(State *s)
1014 {
1015   TxnDebug("http_trans", "START HttpTransact::EndRemapRequest");
1016 
1017   HTTPHdr *incoming_request = &s->hdr_info.client_request;
1018   int method                = incoming_request->method_get_wksidx();
1019   int host_len;
1020   const char *host = incoming_request->host_get(&host_len);
1021   TxnDebug("http_trans", "EndRemapRequest host is %.*s", host_len, host);
1022 
1023   // Setting enable_redirection according to HttpConfig (master or overridable). We
1024   // defer this as late as possible, to allow plugins to modify the overridable
1025   // configurations (e.g. conf_remap.so). We intentionally only modify this if
1026   // the configuration says so.
1027   s->state_machine->enable_redirection = (s->txn_conf->number_of_redirections > 0);
1028 
1029   ////////////////////////////////////////////////////////////////
1030   // if we got back a URL to redirect to, vector the user there //
1031   ////////////////////////////////////////////////////////////////
1032   if (s->remap_redirect != nullptr) {
1033     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
1034     const char *error_body_type;
1035     switch (s->http_return_code) {
1036     case HTTP_STATUS_MOVED_PERMANENTLY:
1037     case HTTP_STATUS_PERMANENT_REDIRECT:
1038     case HTTP_STATUS_SEE_OTHER:
1039     case HTTP_STATUS_USE_PROXY:
1040       error_body_type = "redirect#moved_permanently";
1041       break;
1042     case HTTP_STATUS_MOVED_TEMPORARILY:
1043     case HTTP_STATUS_TEMPORARY_REDIRECT:
1044       error_body_type = "redirect#moved_temporarily";
1045       break;
1046     default:
1047       if (HTTP_STATUS_NONE == s->http_return_code) {
1048         s->http_return_code = HTTP_STATUS_MOVED_TEMPORARILY;
1049         Warning("Changed status code from '0' to '%d'.", s->http_return_code);
1050       } else {
1051         Warning("Using invalid status code for redirect '%d'. Building a response for a temporary redirect.", s->http_return_code);
1052       }
1053       error_body_type = "redirect#moved_temporarily";
1054     }
1055     build_error_response(s, s->http_return_code, "Redirect", error_body_type);
1056     ats_free(s->remap_redirect);
1057     s->reverse_proxy = false;
1058     goto done;
1059   }
1060   /////////////////////////////////////////////////////
1061   // Quick HTTP filtering (primary key: http method) //
1062   /////////////////////////////////////////////////////
1063   process_quick_http_filter(s, method);
1064   /////////////////////////////////////////////////////////////////////////
1065   // We must close this connection if client_connection_enabled == false //
1066   /////////////////////////////////////////////////////////////////////////
1067   if (!s->client_connection_enabled) {
1068     build_error_response(s, HTTP_STATUS_FORBIDDEN, "Access Denied", "access#denied");
1069     s->reverse_proxy = false;
1070     goto done;
1071   }
1072   /////////////////////////////////////////////////////////////////
1073   // Check if remap plugin set HTTP return code and return body  //
1074   /////////////////////////////////////////////////////////////////
1075   if (s->http_return_code != HTTP_STATUS_NONE) {
1076     build_error_response(s, s->http_return_code, nullptr, nullptr);
1077     s->reverse_proxy = false;
1078     goto done;
1079   }
1080 
1081   ///////////////////////////////////////////////////////////////
1082   // if no mapping was found, handle the cases where:          //
1083   //                                                           //
1084   // (1) reverse proxy is on, and no URL host (server request) //
1085   // (2) no mappings are found, but mappings strictly required //
1086   ///////////////////////////////////////////////////////////////
1087 
1088   if (!s->url_remap_success) {
1089     /**
1090      * It's better to test redirect rules just after url_remap failed
1091      * Or those successfully remapped rules might be redirected
1092      **/
1093     if (handleIfRedirect(s)) {
1094       TxnDebug("http_trans", "END HttpTransact::RemapRequest");
1095       TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, nullptr);
1096     }
1097 
1098     if (!s->http_config_param->url_remap_required && !incoming_request->is_target_in_url()) {
1099       s->hdr_info.client_request.set_url_target_from_host_field();
1100     }
1101 
1102     /////////////////////////////////////////////////////////
1103     // check for: (1) reverse proxy is on, and no URL host //
1104     /////////////////////////////////////////////////////////
1105     if (s->http_config_param->reverse_proxy_enabled && !s->client_info.is_transparent && !incoming_request->is_target_in_url()) {
1106       /////////////////////////////////////////////////////////
1107       // the url mapping failed, reverse proxy was enabled,
1108       // and the request contains no host:
1109       //
1110       // * if there is an explanatory redirect, send there.
1111       // * if there was no host, send "no host" error.
1112       // * if there was a host, say "not found".
1113       /////////////////////////////////////////////////////////
1114 
1115       char *redirect_url   = s->http_config_param->reverse_proxy_no_host_redirect;
1116       int redirect_url_len = s->http_config_param->reverse_proxy_no_host_redirect_len;
1117 
1118       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
1119       if (redirect_url) { /* there is a redirect url */
1120         build_error_response(s, HTTP_STATUS_MOVED_TEMPORARILY, "Redirect For Explanation", "request#no_host");
1121         s->hdr_info.client_response.value_set(MIME_FIELD_LOCATION, MIME_LEN_LOCATION, redirect_url, redirect_url_len);
1122         // socket when there is no host. Need to handle DNS failure elsewhere.
1123       } else if (host == nullptr) { /* no host */
1124         build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Host Header Required", "request#no_host");
1125         s->squid_codes.log_code = SQUID_LOG_ERR_INVALID_URL;
1126       } else {
1127         build_error_response(s, HTTP_STATUS_NOT_FOUND, "Not Found on Accelerator", "urlrouting#no_mapping");
1128         s->squid_codes.log_code = SQUID_LOG_ERR_INVALID_URL;
1129       }
1130       s->reverse_proxy = false;
1131       goto done;
1132     } else if (s->http_config_param->url_remap_required) {
1133       ///////////////////////////////////////////////////////
1134       // the url mapping failed, but mappings are strictly //
1135       // required so return an error message.              //
1136       ///////////////////////////////////////////////////////
1137       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
1138       build_error_response(s, HTTP_STATUS_NOT_FOUND, "Not Found", "urlrouting#no_mapping");
1139       s->squid_codes.log_code = SQUID_LOG_ERR_INVALID_URL;
1140 
1141       s->reverse_proxy = false;
1142       goto done;
1143     }
1144   } else {
1145     if (s->http_config_param->reverse_proxy_enabled) {
1146       s->req_flavor = REQ_FLAVOR_REVPROXY;
1147     }
1148   }
1149   s->reverse_proxy              = true;
1150   s->server_info.is_transparent = s->state_machine->ua_txn ? s->state_machine->ua_txn->is_outbound_transparent() : false;
1151 
1152 done:
1153   // We now set the active-timeout again, since it might have been changed as part of the remap rules.
1154   if (s->state_machine->ua_txn) {
1155     s->state_machine->ua_txn->set_active_timeout(HRTIME_SECONDS(s->txn_conf->transaction_active_timeout_in));
1156   }
1157 
1158   if (is_debug_tag_set("http_chdr_describe") || is_debug_tag_set("http_trans") || is_debug_tag_set("url_rewrite")) {
1159     TxnDebug("http_trans", "After Remapping:");
1160     obj_describe(s->hdr_info.client_request.m_http, true);
1161   }
1162 
1163   /*
1164     if s->reverse_proxy == false, we can assume remapping failed in some way
1165       -however-
1166     If an API setup a tunnel to fake the origin or proxy's response we will
1167     continue to handle the request (as this was likely the plugin author's intent)
1168 
1169     otherwise, 502/404 the request right now. /eric
1170   */
1171   if (!s->reverse_proxy && s->state_machine->plugin_tunnel_type == HTTP_NO_PLUGIN_TUNNEL) {
1172     TxnDebug("http_trans", "END HttpTransact::EndRemapRequest");
1173     HTTP_INCREMENT_DYN_STAT(http_invalid_client_requests_stat);
1174     TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1175   } else {
1176     s->hdr_info.client_response.destroy(); // release the underlying memory.
1177     s->hdr_info.client_response.clear();   // clear the pointers.
1178     TxnDebug("http_trans", "END HttpTransact::EndRemapRequest");
1179 
1180     if (s->is_upgrade_request && s->post_remap_upgrade_return_point) {
1181       TRANSACT_RETURN(SM_ACTION_API_POST_REMAP, s->post_remap_upgrade_return_point);
1182     }
1183 
1184     TRANSACT_RETURN(SM_ACTION_API_POST_REMAP, HttpTransact::HandleRequest);
1185   }
1186 
1187   ink_assert(!"not reached");
1188 }
1189 
1190 bool
handle_upgrade_request(State * s)1191 HttpTransact::handle_upgrade_request(State *s)
1192 {
1193   HTTPHdr &request = s->hdr_info.client_request;
1194   s->method        = request.method_get_wksidx();
1195 
1196   // Quickest way to determine that this is defintely not an upgrade.
1197   /* RFC 6455 The method of the request MUST be GET, and the HTTP version MUST
1198         be at least 1.1. */
1199   if (!s->hdr_info.client_request.presence(MIME_PRESENCE_UPGRADE) ||
1200       !s->hdr_info.client_request.presence(MIME_PRESENCE_CONNECTION) || s->method != HTTP_WKSIDX_GET ||
1201       s->hdr_info.client_request.version_get() < HTTP_1_1) {
1202     return false;
1203   }
1204 
1205   MIMEField *upgrade_hdr    = s->hdr_info.client_request.field_find(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
1206   MIMEField *connection_hdr = s->hdr_info.client_request.field_find(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
1207 
1208   StrList connection_hdr_vals;
1209   const char *upgrade_hdr_val = nullptr;
1210   int upgrade_hdr_val_len     = 0;
1211 
1212   if (!upgrade_hdr || !connection_hdr || connection_hdr->value_get_comma_list(&connection_hdr_vals) == 0 ||
1213       (upgrade_hdr_val = upgrade_hdr->value_get(&upgrade_hdr_val_len)) == nullptr) {
1214     TxnDebug("http_trans_upgrade", "Transaction wasn't a valid upgrade request, proceeding as a normal HTTP request.");
1215     return false;
1216   }
1217 
1218   /*
1219    * In order for this request to be treated as a normal upgrade request we must have a Connection: Upgrade header
1220    * and a Upgrade: header, with a non-empty value, otherwise we just assume it's not an Upgrade Request, after
1221    * we've verified that, we will try to match this upgrade to a known upgrade type such as Websockets.
1222    */
1223   bool connection_contains_upgrade = false;
1224   // Next, let's validate that the Connection header contains an Upgrade key
1225   for (int i = 0; i < connection_hdr_vals.count; ++i) {
1226     Str *val = connection_hdr_vals.get_idx(i);
1227     if (ptr_len_casecmp(val->str, val->len, MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE) == 0) {
1228       connection_contains_upgrade = true;
1229       break;
1230     }
1231   }
1232 
1233   if (!connection_contains_upgrade) {
1234     TxnDebug("http_trans_upgrade",
1235              "Transaction wasn't a valid upgrade request, proceeding as a normal HTTP request, missing Connection upgrade header.");
1236     return false;
1237   }
1238 
1239   // Mark this request as an upgrade request.
1240   s->is_upgrade_request = true;
1241 
1242   /*
1243      RFC 6455
1244      The request MUST contain an |Upgrade| header field whose value
1245         MUST include the "websocket" keyword.
1246      The request MUST contain a |Connection| header field whose value
1247         MUST include the "Upgrade" token. // Checked Above
1248      The request MUST include a header field with the name
1249         |Sec-WebSocket-Key|.
1250      The request MUST include a header field with the name
1251         |Sec-WebSocket-Version|.  The value of this header field MUST be
1252         13.
1253    */
1254   if (hdrtoken_tokenize(upgrade_hdr_val, upgrade_hdr_val_len, &s->upgrade_token_wks) >= 0) {
1255     if (s->upgrade_token_wks == MIME_VALUE_WEBSOCKET) {
1256       MIMEField *sec_websocket_key =
1257         s->hdr_info.client_request.field_find(MIME_FIELD_SEC_WEBSOCKET_KEY, MIME_LEN_SEC_WEBSOCKET_KEY);
1258       MIMEField *sec_websocket_ver =
1259         s->hdr_info.client_request.field_find(MIME_FIELD_SEC_WEBSOCKET_VERSION, MIME_LEN_SEC_WEBSOCKET_VERSION);
1260 
1261       if (sec_websocket_key && sec_websocket_ver && sec_websocket_ver->value_get_int() == 13) {
1262         TxnDebug("http_trans_upgrade", "Transaction wants upgrade to websockets");
1263         handle_websocket_upgrade_pre_remap(s);
1264         return true;
1265       } else {
1266         TxnDebug("http_trans_upgrade", "Unable to upgrade connection to websockets, invalid headers (RFC 6455).");
1267       }
1268     } else if (s->upgrade_token_wks == MIME_VALUE_H2C) {
1269       // We need to recognize h2c to not handle it as an error.
1270       // We just ignore the Upgrade header and respond to the request as though the Upgrade header field were absent.
1271       s->is_upgrade_request = false;
1272       return false;
1273     }
1274   } else {
1275     TxnDebug("http_trans_upgrade", "Transaction requested upgrade for unknown protocol: %s", upgrade_hdr_val);
1276   }
1277 
1278   build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid Upgrade Request", "request#syntax_error");
1279 
1280   // we want our modify_request method to just return while we fail out from here.
1281   // this seems like the preferred option because the user wanted to do an upgrade but sent a bad protocol.
1282   TRANSACT_RETURN_VAL(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr, true);
1283 }
1284 
1285 void
handle_websocket_upgrade_pre_remap(State * s)1286 HttpTransact::handle_websocket_upgrade_pre_remap(State *s)
1287 {
1288   TxnDebug("http_trans_websocket_upgrade_pre_remap", "Prepping transaction before remap.");
1289 
1290   /*
1291    * We will use this opportunity to set everything up so that during the remap stage we can deal with
1292    * ws:// and wss:// remap rules, and then we will take over again post remap.
1293    */
1294   s->is_websocket                    = true;
1295   s->post_remap_upgrade_return_point = HttpTransact::handle_websocket_upgrade_post_remap;
1296 
1297   /* let's modify the url scheme to be wss or ws, so remapping will happen as expected */
1298   URL *url = s->hdr_info.client_request.url_get();
1299   if (url->scheme_get_wksidx() == URL_WKSIDX_HTTP) {
1300     TxnDebug("http_trans_websocket_upgrade_pre_remap", "Changing scheme to WS for remapping.");
1301     url->scheme_set(URL_SCHEME_WS, URL_LEN_WS);
1302   } else if (url->scheme_get_wksidx() == URL_WKSIDX_HTTPS) {
1303     TxnDebug("http_trans_websocket_upgrade_pre_remap", "Changing scheme to WSS for remapping.");
1304     url->scheme_set(URL_SCHEME_WSS, URL_LEN_WSS);
1305   } else {
1306     TxnDebug("http_trans_websocket_upgrade_pre_remap", "Invalid scheme for websocket upgrade");
1307     build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid Upgrade Request", "request#syntax_error");
1308     TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1309   }
1310 
1311   TRANSACT_RETURN(SM_ACTION_API_PRE_REMAP, HttpTransact::PerformRemap);
1312 }
1313 
1314 void
handle_websocket_upgrade_post_remap(State * s)1315 HttpTransact::handle_websocket_upgrade_post_remap(State *s)
1316 {
1317   TxnDebug("http_trans_websocket_upgrade_post_remap", "Remap is complete, start websocket upgrade");
1318 
1319   TRANSACT_RETURN(SM_ACTION_API_POST_REMAP, HttpTransact::handle_websocket_connection);
1320 }
1321 
1322 void
handle_websocket_connection(State * s)1323 HttpTransact::handle_websocket_connection(State *s)
1324 {
1325   TxnDebug("http_trans_websocket", "START handle_websocket_connection");
1326 
1327   HandleRequest(s);
1328 }
1329 
1330 static bool
mimefield_value_equal(MIMEField * field,const char * value,const int value_len)1331 mimefield_value_equal(MIMEField *field, const char *value, const int value_len)
1332 {
1333   int field_value_len     = 0;
1334   const char *field_value = field->value_get(&field_value_len);
1335 
1336   if (field_value != nullptr && field_value_len == value_len) {
1337     return !strncasecmp(field_value, value, value_len);
1338   }
1339 
1340   return false;
1341 }
1342 
1343 void
ModifyRequest(State * s)1344 HttpTransact::ModifyRequest(State *s)
1345 {
1346   int scheme, hostname_len;
1347   HTTPHdr &request              = s->hdr_info.client_request;
1348   static const int PORT_PADDING = 8;
1349 
1350   TxnDebug("http_trans", "START HttpTransact::ModifyRequest");
1351 
1352   // Initialize the state vars necessary to sending error responses
1353   bootstrap_state_variables_from_request(s, &request);
1354 
1355   ////////////////////////////////////////////////
1356   // If there is no scheme, default to http      //
1357   ////////////////////////////////////////////////
1358   URL *url = request.url_get();
1359 
1360   s->orig_scheme = (scheme = url->scheme_get_wksidx());
1361 
1362   s->method = request.method_get_wksidx();
1363   if (scheme < 0 && s->method != HTTP_WKSIDX_CONNECT) {
1364     if (s->client_info.port_attribute == HttpProxyPort::TRANSPORT_SSL) {
1365       url->scheme_set(URL_SCHEME_HTTPS, URL_LEN_HTTPS);
1366       s->orig_scheme = URL_WKSIDX_HTTPS;
1367     } else {
1368       url->scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP);
1369       s->orig_scheme = URL_WKSIDX_HTTP;
1370     }
1371   }
1372 
1373   if (s->method == HTTP_WKSIDX_CONNECT && !request.is_port_in_header()) {
1374     url->port_set(80);
1375   }
1376 
1377   // Ugly - this must come after the call to url->scheme_set or
1378   // it can't get the scheme properly and the wrong data is cached.
1379   // The solution should be to move the scheme detecting logic in to
1380   // the header class, rather than doing it in a random bit of
1381   // external code.
1382   const char *buf = request.host_get(&hostname_len);
1383   if (!request.is_target_in_url()) {
1384     s->hdr_info.client_req_is_server_style = true;
1385   }
1386   // Copy out buf to a hostname just in case its heap header memory is freed during coalescing
1387   // due to later HdrHeap operations
1388   char *hostname = static_cast<char *>(alloca(hostname_len + PORT_PADDING));
1389   memcpy(hostname, buf, hostname_len);
1390 
1391   // Make clang analyzer happy. hostname is non-null iff request.is_target_in_url().
1392   ink_assert(hostname || s->hdr_info.client_req_is_server_style);
1393 
1394   // If the incoming request is proxy-style make sure the Host: header
1395   // matches the incoming request URL. The exception is if we have
1396   // Max-Forwards set to 0 in the request
1397   int max_forwards = -1; // -1 is a valid value meaning that it didn't find the header
1398   if (request.presence(MIME_PRESENCE_MAX_FORWARDS)) {
1399     max_forwards = request.get_max_forwards();
1400   }
1401 
1402   if ((max_forwards != 0) && !s->hdr_info.client_req_is_server_style && s->method != HTTP_WKSIDX_CONNECT) {
1403     MIMEField *host_field = request.field_find(MIME_FIELD_HOST, MIME_LEN_HOST);
1404     in_port_t port        = url->port_get_raw();
1405 
1406     // Form the host:port string if not a default port (e.g. 80)
1407     // We allocated extra space for the port above
1408     if (port > 0) {
1409       hostname_len += snprintf(hostname + hostname_len, PORT_PADDING, ":%u", port);
1410     }
1411 
1412     // No host_field means not equal to host and will need to be set, so create it now.
1413     if (!host_field) {
1414       host_field = request.field_create(MIME_FIELD_HOST, MIME_LEN_HOST);
1415       request.field_attach(host_field);
1416     }
1417 
1418     if (mimefield_value_equal(host_field, hostname, hostname_len) == false) {
1419       request.field_value_set(host_field, hostname, hostname_len);
1420       request.mark_target_dirty();
1421     }
1422   }
1423 
1424   TxnDebug("http_trans", "END HttpTransact::ModifyRequest");
1425 
1426   TRANSACT_RETURN(SM_ACTION_API_READ_REQUEST_HDR, HttpTransact::StartRemapRequest);
1427 }
1428 
1429 // This function is supposed to figure out if this transaction is
1430 // susceptible to a redirection as specified by remap.config
1431 bool
handleIfRedirect(State * s)1432 HttpTransact::handleIfRedirect(State *s)
1433 {
1434   int answer;
1435   URL redirect_url;
1436 
1437   answer = request_url_remap_redirect(&s->hdr_info.client_request, &redirect_url, s->state_machine->m_remap);
1438   if ((answer == PERMANENT_REDIRECT) || (answer == TEMPORARY_REDIRECT)) {
1439     int remap_redirect_len;
1440 
1441     s->remap_redirect = redirect_url.string_get(&s->arena, &remap_redirect_len);
1442     redirect_url.destroy();
1443     if (answer == TEMPORARY_REDIRECT) {
1444       if ((s->client_info).http_version == HTTP_1_1) {
1445         build_error_response(s, HTTP_STATUS_TEMPORARY_REDIRECT, "Redirect", "redirect#moved_temporarily");
1446       } else {
1447         build_error_response(s, HTTP_STATUS_MOVED_TEMPORARILY, "Redirect", "redirect#moved_temporarily");
1448       }
1449     } else {
1450       build_error_response(s, HTTP_STATUS_MOVED_PERMANENTLY, "Redirect", "redirect#moved_permanently");
1451     }
1452     s->arena.str_free(s->remap_redirect);
1453     s->remap_redirect = nullptr;
1454     return true;
1455   }
1456 
1457   return false;
1458 }
1459 
1460 void
HandleRequest(State * s)1461 HttpTransact::HandleRequest(State *s)
1462 {
1463   TxnDebug("http_trans", "START HttpTransact::HandleRequest");
1464 
1465   if (!s->state_machine->is_waiting_for_full_body && !s->state_machine->is_using_post_buffer) {
1466     ink_assert(!s->hdr_info.server_request.valid());
1467 
1468     HTTP_INCREMENT_DYN_STAT(http_incoming_requests_stat);
1469 
1470     if (s->client_info.port_attribute == HttpProxyPort::TRANSPORT_SSL) {
1471       HTTP_INCREMENT_DYN_STAT(https_incoming_requests_stat);
1472     }
1473 
1474     ///////////////////////////////////////////////
1475     // if request is bad, return error response  //
1476     ///////////////////////////////////////////////
1477 
1478     if (!(is_request_valid(s, &s->hdr_info.client_request))) {
1479       HTTP_INCREMENT_DYN_STAT(http_invalid_client_requests_stat);
1480       TxnDebug("http_seq", "[HttpTransact::HandleRequest] request invalid.");
1481       s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
1482       //  s->next_action = HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
1483       return;
1484     }
1485     TxnDebug("http_seq", "[HttpTransact::HandleRequest] request valid.");
1486 
1487     if (is_debug_tag_set("http_chdr_describe")) {
1488       obj_describe(s->hdr_info.client_request.m_http, true);
1489     }
1490     // at this point we are guaranteed that the request is good and acceptable.
1491     // initialize some state variables from the request (client version,
1492     // client keep-alive, cache action, etc.
1493     initialize_state_variables_from_request(s, &s->hdr_info.client_request);
1494     // The following chunk of code will limit the maximum number of websocket connections (TS-3659)
1495     if (s->is_upgrade_request && s->is_websocket && s->http_config_param->max_websocket_connections >= 0) {
1496       int64_t val = 0;
1497       HTTP_READ_DYN_SUM(http_websocket_current_active_client_connections_stat, val);
1498       if (val >= s->http_config_param->max_websocket_connections) {
1499         s->is_websocket = false; // unset to avoid screwing up stats.
1500         TxnDebug("http_trans", "Rejecting websocket connection because the limit has been exceeded");
1501         bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
1502         build_error_response(s, HTTP_STATUS_SERVICE_UNAVAILABLE, "WebSocket Connection Limit Exceeded", nullptr);
1503         TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1504       }
1505     }
1506 
1507     // The following code is configurable to allow a user to control the max post size (TS-3631)
1508     if (s->http_config_param->max_post_size > 0 && s->hdr_info.request_content_length > 0 &&
1509         s->hdr_info.request_content_length > s->http_config_param->max_post_size) {
1510       TxnDebug("http_trans", "Max post size %" PRId64 " Client tried to post a body that was too large.",
1511                s->http_config_param->max_post_size);
1512       HTTP_INCREMENT_DYN_STAT(http_post_body_too_large);
1513       bootstrap_state_variables_from_request(s, &s->hdr_info.client_request);
1514       build_error_response(s, HTTP_STATUS_REQUEST_ENTITY_TOO_LARGE, "Request Entity Too Large", "request#entity_too_large");
1515       s->squid_codes.log_code = SQUID_LOG_ERR_POST_ENTITY_TOO_LARGE;
1516       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1517     }
1518 
1519     // The following chunk of code allows you to disallow post w/ expect 100-continue (TS-3459)
1520     if (s->hdr_info.request_content_length && s->http_config_param->disallow_post_100_continue) {
1521       MIMEField *expect = s->hdr_info.client_request.field_find(MIME_FIELD_EXPECT, MIME_LEN_EXPECT);
1522 
1523       if (expect != nullptr) {
1524         const char *expect_hdr_val = nullptr;
1525         int expect_hdr_val_len     = 0;
1526         expect_hdr_val             = expect->value_get(&expect_hdr_val_len);
1527         if (ptr_len_casecmp(expect_hdr_val, expect_hdr_val_len, HTTP_VALUE_100_CONTINUE, HTTP_LEN_100_CONTINUE) == 0) {
1528           // Let's error out this request.
1529           TxnDebug("http_trans", "Client sent a post expect: 100-continue, sending 405.");
1530           HTTP_INCREMENT_DYN_STAT(disallowed_post_100_continue);
1531           build_error_response(s, HTTP_STATUS_METHOD_NOT_ALLOWED, "Method Not Allowed", "request#method_unsupported");
1532           TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1533         }
1534       }
1535     }
1536     if (s->txn_conf->request_buffer_enabled &&
1537         s->state_machine->ua_txn->has_request_body(s->hdr_info.request_content_length,
1538                                                    s->client_info.transfer_encoding == CHUNKED_ENCODING)) {
1539       TRANSACT_RETURN(SM_ACTION_WAIT_FOR_FULL_BODY, nullptr);
1540     }
1541   }
1542 
1543   // Cache lookup or not will be decided later at DecideCacheLookup().
1544   // Before it's decided to do a cache lookup,
1545   // assume no cache lookup and using proxy (not tunneling)
1546   s->cache_info.action = CACHE_DO_NO_ACTION;
1547   s->current.mode      = GENERIC_PROXY;
1548 
1549   // initialize the cache_control structure read from cache.config
1550   update_cache_control_information_from_config(s);
1551 
1552   // We still need to decide whether or not to do a cache lookup since
1553   // the scheduled update code depends on this info.
1554   if (is_request_cache_lookupable(s)) {
1555     s->cache_info.action = CACHE_DO_LOOKUP;
1556   }
1557 
1558   // If the hostname is "$internal$" then this is a request for
1559   // internal proxy information.
1560   if (handle_internal_request(s, &s->hdr_info.client_request)) {
1561     TRANSACT_RETURN(SM_ACTION_INTERNAL_REQUEST, nullptr);
1562   }
1563 
1564   if (s->state_machine->plugin_tunnel_type == HTTP_PLUGIN_AS_INTERCEPT) {
1565     setup_plugin_request_intercept(s);
1566     return;
1567   }
1568 
1569   // if ip in url or cop test page, not do srv lookup.
1570   if (s->txn_conf->srv_enabled) {
1571     IpEndpoint addr;
1572     ats_ip_pton(s->server_info.name, &addr);
1573     s->my_txn_conf().srv_enabled = !ats_is_ip(&addr);
1574   }
1575 
1576   // if the request is a trace or options request, decrement the
1577   // max-forwards value. if the incoming max-forwards value was 0,
1578   // then we have to return a response to the client with the
1579   // appropriate action for trace/option. in this case this routine
1580   // is responsible for building the response.
1581   if (handle_trace_and_options_requests(s, &s->hdr_info.client_request)) {
1582     TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, nullptr);
1583   }
1584 
1585   if (s->http_config_param->no_dns_forward_to_parent && s->scheme != URL_WKSIDX_HTTPS &&
1586       strcmp(s->server_info.name, "127.0.0.1") != 0) {
1587     // for HTTPS requests, we must go directly to the
1588     // origin server. Ignore the no_dns_just_forward_to_parent setting.
1589     // we need to see if the hostname is an
1590     //   ip address since the parent selection code result
1591     //   could change as a result of this ip address
1592     IpEndpoint addr;
1593     ats_ip_pton(s->server_info.name, &addr);
1594     if (ats_is_ip(&addr)) {
1595       ats_ip_copy(&s->request_data.dest_ip, &addr);
1596     }
1597 
1598     if (parentExists(s)) {
1599       // If the proxy is behind and firewall and there is no
1600       //  DNS service available, we just want to forward the request
1601       //  the parent proxy.  In this case, we never find out the
1602       //  origin server's ip.  So just skip past OSDNS
1603       ats_ip_invalidate(&s->server_info.dst_addr);
1604       StartAccessControl(s);
1605       return;
1606     } else if (s->http_config_param->no_origin_server_dns) {
1607       build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Next Hop Connection Failed", "connect#failed_connect");
1608 
1609       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1610     }
1611   }
1612 
1613   // Added to skip the dns if the document is in the cache.
1614   // DNS is requested before cache lookup only if there are rules in cache.config , parent.config or
1615   // if the newly added variable doc_in_cache_skip_dns is not enabled
1616   if (s->dns_info.lookup_name[0] <= '9' && s->dns_info.lookup_name[0] >= '0' &&
1617       (!s->state_machine->enable_redirection || !s->redirect_info.redirect_in_process) &&
1618       s->parent_params->parent_table->hostMatch) {
1619     s->force_dns = true;
1620   }
1621   /* A redirect means we need to check some things again.
1622      If the cache is enabled then we need to check the new (redirected) request against the cache.
1623      If not, then we need to at least do DNS again to guarantee we are using the correct IP address
1624      (if the host changed). Note DNS comes after cache lookup so in both cases we do the DNS.
1625   */
1626   if (s->redirect_info.redirect_in_process && s->state_machine->enable_redirection) {
1627     if (s->txn_conf->cache_http) {
1628       TRANSACT_RETURN(SM_ACTION_CACHE_LOOKUP, nullptr);
1629     } else {
1630       return CallOSDNSLookup(s);
1631     }
1632   }
1633 
1634   if (s->force_dns) {
1635     return CallOSDNSLookup(s);
1636   } else {
1637     // After the requested is properly handled No need of requesting the DNS directly check the ACLs
1638     // if the request is authorized
1639     StartAccessControl(s);
1640   }
1641 }
1642 
1643 void
HandleRequestBufferDone(State * s)1644 HttpTransact::HandleRequestBufferDone(State *s)
1645 {
1646   TRANSACT_RETURN(SM_ACTION_REQUEST_BUFFER_READ_COMPLETE, HttpTransact::HandleRequest);
1647 }
1648 
1649 void
setup_plugin_request_intercept(State * s)1650 HttpTransact::setup_plugin_request_intercept(State *s)
1651 {
1652   ink_assert(s->state_machine->plugin_tunnel != nullptr);
1653 
1654   // Plugin is intercepting the request which means
1655   //  that we don't do dns, cache read or cache write
1656   //
1657   // We just want to write the request straight to the plugin
1658   if (s->cache_info.action != HttpTransact::CACHE_DO_NO_ACTION) {
1659     s->cache_info.action = HttpTransact::CACHE_DO_NO_ACTION;
1660     s->current.mode      = TUNNELLING_PROXY;
1661     HTTP_INCREMENT_DYN_STAT(http_tunnels_stat);
1662   }
1663   // Regardless of the protocol we're gatewaying to
1664   //   we see the scheme as http
1665   s->scheme = s->next_hop_scheme = URL_WKSIDX_HTTP;
1666 
1667   // Set up a "fake" server server entry
1668   update_current_info(&s->current, &s->server_info, HttpTransact::ORIGIN_SERVER, 0);
1669 
1670   // Also "fake" the info we'd normally get from
1671   //   hostDB
1672   s->server_info.http_version                = HTTP_1_0;
1673   s->server_info.keep_alive                  = HTTP_NO_KEEPALIVE;
1674   s->host_db_info.app.http_data.http_version = HTTP_1_0;
1675   s->server_info.dst_addr.setToAnyAddr(AF_INET);                                 // must set an address or we can't set the port.
1676   s->server_info.dst_addr.port() = htons(s->hdr_info.client_request.port_get()); // this is the info that matters.
1677 
1678   // Build the request to the server
1679   build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->client_info.http_version);
1680 
1681   // We don't do keep alive over these impersonated
1682   //  NetVCs so nuke the connection header
1683   s->hdr_info.server_request.field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
1684 
1685   TRANSACT_RETURN(SM_ACTION_ORIGIN_SERVER_OPEN, nullptr);
1686 }
1687 
1688 ////////////////////////////////////////////////////////////////////////
1689 // void HttpTransact::HandleApiErrorJump(State* s)
1690 //
1691 //   Called after an API function indicates it wished to send an
1692 //     error to the user agent
1693 ////////////////////////////////////////////////////////////////////////
1694 void
HandleApiErrorJump(State * s)1695 HttpTransact::HandleApiErrorJump(State *s)
1696 {
1697   TxnDebug("http_trans", "[HttpTransact::HandleApiErrorJump]");
1698 
1699   // since the READ_REQUEST_HDR_HOOK is processed before
1700   //   we examine the request, returning TS_EVENT_ERROR will cause
1701   //   the protocol in the via string to be "?"  Set it here
1702   //   since we know it has to be http
1703   // For CONNECT method, next_hop_scheme is NULL
1704   if (s->next_hop_scheme < 0) {
1705     s->next_hop_scheme = URL_WKSIDX_HTTP;
1706   }
1707   // The client response may not be empty in the
1708   // case the txn was reenabled in error by a plugin from hook SEND_RESPONSE_HDR.
1709   // build_response doesn't clean the header. So clean it up before.
1710   // Do fields_clear() instead of clear() to prevent memory leak
1711   if (s->hdr_info.client_response.valid()) {
1712     s->hdr_info.client_response.fields_clear();
1713   }
1714 
1715   // Set the source to internal so chunking is handled correctly
1716   s->source = SOURCE_INTERNAL;
1717 
1718   /**
1719     The API indicated an error. Lets use a >=400 error from the state (if one's set) or fallback to a
1720     generic HTTP/1.X 500 INKApi Error
1721   **/
1722   if (s->http_return_code && s->http_return_code >= HTTP_STATUS_BAD_REQUEST) {
1723     const char *reason = http_hdr_reason_lookup(s->http_return_code);
1724     ;
1725     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, s->http_return_code, reason ? reason : "Error");
1726   } else {
1727     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, HTTP_STATUS_INTERNAL_SERVER_ERROR, "INKApi Error");
1728   }
1729 
1730   TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, nullptr);
1731   return;
1732 }
1733 
1734 ///////////////////////////////////////////////////////////////////////////////
1735 // Name       : PPDNSLookup
1736 // Description: called after DNS lookup of parent proxy name
1737 //
1738 // Details    :
1739 //
1740 // the configuration information gave us the name of the parent proxy
1741 // to send the request to. this function is called after the dns lookup
1742 // for that name. it may fail, in which case we look for the next parent
1743 // proxy to try and if none exist, then go to the origin server.
1744 // if the lookup succeeds, we open a connection to the parent proxy.
1745 //
1746 //
1747 // Possible Next States From Here:
1748 
1749 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
1750 // - HttpTransact::ORIGIN_SERVER_RAW_OPEN;
1751 // - HttpTransact::ORIGIN_SERVER_OPEN;
1752 //
1753 ///////////////////////////////////////////////////////////////////////////////
1754 void
PPDNSLookup(State * s)1755 HttpTransact::PPDNSLookup(State *s)
1756 {
1757   TxnDebug("http_trans", "[HttpTransact::PPDNSLookup]");
1758 
1759   ink_assert(s->dns_info.looking_up == PARENT_PROXY);
1760   if (!s->dns_info.lookup_success) {
1761     // Mark parent as down due to resolving failure
1762     markParentDown(s);
1763     // DNS lookup of parent failed, find next parent or o.s.
1764     if (find_server_and_update_current_info(s) == HttpTransact::HOST_NONE) {
1765       ink_assert(s->current.request_to == HOST_NONE);
1766       handle_parent_died(s);
1767       return;
1768     }
1769 
1770     if (!s->current.server->dst_addr.isValid()) {
1771       if (s->current.request_to == PARENT_PROXY) {
1772         TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
1773       } else if (s->parent_result.result == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 1) {
1774         // We ran out of parents but parent configuration allows us to go to Origin Server directly
1775         return CallOSDNSLookup(s);
1776       } else {
1777         // We could be out of parents here if all the parents failed DNS lookup
1778         ink_assert(s->current.request_to == HOST_NONE);
1779         handle_parent_died(s);
1780       }
1781       return;
1782     }
1783   } else {
1784     // lookup succeeded, open connection to p.p.
1785     ats_ip_copy(&s->parent_info.dst_addr, s->host_db_info.ip());
1786     s->parent_info.dst_addr.port() = htons(s->parent_result.port);
1787     get_ka_info_from_host_db(s, &s->parent_info, &s->client_info, &s->host_db_info);
1788 
1789     char addrbuf[INET6_ADDRSTRLEN];
1790     TxnDebug("http_trans", "[PPDNSLookup] DNS lookup for sm_id[%" PRId64 "] successful IP: %s", s->state_machine->sm_id,
1791              ats_ip_ntop(&s->parent_info.dst_addr.sa, addrbuf, sizeof(addrbuf)));
1792   }
1793 
1794   // Since this function can be called several times while retrying
1795   //  parents, check to see if we've already built our request
1796   if (!s->hdr_info.server_request.valid()) {
1797     build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
1798 
1799     // Take care of deferred (issue revalidate) work in building
1800     //   the request
1801     if (s->pending_work != nullptr) {
1802       ink_assert(s->pending_work == issue_revalidate);
1803       (*s->pending_work)(s);
1804       s->pending_work = nullptr;
1805     }
1806   }
1807   // what kind of a connection (raw, simple)
1808   s->next_action = how_to_open_connection(s);
1809 }
1810 
1811 ///////////////////////////////////////////////////////////////////////////////
1812 //
1813 // Name       : ReDNSRoundRobin
1814 // Description: Called after we fail to contact part of a round-robin
1815 //              robin server set and we found a another ip address.
1816 //
1817 // Details    :
1818 //
1819 //
1820 //
1821 // Possible Next States From Here:
1822 // - HttpTransact::ORIGIN_SERVER_RAW_OPEN;
1823 // - HttpTransact::ORIGIN_SERVER_OPEN;
1824 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
1825 //
1826 ///////////////////////////////////////////////////////////////////////////////
1827 void
ReDNSRoundRobin(State * s)1828 HttpTransact::ReDNSRoundRobin(State *s)
1829 {
1830   ink_assert(s->current.server == &s->server_info);
1831   ink_assert(s->current.server->had_connect_fail());
1832 
1833   if (s->dns_info.lookup_success) {
1834     // We using a new server now so clear the connection
1835     //  failure mark
1836     s->current.server->clear_connect_fail();
1837 
1838     // Our ReDNS of the server succeeded so update the necessary
1839     //  information and try again. Need to preserve the current port value if possible.
1840     in_port_t server_port = s->current.server->dst_addr.host_order_port();
1841     // Temporary check to make sure the port preservation can be depended upon. That should be the case
1842     // because we get here only after trying a connection. Remove for 6.2.
1843     ink_assert(s->current.server->dst_addr.isValid() && 0 != server_port);
1844 
1845     ats_ip_copy(&s->server_info.dst_addr, s->host_db_info.ip());
1846     s->server_info.dst_addr.port() = htons(server_port);
1847     ats_ip_copy(&s->request_data.dest_ip, &s->server_info.dst_addr);
1848     get_ka_info_from_host_db(s, &s->server_info, &s->client_info, &s->host_db_info);
1849 
1850     char addrbuf[INET6_ADDRSTRLEN];
1851     TxnDebug("http_trans", "[ReDNSRoundRobin] DNS lookup for O.S. successful IP: %s",
1852              ats_ip_ntop(&s->server_info.dst_addr.sa, addrbuf, sizeof(addrbuf)));
1853 
1854     s->next_action = how_to_open_connection(s);
1855   } else {
1856     // Our ReDNS failed so output the DNS failure error message
1857     // Set to internal server error so later logging will pick up SQUID_LOG_ERR_DNS_FAIL
1858     build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Cannot find server.", "connect#dns_failed");
1859     s->cache_info.action = CACHE_DO_NO_ACTION;
1860     s->next_action       = SM_ACTION_SEND_ERROR_CACHE_NOOP;
1861     //  s->next_action = PROXY_INTERNAL_CACHE_NOOP;
1862     char *url_str = s->hdr_info.client_request.url_string_get(&s->arena, nullptr);
1863     Log::error("%s",
1864                lbw().clip(1).print("DNS Error: looking up {}", ts::bwf::FirstOf(url_str, "<none>")).extend(1).write('\0').data());
1865   }
1866 
1867   return;
1868 }
1869 
1870 ///////////////////////////////////////////////////////////////////////////////
1871 // Name       : OSDNSLookup
1872 // Description: called after the DNS lookup of origin server name
1873 //
1874 // Details    :
1875 //
1876 // normally called after Start. may be called more than once, however,
1877 // if the dns lookup fails. this may be because
1878 // it was not possible to resolve the name after several attempts.
1879 //
1880 // the next action depends. since this function is normally called after
1881 // a request has come in, which is valid and does not require an immediate
1882 // response, the next action may just be to open a connection to the
1883 // origin server, or a parent proxy, or the next action may be to do a
1884 // cache lookup, or in the event of an error, the next action may be to
1885 // send a response back to the client.
1886 //
1887 //
1888 // Possible Next States From Here:
1889 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
1890 // - HttpTransact::CACHE_LOOKUP;
1891 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
1892 // - HttpTransact::ORIGIN_SERVER_RAW_OPEN;
1893 // - HttpTransact::ORIGIN_SERVER_OPEN;
1894 //
1895 ///////////////////////////////////////////////////////////////////////////////
1896 void
OSDNSLookup(State * s)1897 HttpTransact::OSDNSLookup(State *s)
1898 {
1899   ink_assert(s->dns_info.looking_up == ORIGIN_SERVER);
1900 
1901   TxnDebug("http_trans", "[HttpTransact::OSDNSLookup]");
1902 
1903   // It's never valid to connect *to* INADDR_ANY, so let's reject the request now.
1904   if (ats_is_ip_any(s->host_db_info.ip())) {
1905     TxnDebug("http_trans", "[OSDNSLookup] Invalid request IP: INADDR_ANY");
1906     build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Bad Destination Address", "request#syntax_error");
1907     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
1908     TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1909   }
1910 
1911   if (!s->dns_info.lookup_success) {
1912     if (DNSLookupInfo::OS_Addr::OS_ADDR_TRY_HOSTDB == s->dns_info.os_addr_style) {
1913       /*
1914        *  Transparent case: We tried to connect to client target address, failed and tried to use a different addr
1915        *  No HostDB data, just keep on with the CTA.
1916        */
1917       s->dns_info.lookup_success = true;
1918       s->dns_info.os_addr_style  = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT;
1919       TxnDebug("http_seq", "[HttpTransact::OSDNSLookup] DNS lookup unsuccessful, using client target address");
1920     } else {
1921       TxnDebug("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup unsuccessful");
1922 
1923       // output the DNS failure error message
1924       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
1925       // Set to internal server error so later logging will pick up SQUID_LOG_ERR_DNS_FAIL
1926       build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Cannot find server.", "connect#dns_failed");
1927       char *url_str = s->hdr_info.client_request.url_string_get(&s->arena, nullptr);
1928       Log::error("%s",
1929                  lbw().clip(1).print("DNS Error: looking up {}", ts::bwf::FirstOf(url_str, "<none>")).extend(1).write('\0').data());
1930       // s->cache_info.action = CACHE_DO_NO_ACTION;
1931       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
1932     }
1933     return;
1934   }
1935 
1936   // The dns lookup succeeded
1937   ink_assert(s->dns_info.lookup_success);
1938   TxnDebug("http_seq", "[HttpTransact::OSDNSLookup] DNS Lookup successful");
1939 
1940   // For the transparent case, nail down the kind of address we are really using
1941   if (DNSLookupInfo::OS_Addr::OS_ADDR_TRY_HOSTDB == s->dns_info.os_addr_style) {
1942     // We've backed off from a client supplied address and found some
1943     // HostDB addresses. We use those if they're different from the CTA.
1944     // In all cases we now commit to client or HostDB for our source.
1945     if (s->host_db_info.round_robin) {
1946       HostDBInfo *cta = s->host_db_info.rr()->select_next(&s->current.server->dst_addr.sa);
1947       if (cta) {
1948         // found another addr, lock in host DB.
1949         s->host_db_info           = *cta;
1950         s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB;
1951       } else {
1952         // nothing else there, continue with CTA.
1953         s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT;
1954       }
1955     } else if (ats_ip_addr_eq(s->host_db_info.ip(), &s->server_info.dst_addr.sa)) {
1956       s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT;
1957     } else {
1958       s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB;
1959     }
1960   }
1961 
1962   // Check to see if can fullfill expect requests based on the cached
1963   // update some state variables with hostdb information that has
1964   // been provided.
1965   ats_ip_copy(&s->server_info.dst_addr, s->host_db_info.ip());
1966   // If the SRV response has a port number, we should honor it. Otherwise we do the port defined in remap
1967   if (s->dns_info.srv_lookup_success) {
1968     s->server_info.dst_addr.port() = htons(s->dns_info.srv_port);
1969   } else if (!s->api_server_addr_set) {
1970     s->server_info.dst_addr.port() = htons(s->hdr_info.client_request.port_get()); // now we can set the port.
1971   }
1972   ats_ip_copy(&s->request_data.dest_ip, &s->server_info.dst_addr);
1973   get_ka_info_from_host_db(s, &s->server_info, &s->client_info, &s->host_db_info);
1974 
1975   char addrbuf[INET6_ADDRSTRLEN];
1976   TxnDebug("http_trans",
1977            "[OSDNSLookup] DNS lookup for O.S. successful "
1978            "IP: %s",
1979            ats_ip_ntop(&s->server_info.dst_addr.sa, addrbuf, sizeof(addrbuf)));
1980 
1981   if (s->redirect_info.redirect_in_process) {
1982     // If dns lookup was not successful, the code below will handle the error.
1983     RedirectEnabled::Action action = RedirectEnabled::Action::INVALID;
1984     if (true == Machine::instance()->is_self(s->host_db_info.ip())) {
1985       action = s->http_config_param->redirect_actions_self_action;
1986     } else {
1987       // Make sure the return value from contains is big enough for a void*.
1988       intptr_t x{intptr_t(RedirectEnabled::Action::INVALID)};
1989       ink_release_assert(s->http_config_param->redirect_actions_map != nullptr);
1990       ink_release_assert(s->http_config_param->redirect_actions_map->contains(s->host_db_info.ip(), reinterpret_cast<void **>(&x)));
1991       action = static_cast<RedirectEnabled::Action>(x);
1992     }
1993 
1994     if (action == RedirectEnabled::Action::FOLLOW) {
1995       TxnDebug("http_trans", "[OSDNSLookup] Invalid redirect address. Following");
1996     } else if (action == RedirectEnabled::Action::REJECT || s->hdr_info.server_response.valid() == false) {
1997       if (action == RedirectEnabled::Action::REJECT) {
1998         TxnDebug("http_trans", "[OSDNSLookup] Invalid redirect address. Rejecting.");
1999       } else {
2000         // Invalid server response, since we can't copy it we are going to reject
2001         TxnDebug("http_trans", "[OSDNSLookup] Invalid server response. Rejecting.");
2002         Error("Invalid server response. Rejecting. IP: %s", ats_ip_ntop(&s->server_info.dst_addr.sa, addrbuf, sizeof(addrbuf)));
2003       }
2004       build_error_response(s, HTTP_STATUS_FORBIDDEN, nullptr, "request#syntax_error");
2005       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
2006       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
2007     } else {
2008       // Return this 3xx to the client as-is
2009       if (action == RedirectEnabled::Action::RETURN) {
2010         TxnDebug("http_trans", "[OSDNSLookup] Configured to return on invalid redirect address.");
2011       } else {
2012         TxnDebug("http_trans", "[OSDNSLookup] Invalid redirect address. Returning.");
2013       }
2014       build_response_copy(s, &s->hdr_info.server_response, &s->hdr_info.client_response, s->client_info.http_version);
2015       TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, nullptr);
2016     }
2017   }
2018 
2019   // everything succeeded with the DNS lookup so do an API callout
2020   //   that allows for filtering.  We'll do traffic_server internal
2021   //   filtering after API filtering
2022 
2023   // After SM_ACTION_DNS_LOOKUP, goto the saved action/state ORIGIN_SERVER_(RAW_)OPEN.
2024   // Should we skip the StartAccessControl()? why?
2025 
2026   if (DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT == s->dns_info.os_addr_style ||
2027       DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB == s->dns_info.os_addr_style) {
2028     // we've come back after already trying the server to get a better address
2029     // and finished with all backtracking - return to trying the server.
2030     TRANSACT_RETURN(how_to_open_connection(s), HttpTransact::HandleResponse);
2031   } else if (s->dns_info.lookup_name[0] <= '9' && s->dns_info.lookup_name[0] >= '0' && s->parent_params->parent_table->hostMatch &&
2032              !s->http_config_param->no_dns_forward_to_parent) {
2033     // note, broken logic: ACC fudges the OR stmt to always be true,
2034     // 'AuthHttpAdapter' should do the rev-dns if needed, not here .
2035     TRANSACT_RETURN(SM_ACTION_DNS_REVERSE_LOOKUP, HttpTransact::StartAccessControl);
2036   } else {
2037     if (s->force_dns) {
2038       StartAccessControl(s); // If skip_dns is enabled and no ip based rules in cache.config and parent.config
2039       // Access Control is called after DNS response
2040     } else {
2041       if ((s->cache_info.action == CACHE_DO_NO_ACTION) &&
2042           (((s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE) && !s->txn_conf->cache_range_write) ||
2043             s->range_setup == RANGE_NOT_SATISFIABLE || s->range_setup == RANGE_NOT_HANDLED))) {
2044         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HandleCacheOpenReadMiss);
2045       } else if (!s->txn_conf->cache_http || s->cache_lookup_result == HttpTransact::CACHE_LOOKUP_SKIPPED) {
2046         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, LookupSkipOpenServer);
2047         // DNS Lookup is done after LOOKUP Skipped  and after we get response
2048         // from the DNS we need to call LookupSkipOpenServer
2049       } else if (is_cache_hit(s->cache_lookup_result)) {
2050         // DNS lookup is done if the content is state need to call handle cache open read hit
2051         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HandleCacheOpenReadHit);
2052       } else if (s->cache_lookup_result == CACHE_LOOKUP_MISS || s->cache_info.action == CACHE_DO_NO_ACTION) {
2053         TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HandleCacheOpenReadMiss);
2054         // DNS lookup is done if the lookup failed and need to call Handle Cache Open Read Miss
2055       } else {
2056         build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Invalid Cache Lookup result", "default");
2057         Log::error("HTTP: Invalid CACHE LOOKUP RESULT : %d", s->cache_lookup_result);
2058         TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
2059       }
2060     }
2061   }
2062 }
2063 
2064 void
StartAccessControl(State * s)2065 HttpTransact::StartAccessControl(State *s)
2066 {
2067   HandleRequestAuthorized(s);
2068 }
2069 
2070 void
HandleRequestAuthorized(State * s)2071 HttpTransact::HandleRequestAuthorized(State *s)
2072 {
2073   if (s->force_dns) {
2074     TRANSACT_RETURN(SM_ACTION_API_OS_DNS, HttpTransact::DecideCacheLookup);
2075   } else {
2076     HttpTransact::DecideCacheLookup(s);
2077   }
2078 }
2079 
2080 void
DecideCacheLookup(State * s)2081 HttpTransact::DecideCacheLookup(State *s)
2082 {
2083   // Check if a client request is lookupable.
2084   if (s->redirect_info.redirect_in_process) {
2085     // for redirect, we want to skip cache lookup and write into
2086     // the cache directly with the URL before the redirect
2087     s->cache_info.action = CACHE_DO_NO_ACTION;
2088     s->current.mode      = GENERIC_PROXY;
2089   } else {
2090     if (is_request_cache_lookupable(s) && !s->is_upgrade_request) {
2091       s->cache_info.action = CACHE_DO_LOOKUP;
2092       s->current.mode      = GENERIC_PROXY;
2093     } else {
2094       s->cache_info.action = CACHE_DO_NO_ACTION;
2095       s->current.mode      = TUNNELLING_PROXY;
2096       HTTP_INCREMENT_DYN_STAT(http_tunnels_stat);
2097     }
2098   }
2099 
2100   // at this point the request is ready to continue down the
2101   // traffic server path.
2102 
2103   // now decide whether the cache can even be looked up.
2104   if (s->cache_info.action == CACHE_DO_LOOKUP) {
2105     TxnDebug("http_trans", "[DecideCacheLookup] Will do cache lookup.");
2106     TxnDebug("http_seq", "[DecideCacheLookup] Will do cache lookup");
2107     ink_assert(s->current.mode != TUNNELLING_PROXY);
2108 
2109     if (s->cache_info.lookup_url == nullptr) {
2110       HTTPHdr *incoming_request = &s->hdr_info.client_request;
2111 
2112       if (s->txn_conf->maintain_pristine_host_hdr) {
2113         s->cache_info.lookup_url_storage.create(nullptr);
2114         s->cache_info.lookup_url_storage.copy(incoming_request->url_get());
2115         s->cache_info.lookup_url = &s->cache_info.lookup_url_storage;
2116         // if the target isn't in the URL, put it in the copy for
2117         // cache lookup.
2118         incoming_request->set_url_target_from_host_field(s->cache_info.lookup_url);
2119       } else {
2120         // make sure the target is in the URL.
2121         incoming_request->set_url_target_from_host_field();
2122         s->cache_info.lookup_url = incoming_request->url_get();
2123       }
2124 
2125       // *somebody* wants us to not hack the host header in a reverse proxy setup.
2126       // In addition, they want us to reverse proxy for 6000 servers, which vary
2127       // the stupid content on the Host header!!!!
2128       // We could a) have 6000 alts (barf, puke, vomit) or b) use the original
2129       // host header in the url before doing all cache actions (lookups, writes, etc.)
2130       if (s->txn_conf->maintain_pristine_host_hdr) {
2131         const char *host_hdr;
2132         const char *port_hdr;
2133         int host_len, port_len;
2134         // So, the host header will have the original host header.
2135         if (incoming_request->get_host_port_values(&host_hdr, &host_len, &port_hdr, &port_len)) {
2136           int port = 0;
2137           if (port_hdr) {
2138             s->cache_info.lookup_url->host_set(host_hdr, host_len);
2139             port = ink_atoi(port_hdr, port_len);
2140           } else {
2141             s->cache_info.lookup_url->host_set(host_hdr, host_len);
2142           }
2143           s->cache_info.lookup_url->port_set(port);
2144         }
2145       }
2146       ink_assert(s->cache_info.lookup_url->valid() == true);
2147     }
2148 
2149     TRANSACT_RETURN(SM_ACTION_CACHE_LOOKUP, nullptr);
2150   } else {
2151     ink_assert(s->cache_info.action != CACHE_DO_LOOKUP && s->cache_info.action != CACHE_DO_SERVE);
2152 
2153     TxnDebug("http_trans", "[DecideCacheLookup] Will NOT do cache lookup.");
2154     TxnDebug("http_seq", "[DecideCacheLookup] Will NOT do cache lookup");
2155     // If this is a push request, we need send an error because
2156     //   since what ever was sent is not cacheable
2157     if (s->method == HTTP_WKSIDX_PUSH) {
2158       HandlePushError(s, "Request Not Cacheable");
2159       return;
2160     }
2161     // for redirect, we skipped cache lookup to do the automatic redirection
2162     if (s->redirect_info.redirect_in_process) {
2163       // without calling out the CACHE_LOOKUP_COMPLETE_HOOK
2164       if (s->txn_conf->cache_http) {
2165         if (s->cache_info.write_lock_state == CACHE_WL_FAIL) {
2166           s->cache_info.action           = CACHE_PREPARE_TO_WRITE;
2167           s->cache_info.write_lock_state = HttpTransact::CACHE_WL_INIT;
2168         } else if (s->cache_info.write_lock_state == CACHE_WL_SUCCESS) {
2169           s->cache_info.action = CACHE_DO_WRITE;
2170         }
2171       }
2172       LookupSkipOpenServer(s);
2173     } else {
2174       // calling out CACHE_LOOKUP_COMPLETE_HOOK even when the cache
2175       // lookup is skipped
2176       s->cache_lookup_result = HttpTransact::CACHE_LOOKUP_SKIPPED;
2177       if (s->force_dns) {
2178         TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, LookupSkipOpenServer);
2179       } else {
2180         // Returning to dns lookup as cache lookup is skipped
2181         TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, CallOSDNSLookup);
2182       }
2183     }
2184   }
2185 
2186   return;
2187 }
2188 
2189 void
LookupSkipOpenServer(State * s)2190 HttpTransact::LookupSkipOpenServer(State *s)
2191 {
2192   // cache will not be looked up. open a connection
2193   // to a parent proxy or to the origin server.
2194   find_server_and_update_current_info(s);
2195 
2196   if (s->current.request_to == PARENT_PROXY) {
2197     TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
2198   } else if (s->parent_result.result == PARENT_FAIL) {
2199     handle_parent_died(s);
2200     return;
2201   }
2202 
2203   ink_assert(s->current.request_to == ORIGIN_SERVER);
2204   // ink_assert(s->current.server->ip != 0);
2205 
2206   build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
2207 
2208   StateMachineAction_t next = how_to_open_connection(s);
2209   s->next_action            = next;
2210   if (next == SM_ACTION_ORIGIN_SERVER_OPEN || next == SM_ACTION_ORIGIN_SERVER_RAW_OPEN) {
2211     TRANSACT_RETURN(next, HttpTransact::HandleResponse);
2212   }
2213 }
2214 
2215 //////////////////////////////////////////////////////////////////////////////
2216 // Name       : HandleCacheOpenReadPush
2217 // Description:
2218 //
2219 // Details    :
2220 //
2221 // Called on PUSH requests from HandleCacheOpenRead
2222 //////////////////////////////////////////////////////////////////////////////
2223 void
HandleCacheOpenReadPush(State * s,bool read_successful)2224 HttpTransact::HandleCacheOpenReadPush(State *s, bool read_successful)
2225 {
2226   if (read_successful) {
2227     s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
2228   } else {
2229     s->cache_info.action = CACHE_PREPARE_TO_WRITE;
2230   }
2231 
2232   TRANSACT_RETURN(SM_ACTION_READ_PUSH_HDR, HandlePushResponseHdr);
2233 }
2234 
2235 //////////////////////////////////////////////////////////////////////////////
2236 // Name       : HandlePushResponseHdr
2237 // Description:
2238 //
2239 // Details    :
2240 //
2241 // Called after reading the response header on PUSH request
2242 //////////////////////////////////////////////////////////////////////////////
2243 void
HandlePushResponseHdr(State * s)2244 HttpTransact::HandlePushResponseHdr(State *s)
2245 {
2246   // Verify the pushed header wasn't longer than the content length
2247   int64_t body_bytes = s->hdr_info.request_content_length - s->state_machine->pushed_response_hdr_bytes;
2248   if (body_bytes < 0) {
2249     HandlePushError(s, "Bad Content Length");
2250     return;
2251   }
2252   // We need to create the request header storing in the cache
2253   s->hdr_info.server_request.create(HTTP_TYPE_REQUEST);
2254   s->hdr_info.server_request.copy(&s->hdr_info.client_request);
2255   s->hdr_info.server_request.method_set(HTTP_METHOD_GET, HTTP_LEN_GET);
2256   s->hdr_info.server_request.value_set("X-Inktomi-Source", 16, "http PUSH", 9);
2257 
2258   DUMP_HEADER("http_hdrs", &s->hdr_info.server_response, s->state_machine_id, "Pushed Response Header");
2259 
2260   DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Generated Request Header");
2261 
2262   s->response_received_time = s->request_sent_time = ink_local_time();
2263 
2264   if (is_response_cacheable(s, &s->hdr_info.server_request, &s->hdr_info.server_response)) {
2265     ink_assert(s->cache_info.action == CACHE_PREPARE_TO_WRITE || s->cache_info.action == CACHE_PREPARE_TO_UPDATE);
2266 
2267     TRANSACT_RETURN(SM_ACTION_CACHE_ISSUE_WRITE, HandlePushCacheWrite);
2268   } else {
2269     HandlePushError(s, "Response Not Cacheable");
2270   }
2271 }
2272 
2273 //////////////////////////////////////////////////////////////////////////////
2274 // Name       : HandlePushCacheWrite
2275 // Description:
2276 //
2277 // Details    :
2278 //
2279 // Called after performing the cache write on a push request
2280 //////////////////////////////////////////////////////////////////////////////
2281 void
HandlePushCacheWrite(State * s)2282 HttpTransact::HandlePushCacheWrite(State *s)
2283 {
2284   switch (s->cache_info.write_lock_state) {
2285   case CACHE_WL_SUCCESS:
2286     // We were able to get the lock for the URL vector in the cache
2287     if (s->cache_info.action == CACHE_PREPARE_TO_WRITE) {
2288       s->cache_info.action = CACHE_DO_WRITE;
2289     } else if (s->cache_info.action == CACHE_PREPARE_TO_UPDATE) {
2290       s->cache_info.action = CACHE_DO_REPLACE;
2291     } else {
2292       ink_release_assert(0);
2293     }
2294     set_headers_for_cache_write(s, &s->cache_info.object_store, &s->hdr_info.server_request, &s->hdr_info.server_response);
2295 
2296     TRANSACT_RETURN(SM_ACTION_STORE_PUSH_BODY, nullptr);
2297     break;
2298 
2299   case CACHE_WL_FAIL:
2300   case CACHE_WL_READ_RETRY:
2301     // No write lock, can not complete request so bail
2302     HandlePushError(s, "Cache Write Failed");
2303     break;
2304   case CACHE_WL_INIT:
2305   default:
2306     ink_release_assert(0);
2307   }
2308 }
2309 
2310 void
HandlePushTunnelSuccess(State * s)2311 HttpTransact::HandlePushTunnelSuccess(State *s)
2312 {
2313   ink_assert(s->cache_info.action == CACHE_DO_WRITE || s->cache_info.action == CACHE_DO_REPLACE);
2314 
2315   // FIX ME: check PUSH spec for status codes
2316   HTTPStatus resp_status = (s->cache_info.action == CACHE_DO_WRITE) ? HTTP_STATUS_CREATED : HTTP_STATUS_OK;
2317 
2318   build_response(s, &s->hdr_info.client_response, s->client_info.http_version, resp_status);
2319 
2320   TRANSACT_RETURN(SM_ACTION_INTERNAL_CACHE_NOOP, nullptr);
2321 }
2322 
2323 void
HandlePushTunnelFailure(State * s)2324 HttpTransact::HandlePushTunnelFailure(State *s)
2325 {
2326   HandlePushError(s, "Cache Error");
2327 }
2328 
2329 void
HandleBadPushRespHdr(State * s)2330 HttpTransact::HandleBadPushRespHdr(State *s)
2331 {
2332   HandlePushError(s, "Malformed Pushed Response Header");
2333 }
2334 
2335 void
HandlePushError(State * s,const char * reason)2336 HttpTransact::HandlePushError(State *s, const char *reason)
2337 {
2338   s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
2339 
2340   // Set half close flag to prevent TCP
2341   //   reset from the body still being transferred
2342   s->state_machine->set_ua_half_close_flag();
2343 
2344   build_error_response(s, HTTP_STATUS_BAD_REQUEST, reason, "default");
2345 }
2346 
2347 ///////////////////////////////////////////////////////////////////////////////
2348 // Name       : HandleCacheOpenRead
2349 // Description: the cache lookup succeeded - may have been a hit or a miss
2350 //
2351 // Details    :
2352 //
2353 // the cache lookup succeeded. first check if the lookup resulted in
2354 // a hit or a miss, if the lookup was for an http request.
2355 // This function just funnels the result into the appropriate
2356 // functions which handle these different cases.
2357 //
2358 //
2359 // Possible Next States From Here:
2360 //
2361 ///////////////////////////////////////////////////////////////////////////////
2362 void
HandleCacheOpenRead(State * s)2363 HttpTransact::HandleCacheOpenRead(State *s)
2364 {
2365   TxnDebug("http_trans", "[HttpTransact::HandleCacheOpenRead]");
2366 
2367   SET_VIA_STRING(VIA_DETAIL_CACHE_TYPE, VIA_DETAIL_CACHE);
2368 
2369   bool read_successful = true;
2370 
2371   if (s->cache_info.object_read == nullptr) {
2372     read_successful = false;
2373     //
2374     // If somebody else was writing the document, proceed just like it was
2375     // a normal cache miss, except don't try to write to the cache
2376     //
2377     if (s->cache_lookup_result == CACHE_LOOKUP_DOC_BUSY) {
2378       s->cache_lookup_result = CACHE_LOOKUP_MISS;
2379       s->cache_info.action   = CACHE_DO_NO_ACTION;
2380     }
2381   } else {
2382     CacheHTTPInfo *obj = s->cache_info.object_read;
2383     if (obj->response_get()->type_get() == HTTP_TYPE_UNKNOWN) {
2384       read_successful = false;
2385     }
2386     if (obj->request_get()->type_get() == HTTP_TYPE_UNKNOWN) {
2387       read_successful = false;
2388     }
2389   }
2390 
2391   if (s->method == HTTP_WKSIDX_PUSH) {
2392     HandleCacheOpenReadPush(s, read_successful);
2393   } else if (read_successful == false) {
2394     // cache miss
2395     TxnDebug("http_trans", "CacheOpenRead -- miss");
2396     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_NOT_CACHED);
2397     // Perform DNS for the origin when it is required.
2398     // 1. If parent configuration does not allow to go to origin there is no need of performing DNS
2399     // 2. If parent satisfies the request there is no need to go to origin to perform DNS
2400     HandleCacheOpenReadMiss(s);
2401   } else {
2402     // cache hit
2403     TxnDebug("http_trans", "CacheOpenRead -- hit");
2404     TRANSACT_RETURN(SM_ACTION_API_READ_CACHE_HDR, HandleCacheOpenReadHitFreshness);
2405   }
2406 
2407   return;
2408 }
2409 
2410 ///////////////////////////////////////////////////////////////////////////////
2411 // Name       : issue_revalidate
2412 // Description:   Sets cache action and does various bookkeeping
2413 //
2414 // Details    :
2415 //
2416 // The Cache Lookup was hit but the document was stale so after
2417 //   calling build_request, we need setup up the cache action,
2418 //   set the via code, and possibly conditionalize the request
2419 // The paths that we take to get this code are:
2420 //   Directly from HandleOpenReadHit if we are going to the origin server
2421 //   After PPDNS if we are going to a parent proxy
2422 //
2423 //
2424 // Possible Next States From Here:
2425 // -
2426 //
2427 ///////////////////////////////////////////////////////////////////////////////
2428 void
issue_revalidate(State * s)2429 HttpTransact::issue_revalidate(State *s)
2430 {
2431   HTTPHdr *c_resp = find_appropriate_cached_resp(s);
2432   SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_STALE);
2433   ink_assert(GET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP) != ' ');
2434 
2435   if (s->www_auth_content == CACHE_AUTH_FRESH) {
2436     s->hdr_info.server_request.method_set(HTTP_METHOD_HEAD, HTTP_LEN_HEAD);
2437     // The document is fresh in cache and we just want to see if the
2438     // the client has the right credentials
2439     // this cache action is just to get us into the hcoofsr function
2440     s->cache_info.action = CACHE_DO_UPDATE;
2441     DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Proxy's Request (Conditionalized)");
2442     return;
2443   }
2444 
2445   if (s->cache_info.write_lock_state == CACHE_WL_INIT) {
2446     // We do a cache lookup for DELETE, PUT and POST requests as well.
2447     // We must, however, delete the cached copy after forwarding the
2448     // request to the server. is_cache_response_returnable will ensure
2449     // that we forward the request. We now specify what the cache
2450     // action should be when the response is received.
2451     if (does_method_require_cache_copy_deletion(s->http_config_param, s->method)) {
2452       s->cache_info.action = CACHE_PREPARE_TO_DELETE;
2453       TxnDebug("http_seq", "[HttpTransact::issue_revalidate] cache action: DELETE");
2454     } else {
2455       s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
2456       TxnDebug("http_seq", "[HttpTransact::issue_revalidate] cache action: UPDATE");
2457     }
2458   } else {
2459     // We've looped back around due to missing the write lock
2460     //  for the cache.  At this point we want to forget about the cache
2461     ink_assert(s->cache_info.write_lock_state == CACHE_WL_READ_RETRY);
2462     s->cache_info.action = CACHE_DO_NO_ACTION;
2463     return;
2464   }
2465 
2466   // if the document is cached, just send a conditional request to the server
2467 
2468   // So the request does not have preconditions. It can, however
2469   // be a simple GET request with a Pragma:no-cache. As on 8/28/98
2470   // we have fixed the whole Reload/Shift-Reload cached copy
2471   // corruption problem. This means that we can issue a conditional
2472   // request to the server only if the incoming request has a conditional
2473   // or the incoming request does NOT have a no-cache header.
2474   // In other words, if the incoming request is not conditional
2475   // but has a no-cache header we can not issue an IMS. check for
2476   // that case here.
2477   bool no_cache_in_request = false;
2478 
2479   if (s->hdr_info.client_request.is_pragma_no_cache_set() || s->hdr_info.client_request.is_cache_control_set(HTTP_VALUE_NO_CACHE)) {
2480     TxnDebug("http_trans", "[issue_revalidate] no-cache header directive in request, folks");
2481     no_cache_in_request = true;
2482   }
2483 
2484   if ((!(s->hdr_info.client_request.presence(MIME_PRESENCE_IF_MODIFIED_SINCE))) &&
2485       (!(s->hdr_info.client_request.presence(MIME_PRESENCE_IF_NONE_MATCH))) && (no_cache_in_request == true) &&
2486       (!s->txn_conf->cache_ims_on_client_no_cache) && (s->www_auth_content == CACHE_AUTH_NONE)) {
2487     TxnDebug("http_trans",
2488              "[issue_revalidate] Can not make this a conditional request. This is the force update of the cached copy case");
2489     // set cache action to update. response will be a 200 or error,
2490     // causing cached copy to be replaced (if 200).
2491     s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
2492     return;
2493   }
2494   // do not conditionalize if the cached response is not a 200
2495   switch (c_resp->status_get()) {
2496   case HTTP_STATUS_OK: // 200
2497     // don't conditionalize if we are configured to repeat the clients
2498     //   conditionals
2499     if (s->txn_conf->cache_when_to_revalidate == 4) {
2500       break;
2501     }
2502     // ok, request is either a conditional or does not have a no-cache.
2503     //   (or is method that we don't conditionalize but lookup the
2504     //    cache on like DELETE)
2505     if (c_resp->get_last_modified() > 0 &&
2506         (s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_GET ||
2507          s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_HEAD) &&
2508         s->range_setup == RANGE_NONE) {
2509       // make this a conditional request
2510       int length;
2511       const char *str = c_resp->value_get(MIME_FIELD_LAST_MODIFIED, MIME_LEN_LAST_MODIFIED, &length);
2512       if (str) {
2513         s->hdr_info.server_request.value_set(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE, str, length);
2514       }
2515       DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Proxy's Request (Conditionalized)");
2516     }
2517     // if Etag exists, also add if-non-match header
2518     if (c_resp->presence(MIME_PRESENCE_ETAG) && (s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_GET ||
2519                                                  s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_HEAD)) {
2520       int length       = 0;
2521       const char *etag = c_resp->value_get(MIME_FIELD_ETAG, MIME_LEN_ETAG, &length);
2522       if (nullptr != etag) {
2523         if ((length >= 2) && (etag[0] == 'W') && (etag[1] == '/')) {
2524           etag += 2;
2525           length -= 2;
2526         }
2527         s->hdr_info.server_request.value_set(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH, etag, length);
2528       }
2529       DUMP_HEADER("http_hdrs", &s->hdr_info.server_request, s->state_machine_id, "Proxy's Request (Conditionalized)");
2530     }
2531     break;
2532   case HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION: // 203
2533   /* fall through */
2534   case HTTP_STATUS_MULTIPLE_CHOICES: // 300
2535   /* fall through */
2536   case HTTP_STATUS_MOVED_PERMANENTLY: // 301
2537   /* fall through */
2538   case HTTP_STATUS_GONE: // 410
2539   /* fall through */
2540   default:
2541     TxnDebug("http_trans", "[issue_revalidate] cached response is"
2542                            "not a 200 response so no conditionalization.");
2543     s->cache_info.action = CACHE_PREPARE_TO_UPDATE;
2544     break;
2545   case HTTP_STATUS_PARTIAL_CONTENT:
2546     ink_assert(!"unexpected status code");
2547     break;
2548   }
2549 }
2550 
2551 void
HandleCacheOpenReadHitFreshness(State * s)2552 HttpTransact::HandleCacheOpenReadHitFreshness(State *s)
2553 {
2554   CacheHTTPInfo *&obj = s->cache_info.object_read;
2555 
2556   ink_release_assert((s->request_sent_time == UNDEFINED_TIME) && (s->response_received_time == UNDEFINED_TIME));
2557   TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] Hit in cache");
2558 
2559   if (delete_all_document_alternates_and_return(s, true)) {
2560     TxnDebug("http_trans", "[HandleCacheOpenReadHitFreshness] Delete and return");
2561     s->cache_info.action = CACHE_DO_DELETE;
2562     s->next_action       = HttpTransact::SM_ACTION_INTERNAL_CACHE_DELETE;
2563     return;
2564   }
2565 
2566   s->request_sent_time      = obj->request_sent_time_get();
2567   s->response_received_time = obj->response_received_time_get();
2568 
2569   // There may be clock skew if one of the machines
2570   // went down and we do not have the correct delta
2571   // for it. this is just to deal with the effects
2572   // of the skew by setting minimum and maximum times
2573   // so that ages are not negative, etc.
2574   s->request_sent_time      = std::min(s->client_request_time, s->request_sent_time);
2575   s->response_received_time = std::min(s->client_request_time, s->response_received_time);
2576 
2577   ink_assert(s->request_sent_time <= s->response_received_time);
2578 
2579   TxnDebug("http_trans", "[HandleCacheOpenReadHitFreshness] request_sent_time      : %" PRId64, (int64_t)s->request_sent_time);
2580   TxnDebug("http_trans", "[HandleCacheOpenReadHitFreshness] response_received_time : %" PRId64, (int64_t)s->response_received_time);
2581   // if the plugin has already decided the freshness, we don't need to
2582   // do it again
2583   if (s->cache_lookup_result == HttpTransact::CACHE_LOOKUP_NONE) {
2584     // is the document still fresh enough to be served back to
2585     // the client without revalidation?
2586     Freshness_t freshness = what_is_document_freshness(s, &s->hdr_info.client_request, obj->response_get());
2587     switch (freshness) {
2588     case FRESHNESS_FRESH:
2589       TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] "
2590                            "Fresh copy");
2591       s->cache_lookup_result = HttpTransact::CACHE_LOOKUP_HIT_FRESH;
2592       break;
2593     case FRESHNESS_WARNING:
2594       TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] "
2595                            "Heuristic-based Fresh copy");
2596       s->cache_lookup_result = HttpTransact::CACHE_LOOKUP_HIT_WARNING;
2597       break;
2598     case FRESHNESS_STALE:
2599       TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHitFreshness] "
2600                            "Stale in cache");
2601       s->cache_lookup_result = HttpTransact::CACHE_LOOKUP_HIT_STALE;
2602       break;
2603     default:
2604       ink_assert(!("what_is_document_freshness has returned unsupported code."));
2605       break;
2606     }
2607   }
2608 
2609   ink_assert(s->cache_lookup_result != HttpTransact::CACHE_LOOKUP_MISS);
2610   if (s->cache_lookup_result == HttpTransact::CACHE_LOOKUP_HIT_STALE) {
2611     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_EXPIRED);
2612     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_STALE);
2613   }
2614 
2615   if (!s->force_dns) { // If DNS is not performed before
2616     if (need_to_revalidate(s)) {
2617       TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE,
2618                       CallOSDNSLookup); // content needs to be revalidated and we did not perform a dns ....calling DNS lookup
2619     } else {                            // document can be served can cache
2620       TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, HttpTransact::HandleCacheOpenReadHit);
2621     }
2622   } else { // we have done dns . Its up to HandleCacheOpenReadHit to decide to go OS or serve from cache
2623     TRANSACT_RETURN(SM_ACTION_API_CACHE_LOOKUP_COMPLETE, HttpTransact::HandleCacheOpenReadHit);
2624   }
2625 }
2626 
2627 ///////////////////////////////////////////////////////////////////////////////
2628 // Name       : CallOSDNSLookup
2629 // Description: Moves in SM_ACTION_DNS_LOOKUP state and sets the transact return to OSDNSLookup
2630 //
2631 // Details    :
2632 /////////////////////////////////////////////////////////////////////////////
2633 void
CallOSDNSLookup(State * s)2634 HttpTransact::CallOSDNSLookup(State *s)
2635 {
2636   TxnDebug("http", "[HttpTransact::callos] %s ", s->server_info.name);
2637   HostStatus &pstatus = HostStatus::instance();
2638   HostStatRec *hst    = pstatus.getHostStatus(s->server_info.name);
2639   if (hst && hst->status == HostStatus_t::HOST_STATUS_DOWN) {
2640     TxnDebug("http", "[HttpTransact::callos] %d ", s->cache_lookup_result);
2641     s->current.state = OUTBOUND_CONGESTION;
2642     if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE || s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING ||
2643         s->cache_lookup_result == CACHE_LOOKUP_HIT_FRESH) {
2644       s->cache_info.action = CACHE_DO_SERVE;
2645     } else {
2646       s->cache_info.action = CACHE_DO_NO_ACTION;
2647     }
2648     handle_server_connection_not_open(s);
2649   } else {
2650     TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, OSDNSLookup);
2651   }
2652 }
2653 
2654 ///////////////////////////////////////////////////////////////////////////////
2655 // Name       : need_to_revalidate
2656 // Description: Checks if a document which is in the cache needs to be revalidates
2657 //
2658 // Details    : Function calls AuthenticationNeeded and is_cache_response_returnable to determine
2659 //              if the cached document can be served
2660 /////////////////////////////////////////////////////////////////////////////
2661 bool
need_to_revalidate(State * s)2662 HttpTransact::need_to_revalidate(State *s)
2663 {
2664   bool needs_revalidate, needs_authenticate = false;
2665   bool needs_cache_auth = false;
2666   CacheHTTPInfo *obj;
2667 
2668   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
2669     obj = &s->cache_info.object_store;
2670     ink_assert(obj->valid());
2671     if (!obj->valid()) {
2672       return true;
2673     }
2674   } else {
2675     obj = s->cache_info.object_read;
2676   }
2677 
2678   // do we have to authenticate with the server before
2679   // sending back the cached response to the client?
2680   Authentication_t authentication_needed = AuthenticationNeeded(s->txn_conf, &s->hdr_info.client_request, obj->response_get());
2681 
2682   switch (authentication_needed) {
2683   case AUTHENTICATION_SUCCESS:
2684     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2685                          "Authentication not needed");
2686     needs_authenticate = false;
2687     break;
2688   case AUTHENTICATION_MUST_REVALIDATE:
2689     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_METHOD);
2690     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2691                          "Authentication needed");
2692     needs_authenticate = true;
2693     break;
2694   case AUTHENTICATION_MUST_PROXY:
2695     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2696                          "Authentication needed");
2697     needs_authenticate = true;
2698     break;
2699   case AUTHENTICATION_CACHE_AUTH:
2700     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2701                          "Authentication needed for cache_auth_content");
2702     needs_authenticate = false;
2703     needs_cache_auth   = true;
2704     break;
2705   default:
2706     ink_assert(!("AuthenticationNeeded has returned unsupported code."));
2707     return true;
2708     break;
2709   }
2710 
2711   ink_assert(is_cache_hit(s->cache_lookup_result));
2712   if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE &&
2713       s->api_update_cached_object != HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
2714     needs_revalidate = true;
2715   } else {
2716     needs_revalidate = false;
2717   }
2718 
2719   bool send_revalidate = ((needs_authenticate == true) || (needs_revalidate == true) || (is_cache_response_returnable(s) == false));
2720   if (needs_cache_auth == true) {
2721     s->www_auth_content = send_revalidate ? CACHE_AUTH_STALE : CACHE_AUTH_FRESH;
2722     send_revalidate     = true;
2723   }
2724   return send_revalidate;
2725 }
2726 
2727 ///////////////////////////////////////////////////////////////////////////////
2728 // Name       : HandleCacheOpenReadHit
2729 // Description: handle result of a cache hit
2730 //
2731 // Details    :
2732 //
2733 // Cache lookup succeeded and resulted in a cache hit. This means
2734 // that the Accept* and Etags fields also matched. The cache lookup
2735 // may have resulted in a vector of alternates (since lookup may
2736 // be based on a url). A different function (SelectFromAlternates)
2737 // goes through the alternates and finds the best match. That is
2738 // then returned to this function. The result may not be sent back
2739 // to the client, still, if the document is not fresh enough, or
2740 // does not have enough authorization, or if the client wants a
2741 // reload, etc. that decision will be made in this routine.
2742 //
2743 //
2744 // Possible Next States From Here:
2745 // - HttpTransact::PROXY_INTERNAL_CACHE_DELETE;
2746 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
2747 // - HttpTransact::ORIGIN_SERVER_OPEN;
2748 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
2749 // - HttpTransact::SERVE_FROM_CACHE;
2750 // - result of how_to_open_connection()
2751 //
2752 //
2753 // For Range requests, we will decide to do simple tunneling if one of the
2754 // following conditions hold:
2755 // - document stale
2756 // - cached response doesn't have Accept-Ranges and Content-Length
2757 //
2758 ///////////////////////////////////////////////////////////////////////////////
2759 void
HandleCacheOpenReadHit(State * s)2760 HttpTransact::HandleCacheOpenReadHit(State *s)
2761 {
2762   bool needs_revalidate   = false;
2763   bool needs_authenticate = false;
2764   bool needs_cache_auth   = false;
2765   bool server_up          = true;
2766   CacheHTTPInfo *obj;
2767 
2768   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
2769     obj = &s->cache_info.object_store;
2770     ink_assert(obj->valid());
2771   } else {
2772     obj = s->cache_info.object_read;
2773   }
2774 
2775   // do we have to authenticate with the server before
2776   // sending back the cached response to the client?
2777   Authentication_t authentication_needed = AuthenticationNeeded(s->txn_conf, &s->hdr_info.client_request, obj->response_get());
2778 
2779   switch (authentication_needed) {
2780   case AUTHENTICATION_SUCCESS:
2781     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2782                          "Authentication not needed");
2783     needs_authenticate = false;
2784     break;
2785   case AUTHENTICATION_MUST_REVALIDATE:
2786     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_METHOD);
2787     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2788                          "Authentication needed");
2789     needs_authenticate = true;
2790     break;
2791   case AUTHENTICATION_MUST_PROXY:
2792     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2793                          "Authentication needed");
2794     HandleCacheOpenReadMiss(s);
2795     return;
2796   case AUTHENTICATION_CACHE_AUTH:
2797     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2798                          "Authentication needed for cache_auth_content");
2799     needs_authenticate = false;
2800     needs_cache_auth   = true;
2801     break;
2802   default:
2803     ink_assert(!("AuthenticationNeeded has returned unsupported code."));
2804     break;
2805   }
2806 
2807   ink_assert(is_cache_hit(s->cache_lookup_result));
2808 
2809   // We'll request a revalidation under one of these conditions:
2810   //
2811   // 1. Cache lookup is a hit, but the response is stale
2812   // 2. The cached object has a "Cache-Control: no-cache" header
2813   //       *and*
2814   //    proxy.config.http.cache.ignore_server_no_cache is set to 0 (i.e don't ignore no cache -- the default setting)
2815   //
2816   // But, we only do this if we're not in an API updating the cached object (see TSHttpTxnUpdateCachedObject)
2817   if ((((s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE) ||
2818         ((obj->response_get()->get_cooked_cc_mask() & MIME_COOKED_MASK_CC_NO_CACHE) && !s->cache_control.ignore_server_no_cache)) &&
2819        (s->api_update_cached_object != HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE))) {
2820     needs_revalidate = true;
2821     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_EXPIRED);
2822   }
2823 
2824   // the response may not be directly returnable to the client. there
2825   // are several reasons for this: config may force revalidation or
2826   // client may have forced a refresh by sending a Pragma:no-cache
2827   // or a Cache-Control:no-cache, or the client may have sent a
2828   // non-GET/HEAD request for a document that is cached. an example
2829   // of a situation for this is when a client sends a DELETE, PUT
2830   // or POST request for a url that is cached. except for DELETE,
2831   // we may actually want to update the cached copy with the contents
2832   // of the PUT/POST, but the easiest, safest and most robust solution
2833   // is to simply delete the cached copy (in order to maintain cache
2834   // consistency). this is particularly true if the server does not
2835   // accept or conditionally accepts the PUT/POST requests.
2836   // anyhow, this is an overloaded function and will return false
2837   // if the origin server still has to be looked up.
2838   bool response_returnable = is_cache_response_returnable(s);
2839 
2840   // do we need to revalidate. in other words if the response
2841   // has to be authorized, is stale or can not be returned, do
2842   // a revalidate.
2843   bool send_revalidate = (needs_authenticate || needs_revalidate || !response_returnable);
2844 
2845   if (needs_cache_auth == true) {
2846     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_EXPIRED);
2847     s->www_auth_content = send_revalidate ? CACHE_AUTH_STALE : CACHE_AUTH_FRESH;
2848     send_revalidate     = true;
2849   }
2850 
2851   TxnDebug("http_trans", "CacheOpenRead --- needs_auth          = %d", needs_authenticate);
2852   TxnDebug("http_trans", "CacheOpenRead --- needs_revalidate    = %d", needs_revalidate);
2853   TxnDebug("http_trans", "CacheOpenRead --- response_returnable = %d", response_returnable);
2854   TxnDebug("http_trans", "CacheOpenRead --- needs_cache_auth    = %d", needs_cache_auth);
2855   TxnDebug("http_trans", "CacheOpenRead --- send_revalidate     = %d", send_revalidate);
2856 
2857   if (send_revalidate) {
2858     TxnDebug("http_trans", "CacheOpenRead --- HIT-STALE");
2859 
2860     TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2861                          "Revalidate document with server");
2862 
2863     find_server_and_update_current_info(s);
2864 
2865     // We do not want to try to revalidate documents if we think
2866     //  the server is down due to the something report problem
2867     //
2868     // Note: we only want to skip origin servers because 1)
2869     //  parent proxies have their own negative caching
2870     //  scheme & 2) If we skip down parents, every page
2871     //  we serve is potentially stale
2872     //
2873     if (s->current.request_to == ORIGIN_SERVER && is_server_negative_cached(s) && response_returnable == true &&
2874         is_stale_cache_response_returnable(s) == true) {
2875       server_up = false;
2876       update_current_info(&s->current, nullptr, UNDEFINED_LOOKUP, 0);
2877       TxnDebug("http_trans", "CacheOpenReadHit - server_down, returning stale document");
2878     }
2879     // a parent lookup could come back as PARENT_FAIL if in parent.config, go_direct == false and
2880     // there are no available parents (all down).
2881     else if (s->current.request_to == HOST_NONE && s->parent_result.result == PARENT_FAIL) {
2882       if (is_server_negative_cached(s) && response_returnable == true && is_stale_cache_response_returnable(s) == true) {
2883         server_up = false;
2884         update_current_info(&s->current, nullptr, UNDEFINED_LOOKUP, 0);
2885         TxnDebug("http_trans", "CacheOpenReadHit - server_down, returning stale document");
2886       } else {
2887         handle_parent_died(s);
2888         return;
2889       }
2890     }
2891 
2892     if (server_up) {
2893       // set a default version for the outgoing request
2894       HTTPVersion http_version;
2895 
2896       if (s->current.server != nullptr) {
2897         bool check_hostdb = get_ka_info_from_config(s, s->current.server);
2898         TxnDebug("http_trans", "CacheOpenReadHit - check_hostdb %d", check_hostdb);
2899         if (check_hostdb || !s->current.server->dst_addr.isValid()) {
2900           // We must be going a PARENT PROXY since so did
2901           //  origin server DNS lookup right after state Start
2902           //
2903           // If we end up here in the release case just fall
2904           //  through.  The request will fail because of the
2905           //  missing ip but we won't take down the system
2906           //
2907           if (s->current.request_to == PARENT_PROXY) {
2908             // Set ourselves up to handle pending revalidate issues
2909             //  after the PP DNS lookup
2910             ink_assert(s->pending_work == nullptr);
2911             s->pending_work = issue_revalidate;
2912 
2913             TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
2914           } else if (s->current.request_to == ORIGIN_SERVER) {
2915             return CallOSDNSLookup(s);
2916           } else {
2917             handle_parent_died(s);
2918             return;
2919           }
2920         }
2921         // override the default version with what the server has
2922         http_version = s->current.server->http_version;
2923       }
2924 
2925       TxnDebug("http_trans", "CacheOpenReadHit - version %d.%d", http_version.get_major(), http_version.get_minor());
2926       build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, http_version);
2927 
2928       issue_revalidate(s);
2929 
2930       // this can not be anything but a simple origin server connection.
2931       // in other words, we would not have looked up the cache for a
2932       // connect request, so the next action can not be origin_server_raw_open.
2933       s->next_action = how_to_open_connection(s);
2934 
2935       ink_release_assert(s->next_action != SM_ACTION_ORIGIN_SERVER_RAW_OPEN);
2936       return;
2937     } else { // server is down but stale response is returnable
2938       SET_VIA_STRING(VIA_DETAIL_CACHE_TYPE, VIA_DETAIL_CACHE);
2939     }
2940   }
2941   // cache hit, document is fresh, does not authorization,
2942   // is valid, etc. etc. send it back to the client.
2943   //
2944   // the important thing to keep in mind is that if we are
2945   // here then we found a match in the cache and the document
2946   // is fresh and we have enough authorization for it to send
2947   // it back to the client without revalidating first with the
2948   // origin server. we are, therefore, allowed to behave as the
2949   // origin server. we can, therefore, make the claim that the
2950   // document has not been modified since or has not been unmodified
2951   // since the time requested by the client. this may not be
2952   // the case in reality, but since the document is fresh in
2953   // the cache, we can make the claim that this is the truth.
2954   //
2955   // so, any decision we make at this point can be made with authority.
2956   // realistically, if we can not make this claim, then there
2957   // is no reason to cache anything.
2958   //
2959   ink_assert((send_revalidate == true && server_up == false) || (send_revalidate == false && server_up == true));
2960 
2961   TxnDebug("http_trans", "CacheOpenRead --- HIT-FRESH");
2962   TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] "
2963                        "Serve from cache");
2964 
2965   // ToDo: Should support other levels of cache hits here, but the cache does not support it (yet)
2966   if (SQUID_HIT_RAM == s->cache_info.hit_miss_code) {
2967     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_RAM_CACHE_FRESH);
2968   } else {
2969     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_FRESH);
2970   }
2971 
2972   if (s->cache_lookup_result == CACHE_LOOKUP_HIT_WARNING) {
2973     build_response_from_cache(s, HTTP_WARNING_CODE_HERUISTIC_EXPIRATION);
2974   } else if (s->cache_lookup_result == CACHE_LOOKUP_HIT_STALE) {
2975     ink_assert(server_up == false);
2976     build_response_from_cache(s, HTTP_WARNING_CODE_REVALIDATION_FAILED);
2977   } else {
2978     build_response_from_cache(s, HTTP_WARNING_CODE_NONE);
2979   }
2980 
2981   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
2982     s->saved_update_next_action  = s->next_action;
2983     s->saved_update_cache_action = s->cache_info.action;
2984     s->next_action               = SM_ACTION_CACHE_PREPARE_UPDATE;
2985   }
2986 }
2987 
2988 ///////////////////////////////////////////////////////////////////////////////
2989 // Name       : build_response_from_cache()
2990 // Description: build a client response from cached response and client request
2991 //
2992 // Input      : State, warning code to be inserted into the response header
2993 // Output     :
2994 //
2995 // Details    : This function is called if we decided to serve a client request
2996 //              using a cached response.
2997 //              It is called by handle_server_connection_not_open()
2998 //              and HandleCacheOpenReadHit().
2999 //
3000 ///////////////////////////////////////////////////////////////////////////////
3001 void
build_response_from_cache(State * s,HTTPWarningCode warning_code)3002 HttpTransact::build_response_from_cache(State *s, HTTPWarningCode warning_code)
3003 {
3004   HTTPHdr *client_request  = &s->hdr_info.client_request;
3005   HTTPHdr *cached_response = nullptr;
3006   HTTPHdr *to_warn         = &s->hdr_info.client_response;
3007   CacheHTTPInfo *obj;
3008 
3009   if (s->api_update_cached_object == HttpTransact::UPDATE_CACHED_OBJECT_CONTINUE) {
3010     obj = &s->cache_info.object_store;
3011     ink_assert(obj->valid());
3012   } else {
3013     obj = s->cache_info.object_read;
3014   }
3015   cached_response = obj->response_get();
3016 
3017   // If the client request is conditional, and the cached copy meets
3018   // the conditions, do not need to send back the full document,
3019   // just a NOT_MODIFIED response.
3020   // If the request is not conditional,
3021   // the function match_response_to_request_conditionals() returns
3022   // the code of the cached response, which means that we should send
3023   // back the full document.
3024   HTTPStatus client_response_code =
3025     HttpTransactCache::match_response_to_request_conditionals(client_request, cached_response, s->response_received_time);
3026 
3027   switch (client_response_code) {
3028   case HTTP_STATUS_NOT_MODIFIED:
3029     // A IMS or INM GET client request with conditions being met
3030     // by the cached response.  Send back a NOT MODIFIED response.
3031     TxnDebug("http_trans", "[build_response_from_cache] Not modified");
3032     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_HIT_CONDITIONAL);
3033 
3034     build_response(s, cached_response, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
3035     s->cache_info.action = CACHE_DO_NO_ACTION;
3036     s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
3037     break;
3038 
3039   case HTTP_STATUS_PRECONDITION_FAILED:
3040     // A conditional request with conditions not being met by the cached
3041     // response.  Send back a PRECONDITION FAILED response.
3042     TxnDebug("http_trans", "[build_response_from_cache] Precondition Failed");
3043     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_CONDITIONAL);
3044 
3045     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
3046     s->cache_info.action = CACHE_DO_NO_ACTION;
3047     s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
3048     break;
3049 
3050   case HTTP_STATUS_RANGE_NOT_SATISFIABLE:
3051   // Check if cached response supports Range. If it does, append
3052   // Range transformation plugin
3053   // A little misnomer. HTTP_STATUS_RANGE_NOT_SATISFIABLE
3054   // actually means If-Range match fails here.
3055   // fall through
3056   default:
3057     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_HIT_SERVED);
3058     if (s->method == HTTP_WKSIDX_GET || s->api_resp_cacheable == true) {
3059       // send back the full document to the client.
3060       TxnDebug("http_trans", "[build_response_from_cache] Match! Serving full document.");
3061       s->cache_info.action = CACHE_DO_SERVE;
3062 
3063       // Check if cached response supports Range. If it does, append
3064       // Range transformation plugin
3065       // only if the cached response is a 200 OK
3066       if (client_response_code == HTTP_STATUS_OK && client_request->presence(MIME_PRESENCE_RANGE)) {
3067         s->state_machine->do_range_setup_if_necessary();
3068         if (s->range_setup == RANGE_NOT_SATISFIABLE) {
3069           build_error_response(s, HTTP_STATUS_RANGE_NOT_SATISFIABLE, "Requested Range Not Satisfiable", "default");
3070           s->cache_info.action = CACHE_DO_NO_ACTION;
3071           s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
3072           break;
3073         } else if ((s->range_setup == RANGE_NOT_HANDLED) || !s->range_in_cache) {
3074           // we switch to tunneling for Range requests if it is out of order.
3075           // or if the range can't be satisfied from the cache
3076           // In that case we fetch the entire source so it's OK to switch
3077           // this late.
3078           TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadHit] Out-of-order Range request - tunneling");
3079           s->cache_info.action = CACHE_DO_NO_ACTION;
3080           if (s->force_dns) {
3081             HandleCacheOpenReadMiss(s); // DNS is already completed no need of doing DNS
3082           } else {
3083             CallOSDNSLookup(s);
3084           }
3085           return;
3086         }
3087       }
3088 
3089       if (s->state_machine->do_transform_open()) {
3090         set_header_for_transform(s, cached_response);
3091         to_warn = &s->hdr_info.transform_response;
3092       } else {
3093         build_response(s, cached_response, &s->hdr_info.client_response, s->client_info.http_version);
3094       }
3095       s->next_action = SM_ACTION_SERVE_FROM_CACHE;
3096     }
3097     // If the client request is a HEAD, then serve the header from cache.
3098     else if (s->method == HTTP_WKSIDX_HEAD) {
3099       TxnDebug("http_trans", "[build_response_from_cache] Match! Serving header only.");
3100 
3101       build_response(s, cached_response, &s->hdr_info.client_response, s->client_info.http_version);
3102       s->cache_info.action = CACHE_DO_NO_ACTION;
3103       s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
3104     } else {
3105       // We handled the request but it's not GET or HEAD (eg. DELETE),
3106       // and server is not reachable: 502
3107       //
3108       TxnDebug("http_trans", "[build_response_from_cache] No match! Connection failed.");
3109       build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Connection Failed", "connect#failed_connect");
3110       s->cache_info.action = CACHE_DO_NO_ACTION;
3111       s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
3112       warning_code         = HTTP_WARNING_CODE_NONE;
3113     }
3114     break;
3115   }
3116 
3117   // After building the client response, add the given warning if provided.
3118   if (warning_code != HTTP_WARNING_CODE_NONE) {
3119     delete_warning_value(to_warn, warning_code);
3120     HttpTransactHeaders::insert_warning_header(s->http_config_param, to_warn, warning_code);
3121   }
3122 }
3123 
3124 ///////////////////////////////////////////////////////////////////////////////
3125 // Name       : handle_cache_write_lock
3126 // Description:
3127 //
3128 // Details    :
3129 //
3130 //
3131 //
3132 // Possible Next States From Here:
3133 // - result of how_to_open_connection
3134 //
3135 ///////////////////////////////////////////////////////////////////////////////
3136 void
handle_cache_write_lock(State * s)3137 HttpTransact::handle_cache_write_lock(State *s)
3138 {
3139   bool remove_ims = false;
3140 
3141   ink_assert(s->cache_info.action == CACHE_PREPARE_TO_DELETE || s->cache_info.action == CACHE_PREPARE_TO_UPDATE ||
3142              s->cache_info.action == CACHE_PREPARE_TO_WRITE);
3143 
3144   switch (s->cache_info.write_lock_state) {
3145   case CACHE_WL_SUCCESS:
3146     // We were able to get the lock for the URL vector in the cache
3147     SET_UNPREPARE_CACHE_ACTION(s->cache_info);
3148     break;
3149   case CACHE_WL_FAIL:
3150     // No write lock, ignore the cache and proxy only;
3151     // FIX: Should just serve from cache if this is a revalidate
3152     s->cache_info.action = CACHE_DO_NO_ACTION;
3153     switch (s->cache_open_write_fail_action) {
3154     case CACHE_WL_FAIL_ACTION_ERROR_ON_MISS:
3155     case CACHE_WL_FAIL_ACTION_ERROR_ON_MISS_STALE_ON_REVALIDATE:
3156     case CACHE_WL_FAIL_ACTION_ERROR_ON_MISS_OR_REVALIDATE:
3157       TxnDebug("http_error", "cache_open_write_fail_action %d, cache miss, return error", s->cache_open_write_fail_action);
3158       s->cache_info.write_status = CACHE_WRITE_ERROR;
3159       build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Connection Failed", "connect#failed_connect");
3160       MIMEField *ats_field;
3161       HTTPHdr *header;
3162       header = &(s->hdr_info.client_response);
3163       if ((ats_field = header->field_find(MIME_FIELD_ATS_INTERNAL, MIME_LEN_ATS_INTERNAL)) == nullptr) {
3164         if (likely((ats_field = header->field_create(MIME_FIELD_ATS_INTERNAL, MIME_LEN_ATS_INTERNAL)) != nullptr)) {
3165           header->field_attach(ats_field);
3166         }
3167       }
3168       if (likely(ats_field)) {
3169         int value = (s->cache_info.object_read) ? 1 : 0;
3170         TxnDebug("http_error", "Adding Ats-Internal-Messages: %d", value);
3171         header->field_value_set_int(ats_field, value);
3172       } else {
3173         TxnDebug("http_error", "failed to add Ats-Internal-Messages");
3174       }
3175 
3176       TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
3177     default:
3178       s->cache_info.write_status = CACHE_WRITE_LOCK_MISS;
3179       remove_ims                 = true;
3180       break;
3181     }
3182     break;
3183   case CACHE_WL_READ_RETRY:
3184     s->request_sent_time      = UNDEFINED_TIME;
3185     s->response_received_time = UNDEFINED_TIME;
3186     s->cache_info.action      = CACHE_DO_LOOKUP;
3187     if (!s->cache_info.object_read) {
3188       //  Write failed and read retry triggered
3189       //  Clean up server_request and re-initiate
3190       //  Cache Lookup
3191       ink_assert(s->cache_open_write_fail_action == CACHE_WL_FAIL_ACTION_READ_RETRY);
3192       s->cache_info.write_status = CACHE_WRITE_LOCK_MISS;
3193       StateMachineAction_t next;
3194       next           = SM_ACTION_CACHE_LOOKUP;
3195       s->next_action = next;
3196       s->hdr_info.server_request.destroy();
3197       TRANSACT_RETURN(next, nullptr);
3198     }
3199     //  Write failed but retried and got a vector to read
3200     //  We need to clean up our state so that transact does
3201     //  not assert later on.  Then handle the open read hit
3202     remove_ims = true;
3203     SET_VIA_STRING(VIA_DETAIL_CACHE_TYPE, VIA_DETAIL_CACHE);
3204     break;
3205   case CACHE_WL_INIT:
3206   default:
3207     ink_release_assert(0);
3208     break;
3209   }
3210 
3211   // Since we've already built the server request and we can't get the write
3212   //  lock we need to remove the ims field from the request since we're
3213   //  ignoring the cache.  If their is a client ims field, copy that since
3214   //  we're tunneling response anyway
3215   if (remove_ims) {
3216     s->hdr_info.server_request.field_delete(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE);
3217     s->hdr_info.server_request.field_delete(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH);
3218     MIMEField *c_ims = s->hdr_info.client_request.field_find(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE);
3219     MIMEField *c_inm = s->hdr_info.client_request.field_find(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH);
3220 
3221     if (c_ims) {
3222       int len;
3223       const char *value = c_ims->value_get(&len);
3224       s->hdr_info.server_request.value_set(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE, value, len);
3225     }
3226     if (c_inm) {
3227       int len;
3228       const char *value = c_inm->value_get(&len);
3229       s->hdr_info.server_request.value_set(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH, value, len);
3230     }
3231   }
3232 
3233   if (s->cache_info.write_lock_state == CACHE_WL_READ_RETRY) {
3234     TxnDebug("http_error", "calling hdr_info.server_request.destroy");
3235     s->hdr_info.server_request.destroy();
3236     HandleCacheOpenReadHitFreshness(s);
3237   } else {
3238     StateMachineAction_t next;
3239     next = how_to_open_connection(s);
3240     if (next == SM_ACTION_ORIGIN_SERVER_OPEN || next == SM_ACTION_ORIGIN_SERVER_RAW_OPEN) {
3241       s->next_action = next;
3242       TRANSACT_RETURN(next, nullptr);
3243     } else {
3244       // hehe!
3245       s->next_action = next;
3246       ink_assert(s->next_action == SM_ACTION_DNS_LOOKUP);
3247       return;
3248     }
3249 
3250     TRANSACT_RETURN(next, nullptr);
3251   }
3252 }
3253 
3254 ///////////////////////////////////////////////////////////////////////////////
3255 // Name       : HandleCacheOpenReadMiss
3256 // Description: cache looked up, miss or hit, but needs authorization
3257 //
3258 // Details    :
3259 //
3260 //
3261 //
3262 // Possible Next States From Here:
3263 // - HttpTransact::SM_ACTION_DNS_LOOKUP;
3264 // - HttpTransact::ORIGIN_SERVER_OPEN;
3265 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
3266 // - result of how_to_open_connection()
3267 //
3268 ///////////////////////////////////////////////////////////////////////////////
3269 void
HandleCacheOpenReadMiss(State * s)3270 HttpTransact::HandleCacheOpenReadMiss(State *s)
3271 {
3272   TxnDebug("http_trans", "[HandleCacheOpenReadMiss] --- MISS");
3273   TxnDebug("http_seq", "[HttpTransact::HandleCacheOpenReadMiss] "
3274                        "Miss in cache");
3275 
3276   if (delete_all_document_alternates_and_return(s, false)) {
3277     TxnDebug("http_trans", "[HandleCacheOpenReadMiss] Delete and return");
3278     s->cache_info.action = CACHE_DO_NO_ACTION;
3279     s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
3280     return;
3281   }
3282   // reinitialize some variables to reflect cache miss state.
3283   s->cache_info.object_read = nullptr;
3284   s->request_sent_time      = UNDEFINED_TIME;
3285   s->response_received_time = UNDEFINED_TIME;
3286   SET_VIA_STRING(VIA_CACHE_RESULT, VIA_CACHE_MISS);
3287   if (GET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP) == ' ') {
3288     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_NOT_CACHED);
3289   }
3290   // We do a cache lookup for DELETE and PUT requests as well.
3291   // We must, however, not cache the responses to these requests.
3292   if (does_method_require_cache_copy_deletion(s->http_config_param, s->method) && s->api_req_cacheable == false) {
3293     s->cache_info.action = CACHE_DO_NO_ACTION;
3294   } else if ((s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE) && !s->txn_conf->cache_range_write) ||
3295              does_method_effect_cache(s->method) == false || s->range_setup == RANGE_NOT_SATISFIABLE ||
3296              s->range_setup == RANGE_NOT_HANDLED) {
3297     s->cache_info.action = CACHE_DO_NO_ACTION;
3298   } else if (s->api_server_response_no_store) { // plugin may have decided not to cache the response
3299     s->cache_info.action = CACHE_DO_NO_ACTION;
3300   } else {
3301     s->cache_info.action = CACHE_PREPARE_TO_WRITE;
3302   }
3303 
3304   ///////////////////////////////////////////////////////////////
3305   // a normal miss would try to fetch the document from the    //
3306   // origin server, unless the origin server isn't resolvable, //
3307   // but if "CacheControl: only-if-cached" is set, then we are //
3308   // supposed to send a 504 (GATEWAY TIMEOUT) response.        //
3309   ///////////////////////////////////////////////////////////////
3310 
3311   HTTPHdr *h = &s->hdr_info.client_request;
3312 
3313   if (!h->is_cache_control_set(HTTP_VALUE_ONLY_IF_CACHED)) {
3314     // Initialize the server_info structure if we haven't been through DNS
3315     // Otherwise, the http_version will not be initialized
3316     if (!s->current.server || !s->current.server->dst_addr.isValid()) {
3317       // Short term hack.  get_ka_info_from_config assumes if http_version is > 0,9 it has already been
3318       // set and skips the rest of the function.  The default functor sets it to 1,0
3319       s->server_info.http_version = HTTP_0_9;
3320       get_ka_info_from_config(s, &s->server_info);
3321     }
3322     find_server_and_update_current_info(s);
3323     // a parent lookup could come back as PARENT_FAIL if in parent.config go_direct == false and
3324     // there are no available parents (all down).
3325     if (s->parent_result.result == PARENT_FAIL) {
3326       handle_parent_died(s);
3327       return;
3328     }
3329     if (!s->current.server->dst_addr.isValid()) {
3330       ink_release_assert(s->parent_result.result == PARENT_DIRECT || s->current.request_to == PARENT_PROXY ||
3331                          s->http_config_param->no_dns_forward_to_parent != 0);
3332       if (s->parent_result.result == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 1) {
3333         return CallOSDNSLookup(s);
3334       }
3335       if (s->current.request_to == PARENT_PROXY) {
3336         TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, HttpTransact::PPDNSLookup);
3337       } else {
3338         handle_parent_died(s);
3339         return;
3340       }
3341     }
3342     build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
3343     s->current.attempts = 0;
3344     s->next_action      = how_to_open_connection(s);
3345     if (s->current.server == &s->server_info && s->next_hop_scheme == URL_WKSIDX_HTTP) {
3346       HttpTransactHeaders::remove_host_name_from_url(&s->hdr_info.server_request);
3347     }
3348   } else { // miss, but only-if-cached is set
3349     build_error_response(s, HTTP_STATUS_GATEWAY_TIMEOUT, "Not Cached", "cache#not_in_cache");
3350     s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
3351   }
3352 
3353   return;
3354 }
3355 
3356 ///////////////////////////////////////////////////////////////////////////////
3357 // Name       : OriginServerRawOpen
3358 // Description: called for ssl tunneling
3359 //
3360 // Details    :
3361 //
3362 // when the method is CONNECT, we open a raw connection to the origin
3363 // server. if the open succeeds, then do ssl tunneling from the client
3364 // to the host.
3365 //
3366 //
3367 // Possible Next States From Here:
3368 // - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
3369 // - HttpTransact::SSL_TUNNEL;
3370 //
3371 ///////////////////////////////////////////////////////////////////////////////
3372 void
OriginServerRawOpen(State * s)3373 HttpTransact::OriginServerRawOpen(State *s)
3374 {
3375   TxnDebug("http_trans", "[HttpTransact::OriginServerRawOpen]");
3376 
3377   switch (s->current.state) {
3378   case STATE_UNDEFINED:
3379   /* fall through */
3380   case OPEN_RAW_ERROR:
3381   /* fall through */
3382   case CONNECTION_ERROR:
3383   /* fall through */
3384   case CONNECTION_CLOSED:
3385     /* fall through */
3386   case OUTBOUND_CONGESTION:
3387     /* fall through */
3388     handle_server_died(s);
3389 
3390     ink_assert(s->cache_info.action == CACHE_DO_NO_ACTION);
3391     s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
3392     break;
3393   case CONNECTION_ALIVE:
3394     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, HTTP_STATUS_OK);
3395 
3396     TxnDebug("http_trans", "[OriginServerRawOpen] connection alive. next action is ssl_tunnel");
3397     s->next_action = SM_ACTION_SSL_TUNNEL;
3398     break;
3399   default:
3400     ink_assert(!("s->current.state is set to something unsupported"));
3401     break;
3402   }
3403 
3404   return;
3405 }
3406 
3407 ///////////////////////////////////////////////////////////////////////////////
3408 // Name       : HandleResponse
3409 // Description: called from the state machine when a response is received
3410 //
3411 // Details    :
3412 //
3413 //   This is the entry into a coin-sorting machine. There are many different
3414 //   bins that the response can fall into. First, the response can be invalid
3415 //   if for example it is not a response, or not complete, or the connection
3416 //   was closed, etc. Then, the response can be from  parent proxy or from
3417 //   the origin server. The next action to take differs for all three of these
3418 //   cases. Finally, good responses can either require a cache action,
3419 //   be it deletion, update, or writing or may just need to be tunnelled
3420 //   to the client. This latter case should be handled with as little processing
3421 //   as possible, since it should represent a fast path.
3422 //
3423 //
3424 // Possible Next States From Here:
3425 //
3426 //
3427 ///////////////////////////////////////////////////////////////////////////////
3428 void
HandleResponse(State * s)3429 HttpTransact::HandleResponse(State *s)
3430 {
3431   TxnDebug("http_trans", "[HttpTransact::HandleResponse]");
3432   TxnDebug("http_seq", "[HttpTransact::HandleResponse] Response received");
3433 
3434   s->source                 = SOURCE_HTTP_ORIGIN_SERVER;
3435   s->response_received_time = ink_local_time();
3436   ink_assert(s->response_received_time >= s->request_sent_time);
3437   s->current.now = s->response_received_time;
3438 
3439   TxnDebug("http_trans", "[HandleResponse] response_received_time: %" PRId64, (int64_t)s->response_received_time);
3440   DUMP_HEADER("http_hdrs", &s->hdr_info.server_response, s->state_machine_id, "Incoming O.S. Response");
3441 
3442   HTTP_INCREMENT_DYN_STAT(http_incoming_responses_stat);
3443 
3444   ink_release_assert(s->current.request_to != UNDEFINED_LOOKUP);
3445   if (s->cache_info.action != CACHE_DO_WRITE) {
3446     ink_release_assert(s->cache_info.action != CACHE_DO_LOOKUP);
3447     ink_release_assert(s->cache_info.action != CACHE_DO_SERVE);
3448     ink_release_assert(s->cache_info.action != CACHE_PREPARE_TO_DELETE);
3449     ink_release_assert(s->cache_info.action != CACHE_PREPARE_TO_UPDATE);
3450     ink_release_assert(s->cache_info.action != CACHE_PREPARE_TO_WRITE);
3451   }
3452 
3453   if (!HttpTransact::is_response_valid(s, &s->hdr_info.server_response)) {
3454     TxnDebug("http_seq", "[HttpTransact::HandleResponse] Response not valid");
3455   } else {
3456     TxnDebug("http_seq", "[HttpTransact::HandleResponse] Response valid");
3457     initialize_state_variables_from_response(s, &s->hdr_info.server_response);
3458   }
3459 
3460   switch (s->current.request_to) {
3461   case PARENT_PROXY:
3462     handle_response_from_parent(s);
3463     break;
3464   case ORIGIN_SERVER:
3465     handle_response_from_server(s);
3466     break;
3467   default:
3468     ink_assert(!("s->current.request_to is not P.P. or O.S. - hmmm."));
3469     break;
3470   }
3471 
3472   return;
3473 }
3474 
3475 ///////////////////////////////////////////////////////////////////////////////
3476 // Name       : HandleUpdateCachedObject
3477 // Description: called from the state machine when we are going to modify
3478 //              headers without any server contact.
3479 //
3480 // Details    : this function does very little. mainly to satisfy
3481 //              the call_transact_and_set_next format and not affect
3482 //              the performance of the non-invalidate operations, which
3483 //              are the majority
3484 //
3485 // Possible Next States From Here:
3486 //
3487 ///////////////////////////////////////////////////////////////////////////////
3488 void
HandleUpdateCachedObject(State * s)3489 HttpTransact::HandleUpdateCachedObject(State *s)
3490 {
3491   if (s->cache_info.write_lock_state == HttpTransact::CACHE_WL_SUCCESS) {
3492     ink_assert(s->cache_info.object_store.valid());
3493     ink_assert(s->cache_info.object_store.response_get() != nullptr);
3494     ink_assert(s->cache_info.object_read != nullptr);
3495     ink_assert(s->cache_info.object_read->valid());
3496 
3497     if (!s->cache_info.object_store.request_get()) {
3498       s->cache_info.object_store.request_set(s->cache_info.object_read->request_get());
3499     }
3500     s->request_sent_time      = s->cache_info.object_read->request_sent_time_get();
3501     s->response_received_time = s->cache_info.object_read->response_received_time_get();
3502     if (s->api_update_cached_object == UPDATE_CACHED_OBJECT_CONTINUE) {
3503       TRANSACT_RETURN(SM_ACTION_CACHE_ISSUE_UPDATE, HttpTransact::HandleUpdateCachedObjectContinue);
3504     } else {
3505       TRANSACT_RETURN(SM_ACTION_CACHE_ISSUE_UPDATE, HttpTransact::HandleApiErrorJump);
3506     }
3507   } else if (s->api_update_cached_object == UPDATE_CACHED_OBJECT_CONTINUE) {
3508     // even failed to update, continue to serve from cache
3509     HandleUpdateCachedObjectContinue(s);
3510   } else {
3511     s->api_update_cached_object = UPDATE_CACHED_OBJECT_FAIL;
3512     HandleApiErrorJump(s);
3513   }
3514 }
3515 
3516 void
HandleUpdateCachedObjectContinue(State * s)3517 HttpTransact::HandleUpdateCachedObjectContinue(State *s)
3518 {
3519   ink_assert(s->api_update_cached_object == UPDATE_CACHED_OBJECT_CONTINUE);
3520   s->cache_info.action = s->saved_update_cache_action;
3521   s->next_action       = s->saved_update_next_action;
3522 }
3523 
3524 ///////////////////////////////////////////////////////////////////////////////
3525 // Name       : HandleStatPage
3526 // Description: called from the state machine when a response is received
3527 //
3528 // Details    :
3529 //
3530 //
3531 //
3532 // Possible Next States From Here:
3533 //
3534 ///////////////////////////////////////////////////////////////////////////////
3535 void
HandleStatPage(State * s)3536 HttpTransact::HandleStatPage(State *s)
3537 {
3538   HTTPStatus status;
3539 
3540   if (s->internal_msg_buffer) {
3541     status = HTTP_STATUS_OK;
3542   } else {
3543     status = HTTP_STATUS_NOT_FOUND;
3544   }
3545 
3546   build_response(s, &s->hdr_info.client_response, s->client_info.http_version, status);
3547 
3548   ///////////////////////////
3549   // insert content-length //
3550   ///////////////////////////
3551   s->hdr_info.client_response.set_content_length(s->internal_msg_buffer_size);
3552 
3553   if (s->internal_msg_buffer_type) {
3554     int len = strlen(s->internal_msg_buffer_type);
3555 
3556     if (len > 0) {
3557       s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE, s->internal_msg_buffer_type, len);
3558     }
3559   } else {
3560     s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE, "text/plain", 10);
3561   }
3562 
3563   s->cache_info.action = CACHE_DO_NO_ACTION;
3564   s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
3565 }
3566 
3567 ///////////////////////////////////////////////////////////////////////////////
3568 // Name       : handle_response_from_parent
3569 // Description: response came from a parent proxy
3570 //
3571 // Details    :
3572 //
3573 //   The configuration file can be used to specify more than one parent
3574 //   proxy. If a connection to one fails, another can be looked up. This
3575 //   function handles responses from parent proxies. If the response is
3576 //   bad the next parent proxy (if any) is looked up. If there are no more
3577 //   parent proxies that can be looked up, the response is sent to the
3578 //   origin server. If the response is good handle_forward_server_connection_open
3579 //   is called.
3580 //
3581 //
3582 // Possible Next States From Here:
3583 //
3584 ///////////////////////////////////////////////////////////////////////////////
3585 void
handle_response_from_parent(State * s)3586 HttpTransact::handle_response_from_parent(State *s)
3587 {
3588   LookingUp_t next_lookup = UNDEFINED_LOOKUP;
3589   TxnDebug("http_trans", "[handle_response_from_parent] (hrfp)");
3590   HTTP_RELEASE_ASSERT(s->current.server == &s->parent_info);
3591 
3592   simple_or_unavailable_server_retry(s);
3593 
3594   s->parent_info.state = s->current.state;
3595   switch (s->current.state) {
3596   case CONNECTION_ALIVE:
3597     TxnDebug("http_trans", "[hrfp] connection alive");
3598     s->current.server->connect_result = 0;
3599     SET_VIA_STRING(VIA_DETAIL_PP_CONNECT, VIA_DETAIL_PP_SUCCESS);
3600     if (s->parent_result.retry) {
3601       markParentUp(s);
3602     }
3603     handle_forward_server_connection_open(s);
3604     break;
3605   case PARENT_RETRY:
3606     if (s->current.retry_type == PARENT_RETRY_SIMPLE) {
3607       s->current.simple_retry_attempts++;
3608     } else {
3609       markParentDown(s);
3610       s->current.unavailable_server_retry_attempts++;
3611     }
3612     next_lookup           = find_server_and_update_current_info(s);
3613     s->current.retry_type = PARENT_RETRY_NONE;
3614     break;
3615   default:
3616     TxnDebug("http_trans", "[hrfp] connection not alive");
3617     SET_VIA_STRING(VIA_DETAIL_PP_CONNECT, VIA_DETAIL_PP_FAILURE);
3618 
3619     ink_assert(s->hdr_info.server_request.valid());
3620 
3621     s->current.server->connect_result = ENOTCONN;
3622     // only mark the parent down in hostdb if the configuration allows it and the parent,
3623     // is not congested, see proxy.config.http.parent_proxy.mark_down_hostdb in records.config.
3624     if (s->txn_conf->parent_failures_update_hostdb && s->current.state != OUTBOUND_CONGESTION) {
3625       s->state_machine->do_hostdb_update_if_necessary();
3626     }
3627 
3628     char addrbuf[INET6_ADDRSTRLEN];
3629     TxnDebug("http_trans", "[%d] failed to connect to parent %s", s->current.attempts,
3630              ats_ip_ntop(&s->current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)));
3631 
3632     // If the request is not retryable, just give up!
3633     if (!is_request_retryable(s)) {
3634       if (s->current.state != OUTBOUND_CONGESTION) {
3635         markParentDown(s);
3636       }
3637       s->parent_result.result = PARENT_FAIL;
3638       handle_parent_died(s);
3639       return;
3640     }
3641 
3642     if (s->current.attempts < s->txn_conf->parent_connect_attempts) {
3643       HTTP_INCREMENT_DYN_STAT(http_total_parent_retries_stat);
3644       s->current.attempts++;
3645 
3646       // Are we done with this particular parent?
3647       if ((s->current.attempts - 1) % s->txn_conf->per_parent_connect_attempts != 0) {
3648         // No we are not done with this parent so retry
3649         HTTP_INCREMENT_DYN_STAT(http_total_parent_switches_stat);
3650         s->next_action = how_to_open_connection(s);
3651         TxnDebug("http_trans", "%s Retrying parent for attempt %d, max %" PRId64, "[handle_response_from_parent]",
3652                  s->current.attempts, s->txn_conf->per_parent_connect_attempts);
3653         return;
3654       } else {
3655         TxnDebug("http_trans", "%s %d per parent attempts exhausted", "[handle_response_from_parent]", s->current.attempts);
3656         HTTP_INCREMENT_DYN_STAT(http_total_parent_retries_exhausted_stat);
3657 
3658         // Only mark the parent down if we failed to connect
3659         //  to the parent otherwise slow origin servers cause
3660         //  us to mark the parent down
3661         if (s->current.state == CONNECTION_ERROR) {
3662           markParentDown(s);
3663         }
3664         // We are done so look for another parent if any
3665         next_lookup = find_server_and_update_current_info(s);
3666       }
3667     } else {
3668       // Done trying parents... fail over to origin server if that is
3669       //   appropriate
3670       HTTP_INCREMENT_DYN_STAT(http_total_parent_retries_exhausted_stat);
3671       TxnDebug("http_trans", "[handle_response_from_parent] Error. No more retries.");
3672       if (s->current.state == CONNECTION_ERROR) {
3673         markParentDown(s);
3674       }
3675       s->parent_result.result = PARENT_FAIL;
3676       next_lookup             = HOST_NONE;
3677     }
3678     break;
3679   }
3680 
3681   // We have either tried to find a new parent or failed over to the
3682   //   origin server
3683   switch (next_lookup) {
3684   case PARENT_PROXY:
3685     ink_assert(s->current.request_to == PARENT_PROXY);
3686     TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookup);
3687     break;
3688   case ORIGIN_SERVER:
3689     // Next lookup is Origin Server, try DNS for Origin Server
3690     return CallOSDNSLookup(s);
3691     break;
3692   case HOST_NONE:
3693     handle_parent_died(s);
3694     break;
3695   default:
3696     // This handles:
3697     // UNDEFINED_LOOKUP
3698     // INCOMING_ROUTER
3699     break;
3700   }
3701 }
3702 
3703 ///////////////////////////////////////////////////////////////////////////////
3704 // Name       : handle_response_from_server
3705 // Description: response is from the origin server
3706 //
3707 // Details    :
3708 //
3709 //   response from the origin server. one of three things can happen now.
3710 //   if the response is bad, then we can either retry (by first downgrading
3711 //   the request, maybe making it non-keepalive, etc.), or we can give up.
3712 //   the latter case is handled by handle_server_connection_not_open and
3713 //   sends an error response back to the client. if the response is good
3714 //   handle_forward_server_connection_open is called.
3715 //
3716 //
3717 // Possible Next States From Here:
3718 //
3719 ///////////////////////////////////////////////////////////////////////////////
3720 void
handle_response_from_server(State * s)3721 HttpTransact::handle_response_from_server(State *s)
3722 {
3723   TxnDebug("http_trans", "[handle_response_from_server] (hrfs)");
3724   HTTP_RELEASE_ASSERT(s->current.server == &s->server_info);
3725   unsigned max_connect_retries = 0;
3726 
3727   // plugin call
3728   s->server_info.state = s->current.state;
3729   if (s->os_response_plugin_inst) {
3730     s->os_response_plugin_inst->osResponse(reinterpret_cast<TSHttpTxn>(s->state_machine), s->current.state);
3731   }
3732 
3733   switch (s->current.state) {
3734   case CONNECTION_ALIVE:
3735     TxnDebug("http_trans", "[hrfs] connection alive");
3736     SET_VIA_STRING(VIA_DETAIL_SERVER_CONNECT, VIA_DETAIL_SERVER_SUCCESS);
3737     s->current.server->clear_connect_fail();
3738     handle_forward_server_connection_open(s);
3739     break;
3740   case OUTBOUND_CONGESTION:
3741     TxnDebug("http_trans", "[handle_response_from_server] Error. congestion control -- congested.");
3742     SET_VIA_STRING(VIA_DETAIL_SERVER_CONNECT, VIA_DETAIL_SERVER_FAILURE);
3743     s->set_connect_fail(EUSERS); // too many users
3744     handle_server_connection_not_open(s);
3745     break;
3746   case OPEN_RAW_ERROR:
3747   case CONNECTION_ERROR:
3748   case STATE_UNDEFINED:
3749   case INACTIVE_TIMEOUT:
3750   case PARSE_ERROR:
3751   case CONNECTION_CLOSED:
3752   case BAD_INCOMING_RESPONSE:
3753 
3754     if (is_server_negative_cached(s)) {
3755       max_connect_retries = s->txn_conf->connect_attempts_max_retries_dead_server - 1;
3756     } else {
3757       // server not yet negative cached - use default number of retries
3758       max_connect_retries = s->txn_conf->connect_attempts_max_retries;
3759     }
3760 
3761     TxnDebug("http_trans", "max_connect_retries: %d s->current.attempts: %d", max_connect_retries, s->current.attempts);
3762 
3763     if (is_request_retryable(s) && s->current.attempts < max_connect_retries) {
3764       // If this is a round robin DNS entry & we're tried configured
3765       //    number of times, we should try another node
3766       if (DNSLookupInfo::OS_Addr::OS_ADDR_TRY_CLIENT == s->dns_info.os_addr_style) {
3767         // attempt was based on client supplied server address. Try again
3768         // using HostDB.
3769         // Allow DNS attempt
3770         s->dns_info.lookup_success = false;
3771         // See if we can get data from HostDB for this.
3772         s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_TRY_HOSTDB;
3773         // Force host resolution to have the same family as the client.
3774         // Because this is a transparent connection, we can't switch address
3775         // families - that is locked in by the client source address.
3776         ats_force_order_by_family(&s->current.server->dst_addr.sa, s->my_txn_conf().host_res_data.order);
3777         return CallOSDNSLookup(s);
3778       } else if ((s->dns_info.srv_lookup_success || s->host_db_info.is_rr_elt()) &&
3779                  (s->txn_conf->connect_attempts_rr_retries > 0) &&
3780                  ((s->current.attempts + 1) % s->txn_conf->connect_attempts_rr_retries == 0)) {
3781         delete_server_rr_entry(s, max_connect_retries);
3782         return;
3783       } else {
3784         retry_server_connection_not_open(s, s->current.state, max_connect_retries);
3785         TxnDebug("http_trans", "[handle_response_from_server] Error. Retrying...");
3786         s->next_action = how_to_open_connection(s);
3787 
3788         if (s->api_server_addr_set) {
3789           // If the plugin set a server address, back up to the OS_DNS hook
3790           // to let it try another one. Force OS_ADDR_USE_CLIENT so that
3791           // in OSDNSLoopkup, we back up to how_to_open_connections which
3792           // will tell HttpSM to connect the origin server.
3793 
3794           s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT;
3795           TRANSACT_RETURN(SM_ACTION_API_OS_DNS, OSDNSLookup);
3796         }
3797         return;
3798       }
3799     } else {
3800       error_log_connection_failure(s, s->current.state);
3801       TxnDebug("http_trans", "[handle_response_from_server] Error. No more retries.");
3802       SET_VIA_STRING(VIA_DETAIL_SERVER_CONNECT, VIA_DETAIL_SERVER_FAILURE);
3803       handle_server_connection_not_open(s);
3804     }
3805     break;
3806   case ACTIVE_TIMEOUT:
3807     TxnDebug("http_trans", "[hrfs] connection not alive");
3808     SET_VIA_STRING(VIA_DETAIL_SERVER_CONNECT, VIA_DETAIL_SERVER_FAILURE);
3809     s->set_connect_fail(ETIMEDOUT);
3810     handle_server_connection_not_open(s);
3811     break;
3812   default:
3813     ink_assert(!("s->current.state is set to something unsupported"));
3814     break;
3815   }
3816 
3817   return;
3818 }
3819 
3820 ///////////////////////////////////////////////////////////////////////////////
3821 // Name       : delete_server_rr_entry
3822 // Description:
3823 //
3824 // Details    :
3825 //
3826 //   connection to server failed mark down the server round robin entry
3827 //
3828 //
3829 // Possible Next States From Here:
3830 //
3831 ///////////////////////////////////////////////////////////////////////////////
3832 void
delete_server_rr_entry(State * s,int max_retries)3833 HttpTransact::delete_server_rr_entry(State *s, int max_retries)
3834 {
3835   char addrbuf[INET6_ADDRSTRLEN];
3836 
3837   TxnDebug("http_trans", "[%d] failed to connect to %s", s->current.attempts,
3838            ats_ip_ntop(&s->current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)));
3839   TxnDebug("http_trans", "[delete_server_rr_entry] marking rr entry "
3840                          "down and finding next one");
3841   ink_assert(s->current.server->had_connect_fail());
3842   ink_assert(s->current.request_to == ORIGIN_SERVER);
3843   ink_assert(s->current.server == &s->server_info);
3844   update_dns_info(&s->dns_info, &s->current);
3845   s->current.attempts++;
3846   TxnDebug("http_trans", "[delete_server_rr_entry] attempts now: %d, max: %d", s->current.attempts, max_retries);
3847   TRANSACT_RETURN(SM_ACTION_ORIGIN_SERVER_RR_MARK_DOWN, ReDNSRoundRobin);
3848 }
3849 
3850 void
error_log_connection_failure(State * s,ServerState_t conn_state)3851 HttpTransact::error_log_connection_failure(State *s, ServerState_t conn_state)
3852 {
3853   char addrbuf[INET6_ADDRSTRLEN];
3854   TxnDebug("http_trans", "[%d] failed to connect [%d] to %s", s->current.attempts, conn_state,
3855            ats_ip_ntop(&s->current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)));
3856 
3857   if (s->current.server->had_connect_fail()) {
3858     char *url_str = s->hdr_info.client_request.url_string_get(&s->arena);
3859     int host_len;
3860     const char *host_name_ptr = s->unmapped_url.host_get(&host_len);
3861     std::string_view host_name{host_name_ptr, size_t(host_len)};
3862     TxnDebug("dead_server", "%s",
3863              lbw()
3864                .clip(1)
3865                .print("CONNECT: attempt fail [{}] to {} for host='{}' "
3866                       "connection_result={::s} error={::s} attempts={} url='{}'",
3867                       HttpDebugNames::get_server_state_name(conn_state), s->current.server->dst_addr, host_name,
3868                       ts::bwf::Errno(s->current.server->connect_result), ts::bwf::Errno(s->cause_of_death_errno),
3869                       s->current.attempts, ts::bwf::FirstOf(url_str, "<none>"))
3870                .extend(1)
3871                .write('\0')
3872                .data());
3873 
3874     s->arena.str_free(url_str);
3875   }
3876 }
3877 
3878 ///////////////////////////////////////////////////////////////////////////////
3879 // Name       : retry_server_connection_not_open
3880 // Description:
3881 //
3882 // Details    :
3883 //
3884 //   connection to server failed. retry.
3885 //
3886 //
3887 // Possible Next States From Here:
3888 //
3889 ///////////////////////////////////////////////////////////////////////////////
3890 void
retry_server_connection_not_open(State * s,ServerState_t conn_state,unsigned max_retries)3891 HttpTransact::retry_server_connection_not_open(State *s, ServerState_t conn_state, unsigned max_retries)
3892 {
3893   ink_assert(s->current.state != CONNECTION_ALIVE);
3894   ink_assert(s->current.state != ACTIVE_TIMEOUT);
3895   ink_assert(s->current.attempts <= max_retries);
3896   ink_assert(s->cause_of_death_errno != -UNKNOWN_INTERNAL_ERROR);
3897 
3898   error_log_connection_failure(s, conn_state);
3899 
3900   //////////////////////////////////////////////
3901   // disable keep-alive for request and retry //
3902   //////////////////////////////////////////////
3903   s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
3904   s->current.attempts++;
3905 
3906   TxnDebug("http_trans", "[retry_server_connection_not_open] attempts now: %d, max: %d", s->current.attempts, max_retries);
3907 
3908   return;
3909 }
3910 
3911 ///////////////////////////////////////////////////////////////////////////////
3912 // Name       : handle_server_connection_not_open
3913 // Description:
3914 //
3915 // Details    :
3916 //
3917 //
3918 // Possible Next States From Here:
3919 //
3920 ///////////////////////////////////////////////////////////////////////////////
3921 void
handle_server_connection_not_open(State * s)3922 HttpTransact::handle_server_connection_not_open(State *s)
3923 {
3924   bool serve_from_cache = false;
3925 
3926   TxnDebug("http_trans", "[handle_server_connection_not_open] (hscno)");
3927   TxnDebug("http_seq", "[HttpTransact::handle_server_connection_not_open] ");
3928   ink_assert(s->current.state != CONNECTION_ALIVE);
3929 
3930   SET_VIA_STRING(VIA_SERVER_RESULT, VIA_SERVER_ERROR);
3931   HTTP_INCREMENT_DYN_STAT(http_broken_server_connections_stat);
3932 
3933   // Fire off a hostdb update to mark the server as down
3934   s->state_machine->do_hostdb_update_if_necessary();
3935 
3936   switch (s->cache_info.action) {
3937   case CACHE_DO_UPDATE:
3938   case CACHE_DO_SERVE:
3939     serve_from_cache = is_stale_cache_response_returnable(s);
3940     break;
3941 
3942   case CACHE_PREPARE_TO_DELETE:
3943   /* fall through */
3944   case CACHE_PREPARE_TO_UPDATE:
3945   /* fall through */
3946   case CACHE_PREPARE_TO_WRITE:
3947     ink_release_assert(!"Why still preparing for cache action - "
3948                         "we skipped a step somehow.");
3949     break;
3950 
3951   case CACHE_DO_LOOKUP:
3952     ink_assert(!("Why server response? Should have been a cache operation"));
3953     break;
3954 
3955   case CACHE_DO_DELETE:
3956   // decisions, decisions. what should we do here?
3957   // we could theoretically still delete the cached
3958   // copy or serve it back with a warning, or easier
3959   // just punt and biff the user. i say: biff the user.
3960   /* fall through */
3961   case CACHE_DO_UNDEFINED:
3962   /* fall through */
3963   case CACHE_DO_NO_ACTION:
3964   /* fall through */
3965   case CACHE_DO_WRITE:
3966   /* fall through */
3967   default:
3968     serve_from_cache = false;
3969     break;
3970   }
3971 
3972   if (serve_from_cache) {
3973     ink_assert(s->cache_info.object_read != nullptr);
3974     ink_assert(s->cache_info.action == CACHE_DO_UPDATE || s->cache_info.action == CACHE_DO_SERVE);
3975     ink_assert(s->internal_msg_buffer == nullptr);
3976     s->source = SOURCE_CACHE;
3977     TxnDebug("http_trans", "[hscno] serving stale doc to client");
3978     build_response_from_cache(s, HTTP_WARNING_CODE_REVALIDATION_FAILED);
3979   } else {
3980     handle_server_died(s);
3981     s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
3982   }
3983 
3984   return;
3985 }
3986 
3987 ///////////////////////////////////////////////////////////////////////////////
3988 // Name       : handle_forward_server_connection_open
3989 // Description: connection to a forward server is open and good
3990 //
3991 // Details    :
3992 //
3993 //   "Forward server" includes the parent proxy
3994 //   or the origin server. This function first determines if the forward
3995 //   server uses HTTP 0.9, in which case it simply tunnels the response
3996 //   to the client. Else, it updates
3997 //
3998 //
3999 // Possible Next States From Here:
4000 //
4001 ///////////////////////////////////////////////////////////////////////////////
4002 void
handle_forward_server_connection_open(State * s)4003 HttpTransact::handle_forward_server_connection_open(State *s)
4004 {
4005   TxnDebug("http_trans", "[handle_forward_server_connection_open] (hfsco)");
4006   TxnDebug("http_seq", "[HttpTransact::handle_server_connection_open] ");
4007   ink_release_assert(s->current.state == CONNECTION_ALIVE);
4008 
4009   if (s->hdr_info.server_response.version_get() != s->current.server->http_version) {
4010     s->updated_server_version = s->hdr_info.server_response.version_get();
4011   }
4012 
4013   if (s->hdr_info.server_response.status_get() == HTTP_STATUS_CONTINUE ||
4014       s->hdr_info.server_response.status_get() == HTTP_STATUS_EARLY_HINTS) {
4015     handle_100_continue_response(s);
4016     return;
4017   }
4018 
4019   s->state_machine->do_hostdb_update_if_necessary();
4020 
4021   if (s->www_auth_content == CACHE_AUTH_FRESH) {
4022     // no update is needed - either to serve from cache if authorized,
4023     // or tunnnel the server response
4024     if (s->hdr_info.server_response.status_get() == HTTP_STATUS_OK) {
4025       // borrow a state variable used by the API function
4026       // this enable us to serve from cache without doing any updating
4027       s->api_server_response_ignore = true;
4028     }
4029     // s->cache_info.action = CACHE_PREPARE_TO_SERVE;
4030     // xing xing in the tunneling case, need to check when the cache_read_vc is closed, make sure the cache_read_vc is closed
4031     // right
4032     // away
4033   }
4034 
4035   CacheVConnection *cw_vc = s->state_machine->get_cache_sm().cache_write_vc;
4036 
4037   if (s->redirect_info.redirect_in_process && s->state_machine->enable_redirection) {
4038     if (s->cache_info.action == CACHE_DO_NO_ACTION) {
4039       switch (s->hdr_info.server_response.status_get()) {
4040       case HTTP_STATUS_MULTIPLE_CHOICES:   // 300
4041       case HTTP_STATUS_MOVED_PERMANENTLY:  // 301
4042       case HTTP_STATUS_MOVED_TEMPORARILY:  // 302
4043       case HTTP_STATUS_SEE_OTHER:          // 303
4044       case HTTP_STATUS_USE_PROXY:          // 305
4045       case HTTP_STATUS_TEMPORARY_REDIRECT: // 307
4046       case HTTP_STATUS_PERMANENT_REDIRECT: // 308
4047         break;
4048       default:
4049         TxnDebug("http_trans", "[hfsco] redirect in progress, non-3xx response, setting cache_do_write");
4050         if (cw_vc && s->txn_conf->cache_http) {
4051           s->cache_info.action = CACHE_DO_WRITE;
4052         }
4053         break;
4054       }
4055     }
4056   }
4057 
4058   switch (s->cache_info.action) {
4059   case CACHE_DO_WRITE:
4060   /* fall through */
4061   case CACHE_DO_UPDATE:
4062   /* fall through */
4063   case CACHE_DO_DELETE:
4064     TxnDebug("http_trans", "[hfsco] cache action: %s", HttpDebugNames::get_cache_action_name(s->cache_info.action));
4065     handle_cache_operation_on_forward_server_response(s);
4066     break;
4067   case CACHE_PREPARE_TO_DELETE:
4068   /* fall through */
4069   case CACHE_PREPARE_TO_UPDATE:
4070   /* fall through */
4071   case CACHE_PREPARE_TO_WRITE:
4072     ink_release_assert(!"Why still preparing for cache action - we skipped a step somehow.");
4073     break;
4074   case CACHE_DO_LOOKUP:
4075   /* fall through */
4076   case CACHE_DO_SERVE:
4077     ink_assert(!("Why server response? Should have been a cache operation"));
4078     break;
4079   case CACHE_DO_UNDEFINED:
4080   /* fall through */
4081   case CACHE_DO_NO_ACTION:
4082   /* fall through */
4083   default:
4084     // Just tunnel?
4085     TxnDebug("http_trans", "[hfsco] cache action: %s", HttpDebugNames::get_cache_action_name(s->cache_info.action));
4086     handle_no_cache_operation_on_forward_server_response(s);
4087     break;
4088   }
4089 
4090   return;
4091 }
4092 
4093 // void HttpTransact::handle_100_continue_response(State* s)
4094 //
4095 //   We've received a 100 continue response.  Determine if
4096 //     we should just swallow the response 100 or forward it
4097 //     the client.  http-1.1-spec-rev-06 section 8.2.3
4098 //
4099 void
handle_100_continue_response(State * s)4100 HttpTransact::handle_100_continue_response(State *s)
4101 {
4102   bool forward_100 = false;
4103 
4104   HTTPVersion ver = s->hdr_info.client_request.version_get();
4105   if (ver == HTTP_1_1) {
4106     forward_100 = true;
4107   } else if (ver == HTTP_1_0) {
4108     if (s->hdr_info.client_request.value_get_int(MIME_FIELD_EXPECT, MIME_LEN_EXPECT) == 100) {
4109       forward_100 = true;
4110     }
4111   }
4112 
4113   if (forward_100) {
4114     // We just want to copy the server's response.  All
4115     //   the other build response functions insist on
4116     //   adding stuff
4117     build_response_copy(s, &s->hdr_info.server_response, &s->hdr_info.client_response, s->client_info.http_version);
4118     TRANSACT_RETURN(SM_ACTION_INTERNAL_100_RESPONSE, HandleResponse);
4119   } else {
4120     TRANSACT_RETURN(SM_ACTION_SERVER_PARSE_NEXT_HDR, HandleResponse);
4121   }
4122 }
4123 
4124 // void HttpTransact::build_response_copy
4125 //
4126 //   Build a response with minimal changes from the base response
4127 //
4128 void
build_response_copy(State * s,HTTPHdr * base_response,HTTPHdr * outgoing_response,HTTPVersion outgoing_version)4129 HttpTransact::build_response_copy(State *s, HTTPHdr *base_response, HTTPHdr *outgoing_response, HTTPVersion outgoing_version)
4130 {
4131   HttpTransactHeaders::copy_header_fields(base_response, outgoing_response, s->txn_conf->fwd_proxy_auth_to_parent, s->current.now);
4132   HttpTransactHeaders::convert_response(outgoing_version, outgoing_response); // http version conversion
4133   HttpTransactHeaders::add_server_header_to_response(s->txn_conf, outgoing_response);
4134 
4135   DUMP_HEADER("http_hdrs", outgoing_response, s->state_machine_id, "Proxy's Response");
4136 }
4137 
4138 //////////////////////////////////////////////////////////////////////////
4139 //   IMS handling table                                                 //
4140 //       OS = Origin Server                                             //
4141 //       IMS = A GET request w/ an If-Modified-Since header             //
4142 //       LMs = Last modified state returned by server                   //
4143 //       D, D' are Last modified dates returned by the origin server    //
4144 //          and are later used for IMS                                  //
4145 //       D < D'                                                         //
4146 //                                                                      //
4147 //  +----------+-----------+----------+-----------+--------------+      //
4148 //  | Client's | Cached    | Proxy's  |   Response to client     |      //
4149 //  | Request  | State     | Request  +-----------+--------------+      //
4150 //  |          |           |          | OS 200    |  OS 304      |      //
4151 //  +==========+===========+==========+===========+==============+      //
4152 //  |  GET     | Fresh     | N/A      |  N/A      |  N/A         |      //
4153 //  +----------+-----------+----------+-----------+--------------+      //
4154 //  |  GET     | Stale, D' | IMS  D'  | 200, new  | 200, cached  |      //
4155 //  +----------+-----------+----------+-----------+--------------+      //
4156 //  |  GET     | Stale, E  | INM  E   | 200, new  | 200, cached  |      //
4157 //  +----------+-----------+----------+-----------+--------------+      //
4158 //  |  INM E   | Stale, E  | INM  E   | 304       | 304          |      //
4159 //  +----------+-----------+----------+-----------+--------------+      //
4160 //  |  INM E + | Stale,    | INM E    | 200, new *| 304          |      //
4161 //  |  IMS D'  | E + D'    | IMS D'   |           |              |      //
4162 //  +----------+-----------+----------+-----------+--------------+      //
4163 //  |  IMS D   | None      | GET      | 200, new *|  N/A         |      //
4164 //  +----------+-----------+----------+-----------+--------------+      //
4165 //  |  INM E   | None      | GET      | 200, new *|  N/A         |      //
4166 //  +----------+-----------+----------+-----------+--------------+      //
4167 //  |  IMS D   | Stale, D' | IMS D'   | 200, new  | Compare      |      //
4168 //  |---------------------------------------------| LMs & D'     |      //
4169 //  |  IMS D'  | Stale, D' | IMS D'   | 200, new  | If match, 304|      //
4170 //  |---------------------------------------------| If no match, |      //
4171 //  |  IMS D'  | Stale D   | IMS D    | 200, new *|  200, cached |      //
4172 //  +------------------------------------------------------------+      //
4173 //                                                                      //
4174 //  Note: * indicates a case that could be optimized to return          //
4175 //     304 to the client but currently is not                           //
4176 //                                                                      //
4177 //////////////////////////////////////////////////////////////////////////
4178 
4179 ///////////////////////////////////////////////////////////////////////////////
4180 // Name       : handle_cache_operation_on_forward_server_response
4181 // Description:
4182 //
4183 // Details    :
4184 //
4185 //
4186 //
4187 // Possible Next States From Here:
4188 //
4189 ///////////////////////////////////////////////////////////////////////////////
4190 void
handle_cache_operation_on_forward_server_response(State * s)4191 HttpTransact::handle_cache_operation_on_forward_server_response(State *s)
4192 {
4193   TxnDebug("http_trans", "[handle_cache_operation_on_forward_server_response] (hcoofsr)");
4194   TxnDebug("http_seq", "[handle_cache_operation_on_forward_server_response]");
4195 
4196   HTTPHdr *base_response          = nullptr;
4197   HTTPStatus server_response_code = HTTP_STATUS_NONE;
4198   HTTPStatus client_response_code = HTTP_STATUS_NONE;
4199   const char *warn_text           = nullptr;
4200   bool cacheable                  = false;
4201 
4202   cacheable = is_response_cacheable(s, &s->hdr_info.client_request, &s->hdr_info.server_response);
4203   TxnDebug("http_trans", "[hcoofsr] response %s cacheable", cacheable ? "is" : "is not");
4204 
4205   // set the correct next action, cache action, response code, and base response
4206 
4207   server_response_code = s->hdr_info.server_response.status_get();
4208   switch (server_response_code) {
4209   case HTTP_STATUS_NOT_MODIFIED: // 304
4210     SET_VIA_STRING(VIA_SERVER_RESULT, VIA_SERVER_NOT_MODIFIED);
4211 
4212     // determine the correct cache action, next state, and response
4213     // precondition: s->cache_info.action should be one of the following
4214     // CACHE_DO_DELETE, or CACHE_DO_UPDATE; otherwise, it's an error.
4215     if (s->api_server_response_ignore && s->cache_info.action == CACHE_DO_UPDATE) {
4216       s->api_server_response_ignore = false;
4217       ink_assert(s->cache_info.object_read);
4218       base_response        = s->cache_info.object_read->response_get();
4219       s->cache_info.action = CACHE_DO_SERVE;
4220       TxnDebug("http_trans", "[hcoofsr] not merging, cache action changed to: %s",
4221                HttpDebugNames::get_cache_action_name(s->cache_info.action));
4222       s->next_action       = SM_ACTION_SERVE_FROM_CACHE;
4223       client_response_code = base_response->status_get();
4224     } else if ((s->cache_info.action == CACHE_DO_DELETE) || ((s->cache_info.action == CACHE_DO_UPDATE) && !cacheable)) {
4225       if (is_request_conditional(&s->hdr_info.client_request)) {
4226         client_response_code = HttpTransactCache::match_response_to_request_conditionals(
4227           &s->hdr_info.client_request, s->cache_info.object_read->response_get(), s->response_received_time);
4228       } else {
4229         client_response_code = HTTP_STATUS_OK;
4230       }
4231 
4232       if (client_response_code != HTTP_STATUS_OK) {
4233         // we can just forward the not modified response
4234         // from the server and delete the cached copy
4235         base_response        = &s->hdr_info.server_response;
4236         client_response_code = base_response->status_get();
4237         s->cache_info.action = CACHE_DO_DELETE;
4238         s->next_action       = SM_ACTION_INTERNAL_CACHE_DELETE;
4239       } else {
4240         // We got screwed. The client did not send a conditional request,
4241         // but we had a cached copy which we revalidated. The server has
4242         // now told us to delete the cached copy and sent back a 304.
4243         // We need to send the cached copy to the client, then delete it.
4244         if (s->method == HTTP_WKSIDX_HEAD) {
4245           s->cache_info.action = CACHE_DO_DELETE;
4246           s->next_action       = SM_ACTION_SERVER_READ;
4247         } else {
4248           if (s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE)) {
4249             s->state_machine->do_range_setup_if_necessary();
4250             // Check client request range header if we cached a stealed content with cacheable=false
4251           }
4252           s->cache_info.action = CACHE_DO_SERVE_AND_DELETE;
4253           s->next_action       = SM_ACTION_SERVE_FROM_CACHE;
4254         }
4255         base_response        = s->cache_info.object_read->response_get();
4256         client_response_code = base_response->status_get();
4257       }
4258 
4259     } else if (s->cache_info.action == CACHE_DO_UPDATE && is_request_conditional(&s->hdr_info.server_request)) {
4260       // CACHE_DO_UPDATE and server response is cacheable
4261       if (is_request_conditional(&s->hdr_info.client_request)) {
4262         if (s->txn_conf->cache_when_to_revalidate != 4) {
4263           client_response_code = HttpTransactCache::match_response_to_request_conditionals(
4264             &s->hdr_info.client_request, s->cache_info.object_read->response_get(), s->response_received_time);
4265         } else {
4266           client_response_code = server_response_code;
4267         }
4268       } else {
4269         client_response_code = HTTP_STATUS_OK;
4270       }
4271 
4272       if (client_response_code != HTTP_STATUS_OK) {
4273         // delete the cached copy unless configured to always verify IMS
4274         if (s->txn_conf->cache_when_to_revalidate != 4) {
4275           s->cache_info.action = CACHE_DO_UPDATE;
4276           s->next_action       = SM_ACTION_INTERNAL_CACHE_UPDATE_HEADERS;
4277           /* base_response will be set after updating headers below */
4278         } else {
4279           s->cache_info.action = CACHE_DO_NO_ACTION;
4280           s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
4281           base_response        = &s->hdr_info.server_response;
4282         }
4283       } else {
4284         if (s->method == HTTP_WKSIDX_HEAD) {
4285           s->cache_info.action = CACHE_DO_UPDATE;
4286           s->next_action       = SM_ACTION_SERVER_READ;
4287         } else {
4288           if (s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE)) {
4289             s->state_machine->do_range_setup_if_necessary();
4290             // Note that even if the Range request is not satisfiable, we
4291             // update and serve this cache. This will give a 200 response to
4292             // a bad client, but allows us to avoid pegging the origin (e.g. abuse).
4293           }
4294           s->cache_info.action = CACHE_DO_SERVE_AND_UPDATE;
4295           s->next_action       = SM_ACTION_SERVE_FROM_CACHE;
4296         }
4297         /* base_response will be set after updating headers below */
4298       }
4299 
4300     } else { // cache action != CACHE_DO_DELETE and != CACHE_DO_UPDATE
4301 
4302       // bogus response from server. deal by tunnelling to client.
4303       // server should not have sent back a 304 because our request
4304       // should not have been an conditional.
4305       TxnDebug("http_trans", "[hcoofsr] 304 for non-conditional request");
4306       s->cache_info.action = CACHE_DO_NO_ACTION;
4307       s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
4308       client_response_code = s->hdr_info.server_response.status_get();
4309       base_response        = &s->hdr_info.server_response;
4310 
4311       // since this is bad, insert warning header into client response
4312       // The only exception case is conditional client request,
4313       // cache miss, and client request being unlikely cacheable.
4314       // In this case, the server request is given the same
4315       // conditional headers as client request (see build_request()).
4316       // So an unexpected 304 might be received.
4317       // FIXME: check this case
4318       if (is_request_likely_cacheable(s, &s->hdr_info.client_request)) {
4319         warn_text = "Proxy received unexpected 304 response; "
4320                     "content may be stale";
4321       }
4322     }
4323 
4324     break;
4325 
4326   case HTTP_STATUS_HTTPVER_NOT_SUPPORTED: // 505
4327   {
4328     bool keep_alive = (s->current.server->keep_alive == HTTP_KEEPALIVE);
4329 
4330     s->next_action = how_to_open_connection(s);
4331 
4332     /* Downgrade the request level and retry */
4333     if (!HttpTransactHeaders::downgrade_request(&keep_alive, &s->hdr_info.server_request)) {
4334       build_error_response(s, HTTP_STATUS_HTTPVER_NOT_SUPPORTED, "HTTP Version Not Supported", "response#bad_version");
4335       s->next_action        = SM_ACTION_SEND_ERROR_CACHE_NOOP;
4336       s->already_downgraded = true;
4337     } else {
4338       if (!keep_alive) {
4339         /* START Hack */
4340         (s->hdr_info.server_request).field_delete(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
4341         /* END   Hack */
4342       }
4343       s->already_downgraded = true;
4344       s->next_action        = how_to_open_connection(s);
4345     }
4346   }
4347     return;
4348 
4349   default:
4350     TxnDebug("http_trans", "[hcoofsr] response code: %d", server_response_code);
4351     SET_VIA_STRING(VIA_SERVER_RESULT, VIA_SERVER_SERVED);
4352     SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVED);
4353 
4354     /* if we receive a 500, 502, 503 or 504 while revalidating
4355        a document, treat the response as a 304 and in effect revalidate the document for
4356        negative_revalidating_lifetime. (negative revalidating)
4357      */
4358 
4359     if ((server_response_code == HTTP_STATUS_INTERNAL_SERVER_ERROR || server_response_code == HTTP_STATUS_GATEWAY_TIMEOUT ||
4360          server_response_code == HTTP_STATUS_BAD_GATEWAY || server_response_code == HTTP_STATUS_SERVICE_UNAVAILABLE) &&
4361         s->cache_info.action == CACHE_DO_UPDATE && s->txn_conf->negative_revalidating_enabled &&
4362         is_stale_cache_response_returnable(s)) {
4363       HTTPStatus cached_response_code = s->cache_info.object_read->response_get()->status_get();
4364       if (!(cached_response_code == HTTP_STATUS_INTERNAL_SERVER_ERROR || cached_response_code == HTTP_STATUS_GATEWAY_TIMEOUT ||
4365             cached_response_code == HTTP_STATUS_BAD_GATEWAY || cached_response_code == HTTP_STATUS_SERVICE_UNAVAILABLE)) {
4366         TxnDebug("http_trans", "[hcoofsr] negative revalidating: revalidate stale object and serve from cache");
4367 
4368         s->cache_info.object_store.create();
4369         s->cache_info.object_store.request_set(&s->hdr_info.client_request);
4370         s->cache_info.object_store.response_set(s->cache_info.object_read->response_get());
4371         base_response   = s->cache_info.object_store.response_get();
4372         time_t exp_time = s->txn_conf->negative_revalidating_lifetime + ink_local_time();
4373         base_response->set_expires(exp_time);
4374 
4375         SET_VIA_STRING(VIA_CACHE_FILL_ACTION, VIA_CACHE_UPDATED);
4376         HTTP_INCREMENT_DYN_STAT(http_cache_updates_stat);
4377 
4378         // unset Cache-control: "need-revalidate-once" (if it's set)
4379         // This directive is used internally by T.S. to invalidate
4380         // documents so that an invalidated document needs to be
4381         // revalidated again.
4382         base_response->unset_cooked_cc_need_revalidate_once();
4383 
4384         if (is_request_conditional(&s->hdr_info.client_request) &&
4385             HttpTransactCache::match_response_to_request_conditionals(&s->hdr_info.client_request,
4386                                                                       s->cache_info.object_read->response_get(),
4387                                                                       s->response_received_time) == HTTP_STATUS_NOT_MODIFIED) {
4388           s->next_action       = SM_ACTION_INTERNAL_CACHE_UPDATE_HEADERS;
4389           client_response_code = HTTP_STATUS_NOT_MODIFIED;
4390         } else {
4391           if (s->method == HTTP_WKSIDX_HEAD) {
4392             s->cache_info.action = CACHE_DO_UPDATE;
4393             s->next_action       = SM_ACTION_INTERNAL_CACHE_NOOP;
4394           } else {
4395             s->cache_info.action = CACHE_DO_SERVE_AND_UPDATE;
4396             s->next_action       = SM_ACTION_SERVE_FROM_CACHE;
4397           }
4398 
4399           client_response_code = s->cache_info.object_read->response_get()->status_get();
4400         }
4401 
4402         ink_assert(base_response->valid());
4403 
4404         if (client_response_code == HTTP_STATUS_NOT_MODIFIED) {
4405           ink_assert(GET_VIA_STRING(VIA_CLIENT_REQUEST) != VIA_CLIENT_SIMPLE);
4406           SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_IMS);
4407           SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_NOT_MODIFIED);
4408         } else {
4409           SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVED);
4410         }
4411 
4412         ink_assert(client_response_code != HTTP_STATUS_NONE);
4413 
4414         if (s->next_action == SM_ACTION_SERVE_FROM_CACHE && s->state_machine->do_transform_open()) {
4415           set_header_for_transform(s, base_response);
4416         } else {
4417           build_response(s, base_response, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
4418         }
4419 
4420         return;
4421       }
4422     }
4423 
4424     s->next_action       = SM_ACTION_SERVER_READ;
4425     client_response_code = server_response_code;
4426     base_response        = &s->hdr_info.server_response;
4427 
4428     s->is_cacheable_due_to_negative_caching_configuration = cacheable && is_negative_caching_appropriate(s);
4429 
4430     // determine the correct cache action given the original cache action,
4431     // cacheability of server response, and request method
4432     // precondition: s->cache_info.action is one of the following
4433     // CACHE_DO_UPDATE, CACHE_DO_WRITE, or CACHE_DO_DELETE
4434     if (s->api_server_response_no_store) {
4435       s->cache_info.action = CACHE_DO_NO_ACTION;
4436     } else if (s->api_server_response_ignore && server_response_code == HTTP_STATUS_OK &&
4437                s->hdr_info.server_request.method_get_wksidx() == HTTP_WKSIDX_HEAD) {
4438       s->api_server_response_ignore = false;
4439       ink_assert(s->cache_info.object_read);
4440       base_response        = s->cache_info.object_read->response_get();
4441       s->cache_info.action = CACHE_DO_SERVE;
4442       TxnDebug("http_trans",
4443                "[hcoofsr] ignoring server response, "
4444                "cache action changed to: %s",
4445                HttpDebugNames::get_cache_action_name(s->cache_info.action));
4446       s->next_action       = SM_ACTION_SERVE_FROM_CACHE;
4447       client_response_code = base_response->status_get();
4448     } else if (s->cache_info.action == CACHE_DO_UPDATE) {
4449       if (s->www_auth_content == CACHE_AUTH_FRESH || s->api_server_response_ignore) {
4450         s->cache_info.action = CACHE_DO_NO_ACTION;
4451       } else if (s->www_auth_content == CACHE_AUTH_STALE && server_response_code == HTTP_STATUS_UNAUTHORIZED) {
4452         s->cache_info.action = CACHE_DO_NO_ACTION;
4453       } else if (!cacheable) {
4454         s->cache_info.action = CACHE_DO_DELETE;
4455       } else if (s->method == HTTP_WKSIDX_HEAD) {
4456         s->cache_info.action = CACHE_DO_DELETE;
4457       } else {
4458         ink_assert(s->cache_info.object_read != nullptr);
4459         s->cache_info.action = CACHE_DO_REPLACE;
4460       }
4461 
4462     } else if (s->cache_info.action == CACHE_DO_WRITE) {
4463       if (!cacheable) {
4464         s->cache_info.action = CACHE_DO_NO_ACTION;
4465       } else if (s->method == HTTP_WKSIDX_HEAD) {
4466         s->cache_info.action = CACHE_DO_NO_ACTION;
4467       } else {
4468         s->cache_info.action = CACHE_DO_WRITE;
4469       }
4470 
4471     } else if (s->cache_info.action == CACHE_DO_DELETE) {
4472       // do nothing
4473 
4474     } else {
4475       ink_assert(!("cache action inconsistent with current state"));
4476     }
4477     // postcondition: s->cache_info.action is one of the following
4478     // CACHE_DO_REPLACE, CACHE_DO_WRITE, CACHE_DO_DELETE, or
4479     // CACHE_DO_NO_ACTION
4480 
4481     // Check see if we ought to serve the client a 304 based on
4482     //   it's IMS date.  We may gotten a 200 back from the origin
4483     //   server if our (the proxies's) cached copy was out of date
4484     //   but the client's wasn't.  However, if the response is
4485     //   not cacheable we ought not issue a 304 to the client so
4486     //   make sure we are writing the document to the cache if
4487     //   before issuing a 304
4488     if (s->cache_info.action == CACHE_DO_WRITE || s->cache_info.action == CACHE_DO_NO_ACTION ||
4489         s->cache_info.action == CACHE_DO_REPLACE) {
4490       if (s->is_cacheable_due_to_negative_caching_configuration) {
4491         HTTPHdr *resp;
4492         s->cache_info.object_store.create();
4493         s->cache_info.object_store.request_set(&s->hdr_info.client_request);
4494         s->cache_info.object_store.response_set(&s->hdr_info.server_response);
4495         resp = s->cache_info.object_store.response_get();
4496         if (!resp->presence(MIME_PRESENCE_EXPIRES)) {
4497           time_t exp_time = s->txn_conf->negative_caching_lifetime + ink_local_time();
4498 
4499           resp->set_expires(exp_time);
4500         }
4501       } else if (is_request_conditional(&s->hdr_info.client_request) && server_response_code == HTTP_STATUS_OK) {
4502         client_response_code = HttpTransactCache::match_response_to_request_conditionals(
4503           &s->hdr_info.client_request, &s->hdr_info.server_response, s->response_received_time);
4504 
4505         TxnDebug("http_trans",
4506                  "[hcoofsr] conditional request, 200 "
4507                  "response, send back 304 if possible [crc=%d]",
4508                  client_response_code);
4509         if ((client_response_code == HTTP_STATUS_NOT_MODIFIED) || (client_response_code == HTTP_STATUS_PRECONDITION_FAILED)) {
4510           switch (s->cache_info.action) {
4511           case CACHE_DO_WRITE:
4512           case CACHE_DO_REPLACE:
4513             s->next_action = SM_ACTION_INTERNAL_CACHE_WRITE;
4514             break;
4515           case CACHE_DO_DELETE:
4516             s->next_action = SM_ACTION_INTERNAL_CACHE_DELETE;
4517             break;
4518           default:
4519             s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
4520             break;
4521           }
4522         } else {
4523           SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVER_REVALIDATED);
4524         }
4525       }
4526     } else if (s->is_cacheable_due_to_negative_caching_configuration) {
4527       s->is_cacheable_due_to_negative_caching_configuration = false;
4528     }
4529 
4530     break;
4531   }
4532 
4533   // update stat, set via string, etc
4534 
4535   switch (s->cache_info.action) {
4536   case CACHE_DO_SERVE_AND_DELETE:
4537   // fall through
4538   case CACHE_DO_DELETE:
4539     TxnDebug("http_trans", "[hcoofsr] delete cached copy");
4540     SET_VIA_STRING(VIA_CACHE_FILL_ACTION, VIA_CACHE_DELETED);
4541     HTTP_INCREMENT_DYN_STAT(http_cache_deletes_stat);
4542     break;
4543   case CACHE_DO_WRITE:
4544     TxnDebug("http_trans", "[hcoofsr] cache write");
4545     SET_VIA_STRING(VIA_CACHE_FILL_ACTION, VIA_CACHE_WRITTEN);
4546     HTTP_INCREMENT_DYN_STAT(http_cache_writes_stat);
4547     break;
4548   case CACHE_DO_SERVE_AND_UPDATE:
4549   // fall through
4550   case CACHE_DO_UPDATE:
4551   // fall through
4552   case CACHE_DO_REPLACE:
4553     TxnDebug("http_trans", "[hcoofsr] cache update/replace");
4554     SET_VIA_STRING(VIA_CACHE_FILL_ACTION, VIA_CACHE_UPDATED);
4555     HTTP_INCREMENT_DYN_STAT(http_cache_updates_stat);
4556     break;
4557   default:
4558     break;
4559   }
4560 
4561   if ((client_response_code == HTTP_STATUS_NOT_MODIFIED) && (s->cache_info.action != CACHE_DO_NO_ACTION)) {
4562     /* ink_assert(GET_VIA_STRING(VIA_CLIENT_REQUEST)
4563        != VIA_CLIENT_SIMPLE); */
4564     TxnDebug("http_trans", "[hcoofsr] Client request was conditional");
4565     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_IMS);
4566     SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_NOT_MODIFIED);
4567   } else {
4568     SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVED);
4569   }
4570 
4571   ink_assert(client_response_code != HTTP_STATUS_NONE);
4572 
4573   // The correct cache action, next action, and response code are set.
4574   // Do the real work below.
4575 
4576   // first update the cached object
4577   if ((s->cache_info.action == CACHE_DO_UPDATE) || (s->cache_info.action == CACHE_DO_SERVE_AND_UPDATE)) {
4578     TxnDebug("http_trans", "[hcoofsr] merge and update cached copy");
4579     merge_and_update_headers_for_cache_update(s);
4580     base_response = s->cache_info.object_store.response_get();
4581     // unset Cache-control: "need-revalidate-once" (if it's set)
4582     // This directive is used internally by T.S. to invalidate documents
4583     // so that an invalidated document needs to be revalidated again.
4584     base_response->unset_cooked_cc_need_revalidate_once();
4585     // unset warning revalidation failed header if it set
4586     // (potentially added by negative revalidating)
4587     delete_warning_value(base_response, HTTP_WARNING_CODE_REVALIDATION_FAILED);
4588   }
4589   ink_assert(base_response->valid());
4590 
4591   if ((s->cache_info.action == CACHE_DO_WRITE) || (s->cache_info.action == CACHE_DO_REPLACE)) {
4592     set_headers_for_cache_write(s, &s->cache_info.object_store, &s->hdr_info.server_request, &s->hdr_info.server_response);
4593   }
4594   // 304, 412, and 416 responses are handled here
4595   if ((client_response_code == HTTP_STATUS_NOT_MODIFIED) || (client_response_code == HTTP_STATUS_PRECONDITION_FAILED)) {
4596     // Because we are decoupling User-Agent validation from
4597     //  Traffic Server validation just build a regular 304
4598     //  if the exception of adding prepending the VIA
4599     //  header to show the revalidation path
4600     build_response(s, base_response, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
4601 
4602     // Copy over the response via field (if any) preserving
4603     //  the order of the fields
4604     MIMEField *resp_via = s->hdr_info.server_response.field_find(MIME_FIELD_VIA, MIME_LEN_VIA);
4605     if (resp_via) {
4606       MIMEField *our_via;
4607       our_via = s->hdr_info.client_response.field_find(MIME_FIELD_VIA, MIME_LEN_VIA);
4608       if (our_via == nullptr) {
4609         our_via = s->hdr_info.client_response.field_create(MIME_FIELD_VIA, MIME_LEN_VIA);
4610         s->hdr_info.client_response.field_attach(our_via);
4611       }
4612       // HDR FIX ME - Multiple appends are VERY slow
4613       while (resp_via) {
4614         int clen;
4615         const char *cfield = resp_via->value_get(&clen);
4616         s->hdr_info.client_response.field_value_append(our_via, cfield, clen, true);
4617         resp_via = resp_via->m_next_dup;
4618       }
4619     }
4620     // a warning text is added only in the case of a NOT MODIFIED response
4621     if (warn_text) {
4622       HttpTransactHeaders::insert_warning_header(s->http_config_param, &s->hdr_info.client_response, HTTP_WARNING_CODE_MISC_WARNING,
4623                                                  warn_text, strlen(warn_text));
4624     }
4625 
4626     DUMP_HEADER("http_hdrs", &s->hdr_info.client_response, s->state_machine_id, "Proxy's Response (Client Conditionals)");
4627     return;
4628   }
4629   // all other responses (not 304, 412, 416) are handled here
4630   else {
4631     if (((s->next_action == SM_ACTION_SERVE_FROM_CACHE) || (s->next_action == SM_ACTION_SERVER_READ)) &&
4632         s->state_machine->do_transform_open()) {
4633       set_header_for_transform(s, base_response);
4634     } else {
4635       build_response(s, base_response, &s->hdr_info.client_response, s->client_info.http_version, client_response_code);
4636     }
4637   }
4638 
4639   return;
4640 }
4641 
4642 ///////////////////////////////////////////////////////////////////////////////
4643 // Name       : handle_no_cache_operation_on_forward_server_response
4644 // Description:
4645 //
4646 // Details    :
4647 //
4648 //
4649 //
4650 // Possible Next States From Here:
4651 //
4652 ///////////////////////////////////////////////////////////////////////////////
4653 void
handle_no_cache_operation_on_forward_server_response(State * s)4654 HttpTransact::handle_no_cache_operation_on_forward_server_response(State *s)
4655 {
4656   TxnDebug("http_trans", "[handle_no_cache_operation_on_forward_server_response] (hncoofsr)");
4657   TxnDebug("http_seq", "[handle_no_cache_operation_on_forward_server_response]");
4658 
4659   bool keep_alive       = s->current.server->keep_alive == HTTP_KEEPALIVE;
4660   const char *warn_text = nullptr;
4661 
4662   switch (s->hdr_info.server_response.status_get()) {
4663   case HTTP_STATUS_OK:
4664     TxnDebug("http_trans", "[hncoofsr] server sent back 200");
4665     SET_VIA_STRING(VIA_SERVER_RESULT, VIA_SERVER_SERVED);
4666     SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_SERVED);
4667     if (s->method == HTTP_WKSIDX_CONNECT) {
4668       TxnDebug("http_trans", "[hncoofsr] next action is SSL_TUNNEL");
4669       s->next_action = SM_ACTION_SSL_TUNNEL;
4670     } else {
4671       TxnDebug("http_trans", "[hncoofsr] next action will be OS_READ_CACHE_NOOP");
4672 
4673       ink_assert(s->cache_info.action == CACHE_DO_NO_ACTION);
4674       s->next_action = SM_ACTION_SERVER_READ;
4675     }
4676     if (s->state_machine->redirect_url == nullptr) {
4677       s->state_machine->enable_redirection = false;
4678     }
4679     break;
4680   case HTTP_STATUS_NOT_MODIFIED:
4681     TxnDebug("http_trans", "[hncoofsr] server sent back 304. IMS from client?");
4682     SET_VIA_STRING(VIA_SERVER_RESULT, VIA_SERVER_NOT_MODIFIED);
4683     SET_VIA_STRING(VIA_PROXY_RESULT, VIA_PROXY_NOT_MODIFIED);
4684 
4685     if (!is_request_conditional(&s->hdr_info.client_request)) {
4686       // bogus server response. not a conditional request
4687       // from the client and probably not a conditional
4688       // request from the proxy.
4689 
4690       // since this is bad, insert warning header into client response
4691       warn_text = "Proxy received unexpected 304 response; content may be stale";
4692     }
4693 
4694     ink_assert(s->cache_info.action == CACHE_DO_NO_ACTION);
4695     s->next_action = SM_ACTION_INTERNAL_CACHE_NOOP;
4696     break;
4697   case HTTP_STATUS_HTTPVER_NOT_SUPPORTED:
4698     s->next_action = how_to_open_connection(s);
4699 
4700     /* Downgrade the request level and retry */
4701     if (!HttpTransactHeaders::downgrade_request(&keep_alive, &s->hdr_info.server_request)) {
4702       s->already_downgraded = true;
4703       build_error_response(s, HTTP_STATUS_HTTPVER_NOT_SUPPORTED, "HTTP Version Not Supported", "response#bad_version");
4704       s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
4705     } else {
4706       s->already_downgraded = true;
4707       s->next_action        = how_to_open_connection(s);
4708     }
4709     return;
4710   case HTTP_STATUS_PARTIAL_CONTENT:
4711     // If we get this back we should be just passing it through.
4712     ink_assert(s->cache_info.action == CACHE_DO_NO_ACTION);
4713     s->next_action = SM_ACTION_SERVER_READ;
4714     break;
4715   default:
4716     TxnDebug("http_trans", "[hncoofsr] server sent back something other than 100,304,200");
4717     /* Default behavior is to pass-through response to the client */
4718 
4719     ink_assert(s->cache_info.action == CACHE_DO_NO_ACTION);
4720     s->next_action = SM_ACTION_SERVER_READ;
4721     break;
4722   }
4723 
4724   HTTPHdr *to_warn;
4725   if (s->next_action == SM_ACTION_SERVER_READ && s->state_machine->do_transform_open()) {
4726     set_header_for_transform(s, &s->hdr_info.server_response);
4727     to_warn = &s->hdr_info.transform_response;
4728   } else {
4729     build_response(s, &s->hdr_info.server_response, &s->hdr_info.client_response, s->client_info.http_version);
4730     to_warn = &s->hdr_info.server_response;
4731   }
4732 
4733   if (warn_text) {
4734     HttpTransactHeaders::insert_warning_header(s->http_config_param, to_warn, HTTP_WARNING_CODE_MISC_WARNING, warn_text,
4735                                                strlen(warn_text));
4736   }
4737 
4738   return;
4739 }
4740 
4741 void
merge_and_update_headers_for_cache_update(State * s)4742 HttpTransact::merge_and_update_headers_for_cache_update(State *s)
4743 {
4744   URL *s_url          = nullptr;
4745   HTTPHdr *cached_hdr = nullptr;
4746 
4747   if (!s->cache_info.object_store.valid()) {
4748     s->cache_info.object_store.create();
4749   }
4750 
4751   s->cache_info.object_store.request_set(&s->hdr_info.server_request);
4752   cached_hdr = s->cache_info.object_store.response_get();
4753 
4754   if (s->redirect_info.redirect_in_process) {
4755     s_url = &s->redirect_info.original_url;
4756   } else {
4757     s_url = &s->cache_info.original_url;
4758   }
4759   ink_assert(s_url != nullptr);
4760 
4761   s->cache_info.object_store.request_get()->url_set(s_url->valid() ? s_url : s->hdr_info.client_request.url_get());
4762 
4763   if (s->cache_info.object_store.request_get()->method_get_wksidx() == HTTP_WKSIDX_HEAD) {
4764     s->cache_info.object_store.request_get()->method_set(HTTP_METHOD_GET, HTTP_LEN_GET);
4765   }
4766 
4767   if (s->api_modifiable_cached_resp) {
4768     ink_assert(cached_hdr != nullptr && cached_hdr->valid());
4769     s->api_modifiable_cached_resp = false;
4770   } else {
4771     s->cache_info.object_store.response_set(s->cache_info.object_read->response_get());
4772   }
4773 
4774   // Delete caching headers from the cached response. If these are
4775   // still being served by the origin we will copy new versions in
4776   // from the server response. RFC 2616 says that a 304 response may
4777   // omit some headers if they were sent in a 200 response (see section
4778   // 10.3.5), but RFC 7232) is clear that the 304 and 200 responses
4779   // must be identical (see section 4.1). This code attempts to strike
4780   // a balance between the two.
4781   cached_hdr->field_delete(MIME_FIELD_AGE, MIME_LEN_AGE);
4782   cached_hdr->field_delete(MIME_FIELD_ETAG, MIME_LEN_ETAG);
4783   cached_hdr->field_delete(MIME_FIELD_EXPIRES, MIME_LEN_EXPIRES);
4784 
4785   merge_response_header_with_cached_header(cached_hdr, &s->hdr_info.server_response);
4786 
4787   // Some special processing for 304
4788   if (s->hdr_info.server_response.status_get() == HTTP_STATUS_NOT_MODIFIED) {
4789     // Hack fix. If the server sends back
4790     // a 304 without a Date Header, use the current time
4791     // as the new Date value in the header to be cached.
4792     time_t date_value = s->hdr_info.server_response.get_date();
4793 
4794     if (date_value <= 0) {
4795       cached_hdr->set_date(s->request_sent_time);
4796       date_value = s->request_sent_time;
4797     }
4798 
4799     // If the cached response has an Age: we should update it
4800     // We could use calculate_document_age but my guess is it's overkill
4801     // Just use 'now' - 304's Date: + Age: (response's Age: if there)
4802     date_value = std::max(s->current.now - date_value, static_cast<ink_time_t>(0));
4803     if (s->hdr_info.server_response.presence(MIME_PRESENCE_AGE)) {
4804       time_t new_age = s->hdr_info.server_response.get_age();
4805 
4806       if (new_age >= 0) {
4807         cached_hdr->set_age(date_value + new_age);
4808       } else {
4809         cached_hdr->set_age(-1); // Overflow
4810       }
4811     }
4812 
4813     delete_warning_value(cached_hdr, HTTP_WARNING_CODE_REVALIDATION_FAILED);
4814   }
4815 
4816   s->cache_info.object_store.request_get()->field_delete(MIME_FIELD_VIA, MIME_LEN_VIA);
4817 }
4818 
4819 void
handle_transform_cache_write(State * s)4820 HttpTransact::handle_transform_cache_write(State *s)
4821 {
4822   ink_assert(s->cache_info.transform_action == CACHE_PREPARE_TO_WRITE);
4823 
4824   switch (s->cache_info.write_lock_state) {
4825   case CACHE_WL_SUCCESS:
4826     // We were able to get the lock for the URL vector in the cache
4827     s->cache_info.transform_action = CACHE_DO_WRITE;
4828     break;
4829   case CACHE_WL_FAIL:
4830     // No write lock, ignore the cache
4831     s->cache_info.transform_action       = CACHE_DO_NO_ACTION;
4832     s->cache_info.transform_write_status = CACHE_WRITE_LOCK_MISS;
4833     break;
4834   default:
4835     ink_release_assert(0);
4836   }
4837 
4838   TRANSACT_RETURN(SM_ACTION_TRANSFORM_READ, nullptr);
4839 }
4840 
4841 void
handle_transform_ready(State * s)4842 HttpTransact::handle_transform_ready(State *s)
4843 {
4844   ink_assert(s->hdr_info.transform_response.valid() == true);
4845 
4846   s->pre_transform_source = s->source;
4847   s->source               = SOURCE_TRANSFORM;
4848 
4849   DUMP_HEADER("http_hdrs", &s->hdr_info.transform_response, s->state_machine_id, "Header From Transform");
4850 
4851   build_response(s, &s->hdr_info.transform_response, &s->hdr_info.client_response, s->client_info.http_version);
4852 
4853   if (s->cache_info.action != CACHE_DO_NO_ACTION && s->cache_info.action != CACHE_DO_DELETE && s->api_info.cache_transformed &&
4854       !s->range_setup) {
4855     HTTPHdr *transform_store_request = nullptr;
4856     switch (s->pre_transform_source) {
4857     case SOURCE_CACHE:
4858       // If we are transforming from the cache, treat
4859       //  the transform as if it were virtual server
4860       //  use in the incoming request
4861       transform_store_request = &s->hdr_info.client_request;
4862       break;
4863     case SOURCE_HTTP_ORIGIN_SERVER:
4864       transform_store_request = &s->hdr_info.server_request;
4865       break;
4866     default:
4867       ink_release_assert(0);
4868     }
4869     ink_assert(transform_store_request->valid() == true);
4870     set_headers_for_cache_write(s, &s->cache_info.transform_store, transform_store_request, &s->hdr_info.transform_response);
4871 
4872     // For debugging
4873     if (is_action_tag_set("http_nullt")) {
4874       s->cache_info.transform_store.request_get()->value_set("InkXform", 8, "nullt", 5);
4875       s->cache_info.transform_store.response_get()->value_set("InkXform", 8, "nullt", 5);
4876     }
4877 
4878     s->cache_info.transform_action = CACHE_PREPARE_TO_WRITE;
4879     TRANSACT_RETURN(SM_ACTION_CACHE_ISSUE_WRITE_TRANSFORM, handle_transform_cache_write);
4880   } else {
4881     s->cache_info.transform_action = CACHE_DO_NO_ACTION;
4882     TRANSACT_RETURN(SM_ACTION_TRANSFORM_READ, nullptr);
4883   }
4884 }
4885 
4886 void
set_header_for_transform(State * s,HTTPHdr * base_header)4887 HttpTransact::set_header_for_transform(State *s, HTTPHdr *base_header)
4888 {
4889   s->hdr_info.transform_response.create(HTTP_TYPE_RESPONSE);
4890   s->hdr_info.transform_response.copy(base_header);
4891 
4892   // Nuke the content length since 1) the transform will probably
4893   //   change it.  2) it would only be valid for the first transform
4894   //   in the chain
4895   s->hdr_info.transform_response.field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
4896 
4897   DUMP_HEADER("http_hdrs", &s->hdr_info.transform_response, s->state_machine_id, "Header To Transform");
4898 }
4899 
4900 void
set_headers_for_cache_write(State * s,HTTPInfo * cache_info,HTTPHdr * request,HTTPHdr * response)4901 HttpTransact::set_headers_for_cache_write(State *s, HTTPInfo *cache_info, HTTPHdr *request, HTTPHdr *response)
4902 {
4903   URL *temp_url;
4904   ink_assert(request->type_get() == HTTP_TYPE_REQUEST);
4905   ink_assert(response->type_get() == HTTP_TYPE_RESPONSE);
4906 
4907   if (!cache_info->valid()) {
4908     cache_info->create();
4909   }
4910 
4911   /* Store the requested URI */
4912   //  Nasty hack. The set calls for
4913   //  marshalled types current do handle something being
4914   //  set to itself.  Make the check here for that case.
4915   //  Why the request url is set before a copy made is
4916   //  quite beyond me.  Seems like a unsafe practice so
4917   //  FIX ME!
4918 
4919   // Logic added to restore the original URL for multiple cache lookup
4920   // and automatic redirection
4921   if (s->redirect_info.redirect_in_process) {
4922     temp_url = &s->redirect_info.original_url;
4923     ink_assert(temp_url->valid());
4924     request->url_set(temp_url);
4925   } else if ((temp_url = &(s->cache_info.original_url))->valid()) {
4926     request->url_set(temp_url);
4927   } else if (request != &s->hdr_info.client_request) {
4928     request->url_set(s->hdr_info.client_request.url_get());
4929   }
4930   cache_info->request_set(request);
4931   /* Why do we check the negative caching case? No one knows. This used to assert if the cache_info
4932      response wasn't already valid, which broke negative caching when a transform is active. Why it
4933      wasn't OK to pull in the @a response explicitly passed in isn't clear and looking at the call
4934      sites yields no insight. So the assert is removed and we keep the behavior that if the response
4935      in @a cache_info is already set, we don't override it.
4936   */
4937   if (!s->is_cacheable_due_to_negative_caching_configuration || !cache_info->response_get()->valid()) {
4938     cache_info->response_set(response);
4939   }
4940 
4941   if (s->api_server_request_body_set) {
4942     cache_info->request_get()->method_set(HTTP_METHOD_GET, HTTP_LEN_GET);
4943   }
4944 
4945   // Set-Cookie should not be put in the cache to prevent
4946   //  sending person A's cookie to person B
4947   cache_info->response_get()->field_delete(MIME_FIELD_SET_COOKIE, MIME_LEN_SET_COOKIE);
4948   cache_info->request_get()->field_delete(MIME_FIELD_VIA, MIME_LEN_VIA);
4949   // server 200 Ok for Range request
4950   cache_info->request_get()->field_delete(MIME_FIELD_RANGE, MIME_LEN_RANGE);
4951 
4952   // If we're ignoring auth, then we don't want to cache WWW-Auth headers
4953   if (s->txn_conf->cache_ignore_auth) {
4954     cache_info->response_get()->field_delete(MIME_FIELD_WWW_AUTHENTICATE, MIME_LEN_WWW_AUTHENTICATE);
4955   }
4956 
4957   DUMP_HEADER("http_hdrs", cache_info->request_get(), s->state_machine_id, "Cached Request Hdr");
4958 }
4959 
4960 void
merge_response_header_with_cached_header(HTTPHdr * cached_header,HTTPHdr * response_header)4961 HttpTransact::merge_response_header_with_cached_header(HTTPHdr *cached_header, HTTPHdr *response_header)
4962 {
4963   MIMEField *field;
4964   MIMEField *new_field;
4965   MIMEFieldIter fiter;
4966   const char *name;
4967   bool dups_seen = false;
4968 
4969   field = response_header->iter_get_first(&fiter);
4970 
4971   for (; field != nullptr; field = response_header->iter_get_next(&fiter)) {
4972     int name_len;
4973     name = field->name_get(&name_len);
4974 
4975     ///////////////////////////
4976     // is hop-by-hop header? //
4977     ///////////////////////////
4978     if (HttpTransactHeaders::is_this_a_hop_by_hop_header(name)) {
4979       continue;
4980     }
4981     /////////////////////////////////////
4982     // dont cache content-length field  and transfer encoding //
4983     /////////////////////////////////////
4984     if (name == MIME_FIELD_CONTENT_LENGTH || name == MIME_FIELD_TRANSFER_ENCODING) {
4985       continue;
4986     }
4987     /////////////////////////////////////
4988     // dont cache Set-Cookie headers   //
4989     /////////////////////////////////////
4990     if (name == MIME_FIELD_SET_COOKIE) {
4991       continue;
4992     }
4993     /////////////////////////////////////////
4994     // dont overwrite the cached content   //
4995     //   type as this wreaks havoc with    //
4996     //   transformed content               //
4997     /////////////////////////////////////////
4998     if (name == MIME_FIELD_CONTENT_TYPE) {
4999       continue;
5000     }
5001     /////////////////////////////////////
5002     // dont delete warning.  a separate//
5003     //  functions merges the two in a  //
5004     //  complex manner                 //
5005     /////////////////////////////////////
5006     if (name == MIME_FIELD_WARNING) {
5007       continue;
5008     }
5009     // Copy all remaining headers with replacement
5010 
5011     // Duplicate header fields cause a bug problem
5012     //   since we need to duplicate with replacement.
5013     //   Without dups, we can just nuke what is already
5014     //   there in the cached header.  With dups, we
5015     //   can't do this because what is already there
5016     //   may be a dup we've already copied in.  If
5017     //   dups show up we look through the remaining
5018     //   header fields in the new response, nuke
5019     //   them in the cached response and then add in
5020     //   the remaining fields one by one from the
5021     //   response header
5022     //
5023     if (field->m_next_dup) {
5024       if (dups_seen == false) {
5025         MIMEField *dfield;
5026         // use a second iterator to delete the
5027         // remaining response headers in the cached response,
5028         // so that they will be added in the next iterations.
5029         MIMEFieldIter fiter2 = fiter;
5030         const char *dname    = name;
5031         int dlen             = name_len;
5032 
5033         while (dname) {
5034           cached_header->field_delete(dname, dlen);
5035           dfield = response_header->iter_get_next(&fiter2);
5036           if (dfield) {
5037             dname = dfield->name_get(&dlen);
5038           } else {
5039             dname = nullptr;
5040           }
5041         }
5042         dups_seen = true;
5043       }
5044     }
5045 
5046     int value_len;
5047     const char *value = field->value_get(&value_len);
5048 
5049     if (dups_seen == false) {
5050       cached_header->value_set(name, name_len, value, value_len);
5051     } else {
5052       new_field = cached_header->field_create(name, name_len);
5053       cached_header->field_attach(new_field);
5054       cached_header->field_value_set(new_field, value, value_len);
5055     }
5056   }
5057 
5058   merge_warning_header(cached_header, response_header);
5059 
5060   Debug("http_hdr_space", "Merged response header with %d dead bytes", cached_header->m_heap->m_lost_string_space);
5061 }
5062 
5063 void
merge_warning_header(HTTPHdr * cached_header,HTTPHdr * response_header)5064 HttpTransact::merge_warning_header(HTTPHdr *cached_header, HTTPHdr *response_header)
5065 {
5066   //  The plan:
5067   //
5068   //    1) The cached header has it's warning codes untouched
5069   //         since merge_response_header_with_cached_header()
5070   //         doesn't deal with warning headers.
5071   //    2) If there are 1xx warning codes in the cached
5072   //         header, they need to be removed.  Removal
5073   //         is difficult since the hdrs don't comma
5074   //         separate values, so build up a new header
5075   //         piecemeal.  Very slow but shouldn't happen
5076   //         very often
5077   //    3) Since we keep the all the warning codes from
5078   //         the response header, append if to
5079   //         the cached header
5080   //
5081   MIMEField *c_warn    = cached_header->field_find(MIME_FIELD_WARNING, MIME_LEN_WARNING);
5082   MIMEField *r_warn    = response_header->field_find(MIME_FIELD_WARNING, MIME_LEN_WARNING);
5083   MIMEField *new_cwarn = nullptr;
5084   int move_warn_len;
5085   const char *move_warn;
5086 
5087   // Loop over the cached warning header and transfer all non 1xx
5088   //   warning values to a new header
5089   if (c_warn) {
5090     HdrCsvIter csv;
5091 
5092     move_warn = csv.get_first(c_warn, &move_warn_len);
5093     while (move_warn) {
5094       int code = ink_atoi(move_warn, move_warn_len);
5095       if (code < 100 || code > 199) {
5096         bool first_move;
5097         if (!new_cwarn) {
5098           new_cwarn  = cached_header->field_create();
5099           first_move = true;
5100         } else {
5101           first_move = false;
5102         }
5103         cached_header->field_value_append(new_cwarn, move_warn, move_warn_len, !first_move);
5104       }
5105 
5106       move_warn = csv.get_next(&move_warn_len);
5107     }
5108 
5109     // At this point we can nuke the old warning headers
5110     cached_header->field_delete(MIME_FIELD_WARNING, MIME_LEN_WARNING);
5111 
5112     // Add in the new header if it has anything in it
5113     if (new_cwarn) {
5114       new_cwarn->name_set(cached_header->m_heap, cached_header->m_mime, MIME_FIELD_WARNING, MIME_LEN_WARNING);
5115       cached_header->field_attach(new_cwarn);
5116     }
5117   }
5118   // Loop over all the dups in the response warning header and append
5119   //  them one by one on to the cached warning header
5120   while (r_warn) {
5121     move_warn = r_warn->value_get(&move_warn_len);
5122 
5123     if (new_cwarn) {
5124       cached_header->field_value_append(new_cwarn, move_warn, move_warn_len, true);
5125     } else {
5126       new_cwarn = cached_header->field_create(MIME_FIELD_WARNING, MIME_LEN_WARNING);
5127       cached_header->field_attach(new_cwarn);
5128       cached_header->field_value_set(new_cwarn, move_warn, move_warn_len);
5129     }
5130 
5131     r_warn = r_warn->m_next_dup;
5132   }
5133 }
5134 
5135 ////////////////////////////////////////////////////////
5136 // Set the keep-alive and version flags for later use //
5137 // in request construction                            //
5138 // this is also used when opening a connection to     //
5139 // the origin server, and search_keepalive_to().      //
5140 ////////////////////////////////////////////////////////
5141 bool
get_ka_info_from_config(State * s,ConnectionAttributes * server_info)5142 HttpTransact::get_ka_info_from_config(State *s, ConnectionAttributes *server_info)
5143 {
5144   bool check_hostdb = false;
5145 
5146   if (server_info->http_version > HTTP_0_9) {
5147     TxnDebug("http_trans", "get_ka_info_from_config, version already set server_info->http_version %d.%d",
5148              server_info->http_version.get_major(), server_info->http_version.get_minor());
5149     return false;
5150   }
5151   switch (s->txn_conf->send_http11_requests) {
5152   case HttpConfigParams::SEND_HTTP11_NEVER:
5153     server_info->http_version = HTTP_1_0;
5154     break;
5155   case HttpConfigParams::SEND_HTTP11_UPGRADE_HOSTDB:
5156     server_info->http_version = HTTP_1_0;
5157     check_hostdb              = true;
5158     break;
5159   case HttpConfigParams::SEND_HTTP11_IF_REQUEST_11_AND_HOSTDB:
5160     server_info->http_version = HTTP_1_0;
5161     if (s->hdr_info.client_request.version_get() == HTTP_1_1) {
5162       // check hostdb only if client req is http/1.1
5163       check_hostdb = true;
5164     }
5165     break;
5166   default:
5167     // The default is the "1" config, SEND_HTTP11_ALWAYS, but assert in debug builds since we shouldn't be here
5168     ink_assert(0);
5169   // fallthrough
5170   case HttpConfigParams::SEND_HTTP11_ALWAYS:
5171     server_info->http_version = HTTP_1_1;
5172     break;
5173   }
5174   TxnDebug("http_trans", "get_ka_info_from_config, server_info->http_version %d.%d, check_hostdb %d",
5175            server_info->http_version.get_major(), server_info->http_version.get_minor(), check_hostdb);
5176 
5177   // Set keep_alive info based on the records.config setting
5178   server_info->keep_alive = s->txn_conf->keep_alive_enabled_out ? HTTP_KEEPALIVE : HTTP_NO_KEEPALIVE;
5179 
5180   return check_hostdb;
5181 }
5182 
5183 ////////////////////////////////////////////////////////
5184 // Set the keep-alive and version flags for later use //
5185 // in request construction                            //
5186 // this is also used when opening a connection to     //
5187 // the origin server, and search_keepalive_to().      //
5188 ////////////////////////////////////////////////////////
5189 void
get_ka_info_from_host_db(State * s,ConnectionAttributes * server_info,ConnectionAttributes *,HostDBInfo * host_db_info)5190 HttpTransact::get_ka_info_from_host_db(State *s, ConnectionAttributes *server_info,
5191                                        ConnectionAttributes * /* client_info ATS_UNUSED */, HostDBInfo *host_db_info)
5192 {
5193   bool force_http11     = false;
5194   bool http11_if_hostdb = false;
5195 
5196   switch (s->txn_conf->send_http11_requests) {
5197   case HttpConfigParams::SEND_HTTP11_NEVER:
5198     // No need to do anything since above vars
5199     //   are defaulted false
5200     break;
5201   case HttpConfigParams::SEND_HTTP11_UPGRADE_HOSTDB:
5202     http11_if_hostdb = true;
5203     break;
5204   case HttpConfigParams::SEND_HTTP11_IF_REQUEST_11_AND_HOSTDB:
5205     if (s->hdr_info.client_request.version_get() == HTTP_1_1) {
5206       http11_if_hostdb = true;
5207     }
5208     break;
5209   default:
5210     // The default is the "1" config, SEND_HTTP11_ALWAYS, but assert in debug builds since we shouldn't be here
5211     ink_assert(0);
5212   // fallthrough
5213   case HttpConfigParams::SEND_HTTP11_ALWAYS:
5214     force_http11 = true;
5215     break;
5216   }
5217 
5218   if (force_http11 == true || (http11_if_hostdb == true && host_db_info->app.http_data.http_version == HTTP_1_1)) {
5219     server_info->http_version = HTTP_1_1;
5220     server_info->keep_alive   = HTTP_KEEPALIVE;
5221   } else if (host_db_info->app.http_data.http_version == HTTP_1_0) {
5222     server_info->http_version = HTTP_1_0;
5223     server_info->keep_alive   = HTTP_KEEPALIVE;
5224   } else if (host_db_info->app.http_data.http_version == HTTP_0_9) {
5225     server_info->http_version = HTTP_0_9;
5226     server_info->keep_alive   = HTTP_NO_KEEPALIVE;
5227   } else {
5228     //////////////////////////////////////////////
5229     // not set yet for this host. set defaults. //
5230     //////////////////////////////////////////////
5231     server_info->http_version                = HTTP_1_0;
5232     server_info->keep_alive                  = HTTP_KEEPALIVE;
5233     host_db_info->app.http_data.http_version = HTTP_1_0;
5234   }
5235 
5236   /////////////////////////////
5237   // origin server keep_alive //
5238   /////////////////////////////
5239   if (!s->txn_conf->keep_alive_enabled_out) {
5240     server_info->keep_alive = HTTP_NO_KEEPALIVE;
5241   }
5242 
5243   return;
5244 }
5245 
5246 void
add_client_ip_to_outgoing_request(State * s,HTTPHdr * request)5247 HttpTransact::add_client_ip_to_outgoing_request(State *s, HTTPHdr *request)
5248 {
5249   char ip_string[INET6_ADDRSTRLEN + 1] = {'\0'};
5250   size_t ip_string_size                = 0;
5251 
5252   if (!ats_is_ip(&s->client_info.src_addr.sa)) {
5253     return;
5254   }
5255 
5256   // Always prepare the IP string.
5257   if (ats_ip_ntop(&s->client_info.src_addr.sa, ip_string, sizeof(ip_string)) != nullptr) {
5258     ip_string_size += strlen(ip_string);
5259   } else {
5260     // Failure, omg
5261     ip_string_size = 0;
5262     ip_string[0]   = 0;
5263   }
5264 
5265   // Check to see if the ip_string has been set
5266   if (ip_string_size == 0) {
5267     return;
5268   }
5269 
5270   // if we want client-ip headers, and there isn't one, add one
5271   if (!s->txn_conf->anonymize_remove_client_ip) {
5272     switch (s->txn_conf->anonymize_insert_client_ip) {
5273     case 1: { // Insert the client-ip, but only if the UA did not send one
5274       bool client_ip_set = request->presence(MIME_PRESENCE_CLIENT_IP);
5275       TxnDebug("http_trans", "client_ip_set = %d", client_ip_set);
5276 
5277       if (client_ip_set == true) {
5278         break;
5279       }
5280     }
5281 
5282     // FALL-THROUGH
5283     case 2: // Always insert the client-ip
5284       request->value_set(MIME_FIELD_CLIENT_IP, MIME_LEN_CLIENT_IP, ip_string, ip_string_size);
5285       TxnDebug("http_trans", "inserted request header 'Client-ip: %s'", ip_string);
5286       break;
5287 
5288     default: // don't insert client-ip
5289       break;
5290     }
5291   }
5292 
5293   // Add or append to the X-Forwarded-For header
5294   if (s->txn_conf->insert_squid_x_forwarded_for) {
5295     request->value_append_or_set(MIME_FIELD_X_FORWARDED_FOR, MIME_LEN_X_FORWARDED_FOR, ip_string, ip_string_size);
5296     TxnDebug("http_trans",
5297              "[add_client_ip_to_outgoing_request] Appended connecting client's "
5298              "(%s) to the X-Forwards header",
5299              ip_string);
5300   }
5301 }
5302 
5303 ///////////////////////////////////////////////////////////////////////////////
5304 // Name       : check_request_validity()
5305 // Description: checks to see if incoming request has necessary fields
5306 //
5307 // Input      : State, header (can we do this without the state?)
5308 // Output     : enum RequestError_t of the error type, if any
5309 //
5310 // Details    :
5311 //
5312 //
5313 ///////////////////////////////////////////////////////////////////////////////
5314 HttpTransact::RequestError_t
check_request_validity(State * s,HTTPHdr * incoming_hdr)5315 HttpTransact::check_request_validity(State *s, HTTPHdr *incoming_hdr)
5316 {
5317   // Called also on receiving request.  Not sure if we need to call this again in case
5318   // the transfer-encoding and content-length headers changed
5319   set_client_request_state(s, incoming_hdr);
5320   if (incoming_hdr == nullptr) {
5321     return NON_EXISTANT_REQUEST_HEADER;
5322   }
5323 
5324   if (!(HttpTransactHeaders::is_request_proxy_authorized(incoming_hdr))) {
5325     return FAILED_PROXY_AUTHORIZATION;
5326   }
5327 
5328   URL *incoming_url = incoming_hdr->url_get();
5329   int hostname_len;
5330   const char *hostname = incoming_hdr->host_get(&hostname_len);
5331 
5332   if (hostname == nullptr) {
5333     return MISSING_HOST_FIELD;
5334   }
5335 
5336   if (hostname_len >= MAXDNAME || hostname_len <= 0 || memchr(hostname, '\0', hostname_len)) {
5337     return BAD_HTTP_HEADER_SYNTAX;
5338   }
5339 
5340   int scheme = incoming_url->scheme_get_wksidx();
5341   int method = incoming_hdr->method_get_wksidx();
5342 
5343   if (!((scheme == URL_WKSIDX_HTTP) && (method == HTTP_WKSIDX_GET))) {
5344     if (scheme != URL_WKSIDX_HTTP && scheme != URL_WKSIDX_HTTPS && method != HTTP_WKSIDX_CONNECT &&
5345         !((scheme == URL_WKSIDX_WS || scheme == URL_WKSIDX_WSS) && s->is_websocket)) {
5346       if (scheme < 0) {
5347         return NO_REQUEST_SCHEME;
5348       } else {
5349         return SCHEME_NOT_SUPPORTED;
5350       }
5351     }
5352 
5353     if (!HttpTransactHeaders::is_this_method_supported(scheme, method)) {
5354       return METHOD_NOT_SUPPORTED;
5355     }
5356     if ((method == HTTP_WKSIDX_CONNECT) && !s->transparent_passthrough &&
5357         (!is_port_in_range(incoming_hdr->url_get()->port_get(), s->http_config_param->connect_ports))) {
5358       return BAD_CONNECT_PORT;
5359     }
5360 
5361     if (s->client_info.transfer_encoding == CHUNKED_ENCODING && incoming_hdr->version_get() < HTTP_1_1) {
5362       // Per spec, Transfer-Encoding is only supported in HTTP/1.1. For earlier
5363       // versions, we must reject Transfer-Encoding rather than interpret it
5364       // since downstream proxies may ignore the chunk header and rely upon the
5365       // Content-Length, or interpret the body some other way. These
5366       // differences in interpretation may open up the door to compatibility
5367       // issues. To protect against this, we reply with a 4xx if the client
5368       // uses Transfer-Encoding with HTTP versions that do not support it.
5369       return UNACCEPTABLE_TE_REQUIRED;
5370     }
5371 
5372     // Require Content-Length/Transfer-Encoding for POST/PUSH/PUT
5373     if ((scheme == URL_WKSIDX_HTTP || scheme == URL_WKSIDX_HTTPS) &&
5374         (method == HTTP_WKSIDX_POST || method == HTTP_WKSIDX_PUSH || method == HTTP_WKSIDX_PUT) &&
5375         s->client_info.transfer_encoding != CHUNKED_ENCODING) {
5376       // In normal operation there will always be a ua_txn at this point, but in one of the -R1  regression tests a request is
5377       // createdindependent of a transaction and this method is called, so we must null check
5378       if (!s->state_machine->ua_txn || s->state_machine->ua_txn->is_chunked_encoding_supported()) {
5379         // See if we need to insert a chunked header
5380         if (!incoming_hdr->presence(MIME_PRESENCE_CONTENT_LENGTH)) {
5381           if (s->txn_conf->post_check_content_length_enabled) {
5382             return NO_POST_CONTENT_LENGTH;
5383           } else {
5384             // Stuff in a TE setting so we treat this as chunked, sort of.
5385             s->client_info.transfer_encoding = HttpTransact::CHUNKED_ENCODING;
5386             incoming_hdr->value_append(MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING, HTTP_VALUE_CHUNKED,
5387                                        HTTP_LEN_CHUNKED, true);
5388           }
5389         }
5390         if (HTTP_UNDEFINED_CL == s->hdr_info.request_content_length) {
5391           return INVALID_POST_CONTENT_LENGTH;
5392         }
5393       }
5394     }
5395   }
5396   // Check whether a Host header field is missing in the request.
5397   if (!incoming_hdr->presence(MIME_PRESENCE_HOST) && incoming_hdr->version_get() != HTTP_0_9) {
5398     // Update the number of incoming 1.0 or 1.1 requests that do
5399     // not contain Host header fields.
5400     HTTP_INCREMENT_DYN_STAT(http_missing_host_hdr_stat);
5401   }
5402   // Did the client send a "TE: identity;q=0"? We have to respond
5403   // with an error message because we only support identity
5404   // Transfer Encoding.
5405 
5406   if (incoming_hdr->presence(MIME_PRESENCE_TE)) {
5407     MIMEField *te_field = incoming_hdr->field_find(MIME_FIELD_TE, MIME_LEN_TE);
5408     HTTPValTE *te_val;
5409 
5410     if (te_field) {
5411       HdrCsvIter csv_iter;
5412       int te_raw_len;
5413       const char *te_raw = csv_iter.get_first(te_field, &te_raw_len);
5414 
5415       while (te_raw) {
5416         te_val = http_parse_te(te_raw, te_raw_len, &s->arena);
5417         if (te_val->encoding == HTTP_VALUE_IDENTITY) {
5418           if (te_val->qvalue <= 0.0) {
5419             s->arena.free(te_val, sizeof(HTTPValTE));
5420             return UNACCEPTABLE_TE_REQUIRED;
5421           }
5422         }
5423         s->arena.free(te_val, sizeof(HTTPValTE));
5424         te_raw = csv_iter.get_next(&te_raw_len);
5425       }
5426     }
5427   }
5428 
5429   return NO_REQUEST_HEADER_ERROR;
5430 }
5431 
5432 void
set_client_request_state(State * s,HTTPHdr * incoming_hdr)5433 HttpTransact::set_client_request_state(State *s, HTTPHdr *incoming_hdr)
5434 {
5435   if (incoming_hdr == nullptr) {
5436     return;
5437   }
5438 
5439   // Set transfer_encoding value
5440   if (incoming_hdr->presence(MIME_PRESENCE_TRANSFER_ENCODING)) {
5441     MIMEField *field = incoming_hdr->field_find(MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING);
5442     if (field) {
5443       HdrCsvIter enc_val_iter;
5444       int enc_val_len;
5445       const char *enc_value = enc_val_iter.get_first(field, &enc_val_len);
5446 
5447       while (enc_value) {
5448         const char *wks_value = hdrtoken_string_to_wks(enc_value, enc_val_len);
5449         if (wks_value == HTTP_VALUE_CHUNKED) {
5450           s->client_info.transfer_encoding = CHUNKED_ENCODING;
5451           break;
5452         }
5453         enc_value = enc_val_iter.get_next(&enc_val_len);
5454       }
5455     }
5456   }
5457 
5458   /////////////////////////////////////////////////////
5459   // get request content length                      //
5460   // To avoid parsing content-length twice, we set   //
5461   // s->hdr_info.request_content_length here rather  //
5462   // than in initialize_state_variables_from_request //
5463   /////////////////////////////////////////////////////
5464   if (incoming_hdr->presence(MIME_PRESENCE_CONTENT_LENGTH)) {
5465     s->hdr_info.request_content_length = incoming_hdr->get_content_length();
5466   } else {
5467     s->hdr_info.request_content_length = HTTP_UNDEFINED_CL; // content length less than zero is invalid
5468   }
5469 
5470   TxnDebug("http_trans", "[set_client_request_state] set req cont length to %" PRId64, s->hdr_info.request_content_length);
5471 }
5472 
5473 HttpTransact::ResponseError_t
check_response_validity(State * s,HTTPHdr * incoming_hdr)5474 HttpTransact::check_response_validity(State *s, HTTPHdr *incoming_hdr)
5475 {
5476   ink_assert(s->next_hop_scheme == URL_WKSIDX_HTTP || s->next_hop_scheme == URL_WKSIDX_HTTPS ||
5477              s->next_hop_scheme == URL_WKSIDX_TUNNEL);
5478 
5479   if (incoming_hdr == nullptr) {
5480     return NON_EXISTANT_RESPONSE_HEADER;
5481   }
5482 
5483   if (incoming_hdr->type_get() != HTTP_TYPE_RESPONSE) {
5484     return NOT_A_RESPONSE_HEADER;
5485   }
5486 
5487   HTTPStatus incoming_status = incoming_hdr->status_get();
5488   if (!incoming_status) {
5489     return MISSING_STATUS_CODE;
5490   }
5491 
5492   if (incoming_status == HTTP_STATUS_INTERNAL_SERVER_ERROR) {
5493     return STATUS_CODE_SERVER_ERROR;
5494   }
5495 
5496   if (!incoming_hdr->presence(MIME_PRESENCE_DATE)) {
5497     incoming_hdr->set_date(s->current.now);
5498   }
5499 
5500 #ifdef REALLY_NEED_TO_CHECK_DATE_VALIDITY
5501 
5502   if (incoming_hdr->presence(MIME_PRESENCE_DATE)) {
5503     time_t date_value = incoming_hdr->get_date();
5504     if (date_value <= 0) {
5505       TxnDebug("http_trans", "[check_response_validity] Bogus date in response");
5506       return BOGUS_OR_NO_DATE_IN_RESPONSE;
5507     }
5508   } else {
5509     TxnDebug("http_trans", "[check_response_validity] No date in response");
5510     return BOGUS_OR_NO_DATE_IN_RESPONSE;
5511   }
5512 #endif
5513 
5514   return NO_RESPONSE_HEADER_ERROR;
5515 }
5516 
5517 bool
handle_internal_request(State *,HTTPHdr * incoming_hdr)5518 HttpTransact::handle_internal_request(State * /* s ATS_UNUSED */, HTTPHdr *incoming_hdr)
5519 {
5520   URL *url;
5521 
5522   ink_assert(incoming_hdr->type_get() == HTTP_TYPE_REQUEST);
5523 
5524   if (incoming_hdr->method_get_wksidx() != HTTP_WKSIDX_GET) {
5525     return false;
5526   }
5527 
5528   url = incoming_hdr->url_get();
5529 
5530   int scheme = url->scheme_get_wksidx();
5531   if (scheme != URL_WKSIDX_HTTP && scheme != URL_WKSIDX_HTTPS) {
5532     return false;
5533   }
5534 
5535   if (!statPagesManager.is_stat_page(url)) {
5536     return false;
5537   }
5538 
5539   return true;
5540 }
5541 
5542 bool
handle_trace_and_options_requests(State * s,HTTPHdr * incoming_hdr)5543 HttpTransact::handle_trace_and_options_requests(State *s, HTTPHdr *incoming_hdr)
5544 {
5545   ink_assert(incoming_hdr->type_get() == HTTP_TYPE_REQUEST);
5546 
5547   // This only applies to TRACE and OPTIONS
5548   if ((s->method != HTTP_WKSIDX_TRACE) && (s->method != HTTP_WKSIDX_OPTIONS)) {
5549     return false;
5550   }
5551 
5552   // If there is no Max-Forwards request header, just return false.
5553   if (!incoming_hdr->presence(MIME_PRESENCE_MAX_FORWARDS)) {
5554     // Trace and Options requests should not be looked up in cache.
5555     // s->cache_info.action = CACHE_DO_NO_ACTION;
5556     s->current.mode = TUNNELLING_PROXY;
5557     HTTP_INCREMENT_DYN_STAT(http_tunnels_stat);
5558     return false;
5559   }
5560 
5561   int max_forwards = incoming_hdr->get_max_forwards();
5562   if (max_forwards <= 0) {
5563     //////////////////////////////////////////////
5564     // if max-forward is 0 the request must not //
5565     // be forwarded to the origin server.       //
5566     //////////////////////////////////////////////
5567     TxnDebug("http_trans", "[handle_trace] max-forwards: 0, building response...");
5568     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
5569     build_response(s, &s->hdr_info.client_response, s->client_info.http_version, HTTP_STATUS_OK);
5570 
5571     ////////////////////////////////////////
5572     // if method is trace we should write //
5573     // the request header as the body.    //
5574     ////////////////////////////////////////
5575     if (s->method == HTTP_WKSIDX_TRACE) {
5576       TxnDebug("http_trans", "[handle_trace] inserting request in body.");
5577       int req_length = incoming_hdr->length_get();
5578       HTTP_RELEASE_ASSERT(req_length > 0);
5579 
5580       s->free_internal_msg_buffer();
5581       s->internal_msg_buffer_size = req_length * 2;
5582 
5583       if (s->internal_msg_buffer_size <= BUFFER_SIZE_FOR_INDEX(s->http_config_param->max_msg_iobuf_index)) {
5584         s->internal_msg_buffer_fast_allocator_size =
5585           buffer_size_to_index(s->internal_msg_buffer_size, s->http_config_param->max_msg_iobuf_index);
5586         s->internal_msg_buffer = static_cast<char *>(ioBufAllocator[s->internal_msg_buffer_fast_allocator_size].alloc_void());
5587       } else {
5588         s->internal_msg_buffer_fast_allocator_size = -1;
5589         s->internal_msg_buffer                     = static_cast<char *>(ats_malloc(s->internal_msg_buffer_size));
5590       }
5591 
5592       // clear the stupid buffer
5593       memset(s->internal_msg_buffer, '\0', s->internal_msg_buffer_size);
5594 
5595       int offset = 0;
5596       int used   = 0;
5597       int done;
5598       done = incoming_hdr->print(s->internal_msg_buffer, s->internal_msg_buffer_size, &used, &offset);
5599       HTTP_RELEASE_ASSERT(done);
5600       s->internal_msg_buffer_size = used;
5601       s->internal_msg_buffer_type = ats_strdup("message/http");
5602 
5603       s->hdr_info.client_response.set_content_length(used);
5604     } else {
5605       // For OPTIONS request insert supported methods in ALLOW field
5606       TxnDebug("http_trans", "[handle_options] inserting methods in Allow.");
5607       HttpTransactHeaders::insert_supported_methods_in_response(&s->hdr_info.client_response, s->scheme);
5608     }
5609     return true;
5610   } else { /* max-forwards != 0 */
5611 
5612     // Logically want to make sure max_forwards is a legitimate non-zero non-negative integer
5613     // Since max_forwards is a signed integer, no sense making sure it is less than INT_MAX.
5614     // Would be negative in that case.  Already checked negative in the other case.  Noted by coverity
5615 
5616     --max_forwards;
5617     TxnDebug("http_trans", "[handle_trace_options] Decrementing max_forwards to %d", max_forwards);
5618     incoming_hdr->set_max_forwards(max_forwards);
5619 
5620     // Trace and Options requests should not be looked up in cache.
5621     // s->cache_info.action = CACHE_DO_NO_ACTION;
5622     s->current.mode = TUNNELLING_PROXY;
5623     HTTP_INCREMENT_DYN_STAT(http_tunnels_stat);
5624   }
5625 
5626   return false;
5627 }
5628 
5629 void
bootstrap_state_variables_from_request(State * s,HTTPHdr * incoming_request)5630 HttpTransact::bootstrap_state_variables_from_request(State *s, HTTPHdr *incoming_request)
5631 {
5632   s->current.now = s->client_request_time = ink_local_time();
5633   s->client_info.http_version             = incoming_request->version_get();
5634 }
5635 
5636 void
initialize_state_variables_from_request(State * s,HTTPHdr * obsolete_incoming_request)5637 HttpTransact::initialize_state_variables_from_request(State *s, HTTPHdr *obsolete_incoming_request)
5638 {
5639   HTTPHdr *incoming_request = &s->hdr_info.client_request;
5640 
5641   // Temporary, until we're confident that the second argument is redundant.
5642   ink_assert(incoming_request == obsolete_incoming_request);
5643 
5644   int host_len;
5645   const char *host_name = incoming_request->host_get(&host_len);
5646 
5647   // check if the request is conditional (IMS or INM)
5648   if (incoming_request->presence(MIME_PRESENCE_IF_MODIFIED_SINCE | MIME_PRESENCE_IF_NONE_MATCH)) {
5649     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_IMS);
5650   } else {
5651     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_SIMPLE);
5652   }
5653 
5654   // Is the user agent Keep-Alive?
5655   //  If we are transparent or if the user-agent is following
5656   //  the 1.1 spec, we will see a "Connection" header to
5657   //  indicate a keep-alive.  However most user-agents including
5658   //  MSIE3.0, Netscape4.04 and Netscape3.01 send Proxy-Connection
5659   //  when they are configured to use a proxy.  Proxy-Connection
5660   //  is not in the spec but was added to prevent problems
5661   //  with a dumb proxy forwarding all headers (including "Connection")
5662   //  to the origin server and confusing it.  In cases of transparent
5663   //  deployments we use the Proxy-Connect hdr (to be as transparent
5664   //  as possible).
5665   MIMEField *pc = incoming_request->field_find(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
5666 
5667   // If we need to send a close header later check to see if it should be "Proxy-Connection"
5668   if (pc != nullptr) {
5669     s->client_info.proxy_connect_hdr = true;
5670   }
5671 
5672   NetVConnection *vc = nullptr;
5673   if (s->state_machine->ua_txn) {
5674     vc = s->state_machine->ua_txn->get_netvc();
5675   }
5676 
5677   if (vc) {
5678     s->request_data.incoming_port = vc->get_local_port();
5679     s->request_data.internal_txn  = vc->get_is_internal_request();
5680   }
5681 
5682   // If this is an internal request, never keep alive
5683   if (!s->txn_conf->keep_alive_enabled_in) {
5684     s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
5685   } else if (vc && vc->get_is_internal_request()) {
5686     // Following the trail of JIRAs back from TS-4960, there can be issues with
5687     // EOS event delivery when using keepalive on internal PluginVC session. As
5688     // an interim measure, if proxy.config.http.keepalive_internal_vc is set,
5689     // we will obey the incoming transaction's keepalive request.
5690     s->client_info.keep_alive =
5691       s->http_config_param->keepalive_internal_vc ? incoming_request->keep_alive_get() : HTTP_NO_KEEPALIVE;
5692   } else {
5693     s->client_info.keep_alive = incoming_request->keep_alive_get();
5694   }
5695 
5696   if (!s->server_info.name || s->redirect_info.redirect_in_process) {
5697     s->server_info.name = s->arena.str_store(host_name, host_len);
5698   }
5699 
5700   s->next_hop_scheme = s->scheme = incoming_request->url_get()->scheme_get_wksidx();
5701 
5702   // With websockets we need to make an outgoing request
5703   // as http or https.
5704   // We switch back to HTTP or HTTPS for the next hop
5705   // I think this is required to properly establish outbound WSS connections,
5706   // you'll need to force the next hop to be https.
5707   if (s->is_websocket) {
5708     if (s->next_hop_scheme == URL_WKSIDX_WS) {
5709       TxnDebug("http_trans", "Switching WS next hop scheme to http.");
5710       s->next_hop_scheme = URL_WKSIDX_HTTP;
5711       s->scheme          = URL_WKSIDX_HTTP;
5712       // s->request_data.hdr->url_get()->scheme_set(URL_SCHEME_HTTP, URL_LEN_HTTP);
5713     } else if (s->next_hop_scheme == URL_WKSIDX_WSS) {
5714       TxnDebug("http_trans", "Switching WSS next hop scheme to https.");
5715       s->next_hop_scheme = URL_WKSIDX_HTTPS;
5716       s->scheme          = URL_WKSIDX_HTTPS;
5717       // s->request_data.hdr->url_get()->scheme_set(URL_SCHEME_HTTPS, URL_LEN_HTTPS);
5718     } else {
5719       Error("Scheme doesn't match websocket...!");
5720     }
5721 
5722     s->current.mode      = GENERIC_PROXY;
5723     s->cache_info.action = CACHE_DO_NO_ACTION;
5724   }
5725 
5726   s->method = incoming_request->method_get_wksidx();
5727 
5728   if (s->method == HTTP_WKSIDX_GET) {
5729     HTTP_INCREMENT_DYN_STAT(http_get_requests_stat);
5730   } else if (s->method == HTTP_WKSIDX_HEAD) {
5731     HTTP_INCREMENT_DYN_STAT(http_head_requests_stat);
5732   } else if (s->method == HTTP_WKSIDX_POST) {
5733     HTTP_INCREMENT_DYN_STAT(http_post_requests_stat);
5734   } else if (s->method == HTTP_WKSIDX_PUT) {
5735     HTTP_INCREMENT_DYN_STAT(http_put_requests_stat);
5736   } else if (s->method == HTTP_WKSIDX_CONNECT) {
5737     HTTP_INCREMENT_DYN_STAT(http_connect_requests_stat);
5738   } else if (s->method == HTTP_WKSIDX_DELETE) {
5739     HTTP_INCREMENT_DYN_STAT(http_delete_requests_stat);
5740   } else if (s->method == HTTP_WKSIDX_PURGE) {
5741     HTTP_INCREMENT_DYN_STAT(http_purge_requests_stat);
5742   } else if (s->method == HTTP_WKSIDX_TRACE) {
5743     HTTP_INCREMENT_DYN_STAT(http_trace_requests_stat);
5744   } else if (s->method == HTTP_WKSIDX_PUSH) {
5745     HTTP_INCREMENT_DYN_STAT(http_push_requests_stat);
5746   } else if (s->method == HTTP_WKSIDX_OPTIONS) {
5747     HTTP_INCREMENT_DYN_STAT(http_options_requests_stat);
5748   } else {
5749     HTTP_INCREMENT_DYN_STAT(http_extension_method_requests_stat);
5750     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_METHOD);
5751     s->squid_codes.log_code      = SQUID_LOG_TCP_MISS;
5752     s->hdr_info.extension_method = true;
5753   }
5754 
5755   // if transfer encoding is chunked content length is undefined
5756   if (s->client_info.transfer_encoding == CHUNKED_ENCODING) {
5757     s->hdr_info.request_content_length = HTTP_UNDEFINED_CL;
5758   }
5759   s->request_data.hdr = &s->hdr_info.client_request;
5760 
5761   s->request_data.hostname_str = s->arena.str_store(host_name, host_len);
5762   ats_ip_copy(&s->request_data.src_ip, &s->client_info.src_addr);
5763   memset(&s->request_data.dest_ip, 0, sizeof(s->request_data.dest_ip));
5764   if (vc) {
5765     s->request_data.incoming_port = vc->get_local_port();
5766     s->pp_info.version            = vc->get_proxy_protocol_version();
5767     if (s->pp_info.version != ProxyProtocolVersion::UNDEFINED) {
5768       ats_ip_copy(s->pp_info.src_addr, vc->get_proxy_protocol_src_addr());
5769       ats_ip_copy(s->pp_info.dst_addr, vc->get_proxy_protocol_dst_addr());
5770     }
5771   }
5772   s->request_data.xact_start                      = s->client_request_time;
5773   s->request_data.api_info                        = &s->api_info;
5774   s->request_data.cache_info_lookup_url           = &s->cache_info.lookup_url;
5775   s->request_data.cache_info_parent_selection_url = &s->cache_info.parent_selection_url;
5776 
5777   /////////////////////////////////////////////
5778   // Do dns lookup for the host. We need     //
5779   // the expanded host for cache lookup, and //
5780   // the host ip for reverse proxy.          //
5781   /////////////////////////////////////////////
5782   s->dns_info.looking_up  = ORIGIN_SERVER;
5783   s->dns_info.lookup_name = s->server_info.name;
5784 }
5785 
5786 void
initialize_state_variables_from_response(State * s,HTTPHdr * incoming_response)5787 HttpTransact::initialize_state_variables_from_response(State *s, HTTPHdr *incoming_response)
5788 {
5789   /* check if the server permits caching */
5790   s->cache_info.directives.does_server_permit_storing =
5791     HttpTransactHeaders::does_server_allow_response_to_be_stored(&s->hdr_info.server_response);
5792 
5793   /*
5794    * A stupid moronic broken pathetic excuse
5795    *   for a server may send us a keep alive response even
5796    *   if we sent "Connection: close"  We need check the response
5797    *   header regardless of what we sent to the server
5798    */
5799   s->current.server->keep_alive = s->hdr_info.server_response.keep_alive_get();
5800 
5801   // Don't allow an upgrade request to Keep Alive
5802   if (s->is_upgrade_request) {
5803     s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
5804   }
5805 
5806   if (s->current.server->keep_alive == HTTP_KEEPALIVE) {
5807     TxnDebug("http_hdrs", "[initialize_state_variables_from_response]"
5808                           "Server is keep-alive.");
5809   } else if (s->state_machine->ua_txn && s->state_machine->ua_txn->is_outbound_transparent() &&
5810              s->state_machine->t_state.http_config_param->use_client_source_port) {
5811     /* If we are reusing the client<->ATS 4-tuple for ATS<->server then if the server side is closed, we can't
5812        re-open it because the 4-tuple may still be in the processing of shutting down. So if the server isn't
5813        keep alive we must turn that off for the client as well.
5814     */
5815     s->state_machine->t_state.client_info.keep_alive = HTTP_NO_KEEPALIVE;
5816   }
5817 
5818   HTTPStatus status_code = incoming_response->status_get();
5819   if (is_response_body_precluded(status_code, s->method)) {
5820     s->hdr_info.response_content_length = 0;
5821     s->hdr_info.trust_response_cl       = true;
5822   } else {
5823     // This code used to discriminate CL: headers when the origin disabled keep-alive.
5824     if (incoming_response->presence(MIME_PRESENCE_CONTENT_LENGTH)) {
5825       int64_t cl = incoming_response->get_content_length();
5826 
5827       s->hdr_info.response_content_length = (cl >= 0) ? cl : HTTP_UNDEFINED_CL;
5828       s->hdr_info.trust_response_cl       = true;
5829     } else {
5830       s->hdr_info.response_content_length = HTTP_UNDEFINED_CL;
5831       s->hdr_info.trust_response_cl       = false;
5832     }
5833   }
5834 
5835   if (incoming_response->presence(MIME_PRESENCE_TRANSFER_ENCODING)) {
5836     MIMEField *field = incoming_response->field_find(MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING);
5837     ink_assert(field != nullptr);
5838 
5839     HdrCsvIter enc_val_iter;
5840     int enc_val_len;
5841     const char *enc_value = enc_val_iter.get_first(field, &enc_val_len);
5842 
5843     while (enc_value) {
5844       const char *wks_value = hdrtoken_string_to_wks(enc_value, enc_val_len);
5845 
5846       if (wks_value == HTTP_VALUE_CHUNKED && !is_response_body_precluded(status_code, s->method)) {
5847         TxnDebug("http_hdrs", "[init_state_vars_from_resp] transfer encoding: chunked!");
5848         s->current.server->transfer_encoding = CHUNKED_ENCODING;
5849 
5850         s->hdr_info.response_content_length = HTTP_UNDEFINED_CL;
5851         s->hdr_info.trust_response_cl       = false;
5852 
5853         // OBJECTIVE: Since we are dechunking the request remove the
5854         //   chunked value If this is the only value, we need to remove
5855         //    the whole field.
5856         MIMEField *new_enc_field = nullptr;
5857         HdrCsvIter new_enc_iter;
5858         int new_enc_len;
5859         const char *new_enc_val = new_enc_iter.get_first(field, &new_enc_len);
5860 
5861         // Loop over the all the values in existing Trans-enc header and
5862         //   copy the ones that aren't our chunked value to a new field
5863         while (new_enc_val) {
5864           const char *new_wks_value = hdrtoken_string_to_wks(new_enc_val, new_enc_len);
5865           if (new_wks_value != wks_value) {
5866             if (new_enc_field) {
5867               new_enc_field->value_append(incoming_response->m_heap, incoming_response->m_mime, new_enc_val, new_enc_len, true);
5868             } else {
5869               new_enc_field = incoming_response->field_create();
5870               incoming_response->field_value_set(new_enc_field, new_enc_val, new_enc_len);
5871             }
5872           }
5873 
5874           new_enc_val = new_enc_iter.get_next(&new_enc_len);
5875         }
5876 
5877         // We're done with the old field since we copied out everything
5878         //   we needed
5879         incoming_response->field_delete(field);
5880 
5881         // If there is a new field (ie: there was more than one
5882         //   transfer-encoding), insert it to the list
5883         if (new_enc_field) {
5884           new_enc_field->name_set(incoming_response->m_heap, incoming_response->m_mime, MIME_FIELD_TRANSFER_ENCODING,
5885                                   MIME_LEN_TRANSFER_ENCODING);
5886           incoming_response->field_attach(new_enc_field);
5887         }
5888 
5889         return;
5890       } //  if (enc_value == CHUNKED)
5891 
5892       enc_value = enc_val_iter.get_next(&enc_val_len);
5893     }
5894   }
5895 
5896   s->current.server->transfer_encoding = NO_TRANSFER_ENCODING;
5897 }
5898 
5899 bool
is_cache_response_returnable(State * s)5900 HttpTransact::is_cache_response_returnable(State *s)
5901 {
5902   if (s->cache_control.never_cache) {
5903     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_CONFIG);
5904     return false;
5905   }
5906 
5907   if (!s->cache_info.directives.does_client_permit_lookup) {
5908     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_CLIENT);
5909     return false;
5910   }
5911 
5912   if (!HttpTransactHeaders::is_method_cacheable(s->http_config_param, s->method) && s->api_resp_cacheable == false) {
5913     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_NOT_ACCEPTABLE);
5914     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_METHOD);
5915     return false;
5916   }
5917   // If cookies in response and no TTL set, we do not cache the doc
5918   if ((s->cache_control.ttl_in_cache <= 0) &&
5919       do_cookies_prevent_caching(static_cast<int>(s->txn_conf->cache_responses_to_cookies), &s->hdr_info.client_request,
5920                                  s->cache_info.object_read->response_get(), s->cache_info.object_read->request_get())) {
5921     SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_NOT_ACCEPTABLE);
5922     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_COOKIE);
5923     return false;
5924   }
5925 
5926   return true;
5927 }
5928 
5929 ///////////////////////////////////////////////////////////////////////////////
5930 // Name       : is_stale_cache_response_returnable()
5931 // Description: check if a stale cached response is returnable to a client
5932 //
5933 // Input      : State
5934 // Output     : true or false
5935 //
5936 // Details    :
5937 //
5938 ///////////////////////////////////////////////////////////////////////////////
5939 bool
is_stale_cache_response_returnable(State * s)5940 HttpTransact::is_stale_cache_response_returnable(State *s)
5941 {
5942   HTTPHdr *cached_response = s->cache_info.object_read->response_get();
5943 
5944   // First check if client allows cached response
5945   // Note does_client_permit_lookup was set to
5946   // does_client_Request_permit_cached_response()
5947   // in update_cache_control_information_from_config().
5948   if (!s->cache_info.directives.does_client_permit_lookup) {
5949     return false;
5950   }
5951   // Spec says that we can not serve a stale document with a
5952   //   "must-revalidate header"
5953   // How about "s-maxage" and "no-cache" directives?
5954   uint32_t cc_mask;
5955   cc_mask = (MIME_COOKED_MASK_CC_MUST_REVALIDATE | MIME_COOKED_MASK_CC_PROXY_REVALIDATE | MIME_COOKED_MASK_CC_NEED_REVALIDATE_ONCE |
5956              MIME_COOKED_MASK_CC_NO_CACHE | MIME_COOKED_MASK_CC_NO_STORE | MIME_COOKED_MASK_CC_S_MAXAGE);
5957   if ((cached_response->get_cooked_cc_mask() & cc_mask) || cached_response->is_pragma_no_cache_set()) {
5958     TxnDebug("http_trans", "[is_stale_cache_response_returnable] "
5959                            "document headers prevent serving stale");
5960     return false;
5961   }
5962   // See how old the document really is.  We don't want create a
5963   //   stale content museum of documents that are no longer available
5964   time_t current_age = HttpTransactHeaders::calculate_document_age(s->cache_info.object_read->request_sent_time_get(),
5965                                                                    s->cache_info.object_read->response_received_time_get(),
5966                                                                    cached_response, cached_response->get_date(), s->current.now);
5967   // Negative age is overflow
5968   if ((current_age < 0) || (current_age > s->txn_conf->cache_max_stale_age)) {
5969     TxnDebug("http_trans",
5970              "[is_stale_cache_response_returnable] "
5971              "document age is too large %" PRId64,
5972              (int64_t)current_age);
5973     return false;
5974   }
5975   // If the stale document requires authorization, we can't return it either.
5976   Authentication_t auth_needed = AuthenticationNeeded(s->txn_conf, &s->hdr_info.client_request, cached_response);
5977 
5978   if (auth_needed != AUTHENTICATION_SUCCESS) {
5979     TxnDebug("http_trans", "[is_stale_cache_response_returnable] "
5980                            "authorization prevent serving stale");
5981     return false;
5982   }
5983 
5984   TxnDebug("http_trans", "[is_stale_cache_response_returnable] can serve stale");
5985   return true;
5986 }
5987 
5988 bool
url_looks_dynamic(URL * url)5989 HttpTransact::url_looks_dynamic(URL *url)
5990 {
5991   const char *p_start, *p, *t;
5992   static const char *asp = ".asp";
5993   const char *part;
5994   int part_length;
5995 
5996   if (url->scheme_get_wksidx() != URL_WKSIDX_HTTP && url->scheme_get_wksidx() != URL_WKSIDX_HTTPS) {
5997     return false;
5998   }
5999   ////////////////////////////////////////////////////////////
6000   // (1) If URL contains query stuff in it, call it dynamic //
6001   ////////////////////////////////////////////////////////////
6002 
6003   part = url->params_get(&part_length);
6004   if (part != nullptr) {
6005     return true;
6006   }
6007   part = url->query_get(&part_length);
6008   if (part != nullptr) {
6009     return true;
6010   }
6011   ///////////////////////////////////////////////
6012   // (2) If path ends in "asp" call it dynamic //
6013   ///////////////////////////////////////////////
6014 
6015   part = url->path_get(&part_length);
6016   if (part) {
6017     p = &part[part_length - 1];
6018     t = &asp[3];
6019 
6020     while (p != part) {
6021       if (ParseRules::ink_tolower(*p) == ParseRules::ink_tolower(*t)) {
6022         p -= 1;
6023         t -= 1;
6024         if (t == asp) {
6025           return true;
6026         }
6027       } else {
6028         break;
6029       }
6030     }
6031   }
6032   /////////////////////////////////////////////////////////////////
6033   // (3) If the path of the url contains "cgi", call it dynamic. //
6034   /////////////////////////////////////////////////////////////////
6035 
6036   if (part && part_length >= 3) {
6037     for (p_start = part; p_start <= &part[part_length - 3]; p_start++) {
6038       if (((p_start[0] == 'c') || (p_start[0] == 'C')) && ((p_start[1] == 'g') || (p_start[1] == 'G')) &&
6039           ((p_start[2] == 'i') || (p_start[2] == 'I'))) {
6040         return (true);
6041       }
6042     }
6043   }
6044 
6045   return (false);
6046 }
6047 
6048 ///////////////////////////////////////////////////////////////////////////////
6049 // Name       : is_request_cache_lookupable()
6050 // Description: check if a request should be looked up in cache
6051 //
6052 // Input      : State, request header
6053 // Output     : true or false
6054 //
6055 // Details    :
6056 //
6057 //
6058 ///////////////////////////////////////////////////////////////////////////////
6059 bool
is_request_cache_lookupable(State * s)6060 HttpTransact::is_request_cache_lookupable(State *s)
6061 {
6062   // ummm, someone has already decided that proxy should tunnel
6063   if (s->current.mode == TUNNELLING_PROXY) {
6064     return false;
6065   }
6066   // don't bother with remaining checks if we already did a cache lookup
6067   if (s->cache_info.lookup_count > 0) {
6068     return true;
6069   }
6070   // is cache turned on?
6071   if (!s->txn_conf->cache_http) {
6072     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_CACHE_OFF);
6073     return false;
6074   }
6075   // GET, HEAD, POST, DELETE, and PUT are all cache lookupable
6076   if (!HttpTransactHeaders::is_method_cache_lookupable(s->method) && s->api_req_cacheable == false) {
6077     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_METHOD);
6078     return false;
6079   }
6080   // don't cache page if URL "looks dynamic" and this filter is enabled
6081   // We can do the check in is_response_cacheable() or here.
6082   // It may be more efficient if we are not going to cache dynamic looking urls
6083   // (the default config?) since we don't even need to do cache lookup.
6084   // So for the time being, it'll be left here.
6085 
6086   // If url looks dynamic but a ttl is set, request is cache lookupable
6087   if ((!s->txn_conf->cache_urls_that_look_dynamic) && url_looks_dynamic(s->hdr_info.client_request.url_get()) &&
6088       (s->cache_control.ttl_in_cache <= 0)) {
6089     // We do not want to forward the request for a dynamic URL onto the
6090     // origin server if the value of the Max-Forwards header is zero.
6091     int max_forwards = -1;
6092     if (s->hdr_info.client_request.presence(MIME_PRESENCE_MAX_FORWARDS)) {
6093       MIMEField *max_forwards_f = s->hdr_info.client_request.field_find(MIME_FIELD_MAX_FORWARDS, MIME_LEN_MAX_FORWARDS);
6094 
6095       if (max_forwards_f) {
6096         max_forwards = max_forwards_f->value_get_int();
6097       }
6098     }
6099 
6100     if (max_forwards != 0) {
6101       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_URL);
6102       return false;
6103     }
6104   }
6105 
6106   // Don't look in cache if it's a RANGE request but the cache is not enabled for RANGE.
6107   if (!s->txn_conf->cache_range_lookup && s->hdr_info.client_request.presence(MIME_PRESENCE_RANGE)) {
6108     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_HEADER_FIELD);
6109     return false;
6110   }
6111 
6112   // Even with "no-cache" directive, we want to do a cache lookup
6113   // because we need to update our cached copy.
6114   // Client request "no-cache" directive is handle elsewhere:
6115   // update_cache_control_information_from_config()
6116 
6117   return true;
6118 }
6119 
6120 ///////////////////////////////////////////////////////////////////////////////
6121 // Name       : response_cacheable_indicated_by_cc()
6122 // Description: check if a response is cacheable as indicated by Cache-Control
6123 //
6124 // Input      : Response header
6125 // Output     : -1, 0, or +1
6126 //
6127 // Details    :
6128 // (1) return -1 if cache control indicates response not cacheable,
6129 //     ie, with no-store, or private directives;
6130 // (2) return +1 if cache control indicates response cacheable
6131 //     ie, with public, max-age, s-maxage, must-revalidate, or proxy-revalidate;
6132 // (3) otherwise, return 0 if cache control does not indicate.
6133 //
6134 ///////////////////////////////////////////////////////////////////////////////
6135 int
response_cacheable_indicated_by_cc(HTTPHdr * response)6136 response_cacheable_indicated_by_cc(HTTPHdr *response)
6137 {
6138   uint32_t cc_mask;
6139   // the following directives imply not cacheable
6140   cc_mask = (MIME_COOKED_MASK_CC_NO_STORE | MIME_COOKED_MASK_CC_PRIVATE);
6141   if (response->get_cooked_cc_mask() & cc_mask) {
6142     return -1;
6143   }
6144   // the following directives imply cacheable
6145   cc_mask = (MIME_COOKED_MASK_CC_PUBLIC | MIME_COOKED_MASK_CC_MAX_AGE | MIME_COOKED_MASK_CC_S_MAXAGE |
6146              MIME_COOKED_MASK_CC_MUST_REVALIDATE | MIME_COOKED_MASK_CC_PROXY_REVALIDATE);
6147   if (response->get_cooked_cc_mask() & cc_mask) {
6148     return 1;
6149   }
6150   // otherwise, no indication
6151   return 0;
6152 }
6153 
6154 ///////////////////////////////////////////////////////////////////////////////
6155 // Name       : is_response_cacheable()
6156 // Description: check if a response is cacheable
6157 //
6158 // Input      : State, request header, response header
6159 // Output     : true or false
6160 //
6161 // Details    :
6162 //
6163 ///////////////////////////////////////////////////////////////////////////////
6164 bool
is_response_cacheable(State * s,HTTPHdr * request,HTTPHdr * response)6165 HttpTransact::is_response_cacheable(State *s, HTTPHdr *request, HTTPHdr *response)
6166 {
6167   // If the use_client_target_addr is specified but the client
6168   // specified OS addr does not match any of trafficserver's looked up
6169   // host addresses, do not allow cache.  This may cause DNS cache poisoning
6170   // of other trafficserver clients. The flag is set in the
6171   // process_host_db_info method
6172   if (!s->dns_info.lookup_validated && s->client_info.is_transparent) {
6173     TxnDebug("http_trans", "[is_response_cacheable] "
6174                            "Lookup not validated.  Possible DNS cache poison.  Don't cache");
6175     return false;
6176   }
6177 
6178   // the plugin may decide we don't want to cache the response
6179   if (s->api_server_response_no_store) {
6180     return false;
6181   }
6182   // if method is not GET or HEAD, do not cache.
6183   // Note: POST is also cacheable with Expires or Cache-control.
6184   // but due to INKqa11567, we are not caching POST responses.
6185   // Basically, the problem is the resp for POST url1 req should not
6186   // be served to a GET url1 request, but we just match URL not method.
6187   int req_method = request->method_get_wksidx();
6188   if (!(HttpTransactHeaders::is_method_cacheable(s->http_config_param, req_method)) && s->api_req_cacheable == false) {
6189     TxnDebug("http_trans", "[is_response_cacheable] "
6190                            "only GET, and some HEAD and POST are cacheable");
6191     return false;
6192   }
6193   // TxnDebug("http_trans", "[is_response_cacheable] method is cacheable");
6194   // If the request was not looked up in the cache, the response
6195   // should not be cached (same subsequent requests will not be
6196   // looked up, either, so why cache this).
6197   if (!(is_request_cache_lookupable(s))) {
6198     TxnDebug("http_trans", "[is_response_cacheable] "
6199                            "request is not cache lookupable, response is not cacheable");
6200     return false;
6201   }
6202   // already has a fresh copy in the cache
6203   if (s->range_setup == RANGE_NOT_HANDLED) {
6204     return false;
6205   }
6206 
6207   // Check whether the response is cacheable based on its cookie
6208   // If there are cookies in response but a ttl is set, allow caching
6209   if ((s->cache_control.ttl_in_cache <= 0) &&
6210       do_cookies_prevent_caching(static_cast<int>(s->txn_conf->cache_responses_to_cookies), request, response)) {
6211     TxnDebug("http_trans", "[is_response_cacheable] "
6212                            "response has uncachable cookies, response is not cacheable");
6213     return false;
6214   }
6215   // if server spits back a WWW-Authenticate
6216   if ((s->txn_conf->cache_ignore_auth) == 0 && response->presence(MIME_PRESENCE_WWW_AUTHENTICATE)) {
6217     TxnDebug("http_trans", "[is_response_cacheable] "
6218                            "response has WWW-Authenticate, response is not cacheable");
6219     return false;
6220   }
6221   // does server explicitly forbid storing?
6222   // If OS forbids storing but a ttl is set, allow caching
6223   if (!s->cache_info.directives.does_server_permit_storing && (s->cache_control.ttl_in_cache <= 0)) {
6224     TxnDebug("http_trans", "[is_response_cacheable] server does not permit storing and config file does not "
6225                            "indicate that server directive should be ignored");
6226     return false;
6227   }
6228   // TxnDebug("http_trans", "[is_response_cacheable] server permits storing");
6229 
6230   // does config explicitly forbid storing?
6231   // ttl overrides other config parameters
6232   if ((!s->cache_info.directives.does_config_permit_storing && (s->cache_control.ttl_in_cache <= 0)) ||
6233       (s->cache_control.never_cache)) {
6234     TxnDebug("http_trans", "[is_response_cacheable] config doesn't allow storing, and cache control does not "
6235                            "say to ignore no-cache and does not specify never-cache or a ttl");
6236     return false;
6237   }
6238   // TxnDebug("http_trans", "[is_response_cacheable] config permits storing");
6239 
6240   // does client explicitly forbid storing?
6241   if (!s->cache_info.directives.does_client_permit_storing && !s->cache_control.ignore_client_no_cache) {
6242     TxnDebug("http_trans", "[is_response_cacheable] client does not permit storing, "
6243                            "and cache control does not say to ignore client no-cache");
6244     return false;
6245   }
6246   TxnDebug("http_trans", "[is_response_cacheable] client permits storing");
6247 
6248   HTTPStatus response_code = response->status_get();
6249 
6250   // caching/not-caching based on required headers
6251   // only makes sense when the server sends back a
6252   // 200 and a document.
6253   if (response_code == HTTP_STATUS_OK) {
6254     // If a ttl is set: no header required for caching
6255     // otherwise: follow parameter http.cache.required_headers
6256     if (s->cache_control.ttl_in_cache <= 0) {
6257       uint32_t cc_mask = (MIME_COOKED_MASK_CC_MAX_AGE | MIME_COOKED_MASK_CC_S_MAXAGE);
6258       // server did not send expires header or last modified
6259       // and we are configured to not cache without them.
6260       switch (s->txn_conf->cache_required_headers) {
6261       case HttpConfigParams::CACHE_REQUIRED_HEADERS_NONE:
6262         TxnDebug("http_trans", "[is_response_cacheable] "
6263                                "no response headers required");
6264         break;
6265 
6266       case HttpConfigParams::CACHE_REQUIRED_HEADERS_AT_LEAST_LAST_MODIFIED:
6267         if (!response->presence(MIME_PRESENCE_EXPIRES) && !(response->get_cooked_cc_mask() & cc_mask) &&
6268             !response->get_last_modified()) {
6269           TxnDebug("http_trans", "[is_response_cacheable] "
6270                                  "last_modified, expires, or max-age is required");
6271 
6272           s->squid_codes.hit_miss_code = ((response->get_date() == 0) ? (SQUID_MISS_HTTP_NO_DLE) : (SQUID_MISS_HTTP_NO_LE));
6273           return false;
6274         }
6275         break;
6276 
6277       case HttpConfigParams::CACHE_REQUIRED_HEADERS_CACHE_CONTROL:
6278         if (!response->presence(MIME_PRESENCE_EXPIRES) && !(response->get_cooked_cc_mask() & cc_mask)) {
6279           TxnDebug("http_trans", "[is_response_cacheable] "
6280                                  "expires header or max-age is required");
6281           return false;
6282         }
6283         break;
6284 
6285       default:
6286         break;
6287       }
6288     }
6289   }
6290   // do not cache partial content - Range response
6291   if (response_code == HTTP_STATUS_PARTIAL_CONTENT || response_code == HTTP_STATUS_RANGE_NOT_SATISFIABLE) {
6292     TxnDebug("http_trans",
6293              "[is_response_cacheable] "
6294              "response code %d - don't cache",
6295              response_code);
6296     return false;
6297   }
6298 
6299   // check if cache control overrides default cacheability
6300   int indicator;
6301   indicator = response_cacheable_indicated_by_cc(response);
6302   if (indicator > 0) { // cacheable indicated by cache control header
6303     TxnDebug("http_trans", "[is_response_cacheable] YES by response cache control");
6304     // even if it is authenticated, this is cacheable based on regular rules
6305     s->www_auth_content = CACHE_AUTH_NONE;
6306     return true;
6307   } else if (indicator < 0) { // not cacheable indicated by cache control header
6308 
6309     // If a ttl is set, allow caching even if response contains
6310     // Cache-Control headers to prevent caching
6311     if (s->cache_control.ttl_in_cache > 0) {
6312       TxnDebug("http_trans", "[is_response_cacheable] Cache-control header directives in response overridden by ttl in %s",
6313                ts::filename::CACHE);
6314     } else {
6315       TxnDebug("http_trans", "[is_response_cacheable] NO by response cache control");
6316       return false;
6317     }
6318   }
6319   // else no indication by cache control header
6320   // continue to determine cacheability
6321 
6322   if (response->presence(MIME_PRESENCE_EXPIRES)) {
6323     TxnDebug("http_trans", "[is_response_cacheable] YES response w/ Expires");
6324     return true;
6325   }
6326   // if it's a 302 or 307 and no positive indicator from cache-control, reject
6327   if (response_code == HTTP_STATUS_MOVED_TEMPORARILY || response_code == HTTP_STATUS_TEMPORARY_REDIRECT) {
6328     TxnDebug("http_trans", "[is_response_cacheable] cache-control or expires header is required for 302");
6329     return false;
6330   }
6331   // if it's a POST request and no positive indicator from cache-control
6332   if (req_method == HTTP_WKSIDX_POST) {
6333     // allow caching for a POST requests w/o Expires but with a ttl
6334     if (s->cache_control.ttl_in_cache > 0) {
6335       TxnDebug("http_trans", "[is_response_cacheable] POST method with a TTL");
6336     } else {
6337       TxnDebug("http_trans", "[is_response_cacheable] NO POST w/o Expires or CC");
6338       return false;
6339     }
6340   }
6341 
6342   if ((response_code == HTTP_STATUS_OK) || (response_code == HTTP_STATUS_NOT_MODIFIED) ||
6343       (response_code == HTTP_STATUS_NON_AUTHORITATIVE_INFORMATION) || (response_code == HTTP_STATUS_MOVED_PERMANENTLY) ||
6344       (response_code == HTTP_STATUS_MULTIPLE_CHOICES) || (response_code == HTTP_STATUS_GONE)) {
6345     TxnDebug("http_trans", "[is_response_cacheable] YES response code seems fine");
6346     return true;
6347   }
6348   // Notice that the following are not overridable by negative caching.
6349   if (response_code == HTTP_STATUS_SEE_OTHER || response_code == HTTP_STATUS_UNAUTHORIZED ||
6350       response_code == HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
6351     return false;
6352   }
6353   // The response code does not look appropriate for caching. Check, however,
6354   // whether the user has specified it should be cached via negative response
6355   // caching configuration.
6356   if (is_negative_caching_appropriate(s)) {
6357     return true;
6358   }
6359   return false;
6360   /* Since we weren't caching response obtained with
6361      Authorization (the cache control stuff was commented out previously)
6362      I've moved this check to is_request_cache_lookupable().
6363      We should consider this matter further.  It is unclear
6364      how many sites actually add Cache-Control headers for Authorized content.
6365   */
6366 }
6367 
6368 bool
is_request_valid(State * s,HTTPHdr * incoming_request)6369 HttpTransact::is_request_valid(State *s, HTTPHdr *incoming_request)
6370 {
6371   RequestError_t incoming_error;
6372   URL *url = nullptr;
6373 
6374   if (incoming_request) {
6375     url = incoming_request->url_get();
6376   }
6377 
6378   incoming_error = check_request_validity(s, incoming_request);
6379   switch (incoming_error) {
6380   case NO_REQUEST_HEADER_ERROR:
6381     TxnDebug("http_trans", "[is_request_valid] no request header errors");
6382     break;
6383   case FAILED_PROXY_AUTHORIZATION:
6384     TxnDebug("http_trans", "[is_request_valid] failed proxy authorization");
6385     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
6386     build_error_response(s, HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required",
6387                          "access#proxy_auth_required");
6388     return false;
6389   case NON_EXISTANT_REQUEST_HEADER:
6390   /* fall through */
6391   case BAD_HTTP_HEADER_SYNTAX: {
6392     TxnDebug("http_trans", "[is_request_valid] non-existent/bad header");
6393     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
6394     build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid HTTP Request", "request#syntax_error");
6395     return false;
6396   }
6397 
6398   case MISSING_HOST_FIELD:
6399 
6400     ////////////////////////////////////////////////////////////////////
6401     // FIX: are we sure the following logic is right?  it seems that  //
6402     //      we shouldn't complain about the missing host header until //
6403     //      we know we really need one --- are we sure we need a host //
6404     //      header at this point?                                     //
6405     //                                                                //
6406     // FIX: also, let's clean up the transparency code to remove the  //
6407     //      SunOS conditionals --- we will be transparent on all      //
6408     //      platforms soon!  in fact, I really want a method that i   //
6409     //      can call for each transaction to say if the transaction   //
6410     //      is a forward proxy request, a transparent request, a      //
6411     //      reverse proxy request, etc --- the detail of how we       //
6412     //      determine the cases should be hidden behind the method.   //
6413     ////////////////////////////////////////////////////////////////////
6414 
6415     TxnDebug("http_trans", "[is_request_valid] missing host field");
6416     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
6417     if (s->http_config_param->reverse_proxy_enabled) { // host header missing and reverse proxy on
6418       build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Host Header Required", "request#no_host");
6419     } else {
6420       // host header missing and reverse proxy off
6421       build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Host Required In Request", "request#no_host");
6422     }
6423 
6424     return false;
6425   case SCHEME_NOT_SUPPORTED:
6426   case NO_REQUEST_SCHEME: {
6427     TxnDebug("http_trans", "[is_request_valid] unsupported or missing request scheme");
6428     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
6429     build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Unsupported URL Scheme", "request#scheme_unsupported");
6430     return false;
6431   }
6432   /* fall through */
6433   case METHOD_NOT_SUPPORTED:
6434     TxnDebug("http_trans", "[is_request_valid] unsupported method");
6435     s->current.mode = TUNNELLING_PROXY;
6436     return true;
6437   case BAD_CONNECT_PORT:
6438     int port;
6439     port = url ? url->port_get() : 0;
6440     TxnDebug("http_trans", "[is_request_valid] %d is an invalid connect port", port);
6441     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
6442     build_error_response(s, HTTP_STATUS_FORBIDDEN, "Tunnel Forbidden", "access#connect_forbidden");
6443     return false;
6444   case NO_POST_CONTENT_LENGTH: {
6445     TxnDebug("http_trans", "[is_request_valid] post request without content length");
6446     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
6447     build_error_response(s, HTTP_STATUS_LENGTH_REQUIRED, "Content Length Required", "request#no_content_length");
6448     return false;
6449   }
6450   case UNACCEPTABLE_TE_REQUIRED: {
6451     TxnDebug("http_trans", "[is_request_valid] TE required is unacceptable.");
6452     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
6453     build_error_response(s, HTTP_STATUS_NOT_ACCEPTABLE, "Transcoding Not Available", "transcoding#unsupported");
6454     return false;
6455   }
6456   case INVALID_POST_CONTENT_LENGTH: {
6457     TxnDebug("http_trans", "[is_request_valid] post request with negative content length value");
6458     SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
6459     build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Invalid Content Length", "request#invalid_content_length");
6460     return false;
6461   }
6462   default:
6463     return true;
6464   }
6465 
6466   return true;
6467 }
6468 
6469 // bool HttpTransact::is_request_retryable
6470 //
6471 // In the general case once bytes have been sent on the wire the request cannot be retried.
6472 // The reason we cannot retry is that the rfc2616 does not make any guarantees about the
6473 // retry-ability of a request. In fact in the reverse proxy case it is quite common for GET
6474 // requests on the origin to fire tracking events etc. So, as a proxy, once bytes have been ACKd
6475 // by the server we cannot guarantee that the request is safe to retry or redispatch to another server.
6476 // This is distinction of "ACKd" vs "sent" is intended, and has reason. In the case of a
6477 // new origin connection there is little difference, as the chance of a RST between setup
6478 // and the first set of bytes is relatively small. This distinction is more apparent in the
6479 // case where the origin connection is a KA session. In this case, the session may not have
6480 // been used for a long time. In that case, we'll immediately queue up session to send to the
6481 // origin, without any idea of the state of the connection. If the origin is dead (or the connection
6482 // is broken for some other reason) we'll immediately get a RST back. In that case-- since no
6483 // bytes where ACKd by the remote end, we can retry/redispatch the request.
6484 //
6485 bool
is_request_retryable(State * s)6486 HttpTransact::is_request_retryable(State *s)
6487 {
6488   // If safe requests are  retryable, it should be safe to retry safe requests irrespective of bytes sent or connection state
6489   // according to RFC the following methods are safe (https://tools.ietf.org/html/rfc7231#section-4.2.1)
6490   // Otherwise, if there was no error establishing the connection (and we sent bytes)-- we cannot retry
6491   if (!HttpTransactHeaders::is_method_safe(s->method) && s->current.state != CONNECTION_ERROR &&
6492       s->state_machine->server_request_hdr_bytes > 0) {
6493     return false;
6494   }
6495 
6496   // FIXME: disable the post transform retry currently.
6497   if (s->state_machine->is_post_transform_request()) {
6498     return false;
6499   }
6500 
6501   if (s->state_machine->plugin_tunnel_type != HTTP_NO_PLUGIN_TUNNEL) {
6502     // API can override
6503     if (s->state_machine->plugin_tunnel_type == HTTP_PLUGIN_AS_SERVER && s->api_info.retry_intercept_failures == true) {
6504       // This used to be an == comparison, which made no sense. Changed
6505       // to be an assignment, hoping the state is correct.
6506       s->state_machine->plugin_tunnel_type = HTTP_NO_PLUGIN_TUNNEL;
6507     } else {
6508       return false;
6509     }
6510   }
6511 
6512   return true;
6513 }
6514 
6515 void
process_quick_http_filter(State * s,int method)6516 HttpTransact::process_quick_http_filter(State *s, int method)
6517 {
6518   // connection already disabled by previous ACL filtering, don't modify it.
6519   if (!s->client_connection_enabled) {
6520     return;
6521   }
6522 
6523   // if ipallow rules are disabled by remap then don't modify anything
6524   url_mapping *mp = s->url_map.getMapping();
6525   if (mp && !mp->ip_allow_check_enabled_p) {
6526     return;
6527   }
6528 
6529   if (s->state_machine->ua_txn) {
6530     auto &acl              = s->state_machine->ua_txn->get_acl();
6531     bool deny_request      = !acl.isValid();
6532     int method_str_len     = 0;
6533     const char *method_str = nullptr;
6534 
6535     if (acl.isValid() && !acl.isAllowAll()) {
6536       if (method != -1) {
6537         deny_request = !acl.isMethodAllowed(method);
6538       } else {
6539         method_str   = s->hdr_info.client_request.method_get(&method_str_len);
6540         deny_request = !acl.isNonstandardMethodAllowed(std::string_view(method_str, method_str_len));
6541       }
6542     }
6543     if (deny_request) {
6544       if (is_debug_tag_set("ip-allow")) {
6545         ip_text_buffer ipb;
6546         if (method != -1) {
6547           method_str     = hdrtoken_index_to_wks(method);
6548           method_str_len = strlen(method_str);
6549         } else if (!method_str) {
6550           method_str = s->hdr_info.client_request.method_get(&method_str_len);
6551         }
6552         TxnDebug("ip-allow", "Line %d denial for '%.*s' from %s", acl.source_line(), method_str_len, method_str,
6553                  ats_ip_ntop(&s->client_info.src_addr.sa, ipb, sizeof(ipb)));
6554       }
6555       s->client_connection_enabled = false;
6556     }
6557   }
6558 }
6559 
6560 bool
will_this_request_self_loop(State * s)6561 HttpTransact::will_this_request_self_loop(State *s)
6562 {
6563   ////////////////////////////////////////
6564   // check if we are about to self loop //
6565   ////////////////////////////////////////
6566   if (s->dns_info.lookup_success) {
6567     in_port_t dst_port   = s->hdr_info.client_request.url_get()->port_get(); // going to this port.
6568     in_port_t local_port = s->client_info.dst_addr.host_order_port();        // already connected proxy port.
6569     // It's a loop if connecting to the same port as it already connected to the proxy and
6570     // it's a proxy address or the same address it already connected to.
6571     if (dst_port == local_port && (ats_ip_addr_eq(s->host_db_info.ip(), &Machine::instance()->ip.sa) ||
6572                                    ats_ip_addr_eq(s->host_db_info.ip(), s->client_info.dst_addr))) {
6573       switch (s->dns_info.looking_up) {
6574       case ORIGIN_SERVER:
6575         TxnDebug("http_transact", "host ip and port same as local ip and port - bailing");
6576         break;
6577       case PARENT_PROXY:
6578         TxnDebug("http_transact", "parent proxy ip and port same as local ip and port - bailing");
6579         break;
6580       default:
6581         TxnDebug("http_transact", "unknown's ip and port same as local ip and port - bailing");
6582         break;
6583       }
6584       SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_LOOP_DETECTED);
6585       build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Cycle Detected", "request#cycle_detected");
6586       return true;
6587     }
6588 
6589     // Now check for a loop using the Via string.
6590     const char *uuid     = Machine::instance()->uuid.getString();
6591     MIMEField *via_field = s->hdr_info.client_request.field_find(MIME_FIELD_VIA, MIME_LEN_VIA);
6592 
6593     while (via_field) {
6594       // No need to waste cycles comma separating the via values since we want to do a match anywhere in the
6595       // in the string.  We can just loop over the dup hdr fields
6596       int via_len;
6597       const char *via_string = via_field->value_get(&via_len);
6598 
6599       if (via_string && ptr_len_str(via_string, via_len, uuid)) {
6600         SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_LOOP_DETECTED);
6601         TxnDebug("http_transact", "Incoming via: %.*s has (%s[%s] (%s))", via_len, via_string, s->http_config_param->proxy_hostname,
6602                  uuid, s->http_config_param->proxy_request_via_string);
6603         build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Multi-Hop Cycle Detected", "request#cycle_detected");
6604         return true;
6605       }
6606 
6607       via_field = via_field->m_next_dup;
6608     }
6609   }
6610   return false;
6611 }
6612 
6613 /*
6614  * handle_content_length_header(...)
6615  *  Function handles the insertion of content length headers into
6616  * header. header CAN equal base.
6617  */
6618 void
handle_content_length_header(State * s,HTTPHdr * header,HTTPHdr * base)6619 HttpTransact::handle_content_length_header(State *s, HTTPHdr *header, HTTPHdr *base)
6620 {
6621   int64_t cl = HTTP_UNDEFINED_CL;
6622   ink_assert(header->type_get() == HTTP_TYPE_RESPONSE);
6623   if (base->presence(MIME_PRESENCE_CONTENT_LENGTH)) {
6624     cl = base->get_content_length();
6625     if (cl >= 0) {
6626       // header->set_content_length(cl);
6627       ink_assert(header->get_content_length() == cl);
6628 
6629       switch (s->source) {
6630       case SOURCE_HTTP_ORIGIN_SERVER:
6631         // We made our decision about whether to trust the
6632         //   response content length in init_state_vars_from_response()
6633         if (s->range_setup != HttpTransact::RANGE_NOT_TRANSFORM_REQUESTED) {
6634           break;
6635         }
6636         // fallthrough
6637 
6638       case SOURCE_CACHE:
6639         // if we are doing a single Range: request, calculate the new
6640         // C-L: header
6641         if (s->range_setup == HttpTransact::RANGE_NOT_TRANSFORM_REQUESTED) {
6642           change_response_header_because_of_range_request(s, header);
6643           s->hdr_info.trust_response_cl = true;
6644         }
6645         ////////////////////////////////////////////////
6646         //  Make sure that the cache's object size    //
6647         //   agrees with the Content-Length           //
6648         //   Otherwise, set the state's machine view  //
6649         //   of c-l to undefined to turn off K-A      //
6650         ////////////////////////////////////////////////
6651         else if (s->cache_info.object_read->object_size_get() == cl) {
6652           s->hdr_info.trust_response_cl = true;
6653         } else {
6654           TxnDebug("http_trans", "Content Length header and cache object size mismatch."
6655                                  "Disabling keep-alive");
6656           s->hdr_info.trust_response_cl = false;
6657         }
6658         break;
6659 
6660       case SOURCE_TRANSFORM:
6661         if (s->range_setup == HttpTransact::RANGE_REQUESTED) {
6662           header->set_content_length(s->range_output_cl);
6663           s->hdr_info.trust_response_cl = true;
6664         } else if (s->hdr_info.transform_response_cl == HTTP_UNDEFINED_CL) {
6665           s->hdr_info.trust_response_cl = false;
6666         } else {
6667           s->hdr_info.trust_response_cl = true;
6668         }
6669         break;
6670 
6671       default:
6672         ink_release_assert(0);
6673         break;
6674       }
6675     } else {
6676       header->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
6677       s->hdr_info.trust_response_cl = false;
6678     }
6679     TxnDebug("http_trans", "[handle_content_length_header] RESPONSE cont len in hdr is %" PRId64, header->get_content_length());
6680   } else {
6681     // No content length header.
6682     // If the source is cache or server returned 304 response,
6683     // we can try to get the content length based on object size.
6684     // Also, we should check the scenario of server sending't  a unexpected 304 response for a non conditional request( no cached
6685     // object )
6686     if (s->source == SOURCE_CACHE ||
6687         (s->source == SOURCE_HTTP_ORIGIN_SERVER && s->hdr_info.server_response.status_get() == HTTP_STATUS_NOT_MODIFIED &&
6688          s->cache_info.object_read != nullptr)) {
6689       // If there is no content-length header, we can
6690       //   insert one since the cache knows definitely
6691       //   how long the object is unless we're in a
6692       //   read-while-write mode and object hasn't been
6693       //   written into a cache completely.
6694       cl = s->cache_info.object_read->object_size_get();
6695       if (cl == INT64_MAX) { // INT64_MAX cl in cache indicates rww in progress
6696         header->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
6697         s->hdr_info.trust_response_cl      = false;
6698         s->hdr_info.request_content_length = HTTP_UNDEFINED_CL;
6699         ink_assert(s->range_setup == RANGE_NONE);
6700       } else if (s->range_setup == RANGE_NOT_TRANSFORM_REQUESTED) {
6701         // if we are doing a single Range: request, calculate the new
6702         // C-L: header
6703         // either the object is in cache or origin returned a 304 Not Modified response. We can still turn this into a proper Range
6704         // response from the cached object.
6705         change_response_header_because_of_range_request(s, header);
6706         s->hdr_info.trust_response_cl = true;
6707       } else {
6708         header->set_content_length(cl);
6709         s->hdr_info.trust_response_cl = true;
6710       }
6711     } else {
6712       // Check to see if there is no content length
6713       //  header because the response precludes a
6714       // body
6715       if (is_response_body_precluded(header->status_get(), s->method)) {
6716         // We want to be able to do keep-alive here since
6717         //   there can't be body so we don't have any
6718         //   issues about trusting the body length
6719         s->hdr_info.trust_response_cl = true;
6720       } else {
6721         s->hdr_info.trust_response_cl = false;
6722       }
6723       header->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
6724       ink_assert(s->range_setup != RANGE_NOT_TRANSFORM_REQUESTED);
6725     }
6726   }
6727   return;
6728 } /* End HttpTransact::handle_content_length_header */
6729 
6730 //////////////////////////////////////////////////////////////////////////////
6731 //
6732 //      void HttpTransact::handle_request_keep_alive_headers(
6733 //          State* s, bool ka_on, HTTPVersion ver, HTTPHdr *heads)
6734 //
6735 //      Removes keep alive headers from user-agent from <heads>
6736 //
6737 //      Adds the appropriate keep alive headers (if any) to <heads>
6738 //      for keep-alive state <ka_on>, and HTTP version <ver>.
6739 //
6740 //////////////////////////////////////////////////////////////////////////////
6741 void
handle_request_keep_alive_headers(State * s,HTTPVersion ver,HTTPHdr * heads)6742 HttpTransact::handle_request_keep_alive_headers(State *s, HTTPVersion ver, HTTPHdr *heads)
6743 {
6744   enum KA_Action_t {
6745     KA_UNKNOWN,
6746     KA_DISABLED,
6747     KA_CLOSE,
6748     KA_CONNECTION,
6749   };
6750 
6751   KA_Action_t ka_action = KA_UNKNOWN;
6752   bool upstream_ka      = (s->current.server->keep_alive == HTTP_KEEPALIVE);
6753 
6754   ink_assert(heads->type_get() == HTTP_TYPE_REQUEST);
6755 
6756   // Check preconditions for Keep-Alive
6757   if (!upstream_ka) {
6758     ka_action = KA_DISABLED;
6759   } else if (ver.get_major() == 0) { /* No K-A for 0.9 apps */
6760     ka_action = KA_DISABLED;
6761   }
6762   // If preconditions are met, figure out what action to take
6763   if (ka_action == KA_UNKNOWN) {
6764     int method = heads->method_get_wksidx();
6765     if (method == HTTP_WKSIDX_GET || method == HTTP_WKSIDX_HEAD || method == HTTP_WKSIDX_OPTIONS || method == HTTP_WKSIDX_PURGE ||
6766         method == HTTP_WKSIDX_DELETE || method == HTTP_WKSIDX_TRACE) {
6767       // These methods do not need a content-length header
6768       ka_action = KA_CONNECTION;
6769     } else {
6770       // All remaining methods require a content length header
6771       if (heads->get_content_length() == -1) {
6772         ka_action = KA_CLOSE;
6773       } else {
6774         ka_action = KA_CONNECTION;
6775       }
6776     }
6777   }
6778 
6779   ink_assert(ka_action != KA_UNKNOWN);
6780 
6781   // Since connection headers are hop-to-hop, strip the
6782   //  the ones we received from the user-agent
6783   heads->field_delete(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
6784   heads->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
6785 
6786   if (!s->is_upgrade_request) {
6787     // Insert K-A headers as necessary
6788     switch (ka_action) {
6789     case KA_CONNECTION:
6790       ink_assert(s->current.server->keep_alive != HTTP_NO_KEEPALIVE);
6791       if (ver == HTTP_1_0) {
6792         if (s->current.request_to == PARENT_PROXY && parent_is_proxy(s)) {
6793           heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "keep-alive", 10);
6794         } else {
6795           heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "keep-alive", 10);
6796         }
6797       }
6798       // NOTE: if the version is 1.1 we don't need to do
6799       //  anything since keep-alive is assumed
6800       break;
6801     case KA_DISABLED:
6802     case KA_CLOSE:
6803       if (s->current.server->keep_alive != HTTP_NO_KEEPALIVE || (ver == HTTP_1_1)) {
6804         /* Had keep-alive */
6805         s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
6806         if (s->current.request_to == PARENT_PROXY && parent_is_proxy(s)) {
6807           heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "close", 5);
6808         } else {
6809           heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "close", 5);
6810         }
6811       }
6812       // Note: if we are 1.1, we always need to send the close
6813       //  header since persistent connections are the default
6814       break;
6815     default:
6816       ink_assert(0);
6817       break;
6818     }
6819   } else { /* websocket connection */
6820     s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
6821     s->client_info.keep_alive     = HTTP_NO_KEEPALIVE;
6822     heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
6823 
6824     if (s->is_websocket) {
6825       heads->value_set(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE, "websocket", 9);
6826     }
6827   }
6828 } /* End HttpTransact::handle_request_keep_alive_headers */
6829 
6830 //////////////////////////////////////////////////////////////////////////////
6831 //
6832 //      void HttpTransact::handle_response_keep_alive_headers(
6833 //          State* s, bool ka_on, HTTPVersion ver, HTTPHdr *heads)
6834 //
6835 //      Removes keep alive headers from origin server from <heads>
6836 //
6837 //      Adds the appropriate Transfer-Encoding: chunked header.
6838 //
6839 //      Adds the appropriate keep alive headers (if any) to <heads>
6840 //      for keep-alive state <ka_on>, and HTTP version <ver>.
6841 //
6842 //////////////////////////////////////////////////////////////////////////////
6843 void
handle_response_keep_alive_headers(State * s,HTTPVersion ver,HTTPHdr * heads)6844 HttpTransact::handle_response_keep_alive_headers(State *s, HTTPVersion ver, HTTPHdr *heads)
6845 {
6846   enum KA_Action_t {
6847     KA_UNKNOWN,
6848     KA_DISABLED,
6849     KA_CLOSE,
6850     KA_CONNECTION,
6851   };
6852   KA_Action_t ka_action = KA_UNKNOWN;
6853 
6854   ink_assert(heads->type_get() == HTTP_TYPE_RESPONSE);
6855 
6856   // Since connection headers are hop-to-hop, strip the
6857   //  the ones we received from upstream
6858   heads->field_delete(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
6859   heads->field_delete(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION);
6860 
6861   // Handle the upgrade cases
6862   if (s->is_upgrade_request && heads->status_get() == HTTP_STATUS_SWITCHING_PROTOCOL && s->source == SOURCE_HTTP_ORIGIN_SERVER) {
6863     s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
6864     if (s->is_websocket) {
6865       TxnDebug("http_trans", "transaction successfully upgraded to websockets.");
6866       // s->transparent_passthrough = true;
6867       heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE);
6868       heads->value_set(MIME_FIELD_UPGRADE, MIME_LEN_UPGRADE, "websocket", 9);
6869     }
6870 
6871     // We set this state so that we can jump to our blind forwarding state once
6872     // the response is sent to the client.
6873     s->did_upgrade_succeed = true;
6874     return;
6875   }
6876 
6877   int c_hdr_field_len;
6878   const char *c_hdr_field_str;
6879   if (s->client_info.proxy_connect_hdr) {
6880     c_hdr_field_str = MIME_FIELD_PROXY_CONNECTION;
6881     c_hdr_field_len = MIME_LEN_PROXY_CONNECTION;
6882   } else {
6883     c_hdr_field_str = MIME_FIELD_CONNECTION;
6884     c_hdr_field_len = MIME_LEN_CONNECTION;
6885   }
6886 
6887   // Check pre-conditions for keep-alive
6888   if (ver.get_major() == 0) { /* No K-A for 0.9 apps */
6889     ka_action = KA_DISABLED;
6890   } else if (heads->status_get() == HTTP_STATUS_NO_CONTENT &&
6891              ((s->source == SOURCE_HTTP_ORIGIN_SERVER && s->current.server->transfer_encoding != NO_TRANSFER_ENCODING) ||
6892               heads->get_content_length() != 0)) {
6893     // some systems hang until the connection closes when receiving a 204 regardless of the K-A headers
6894     // close if there is any body response from the origin
6895     ka_action = KA_CLOSE;
6896   } else {
6897     // Determine if we are going to send either a server-generated or
6898     // proxy-generated chunked response to the client. If we cannot
6899     // trust the content-length, we may be able to chunk the response
6900     // to the client to keep the connection alive.
6901     // Insert a Transfer-Encoding header in the response if necessary.
6902 
6903     // check that the client protocol is HTTP/1.1 and the conf allows chunking or
6904     // the client protocol doesn't support chunked transfer coding (i.e. HTTP/1.0, HTTP/2, and HTTP/3)
6905     if (s->state_machine->ua_txn && s->state_machine->ua_txn->is_chunked_encoding_supported() &&
6906         s->client_info.http_version == HTTP_1_1 && s->txn_conf->chunking_enabled == 1 &&
6907         s->state_machine->ua_txn->is_chunked_encoding_supported() &&
6908         // if we're not sending a body, don't set a chunked header regardless of server response
6909         !is_response_body_precluded(s->hdr_info.client_response.status_get(), s->method) &&
6910         // we do not need chunked encoding for internal error messages
6911         // that are sent to the client if the server response is not valid.
6912         (((s->source == SOURCE_HTTP_ORIGIN_SERVER || s->source == SOURCE_TRANSFORM) && s->hdr_info.server_response.valid() &&
6913           // if we receive a 304, we will serve the client from the
6914           // cache and thus do not need chunked encoding.
6915           s->hdr_info.server_response.status_get() != HTTP_STATUS_NOT_MODIFIED &&
6916           (s->current.server->transfer_encoding == HttpTransact::CHUNKED_ENCODING ||
6917            // we can use chunked encoding if we cannot trust the content
6918            // length (e.g. no Content-Length and Connection:close in HTTP/1.1 responses)
6919            s->hdr_info.trust_response_cl == false)) ||
6920          // handle serve from cache (read-while-write) case
6921          (s->source == SOURCE_CACHE && s->hdr_info.trust_response_cl == false) ||
6922          // any transform will potentially alter the content length. try chunking if possible
6923          (s->source == SOURCE_TRANSFORM && s->hdr_info.trust_response_cl == false))) {
6924       s->client_info.receive_chunked_response = true;
6925       heads->value_append(MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING, HTTP_VALUE_CHUNKED, HTTP_LEN_CHUNKED, true);
6926     } else {
6927       s->client_info.receive_chunked_response = false;
6928     }
6929 
6930     // make sure no content length header is send when transfer encoding is chunked
6931     if (s->client_info.receive_chunked_response) {
6932       s->hdr_info.trust_response_cl = false;
6933 
6934       // And delete the header if it's already been added...
6935       heads->field_delete(MIME_FIELD_CONTENT_LENGTH, MIME_LEN_CONTENT_LENGTH);
6936     }
6937 
6938     // Close the connection if client_info is not keep-alive.
6939     // Otherwise, if we cannot trust the content length, we will close the connection
6940     // unless we are going to use chunked encoding on HTTP/1.1 or the client issued a PUSH request
6941     if (s->client_info.keep_alive != HTTP_KEEPALIVE) {
6942       ka_action = KA_DISABLED;
6943     } else if (s->hdr_info.trust_response_cl == false && s->state_machine->ua_txn &&
6944                s->state_machine->ua_txn->is_chunked_encoding_supported() &&
6945                !(s->client_info.receive_chunked_response == true ||
6946                  (s->method == HTTP_WKSIDX_PUSH && s->client_info.keep_alive == HTTP_KEEPALIVE))) {
6947       ka_action = KA_CLOSE;
6948     } else {
6949       ka_action = KA_CONNECTION;
6950     }
6951   }
6952 
6953   ink_assert(ka_action != KA_UNKNOWN);
6954 
6955   // Insert K-A headers as necessary
6956   switch (ka_action) {
6957   case KA_CONNECTION:
6958     ink_assert(s->client_info.keep_alive != HTTP_NO_KEEPALIVE);
6959     // This is a hack, we send the keep-alive header for both 1.0
6960     // and 1.1, to be "compatible" with Akamai.
6961     heads->value_set(c_hdr_field_str, c_hdr_field_len, "keep-alive", 10);
6962     // NOTE: if the version is 1.1 we don't need to do
6963     //  anything since keep-alive is assumed
6964     break;
6965   case KA_CLOSE:
6966   case KA_DISABLED:
6967     if (s->client_info.keep_alive != HTTP_NO_KEEPALIVE || (ver == HTTP_1_1)) {
6968       heads->value_set(c_hdr_field_str, c_hdr_field_len, "close", 5);
6969       s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
6970     }
6971     // Note: if we are 1.1, we always need to send the close
6972     //  header since persistent connections are the default
6973     break;
6974   default:
6975     ink_assert(0);
6976     break;
6977   }
6978 } /* End HttpTransact::handle_response_keep_alive_headers */
6979 
6980 bool
delete_all_document_alternates_and_return(State * s,bool cache_hit)6981 HttpTransact::delete_all_document_alternates_and_return(State *s, bool cache_hit)
6982 {
6983   if (cache_hit == true) {
6984     // ToDo: Should support other levels of cache hits here, but the cache does not support it (yet)
6985     if (SQUID_HIT_RAM == s->cache_info.hit_miss_code) {
6986       SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_RAM_CACHE_FRESH);
6987     } else {
6988       SET_VIA_STRING(VIA_CACHE_RESULT, VIA_IN_CACHE_FRESH);
6989     }
6990   } else {
6991     SET_VIA_STRING(VIA_DETAIL_CACHE_LOOKUP, VIA_DETAIL_MISS_NOT_CACHED);
6992   }
6993 
6994   if ((s->method != HTTP_WKSIDX_GET) && (s->method == HTTP_WKSIDX_DELETE || s->method == HTTP_WKSIDX_PURGE)) {
6995     bool valid_max_forwards;
6996     int max_forwards          = -1;
6997     MIMEField *max_forwards_f = s->hdr_info.client_request.field_find(MIME_FIELD_MAX_FORWARDS, MIME_LEN_MAX_FORWARDS);
6998 
6999     // Check the max forwards value for DELETE
7000     if (max_forwards_f) {
7001       valid_max_forwards = true;
7002       max_forwards       = max_forwards_f->value_get_int();
7003     } else {
7004       valid_max_forwards = false;
7005     }
7006 
7007     if (s->method == HTTP_WKSIDX_PURGE || (valid_max_forwards && max_forwards <= 0)) {
7008       TxnDebug("http_trans",
7009                "[delete_all_document_alternates_and_return] "
7010                "DELETE with Max-Forwards: %d",
7011                max_forwards);
7012 
7013       SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
7014 
7015       // allow deletes to be pipelined
7016       //   We want to allow keep-alive so trust the response content
7017       //    length.  There really isn't one and the SM will add the
7018       //    zero content length when setting up the transfer
7019       s->hdr_info.trust_response_cl = true;
7020       build_response(s, &s->hdr_info.client_response, s->client_info.http_version,
7021                      (cache_hit == true) ? HTTP_STATUS_OK : HTTP_STATUS_NOT_FOUND);
7022 
7023       return true;
7024     } else {
7025       if (valid_max_forwards) {
7026         --max_forwards;
7027         TxnDebug("http_trans",
7028                  "[delete_all_document_alternates_and_return] "
7029                  "Decrementing max_forwards to %d",
7030                  max_forwards);
7031         s->hdr_info.client_request.value_set_int(MIME_FIELD_MAX_FORWARDS, MIME_LEN_MAX_FORWARDS, max_forwards);
7032       }
7033     }
7034   }
7035 
7036   return false;
7037 }
7038 
7039 bool
does_client_request_permit_cached_response(const OverridableHttpConfigParams * p,CacheControlResult * c,HTTPHdr * h,char * via_string)7040 HttpTransact::does_client_request_permit_cached_response(const OverridableHttpConfigParams *p, CacheControlResult *c, HTTPHdr *h,
7041                                                          char *via_string)
7042 {
7043   ////////////////////////////////////////////////////////////////////////
7044   // If aren't ignoring client's cache directives, meet client's wishes //
7045   ////////////////////////////////////////////////////////////////////////
7046 
7047   if (!c->ignore_client_no_cache) {
7048     if (h->is_cache_control_set(HTTP_VALUE_NO_CACHE)) {
7049       return (false);
7050     }
7051     if (h->is_pragma_no_cache_set()) {
7052       // if we are going to send out an ims anyway,
7053       // no need to flag this as a no-cache.
7054       if (!p->cache_ims_on_client_no_cache) {
7055         via_string[VIA_CLIENT_REQUEST] = VIA_CLIENT_NO_CACHE;
7056       }
7057       return (false);
7058     }
7059   }
7060 
7061   return (true);
7062 }
7063 
7064 bool
does_client_request_permit_dns_caching(CacheControlResult * c,HTTPHdr * h)7065 HttpTransact::does_client_request_permit_dns_caching(CacheControlResult *c, HTTPHdr *h)
7066 {
7067   if (h->is_pragma_no_cache_set() && h->is_cache_control_set(HTTP_VALUE_NO_CACHE) && (!c->ignore_client_no_cache)) {
7068     return (false);
7069   }
7070   return (true);
7071 }
7072 
7073 bool
does_client_request_permit_storing(CacheControlResult * c,HTTPHdr * h)7074 HttpTransact::does_client_request_permit_storing(CacheControlResult *c, HTTPHdr *h)
7075 {
7076   ////////////////////////////////////////////////////////////////////////
7077   // If aren't ignoring client's cache directives, meet client's wishes //
7078   ////////////////////////////////////////////////////////////////////////
7079   if (!c->ignore_client_no_cache) {
7080     if (h->is_cache_control_set(HTTP_VALUE_NO_STORE)) {
7081       return (false);
7082     }
7083   }
7084 
7085   return (true);
7086 }
7087 
7088 int
get_max_age(HTTPHdr * response)7089 HttpTransact::get_max_age(HTTPHdr *response)
7090 {
7091   int max_age      = -1;
7092   uint32_t cc_mask = response->get_cooked_cc_mask();
7093 
7094   bool max_age_is_present = false;
7095   if (cc_mask & MIME_COOKED_MASK_CC_S_MAXAGE) {
7096     // Precedence to s-maxage
7097     max_age            = static_cast<int>(response->get_cooked_cc_s_maxage());
7098     max_age_is_present = true;
7099   } else if (cc_mask & MIME_COOKED_MASK_CC_MAX_AGE) {
7100     // If s-maxage isn't set, try max-age
7101     max_age            = static_cast<int>(response->get_cooked_cc_max_age());
7102     max_age_is_present = true;
7103   }
7104 
7105   // Negative max-age values:
7106   //
7107   // Per RFC 7234, section-1.2.1, max-age values should be a non-negative
7108   // value. If it is negative, therefore, the value is invalid.  Per RFC 7234,
7109   // section-4.2.1, invalid freshness specifications should be considered
7110   // stale.
7111   //
7112   // Negative return values from this function are used to indicate that the
7113   // max-age value was not present, resulting in a default value likely being
7114   // used. If the max-age is negative, therefore, we return 0 to indicate to
7115   // the caller that the max-age directive was present and indicates that the
7116   // object should be considered stale.
7117   if (max_age_is_present && max_age < 0) {
7118     max_age = 0;
7119   }
7120 
7121   return max_age;
7122 }
7123 
7124 int
calculate_document_freshness_limit(State * s,HTTPHdr * response,time_t response_date,bool * heuristic)7125 HttpTransact::calculate_document_freshness_limit(State *s, HTTPHdr *response, time_t response_date, bool *heuristic)
7126 {
7127   bool expires_set, date_set, last_modified_set;
7128   time_t date_value, expires_value, last_modified_value;
7129   MgmtInt min_freshness_bounds, max_freshness_bounds;
7130   int freshness_limit = 0;
7131   int max_age         = get_max_age(response);
7132 
7133   *heuristic = false;
7134 
7135   if (max_age >= 0) {
7136     freshness_limit = std::min(std::max(0, max_age), static_cast<int>(s->txn_conf->cache_guaranteed_max_lifetime));
7137     TxnDebug("http_match", "calculate_document_freshness_limit --- freshness_limit = %d", freshness_limit);
7138   } else {
7139     date_set = last_modified_set = false;
7140 
7141     if (s->plugin_set_expire_time != UNDEFINED_TIME) {
7142       expires_set   = true;
7143       expires_value = s->plugin_set_expire_time;
7144     } else {
7145       expires_set   = (response->presence(MIME_PRESENCE_EXPIRES) != 0);
7146       expires_value = response->get_expires();
7147     }
7148 
7149     date_value = response_date;
7150     if (date_value > 0) {
7151       date_set = true;
7152     } else {
7153       date_value = s->request_sent_time;
7154       TxnDebug("http_match",
7155                "calculate_document_freshness_limit --- Expires header = %" PRId64 " no date, using sent time %" PRId64,
7156                (int64_t)expires_value, (int64_t)date_value);
7157     }
7158     ink_assert(date_value > 0);
7159 
7160     // Getting the cache_sm object
7161     HttpCacheSM &cache_sm = s->state_machine->get_cache_sm();
7162 
7163     // Bypassing if loop to set freshness_limit to heuristic value
7164     if (expires_set && !cache_sm.is_readwhilewrite_inprogress()) {
7165       if (expires_value == UNDEFINED_TIME || expires_value <= date_value) {
7166         expires_value = date_value;
7167         TxnDebug("http_match", "calculate_document_freshness_limit --- no expires, using date %" PRId64, (int64_t)expires_value);
7168       }
7169       freshness_limit = static_cast<int>(expires_value - date_value);
7170 
7171       TxnDebug("http_match", "calculate_document_freshness_limit --- Expires: %" PRId64 ", Date: %" PRId64 ", freshness_limit = %d",
7172                (int64_t)expires_value, (int64_t)date_value, freshness_limit);
7173 
7174       freshness_limit = std::min(std::max(0, freshness_limit), static_cast<int>(s->txn_conf->cache_guaranteed_max_lifetime));
7175     } else {
7176       last_modified_value = 0;
7177       if (response->presence(MIME_PRESENCE_LAST_MODIFIED)) {
7178         last_modified_set   = true;
7179         last_modified_value = response->get_last_modified();
7180         TxnDebug("http_match", "calculate_document_freshness_limit --- Last Modified header = %" PRId64,
7181                  (int64_t)last_modified_value);
7182 
7183         if (last_modified_value == UNDEFINED_TIME) {
7184           last_modified_set = false;
7185         } else if (last_modified_value > date_value) {
7186           last_modified_value = date_value;
7187           TxnDebug("http_match", "calculate_document_freshness_limit --- no last-modified, using sent time %" PRId64,
7188                    (int64_t)last_modified_value);
7189         }
7190       }
7191 
7192       *heuristic = true;
7193       if (date_set && last_modified_set) {
7194         MgmtFloat f = s->txn_conf->cache_heuristic_lm_factor;
7195         ink_assert((f >= 0.0) && (f <= 1.0));
7196         ink_time_t time_since_last_modify = date_value - last_modified_value;
7197         int h_freshness                   = static_cast<int>(time_since_last_modify * f);
7198         freshness_limit                   = std::max(h_freshness, 0);
7199         TxnDebug("http_match",
7200                  "calculate_document_freshness_limit --- heuristic: date=%" PRId64 ", lm=%" PRId64
7201                  ", time_since_last_modify=%" PRId64 ", f=%g, freshness_limit = %d",
7202                  (int64_t)date_value, (int64_t)last_modified_value, (int64_t)time_since_last_modify, f, freshness_limit);
7203       } else {
7204         freshness_limit = s->txn_conf->cache_heuristic_min_lifetime;
7205         TxnDebug("http_match", "calculate_document_freshness_limit --- heuristic: freshness_limit = %d", freshness_limit);
7206       }
7207     }
7208   }
7209 
7210   // The freshness limit must always fall within the min and max guaranteed bounds.
7211   min_freshness_bounds = std::max(static_cast<MgmtInt>(0), s->txn_conf->cache_guaranteed_min_lifetime);
7212   max_freshness_bounds = s->txn_conf->cache_guaranteed_max_lifetime;
7213 
7214   // Heuristic freshness can be more strict.
7215   if (*heuristic) {
7216     min_freshness_bounds = std::max(min_freshness_bounds, s->txn_conf->cache_heuristic_min_lifetime);
7217     max_freshness_bounds = std::min(max_freshness_bounds, s->txn_conf->cache_heuristic_max_lifetime);
7218   }
7219   // Now clip the freshness limit.
7220   if (freshness_limit > max_freshness_bounds) {
7221     freshness_limit = max_freshness_bounds;
7222   }
7223   if (freshness_limit < min_freshness_bounds) {
7224     freshness_limit = min_freshness_bounds;
7225   }
7226 
7227   TxnDebug("http_match", "calculate_document_freshness_limit --- final freshness_limit = %d", freshness_limit);
7228 
7229   return (freshness_limit);
7230 }
7231 
7232 //////////////////////////////////////////////////////////////////////////////
7233 //
7234 //
7235 //      This function takes the request and response headers for a cached
7236 //      object, and the current HTTP parameters, and decides if the object
7237 //      is still "fresh enough" to serve.  One of the following values
7238 //      is returned:
7239 //
7240 //          FRESHNESS_FRESH             Fresh enough, serve it
7241 //          FRESHNESS_WARNING           Stale but client says it's okay
7242 //          FRESHNESS_STALE             Too stale, don't use
7243 //
7244 //////////////////////////////////////////////////////////////////////////////
7245 HttpTransact::Freshness_t
what_is_document_freshness(State * s,HTTPHdr * client_request,HTTPHdr * cached_obj_response)7246 HttpTransact::what_is_document_freshness(State *s, HTTPHdr *client_request, HTTPHdr *cached_obj_response)
7247 {
7248   bool heuristic, do_revalidate = false;
7249   int age_limit;
7250   int fresh_limit;
7251   ink_time_t current_age, response_date;
7252   uint32_t cc_mask, cooked_cc_mask;
7253   uint32_t os_specifies_revalidate;
7254 
7255   if (s->cache_open_write_fail_action & CACHE_WL_FAIL_ACTION_STALE_ON_REVALIDATE) {
7256     if (is_stale_cache_response_returnable(s)) {
7257       TxnDebug("http_match", "[what_is_document_freshness] cache_serve_stale_on_write_lock_fail, return FRESH");
7258       return (FRESHNESS_FRESH);
7259     }
7260   }
7261 
7262   //////////////////////////////////////////////////////
7263   // If config file has a ttl-in-cache field set,     //
7264   // it has priority over any other http headers and  //
7265   // other configuration parameters.                  //
7266   //////////////////////////////////////////////////////
7267   if (s->cache_control.ttl_in_cache > 0) {
7268     // what matters if ttl is set is not the age of the document
7269     // but for how long it has been stored in the cache (resident time)
7270     int resident_time = s->current.now - s->response_received_time;
7271 
7272     TxnDebug("http_match", "[..._document_freshness] ttl-in-cache = %d, resident time = %d", s->cache_control.ttl_in_cache,
7273              resident_time);
7274     if (resident_time > s->cache_control.ttl_in_cache) {
7275       return (FRESHNESS_STALE);
7276     } else {
7277       return (FRESHNESS_FRESH);
7278     }
7279   }
7280 
7281   cooked_cc_mask          = cached_obj_response->get_cooked_cc_mask();
7282   os_specifies_revalidate = cooked_cc_mask & (MIME_COOKED_MASK_CC_MUST_REVALIDATE | MIME_COOKED_MASK_CC_PROXY_REVALIDATE);
7283   cc_mask                 = MIME_COOKED_MASK_CC_NEED_REVALIDATE_ONCE;
7284 
7285   // Check to see if the server forces revalidation
7286 
7287   if ((cooked_cc_mask & cc_mask) && s->cache_control.revalidate_after <= 0) {
7288     TxnDebug("http_match", "[what_is_document_freshness] document stale due to "
7289                            "server must-revalidate");
7290     return FRESHNESS_STALE;
7291   }
7292 
7293   response_date = cached_obj_response->get_date();
7294   fresh_limit   = calculate_document_freshness_limit(s, cached_obj_response, response_date, &heuristic);
7295   ink_assert(fresh_limit >= 0);
7296 
7297   current_age = HttpTransactHeaders::calculate_document_age(s->request_sent_time, s->response_received_time, cached_obj_response,
7298                                                             response_date, s->current.now);
7299 
7300   // First check overflow status
7301   // Second if current_age is under the max, use the smaller value
7302   // Finally we take the max of current age or guaranteed max, this ensures it will
7303   // age out properly, otherwise a doc will never expire if guaranteed < document max-age
7304   if (current_age < 0) {
7305     current_age = s->txn_conf->cache_guaranteed_max_lifetime;
7306   } else if (current_age < s->txn_conf->cache_guaranteed_max_lifetime) {
7307     current_age = std::min(static_cast<time_t>(s->txn_conf->cache_guaranteed_max_lifetime), current_age);
7308   } else {
7309     current_age = std::max(static_cast<time_t>(s->txn_conf->cache_guaranteed_max_lifetime), current_age);
7310   }
7311 
7312   TxnDebug("http_match", "[what_is_document_freshness] fresh_limit:  %d  current_age: %" PRId64, fresh_limit, (int64_t)current_age);
7313 
7314   ink_assert(client_request == &s->hdr_info.client_request);
7315 
7316   if (auto scheme = client_request->url_get()->scheme_get_wksidx(); scheme == URL_WKSIDX_HTTP || scheme == URL_WKSIDX_HTTPS) {
7317     switch (s->txn_conf->cache_when_to_revalidate) {
7318     case 0: // Use cache directives or heuristic (the default value)
7319       // Nothing to do here
7320       break;
7321     case 1: // Stale if heuristic
7322       if (heuristic) {
7323         TxnDebug("http_match", "[what_is_document_freshness] config requires FRESHNESS_STALE because heuristic calculation");
7324         return (FRESHNESS_STALE);
7325       }
7326       break;
7327     case 2: // Always stale
7328       TxnDebug("http_match", "[what_is_document_freshness] config "
7329                              "specifies always FRESHNESS_STALE");
7330       return (FRESHNESS_STALE);
7331     case 3: // Never stale
7332       TxnDebug("http_match", "[what_is_document_freshness] config "
7333                              "specifies always FRESHNESS_FRESH");
7334       return (FRESHNESS_FRESH);
7335     case 4: // Stale if IMS
7336       if (client_request->presence(MIME_PRESENCE_IF_MODIFIED_SINCE)) {
7337         TxnDebug("http_match", "[what_is_document_freshness] config "
7338                                "specifies FRESHNESS_STALE if IMS present");
7339         return (FRESHNESS_STALE);
7340       }
7341     default: // Bad config, ignore
7342       break;
7343     }
7344   }
7345   //////////////////////////////////////////////////////////////////////
7346   // the normal expiration policy allows serving a doc from cache if: //
7347   //     basic:          (current_age <= fresh_limit)                 //
7348   //                                                                  //
7349   // this can be modified by client Cache-Control headers:            //
7350   //     max-age:        (current_age <= max_age)                     //
7351   //     min-fresh:      (current_age <= fresh_limit - min_fresh)     //
7352   //     max-stale:      (current_age <= fresh_limit + max_stale)     //
7353   //////////////////////////////////////////////////////////////////////
7354   age_limit = fresh_limit; // basic constraint
7355   TxnDebug("http_match", "[..._document_freshness] initial age limit: %d", age_limit);
7356 
7357   cooked_cc_mask = client_request->get_cooked_cc_mask();
7358   cc_mask        = (MIME_COOKED_MASK_CC_MAX_STALE | MIME_COOKED_MASK_CC_MIN_FRESH | MIME_COOKED_MASK_CC_MAX_AGE);
7359   if (cooked_cc_mask & cc_mask) {
7360     /////////////////////////////////////////////////
7361     // if max-stale set, relax the freshness limit //
7362     /////////////////////////////////////////////////
7363     if (cooked_cc_mask & MIME_COOKED_MASK_CC_MAX_STALE) {
7364       if (os_specifies_revalidate) {
7365         TxnDebug("http_match", "[...document_freshness] OS specifies revalidation; "
7366                                "ignoring client's max-stale request...");
7367       } else {
7368         int max_stale_val = client_request->get_cooked_cc_max_stale();
7369 
7370         if (max_stale_val != INT_MAX) {
7371           age_limit += max_stale_val;
7372         } else {
7373           age_limit = max_stale_val;
7374         }
7375         TxnDebug("http_match", "[..._document_freshness] max-stale set, age limit: %d", age_limit);
7376       }
7377     }
7378     /////////////////////////////////////////////////////
7379     // if min-fresh set, constrain the freshness limit //
7380     /////////////////////////////////////////////////////
7381     if (cooked_cc_mask & MIME_COOKED_MASK_CC_MIN_FRESH) {
7382       age_limit = std::min(age_limit, fresh_limit - client_request->get_cooked_cc_min_fresh());
7383       TxnDebug("http_match", "[..._document_freshness] min_fresh set, age limit: %d", age_limit);
7384     }
7385     ///////////////////////////////////////////////////
7386     // if max-age set, constrain the freshness limit //
7387     ///////////////////////////////////////////////////
7388     if (!s->cache_control.ignore_client_cc_max_age && (cooked_cc_mask & MIME_COOKED_MASK_CC_MAX_AGE)) {
7389       int age_val = client_request->get_cooked_cc_max_age();
7390       if (age_val == 0) {
7391         do_revalidate = true;
7392       }
7393       age_limit = std::min(age_limit, age_val);
7394       TxnDebug("http_match", "[..._document_freshness] min_fresh set, age limit: %d", age_limit);
7395     }
7396   }
7397   /////////////////////////////////////////////////////////
7398   // config file may have a "revalidate_after" field set //
7399   /////////////////////////////////////////////////////////
7400   // bug fix changed ">0" to ">=0"
7401   if (s->cache_control.revalidate_after >= 0) {
7402     // if we want the minimum of the already-computed age_limit and revalidate_after
7403     //      age_limit = mine(age_limit, s->cache_control.revalidate_after);
7404 
7405     // if instead the revalidate_after overrides all other variables
7406     age_limit = s->cache_control.revalidate_after;
7407 
7408     TxnDebug("http_match", "[..._document_freshness] revalidate_after set, age limit: %d", age_limit);
7409   }
7410 
7411   TxnDebug("http_match", "document_freshness --- current_age = %" PRId64, (int64_t)current_age);
7412   TxnDebug("http_match", "document_freshness --- age_limit   = %d", age_limit);
7413   TxnDebug("http_match", "document_freshness --- fresh_limit = %d", fresh_limit);
7414   TxnDebug("http_seq", "document_freshness --- current_age = %" PRId64, (int64_t)current_age);
7415   TxnDebug("http_seq", "document_freshness --- age_limit   = %d", age_limit);
7416   TxnDebug("http_seq", "document_freshness --- fresh_limit = %d", fresh_limit);
7417   ///////////////////////////////////////////
7418   // now, see if the age is "fresh enough" //
7419   ///////////////////////////////////////////
7420 
7421   if (do_revalidate || !age_limit || current_age > age_limit) { // client-modified limit
7422     TxnDebug("http_match", "[..._document_freshness] document needs revalidate/too old; "
7423                            "returning FRESHNESS_STALE");
7424     return (FRESHNESS_STALE);
7425   } else if (current_age > fresh_limit) { // original limit
7426     if (os_specifies_revalidate) {
7427       TxnDebug("http_match", "[..._document_freshness] document is stale and OS specifies revalidation; "
7428                              "returning FRESHNESS_STALE");
7429       return (FRESHNESS_STALE);
7430     }
7431     TxnDebug("http_match", "[..._document_freshness] document is stale but no revalidation explicitly required; "
7432                            "returning FRESHNESS_WARNING");
7433     return (FRESHNESS_WARNING);
7434   } else {
7435     TxnDebug("http_match", "[..._document_freshness] document is fresh; returning FRESHNESS_FRESH");
7436     return (FRESHNESS_FRESH);
7437   }
7438 }
7439 
7440 //////////////////////////////////////////////////////////////////////////////
7441 //
7442 //      HttpTransact::Authentication_t HttpTransact::AuthenticationNeeded(
7443 //          const OverridableHttpConfigParams *p,
7444 //          HTTPHdr *client_request,
7445 //          HTTPHdr *obj_response)
7446 //
7447 //      This function takes the current client request, and the headers
7448 //      from a potential response (e.g. from cache or proxy), and decides
7449 //      if the object needs to be authenticated with the origin server,
7450 //      before it can be sent to the client.
7451 //
7452 //      The return value describes the authentication process needed.  In
7453 //      this function, three results are possible:
7454 //
7455 //          AUTHENTICATION_SUCCESS              Can serve object directly
7456 //          AUTHENTICATION_MUST_REVALIDATE      Must revalidate with server
7457 //          AUTHENTICATION_MUST_PROXY           Must not serve object
7458 //
7459 //////////////////////////////////////////////////////////////////////////////
7460 
7461 HttpTransact::Authentication_t
AuthenticationNeeded(const OverridableHttpConfigParams * p,HTTPHdr * client_request,HTTPHdr * obj_response)7462 HttpTransact::AuthenticationNeeded(const OverridableHttpConfigParams *p, HTTPHdr *client_request, HTTPHdr *obj_response)
7463 {
7464   ///////////////////////////////////////////////////////////////////////
7465   // from RFC2068, sec 14.8, if a client request has the Authorization //
7466   // header set, we can't serve it unless the response is public, or   //
7467   // if it has a Cache-Control revalidate flag, and we do revalidate.  //
7468   ///////////////////////////////////////////////////////////////////////
7469 
7470   if ((p->cache_ignore_auth == 0) && client_request->presence(MIME_PRESENCE_AUTHORIZATION)) {
7471     if (obj_response->is_cache_control_set(HTTP_VALUE_MUST_REVALIDATE) ||
7472         obj_response->is_cache_control_set(HTTP_VALUE_PROXY_REVALIDATE)) {
7473       return AUTHENTICATION_MUST_REVALIDATE;
7474     } else if (obj_response->is_cache_control_set(HTTP_VALUE_PROXY_REVALIDATE)) {
7475       return AUTHENTICATION_MUST_REVALIDATE;
7476     } else if (obj_response->is_cache_control_set(HTTP_VALUE_PUBLIC)) {
7477       return AUTHENTICATION_SUCCESS;
7478     } else {
7479       if (obj_response->field_find("@WWW-Auth", 9) && client_request->method_get_wksidx() == HTTP_WKSIDX_GET) {
7480         return AUTHENTICATION_CACHE_AUTH;
7481       }
7482       return AUTHENTICATION_MUST_PROXY;
7483     }
7484   }
7485 
7486   if (obj_response->field_find("@WWW-Auth", 9) && client_request->method_get_wksidx() == HTTP_WKSIDX_GET) {
7487     return AUTHENTICATION_CACHE_AUTH;
7488   }
7489 
7490   return (AUTHENTICATION_SUCCESS);
7491 }
7492 
7493 void
handle_parent_died(State * s)7494 HttpTransact::handle_parent_died(State *s)
7495 {
7496   ink_assert(s->parent_result.result == PARENT_FAIL);
7497 
7498   switch (s->current.state) {
7499   case OUTBOUND_CONGESTION:
7500     build_error_response(s, HTTP_STATUS_SERVICE_UNAVAILABLE, "Next Hop Congested", "congestion#retryAfter");
7501     break;
7502   case INACTIVE_TIMEOUT:
7503     build_error_response(s, HTTP_STATUS_GATEWAY_TIMEOUT, "Next Hop Timeout", "timeout#inactivity");
7504     break;
7505   case ACTIVE_TIMEOUT:
7506     build_error_response(s, HTTP_STATUS_GATEWAY_TIMEOUT, "Next Hop Timeout", "timeout#activity");
7507     break;
7508   default:
7509     build_error_response(s, HTTP_STATUS_BAD_GATEWAY, "Next Hop Connection Failed", "connect");
7510   }
7511   TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
7512 }
7513 
7514 void
handle_server_died(State * s)7515 HttpTransact::handle_server_died(State *s)
7516 {
7517   const char *reason    = nullptr;
7518   const char *body_type = "UNKNOWN";
7519   HTTPStatus status     = HTTP_STATUS_BAD_GATEWAY;
7520 
7521   ////////////////////////////////////////////////////////
7522   // FIX: all the body types below need to be filled in //
7523   ////////////////////////////////////////////////////////
7524 
7525   switch (s->current.state) {
7526   case CONNECTION_ALIVE: /* died while alive for unknown reason */
7527     ink_release_assert(s->hdr_info.response_error != NO_RESPONSE_HEADER_ERROR);
7528     status    = HTTP_STATUS_BAD_GATEWAY;
7529     reason    = "Unknown Error";
7530     body_type = "response#bad_response";
7531     break;
7532   case CONNECTION_ERROR:
7533     status    = HTTP_STATUS_BAD_GATEWAY;
7534     reason    = get_error_string(s->cause_of_death_errno == 0 ? -ENET_CONNECT_FAILED : s->cause_of_death_errno);
7535     body_type = "connect#failed_connect";
7536     break;
7537   case OPEN_RAW_ERROR:
7538     status    = HTTP_STATUS_BAD_GATEWAY;
7539     reason    = "Tunnel Connection Failed";
7540     body_type = "connect#failed_connect";
7541     break;
7542   case CONNECTION_CLOSED:
7543     status    = HTTP_STATUS_BAD_GATEWAY;
7544     reason    = "Server Hangup";
7545     body_type = "connect#hangup";
7546     break;
7547   case ACTIVE_TIMEOUT:
7548     if (s->api_txn_active_timeout_value != -1) {
7549       TxnDebug("http_timeout", "Maximum active time of %d msec exceeded", s->api_txn_active_timeout_value);
7550     }
7551     status    = HTTP_STATUS_GATEWAY_TIMEOUT;
7552     reason    = "Maximum Transaction Time Exceeded";
7553     body_type = "timeout#activity";
7554     break;
7555   case INACTIVE_TIMEOUT:
7556     if (s->api_txn_connect_timeout_value != -1) {
7557       TxnDebug("http_timeout", "Maximum connect time of %d msec exceeded", s->api_txn_connect_timeout_value);
7558     }
7559     status    = HTTP_STATUS_GATEWAY_TIMEOUT;
7560     reason    = "Connection Timed Out";
7561     body_type = "timeout#inactivity";
7562     break;
7563   case PARSE_ERROR:
7564   case BAD_INCOMING_RESPONSE:
7565     status    = HTTP_STATUS_BAD_GATEWAY;
7566     reason    = "Invalid HTTP Response";
7567     body_type = "response#bad_response";
7568     break;
7569   case OUTBOUND_CONGESTION:
7570     status                     = HTTP_STATUS_SERVICE_UNAVAILABLE;
7571     reason                     = "Origin server congested";
7572     body_type                  = "congestion#retryAfter";
7573     s->hdr_info.response_error = TOTAL_RESPONSE_ERROR_TYPES;
7574     break;
7575   case STATE_UNDEFINED:
7576   case TRANSACTION_COMPLETE:
7577   default: /* unknown death */
7578     ink_release_assert(!"[handle_server_died] Unreasonable state - not dead, shouldn't be here");
7579     status    = HTTP_STATUS_BAD_GATEWAY;
7580     reason    = nullptr;
7581     body_type = "response#bad_response";
7582     break;
7583   }
7584 
7585   ////////////////////////////////////////////////////////
7586   // FIX: comment stuff above and below here, not clear //
7587   ////////////////////////////////////////////////////////
7588 
7589   switch (s->hdr_info.response_error) {
7590   case NON_EXISTANT_RESPONSE_HEADER:
7591     status    = HTTP_STATUS_BAD_GATEWAY;
7592     reason    = "No Response Header From Server";
7593     body_type = "response#bad_response";
7594     break;
7595   case MISSING_REASON_PHRASE:
7596   case NO_RESPONSE_HEADER_ERROR:
7597   case NOT_A_RESPONSE_HEADER:
7598 #ifdef REALLY_NEED_TO_CHECK_DATE_VALIDITY
7599   case BOGUS_OR_NO_DATE_IN_RESPONSE:
7600 #endif
7601     status    = HTTP_STATUS_BAD_GATEWAY;
7602     reason    = "Malformed Server Response";
7603     body_type = "response#bad_response";
7604     break;
7605   case MISSING_STATUS_CODE:
7606     status    = HTTP_STATUS_BAD_GATEWAY;
7607     reason    = "Malformed Server Response Status";
7608     body_type = "response#bad_response";
7609     break;
7610   default:
7611     break;
7612   }
7613 
7614   if (reason == nullptr) {
7615     status    = HTTP_STATUS_BAD_GATEWAY;
7616     reason    = "Server Connection Failed";
7617     body_type = "connect#failed_connect";
7618   }
7619 
7620   build_error_response(s, status, reason, body_type);
7621 
7622   return;
7623 }
7624 
7625 // return true if the response to the given request is likely cacheable
7626 // This function is called by build_request() to determine if the conditional
7627 // headers should be removed from server request.
7628 bool
is_request_likely_cacheable(State * s,HTTPHdr * request)7629 HttpTransact::is_request_likely_cacheable(State *s, HTTPHdr *request)
7630 {
7631   if ((s->method == HTTP_WKSIDX_GET || s->api_req_cacheable) && !s->api_server_response_no_store &&
7632       !request->presence(MIME_PRESENCE_AUTHORIZATION) &&
7633       (!request->presence(MIME_PRESENCE_RANGE) || s->txn_conf->cache_range_write)) {
7634     return true;
7635   }
7636   return false;
7637 }
7638 
7639 bool
is_fresh_cache_hit(CacheLookupResult_t r)7640 HttpTransact::is_fresh_cache_hit(CacheLookupResult_t r)
7641 {
7642   return (r == CACHE_LOOKUP_HIT_FRESH || r == CACHE_LOOKUP_HIT_WARNING);
7643 }
7644 
7645 bool
is_cache_hit(CacheLookupResult_t r)7646 HttpTransact::is_cache_hit(CacheLookupResult_t r)
7647 {
7648   return (is_fresh_cache_hit(r) || r == CACHE_LOOKUP_HIT_STALE);
7649 }
7650 
7651 void
build_request(State * s,HTTPHdr * base_request,HTTPHdr * outgoing_request,HTTPVersion outgoing_version)7652 HttpTransact::build_request(State *s, HTTPHdr *base_request, HTTPHdr *outgoing_request, HTTPVersion outgoing_version)
7653 {
7654   // this part is to restore the original URL in case, multiple cache
7655   // lookups have happened - client request has been changed as the result
7656   //
7657   // notice that currently, based_request IS client_request
7658   if (base_request == &s->hdr_info.client_request) {
7659     if (!s->redirect_info.redirect_in_process) {
7660       // this is for multiple cache lookup
7661       URL *o_url = &s->cache_info.original_url;
7662 
7663       if (o_url->valid()) {
7664         base_request->url_get()->copy(o_url);
7665       }
7666     }
7667 
7668     // Perform any configured normalization (including per-remap-rule configuration overrides) of the Accept-Encoding header
7669     // field (if any).  This has to be done in the request from the client, for the benefit of the gzip plugin.
7670     //
7671     HttpTransactHeaders::normalize_accept_encoding(s->txn_conf, base_request);
7672   }
7673 
7674   HttpTransactHeaders::copy_header_fields(base_request, outgoing_request, s->txn_conf->fwd_proxy_auth_to_parent);
7675   add_client_ip_to_outgoing_request(s, outgoing_request);
7676   HttpTransactHeaders::add_forwarded_field_to_request(s, outgoing_request);
7677   HttpTransactHeaders::remove_privacy_headers_from_request(s->http_config_param, s->txn_conf, outgoing_request);
7678   HttpTransactHeaders::add_global_user_agent_header_to_request(s->txn_conf, outgoing_request);
7679   handle_request_keep_alive_headers(s, outgoing_version, outgoing_request);
7680 
7681   if (s->next_hop_scheme < 0) {
7682     s->next_hop_scheme = URL_WKSIDX_HTTP;
7683   }
7684   if (s->orig_scheme < 0) {
7685     s->orig_scheme = URL_WKSIDX_HTTP;
7686   }
7687 
7688   if (s->txn_conf->insert_request_via_string) {
7689     HttpTransactHeaders::insert_via_header_in_request(s, outgoing_request);
7690   }
7691 
7692   // We build 1.1 request header and then convert as necessary to
7693   //  the appropriate version in HttpTransact::build_request
7694   outgoing_request->version_set(HTTP_1_1);
7695 
7696   // Make sure our request version is defined
7697   ink_assert(outgoing_version != HTTP_0_9);
7698 
7699   // HttpTransactHeaders::convert_request(outgoing_version, outgoing_request); // commented out this idea
7700 
7701   URL *url = outgoing_request->url_get();
7702   // Remove fragment from upstream URL
7703   url->fragment_set(nullptr, 0);
7704 
7705   // Check whether a Host header field is missing from a 1.0 or 1.1 request.
7706   if (outgoing_version != HTTP_0_9 && !outgoing_request->presence(MIME_PRESENCE_HOST)) {
7707     int host_len;
7708     const char *host = url->host_get(&host_len);
7709 
7710     // Add a ':port' to the HOST header if the request is not going
7711     // to the default port.
7712     int port = url->port_get();
7713     if (port != url_canonicalize_port(URL_TYPE_HTTP, 0)) {
7714       char *buf = static_cast<char *>(alloca(host_len + 15));
7715       memcpy(buf, host, host_len);
7716       host_len += snprintf(buf + host_len, 15, ":%d", port);
7717       outgoing_request->value_set(MIME_FIELD_HOST, MIME_LEN_HOST, buf, host_len);
7718     } else {
7719       outgoing_request->value_set(MIME_FIELD_HOST, MIME_LEN_HOST, host, host_len);
7720     }
7721   }
7722 
7723   // Figure out whether to force the outgoing request URL into absolute or relative styles.
7724   if (outgoing_request->method_get_wksidx() == HTTP_WKSIDX_CONNECT) {
7725     // CONNECT method requires a target in the URL, so always force it from the Host header.
7726     outgoing_request->set_url_target_from_host_field();
7727   } else if (s->current.request_to == PARENT_PROXY && parent_is_proxy(s)) {
7728     // If we have a parent proxy set the URL target field.
7729     if (!outgoing_request->is_target_in_url()) {
7730       TxnDebug("http_trans", "[build_request] adding target to URL for parent proxy");
7731       outgoing_request->set_url_target_from_host_field();
7732     }
7733   } else if (s->next_hop_scheme == URL_WKSIDX_HTTP || s->next_hop_scheme == URL_WKSIDX_HTTPS ||
7734              s->next_hop_scheme == URL_WKSIDX_WS || s->next_hop_scheme == URL_WKSIDX_WSS) {
7735     // Otherwise, remove the URL target from HTTP and Websocket URLs since certain origins
7736     // cannot deal with absolute URLs.
7737     TxnDebug("http_trans", "[build_request] removing host name from url");
7738     HttpTransactHeaders::remove_host_name_from_url(outgoing_request);
7739   }
7740 
7741   // If the response is most likely not cacheable, eg, request with Authorization,
7742   // do we really want to remove conditional headers to get large 200 response?
7743   // Answer: NO.  Since if the response is most likely not cacheable,
7744   // we don't remove conditional headers so that for a non-200 response
7745   // from the O.S., we will save bandwidth between proxy and O.S.
7746   if (s->current.mode == GENERIC_PROXY) {
7747     if (is_request_likely_cacheable(s, base_request)) {
7748       if (s->txn_conf->cache_when_to_revalidate != 4) {
7749         TxnDebug("http_trans", "[build_request] "
7750                                "request like cacheable and conditional headers removed");
7751         HttpTransactHeaders::remove_conditional_headers(outgoing_request);
7752       } else {
7753         TxnDebug("http_trans", "[build_request] "
7754                                "request like cacheable but keep conditional headers");
7755       }
7756     } else {
7757       // In this case, we send a conditional request
7758       // instead of the normal non-conditional request.
7759       TxnDebug("http_trans", "[build_request] "
7760                              "request not like cacheable and conditional headers not removed");
7761     }
7762   }
7763 
7764   if (s->http_config_param->send_100_continue_response) {
7765     HttpTransactHeaders::remove_100_continue_headers(s, outgoing_request);
7766     TxnDebug("http_trans", "[build_request] request expect 100-continue headers removed");
7767   }
7768 
7769   if (base_request->is_early_data()) {
7770     outgoing_request->value_set_int(MIME_FIELD_EARLY_DATA, MIME_LEN_EARLY_DATA, 1);
7771   }
7772 
7773   s->request_sent_time = ink_local_time();
7774   s->current.now       = s->request_sent_time;
7775 
7776   // The assert is backwards in this case because request is being (re)sent.
7777   ink_assert(s->request_sent_time >= s->response_received_time);
7778 
7779   TxnDebug("http_trans", "[build_request] request_sent_time: %" PRId64, (int64_t)s->request_sent_time);
7780   DUMP_HEADER("http_hdrs", outgoing_request, s->state_machine_id, "Proxy's Request");
7781 
7782   HTTP_INCREMENT_DYN_STAT(http_outgoing_requests_stat);
7783 }
7784 
7785 // build a (status_code) response based upon the given info
7786 
7787 void
build_response(State * s,HTTPHdr * base_response,HTTPHdr * outgoing_response,HTTPVersion outgoing_version)7788 HttpTransact::build_response(State *s, HTTPHdr *base_response, HTTPHdr *outgoing_response, HTTPVersion outgoing_version)
7789 {
7790   build_response(s, base_response, outgoing_response, outgoing_version, HTTP_STATUS_NONE, nullptr);
7791   return;
7792 }
7793 
7794 void
build_response(State * s,HTTPHdr * outgoing_response,HTTPVersion outgoing_version,HTTPStatus status_code,const char * reason_phrase)7795 HttpTransact::build_response(State *s, HTTPHdr *outgoing_response, HTTPVersion outgoing_version, HTTPStatus status_code,
7796                              const char *reason_phrase)
7797 {
7798   build_response(s, nullptr, outgoing_response, outgoing_version, status_code, reason_phrase);
7799   return;
7800 }
7801 
7802 void
build_response(State * s,HTTPHdr * base_response,HTTPHdr * outgoing_response,HTTPVersion outgoing_version,HTTPStatus status_code,const char * reason_phrase)7803 HttpTransact::build_response(State *s, HTTPHdr *base_response, HTTPHdr *outgoing_response, HTTPVersion outgoing_version,
7804                              HTTPStatus status_code, const char *reason_phrase)
7805 {
7806   if (reason_phrase == nullptr) {
7807     reason_phrase = http_hdr_reason_lookup(status_code);
7808   }
7809 
7810   if (base_response == nullptr) {
7811     HttpTransactHeaders::build_base_response(outgoing_response, status_code, reason_phrase, strlen(reason_phrase), s->current.now);
7812   } else {
7813     if ((status_code == HTTP_STATUS_NONE) || (status_code == base_response->status_get())) {
7814       HttpTransactHeaders::copy_header_fields(base_response, outgoing_response, s->txn_conf->fwd_proxy_auth_to_parent);
7815 
7816       if (s->txn_conf->insert_age_in_response) {
7817         HttpTransactHeaders::insert_time_and_age_headers_in_response(s->request_sent_time, s->response_received_time,
7818                                                                      s->current.now, base_response, outgoing_response);
7819       }
7820 
7821       // Note: We need to handle the "Content-Length" header first here
7822       //  since handle_content_length_header()
7823       //  determines whether we accept origin server's content-length.
7824       //  We need to have made a decision regard the content-length
7825       //  before processing the keep_alive headers
7826       //
7827       handle_content_length_header(s, outgoing_response, base_response);
7828     } else {
7829       switch (status_code) {
7830       case HTTP_STATUS_NOT_MODIFIED:
7831         HttpTransactHeaders::build_base_response(outgoing_response, status_code, reason_phrase, strlen(reason_phrase),
7832                                                  s->current.now);
7833 
7834         // According to RFC 2616, Section 10.3.5,
7835         // a 304 response MUST contain Date header,
7836         // Etag and/or Content-location header,
7837         // and Expires, Cache-control, and Vary
7838         // (if they might be changed).
7839         // Since a proxy doesn't know if a header differs from
7840         // a user agent's cached document or not, all are sent.
7841         {
7842           static const struct {
7843             const char *name;
7844             int len;
7845             uint64_t presence;
7846           } fields[] = {
7847             {MIME_FIELD_ETAG, MIME_LEN_ETAG, MIME_PRESENCE_ETAG},
7848             {MIME_FIELD_CONTENT_LOCATION, MIME_LEN_CONTENT_LOCATION, MIME_PRESENCE_CONTENT_LOCATION},
7849             {MIME_FIELD_EXPIRES, MIME_LEN_EXPIRES, MIME_PRESENCE_EXPIRES},
7850             {MIME_FIELD_CACHE_CONTROL, MIME_LEN_CACHE_CONTROL, MIME_PRESENCE_CACHE_CONTROL},
7851             {MIME_FIELD_VARY, MIME_LEN_VARY, MIME_PRESENCE_VARY},
7852           };
7853 
7854           for (size_t i = 0; i < countof(fields); i++) {
7855             if (base_response->presence(fields[i].presence)) {
7856               MIMEField *field;
7857               int len;
7858               const char *value;
7859 
7860               field = base_response->field_find(fields[i].name, fields[i].len);
7861               ink_assert(field != nullptr);
7862               value = field->value_get(&len);
7863               outgoing_response->value_append(fields[i].name, fields[i].len, value, len, false);
7864               if (field->has_dups()) {
7865                 field = field->m_next_dup;
7866                 while (field) {
7867                   value = field->value_get(&len);
7868                   outgoing_response->value_append(fields[i].name, fields[i].len, value, len, true);
7869                   field = field->m_next_dup;
7870                 }
7871               }
7872             }
7873           }
7874         }
7875         break;
7876 
7877       case HTTP_STATUS_PRECONDITION_FAILED:
7878       // fall through
7879       case HTTP_STATUS_RANGE_NOT_SATISFIABLE:
7880         HttpTransactHeaders::build_base_response(outgoing_response, status_code, reason_phrase, strlen(reason_phrase),
7881                                                  s->current.now);
7882         break;
7883       default:
7884         // ink_assert(!"unexpected status code in build_response()");
7885         break;
7886       }
7887     }
7888   }
7889 
7890   // the following is done whether base_response == NULL or not
7891 
7892   // If the response is prohibited from containing a body,
7893   //  we know the content length is trustable for keep-alive
7894   if (is_response_body_precluded(status_code, s->method)) {
7895     s->hdr_info.trust_response_cl = true;
7896   }
7897 
7898   handle_response_keep_alive_headers(s, outgoing_version, outgoing_response);
7899 
7900   if (s->next_hop_scheme < 0) {
7901     s->next_hop_scheme = URL_WKSIDX_HTTP;
7902   }
7903 
7904   // Add HSTS header (Strict-Transport-Security) if max-age is set and the request was https
7905   // and the incoming request was remapped correctly
7906   if (s->orig_scheme == URL_WKSIDX_HTTPS && s->txn_conf->proxy_response_hsts_max_age >= 0 && s->url_remap_success == true) {
7907     TxnDebug("http_hdrs", "hsts max-age=%" PRId64, s->txn_conf->proxy_response_hsts_max_age);
7908     HttpTransactHeaders::insert_hsts_header_in_response(s, outgoing_response);
7909   }
7910 
7911   if (s->txn_conf->insert_response_via_string) {
7912     HttpTransactHeaders::insert_via_header_in_response(s, outgoing_response);
7913   }
7914 
7915   HttpTransactHeaders::convert_response(outgoing_version, outgoing_response);
7916 
7917   // process reverse mappings on the location header
7918   // TS-1364: do this regardless of response code
7919   response_url_remap(outgoing_response, s->state_machine->m_remap);
7920 
7921   if (s->http_config_param->enable_http_stats) {
7922     HttpTransactHeaders::generate_and_set_squid_codes(outgoing_response, s->via_string, &s->squid_codes);
7923   }
7924 
7925   HttpTransactHeaders::add_server_header_to_response(s->txn_conf, outgoing_response);
7926 
7927   if (s->state_machine->ua_txn && s->state_machine->ua_txn->get_proxy_ssn()->is_draining()) {
7928     HttpTransactHeaders::add_connection_close(outgoing_response);
7929   }
7930 
7931   if (is_debug_tag_set("http_hdrs")) {
7932     if (base_response) {
7933       DUMP_HEADER("http_hdrs", base_response, s->state_machine_id, "Base Header for Building Response");
7934     }
7935 
7936     DUMP_HEADER("http_hdrs", outgoing_response, s->state_machine_id, "Proxy's Response 2");
7937   }
7938 
7939   return;
7940 }
7941 
7942 //////////////////////////////////////////////////////////////////////////////
7943 //
7944 //      void HttpTransact::build_error_response(
7945 //          State *s,
7946 //          HTTPStatus status_code,
7947 //          char *reason_phrase_or_null,
7948 //          char *error_body_type,
7949 //          char *format, ...)
7950 //
7951 //      This method sets the requires state for an error reply, including
7952 //      the error text, status code, reason phrase, and reply headers.  The
7953 //      caller calls the method with the HttpTransact::State <s>, the
7954 //      HTTP status code <status_code>, a user-specified reason phrase
7955 //      string (or NULL) <reason_phrase_or_null>, and a printf-like
7956 //      text format and arguments which are appended to the error text.
7957 //
7958 //      The <error_body_type> is the error message type, as specified by
7959 //      the HttpBodyFactory customized error page system.
7960 //
7961 //      If the descriptive text <format> is not NULL or "", it is also
7962 //      added to the error text body as descriptive text in the error body.
7963 //      If <reason_phrase_or_null> is NULL, the default HTTP reason phrase
7964 //      is used.  This routine DOES NOT check for buffer overflows.  The
7965 //      caller should keep the messages small to be sure the error text
7966 //      fits in the error buffer (ok, it's nasty, but at least I admit it!).
7967 //
7968 //////////////////////////////////////////////////////////////////////////////
7969 void
build_error_response(State * s,HTTPStatus status_code,const char * reason_phrase_or_null,const char * error_body_type)7970 HttpTransact::build_error_response(State *s, HTTPStatus status_code, const char *reason_phrase_or_null, const char *error_body_type)
7971 {
7972   char body_language[256], body_type[256];
7973 
7974   if (nullptr == error_body_type) {
7975     error_body_type = "default";
7976   }
7977 
7978   // Make sure that if this error occurred before we initialized the state variables that we do now.
7979   initialize_state_variables_from_request(s, &s->hdr_info.client_request);
7980 
7981   //////////////////////////////////////////////////////
7982   //  If there is a request body, we must disable     //
7983   //  keep-alive to prevent the body being read as    //
7984   //  the next header (unless we've already drained   //
7985   //  which we do for NTLM auth)                      //
7986   //////////////////////////////////////////////////////
7987   if (status_code == HTTP_STATUS_REQUEST_TIMEOUT || s->hdr_info.client_request.get_content_length() != 0 ||
7988       s->client_info.transfer_encoding == HttpTransact::CHUNKED_ENCODING) {
7989     s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
7990   } else {
7991     // We don't have a request body.  Since we are
7992     //  generating the error, we know we can trust
7993     //  the content-length
7994     s->hdr_info.trust_response_cl = true;
7995   }
7996   // If transparent and the forward server connection looks unhappy don't
7997   // keep alive the ua connection.
7998   if ((s->state_machine->ua_txn && s->state_machine->ua_txn->is_outbound_transparent()) &&
7999       (status_code == HTTP_STATUS_INTERNAL_SERVER_ERROR || status_code == HTTP_STATUS_GATEWAY_TIMEOUT ||
8000        status_code == HTTP_STATUS_BAD_GATEWAY || status_code == HTTP_STATUS_SERVICE_UNAVAILABLE)) {
8001     s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
8002   }
8003 
8004   // If there is a parse error on reading the request it can leave reading the request stream in an undetermined state
8005   if (status_code == HTTP_STATUS_BAD_REQUEST) {
8006     s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
8007   }
8008 
8009   switch (status_code) {
8010   case HTTP_STATUS_BAD_REQUEST:
8011     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_ERROR);
8012     // Did the via error already get set by the loop detection
8013     if (s->via_string[VIA_ERROR_TYPE] != VIA_ERROR_LOOP_DETECTED) {
8014       SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_HEADER_SYNTAX);
8015     }
8016     break;
8017   case HTTP_STATUS_BAD_GATEWAY:
8018     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_CONNECTION);
8019     break;
8020   case HTTP_STATUS_GATEWAY_TIMEOUT:
8021     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_TIMEOUT);
8022     break;
8023   case HTTP_STATUS_NOT_FOUND:
8024     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_SERVER);
8025     break;
8026   case HTTP_STATUS_FORBIDDEN:
8027     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_ERROR);
8028     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_FORBIDDEN);
8029     break;
8030   case HTTP_STATUS_HTTPVER_NOT_SUPPORTED:
8031     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_ERROR);
8032     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_SERVER);
8033     break;
8034   case HTTP_STATUS_INTERNAL_SERVER_ERROR:
8035     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_DNS_FAILURE);
8036     break;
8037   case HTTP_STATUS_MOVED_TEMPORARILY:
8038     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_MOVED_TEMPORARILY);
8039     break;
8040   case HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED:
8041     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_ERROR);
8042     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_AUTHORIZATION);
8043     break;
8044   case HTTP_STATUS_UNAUTHORIZED:
8045     SET_VIA_STRING(VIA_CLIENT_REQUEST, VIA_CLIENT_ERROR);
8046     SET_VIA_STRING(VIA_ERROR_TYPE, VIA_ERROR_AUTHORIZATION);
8047     break;
8048   default:
8049     break;
8050   }
8051 
8052   const char *reason_phrase =
8053     (reason_phrase_or_null ? reason_phrase_or_null : const_cast<char *>(http_hdr_reason_lookup(status_code)));
8054   if (unlikely(!reason_phrase)) {
8055     reason_phrase = "Unknown HTTP Status";
8056   }
8057 
8058   // set the source to internal so that chunking is handled correctly
8059   s->source = SOURCE_INTERNAL;
8060   build_response(s, &s->hdr_info.client_response, s->client_info.http_version, status_code, reason_phrase);
8061 
8062   if (status_code == HTTP_STATUS_SERVICE_UNAVAILABLE) {
8063     int ret_tmp;
8064     int retry_after = 0;
8065 
8066     if (s->hdr_info.client_response.value_get(MIME_FIELD_RETRY_AFTER, MIME_LEN_RETRY_AFTER, &ret_tmp) != nullptr) {
8067       retry_after = ret_tmp;
8068     }
8069     s->congestion_control_crat = retry_after;
8070   } else if (status_code == HTTP_STATUS_BAD_REQUEST) {
8071     // Close the client connection after a malformed request
8072     s->client_info.keep_alive = HTTP_NO_KEEPALIVE;
8073   }
8074 
8075   // Add a bunch of headers to make sure that caches between
8076   // the Traffic Server and the client do not cache the error
8077   // page.
8078   s->hdr_info.client_response.value_set(MIME_FIELD_CACHE_CONTROL, MIME_LEN_CACHE_CONTROL, "no-store", 8);
8079   // Make sure there are no Expires and Last-Modified headers.
8080   s->hdr_info.client_response.field_delete(MIME_FIELD_EXPIRES, MIME_LEN_EXPIRES);
8081   s->hdr_info.client_response.field_delete(MIME_FIELD_LAST_MODIFIED, MIME_LEN_LAST_MODIFIED);
8082 
8083   if ((status_code == HTTP_STATUS_PERMANENT_REDIRECT || status_code == HTTP_STATUS_TEMPORARY_REDIRECT ||
8084        status_code == HTTP_STATUS_MOVED_TEMPORARILY || status_code == HTTP_STATUS_MOVED_PERMANENTLY) &&
8085       s->remap_redirect) {
8086     s->hdr_info.client_response.value_set(MIME_FIELD_LOCATION, MIME_LEN_LOCATION, s->remap_redirect, strlen(s->remap_redirect));
8087   }
8088 
8089   ////////////////////////////////////////////////////////////////////
8090   // create the error message using the "body factory", which will  //
8091   // build a customized error message if available, or generate the //
8092   // old style internal defaults otherwise --- the body factory     //
8093   // supports language targeting using the Accept-Language header   //
8094   ////////////////////////////////////////////////////////////////////
8095 
8096   int64_t len;
8097   char *new_msg;
8098 
8099   new_msg = body_factory->fabricate_with_old_api(
8100     error_body_type, s, s->http_config_param->body_factory_response_max_size, &len, body_language, sizeof(body_language), body_type,
8101     sizeof(body_type), s->internal_msg_buffer_size, s->internal_msg_buffer_size ? s->internal_msg_buffer : nullptr);
8102 
8103   // After the body factory is called, a new "body" is allocated, and we must replace it. It is
8104   // unfortunate that there's no way to avoid this fabrication even when there is no substitutions...
8105   s->free_internal_msg_buffer();
8106   if (len == 0) {
8107     // If the file is empty, we may have a malloc(1) buffer. Release it.
8108     new_msg = static_cast<char *>(ats_free_null(new_msg));
8109   }
8110   s->internal_msg_buffer                     = new_msg;
8111   s->internal_msg_buffer_size                = len;
8112   s->internal_msg_buffer_fast_allocator_size = -1;
8113 
8114   if (len > 0) {
8115     s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE, body_type, strlen(body_type));
8116     s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_LANGUAGE, MIME_LEN_CONTENT_LANGUAGE, body_language,
8117                                           strlen(body_language));
8118   } else {
8119     s->hdr_info.client_response.field_delete(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
8120     s->hdr_info.client_response.field_delete(MIME_FIELD_CONTENT_LANGUAGE, MIME_LEN_CONTENT_LANGUAGE);
8121   }
8122 
8123   s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
8124   return;
8125 }
8126 
8127 void
build_redirect_response(State * s)8128 HttpTransact::build_redirect_response(State *s)
8129 {
8130   TxnDebug("http_redirect", "[HttpTransact::build_redirect_response]");
8131   URL *u;
8132   const char *old_host;
8133   int old_host_len;
8134   const char *new_url = nullptr;
8135   int new_url_len;
8136   char *to_free = nullptr;
8137 
8138   HTTPStatus status_code = HTTP_STATUS_MOVED_TEMPORARILY;
8139   char *reason_phrase    = const_cast<char *>(http_hdr_reason_lookup(status_code));
8140 
8141   build_response(s, &s->hdr_info.client_response, s->client_info.http_version, status_code, reason_phrase);
8142 
8143   //////////////////////////////////////////////////////////
8144   // figure out what new url should be.  this little hack //
8145   // inserts expanded hostname into old url in order to   //
8146   // get scheme information, then puts the old url back.  //
8147   //////////////////////////////////////////////////////////
8148   u        = s->hdr_info.client_request.url_get();
8149   old_host = u->host_get(&old_host_len);
8150   u->host_set(s->dns_info.lookup_name, strlen(s->dns_info.lookup_name));
8151   new_url = to_free = u->string_get(&s->arena, &new_url_len);
8152   if (new_url == nullptr) {
8153     new_url = "";
8154   }
8155   u->host_set(old_host, old_host_len);
8156 
8157   //////////////////////////
8158   // set redirect headers //
8159   //////////////////////////
8160   HTTPHdr *h = &s->hdr_info.client_response;
8161   if (s->txn_conf->insert_response_via_string) {
8162     const char pa[] = "Proxy-agent";
8163 
8164     h->value_append(pa, sizeof(pa) - 1, s->http_config_param->proxy_response_via_string,
8165                     s->http_config_param->proxy_response_via_string_len);
8166   }
8167   h->value_set(MIME_FIELD_LOCATION, MIME_LEN_LOCATION, new_url, new_url_len);
8168 
8169   //////////////////////////
8170   // set descriptive text //
8171   //////////////////////////
8172   s->free_internal_msg_buffer();
8173   s->internal_msg_buffer_fast_allocator_size = -1;
8174   // template redirect#temporarily can not be used here since there is no way to pass the computed url to the template.
8175   s->internal_msg_buffer = body_factory->getFormat(8192, &s->internal_msg_buffer_size, "%s <a href=\"%s\">%s</a>.  %s.",
8176                                                    "The document you requested is now", new_url, new_url,
8177                                                    "Please update your documents and bookmarks accordingly", nullptr);
8178 
8179   h->set_content_length(s->internal_msg_buffer_size);
8180   h->value_set(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE, "text/html", 9);
8181 
8182   s->arena.str_free(to_free);
8183 }
8184 
8185 const char *
get_error_string(int erno)8186 HttpTransact::get_error_string(int erno)
8187 {
8188   if (erno >= 0) {
8189     return (strerror(erno));
8190   } else {
8191     switch (-erno) {
8192     case ENET_THROTTLING:
8193       return ("throttling");
8194     case ESOCK_DENIED:
8195       return ("socks error - denied");
8196     case ESOCK_TIMEOUT:
8197       return ("socks error - timeout");
8198     case ESOCK_NO_SOCK_SERVER_CONN:
8199       return ("socks error - no server connection");
8200     //              this assumes that the following case occurs
8201     //              when HttpSM.cc::state_origin_server_read_response
8202     //                 receives an HTTP_EVENT_EOS. (line 1729 in HttpSM.cc,
8203     //                 version 1.145.2.13.2.57)
8204     case ENET_CONNECT_FAILED:
8205       return ("connect failed");
8206     case UNKNOWN_INTERNAL_ERROR:
8207       return ("internal error - server connection terminated");
8208     default:
8209       return ("");
8210     }
8211   }
8212 }
8213 
8214 ink_time_t
ink_local_time()8215 ink_local_time()
8216 {
8217   return Thread::get_hrtime() / HRTIME_SECOND;
8218 }
8219 
8220 //
8221 // The stat functions
8222 //
8223 void
histogram_response_document_size(State * s,int64_t doc_size)8224 HttpTransact::histogram_response_document_size(State *s, int64_t doc_size)
8225 {
8226   if (doc_size >= 0 && doc_size <= 100) {
8227     HTTP_INCREMENT_DYN_STAT(http_response_document_size_100_stat);
8228   } else if (doc_size <= 1024) {
8229     HTTP_INCREMENT_DYN_STAT(http_response_document_size_1K_stat);
8230   } else if (doc_size <= 3072) {
8231     HTTP_INCREMENT_DYN_STAT(http_response_document_size_3K_stat);
8232   } else if (doc_size <= 5120) {
8233     HTTP_INCREMENT_DYN_STAT(http_response_document_size_5K_stat);
8234   } else if (doc_size <= 10240) {
8235     HTTP_INCREMENT_DYN_STAT(http_response_document_size_10K_stat);
8236   } else if (doc_size <= 1048576) {
8237     HTTP_INCREMENT_DYN_STAT(http_response_document_size_1M_stat);
8238   } else {
8239     HTTP_INCREMENT_DYN_STAT(http_response_document_size_inf_stat);
8240   }
8241   return;
8242 }
8243 
8244 void
histogram_request_document_size(State * s,int64_t doc_size)8245 HttpTransact::histogram_request_document_size(State *s, int64_t doc_size)
8246 {
8247   if (doc_size >= 0 && doc_size <= 100) {
8248     HTTP_INCREMENT_DYN_STAT(http_request_document_size_100_stat);
8249   } else if (doc_size <= 1024) {
8250     HTTP_INCREMENT_DYN_STAT(http_request_document_size_1K_stat);
8251   } else if (doc_size <= 3072) {
8252     HTTP_INCREMENT_DYN_STAT(http_request_document_size_3K_stat);
8253   } else if (doc_size <= 5120) {
8254     HTTP_INCREMENT_DYN_STAT(http_request_document_size_5K_stat);
8255   } else if (doc_size <= 10240) {
8256     HTTP_INCREMENT_DYN_STAT(http_request_document_size_10K_stat);
8257   } else if (doc_size <= 1048576) {
8258     HTTP_INCREMENT_DYN_STAT(http_request_document_size_1M_stat);
8259   } else {
8260     HTTP_INCREMENT_DYN_STAT(http_request_document_size_inf_stat);
8261   }
8262   return;
8263 }
8264 
8265 void
user_agent_connection_speed(State * s,ink_hrtime transfer_time,int64_t nbytes)8266 HttpTransact::user_agent_connection_speed(State *s, ink_hrtime transfer_time, int64_t nbytes)
8267 {
8268   float bytes_per_hrtime =
8269     (transfer_time == 0) ? (nbytes) : (static_cast<float>(nbytes) / static_cast<float>(static_cast<int64_t>(transfer_time)));
8270   int bytes_per_sec = static_cast<int>(bytes_per_hrtime * HRTIME_SECOND);
8271 
8272   if (bytes_per_sec <= 100) {
8273     HTTP_INCREMENT_DYN_STAT(http_user_agent_speed_bytes_per_sec_100_stat);
8274   } else if (bytes_per_sec <= 1024) {
8275     HTTP_INCREMENT_DYN_STAT(http_user_agent_speed_bytes_per_sec_1K_stat);
8276   } else if (bytes_per_sec <= 10240) {
8277     HTTP_INCREMENT_DYN_STAT(http_user_agent_speed_bytes_per_sec_10K_stat);
8278   } else if (bytes_per_sec <= 102400) {
8279     HTTP_INCREMENT_DYN_STAT(http_user_agent_speed_bytes_per_sec_100K_stat);
8280   } else if (bytes_per_sec <= 1048576) {
8281     HTTP_INCREMENT_DYN_STAT(http_user_agent_speed_bytes_per_sec_1M_stat);
8282   } else if (bytes_per_sec <= 10485760) {
8283     HTTP_INCREMENT_DYN_STAT(http_user_agent_speed_bytes_per_sec_10M_stat);
8284   } else {
8285     HTTP_INCREMENT_DYN_STAT(http_user_agent_speed_bytes_per_sec_100M_stat);
8286   }
8287 
8288   return;
8289 }
8290 
8291 /*
8292  * added request_process_time stat for loadshedding foo
8293  */
8294 void
client_result_stat(State * s,ink_hrtime total_time,ink_hrtime request_process_time)8295 HttpTransact::client_result_stat(State *s, ink_hrtime total_time, ink_hrtime request_process_time)
8296 {
8297   ClientTransactionResult_t client_transaction_result = CLIENT_TRANSACTION_RESULT_UNDEFINED;
8298 
8299   ///////////////////////////////////////////////////////
8300   // don't count errors we generated as hits or misses //
8301   ///////////////////////////////////////////////////////
8302   int client_response_status = HTTP_STATUS_NONE;
8303   if (s->hdr_info.client_response.valid()) {
8304     client_response_status = s->hdr_info.client_response.status_get();
8305   }
8306 
8307   if ((s->source == SOURCE_INTERNAL) && client_response_status >= 400) {
8308     client_transaction_result = CLIENT_TRANSACTION_RESULT_ERROR_OTHER;
8309   }
8310 
8311   switch (s->squid_codes.log_code) {
8312   case SQUID_LOG_ERR_CONNECT_FAIL:
8313     HTTP_INCREMENT_DYN_STAT(http_cache_miss_cold_stat);
8314     client_transaction_result = CLIENT_TRANSACTION_RESULT_ERROR_CONNECT_FAIL;
8315     break;
8316 
8317   case SQUID_LOG_TCP_MEM_HIT:
8318     HTTP_INCREMENT_DYN_STAT(http_cache_hit_mem_fresh_stat);
8319     // fallthrough
8320 
8321   case SQUID_LOG_TCP_HIT:
8322     // It's possible to have two stat's instead of one, if needed.
8323     HTTP_INCREMENT_DYN_STAT(http_cache_hit_fresh_stat);
8324     client_transaction_result = CLIENT_TRANSACTION_RESULT_HIT_FRESH;
8325     break;
8326 
8327   case SQUID_LOG_TCP_REFRESH_HIT:
8328     HTTP_INCREMENT_DYN_STAT(http_cache_hit_reval_stat);
8329     client_transaction_result = CLIENT_TRANSACTION_RESULT_HIT_REVALIDATED;
8330     break;
8331 
8332   case SQUID_LOG_TCP_IMS_HIT:
8333     HTTP_INCREMENT_DYN_STAT(http_cache_hit_ims_stat);
8334     client_transaction_result = CLIENT_TRANSACTION_RESULT_HIT_FRESH;
8335     break;
8336 
8337   case SQUID_LOG_TCP_REF_FAIL_HIT:
8338     HTTP_INCREMENT_DYN_STAT(http_cache_hit_stale_served_stat);
8339     client_transaction_result = CLIENT_TRANSACTION_RESULT_HIT_FRESH;
8340     break;
8341 
8342   case SQUID_LOG_TCP_MISS:
8343     if ((GET_VIA_STRING(VIA_CACHE_RESULT) == VIA_IN_CACHE_NOT_ACCEPTABLE) || (GET_VIA_STRING(VIA_CACHE_RESULT) == VIA_CACHE_MISS)) {
8344       HTTP_INCREMENT_DYN_STAT(http_cache_miss_cold_stat);
8345       client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_COLD;
8346     } else {
8347       // FIX: what case is this for?  can it ever happen?
8348       HTTP_INCREMENT_DYN_STAT(http_cache_miss_uncacheable_stat);
8349       client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_UNCACHABLE;
8350     }
8351     break;
8352 
8353   case SQUID_LOG_TCP_REFRESH_MISS:
8354     HTTP_INCREMENT_DYN_STAT(http_cache_miss_changed_stat);
8355     client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_CHANGED;
8356     break;
8357 
8358   case SQUID_LOG_TCP_CLIENT_REFRESH:
8359     HTTP_INCREMENT_DYN_STAT(http_cache_miss_client_no_cache_stat);
8360     client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_CLIENT_NO_CACHE;
8361     break;
8362 
8363   case SQUID_LOG_TCP_IMS_MISS:
8364     HTTP_INCREMENT_DYN_STAT(http_cache_miss_ims_stat);
8365     client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_COLD;
8366     break;
8367 
8368   case SQUID_LOG_TCP_SWAPFAIL:
8369     HTTP_INCREMENT_DYN_STAT(http_cache_read_error_stat);
8370     client_transaction_result = CLIENT_TRANSACTION_RESULT_HIT_FRESH;
8371     break;
8372 
8373   case SQUID_LOG_ERR_READ_TIMEOUT:
8374   case SQUID_LOG_TCP_DENIED:
8375     // No cache result due to error
8376     client_transaction_result = CLIENT_TRANSACTION_RESULT_ERROR_OTHER;
8377     break;
8378 
8379   default:
8380     // FIX: What is the conditional below doing?
8381     //          if (s->local_trans_stats[http_cache_lookups_stat].count == 1L)
8382     //              HTTP_INCREMENT_DYN_STAT(http_cache_miss_cold_stat);
8383 
8384     // FIX: I suspect the following line should not be set here,
8385     //      because it overrides the error classification above.
8386     //      Commenting out.
8387     // client_transaction_result = CLIENT_TRANSACTION_RESULT_MISS_COLD;
8388 
8389     break;
8390   }
8391 
8392   //////////////////////////////////////////
8393   // don't count aborts as hits or misses //
8394   //////////////////////////////////////////
8395   if (s->client_info.abort == ABORTED) {
8396     client_transaction_result = CLIENT_TRANSACTION_RESULT_ERROR_ABORT;
8397   }
8398   // Count the status codes, assuming the client didn't abort (i.e. there is an m_http)
8399   if ((s->source != SOURCE_NONE) && (s->client_info.abort == DIDNOT_ABORT)) {
8400     switch (client_response_status) {
8401     case 100:
8402       HTTP_INCREMENT_DYN_STAT(http_response_status_100_count_stat);
8403       break;
8404     case 101:
8405       HTTP_INCREMENT_DYN_STAT(http_response_status_101_count_stat);
8406       break;
8407     case 200:
8408       HTTP_INCREMENT_DYN_STAT(http_response_status_200_count_stat);
8409       break;
8410     case 201:
8411       HTTP_INCREMENT_DYN_STAT(http_response_status_201_count_stat);
8412       break;
8413     case 202:
8414       HTTP_INCREMENT_DYN_STAT(http_response_status_202_count_stat);
8415       break;
8416     case 203:
8417       HTTP_INCREMENT_DYN_STAT(http_response_status_203_count_stat);
8418       break;
8419     case 204:
8420       HTTP_INCREMENT_DYN_STAT(http_response_status_204_count_stat);
8421       break;
8422     case 205:
8423       HTTP_INCREMENT_DYN_STAT(http_response_status_205_count_stat);
8424       break;
8425     case 206:
8426       HTTP_INCREMENT_DYN_STAT(http_response_status_206_count_stat);
8427       break;
8428     case 300:
8429       HTTP_INCREMENT_DYN_STAT(http_response_status_300_count_stat);
8430       break;
8431     case 301:
8432       HTTP_INCREMENT_DYN_STAT(http_response_status_301_count_stat);
8433       break;
8434     case 302:
8435       HTTP_INCREMENT_DYN_STAT(http_response_status_302_count_stat);
8436       break;
8437     case 303:
8438       HTTP_INCREMENT_DYN_STAT(http_response_status_303_count_stat);
8439       break;
8440     case 304:
8441       HTTP_INCREMENT_DYN_STAT(http_response_status_304_count_stat);
8442       break;
8443     case 305:
8444       HTTP_INCREMENT_DYN_STAT(http_response_status_305_count_stat);
8445       break;
8446     case 307:
8447       HTTP_INCREMENT_DYN_STAT(http_response_status_307_count_stat);
8448       break;
8449     case 308:
8450       HTTP_INCREMENT_DYN_STAT(http_response_status_308_count_stat);
8451       break;
8452     case 400:
8453       HTTP_INCREMENT_DYN_STAT(http_response_status_400_count_stat);
8454       break;
8455     case 401:
8456       HTTP_INCREMENT_DYN_STAT(http_response_status_401_count_stat);
8457       break;
8458     case 402:
8459       HTTP_INCREMENT_DYN_STAT(http_response_status_402_count_stat);
8460       break;
8461     case 403:
8462       HTTP_INCREMENT_DYN_STAT(http_response_status_403_count_stat);
8463       break;
8464     case 404:
8465       HTTP_INCREMENT_DYN_STAT(http_response_status_404_count_stat);
8466       break;
8467     case 405:
8468       HTTP_INCREMENT_DYN_STAT(http_response_status_405_count_stat);
8469       break;
8470     case 406:
8471       HTTP_INCREMENT_DYN_STAT(http_response_status_406_count_stat);
8472       break;
8473     case 407:
8474       HTTP_INCREMENT_DYN_STAT(http_response_status_407_count_stat);
8475       break;
8476     case 408:
8477       HTTP_INCREMENT_DYN_STAT(http_response_status_408_count_stat);
8478       break;
8479     case 409:
8480       HTTP_INCREMENT_DYN_STAT(http_response_status_409_count_stat);
8481       break;
8482     case 410:
8483       HTTP_INCREMENT_DYN_STAT(http_response_status_410_count_stat);
8484       break;
8485     case 411:
8486       HTTP_INCREMENT_DYN_STAT(http_response_status_411_count_stat);
8487       break;
8488     case 412:
8489       HTTP_INCREMENT_DYN_STAT(http_response_status_412_count_stat);
8490       break;
8491     case 413:
8492       HTTP_INCREMENT_DYN_STAT(http_response_status_413_count_stat);
8493       break;
8494     case 414:
8495       HTTP_INCREMENT_DYN_STAT(http_response_status_414_count_stat);
8496       break;
8497     case 415:
8498       HTTP_INCREMENT_DYN_STAT(http_response_status_415_count_stat);
8499       break;
8500     case 416:
8501       HTTP_INCREMENT_DYN_STAT(http_response_status_416_count_stat);
8502       break;
8503     case 500:
8504       HTTP_INCREMENT_DYN_STAT(http_response_status_500_count_stat);
8505       break;
8506     case 501:
8507       HTTP_INCREMENT_DYN_STAT(http_response_status_501_count_stat);
8508       break;
8509     case 502:
8510       HTTP_INCREMENT_DYN_STAT(http_response_status_502_count_stat);
8511       break;
8512     case 503:
8513       HTTP_INCREMENT_DYN_STAT(http_response_status_503_count_stat);
8514       break;
8515     case 504:
8516       HTTP_INCREMENT_DYN_STAT(http_response_status_504_count_stat);
8517       break;
8518     case 505:
8519       HTTP_INCREMENT_DYN_STAT(http_response_status_505_count_stat);
8520       break;
8521     default:
8522       break;
8523     }
8524     switch (client_response_status / 100) {
8525     case 1:
8526       HTTP_INCREMENT_DYN_STAT(http_response_status_1xx_count_stat);
8527       break;
8528     case 2:
8529       HTTP_INCREMENT_DYN_STAT(http_response_status_2xx_count_stat);
8530       break;
8531     case 3:
8532       HTTP_INCREMENT_DYN_STAT(http_response_status_3xx_count_stat);
8533       break;
8534     case 4:
8535       HTTP_INCREMENT_DYN_STAT(http_response_status_4xx_count_stat);
8536       break;
8537     case 5:
8538       HTTP_INCREMENT_DYN_STAT(http_response_status_5xx_count_stat);
8539       break;
8540     default:
8541       break;
8542     }
8543   }
8544 
8545   // Increment the completed connection count
8546   HTTP_INCREMENT_DYN_STAT(http_completed_requests_stat);
8547 
8548   // Set the stat now that we know what happend
8549   ink_hrtime total_msec   = ink_hrtime_to_msec(total_time);
8550   ink_hrtime process_msec = ink_hrtime_to_msec(request_process_time);
8551   switch (client_transaction_result) {
8552   case CLIENT_TRANSACTION_RESULT_HIT_FRESH:
8553     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_hit_fresh_stat, total_msec);
8554     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_hit_fresh_process_stat, process_msec);
8555     break;
8556   case CLIENT_TRANSACTION_RESULT_HIT_REVALIDATED:
8557     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_hit_reval_stat, total_msec);
8558     break;
8559   case CLIENT_TRANSACTION_RESULT_MISS_COLD:
8560     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_miss_cold_stat, total_msec);
8561     break;
8562   case CLIENT_TRANSACTION_RESULT_MISS_CHANGED:
8563     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_miss_changed_stat, total_msec);
8564     break;
8565   case CLIENT_TRANSACTION_RESULT_MISS_CLIENT_NO_CACHE:
8566     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_miss_client_no_cache_stat, total_msec);
8567     break;
8568   case CLIENT_TRANSACTION_RESULT_MISS_UNCACHABLE:
8569     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_miss_uncacheable_stat, total_msec);
8570     break;
8571   case CLIENT_TRANSACTION_RESULT_ERROR_ABORT:
8572     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_errors_aborts_stat, total_msec);
8573     break;
8574   case CLIENT_TRANSACTION_RESULT_ERROR_POSSIBLE_ABORT:
8575     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_errors_possible_aborts_stat, total_msec);
8576     break;
8577   case CLIENT_TRANSACTION_RESULT_ERROR_CONNECT_FAIL:
8578     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_errors_connect_failed_stat, total_msec);
8579     break;
8580   case CLIENT_TRANSACTION_RESULT_ERROR_OTHER:
8581     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_errors_other_stat, total_msec);
8582     break;
8583   default:
8584     HTTP_SUM_DYN_STAT(http_ua_msecs_counts_other_unclassified_stat, total_msec);
8585     // This can happen if a plugin manually sets the status code after an error.
8586     TxnDebug("http", "Unclassified statistic");
8587     break;
8588   }
8589 }
8590 
8591 void
origin_server_connection_speed(State * s,ink_hrtime transfer_time,int64_t nbytes)8592 HttpTransact::origin_server_connection_speed(State *s, ink_hrtime transfer_time, int64_t nbytes)
8593 {
8594   float bytes_per_hrtime =
8595     (transfer_time == 0) ? (nbytes) : (static_cast<float>(nbytes) / static_cast<float>(static_cast<int64_t>(transfer_time)));
8596   int bytes_per_sec = static_cast<int>(bytes_per_hrtime * HRTIME_SECOND);
8597 
8598   if (bytes_per_sec <= 100) {
8599     HTTP_INCREMENT_DYN_STAT(http_origin_server_speed_bytes_per_sec_100_stat);
8600   } else if (bytes_per_sec <= 1024) {
8601     HTTP_INCREMENT_DYN_STAT(http_origin_server_speed_bytes_per_sec_1K_stat);
8602   } else if (bytes_per_sec <= 10240) {
8603     HTTP_INCREMENT_DYN_STAT(http_origin_server_speed_bytes_per_sec_10K_stat);
8604   } else if (bytes_per_sec <= 102400) {
8605     HTTP_INCREMENT_DYN_STAT(http_origin_server_speed_bytes_per_sec_100K_stat);
8606   } else if (bytes_per_sec <= 1048576) {
8607     HTTP_INCREMENT_DYN_STAT(http_origin_server_speed_bytes_per_sec_1M_stat);
8608   } else if (bytes_per_sec <= 10485760) {
8609     HTTP_INCREMENT_DYN_STAT(http_origin_server_speed_bytes_per_sec_10M_stat);
8610   } else {
8611     HTTP_INCREMENT_DYN_STAT(http_origin_server_speed_bytes_per_sec_100M_stat);
8612   }
8613 
8614   return;
8615 }
8616 
8617 void
update_size_and_time_stats(State * s,ink_hrtime total_time,ink_hrtime user_agent_write_time,ink_hrtime origin_server_read_time,int user_agent_request_header_size,int64_t user_agent_request_body_size,int user_agent_response_header_size,int64_t user_agent_response_body_size,int origin_server_request_header_size,int64_t origin_server_request_body_size,int origin_server_response_header_size,int64_t origin_server_response_body_size,int pushed_response_header_size,int64_t pushed_response_body_size,const TransactionMilestones & milestones)8618 HttpTransact::update_size_and_time_stats(State *s, ink_hrtime total_time, ink_hrtime user_agent_write_time,
8619                                          ink_hrtime origin_server_read_time, int user_agent_request_header_size,
8620                                          int64_t user_agent_request_body_size, int user_agent_response_header_size,
8621                                          int64_t user_agent_response_body_size, int origin_server_request_header_size,
8622                                          int64_t origin_server_request_body_size, int origin_server_response_header_size,
8623                                          int64_t origin_server_response_body_size, int pushed_response_header_size,
8624                                          int64_t pushed_response_body_size, const TransactionMilestones &milestones)
8625 {
8626   int64_t user_agent_request_size  = user_agent_request_header_size + user_agent_request_body_size;
8627   int64_t user_agent_response_size = user_agent_response_header_size + user_agent_response_body_size;
8628   int64_t user_agent_bytes         = user_agent_request_size + user_agent_response_size;
8629 
8630   int64_t origin_server_request_size  = origin_server_request_header_size + origin_server_request_body_size;
8631   int64_t origin_server_response_size = origin_server_response_header_size + origin_server_response_body_size;
8632   int64_t origin_server_bytes         = origin_server_request_size + origin_server_response_size;
8633 
8634   // Background fill stats
8635   switch (s->state_machine->background_fill) {
8636   case BACKGROUND_FILL_COMPLETED: {
8637     int64_t bg_size = origin_server_response_body_size - user_agent_response_body_size;
8638     bg_size         = std::max(static_cast<int64_t>(0), bg_size);
8639     HTTP_SUM_DYN_STAT(http_background_fill_bytes_completed_stat, bg_size);
8640     break;
8641   }
8642   case BACKGROUND_FILL_ABORTED: {
8643     int64_t bg_size = origin_server_response_body_size - user_agent_response_body_size;
8644 
8645     if (bg_size < 0) {
8646       bg_size = 0;
8647     }
8648     HTTP_SUM_DYN_STAT(http_background_fill_bytes_aborted_stat, bg_size);
8649     break;
8650   }
8651   case BACKGROUND_FILL_NONE:
8652     break;
8653   case BACKGROUND_FILL_STARTED:
8654   default:
8655     ink_assert(0);
8656   }
8657 
8658   // Bandwidth Savings
8659   switch (s->squid_codes.log_code) {
8660   case SQUID_LOG_TCP_HIT:
8661   case SQUID_LOG_TCP_MEM_HIT:
8662     // It's possible to have two stat's instead of one, if needed.
8663     HTTP_INCREMENT_DYN_STAT(http_tcp_hit_count_stat);
8664     HTTP_SUM_DYN_STAT(http_tcp_hit_user_agent_bytes_stat, user_agent_bytes);
8665     HTTP_SUM_DYN_STAT(http_tcp_hit_origin_server_bytes_stat, origin_server_bytes);
8666     break;
8667   case SQUID_LOG_TCP_MISS:
8668     HTTP_INCREMENT_DYN_STAT(http_tcp_miss_count_stat);
8669     HTTP_SUM_DYN_STAT(http_tcp_miss_user_agent_bytes_stat, user_agent_bytes);
8670     HTTP_SUM_DYN_STAT(http_tcp_miss_origin_server_bytes_stat, origin_server_bytes);
8671     break;
8672   case SQUID_LOG_TCP_EXPIRED_MISS:
8673     HTTP_INCREMENT_DYN_STAT(http_tcp_expired_miss_count_stat);
8674     HTTP_SUM_DYN_STAT(http_tcp_expired_miss_user_agent_bytes_stat, user_agent_bytes);
8675     HTTP_SUM_DYN_STAT(http_tcp_expired_miss_origin_server_bytes_stat, origin_server_bytes);
8676     break;
8677   case SQUID_LOG_TCP_REFRESH_HIT:
8678     HTTP_INCREMENT_DYN_STAT(http_tcp_refresh_hit_count_stat);
8679     HTTP_SUM_DYN_STAT(http_tcp_refresh_hit_user_agent_bytes_stat, user_agent_bytes);
8680     HTTP_SUM_DYN_STAT(http_tcp_refresh_hit_origin_server_bytes_stat, origin_server_bytes);
8681     break;
8682   case SQUID_LOG_TCP_REFRESH_MISS:
8683     HTTP_INCREMENT_DYN_STAT(http_tcp_refresh_miss_count_stat);
8684     HTTP_SUM_DYN_STAT(http_tcp_refresh_miss_user_agent_bytes_stat, user_agent_bytes);
8685     HTTP_SUM_DYN_STAT(http_tcp_refresh_miss_origin_server_bytes_stat, origin_server_bytes);
8686     break;
8687   case SQUID_LOG_TCP_CLIENT_REFRESH:
8688     HTTP_INCREMENT_DYN_STAT(http_tcp_client_refresh_count_stat);
8689     HTTP_SUM_DYN_STAT(http_tcp_client_refresh_user_agent_bytes_stat, user_agent_bytes);
8690     HTTP_SUM_DYN_STAT(http_tcp_client_refresh_origin_server_bytes_stat, origin_server_bytes);
8691     break;
8692   case SQUID_LOG_TCP_IMS_HIT:
8693     HTTP_INCREMENT_DYN_STAT(http_tcp_ims_hit_count_stat);
8694     HTTP_SUM_DYN_STAT(http_tcp_ims_hit_user_agent_bytes_stat, user_agent_bytes);
8695     HTTP_SUM_DYN_STAT(http_tcp_ims_hit_origin_server_bytes_stat, origin_server_bytes);
8696     break;
8697   case SQUID_LOG_TCP_IMS_MISS:
8698     HTTP_INCREMENT_DYN_STAT(http_tcp_ims_miss_count_stat);
8699     HTTP_SUM_DYN_STAT(http_tcp_ims_miss_user_agent_bytes_stat, user_agent_bytes);
8700     HTTP_SUM_DYN_STAT(http_tcp_ims_miss_origin_server_bytes_stat, origin_server_bytes);
8701     break;
8702   case SQUID_LOG_ERR_CLIENT_ABORT:
8703     HTTP_INCREMENT_DYN_STAT(http_err_client_abort_count_stat);
8704     HTTP_SUM_DYN_STAT(http_err_client_abort_user_agent_bytes_stat, user_agent_bytes);
8705     HTTP_SUM_DYN_STAT(http_err_client_abort_origin_server_bytes_stat, origin_server_bytes);
8706     break;
8707   case SQUID_LOG_ERR_CLIENT_READ_ERROR:
8708     HTTP_INCREMENT_DYN_STAT(http_err_client_read_error_count_stat);
8709     HTTP_SUM_DYN_STAT(http_err_client_read_error_user_agent_bytes_stat, user_agent_bytes);
8710     HTTP_SUM_DYN_STAT(http_err_client_read_error_origin_server_bytes_stat, origin_server_bytes);
8711     break;
8712   case SQUID_LOG_ERR_CONNECT_FAIL:
8713     HTTP_INCREMENT_DYN_STAT(http_err_connect_fail_count_stat);
8714     HTTP_SUM_DYN_STAT(http_err_connect_fail_user_agent_bytes_stat, user_agent_bytes);
8715     HTTP_SUM_DYN_STAT(http_err_connect_fail_origin_server_bytes_stat, origin_server_bytes);
8716     break;
8717   default:
8718     HTTP_INCREMENT_DYN_STAT(http_misc_count_stat);
8719     HTTP_SUM_DYN_STAT(http_misc_user_agent_bytes_stat, user_agent_bytes);
8720     HTTP_SUM_DYN_STAT(http_misc_origin_server_bytes_stat, origin_server_bytes);
8721     break;
8722   }
8723 
8724   // times
8725   HTTP_SUM_DYN_STAT(http_total_transactions_time_stat, total_time);
8726 
8727   // sizes
8728   HTTP_SUM_DYN_STAT(http_user_agent_request_header_total_size_stat, user_agent_request_header_size);
8729   HTTP_SUM_DYN_STAT(http_user_agent_response_header_total_size_stat, user_agent_response_header_size);
8730   HTTP_SUM_DYN_STAT(http_user_agent_request_document_total_size_stat, user_agent_request_body_size);
8731   HTTP_SUM_DYN_STAT(http_user_agent_response_document_total_size_stat, user_agent_response_body_size);
8732 
8733   // proxy stats
8734   if (s->current.request_to == HttpTransact::PARENT_PROXY) {
8735     HTTP_SUM_DYN_STAT(http_parent_proxy_request_total_bytes_stat,
8736                       origin_server_request_header_size + origin_server_request_body_size);
8737     HTTP_SUM_DYN_STAT(http_parent_proxy_response_total_bytes_stat,
8738                       origin_server_response_header_size + origin_server_response_body_size);
8739     HTTP_SUM_DYN_STAT(http_parent_proxy_transaction_time_stat, total_time);
8740   }
8741   // request header zero means the document was cached.
8742   // do not add to stats.
8743   if (origin_server_request_header_size > 0) {
8744     HTTP_SUM_DYN_STAT(http_origin_server_request_header_total_size_stat, origin_server_request_header_size);
8745     HTTP_SUM_DYN_STAT(http_origin_server_response_header_total_size_stat, origin_server_response_header_size);
8746     HTTP_SUM_DYN_STAT(http_origin_server_request_document_total_size_stat, origin_server_request_body_size);
8747     HTTP_SUM_DYN_STAT(http_origin_server_response_document_total_size_stat, origin_server_response_body_size);
8748   }
8749 
8750   if (s->method == HTTP_WKSIDX_PUSH) {
8751     HTTP_SUM_DYN_STAT(http_pushed_response_header_total_size_stat, pushed_response_header_size);
8752     HTTP_SUM_DYN_STAT(http_pushed_document_total_size_stat, pushed_response_body_size);
8753   }
8754 
8755   histogram_request_document_size(s, user_agent_request_body_size);
8756   histogram_response_document_size(s, user_agent_response_body_size);
8757 
8758   if (user_agent_write_time >= 0) {
8759     user_agent_connection_speed(s, user_agent_write_time, user_agent_response_size);
8760   }
8761 
8762   if (origin_server_request_header_size > 0 && origin_server_read_time > 0) {
8763     origin_server_connection_speed(s, origin_server_read_time, origin_server_response_size);
8764   }
8765 
8766   // update milestones stats
8767   HTTP_SUM_DYN_STAT(http_ua_begin_time_stat, milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_BEGIN));
8768   HTTP_SUM_DYN_STAT(http_ua_first_read_time_stat, milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_FIRST_READ));
8769   HTTP_SUM_DYN_STAT(http_ua_read_header_done_time_stat,
8770                     milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_READ_HEADER_DONE));
8771   HTTP_SUM_DYN_STAT(http_ua_begin_write_time_stat, milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_BEGIN_WRITE));
8772   HTTP_SUM_DYN_STAT(http_ua_close_time_stat, milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_UA_CLOSE));
8773   HTTP_SUM_DYN_STAT(http_server_first_connect_time_stat,
8774                     milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_FIRST_CONNECT));
8775   HTTP_SUM_DYN_STAT(http_server_connect_time_stat, milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_CONNECT));
8776   HTTP_SUM_DYN_STAT(http_server_connect_end_time_stat,
8777                     milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_CONNECT_END));
8778   HTTP_SUM_DYN_STAT(http_server_begin_write_time_stat,
8779                     milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_BEGIN_WRITE));
8780   HTTP_SUM_DYN_STAT(http_server_first_read_time_stat,
8781                     milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_FIRST_READ));
8782   HTTP_SUM_DYN_STAT(http_server_read_header_done_time_stat,
8783                     milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_READ_HEADER_DONE));
8784   HTTP_SUM_DYN_STAT(http_server_close_time_stat, milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SERVER_CLOSE));
8785   HTTP_SUM_DYN_STAT(http_cache_open_read_begin_time_stat,
8786                     milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_READ_BEGIN));
8787   HTTP_SUM_DYN_STAT(http_cache_open_read_end_time_stat,
8788                     milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_READ_END));
8789   HTTP_SUM_DYN_STAT(http_cache_open_write_begin_time_stat,
8790                     milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_WRITE_BEGIN));
8791   HTTP_SUM_DYN_STAT(http_cache_open_write_end_time_stat,
8792                     milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_CACHE_OPEN_WRITE_END));
8793   HTTP_SUM_DYN_STAT(http_dns_lookup_begin_time_stat,
8794                     milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_DNS_LOOKUP_BEGIN));
8795   HTTP_SUM_DYN_STAT(http_dns_lookup_end_time_stat, milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_DNS_LOOKUP_END));
8796   HTTP_SUM_DYN_STAT(http_sm_start_time_stat, milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SM_START));
8797   HTTP_SUM_DYN_STAT(http_sm_finish_time_stat, milestones.difference_msec(TS_MILESTONE_SM_START, TS_MILESTONE_SM_FINISH));
8798 }
8799 
8800 void
delete_warning_value(HTTPHdr * to_warn,HTTPWarningCode warning_code)8801 HttpTransact::delete_warning_value(HTTPHdr *to_warn, HTTPWarningCode warning_code)
8802 {
8803   int w_code       = static_cast<int>(warning_code);
8804   MIMEField *field = to_warn->field_find(MIME_FIELD_WARNING, MIME_LEN_WARNING);
8805 
8806   // Loop over the values to see if we need to do anything
8807   if (field) {
8808     HdrCsvIter iter;
8809     int val_code;
8810     MIMEField *new_field = nullptr;
8811 
8812     bool valid_p = iter.get_first_int(field, val_code);
8813 
8814     while (valid_p) {
8815       if (val_code == w_code) {
8816         // Ok, found the value we're look to delete Look over and create a new field appending all
8817         // elements that are not this value
8818         valid_p = iter.get_first_int(field, val_code);
8819 
8820         while (valid_p) {
8821           if (val_code != warning_code) {
8822             auto value = iter.get_current();
8823             if (new_field) {
8824               new_field->value_append(to_warn->m_heap, to_warn->m_mime, value.data(), value.size(), true);
8825             } else {
8826               new_field = to_warn->field_create();
8827               to_warn->field_value_set(new_field, value.data(), value.size());
8828             }
8829           }
8830           valid_p = iter.get_next_int(val_code);
8831         }
8832 
8833         to_warn->field_delete(MIME_FIELD_WARNING, MIME_LEN_WARNING);
8834         if (new_field) {
8835           new_field->name_set(to_warn->m_heap, to_warn->m_mime, MIME_FIELD_WARNING, MIME_LEN_WARNING);
8836           to_warn->field_attach(new_field);
8837         }
8838 
8839         return;
8840       }
8841       valid_p = iter.get_next_int(val_code);
8842     }
8843   }
8844 }
8845 
8846 void
change_response_header_because_of_range_request(State * s,HTTPHdr * header)8847 HttpTransact::change_response_header_because_of_range_request(State *s, HTTPHdr *header)
8848 {
8849   MIMEField *field;
8850   char *reason_phrase;
8851 
8852   TxnDebug("http_trans", "Partial content requested, re-calculating content-length");
8853 
8854   header->status_set(HTTP_STATUS_PARTIAL_CONTENT);
8855   reason_phrase = const_cast<char *>(http_hdr_reason_lookup(HTTP_STATUS_PARTIAL_CONTENT));
8856   header->reason_set(reason_phrase, strlen(reason_phrase));
8857 
8858   // set the right Content-Type for multiple entry Range
8859   if (s->num_range_fields > 1) {
8860     field = header->field_find(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
8861 
8862     if (field != nullptr) {
8863       header->field_delete(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
8864     }
8865 
8866     field = header->field_create(MIME_FIELD_CONTENT_TYPE, MIME_LEN_CONTENT_TYPE);
8867     field->value_append(header->m_heap, header->m_mime, range_type, sizeof(range_type) - 1);
8868 
8869     header->field_attach(field);
8870     // TODO: There's a known bug here where the Content-Length is not correct for multi-part
8871     // Range: requests.
8872     header->set_content_length(s->range_output_cl);
8873   } else {
8874     if (s->cache_info.object_read && s->cache_info.object_read->valid()) {
8875       // TODO: It's unclear under which conditions we need to update the Content-Range: header,
8876       // many times it's already set correctly before calling this. For now, always try do it
8877       // when we have the information for it available.
8878       // TODO: Also, it's unclear as to why object_read->valid() is not always true here.
8879       char numbers[RANGE_NUMBERS_LENGTH];
8880       header->field_delete(MIME_FIELD_CONTENT_RANGE, MIME_LEN_CONTENT_RANGE);
8881       field = header->field_create(MIME_FIELD_CONTENT_RANGE, MIME_LEN_CONTENT_RANGE);
8882       snprintf(numbers, sizeof(numbers), "bytes %" PRId64 "-%" PRId64 "/%" PRId64, s->ranges[0]._start, s->ranges[0]._end,
8883                s->cache_info.object_read->object_size_get());
8884       field->value_set(header->m_heap, header->m_mime, numbers, strlen(numbers));
8885       header->field_attach(field);
8886     }
8887     // Always update the Content-Length: header.
8888     header->set_content_length(s->range_output_cl);
8889   }
8890 }
8891 
8892 #if TS_HAS_TESTS
8893 void forceLinkRegressionHttpTransact();
8894 void
forceLinkRegressionHttpTransactCaller()8895 forceLinkRegressionHttpTransactCaller()
8896 {
8897   forceLinkRegressionHttpTransact();
8898 }
8899 #endif
8900