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 <bitset>
25 #include <algorithm>
26 #include <array>
27 #include <string_view>
28 
29 #include "tscore/ink_platform.h"
30 #include "tscore/BufferWriter.h"
31 
32 #include "HttpTransact.h"
33 #include "HttpTransactHeaders.h"
34 #include "HTTP.h"
35 #include "HdrUtils.h"
36 #include "HttpCompat.h"
37 #include "HttpSM.h"
38 #include "PoolableSession.h"
39 
40 #include "I_Machine.h"
41 
42 using namespace std::literals;
43 
44 bool
is_method_cacheable(const HttpConfigParams * http_config_param,const int method)45 HttpTransactHeaders::is_method_cacheable(const HttpConfigParams *http_config_param, const int method)
46 {
47   return (method == HTTP_WKSIDX_GET || method == HTTP_WKSIDX_HEAD ||
48           (http_config_param->cache_post_method == 1 && method == HTTP_WKSIDX_POST));
49 }
50 
51 bool
is_method_cache_lookupable(int method)52 HttpTransactHeaders::is_method_cache_lookupable(int method)
53 {
54   // responses to GET, HEAD, and POST are cacheable
55   // URL's requested in DELETE and PUT are looked up to remove cached copies
56   return (method == HTTP_WKSIDX_GET || method == HTTP_WKSIDX_HEAD || method == HTTP_WKSIDX_POST || method == HTTP_WKSIDX_DELETE ||
57           method == HTTP_WKSIDX_PUT || method == HTTP_WKSIDX_PURGE || method == HTTP_WKSIDX_PUSH);
58 }
59 
60 bool
is_this_a_hop_by_hop_header(const char * field_name)61 HttpTransactHeaders::is_this_a_hop_by_hop_header(const char *field_name)
62 {
63   if (!hdrtoken_is_wks(field_name)) {
64     return (false);
65   }
66   if ((hdrtoken_wks_to_flags(field_name) & HTIF_HOPBYHOP) && (field_name != MIME_FIELD_KEEP_ALIVE)) {
67     return (true);
68   } else {
69     return (false);
70   }
71 }
72 
73 bool
is_this_method_supported(int the_scheme,int the_method)74 HttpTransactHeaders::is_this_method_supported(int the_scheme, int the_method)
75 {
76   if (the_method == HTTP_WKSIDX_CONNECT) {
77     return true;
78   } else if (the_scheme == URL_WKSIDX_HTTP || the_scheme == URL_WKSIDX_HTTPS) {
79     return is_this_http_method_supported(the_method);
80   } else if ((the_scheme == URL_WKSIDX_WS || the_scheme == URL_WKSIDX_WSS) && the_method == HTTP_WKSIDX_GET) {
81     return true;
82   } else {
83     return false;
84   }
85 }
86 
87 bool
is_method_safe(int method)88 HttpTransactHeaders::is_method_safe(int method)
89 {
90   return (method == HTTP_WKSIDX_GET || method == HTTP_WKSIDX_OPTIONS || method == HTTP_WKSIDX_HEAD || method == HTTP_WKSIDX_TRACE);
91 }
92 
93 bool
is_method_idempotent(int method)94 HttpTransactHeaders::is_method_idempotent(int method)
95 {
96   return (method == HTTP_WKSIDX_CONNECT || method == HTTP_WKSIDX_DELETE || method == HTTP_WKSIDX_GET ||
97           method == HTTP_WKSIDX_HEAD || method == HTTP_WKSIDX_PUT || method == HTTP_WKSIDX_OPTIONS || method == HTTP_WKSIDX_TRACE);
98 }
99 
100 void
insert_supported_methods_in_response(HTTPHdr * response,int scheme)101 HttpTransactHeaders::insert_supported_methods_in_response(HTTPHdr *response, int scheme)
102 {
103   int method_output_lengths[32];
104   const char *methods[] = {
105     HTTP_METHOD_CONNECT, HTTP_METHOD_DELETE, HTTP_METHOD_GET, HTTP_METHOD_HEAD, HTTP_METHOD_OPTIONS,
106     HTTP_METHOD_POST,    HTTP_METHOD_PURGE,  HTTP_METHOD_PUT, HTTP_METHOD_PUSH, HTTP_METHOD_TRACE,
107   };
108   char inline_buffer[64];
109   char *alloced_buffer, *value_buffer;
110 
111   int nmethods = sizeof(methods) / sizeof(methods[0]);
112   ink_assert(nmethods <= 32);
113 
114   char *p;
115   int i, is_supported;
116   size_t bytes              = 0;
117   int num_methods_supported = 0;
118   MIMEField *field;
119 
120   // step 1: determine supported methods, count bytes & allocate
121   for (i = 0; i < nmethods; i++) {
122     const char *method_wks = methods[i];
123     ink_assert(hdrtoken_is_wks(method_wks));
124 
125     is_supported = is_this_method_supported(scheme, hdrtoken_wks_to_index(method_wks));
126 
127     if (is_supported) {
128       ++num_methods_supported;
129       method_output_lengths[i] = hdrtoken_wks_to_length(method_wks);
130       bytes += method_output_lengths[i];
131       if (num_methods_supported > 1) {
132         bytes += 2; // +2 if need leading ", "
133       }
134     } else {
135       method_output_lengths[i] = 0;
136     }
137   }
138 
139   // step 2: create Allow field if not present
140   field = response->field_find(MIME_FIELD_ALLOW, MIME_LEN_ALLOW);
141   if (!field) {
142     field = response->field_create(MIME_FIELD_ALLOW, MIME_LEN_ALLOW);
143     response->field_attach(field);
144   }
145   // step 3: get a big enough buffer
146   if (bytes <= sizeof(inline_buffer)) {
147     alloced_buffer = nullptr;
148     value_buffer   = inline_buffer;
149   } else {
150     alloced_buffer = static_cast<char *>(ats_malloc(bytes));
151     value_buffer   = alloced_buffer;
152   }
153 
154   // step 4: build the value
155   p = value_buffer;
156   for (i = 0; i < nmethods; i++) {
157     if (method_output_lengths[i]) {
158       memcpy(p, methods[i], method_output_lengths[i]);
159       p += method_output_lengths[i];
160       if (num_methods_supported > 1) {
161         *p++ = ',';
162         *p++ = ' ';
163       }
164       --num_methods_supported;
165     }
166   }
167 
168   // FIXME: do we really want to append to old list, or replace old list?
169 
170   // step 5: attach new allow list to end of previous list
171   field->value_append(response->m_heap, response->m_mime, value_buffer, bytes);
172 
173   // step 6: free up temp storage
174   ats_free(alloced_buffer);
175 }
176 
177 void
build_base_response(HTTPHdr * outgoing_response,HTTPStatus status,const char * reason_phrase,int reason_phrase_len,ink_time_t date)178 HttpTransactHeaders::build_base_response(HTTPHdr *outgoing_response, HTTPStatus status, const char *reason_phrase,
179                                          int reason_phrase_len, ink_time_t date)
180 {
181   if (!outgoing_response->valid()) {
182     outgoing_response->create(HTTP_TYPE_RESPONSE);
183   }
184 
185   ink_assert(outgoing_response->type_get() == HTTP_TYPE_RESPONSE);
186 
187   outgoing_response->version_set(HTTPVersion(1, 1));
188   outgoing_response->status_set(status);
189   outgoing_response->reason_set(reason_phrase, reason_phrase_len);
190   outgoing_response->set_date(date);
191 }
192 
193 ////////////////////////////////////////////////////////////////////////
194 // Copy all non hop-by-hop header fields from src_hdr to new_hdr.
195 // If header Date: is not present or invalid in src_hdr,
196 // then the given date will be used.
197 void
copy_header_fields(HTTPHdr * src_hdr,HTTPHdr * new_hdr,bool retain_proxy_auth_hdrs,ink_time_t date)198 HttpTransactHeaders::copy_header_fields(HTTPHdr *src_hdr, HTTPHdr *new_hdr, bool retain_proxy_auth_hdrs, ink_time_t date)
199 {
200   ink_assert(src_hdr->valid());
201   ink_assert(!new_hdr->valid());
202 
203   MIMEField *field;
204   MIMEFieldIter field_iter;
205   bool date_hdr = false;
206 
207   // Start with an exact duplicate
208   new_hdr->copy(src_hdr);
209 
210   // Nuke hop-by-hop headers
211   //
212   //    The hop-by-hop header fields are laid out by the spec
213   //    with two adjustments
214   //      1) we treat TE as hop-by-hop because spec implies
215   //         that it is by declaring anyone who sends a TE must
216   //         include TE in the connection header.  This in
217   //         my opinion error prone and if the client doesn't follow the spec
218   //         we'll have problems with the TE being forwarded to the server
219   //         and us caching the transfer encoded documents and then
220   //         serving it to a client that can not handle it
221   //      2) Transfer encoding is copied.  If the transfer encoding
222   //         is changed for example by dechunking, the transfer encoding
223   //         should be modified when when the decision is made to dechunk it
224 
225   for (field = new_hdr->iter_get_first(&field_iter); field != nullptr; field = new_hdr->iter_get_next(&field_iter)) {
226     if (field->m_wks_idx == -1) {
227       continue;
228     }
229 
230     int field_flags = hdrtoken_index_to_flags(field->m_wks_idx);
231 
232     if (field_flags & HTIF_HOPBYHOP) {
233       // Delete header if not in special proxy_auth retention mode
234       if ((!retain_proxy_auth_hdrs) || (!(field_flags & HTIF_PROXYAUTH))) {
235         new_hdr->field_delete(field);
236       }
237     } else if (field->m_wks_idx == MIME_WKSIDX_DATE) {
238       date_hdr = true;
239     }
240   }
241 
242   // Set date hdr if not already set and valid value passed in
243   if ((date_hdr == false) && (date > 0)) {
244     new_hdr->set_date(date);
245   }
246 }
247 
248 ////////////////////////////////////////////////////////////////////////
249 // Just convert the outgoing request to the appropriate version
250 void
convert_request(HTTPVersion outgoing_ver,HTTPHdr * outgoing_request)251 HttpTransactHeaders::convert_request(HTTPVersion outgoing_ver, HTTPHdr *outgoing_request)
252 {
253   if (outgoing_ver == HTTPVersion(1, 0)) {
254     convert_to_1_0_request_header(outgoing_request);
255   } else if (outgoing_ver == HTTPVersion(1, 1)) {
256     convert_to_1_1_request_header(outgoing_request);
257   } else {
258     Debug("http_trans", "[HttpTransactHeaders::convert_request]"
259                         "Unsupported Version - passing through");
260   }
261 }
262 
263 ////////////////////////////////////////////////////////////////////////
264 // Just convert the outgoing response to the appropriate version
265 void
convert_response(HTTPVersion outgoing_ver,HTTPHdr * outgoing_response)266 HttpTransactHeaders::convert_response(HTTPVersion outgoing_ver, HTTPHdr *outgoing_response)
267 {
268   if (outgoing_ver == HTTPVersion(1, 0)) {
269     convert_to_1_0_response_header(outgoing_response);
270   } else if (outgoing_ver == HTTPVersion(1, 1)) {
271     convert_to_1_1_response_header(outgoing_response);
272   } else {
273     Debug("http_trans", "[HttpTransactHeaders::convert_response]"
274                         "Unsupported Version - passing through");
275   }
276 }
277 
278 ////////////////////////////////////////////////////////////////////////
279 // Take an existing outgoing request header and make it HTTP/1.0
280 void
convert_to_1_0_request_header(HTTPHdr * outgoing_request)281 HttpTransactHeaders::convert_to_1_0_request_header(HTTPHdr *outgoing_request)
282 {
283   // These are required
284   ink_assert(outgoing_request->url_get()->valid());
285 
286   // Set HTTP version to 1.0
287   outgoing_request->version_set(HTTPVersion(1, 0));
288 
289   // FIXME (P2): Need to change cache directives into pragma, cleanly
290   //             Now, any Cache-Control hdr becomes Pragma: no-cache
291 
292   if (outgoing_request->presence(MIME_PRESENCE_CACHE_CONTROL) && !outgoing_request->is_pragma_no_cache_set()) {
293     outgoing_request->value_append(MIME_FIELD_PRAGMA, MIME_LEN_PRAGMA, "no-cache", 8, true);
294   }
295   // We do not currently support chunked transfer encoding,
296   // so specify that response should use identity transfer coding.
297   // outgoing_request->value_insert(MIME_FIELD_TE, "identity;q=1.0");
298   // outgoing_request->value_insert(MIME_FIELD_TE, "chunked;q=0.0");
299 }
300 
301 ////////////////////////////////////////////////////////////////////////
302 // Take an existing outgoing request header and make it HTTP/1.1
303 void
convert_to_1_1_request_header(HTTPHdr * outgoing_request)304 HttpTransactHeaders::convert_to_1_1_request_header(HTTPHdr *outgoing_request)
305 {
306   // These are required
307   ink_assert(outgoing_request->url_get()->valid());
308   ink_assert(outgoing_request->version_get() == HTTPVersion(1, 1));
309 
310   if (outgoing_request->get_cooked_pragma_no_cache() && !(outgoing_request->get_cooked_cc_mask() & MIME_COOKED_MASK_CC_NO_CACHE)) {
311     outgoing_request->value_append(MIME_FIELD_CACHE_CONTROL, MIME_LEN_CACHE_CONTROL, "no-cache", 8, true);
312   }
313   // We do not currently support chunked transfer encoding,
314   // so specify that response should use identity transfer coding.
315   // outgoing_request->value_insert(MIME_FIELD_TE, "identity;q=1.0");
316   // outgoing_request->value_insert(MIME_FIELD_TE, "chunked;q=0.0");
317 }
318 
319 ////////////////////////////////////////////////////////////////////////
320 // Take an existing outgoing response header and make it HTTP/1.0
321 void
convert_to_1_0_response_header(HTTPHdr * outgoing_response)322 HttpTransactHeaders::convert_to_1_0_response_header(HTTPHdr *outgoing_response)
323 {
324   //     // These are required
325   //     ink_assert(outgoing_response->status_get());
326   //     ink_assert(outgoing_response->reason_get());
327 
328   // Set HTTP version to 1.0
329   outgoing_response->version_set(HTTPVersion(1, 0));
330 
331   // Keep-Alive?
332 
333   // Cache-Control?
334 }
335 
336 ////////////////////////////////////////////////////////////////////////
337 // Take an existing outgoing response header and make it HTTP/1.1
338 void
convert_to_1_1_response_header(HTTPHdr * outgoing_response)339 HttpTransactHeaders::convert_to_1_1_response_header(HTTPHdr *outgoing_response)
340 {
341   // These are required
342   ink_assert(outgoing_response->status_get());
343 
344   // Set HTTP version to 1.1
345   //    ink_assert(outgoing_response->version_get() == HTTPVersion (1, 1));
346   outgoing_response->version_set(HTTPVersion(1, 1));
347 }
348 
349 ///////////////////////////////////////////////////////////////////////////////
350 // Name       : calculate_document_age()
351 // Description: returns age of document
352 //
353 // Input      :
354 // Output     : ink_time_t age
355 //
356 // Details    :
357 //   Algorithm is straight out of March 1998 1.1 specs, Section 13.2.3
358 //
359 ///////////////////////////////////////////////////////////////////////////////
360 ink_time_t
calculate_document_age(ink_time_t request_time,ink_time_t response_time,HTTPHdr * base_response,ink_time_t base_response_date,ink_time_t now)361 HttpTransactHeaders::calculate_document_age(ink_time_t request_time, ink_time_t response_time, HTTPHdr *base_response,
362                                             ink_time_t base_response_date, ink_time_t now)
363 {
364   ink_time_t age_value              = base_response->get_age();
365   ink_time_t date_value             = 0;
366   ink_time_t apparent_age           = 0;
367   ink_time_t corrected_received_age = 0;
368   ink_time_t response_delay         = 0;
369   ink_time_t corrected_initial_age  = 0;
370   ink_time_t current_age            = 0;
371   ink_time_t resident_time          = 0;
372   ink_time_t now_value              = 0;
373 
374   ink_time_t tmp_value = 0;
375 
376   tmp_value  = base_response_date;
377   date_value = (tmp_value > 0) ? tmp_value : 0;
378 
379   // Deal with clock skew. Sigh.
380   //
381   // TODO solve this global clock problem
382   now_value = std::max(now, response_time);
383 
384   ink_assert(response_time >= 0);
385   ink_assert(request_time >= 0);
386   ink_assert(response_time >= request_time);
387   ink_assert(now_value >= response_time);
388 
389   if (date_value > 0) {
390     apparent_age = std::max(static_cast<time_t>(0), (response_time - date_value));
391   }
392   if (age_value < 0) {
393     current_age = -1; // Overflow from Age: header
394   } else {
395     corrected_received_age = std::max(apparent_age, age_value);
396     response_delay         = response_time - request_time;
397     corrected_initial_age  = corrected_received_age + response_delay;
398     resident_time          = now_value - response_time;
399     current_age            = corrected_initial_age + resident_time;
400   }
401 
402   Debug("http_age", "[calculate_document_age] age_value:              %" PRId64, (int64_t)age_value);
403   Debug("http_age", "[calculate_document_age] date_value:             %" PRId64, (int64_t)date_value);
404   Debug("http_age", "[calculate_document_age] response_time:          %" PRId64, (int64_t)response_time);
405   Debug("http_age", "[calculate_document_age] now:                    %" PRId64, (int64_t)now);
406   Debug("http_age", "[calculate_document_age] now (fixed):            %" PRId64, (int64_t)now_value);
407   Debug("http_age", "[calculate_document_age] apparent_age:           %" PRId64, (int64_t)apparent_age);
408   Debug("http_age", "[calculate_document_age] corrected_received_age: %" PRId64, (int64_t)corrected_received_age);
409   Debug("http_age", "[calculate_document_age] response_delay:         %" PRId64, (int64_t)response_delay);
410   Debug("http_age", "[calculate_document_age] corrected_initial_age:  %" PRId64, (int64_t)corrected_initial_age);
411   Debug("http_age", "[calculate_document_age] resident_time:          %" PRId64, (int64_t)resident_time);
412   Debug("http_age", "[calculate_document_age] current_age:            %" PRId64, (int64_t)current_age);
413 
414   return current_age;
415 }
416 
417 bool
does_server_allow_response_to_be_stored(HTTPHdr * resp)418 HttpTransactHeaders::does_server_allow_response_to_be_stored(HTTPHdr *resp)
419 {
420   uint32_t cc_mask = (MIME_COOKED_MASK_CC_NO_STORE | MIME_COOKED_MASK_CC_PRIVATE);
421 
422   if ((resp->get_cooked_cc_mask() & cc_mask) || (resp->get_cooked_pragma_no_cache())) {
423     return false;
424   } else {
425     return true;
426   }
427 }
428 
429 bool
downgrade_request(bool * origin_server_keep_alive,HTTPHdr * outgoing_request)430 HttpTransactHeaders::downgrade_request(bool *origin_server_keep_alive, HTTPHdr *outgoing_request)
431 {
432   // HTTPVersion ver;
433   /* First try turning keep_alive off */
434   if (*origin_server_keep_alive) {
435     *origin_server_keep_alive = false;
436     // ver.set(outgoing_request->version_get());
437   }
438 
439   if (outgoing_request->version_get() == HTTPVersion(1, 1)) {
440     // ver.set (HTTPVersion (1, 0));
441     convert_to_1_0_request_header(outgoing_request);
442   } else {
443     return false;
444   }
445 
446   return true;
447 }
448 
449 void
generate_and_set_squid_codes(HTTPHdr * header,char * via_string,HttpTransact::SquidLogInfo * squid_codes)450 HttpTransactHeaders::generate_and_set_squid_codes(HTTPHdr *header, char *via_string, HttpTransact::SquidLogInfo *squid_codes)
451 {
452   SquidLogCode log_code          = SQUID_LOG_EMPTY;
453   SquidHierarchyCode hier_code   = SQUID_HIER_EMPTY;
454   SquidHitMissCode hit_miss_code = SQUID_HIT_RESERVED;
455 
456   /////////////////////////////
457   // First the Hit-Miss Code //
458   /////////////////////////////
459   if ((via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_HIT_CONDITIONAL) ||
460       (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_MISS_CONDITIONAL) ||
461       (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_HIT_SERVED)) {
462     // its a cache hit.
463     if (via_string[VIA_CACHE_RESULT] == VIA_IN_RAM_CACHE_FRESH) {
464       hit_miss_code = SQUID_HIT_RAM;
465     } else { // TODO: Support other cache tiers here
466       hit_miss_code = SQUID_HIT_RESERVED;
467     }
468   } else {
469     int reason_len;
470     const char *reason = header->reason_get(&reason_len);
471 
472     if (reason != nullptr && reason_len >= 24 && reason[0] == '!' && reason[1] == SQUID_HIT_RESERVED) {
473       hit_miss_code = SQUID_HIT_RESERVED;
474       // its a miss in the cache. find out why.
475     } else if (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_MISS_EXPIRED) {
476       hit_miss_code = SQUID_MISS_PRE_EXPIRED;
477     } else if (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_MISS_CONFIG) {
478       hit_miss_code = SQUID_MISS_NONE;
479     } else if (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_MISS_CLIENT) {
480       hit_miss_code = SQUID_MISS_PRAGMA_NOCACHE;
481     } else if (via_string[VIA_DETAIL_CACHE_LOOKUP] == VIA_DETAIL_MISS_METHOD) {
482       hit_miss_code = SQUID_MISS_HTTP_NON_CACHE;
483     } else if (via_string[VIA_CLIENT_REQUEST] == VIA_CLIENT_ERROR) {
484       hit_miss_code = SQUID_MISS_ERROR;
485     } else if (via_string[VIA_CLIENT_REQUEST] == VIA_CLIENT_NO_CACHE) {
486       hit_miss_code = SQUID_MISS_PRAGMA_NOCACHE;
487     } else {
488       hit_miss_code = SQUID_MISS_NONE;
489     }
490   }
491 
492   //////////////////////
493   // Now the Log Code //
494   //////////////////////
495   if (via_string[VIA_CLIENT_REQUEST] == VIA_CLIENT_NO_CACHE) {
496     log_code = SQUID_LOG_TCP_CLIENT_REFRESH;
497   }
498 
499   else {
500     if (via_string[VIA_CLIENT_REQUEST] == VIA_CLIENT_IMS) {
501       if ((via_string[VIA_CACHE_RESULT] == VIA_IN_CACHE_FRESH) || (via_string[VIA_CACHE_RESULT] == VIA_IN_RAM_CACHE_FRESH)) {
502         log_code = SQUID_LOG_TCP_IMS_HIT;
503       } else {
504         if (via_string[VIA_CACHE_RESULT] == VIA_IN_CACHE_STALE && via_string[VIA_SERVER_RESULT] == VIA_SERVER_NOT_MODIFIED) {
505           log_code = SQUID_LOG_TCP_REFRESH_HIT;
506         } else {
507           log_code = SQUID_LOG_TCP_IMS_MISS;
508         }
509       }
510     }
511 
512     else {
513       if (via_string[VIA_CACHE_RESULT] == VIA_IN_CACHE_STALE) {
514         if (via_string[VIA_SERVER_RESULT] == VIA_SERVER_NOT_MODIFIED) {
515           log_code = SQUID_LOG_TCP_REFRESH_HIT;
516         } else {
517           if (via_string[VIA_SERVER_RESULT] == VIA_SERVER_ERROR) {
518             log_code = SQUID_LOG_TCP_REF_FAIL_HIT;
519           } else {
520             log_code = SQUID_LOG_TCP_REFRESH_MISS;
521           }
522         }
523       } else {
524         if (via_string[VIA_CACHE_RESULT] == VIA_IN_CACHE_FRESH) {
525           log_code = SQUID_LOG_TCP_HIT;
526         } else if (via_string[VIA_CACHE_RESULT] == VIA_IN_RAM_CACHE_FRESH) {
527           log_code = SQUID_LOG_TCP_MEM_HIT;
528         } else {
529           log_code = SQUID_LOG_TCP_MISS;
530         }
531       }
532     }
533   }
534 
535   ////////////////////////
536   // The Hierarchy Code //
537   ////////////////////////
538   if ((via_string[VIA_CACHE_RESULT] == VIA_IN_CACHE_FRESH) || (via_string[VIA_CACHE_RESULT] == VIA_IN_RAM_CACHE_FRESH)) {
539     hier_code = SQUID_HIER_NONE;
540   } else if (via_string[VIA_DETAIL_PP_CONNECT] == VIA_DETAIL_PP_SUCCESS) {
541     hier_code = SQUID_HIER_PARENT_HIT;
542   } else if (via_string[VIA_DETAIL_CACHE_TYPE] == VIA_DETAIL_PARENT) {
543     hier_code = SQUID_HIER_DEFAULT_PARENT;
544   } else if (via_string[VIA_DETAIL_TUNNEL] == VIA_DETAIL_TUNNEL_NO_FORWARD) {
545     hier_code = SQUID_HIER_NONE;
546   } else {
547     hier_code = SQUID_HIER_DIRECT;
548   }
549 
550   // Errors may override the other codes, so check the via string error codes last
551   switch (via_string[VIA_ERROR_TYPE]) {
552   case VIA_ERROR_AUTHORIZATION:
553     log_code = SQUID_LOG_ERR_PROXY_DENIED;
554     break;
555   case VIA_ERROR_CONNECTION:
556     if (log_code == SQUID_LOG_TCP_MISS || log_code == SQUID_LOG_TCP_REFRESH_MISS) {
557       log_code = SQUID_LOG_ERR_CONNECT_FAIL;
558     }
559     break;
560   case VIA_ERROR_DNS_FAILURE:
561     log_code  = SQUID_LOG_ERR_DNS_FAIL;
562     hier_code = SQUID_HIER_NONE;
563     break;
564   case VIA_ERROR_FORBIDDEN:
565     log_code = SQUID_LOG_ERR_PROXY_DENIED;
566     break;
567   case VIA_ERROR_HEADER_SYNTAX:
568     log_code  = SQUID_LOG_ERR_INVALID_REQ;
569     hier_code = SQUID_HIER_NONE;
570     break;
571   case VIA_ERROR_SERVER:
572     if (log_code == SQUID_LOG_TCP_MISS || log_code == SQUID_LOG_TCP_IMS_MISS) {
573       log_code = SQUID_LOG_ERR_CONNECT_FAIL;
574     }
575     break;
576   case VIA_ERROR_TIMEOUT:
577     if (log_code == SQUID_LOG_TCP_MISS || log_code == SQUID_LOG_TCP_IMS_MISS) {
578       log_code = SQUID_LOG_ERR_READ_TIMEOUT;
579     }
580     if (hier_code == SQUID_HIER_PARENT_HIT) {
581       hier_code = SQUID_HIER_TIMEOUT_PARENT_HIT;
582     } else {
583       hier_code = SQUID_HIER_TIMEOUT_DIRECT;
584     }
585     break;
586   case VIA_ERROR_CACHE_READ:
587     log_code  = SQUID_LOG_TCP_SWAPFAIL;
588     hier_code = SQUID_HIER_NONE;
589     break;
590   case VIA_ERROR_LOOP_DETECTED:
591     log_code  = SQUID_LOG_ERR_LOOP_DETECTED;
592     hier_code = SQUID_HIER_NONE;
593     break;
594   default:
595     break;
596   }
597 
598   squid_codes->log_code      = log_code;
599   squid_codes->hier_code     = hier_code;
600   squid_codes->hit_miss_code = hit_miss_code;
601 }
602 
603 #include "HttpDebugNames.h"
604 
605 void
insert_warning_header(HttpConfigParams * http_config_param,HTTPHdr * header,HTTPWarningCode code,const char * warn_text,int warn_text_len)606 HttpTransactHeaders::insert_warning_header(HttpConfigParams *http_config_param, HTTPHdr *header, HTTPWarningCode code,
607                                            const char *warn_text, int warn_text_len)
608 {
609   int bufsize, len;
610 
611   // + 23 for 20 possible digits of warning code (long long max
612   //  digits) & 2 spaces & the string terminator
613   bufsize = http_config_param->proxy_response_via_string_len + 23;
614   if (warn_text != nullptr) {
615     bufsize += warn_text_len;
616   } else {
617     warn_text_len = 0; // Make sure it's really zero
618   }
619 
620   char *warning_text = static_cast<char *>(alloca(bufsize));
621 
622   len =
623     snprintf(warning_text, bufsize, "%3d %s %.*s", code, http_config_param->proxy_response_via_string, warn_text_len, warn_text);
624   header->value_set(MIME_FIELD_WARNING, MIME_LEN_WARNING, warning_text, len);
625 }
626 
627 void
insert_time_and_age_headers_in_response(ink_time_t request_sent_time,ink_time_t response_received_time,ink_time_t now,HTTPHdr * base,HTTPHdr * outgoing)628 HttpTransactHeaders::insert_time_and_age_headers_in_response(ink_time_t request_sent_time, ink_time_t response_received_time,
629                                                              ink_time_t now, HTTPHdr *base, HTTPHdr *outgoing)
630 {
631   ink_time_t date        = base->get_date();
632   ink_time_t current_age = calculate_document_age(request_sent_time, response_received_time, base, date, now);
633 
634   outgoing->set_age(current_age); // set_age() deals with overflow properly, so pass it along
635 
636   // FIX: should handle missing date when response is received, not here.
637   //      See INKqa09852.
638   if (date <= 0) {
639     outgoing->set_date(now);
640   }
641 }
642 
643 /// write the protocol stack to the @a via_string.
644 /// If @a detailed then do the full stack, otherwise just the "top level" protocol.
645 /// Returns the number of characters appended to hdr_string (no nul appended).
646 int
write_hdr_protocol_stack(char * hdr_string,size_t len,ProtocolStackDetail pSDetail,std::string_view * proto_buf,int n_proto,char separator)647 HttpTransactHeaders::write_hdr_protocol_stack(char *hdr_string, size_t len, ProtocolStackDetail pSDetail,
648                                               std::string_view *proto_buf, int n_proto, char separator)
649 {
650   char *hdr   = hdr_string; // keep original pointer for size computation later.
651   char *limit = hdr_string + len;
652 
653   if (n_proto <= 0 || hdr == nullptr || len <= 0) {
654     // nothing
655   } else if (ProtocolStackDetail::Full == pSDetail) {
656     for (std::string_view *v = proto_buf, *v_limit = proto_buf + n_proto; v < v_limit && (hdr + v->size() + 1) < limit; ++v) {
657       if (v != proto_buf) {
658         *hdr++ = separator;
659       }
660       memcpy(hdr, v->data(), v->size());
661       hdr += v->size();
662     }
663   } else {
664     std::string_view *proto_end = proto_buf + n_proto;
665     bool http_1_0_p             = std::find(proto_buf, proto_end, IP_PROTO_TAG_HTTP_1_0) != proto_end;
666     bool http_1_1_p             = std::find(proto_buf, proto_end, IP_PROTO_TAG_HTTP_1_1) != proto_end;
667 
668     if ((http_1_0_p || http_1_1_p) && hdr + 10 < limit) {
669       bool tls_p = std::find_if(proto_buf, proto_end, [](std::string_view tag) { return IsPrefixOf("tls/"sv, tag); }) != proto_end;
670 
671       memcpy(hdr, "http", 4);
672       hdr += 4;
673       if (tls_p) {
674         *hdr++ = 's';
675       }
676 
677       // If detail level is compact (RFC 7239 compliant "proto" value for Forwarded field), stop here.
678 
679       if (ProtocolStackDetail::Standard == pSDetail) {
680         *hdr++        = '/';
681         bool http_2_p = std::find(proto_buf, proto_end, IP_PROTO_TAG_HTTP_2_0) != proto_end;
682         if (http_2_p) {
683           *hdr++ = '2';
684         } else if (http_1_0_p) {
685           memcpy(hdr, "1.0", 3);
686           hdr += 3;
687         } else if (http_1_1_p) {
688           memcpy(hdr, "1.1", 3);
689           hdr += 3;
690         }
691       }
692     }
693   }
694   return hdr - hdr_string;
695 }
696 
697 ///////////////////////////////////////////////////////////////////////////////
698 // Name       : insert_via_header_in_request
699 // Description: takes in existing via_string and inserts it in header
700 //
701 // Input      :
702 // Output     :
703 //
704 // Details    :
705 //
706 // [u<client-stuff> l<cache-lookup-stuff> o<server-stuff> f<cache-fill-stuff> p<proxy-stuff>]
707 //
708 //      client stuff
709 //              I       IMS
710 //              N       no-cache
711 //              A       accept headers
712 //              C       cookie
713 //
714 //      cache lookup stuff
715 //              M       miss
716 //              A       in cache, not acceptable
717 //              S       in cache, stale
718 //              H       in cache, fresh
719 //
720 //      server stuff
721 //              N       not-modified
722 //              S       served
723 //
724 //      cache fill stuff
725 //              F       filled into cache
726 //              U       updated cache
727 //
728 //      proxy stuff
729 //              N       not-modified
730 //              S       served
731 //              R       origin server revalidated
732 //
733 // For example:
734 //
735 //      [u lH o f pS]      cache hit
736 //      [u lM oS fF pS]    cache miss
737 //      [uN l oS f pS]     no-cache origin server fetch
738 //
739 //
740 ///////////////////////////////////////////////////////////////////////////////
741 void
insert_via_header_in_request(HttpTransact::State * s,HTTPHdr * header)742 HttpTransactHeaders::insert_via_header_in_request(HttpTransact::State *s, HTTPHdr *header)
743 {
744   char new_via_string[1024]; // 512-bytes for hostname+via string, 512-bytes for the debug info
745   char *via_string = new_via_string;
746   char *via_limit  = via_string + sizeof(new_via_string);
747 
748   if ((s->http_config_param->proxy_hostname_len + s->http_config_param->proxy_request_via_string_len) > 512) {
749     header->value_append(MIME_FIELD_VIA, MIME_LEN_VIA, "TrafficServer", 13, true);
750     return;
751   }
752 
753   char *incoming_via = s->via_string;
754   std::array<std::string_view, 10> proto_buf; // 10 seems like a reasonable number of protos to print
755   int n_proto = s->state_machine->populate_client_protocol(proto_buf.data(), proto_buf.size());
756 
757   via_string +=
758     write_hdr_protocol_stack(via_string, via_limit - via_string, ProtocolStackDetail::Standard, proto_buf.data(), n_proto);
759   *via_string++ = ' ';
760 
761   via_string += nstrcpy(via_string, s->http_config_param->proxy_hostname);
762 
763   *via_string++ = '[';
764   memcpy(via_string, Machine::instance()->uuid.getString(), TS_UUID_STRING_LEN);
765   via_string += TS_UUID_STRING_LEN;
766   *via_string++ = ']';
767   *via_string++ = ' ';
768   *via_string++ = '(';
769 
770   memcpy(via_string, s->http_config_param->proxy_request_via_string, s->http_config_param->proxy_request_via_string_len);
771   via_string += s->http_config_param->proxy_request_via_string_len;
772 
773   if (s->txn_conf->insert_request_via_string > 1) {
774     *via_string++ = ' ';
775     *via_string++ = '[';
776 
777     // incoming_via can be max MAX_VIA_INDICES+1 long (i.e. around 25 or so)
778     if (s->txn_conf->insert_request_via_string > 2) { // Highest verbosity
779       via_string += nstrcpy(via_string, incoming_via);
780     } else {
781       memcpy(via_string, incoming_via + VIA_CLIENT, VIA_SERVER - VIA_CLIENT);
782       via_string += VIA_SERVER - VIA_CLIENT;
783     }
784     *via_string++ = ']';
785 
786     // reserve 4 for " []" and 3 for "])".
787     if (via_limit - via_string > 4 && s->txn_conf->insert_request_via_string > 3) { // Ultra highest verbosity
788       *via_string++ = ' ';
789       *via_string++ = '[';
790       via_string +=
791         write_hdr_protocol_stack(via_string, via_limit - via_string - 3, ProtocolStackDetail::Full, proto_buf.data(), n_proto);
792       *via_string++ = ']';
793     }
794   }
795 
796   *via_string++ = ')';
797   *via_string   = 0;
798 
799   ink_assert((size_t)(via_string - new_via_string) < (sizeof(new_via_string) - 1));
800   header->value_append(MIME_FIELD_VIA, MIME_LEN_VIA, new_via_string, via_string - new_via_string, true);
801 }
802 
803 void
insert_hsts_header_in_response(HttpTransact::State * s,HTTPHdr * header)804 HttpTransactHeaders::insert_hsts_header_in_response(HttpTransact::State *s, HTTPHdr *header)
805 {
806   char new_hsts_string[64];
807   char *hsts_string                   = new_hsts_string;
808   constexpr char include_subdomains[] = "; includeSubDomains";
809 
810   // add max-age
811   int length = snprintf(new_hsts_string, sizeof(new_hsts_string), "max-age=%" PRId64, s->txn_conf->proxy_response_hsts_max_age);
812 
813   // add include subdomain if set
814   if (s->txn_conf->proxy_response_hsts_include_subdomains) {
815     hsts_string += length;
816     memcpy(hsts_string, include_subdomains, sizeof(include_subdomains) - 1);
817     length += sizeof(include_subdomains) - 1;
818   }
819 
820   header->value_set(MIME_FIELD_STRICT_TRANSPORT_SECURITY, MIME_LEN_STRICT_TRANSPORT_SECURITY, new_hsts_string, length);
821 }
822 
823 void
insert_via_header_in_response(HttpTransact::State * s,HTTPHdr * header)824 HttpTransactHeaders::insert_via_header_in_response(HttpTransact::State *s, HTTPHdr *header)
825 {
826   char new_via_string[1024]; // 512-bytes for hostname+via string, 512-bytes for the debug info
827   char *via_string = new_via_string;
828   char *via_limit  = via_string + sizeof(new_via_string);
829 
830   if ((s->http_config_param->proxy_hostname_len + s->http_config_param->proxy_response_via_string_len) > 512) {
831     header->value_append(MIME_FIELD_VIA, MIME_LEN_VIA, "TrafficServer", 13, true);
832     return;
833   }
834 
835   char *incoming_via = s->via_string;
836   std::array<std::string_view, 10> proto_buf; // 10 seems like a reasonable number of protos to print
837   int n_proto = 0;
838 
839   // Should suffice - if we're adding a response VIA, the connection is HTTP and only 1.0 and 1.1 are supported outbound.
840   // TODO H2 expand for HTTP/2 outbound
841   proto_buf[n_proto++] = header->version_get().get_minor() == 0 ? IP_PROTO_TAG_HTTP_1_0 : IP_PROTO_TAG_HTTP_1_1;
842 
843   auto ss = s->state_machine->get_server_session();
844   if (ss) {
845     n_proto += ss->populate_protocol(proto_buf.data() + n_proto, proto_buf.size() - n_proto);
846   }
847   via_string +=
848     write_hdr_protocol_stack(via_string, via_limit - via_string, ProtocolStackDetail::Standard, proto_buf.data(), n_proto);
849   *via_string++ = ' ';
850 
851   via_string += nstrcpy(via_string, s->http_config_param->proxy_hostname);
852   *via_string++ = ' ';
853   *via_string++ = '(';
854 
855   memcpy(via_string, s->http_config_param->proxy_response_via_string, s->http_config_param->proxy_response_via_string_len);
856   via_string += s->http_config_param->proxy_response_via_string_len;
857 
858   if (s->txn_conf->insert_response_via_string > 1) {
859     *via_string++ = ' ';
860     *via_string++ = '[';
861 
862     // incoming_via can be max MAX_VIA_INDICES+1 long (i.e. around 25 or so)
863     if (s->txn_conf->insert_response_via_string > 2) { // Highest verbosity
864       via_string += nstrcpy(via_string, incoming_via);
865     } else {
866       memcpy(via_string, incoming_via + VIA_CACHE, VIA_PROXY - VIA_CACHE);
867       via_string += VIA_PROXY - VIA_CACHE;
868     }
869     *via_string++ = ']';
870 
871     if (via_limit - via_string > 4 && s->txn_conf->insert_response_via_string > 3) { // Ultra highest verbosity
872       *via_string++ = ' ';
873       *via_string++ = '[';
874       via_string +=
875         write_hdr_protocol_stack(via_string, via_limit - via_string - 3, ProtocolStackDetail::Full, proto_buf.data(), n_proto);
876       *via_string++ = ']';
877     }
878   }
879 
880   *via_string++ = ')';
881   *via_string   = 0;
882 
883   ink_assert((size_t)(via_string - new_via_string) < (sizeof(new_via_string) - 1));
884   header->value_append(MIME_FIELD_VIA, MIME_LEN_VIA, new_via_string, via_string - new_via_string, true);
885 }
886 
887 void
remove_conditional_headers(HTTPHdr * outgoing)888 HttpTransactHeaders::remove_conditional_headers(HTTPHdr *outgoing)
889 {
890   if (outgoing->presence(MIME_PRESENCE_IF_MODIFIED_SINCE | MIME_PRESENCE_IF_UNMODIFIED_SINCE | MIME_PRESENCE_IF_MATCH |
891                          MIME_PRESENCE_IF_NONE_MATCH)) {
892     outgoing->field_delete(MIME_FIELD_IF_MODIFIED_SINCE, MIME_LEN_IF_MODIFIED_SINCE);
893     outgoing->field_delete(MIME_FIELD_IF_UNMODIFIED_SINCE, MIME_LEN_IF_UNMODIFIED_SINCE);
894     outgoing->field_delete(MIME_FIELD_IF_MATCH, MIME_LEN_IF_MATCH);
895     outgoing->field_delete(MIME_FIELD_IF_NONE_MATCH, MIME_LEN_IF_NONE_MATCH);
896   }
897   // TODO: how about RANGE and IF_RANGE?
898 }
899 
900 void
remove_100_continue_headers(HttpTransact::State * s,HTTPHdr * outgoing)901 HttpTransactHeaders::remove_100_continue_headers(HttpTransact::State *s, HTTPHdr *outgoing)
902 {
903   int len            = 0;
904   const char *expect = s->hdr_info.client_request.value_get(MIME_FIELD_EXPECT, MIME_LEN_EXPECT, &len);
905 
906   if ((len == HTTP_LEN_100_CONTINUE) && (strncasecmp(expect, HTTP_VALUE_100_CONTINUE, HTTP_LEN_100_CONTINUE) == 0)) {
907     outgoing->field_delete(MIME_FIELD_EXPECT, MIME_LEN_EXPECT);
908   }
909 }
910 
911 ////////////////////////////////////////////////////////////////////////
912 // Deal with lame-o servers by removing the host name from the url.
913 void
remove_host_name_from_url(HTTPHdr * outgoing_request)914 HttpTransactHeaders::remove_host_name_from_url(HTTPHdr *outgoing_request)
915 {
916   URL *outgoing_url = outgoing_request->url_get();
917   outgoing_url->nuke_proxy_stuff();
918 }
919 
920 void
add_global_user_agent_header_to_request(const OverridableHttpConfigParams * http_txn_conf,HTTPHdr * header)921 HttpTransactHeaders::add_global_user_agent_header_to_request(const OverridableHttpConfigParams *http_txn_conf, HTTPHdr *header)
922 {
923   if (http_txn_conf->global_user_agent_header) {
924     MIMEField *ua_field;
925 
926     Debug("http_trans", "Adding User-Agent: %.*s", static_cast<int>(http_txn_conf->global_user_agent_header_size),
927           http_txn_conf->global_user_agent_header);
928     if ((ua_field = header->field_find(MIME_FIELD_USER_AGENT, MIME_LEN_USER_AGENT)) == nullptr) {
929       if (likely((ua_field = header->field_create(MIME_FIELD_USER_AGENT, MIME_LEN_USER_AGENT)) != nullptr)) {
930         header->field_attach(ua_field);
931       }
932     }
933     // This will remove any old string (free it), and set our User-Agent.
934     if (likely(ua_field)) {
935       header->field_value_set(ua_field, http_txn_conf->global_user_agent_header, http_txn_conf->global_user_agent_header_size);
936     }
937   }
938 }
939 
940 void
add_forwarded_field_to_request(HttpTransact::State * s,HTTPHdr * request)941 HttpTransactHeaders::add_forwarded_field_to_request(HttpTransact::State *s, HTTPHdr *request)
942 {
943   HttpForwarded::OptionBitSet optSet = s->txn_conf->insert_forwarded;
944 
945   if (optSet.any()) { // One or more Forwarded parameters enabled, so insert/append to Forwarded header.
946 
947     ts::LocalBufferWriter<1024> hdr;
948 
949     if (optSet[HttpForwarded::FOR] and ats_is_ip(&s->client_info.src_addr.sa)) {
950       // NOTE:  The logic within this if statement assumes that hdr is empty at this point.
951 
952       hdr << "for=";
953 
954       bool is_ipv6 = ats_is_ip6(&s->client_info.src_addr.sa);
955 
956       if (is_ipv6) {
957         hdr << "\"[";
958       }
959 
960       if (ats_ip_ntop(&s->client_info.src_addr.sa, hdr.auxBuffer(), hdr.remaining()) == nullptr) {
961         Debug("http_trans", "[add_forwarded_field_to_outgoing_request] ats_ip_ntop() call failed");
962         return;
963       }
964 
965       // Fail-safe.
966       hdr.auxBuffer()[hdr.remaining() - 1] = '\0';
967 
968       hdr.fill(strlen(hdr.auxBuffer()));
969 
970       if (is_ipv6) {
971         hdr << "]\"";
972       }
973     }
974 
975     if (optSet[HttpForwarded::BY_UNKNOWN]) {
976       if (hdr.size()) {
977         hdr << ';';
978       }
979 
980       hdr << "by=unknown";
981     }
982 
983     if (optSet[HttpForwarded::BY_SERVER_NAME]) {
984       if (hdr.size()) {
985         hdr << ';';
986       }
987 
988       hdr << "by=" << s->http_config_param->proxy_hostname;
989     }
990 
991     const Machine &m = *Machine::instance();
992 
993     if (optSet[HttpForwarded::BY_UUID] and m.uuid.valid()) {
994       if (hdr.size()) {
995         hdr << ';';
996       }
997 
998       hdr << "by=_" << m.uuid.getString();
999     }
1000 
1001     if (optSet[HttpForwarded::BY_IP] and (m.ip_string_len > 0)) {
1002       if (hdr.size()) {
1003         hdr << ';';
1004       }
1005 
1006       hdr << "by=";
1007 
1008       bool is_ipv6 = ats_is_ip6(&s->client_info.dst_addr.sa);
1009 
1010       if (is_ipv6) {
1011         hdr << "\"[";
1012       }
1013 
1014       if (ats_ip_ntop(&s->client_info.dst_addr.sa, hdr.auxBuffer(), hdr.remaining()) == nullptr) {
1015         Debug("http_trans", "[add_forwarded_field_to_outgoing_request] ats_ip_ntop() call failed");
1016         return;
1017       }
1018 
1019       // Fail-safe.
1020       hdr.auxBuffer()[hdr.remaining() - 1] = '\0';
1021 
1022       hdr.fill(strlen(hdr.auxBuffer()));
1023 
1024       if (is_ipv6) {
1025         hdr << "]\"";
1026       }
1027     }
1028 
1029     std::array<std::string_view, 10> protoBuf; // 10 seems like a reasonable number of protos to print
1030     int n_proto = 0;                           // Indulge clang's incorrect claim that this need to be initialized.
1031 
1032     static const HttpForwarded::OptionBitSet OptionsNeedingProtocol = HttpForwarded::OptionBitSet()
1033                                                                         .set(HttpForwarded::PROTO)
1034                                                                         .set(HttpForwarded::CONNECTION_COMPACT)
1035                                                                         .set(HttpForwarded::CONNECTION_STD)
1036                                                                         .set(HttpForwarded::CONNECTION_FULL);
1037 
1038     if ((optSet bitand OptionsNeedingProtocol).any()) {
1039       n_proto = s->state_machine->populate_client_protocol(protoBuf.data(), protoBuf.size());
1040     }
1041 
1042     if (optSet[HttpForwarded::PROTO] and (n_proto > 0)) {
1043       if (hdr.size()) {
1044         hdr << ';';
1045       }
1046 
1047       hdr << "proto=";
1048 
1049       int numChars = HttpTransactHeaders::write_hdr_protocol_stack(hdr.auxBuffer(), hdr.remaining(), ProtocolStackDetail::Compact,
1050                                                                    protoBuf.data(), n_proto, '-');
1051       if (numChars > 0) {
1052         hdr.fill(size_t(numChars));
1053       }
1054     }
1055 
1056     if (optSet[HttpForwarded::HOST]) {
1057       const MIMEField *hostField = s->hdr_info.client_request.field_find(MIME_FIELD_HOST, MIME_LEN_HOST);
1058 
1059       if (hostField and hostField->m_len_value) {
1060         std::string_view hSV{hostField->m_ptr_value, hostField->m_len_value};
1061 
1062         bool needsDoubleQuotes = hSV.find(':') != std::string_view::npos;
1063 
1064         if (hdr.size()) {
1065           hdr << ';';
1066         }
1067 
1068         hdr << "host=";
1069         if (needsDoubleQuotes) {
1070           hdr << '"';
1071         }
1072         hdr << hSV;
1073         if (needsDoubleQuotes) {
1074           hdr << '"';
1075         }
1076       }
1077     }
1078 
1079     if (n_proto > 0) {
1080       auto Conn = [&](HttpForwarded::Option opt, HttpTransactHeaders::ProtocolStackDetail detail) -> void {
1081         if (optSet[opt] && hdr.remaining() > 0) {
1082           ts::FixedBufferWriter lw{hdr.auxBuffer(), hdr.remaining()};
1083 
1084           if (hdr.size()) {
1085             lw << ';';
1086           }
1087 
1088           lw << "connection=";
1089 
1090           int numChars =
1091             HttpTransactHeaders::write_hdr_protocol_stack(lw.auxBuffer(), lw.remaining(), detail, protoBuf.data(), n_proto, '-');
1092           if (numChars > 0 && !lw.fill(size_t(numChars)).error()) {
1093             hdr.fill(lw.size());
1094           }
1095         }
1096       };
1097 
1098       Conn(HttpForwarded::CONNECTION_COMPACT, HttpTransactHeaders::ProtocolStackDetail::Compact);
1099       Conn(HttpForwarded::CONNECTION_STD, HttpTransactHeaders::ProtocolStackDetail::Standard);
1100       Conn(HttpForwarded::CONNECTION_FULL, HttpTransactHeaders::ProtocolStackDetail::Full);
1101     }
1102 
1103     // Add or append to the Forwarded header.  As a fail-safe against corrupting the MIME header, don't add Forwarded if
1104     // it's size is exactly the capacity of the buffer.
1105     //
1106     if (hdr.size() and !hdr.error() and (hdr.size() < hdr.capacity())) {
1107       std::string_view sV = hdr.view();
1108 
1109       request->value_append(MIME_FIELD_FORWARDED, MIME_LEN_FORWARDED, sV.data(), sV.size(), true, ','); // true => separator must
1110                                                                                                         // be inserted
1111 
1112       Debug("http_trans", "[add_forwarded_field_to_outgoing_request] Forwarded header (%.*s) added", static_cast<int>(hdr.size()),
1113             hdr.data());
1114     }
1115   }
1116 
1117 } // end HttpTransact::add_forwarded_field_to_outgoing_request()
1118 
1119 void
add_server_header_to_response(const OverridableHttpConfigParams * http_txn_conf,HTTPHdr * header)1120 HttpTransactHeaders::add_server_header_to_response(const OverridableHttpConfigParams *http_txn_conf, HTTPHdr *header)
1121 {
1122   if (http_txn_conf->proxy_response_server_enabled && http_txn_conf->proxy_response_server_string) {
1123     MIMEField *ua_field;
1124     bool do_add = true;
1125 
1126     if ((ua_field = header->field_find(MIME_FIELD_SERVER, MIME_LEN_SERVER)) == nullptr) {
1127       if (likely((ua_field = header->field_create(MIME_FIELD_SERVER, MIME_LEN_SERVER)) != nullptr)) {
1128         header->field_attach(ua_field);
1129       }
1130     } else {
1131       // There was an existing header from Origin, so only add if setting allows to overwrite.
1132       do_add = (1 == http_txn_conf->proxy_response_server_enabled);
1133     }
1134 
1135     // This will remove any old string (free it), and set our Server header.
1136     if (do_add && likely(ua_field)) {
1137       Debug("http_trans", "Adding Server: %s", http_txn_conf->proxy_response_server_string);
1138       header->field_value_set(ua_field, http_txn_conf->proxy_response_server_string,
1139                               http_txn_conf->proxy_response_server_string_len);
1140     }
1141   }
1142 }
1143 
1144 void
remove_privacy_headers_from_request(HttpConfigParams * http_config_param,const OverridableHttpConfigParams * http_txn_conf,HTTPHdr * header)1145 HttpTransactHeaders::remove_privacy_headers_from_request(HttpConfigParams *http_config_param,
1146                                                          const OverridableHttpConfigParams *http_txn_conf, HTTPHdr *header)
1147 {
1148   if (!header) {
1149     return;
1150   }
1151 
1152   // From
1153   if (http_txn_conf->anonymize_remove_from) {
1154     Debug("anon", "removing 'From' headers");
1155     header->field_delete(MIME_FIELD_FROM, MIME_LEN_FROM);
1156   }
1157   // Referer
1158   if (http_txn_conf->anonymize_remove_referer) {
1159     Debug("anon", "removing 'Referer' headers");
1160     header->field_delete(MIME_FIELD_REFERER, MIME_LEN_REFERER);
1161   }
1162   // User-Agent
1163   if (http_txn_conf->anonymize_remove_user_agent) {
1164     Debug("anon", "removing 'User-agent' headers");
1165     header->field_delete(MIME_FIELD_USER_AGENT, MIME_LEN_USER_AGENT);
1166   }
1167   // Cookie
1168   if (http_txn_conf->anonymize_remove_cookie) {
1169     Debug("anon", "removing 'Cookie' headers");
1170     header->field_delete(MIME_FIELD_COOKIE, MIME_LEN_COOKIE);
1171   }
1172   // Client-ip
1173   if (http_txn_conf->anonymize_remove_client_ip) {
1174     Debug("anon", "removing 'Client-ip' headers");
1175     header->field_delete(MIME_FIELD_CLIENT_IP, MIME_LEN_CLIENT_IP);
1176   }
1177   /////////////////////////////////////////////
1178   // remove any other user specified headers //
1179   /////////////////////////////////////////////
1180 
1181   // FIXME: we shouldn't parse this list every time, only when the
1182   // FIXME: config file changes.
1183   if (http_config_param->anonymize_other_header_list) {
1184     Str *field;
1185     StrList anon_list(false);
1186     const char *anon_string;
1187 
1188     anon_string = http_config_param->anonymize_other_header_list;
1189     Debug("anon", "removing other headers (%s)", anon_string);
1190     HttpCompat::parse_comma_list(&anon_list, anon_string);
1191     for (field = anon_list.head; field != nullptr; field = field->next) {
1192       Debug("anon", "removing '%s' headers", field->str);
1193       header->field_delete(field->str, field->len);
1194     }
1195   }
1196 }
1197 
1198 void
normalize_accept_encoding(const OverridableHttpConfigParams * ohcp,HTTPHdr * header)1199 HttpTransactHeaders::normalize_accept_encoding(const OverridableHttpConfigParams *ohcp, HTTPHdr *header)
1200 {
1201   int normalize_ae = ohcp->normalize_ae;
1202 
1203   if (normalize_ae) {
1204     MIMEField *ae_field = header->field_find(MIME_FIELD_ACCEPT_ENCODING, MIME_LEN_ACCEPT_ENCODING);
1205 
1206     if (ae_field) {
1207       if (normalize_ae == 1) {
1208         // Force Accept-Encoding header to gzip or no header.
1209         if (HttpTransactCache::match_content_encoding(ae_field, "gzip")) {
1210           header->field_value_set(ae_field, "gzip", 4);
1211           Debug("http_trans", "[Headers::normalize_accept_encoding] normalized Accept-Encoding to gzip");
1212         } else {
1213           header->field_delete(ae_field);
1214           Debug("http_trans", "[Headers::normalize_accept_encoding] removed non-gzip Accept-Encoding");
1215         }
1216       } else if (normalize_ae == 2) {
1217         // Force Accept-Encoding header to br (Brotli) or no header.
1218         if (HttpTransactCache::match_content_encoding(ae_field, "br")) {
1219           header->field_value_set(ae_field, "br", 2);
1220           Debug("http_trans", "[Headers::normalize_accept_encoding] normalized Accept-Encoding to br");
1221         } else if (HttpTransactCache::match_content_encoding(ae_field, "gzip")) {
1222           header->field_value_set(ae_field, "gzip", 4);
1223           Debug("http_trans", "[Headers::normalize_accept_encoding] normalized Accept-Encoding to gzip");
1224         } else {
1225           header->field_delete(ae_field);
1226           Debug("http_trans", "[Headers::normalize_accept_encoding] removed non-br Accept-Encoding");
1227         }
1228       } else {
1229         static bool logged = false;
1230 
1231         if (!logged) {
1232           Error("proxy.config.http.normalize_ae value out of range");
1233           logged = true;
1234         }
1235       }
1236     }
1237   }
1238 }
1239 
1240 void
add_connection_close(HTTPHdr * header)1241 HttpTransactHeaders::add_connection_close(HTTPHdr *header)
1242 {
1243   MIMEField *field = header->field_find(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
1244   if (!field) {
1245     field = header->field_create(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION);
1246     header->field_attach(field);
1247   }
1248   header->field_value_set(field, HTTP_VALUE_CLOSE, HTTP_LEN_CLOSE);
1249 }
1250