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