LCOV - code coverage report
Current view: top level - source/common/http - utility.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 641 1030 62.2 %
Date: 2024-01-05 06:35:25 Functions: 52 79 65.8 %

          Line data    Source code
       1             : #include "source/common/http/utility.h"
       2             : 
       3             : #include <http_parser.h>
       4             : 
       5             : #include <cstdint>
       6             : #include <string>
       7             : #include <vector>
       8             : 
       9             : #include "envoy/config/core/v3/http_uri.pb.h"
      10             : #include "envoy/config/core/v3/protocol.pb.h"
      11             : #include "envoy/http/header_map.h"
      12             : 
      13             : #include "source/common/buffer/buffer_impl.h"
      14             : #include "source/common/common/assert.h"
      15             : #include "source/common/common/empty_string.h"
      16             : #include "source/common/common/enum_to_int.h"
      17             : #include "source/common/common/fmt.h"
      18             : #include "source/common/common/utility.h"
      19             : #include "source/common/grpc/status.h"
      20             : #include "source/common/http/character_set_validation.h"
      21             : #include "source/common/http/exception.h"
      22             : #include "source/common/http/header_map_impl.h"
      23             : #include "source/common/http/headers.h"
      24             : #include "source/common/http/message_impl.h"
      25             : #include "source/common/network/utility.h"
      26             : #include "source/common/protobuf/utility.h"
      27             : #include "source/common/runtime/runtime_features.h"
      28             : 
      29             : #include "absl/container/node_hash_set.h"
      30             : #include "absl/strings/match.h"
      31             : #include "absl/strings/numbers.h"
      32             : #include "absl/strings/str_cat.h"
      33             : #include "absl/strings/str_split.h"
      34             : #include "absl/strings/string_view.h"
      35             : #include "absl/types/optional.h"
      36             : #include "quiche/http2/adapter/http2_protocol.h"
      37             : 
      38             : namespace Envoy {
      39             : namespace {
      40             : 
      41             : // Get request host from the request header map, removing the port if the port
      42             : // does not match the scheme, or if port is provided.
      43             : absl::string_view processRequestHost(const Http::RequestHeaderMap& headers,
      44         202 :                                      absl::string_view new_scheme, absl::string_view new_port) {
      45             : 
      46         202 :   absl::string_view request_host = headers.getHostValue();
      47         202 :   size_t host_end;
      48         202 :   if (request_host.empty()) {
      49           2 :     return request_host;
      50           2 :   }
      51             :   // Detect if IPv6 URI
      52         200 :   if (request_host[0] == '[') {
      53           0 :     host_end = request_host.rfind("]:");
      54           0 :     if (host_end != absl::string_view::npos) {
      55           0 :       host_end += 1; // advance to :
      56           0 :     }
      57         200 :   } else {
      58         200 :     host_end = request_host.rfind(':');
      59         200 :   }
      60             : 
      61         200 :   if (host_end != absl::string_view::npos) {
      62           0 :     absl::string_view request_port = request_host.substr(host_end);
      63             :     // In the rare case that X-Forwarded-Proto and scheme disagree (say http URL over an HTTPS
      64             :     // connection), do port stripping based on X-Forwarded-Proto so http://foo.com:80 won't
      65             :     // have the port stripped when served over TLS.
      66           0 :     absl::string_view request_protocol = headers.getForwardedProtoValue();
      67           0 :     bool remove_port = !new_port.empty();
      68             : 
      69           0 :     if (new_scheme != request_protocol) {
      70           0 :       remove_port |= Http::Utility::schemeIsHttps(request_protocol) && request_port == ":443";
      71           0 :       remove_port |= Http::Utility::schemeIsHttp(request_protocol) && request_port == ":80";
      72           0 :     }
      73             : 
      74           0 :     if (remove_port) {
      75           0 :       return request_host.substr(0, host_end);
      76           0 :     }
      77           0 :   }
      78             : 
      79         200 :   return request_host;
      80         200 : }
      81             : 
      82             : } // namespace
      83             : namespace Http2 {
      84             : namespace Utility {
      85             : 
      86             : namespace {
      87             : 
      88             : struct SettingsEntry {
      89             :   uint16_t settings_id;
      90             :   uint32_t value;
      91             : };
      92             : 
      93             : struct SettingsEntryHash {
      94         114 :   size_t operator()(const SettingsEntry& entry) const {
      95         114 :     return absl::Hash<decltype(entry.settings_id)>()(entry.settings_id);
      96         114 :   }
      97             : };
      98             : 
      99             : struct SettingsEntryEquals {
     100          37 :   bool operator()(const SettingsEntry& lhs, const SettingsEntry& rhs) const {
     101          37 :     return lhs.settings_id == rhs.settings_id;
     102          37 :   }
     103             : };
     104             : 
     105             : void validateCustomSettingsParameters(
     106        3165 :     const envoy::config::core::v3::Http2ProtocolOptions& options) {
     107        3165 :   std::vector<std::string> parameter_collisions, custom_parameter_collisions;
     108        3165 :   absl::node_hash_set<SettingsEntry, SettingsEntryHash, SettingsEntryEquals> custom_parameters;
     109             :   // User defined and named parameters with the same SETTINGS identifier can not both be set.
     110        3191 :   for (const auto& it : options.custom_settings_parameters()) {
     111          91 :     ASSERT(it.identifier().value() <= std::numeric_limits<uint16_t>::max());
     112             :     // Check for custom parameter inconsistencies.
     113          91 :     const auto result = custom_parameters.insert(
     114          91 :         {static_cast<uint16_t>(it.identifier().value()), it.value().value()});
     115          91 :     if (!result.second) {
     116          37 :       if (result.first->value != it.value().value()) {
     117          17 :         custom_parameter_collisions.push_back(
     118          17 :             absl::StrCat("0x", absl::Hex(it.identifier().value(), absl::kZeroPad2)));
     119             :         // Fall through to allow unbatched exceptions to throw first.
     120          17 :       }
     121          37 :     }
     122          91 :     switch (it.identifier().value()) {
     123           0 :     case http2::adapter::ENABLE_PUSH:
     124           0 :       if (it.value().value() == 1) {
     125           0 :         throwEnvoyExceptionOrPanic(
     126           0 :             "server push is not supported by Envoy and can not be enabled via a "
     127           0 :             "SETTINGS parameter.");
     128           0 :       }
     129           0 :       break;
     130           1 :     case http2::adapter::ENABLE_CONNECT_PROTOCOL:
     131             :       // An exception is made for `allow_connect` which can't be checked for presence due to the
     132             :       // use of a primitive type (bool).
     133           1 :       throwEnvoyExceptionOrPanic("the \"allow_connect\" SETTINGS parameter must only be configured "
     134           0 :                                  "through the named field");
     135           4 :     case http2::adapter::HEADER_TABLE_SIZE:
     136           4 :       if (options.has_hpack_table_size()) {
     137           1 :         parameter_collisions.push_back("hpack_table_size");
     138           1 :       }
     139           4 :       break;
     140           0 :     case http2::adapter::MAX_CONCURRENT_STREAMS:
     141           0 :       if (options.has_max_concurrent_streams()) {
     142           0 :         parameter_collisions.push_back("max_concurrent_streams");
     143           0 :       }
     144           0 :       break;
     145           4 :     case http2::adapter::INITIAL_WINDOW_SIZE:
     146           4 :       if (options.has_initial_stream_window_size()) {
     147           1 :         parameter_collisions.push_back("initial_stream_window_size");
     148           1 :       }
     149           4 :       break;
     150          82 :     default:
     151             :       // Ignore unknown parameters.
     152          82 :       break;
     153          91 :     }
     154          91 :   }
     155             : 
     156        3164 :   if (!custom_parameter_collisions.empty()) {
     157          13 :     throwEnvoyExceptionOrPanic(fmt::format(
     158          13 :         "inconsistent HTTP/2 custom SETTINGS parameter(s) detected; identifiers = {{{}}}",
     159          13 :         absl::StrJoin(custom_parameter_collisions, ",")));
     160          13 :   }
     161        3151 :   if (!parameter_collisions.empty()) {
     162           2 :     throwEnvoyExceptionOrPanic(fmt::format(
     163           2 :         "the {{{}}} HTTP/2 SETTINGS parameter(s) can not be configured through both named and "
     164           2 :         "custom parameters",
     165           2 :         absl::StrJoin(parameter_collisions, ",")));
     166           2 :   }
     167        3151 : }
     168             : 
     169             : } // namespace
     170             : 
     171             : const uint32_t OptionsLimits::MIN_HPACK_TABLE_SIZE;
     172             : const uint32_t OptionsLimits::DEFAULT_HPACK_TABLE_SIZE;
     173             : const uint32_t OptionsLimits::MAX_HPACK_TABLE_SIZE;
     174             : const uint32_t OptionsLimits::MIN_MAX_CONCURRENT_STREAMS;
     175             : const uint32_t OptionsLimits::DEFAULT_MAX_CONCURRENT_STREAMS;
     176             : const uint32_t OptionsLimits::MAX_MAX_CONCURRENT_STREAMS;
     177             : const uint32_t OptionsLimits::MIN_INITIAL_STREAM_WINDOW_SIZE;
     178             : const uint32_t OptionsLimits::DEFAULT_INITIAL_STREAM_WINDOW_SIZE;
     179             : const uint32_t OptionsLimits::MAX_INITIAL_STREAM_WINDOW_SIZE;
     180             : const uint32_t OptionsLimits::MIN_INITIAL_CONNECTION_WINDOW_SIZE;
     181             : const uint32_t OptionsLimits::DEFAULT_INITIAL_CONNECTION_WINDOW_SIZE;
     182             : const uint32_t OptionsLimits::MAX_INITIAL_CONNECTION_WINDOW_SIZE;
     183             : const uint32_t OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES;
     184             : const uint32_t OptionsLimits::DEFAULT_MAX_OUTBOUND_CONTROL_FRAMES;
     185             : const uint32_t OptionsLimits::DEFAULT_MAX_CONSECUTIVE_INBOUND_FRAMES_WITH_EMPTY_PAYLOAD;
     186             : const uint32_t OptionsLimits::DEFAULT_MAX_INBOUND_PRIORITY_FRAMES_PER_STREAM;
     187             : const uint32_t OptionsLimits::DEFAULT_MAX_INBOUND_WINDOW_UPDATE_FRAMES_PER_DATA_FRAME_SENT;
     188             : 
     189             : envoy::config::core::v3::Http2ProtocolOptions
     190             : initializeAndValidateOptions(const envoy::config::core::v3::Http2ProtocolOptions& options,
     191             :                              bool hcm_stream_error_set,
     192         140 :                              const ProtobufWkt::BoolValue& hcm_stream_error) {
     193         140 :   auto ret = initializeAndValidateOptions(options);
     194         140 :   if (!options.has_override_stream_error_on_invalid_http_message() && hcm_stream_error_set) {
     195           0 :     ret.mutable_override_stream_error_on_invalid_http_message()->set_value(
     196           0 :         hcm_stream_error.value());
     197           0 :   }
     198         140 :   return ret;
     199         140 : }
     200             : 
     201             : envoy::config::core::v3::Http2ProtocolOptions
     202        3165 : initializeAndValidateOptions(const envoy::config::core::v3::Http2ProtocolOptions& options) {
     203        3165 :   envoy::config::core::v3::Http2ProtocolOptions options_clone(options);
     204             :   // This will throw an exception when a custom parameter and a named parameter collide.
     205        3165 :   validateCustomSettingsParameters(options);
     206             : 
     207        3165 :   if (!options.has_override_stream_error_on_invalid_http_message()) {
     208        3136 :     options_clone.mutable_override_stream_error_on_invalid_http_message()->set_value(
     209        3136 :         options.stream_error_on_invalid_http_messaging());
     210        3136 :   }
     211             : 
     212        3165 :   if (!options_clone.has_hpack_table_size()) {
     213        3134 :     options_clone.mutable_hpack_table_size()->set_value(OptionsLimits::DEFAULT_HPACK_TABLE_SIZE);
     214        3134 :   }
     215        3165 :   ASSERT(options_clone.hpack_table_size().value() <= OptionsLimits::MAX_HPACK_TABLE_SIZE);
     216        3165 :   if (!options_clone.has_max_concurrent_streams()) {
     217        3139 :     options_clone.mutable_max_concurrent_streams()->set_value(
     218        3139 :         OptionsLimits::DEFAULT_MAX_CONCURRENT_STREAMS);
     219        3139 :   }
     220        3165 :   ASSERT(
     221        3165 :       options_clone.max_concurrent_streams().value() >= OptionsLimits::MIN_MAX_CONCURRENT_STREAMS &&
     222        3165 :       options_clone.max_concurrent_streams().value() <= OptionsLimits::MAX_MAX_CONCURRENT_STREAMS);
     223        3165 :   if (!options_clone.has_initial_stream_window_size()) {
     224        3137 :     options_clone.mutable_initial_stream_window_size()->set_value(
     225        3137 :         OptionsLimits::DEFAULT_INITIAL_STREAM_WINDOW_SIZE);
     226        3137 :   }
     227        3165 :   ASSERT(options_clone.initial_stream_window_size().value() >=
     228        3165 :              OptionsLimits::MIN_INITIAL_STREAM_WINDOW_SIZE &&
     229        3165 :          options_clone.initial_stream_window_size().value() <=
     230        3165 :              OptionsLimits::MAX_INITIAL_STREAM_WINDOW_SIZE);
     231        3165 :   if (!options_clone.has_initial_connection_window_size()) {
     232        3141 :     options_clone.mutable_initial_connection_window_size()->set_value(
     233        3141 :         OptionsLimits::DEFAULT_INITIAL_CONNECTION_WINDOW_SIZE);
     234        3141 :   }
     235        3165 :   ASSERT(options_clone.initial_connection_window_size().value() >=
     236        3165 :              OptionsLimits::MIN_INITIAL_CONNECTION_WINDOW_SIZE &&
     237        3165 :          options_clone.initial_connection_window_size().value() <=
     238        3165 :              OptionsLimits::MAX_INITIAL_CONNECTION_WINDOW_SIZE);
     239        3165 :   if (!options_clone.has_max_outbound_frames()) {
     240        3138 :     options_clone.mutable_max_outbound_frames()->set_value(
     241        3138 :         OptionsLimits::DEFAULT_MAX_OUTBOUND_FRAMES);
     242        3138 :   }
     243        3165 :   if (!options_clone.has_max_outbound_control_frames()) {
     244        3140 :     options_clone.mutable_max_outbound_control_frames()->set_value(
     245        3140 :         OptionsLimits::DEFAULT_MAX_OUTBOUND_CONTROL_FRAMES);
     246        3140 :   }
     247        3165 :   if (!options_clone.has_max_consecutive_inbound_frames_with_empty_payload()) {
     248        3129 :     options_clone.mutable_max_consecutive_inbound_frames_with_empty_payload()->set_value(
     249        3129 :         OptionsLimits::DEFAULT_MAX_CONSECUTIVE_INBOUND_FRAMES_WITH_EMPTY_PAYLOAD);
     250        3129 :   }
     251        3165 :   if (!options_clone.has_max_inbound_priority_frames_per_stream()) {
     252        3135 :     options_clone.mutable_max_inbound_priority_frames_per_stream()->set_value(
     253        3135 :         OptionsLimits::DEFAULT_MAX_INBOUND_PRIORITY_FRAMES_PER_STREAM);
     254        3135 :   }
     255        3165 :   if (!options_clone.has_max_inbound_window_update_frames_per_data_frame_sent()) {
     256        3143 :     options_clone.mutable_max_inbound_window_update_frames_per_data_frame_sent()->set_value(
     257        3143 :         OptionsLimits::DEFAULT_MAX_INBOUND_WINDOW_UPDATE_FRAMES_PER_DATA_FRAME_SENT);
     258        3143 :   }
     259             : 
     260        3165 :   return options_clone;
     261        3165 : }
     262             : 
     263             : } // namespace Utility
     264             : } // namespace Http2
     265             : 
     266             : namespace Http3 {
     267             : namespace Utility {
     268             : 
     269             : const uint32_t OptionsLimits::DEFAULT_INITIAL_STREAM_WINDOW_SIZE;
     270             : const uint32_t OptionsLimits::DEFAULT_INITIAL_CONNECTION_WINDOW_SIZE;
     271             : 
     272             : envoy::config::core::v3::Http3ProtocolOptions
     273             : initializeAndValidateOptions(const envoy::config::core::v3::Http3ProtocolOptions& options,
     274             :                              bool hcm_stream_error_set,
     275         140 :                              const ProtobufWkt::BoolValue& hcm_stream_error) {
     276         140 :   if (options.has_override_stream_error_on_invalid_http_message()) {
     277           0 :     return options;
     278           0 :   }
     279         140 :   envoy::config::core::v3::Http3ProtocolOptions options_clone(options);
     280         140 :   if (hcm_stream_error_set) {
     281           0 :     options_clone.mutable_override_stream_error_on_invalid_http_message()->set_value(
     282           0 :         hcm_stream_error.value());
     283         140 :   } else {
     284         140 :     options_clone.mutable_override_stream_error_on_invalid_http_message()->set_value(false);
     285         140 :   }
     286         140 :   return options_clone;
     287         140 : }
     288             : 
     289             : } // namespace Utility
     290             : } // namespace Http3
     291             : 
     292             : namespace Http {
     293             : 
     294             : static const char kDefaultPath[] = "/";
     295             : 
     296             : // If http_parser encounters an IP address [address] as the host it will set the offset and
     297             : // length to point to 'address' rather than '[address]'. Fix this by adjusting the offset
     298             : // and length to include the brackets.
     299             : // @param absolute_url the absolute URL. This is usually of the form // http://host/path
     300             : //        but may be host:port for CONNECT requests
     301             : // @param offset the offset for the first character of the host. For IPv6 hosts
     302             : //        this will point to the first character inside the brackets and will be
     303             : //        adjusted to point at the brackets
     304             : // @param len the length of the host-and-port field. For IPv6 hosts this will
     305             : //        not include the brackets and will be adjusted to do so.
     306           1 : bool maybeAdjustForIpv6(absl::string_view absolute_url, uint64_t& offset, uint64_t& len) {
     307             :   // According to https://tools.ietf.org/html/rfc3986#section-3.2.2 the only way a hostname
     308             :   // may begin with '[' is if it's an ipv6 address.
     309           1 :   if (offset == 0 || *(absolute_url.data() + offset - 1) != '[') {
     310           1 :     return false;
     311           1 :   }
     312             :   // Start one character sooner and end one character later.
     313           0 :   offset--;
     314           0 :   len += 2;
     315             :   // HTTP parser ensures that any [ has a closing ]
     316           0 :   ASSERT(absolute_url.length() >= offset + len);
     317           0 :   return true;
     318           1 : }
     319             : 
     320             : void forEachCookie(
     321             :     const HeaderMap& headers, const LowerCaseString& cookie_header,
     322         129 :     const std::function<bool(absl::string_view, absl::string_view)>& cookie_consumer) {
     323         129 :   const Http::HeaderMap::GetResult cookie_headers = headers.get(cookie_header);
     324             : 
     325       66211 :   for (size_t index = 0; index < cookie_headers.size(); index++) {
     326       66113 :     auto cookie_header_value = cookie_headers[index]->value().getStringView();
     327             : 
     328             :     // Split the cookie header into individual cookies.
     329       66113 :     for (const auto& s : StringUtil::splitToken(cookie_header_value, ";")) {
     330             :       // Find the key part of the cookie (i.e. the name of the cookie).
     331        1086 :       size_t first_non_space = s.find_first_not_of(' ');
     332        1086 :       size_t equals_index = s.find('=');
     333        1086 :       if (equals_index == absl::string_view::npos) {
     334             :         // The cookie is malformed if it does not have an `=`. Continue
     335             :         // checking other cookies in this header.
     336         325 :         continue;
     337         325 :       }
     338         761 :       absl::string_view k = s.substr(first_non_space, equals_index - first_non_space);
     339         761 :       absl::string_view v = s.substr(equals_index + 1, s.size() - 1);
     340             : 
     341             :       // Cookie values may be wrapped in double quotes.
     342             :       // https://tools.ietf.org/html/rfc6265#section-4.1.1
     343         761 :       if (v.size() >= 2 && v.back() == '"' && v[0] == '"') {
     344         132 :         v = v.substr(1, v.size() - 2);
     345         132 :       }
     346             : 
     347         761 :       if (!cookie_consumer(k, v)) {
     348          31 :         return;
     349          31 :       }
     350         761 :     }
     351       66113 :   }
     352         129 : }
     353             : 
     354             : std::string parseCookie(const HeaderMap& headers, const std::string& key,
     355         126 :                         const LowerCaseString& cookie) {
     356         126 :   std::string value;
     357             : 
     358             :   // Iterate over each cookie & return if its value is not empty.
     359         758 :   forEachCookie(headers, cookie, [&key, &value](absl::string_view k, absl::string_view v) -> bool {
     360         758 :     if (key == k) {
     361          31 :       value = std::string{v};
     362          31 :       return false;
     363          31 :     }
     364             : 
     365             :     // continue iterating until a cookie that matches `key` is found.
     366         727 :     return true;
     367         758 :   });
     368             : 
     369         126 :   return value;
     370         126 : }
     371             : 
     372             : absl::flat_hash_map<std::string, std::string>
     373           0 : Utility::parseCookies(const RequestHeaderMap& headers) {
     374           0 :   return Utility::parseCookies(headers, [](absl::string_view) -> bool { return true; });
     375           0 : }
     376             : 
     377             : absl::flat_hash_map<std::string, std::string>
     378             : Utility::parseCookies(const RequestHeaderMap& headers,
     379           3 :                       const std::function<bool(absl::string_view)>& key_filter) {
     380           3 :   absl::flat_hash_map<std::string, std::string> cookies;
     381             : 
     382           3 :   forEachCookie(headers, Http::Headers::get().Cookie,
     383           3 :                 [&cookies, &key_filter](absl::string_view k, absl::string_view v) -> bool {
     384           3 :                   if (key_filter(k)) {
     385           3 :                     cookies.emplace(k, v);
     386           3 :                   }
     387             : 
     388             :                   // continue iterating until all cookies are processed.
     389           3 :                   return true;
     390           3 :                 });
     391             : 
     392           3 :   return cookies;
     393           3 : }
     394             : 
     395           0 : bool Utility::Url::containsFragment() { return (component_bitmap_ & (1 << UcFragment)); }
     396             : 
     397           0 : bool Utility::Url::containsUserinfo() { return (component_bitmap_ & (1 << UcUserinfo)); }
     398             : 
     399          29 : bool Utility::Url::initialize(absl::string_view absolute_url, bool is_connect) {
     400          29 :   struct http_parser_url u;
     401          29 :   http_parser_url_init(&u);
     402          29 :   const int result =
     403          29 :       http_parser_parse_url(absolute_url.data(), absolute_url.length(), is_connect, &u);
     404             : 
     405          29 :   if (result != 0) {
     406          28 :     return false;
     407          28 :   }
     408             : 
     409           1 :   if ((u.field_set & (1 << UF_HOST)) != (1 << UF_HOST) &&
     410           1 :       (u.field_set & (1 << UF_SCHEMA)) != (1 << UF_SCHEMA)) {
     411           0 :     return false;
     412           0 :   }
     413             : 
     414           1 :   component_bitmap_ = u.field_set;
     415           1 :   scheme_ = absl::string_view(absolute_url.data() + u.field_data[UF_SCHEMA].off,
     416           1 :                               u.field_data[UF_SCHEMA].len);
     417             : 
     418           1 :   uint64_t authority_len = u.field_data[UF_HOST].len;
     419           1 :   if ((u.field_set & (1 << UF_PORT)) == (1 << UF_PORT)) {
     420           1 :     authority_len = authority_len + u.field_data[UF_PORT].len + 1;
     421           1 :   }
     422             : 
     423           1 :   uint64_t authority_beginning = u.field_data[UF_HOST].off;
     424           1 :   const bool is_ipv6 = maybeAdjustForIpv6(absolute_url, authority_beginning, authority_len);
     425           1 :   host_and_port_ = absl::string_view(absolute_url.data() + authority_beginning, authority_len);
     426           1 :   if (is_ipv6 && !parseAuthority(host_and_port_).is_ip_address_) {
     427           0 :     return false;
     428           0 :   }
     429             : 
     430             :   // RFC allows the absolute-uri to not end in /, but the absolute path form
     431             :   // must start with. Determine if there's a non-zero path, and if so determine
     432             :   // the length of the path, query params etc.
     433           1 :   uint64_t path_etc_len = absolute_url.length() - (authority_beginning + hostAndPort().length());
     434           1 :   if (path_etc_len > 0) {
     435           1 :     uint64_t path_beginning = authority_beginning + hostAndPort().length();
     436           1 :     path_and_query_params_ = absl::string_view(absolute_url.data() + path_beginning, path_etc_len);
     437           1 :   } else if (!is_connect) {
     438           0 :     ASSERT((u.field_set & (1 << UF_PATH)) == 0);
     439           0 :     path_and_query_params_ = absl::string_view(kDefaultPath, 1);
     440           0 :   }
     441           1 :   return true;
     442           1 : }
     443             : 
     444           0 : std::string Utility::Url::toString() const {
     445           0 :   return absl::StrCat(scheme_, "://", host_and_port_, path_and_query_params_);
     446           0 : }
     447             : 
     448             : void Utility::appendXff(RequestHeaderMap& headers,
     449         188 :                         const Network::Address::Instance& remote_address) {
     450         188 :   if (remote_address.type() != Network::Address::Type::Ip) {
     451           0 :     return;
     452           0 :   }
     453             : 
     454         188 :   headers.appendForwardedFor(remote_address.ip()->addressAsString(), ",");
     455         188 : }
     456             : 
     457           1 : void Utility::appendVia(RequestOrResponseHeaderMap& headers, const std::string& via) {
     458             :   // TODO(asraa): Investigate whether it is necessary to append with whitespace here by:
     459             :   //     (a) Validating we do not expect whitespace in via headers
     460             :   //     (b) Add runtime guarding in case users have upstreams which expect it.
     461           1 :   headers.appendVia(via, ", ");
     462           1 : }
     463             : 
     464             : void Utility::updateAuthority(RequestHeaderMap& headers, absl::string_view hostname,
     465           0 :                               const bool append_xfh) {
     466           0 :   const auto host = headers.getHostValue();
     467             : 
     468           0 :   if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.append_xfh_idempotent")) {
     469             :     // Only append to x-forwarded-host if the value was not the last value appended.
     470           0 :     const auto xfh = headers.getForwardedHostValue();
     471             : 
     472           0 :     if (append_xfh && !host.empty()) {
     473           0 :       if (!xfh.empty()) {
     474           0 :         const auto xfh_split = StringUtil::splitToken(xfh, ",");
     475           0 :         if (!xfh_split.empty() && xfh_split.back() != host) {
     476           0 :           headers.appendForwardedHost(host, ",");
     477           0 :         }
     478           0 :       } else {
     479           0 :         headers.appendForwardedHost(host, ",");
     480           0 :       }
     481           0 :     }
     482           0 :   } else {
     483           0 :     if (append_xfh && !host.empty()) {
     484           0 :       headers.appendForwardedHost(host, ",");
     485           0 :     }
     486           0 :   }
     487             : 
     488           0 :   headers.setHost(hostname);
     489           0 : }
     490             : 
     491           0 : std::string Utility::createSslRedirectPath(const RequestHeaderMap& headers) {
     492           0 :   ASSERT(headers.Host());
     493           0 :   ASSERT(headers.Path());
     494           0 :   return fmt::format("https://{}{}", headers.getHostValue(), headers.getPathValue());
     495           0 : }
     496             : 
     497          12 : Utility::QueryParamsMulti Utility::QueryParamsMulti::parseQueryString(absl::string_view url) {
     498          12 :   size_t start = url.find('?');
     499          12 :   if (start == std::string::npos) {
     500           1 :     return {};
     501           1 :   }
     502             : 
     503          11 :   start++;
     504          11 :   return Utility::QueryParamsMulti::parseParameters(url, start, /*decode_params=*/false);
     505          12 : }
     506             : 
     507             : Utility::QueryParamsMulti
     508         122 : Utility::QueryParamsMulti::parseAndDecodeQueryString(absl::string_view url) {
     509         122 :   size_t start = url.find('?');
     510         122 :   if (start == std::string::npos) {
     511         108 :     return {};
     512         108 :   }
     513             : 
     514          14 :   start++;
     515          14 :   return Utility::QueryParamsMulti::parseParameters(url, start, /*decode_params=*/true);
     516         122 : }
     517             : 
     518             : Utility::QueryParamsMulti Utility::QueryParamsMulti::parseParameters(absl::string_view data,
     519             :                                                                      size_t start,
     520          43 :                                                                      bool decode_params) {
     521          43 :   QueryParamsMulti params;
     522             : 
     523         102 :   while (start < data.size()) {
     524          59 :     size_t end = data.find('&', start);
     525          59 :     if (end == std::string::npos) {
     526          37 :       end = data.size();
     527          37 :     }
     528          59 :     absl::string_view param(data.data() + start, end - start);
     529             : 
     530          59 :     const size_t equal = param.find('=');
     531          59 :     if (equal != std::string::npos) {
     532          45 :       const auto param_name = StringUtil::subspan(data, start, start + equal);
     533          45 :       const auto param_value = StringUtil::subspan(data, start + equal + 1, end);
     534          45 :       params.add(decode_params ? PercentEncoding::decode(param_name) : param_name,
     535          45 :                  decode_params ? PercentEncoding::decode(param_value) : param_value);
     536          45 :     } else {
     537          14 :       const auto param_name = StringUtil::subspan(data, start, end);
     538          14 :       params.add(decode_params ? PercentEncoding::decode(param_name) : param_name, "");
     539          14 :     }
     540             : 
     541          59 :     start = end + 1;
     542          59 :   }
     543             : 
     544          43 :   return params;
     545          43 : }
     546             : 
     547           0 : void Utility::QueryParamsMulti::remove(absl::string_view key) { this->data_.erase(key); }
     548             : 
     549          59 : void Utility::QueryParamsMulti::add(absl::string_view key, absl::string_view value) {
     550          59 :   auto result = this->data_.emplace(std::string(key), std::vector<std::string>{std::string(value)});
     551          59 :   if (!result.second) {
     552           8 :     result.first->second.push_back(std::string(value));
     553           8 :   }
     554          59 : }
     555             : 
     556           0 : void Utility::QueryParamsMulti::overwrite(absl::string_view key, absl::string_view value) {
     557           0 :   this->data_[key] = std::vector<std::string>{std::string(value)};
     558           0 : }
     559             : 
     560         404 : absl::optional<std::string> Utility::QueryParamsMulti::getFirstValue(absl::string_view key) const {
     561         404 :   auto it = this->data_.find(key);
     562         404 :   if (it == this->data_.end()) {
     563         401 :     return std::nullopt;
     564         401 :   }
     565             : 
     566           3 :   return absl::optional<std::string>{it->second.at(0)};
     567         404 : }
     568             : 
     569          16 : absl::string_view Utility::findQueryStringStart(const HeaderString& path) {
     570          16 :   absl::string_view path_str = path.getStringView();
     571          16 :   size_t query_offset = path_str.find('?');
     572          16 :   if (query_offset == absl::string_view::npos) {
     573          12 :     query_offset = path_str.length();
     574          12 :   }
     575          16 :   path_str.remove_prefix(query_offset);
     576          16 :   return path_str;
     577          16 : }
     578             : 
     579           0 : std::string Utility::stripQueryString(const HeaderString& path) {
     580           0 :   absl::string_view path_str = path.getStringView();
     581           0 :   size_t query_offset = path_str.find('?');
     582           0 :   return {path_str.data(), query_offset != path_str.npos ? query_offset : path_str.size()};
     583           0 : }
     584             : 
     585           0 : std::string Utility::QueryParamsMulti::replaceQueryString(const HeaderString& path) const {
     586           0 :   std::string new_path{Http::Utility::stripQueryString(path)};
     587             : 
     588           0 :   if (!this->data_.empty()) {
     589           0 :     absl::StrAppend(&new_path, this->toString());
     590           0 :   }
     591             : 
     592           0 :   return new_path;
     593           0 : }
     594             : 
     595         126 : std::string Utility::parseCookieValue(const HeaderMap& headers, const std::string& key) {
     596             :   // TODO(wbpcode): Modify the headers parameter type to 'RequestHeaderMap'.
     597         126 :   return parseCookie(headers, key, Http::Headers::get().Cookie);
     598         126 : }
     599             : 
     600           0 : std::string Utility::parseSetCookieValue(const Http::HeaderMap& headers, const std::string& key) {
     601           0 :   return parseCookie(headers, key, Http::Headers::get().SetCookie);
     602           0 : }
     603             : 
     604             : std::string Utility::makeSetCookieValue(const std::string& key, const std::string& value,
     605             :                                         const std::string& path, const std::chrono::seconds max_age,
     606             :                                         bool httponly,
     607          17 :                                         const Http::CookieAttributeRefVector attributes) {
     608          17 :   std::string cookie_value;
     609             :   // Best effort attempt to avoid numerous string copies.
     610          17 :   cookie_value.reserve(value.size() + path.size() + 30);
     611             : 
     612          17 :   cookie_value = absl::StrCat(key, "=\"", value, "\"");
     613          17 :   if (max_age != std::chrono::seconds::zero()) {
     614          11 :     absl::StrAppend(&cookie_value, "; Max-Age=", max_age.count());
     615          11 :   }
     616          17 :   if (!path.empty()) {
     617          10 :     absl::StrAppend(&cookie_value, "; Path=", path);
     618          10 :   }
     619             : 
     620          17 :   for (auto const& attribute : attributes) {
     621           0 :     if (attribute.get().value().empty()) {
     622           0 :       absl::StrAppend(&cookie_value, "; ", attribute.get().name());
     623           0 :     } else {
     624           0 :       absl::StrAppend(&cookie_value, "; ", attribute.get().name(), "=", attribute.get().value());
     625           0 :     }
     626           0 :   }
     627             : 
     628          17 :   if (httponly) {
     629           1 :     absl::StrAppend(&cookie_value, "; HttpOnly");
     630           1 :   }
     631          17 :   return cookie_value;
     632          17 : }
     633             : 
     634        1397 : uint64_t Utility::getResponseStatus(const ResponseHeaderMap& headers) {
     635        1397 :   auto status = Utility::getResponseStatusOrNullopt(headers);
     636        1397 :   if (!status.has_value()) {
     637           0 :     IS_ENVOY_BUG("No status in headers");
     638           0 :     return 0;
     639           0 :   }
     640        1397 :   return status.value();
     641        1397 : }
     642             : 
     643        2185 : absl::optional<uint64_t> Utility::getResponseStatusOrNullopt(const ResponseHeaderMap& headers) {
     644        2185 :   const HeaderEntry* header = headers.Status();
     645        2185 :   uint64_t response_code;
     646        2185 :   if (!header || !absl::SimpleAtoi(headers.getStatusValue(), &response_code)) {
     647          45 :     return absl::nullopt;
     648          45 :   }
     649        2140 :   return response_code;
     650        2185 : }
     651             : 
     652        3437 : bool Utility::isUpgrade(const RequestOrResponseHeaderMap& headers) {
     653             :   // In firefox the "Connection" request header value is "keep-alive, Upgrade",
     654             :   // we should check if it contains the "Upgrade" token.
     655        3437 :   return (headers.Upgrade() &&
     656        3437 :           Envoy::StringUtil::caseFindToken(headers.getConnectionValue(), ",",
     657          24 :                                            Http::Headers::get().ConnectionValues.Upgrade));
     658        3437 : }
     659             : 
     660         315 : bool Utility::isH2UpgradeRequest(const RequestHeaderMap& headers) {
     661         315 :   return headers.getMethodValue() == Http::Headers::get().MethodValues.Connect &&
     662         315 :          headers.Protocol() && !headers.Protocol()->value().empty() &&
     663         315 :          headers.Protocol()->value() != Headers::get().ProtocolValues.Bytestream;
     664         315 : }
     665             : 
     666           0 : bool Utility::isH3UpgradeRequest(const RequestHeaderMap& headers) {
     667           0 :   return isH2UpgradeRequest(headers);
     668           0 : }
     669             : 
     670           3 : bool Utility::isWebSocketUpgradeRequest(const RequestHeaderMap& headers) {
     671           3 :   return (isUpgrade(headers) &&
     672           3 :           absl::EqualsIgnoreCase(headers.getUpgradeValue(),
     673           0 :                                  Http::Headers::get().UpgradeValues.WebSocket));
     674           3 : }
     675             : 
     676             : Utility::PreparedLocalReplyPtr Utility::prepareLocalReply(const EncodeFunctions& encode_functions,
     677         397 :                                                           const LocalReplyData& local_reply_data) {
     678         397 :   Code response_code = local_reply_data.response_code_;
     679         397 :   std::string body_text(local_reply_data.body_text_);
     680         397 :   absl::string_view content_type(Headers::get().ContentTypeValues.Text);
     681             : 
     682         397 :   ResponseHeaderMapPtr response_headers{createHeaderMap<ResponseHeaderMapImpl>(
     683         397 :       {{Headers::get().Status, std::to_string(enumToInt(response_code))}})};
     684             : 
     685         397 :   if (encode_functions.modify_headers_) {
     686         383 :     encode_functions.modify_headers_(*response_headers);
     687         383 :   }
     688         397 :   bool has_custom_content_type = false;
     689         397 :   if (encode_functions.rewrite_) {
     690         383 :     std::string content_type_value = std::string(response_headers->getContentTypeValue());
     691         383 :     encode_functions.rewrite_(*response_headers, response_code, body_text, content_type);
     692         383 :     has_custom_content_type = (content_type_value != response_headers->getContentTypeValue());
     693         383 :   }
     694             : 
     695         397 :   if (local_reply_data.is_grpc_) {
     696           4 :     response_headers->setStatus(std::to_string(enumToInt(Code::OK)));
     697           4 :     response_headers->setReferenceContentType(Headers::get().ContentTypeValues.Grpc);
     698             : 
     699           4 :     if (response_headers->getGrpcStatusValue().empty()) {
     700           4 :       response_headers->setGrpcStatus(std::to_string(
     701           4 :           enumToInt(local_reply_data.grpc_status_
     702           4 :                         ? local_reply_data.grpc_status_.value()
     703           4 :                         : Grpc::Utility::httpToGrpcStatus(enumToInt(response_code)))));
     704           4 :     }
     705             : 
     706           4 :     if (!body_text.empty() && !local_reply_data.is_head_request_) {
     707             :       // TODO(dio): Probably it is worth to consider caching the encoded message based on gRPC
     708             :       // status.
     709             :       // JsonFormatter adds a '\n' at the end. For header value, it should be removed.
     710             :       // https://github.com/envoyproxy/envoy/blob/main/source/common/formatter/substitution_formatter.cc#L129
     711           0 :       if (content_type == Headers::get().ContentTypeValues.Json &&
     712           0 :           body_text[body_text.length() - 1] == '\n') {
     713           0 :         body_text = body_text.substr(0, body_text.length() - 1);
     714           0 :       }
     715           0 :       response_headers->setGrpcMessage(PercentEncoding::encode(body_text));
     716           0 :     }
     717             :     // The `modify_headers` function may have added content-length, remove it.
     718           4 :     response_headers->removeContentLength();
     719             : 
     720             :     // TODO(kbaichoo): In C++20 we will be able to use make_unique with
     721             :     // aggregate initialization.
     722             :     // NOLINTNEXTLINE(modernize-make-unique)
     723           4 :     return PreparedLocalReplyPtr(new PreparedLocalReply{
     724           4 :         local_reply_data.is_grpc_, local_reply_data.is_head_request_, std::move(response_headers),
     725           4 :         std::move(body_text), encode_functions.encode_headers_, encode_functions.encode_data_});
     726           4 :   }
     727             : 
     728         393 :   if (!body_text.empty()) {
     729         348 :     response_headers->setContentLength(body_text.size());
     730             :     // If the content-type is not set, set it.
     731             :     // Alternately if the `rewrite` function has changed body_text and the config didn't explicitly
     732             :     // set a content type header, set the content type to be based on the changed body.
     733         348 :     if (response_headers->ContentType() == nullptr ||
     734         348 :         (body_text != local_reply_data.body_text_ && !has_custom_content_type)) {
     735         348 :       response_headers->setReferenceContentType(content_type);
     736         348 :     }
     737         385 :   } else {
     738          45 :     response_headers->removeContentLength();
     739          45 :     response_headers->removeContentType();
     740          45 :   }
     741             : 
     742             :   // TODO(kbaichoo): In C++20 we will be able to use make_unique with
     743             :   // aggregate initialization.
     744             :   // NOLINTNEXTLINE(modernize-make-unique)
     745         393 :   return PreparedLocalReplyPtr(new PreparedLocalReply{
     746         393 :       local_reply_data.is_grpc_, local_reply_data.is_head_request_, std::move(response_headers),
     747         393 :       std::move(body_text), encode_functions.encode_headers_, encode_functions.encode_data_});
     748         397 : }
     749             : 
     750         397 : void Utility::encodeLocalReply(const bool& is_reset, PreparedLocalReplyPtr prepared_local_reply) {
     751         397 :   ASSERT(prepared_local_reply != nullptr);
     752         397 :   ResponseHeaderMapPtr response_headers{std::move(prepared_local_reply->response_headers_)};
     753             : 
     754         397 :   if (prepared_local_reply->is_grpc_request_) {
     755             :     // Trailers only response
     756           4 :     prepared_local_reply->encode_headers_(std::move(response_headers), true);
     757           4 :     return;
     758           4 :   }
     759             : 
     760         393 :   if (prepared_local_reply->is_head_request_) {
     761           0 :     prepared_local_reply->encode_headers_(std::move(response_headers), true);
     762           0 :     return;
     763           0 :   }
     764             : 
     765         393 :   const bool bodyless_response = prepared_local_reply->response_body_.empty();
     766         393 :   prepared_local_reply->encode_headers_(std::move(response_headers), bodyless_response);
     767             :   // encode_headers() may have changed the referenced is_reset so we need to test it
     768         393 :   if (!bodyless_response && !is_reset) {
     769         348 :     Buffer::OwnedImpl buffer(prepared_local_reply->response_body_);
     770         348 :     prepared_local_reply->encode_data_(buffer, true);
     771         348 :   }
     772         393 : }
     773             : 
     774             : void Utility::sendLocalReply(const bool& is_reset, const EncodeFunctions& encode_functions,
     775         195 :                              const LocalReplyData& local_reply_data) {
     776             :   // encode_headers() may reset the stream, so the stream must not be reset before calling it.
     777         195 :   ASSERT(!is_reset);
     778         195 :   PreparedLocalReplyPtr prepared_local_reply =
     779         195 :       prepareLocalReply(encode_functions, local_reply_data);
     780             : 
     781         195 :   encodeLocalReply(is_reset, std::move(prepared_local_reply));
     782         195 : }
     783             : 
     784             : Utility::GetLastAddressFromXffInfo
     785             : Utility::getLastAddressFromXFF(const Http::RequestHeaderMap& request_headers,
     786         420 :                                uint32_t num_to_skip) {
     787         420 :   const auto xff_header = request_headers.ForwardedFor();
     788         420 :   if (xff_header == nullptr) {
     789         204 :     return {nullptr, false};
     790         204 :   }
     791             : 
     792         216 :   absl::string_view xff_string(xff_header->value().getStringView());
     793         216 :   static constexpr absl::string_view separator(",");
     794             :   // Ignore the last num_to_skip addresses at the end of XFF.
     795         223 :   for (uint32_t i = 0; i < num_to_skip; i++) {
     796           9 :     const absl::string_view::size_type last_comma = xff_string.rfind(separator);
     797           9 :     if (last_comma == absl::string_view::npos) {
     798           2 :       return {nullptr, false};
     799           2 :     }
     800           7 :     xff_string = xff_string.substr(0, last_comma);
     801           7 :   }
     802             :   // The text after the last remaining comma, or the entirety of the string if there
     803             :   // is no comma, is the requested IP address.
     804         214 :   const absl::string_view::size_type last_comma = xff_string.rfind(separator);
     805         214 :   if (last_comma != absl::string_view::npos && last_comma + separator.size() < xff_string.size()) {
     806           7 :     xff_string = xff_string.substr(last_comma + separator.size());
     807           7 :   }
     808             : 
     809             :   // Ignore the whitespace, since they are allowed in HTTP lists (see RFC7239#section-7.1).
     810         214 :   xff_string = StringUtil::ltrim(xff_string);
     811         214 :   xff_string = StringUtil::rtrim(xff_string);
     812             : 
     813             :   // This technically requires a copy because inet_pton takes a null terminated string. In
     814             :   // practice, we are working with a view at the end of the owning string, and could pass the
     815             :   // raw pointer.
     816             :   // TODO(mattklein123) PERF: Avoid the copy here.
     817         214 :   Network::Address::InstanceConstSharedPtr address =
     818         214 :       Network::Utility::parseInternetAddressNoThrow(std::string(xff_string));
     819         214 :   if (address != nullptr) {
     820         184 :     return {address, last_comma == absl::string_view::npos && num_to_skip == 0};
     821         184 :   }
     822          30 :   return {nullptr, false};
     823         214 : }
     824             : 
     825           2 : bool Utility::sanitizeConnectionHeader(Http::RequestHeaderMap& headers) {
     826           2 :   static constexpr size_t MAX_ALLOWED_NOMINATED_HEADERS = 10;
     827           2 :   static constexpr size_t MAX_ALLOWED_TE_VALUE_SIZE = 256;
     828             : 
     829             :   // Remove any headers nominated by the Connection header. The TE header
     830             :   // is sanitized and removed only if it's empty after removing unsupported values
     831             :   // See https://github.com/envoyproxy/envoy/issues/8623
     832           2 :   const auto& cv = Http::Headers::get().ConnectionValues;
     833           2 :   const auto& connection_header_value = headers.Connection()->value();
     834             : 
     835           2 :   StringUtil::CaseUnorderedSet headers_to_remove{};
     836           2 :   std::vector<absl::string_view> connection_header_tokens =
     837           2 :       StringUtil::splitToken(connection_header_value.getStringView(), ",", false);
     838             : 
     839             :   // If we have 10 or more nominated headers, fail this request
     840           2 :   if (connection_header_tokens.size() >= MAX_ALLOWED_NOMINATED_HEADERS) {
     841           0 :     ENVOY_LOG_MISC(trace, "Too many nominated headers in request");
     842           0 :     return false;
     843           0 :   }
     844             : 
     845             :   // Split the connection header and evaluate each nominated header
     846           4 :   for (const auto& token : connection_header_tokens) {
     847             : 
     848           4 :     const auto token_sv = StringUtil::trim(token);
     849             : 
     850             :     // Build the LowerCaseString for header lookup
     851           4 :     const LowerCaseString lcs_header_to_remove{std::string(token_sv)};
     852             : 
     853             :     // If the Connection token value is not a nominated header, ignore it here since
     854             :     // the connection header is removed elsewhere when the H1 request is upgraded to H2
     855           4 :     if ((lcs_header_to_remove.get() == cv.Close) ||
     856           4 :         (lcs_header_to_remove.get() == cv.Http2Settings) ||
     857           4 :         (lcs_header_to_remove.get() == cv.KeepAlive) ||
     858           4 :         (lcs_header_to_remove.get() == cv.Upgrade)) {
     859           4 :       continue;
     860           4 :     }
     861             : 
     862             :     // By default we will remove any nominated headers
     863           0 :     bool keep_header = false;
     864             : 
     865             :     // Determine whether the nominated header contains invalid values
     866           0 :     HeaderMap::GetResult nominated_header;
     867             : 
     868           0 :     if (lcs_header_to_remove == Http::Headers::get().Connection) {
     869             :       // Remove the connection header from the nominated tokens if it's self nominated
     870             :       // The connection header itself is *not removed*
     871           0 :       ENVOY_LOG_MISC(trace, "Skipping self nominated header [{}]", token_sv);
     872           0 :       keep_header = true;
     873           0 :       headers_to_remove.emplace(token_sv);
     874             : 
     875           0 :     } else if ((lcs_header_to_remove == Http::Headers::get().ForwardedFor) ||
     876           0 :                (lcs_header_to_remove == Http::Headers::get().ForwardedHost) ||
     877           0 :                (lcs_header_to_remove == Http::Headers::get().ForwardedProto) ||
     878           0 :                !token_sv.find(':')) {
     879             : 
     880             :       // An attacker could nominate an X-Forwarded* header, and its removal may mask
     881             :       // the origin of the incoming request and potentially alter its handling.
     882             :       // Additionally, pseudo headers should never be nominated. In both cases, we
     883             :       // should fail the request.
     884             :       // See: https://nathandavison.com/blog/abusing-http-hop-by-hop-request-headers
     885             : 
     886           0 :       ENVOY_LOG_MISC(trace, "Invalid nomination of {} header", token_sv);
     887           0 :       return false;
     888           0 :     } else {
     889             :       // Examine the value of all other nominated headers
     890           0 :       nominated_header = headers.get(lcs_header_to_remove);
     891           0 :     }
     892             : 
     893           0 :     if (!nominated_header.empty()) {
     894             :       // NOTE: The TE header is an inline header, so by definition if we operate on it there can
     895             :       // only be a single value. In all other cases we remove the nominated header.
     896           0 :       auto nominated_header_value_sv = nominated_header[0]->value().getStringView();
     897             : 
     898           0 :       const bool is_te_header = (lcs_header_to_remove == Http::Headers::get().TE);
     899             : 
     900             :       // reject the request if the TE header is too large
     901           0 :       if (is_te_header && (nominated_header_value_sv.size() >= MAX_ALLOWED_TE_VALUE_SIZE)) {
     902           0 :         ENVOY_LOG_MISC(trace, "TE header contains a value that exceeds the allowable length");
     903           0 :         return false;
     904           0 :       }
     905             : 
     906           0 :       if (is_te_header) {
     907           0 :         ASSERT(nominated_header.size() == 1);
     908           0 :         for (const auto& header_value :
     909           0 :              StringUtil::splitToken(nominated_header_value_sv, ",", false)) {
     910             : 
     911           0 :           const absl::string_view header_sv = StringUtil::trim(header_value);
     912             : 
     913             :           // If trailers exist in the TE value tokens, keep the header, removing any other values
     914             :           // that may exist
     915           0 :           if (StringUtil::CaseInsensitiveCompare()(header_sv,
     916           0 :                                                    Http::Headers::get().TEValues.Trailers)) {
     917           0 :             keep_header = true;
     918           0 :             break;
     919           0 :           }
     920           0 :         }
     921             : 
     922           0 :         if (keep_header) {
     923           0 :           headers.setTE(Http::Headers::get().TEValues.Trailers);
     924           0 :         }
     925           0 :       }
     926           0 :     }
     927             : 
     928           0 :     if (!keep_header) {
     929           0 :       ENVOY_LOG_MISC(trace, "Removing nominated header [{}]", token_sv);
     930           0 :       headers.remove(lcs_header_to_remove);
     931           0 :       headers_to_remove.emplace(token_sv);
     932           0 :     }
     933           0 :   }
     934             : 
     935             :   // Lastly remove extra nominated headers from the Connection header
     936           2 :   if (!headers_to_remove.empty()) {
     937           0 :     const std::string new_value = StringUtil::removeTokens(connection_header_value.getStringView(),
     938           0 :                                                            ",", headers_to_remove, ",");
     939             : 
     940           0 :     if (new_value.empty()) {
     941           0 :       headers.removeConnection();
     942           0 :     } else {
     943           0 :       headers.setConnection(new_value);
     944           0 :     }
     945           0 :   }
     946             : 
     947           2 :   return true;
     948           2 : }
     949             : 
     950         612 : const std::string& Utility::getProtocolString(const Protocol protocol) {
     951         612 :   switch (protocol) {
     952           0 :   case Protocol::Http10:
     953           0 :     return Headers::get().ProtocolStrings.Http10String;
     954         400 :   case Protocol::Http11:
     955         400 :     return Headers::get().ProtocolStrings.Http11String;
     956         212 :   case Protocol::Http2:
     957         212 :     return Headers::get().ProtocolStrings.Http2String;
     958           0 :   case Protocol::Http3:
     959           0 :     return Headers::get().ProtocolStrings.Http3String;
     960         612 :   }
     961             : 
     962           0 :   return EMPTY_STRING;
     963         612 : }
     964             : 
     965             : std::string Utility::buildOriginalUri(const Http::RequestHeaderMap& request_headers,
     966          21 :                                       const absl::optional<uint32_t> max_path_length) {
     967          21 :   if (!request_headers.Path()) {
     968           0 :     return "";
     969           0 :   }
     970          21 :   absl::string_view path(request_headers.EnvoyOriginalPath()
     971          21 :                              ? request_headers.getEnvoyOriginalPathValue()
     972          21 :                              : request_headers.getPathValue());
     973             : 
     974          21 :   if (max_path_length.has_value() && path.length() > max_path_length) {
     975           3 :     path = path.substr(0, max_path_length.value());
     976           3 :   }
     977             : 
     978          21 :   return absl::StrCat(request_headers.getSchemeValue(), "://", request_headers.getHostValue(),
     979          21 :                       path);
     980          21 : }
     981             : 
     982             : void Utility::extractHostPathFromUri(const absl::string_view& uri, absl::string_view& host,
     983          26 :                                      absl::string_view& path) {
     984             :   /**
     985             :    *  URI RFC: https://www.ietf.org/rfc/rfc2396.txt
     986             :    *
     987             :    *  Example:
     988             :    *  uri  = "https://example.com:8443/certs"
     989             :    *  pos:         ^
     990             :    *  host_pos:       ^
     991             :    *  path_pos:                       ^
     992             :    *  host = "example.com:8443"
     993             :    *  path = "/certs"
     994             :    */
     995          26 :   const auto pos = uri.find("://");
     996             :   // Start position of the host
     997          26 :   const auto host_pos = (pos == std::string::npos) ? 0 : pos + 3;
     998             :   // Start position of the path
     999          26 :   const auto path_pos = uri.find('/', host_pos);
    1000          26 :   if (path_pos == std::string::npos) {
    1001             :     // If uri doesn't have "/", the whole string is treated as host.
    1002          15 :     host = uri.substr(host_pos);
    1003          15 :     path = "/";
    1004          18 :   } else {
    1005          11 :     host = uri.substr(host_pos, path_pos - host_pos);
    1006          11 :     path = uri.substr(path_pos);
    1007          11 :   }
    1008          26 : }
    1009             : 
    1010           0 : std::string Utility::localPathFromFilePath(const absl::string_view& file_path) {
    1011           0 :   if (file_path.size() >= 3 && file_path[1] == ':' && file_path[2] == '/' &&
    1012           0 :       std::isalpha(file_path[0])) {
    1013           0 :     return std::string(file_path);
    1014           0 :   }
    1015           0 :   return absl::StrCat("/", file_path);
    1016           0 : }
    1017             : 
    1018           3 : RequestMessagePtr Utility::prepareHeaders(const envoy::config::core::v3::HttpUri& http_uri) {
    1019           3 :   absl::string_view host, path;
    1020           3 :   extractHostPathFromUri(http_uri.uri(), host, path);
    1021             : 
    1022           3 :   RequestMessagePtr message(new RequestMessageImpl());
    1023           3 :   message->headers().setPath(path);
    1024           3 :   message->headers().setHost(host);
    1025             : 
    1026           3 :   return message;
    1027           3 : }
    1028             : 
    1029           0 : std::string Utility::QueryParamsMulti::toString() const {
    1030           0 :   std::string out;
    1031           0 :   std::string delim = "?";
    1032           0 :   for (const auto& p : this->data_) {
    1033           0 :     for (const auto& v : p.second) {
    1034           0 :       absl::StrAppend(&out, delim, p.first, "=", v);
    1035           0 :       delim = "&";
    1036           0 :     }
    1037           0 :   }
    1038           0 :   return out;
    1039           0 : }
    1040             : 
    1041         172 : const std::string Utility::resetReasonToString(const Http::StreamResetReason reset_reason) {
    1042         172 :   switch (reset_reason) {
    1043           0 :   case Http::StreamResetReason::LocalConnectionFailure:
    1044           0 :     return "local connection failure";
    1045          12 :   case Http::StreamResetReason::RemoteConnectionFailure:
    1046          12 :     return "remote connection failure";
    1047           0 :   case Http::StreamResetReason::ConnectionTimeout:
    1048           0 :     return "connection timeout";
    1049          86 :   case Http::StreamResetReason::ConnectionTermination:
    1050          86 :     return "connection termination";
    1051           0 :   case Http::StreamResetReason::LocalReset:
    1052           0 :     return "local reset";
    1053           0 :   case Http::StreamResetReason::LocalRefusedStreamReset:
    1054           0 :     return "local refused stream reset";
    1055           0 :   case Http::StreamResetReason::Overflow:
    1056           0 :     return "overflow";
    1057           0 :   case Http::StreamResetReason::RemoteReset:
    1058           0 :     return "remote reset";
    1059           0 :   case Http::StreamResetReason::RemoteRefusedStreamReset:
    1060           0 :     return "remote refused stream reset";
    1061           0 :   case Http::StreamResetReason::ConnectError:
    1062           0 :     return "remote error with CONNECT request";
    1063          74 :   case Http::StreamResetReason::ProtocolError:
    1064          74 :     return "protocol error";
    1065           0 :   case Http::StreamResetReason::OverloadManager:
    1066           0 :     return "overload manager reset";
    1067         172 :   }
    1068             : 
    1069           0 :   return "";
    1070         172 : }
    1071             : 
    1072           4 : void Utility::transformUpgradeRequestFromH1toH2(RequestHeaderMap& headers) {
    1073           4 :   ASSERT(Utility::isUpgrade(headers));
    1074             : 
    1075           4 :   headers.setReferenceMethod(Http::Headers::get().MethodValues.Connect);
    1076           4 :   headers.setProtocol(headers.getUpgradeValue());
    1077           4 :   headers.removeUpgrade();
    1078           4 :   headers.removeConnection();
    1079             :   // nghttp2 rejects upgrade requests/responses with content length, so strip
    1080             :   // any unnecessary content length header.
    1081           4 :   if (headers.getContentLengthValue() == "0") {
    1082           0 :     headers.removeContentLength();
    1083           0 :   }
    1084           4 : }
    1085             : 
    1086           0 : void Utility::transformUpgradeRequestFromH1toH3(RequestHeaderMap& headers) {
    1087           0 :   transformUpgradeRequestFromH1toH2(headers);
    1088           0 : }
    1089             : 
    1090           2 : void Utility::transformUpgradeResponseFromH1toH2(ResponseHeaderMap& headers) {
    1091           2 :   if (getResponseStatus(headers) == 101) {
    1092           0 :     headers.setStatus(200);
    1093           0 :   }
    1094           2 :   headers.removeUpgrade();
    1095           2 :   headers.removeConnection();
    1096           2 :   if (headers.getContentLengthValue() == "0") {
    1097           2 :     headers.removeContentLength();
    1098           2 :   }
    1099           2 : }
    1100             : 
    1101           0 : void Utility::transformUpgradeResponseFromH1toH3(ResponseHeaderMap& headers) {
    1102           0 :   transformUpgradeResponseFromH1toH2(headers);
    1103           0 : }
    1104             : 
    1105           0 : void Utility::transformUpgradeRequestFromH2toH1(RequestHeaderMap& headers) {
    1106           0 :   ASSERT(Utility::isH2UpgradeRequest(headers));
    1107             : 
    1108           0 :   headers.setReferenceMethod(Http::Headers::get().MethodValues.Get);
    1109           0 :   headers.setUpgrade(headers.getProtocolValue());
    1110           0 :   headers.setReferenceConnection(Http::Headers::get().ConnectionValues.Upgrade);
    1111           0 :   headers.removeProtocol();
    1112           0 : }
    1113             : 
    1114           0 : void Utility::transformUpgradeRequestFromH3toH1(RequestHeaderMap& headers) {
    1115           0 :   transformUpgradeRequestFromH2toH1(headers);
    1116           0 : }
    1117             : 
    1118             : void Utility::transformUpgradeResponseFromH2toH1(ResponseHeaderMap& headers,
    1119           2 :                                                  absl::string_view upgrade) {
    1120           2 :   if (getResponseStatus(headers) == 200) {
    1121           2 :     headers.setUpgrade(upgrade);
    1122           2 :     headers.setReferenceConnection(Http::Headers::get().ConnectionValues.Upgrade);
    1123           2 :     headers.setStatus(101);
    1124           2 :   }
    1125           2 : }
    1126             : 
    1127             : void Utility::transformUpgradeResponseFromH3toH1(ResponseHeaderMap& headers,
    1128           0 :                                                  absl::string_view upgrade) {
    1129           0 :   transformUpgradeResponseFromH2toH1(headers, upgrade);
    1130           0 : }
    1131             : 
    1132             : std::string Utility::PercentEncoding::encode(absl::string_view value,
    1133          49 :                                              absl::string_view reserved_chars) {
    1134          49 :   absl::flat_hash_set<char> reserved_char_set{reserved_chars.begin(), reserved_chars.end()};
    1135         599 :   for (size_t i = 0; i < value.size(); ++i) {
    1136         588 :     const char& ch = value[i];
    1137             :     // The escaping characters are defined in
    1138             :     // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#responses.
    1139             :     //
    1140             :     // We do checking for each char in the string. If the current char is included in the defined
    1141             :     // escaping characters, we jump to "the slow path" (append the char [encoded or not encoded]
    1142             :     // to the returned string one by one) started from the current index.
    1143         588 :     if (ch < ' ' || ch >= '~' || reserved_char_set.find(ch) != reserved_char_set.end()) {
    1144          38 :       return PercentEncoding::encode(value, i, reserved_char_set);
    1145          38 :     }
    1146         588 :   }
    1147          11 :   return std::string(value);
    1148          49 : }
    1149             : 
    1150             : std::string Utility::PercentEncoding::encode(absl::string_view value, const size_t index,
    1151          38 :                                              const absl::flat_hash_set<char>& reserved_char_set) {
    1152          38 :   std::string encoded;
    1153          38 :   if (index > 0) {
    1154          13 :     absl::StrAppend(&encoded, value.substr(0, index));
    1155          13 :   }
    1156             : 
    1157        2108 :   for (size_t i = index; i < value.size(); ++i) {
    1158        2070 :     const char& ch = value[i];
    1159        2070 :     if (ch < ' ' || ch >= '~' || reserved_char_set.find(ch) != reserved_char_set.end()) {
    1160             :       // For consistency, URI producers should use uppercase hexadecimal digits for all
    1161             :       // percent-encodings. https://tools.ietf.org/html/rfc3986#section-2.1.
    1162        1105 :       absl::StrAppend(&encoded, fmt::format("%{:02X}", static_cast<const unsigned char&>(ch)));
    1163        1105 :     } else {
    1164         965 :       encoded.push_back(ch);
    1165         965 :     }
    1166        2070 :   }
    1167          38 :   return encoded;
    1168          38 : }
    1169             : 
    1170          69 : std::string Utility::PercentEncoding::decode(absl::string_view encoded) {
    1171          69 :   std::string decoded;
    1172          69 :   decoded.reserve(encoded.size());
    1173        3896 :   for (size_t i = 0; i < encoded.size(); ++i) {
    1174        3827 :     char ch = encoded[i];
    1175        3827 :     if (ch == '%' && i + 2 < encoded.size()) {
    1176          44 :       const char& hi = encoded[i + 1];
    1177          44 :       const char& lo = encoded[i + 2];
    1178          44 :       if (absl::ascii_isxdigit(hi) && absl::ascii_isxdigit(lo)) {
    1179           6 :         if (absl::ascii_isdigit(hi)) {
    1180           6 :           ch = hi - '0';
    1181           6 :         } else {
    1182           0 :           ch = absl::ascii_toupper(hi) - 'A' + 10;
    1183           0 :         }
    1184             : 
    1185           6 :         ch *= 16;
    1186           6 :         if (absl::ascii_isdigit(lo)) {
    1187           5 :           ch += lo - '0';
    1188           5 :         } else {
    1189           1 :           ch += absl::ascii_toupper(lo) - 'A' + 10;
    1190           1 :         }
    1191           6 :         i += 2;
    1192           6 :       }
    1193          44 :     }
    1194        3827 :     decoded.push_back(ch);
    1195        3827 :   }
    1196          69 :   return decoded;
    1197          69 : }
    1198             : 
    1199             : namespace {
    1200             : // %-encode all ASCII character codepoints, EXCEPT:
    1201             : // ALPHA | DIGIT | * | - | . | _
    1202             : // SPACE is encoded as %20, NOT as the + character
    1203             : constexpr std::array<uint32_t, 8> kUrlEncodedCharTable = {
    1204             :     // control characters
    1205             :     0b11111111111111111111111111111111,
    1206             :     // !"#$%&'()*+,-./0123456789:;<=>?
    1207             :     0b11111111110110010000000000111111,
    1208             :     //@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
    1209             :     0b10000000000000000000000000011110,
    1210             :     //`abcdefghijklmnopqrstuvwxyz{|}~
    1211             :     0b10000000000000000000000000011111,
    1212             :     // extended ascii
    1213             :     0b11111111111111111111111111111111,
    1214             :     0b11111111111111111111111111111111,
    1215             :     0b11111111111111111111111111111111,
    1216             :     0b11111111111111111111111111111111,
    1217             : };
    1218             : 
    1219             : constexpr std::array<uint32_t, 8> kUrlDecodedCharTable = {
    1220             :     // control characters
    1221             :     0b00000000000000000000000000000000,
    1222             :     // !"#$%&'()*+,-./0123456789:;<=>?
    1223             :     0b01011111111111111111111111110101,
    1224             :     //@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
    1225             :     0b11111111111111111111111111110101,
    1226             :     //`abcdefghijklmnopqrstuvwxyz{|}~
    1227             :     0b11111111111111111111111111100010,
    1228             :     // extended ascii
    1229             :     0b00000000000000000000000000000000,
    1230             :     0b00000000000000000000000000000000,
    1231             :     0b00000000000000000000000000000000,
    1232             :     0b00000000000000000000000000000000,
    1233             : };
    1234             : 
    1235           0 : bool shouldPercentEncodeChar(char c) { return testCharInTable(kUrlEncodedCharTable, c); }
    1236             : 
    1237           0 : bool shouldPercentDecodeChar(char c) { return testCharInTable(kUrlDecodedCharTable, c); }
    1238             : } // namespace
    1239             : 
    1240           0 : std::string Utility::PercentEncoding::urlEncodeQueryParameter(absl::string_view value) {
    1241           0 :   std::string encoded;
    1242           0 :   encoded.reserve(value.size());
    1243           0 :   for (char ch : value) {
    1244           0 :     if (shouldPercentEncodeChar(ch)) {
    1245             :       // For consistency, URI producers should use uppercase hexadecimal digits for all
    1246             :       // percent-encodings. https://tools.ietf.org/html/rfc3986#section-2.1.
    1247           0 :       absl::StrAppend(&encoded, fmt::format("%{:02X}", static_cast<const unsigned char&>(ch)));
    1248           0 :     } else {
    1249           0 :       encoded.push_back(ch);
    1250           0 :     }
    1251           0 :   }
    1252           0 :   return encoded;
    1253           0 : }
    1254             : 
    1255           0 : std::string Utility::PercentEncoding::urlDecodeQueryParameter(absl::string_view encoded) {
    1256           0 :   std::string decoded;
    1257           0 :   decoded.reserve(encoded.size());
    1258           0 :   for (size_t i = 0; i < encoded.size(); ++i) {
    1259           0 :     char ch = encoded[i];
    1260           0 :     if (ch == '%' && i + 2 < encoded.size()) {
    1261           0 :       const char& hi = encoded[i + 1];
    1262           0 :       const char& lo = encoded[i + 2];
    1263           0 :       if (absl::ascii_isxdigit(hi) && absl::ascii_isxdigit(lo)) {
    1264           0 :         if (absl::ascii_isdigit(hi)) {
    1265           0 :           ch = hi - '0';
    1266           0 :         } else {
    1267           0 :           ch = absl::ascii_toupper(hi) - 'A' + 10;
    1268           0 :         }
    1269             : 
    1270           0 :         ch *= 16;
    1271           0 :         if (absl::ascii_isdigit(lo)) {
    1272           0 :           ch += lo - '0';
    1273           0 :         } else {
    1274           0 :           ch += absl::ascii_toupper(lo) - 'A' + 10;
    1275           0 :         }
    1276           0 :         if (shouldPercentDecodeChar(ch)) {
    1277             :           // Decode the character only if it is present in the characters_to_decode
    1278           0 :           decoded.push_back(ch);
    1279           0 :         } else {
    1280             :           // Otherwise keep it as is.
    1281           0 :           decoded.push_back('%');
    1282           0 :           decoded.push_back(hi);
    1283           0 :           decoded.push_back(lo);
    1284           0 :         }
    1285           0 :         i += 2;
    1286           0 :       }
    1287           0 :     } else {
    1288           0 :       decoded.push_back(ch);
    1289           0 :     }
    1290           0 :   }
    1291           0 :   return decoded;
    1292           0 : }
    1293             : 
    1294          26 : Utility::AuthorityAttributes Utility::parseAuthority(absl::string_view host) {
    1295             :   // First try to see if there is a port included. This also checks to see that there is not a ']'
    1296             :   // as the last character which is indicative of an IPv6 address without a port. This is a best
    1297             :   // effort attempt.
    1298          26 :   const auto colon_pos = host.rfind(':');
    1299          26 :   absl::string_view host_to_resolve = host;
    1300          26 :   absl::optional<uint16_t> port;
    1301          26 :   if (colon_pos != absl::string_view::npos && host_to_resolve.back() != ']') {
    1302          17 :     const absl::string_view string_view_host = host;
    1303          17 :     host_to_resolve = string_view_host.substr(0, colon_pos);
    1304          17 :     const auto port_str = string_view_host.substr(colon_pos + 1);
    1305          17 :     uint64_t port64;
    1306          17 :     if (port_str.empty() || !absl::SimpleAtoi(port_str, &port64) || port64 > 65535) {
    1307             :       // Just attempt to resolve whatever we were given. This will very likely fail.
    1308           7 :       host_to_resolve = host;
    1309          13 :     } else {
    1310          10 :       port = static_cast<uint16_t>(port64);
    1311          10 :     }
    1312          17 :   }
    1313             : 
    1314             :   // Now see if this is an IP address. We need to know this because some things (such as setting
    1315             :   // SNI) are special cased if this is an IP address. Either way, we still go through the normal
    1316             :   // resolver flow. We could short-circuit the DNS resolver in this case, but the extra code to do
    1317             :   // so is not worth it since the DNS resolver should handle it for us.
    1318          26 :   bool is_ip_address = false;
    1319          26 :   absl::string_view potential_ip_address = host_to_resolve;
    1320             :   // TODO(mattklein123): Optimally we would support bracket parsing in parseInternetAddress(),
    1321             :   // but we still need to trim the brackets to send the IPv6 address into the DNS resolver. For
    1322             :   // now, just do all the trimming here, but in the future we should consider whether we can
    1323             :   // have unified [] handling as low as possible in the stack.
    1324          26 :   if (!potential_ip_address.empty() && potential_ip_address.front() == '[' &&
    1325          26 :       potential_ip_address.back() == ']') {
    1326           2 :     potential_ip_address.remove_prefix(1);
    1327           2 :     potential_ip_address.remove_suffix(1);
    1328           2 :   }
    1329          26 :   if (Network::Utility::parseInternetAddressNoThrow(std::string(potential_ip_address)) != nullptr) {
    1330          10 :     is_ip_address = true;
    1331          10 :     host_to_resolve = potential_ip_address;
    1332          10 :   }
    1333             : 
    1334          26 :   return {is_ip_address, host_to_resolve, port};
    1335          26 : }
    1336             : 
    1337           1 : void Utility::validateCoreRetryPolicy(const envoy::config::core::v3::RetryPolicy& retry_policy) {
    1338           1 :   if (retry_policy.has_retry_back_off()) {
    1339           1 :     const auto& core_back_off = retry_policy.retry_back_off();
    1340             : 
    1341           1 :     uint64_t base_interval_ms = PROTOBUF_GET_MS_REQUIRED(core_back_off, base_interval);
    1342           1 :     uint64_t max_interval_ms =
    1343           1 :         PROTOBUF_GET_MS_OR_DEFAULT(core_back_off, max_interval, base_interval_ms * 10);
    1344             : 
    1345           1 :     if (max_interval_ms < base_interval_ms) {
    1346           1 :       throwEnvoyExceptionOrPanic("max_interval must be greater than or equal to the base_interval");
    1347           1 :     }
    1348           1 :   }
    1349           1 : }
    1350             : 
    1351             : envoy::config::route::v3::RetryPolicy
    1352             : Utility::convertCoreToRouteRetryPolicy(const envoy::config::core::v3::RetryPolicy& retry_policy,
    1353           0 :                                        const std::string& retry_on) {
    1354           0 :   envoy::config::route::v3::RetryPolicy route_retry_policy;
    1355           0 :   constexpr uint64_t default_base_interval_ms = 1000;
    1356           0 :   constexpr uint64_t default_max_interval_ms = 10 * default_base_interval_ms;
    1357             : 
    1358           0 :   uint64_t base_interval_ms = default_base_interval_ms;
    1359           0 :   uint64_t max_interval_ms = default_max_interval_ms;
    1360             : 
    1361           0 :   if (retry_policy.has_retry_back_off()) {
    1362           0 :     const auto& core_back_off = retry_policy.retry_back_off();
    1363             : 
    1364           0 :     base_interval_ms = PROTOBUF_GET_MS_REQUIRED(core_back_off, base_interval);
    1365           0 :     max_interval_ms =
    1366           0 :         PROTOBUF_GET_MS_OR_DEFAULT(core_back_off, max_interval, base_interval_ms * 10);
    1367             : 
    1368           0 :     if (max_interval_ms < base_interval_ms) {
    1369           0 :       ENVOY_BUG(false, "max_interval must be greater than or equal to the base_interval");
    1370           0 :       base_interval_ms = max_interval_ms / 2;
    1371           0 :     }
    1372           0 :   }
    1373             : 
    1374           0 :   route_retry_policy.mutable_num_retries()->set_value(
    1375           0 :       PROTOBUF_GET_WRAPPED_OR_DEFAULT(retry_policy, num_retries, 1));
    1376             : 
    1377           0 :   auto* route_mutable_back_off = route_retry_policy.mutable_retry_back_off();
    1378             : 
    1379           0 :   route_mutable_back_off->mutable_base_interval()->CopyFrom(
    1380           0 :       Protobuf::util::TimeUtil::MillisecondsToDuration(base_interval_ms));
    1381           0 :   route_mutable_back_off->mutable_max_interval()->CopyFrom(
    1382           0 :       Protobuf::util::TimeUtil::MillisecondsToDuration(max_interval_ms));
    1383             : 
    1384             :   // set all the other fields with appropriate values.
    1385           0 :   route_retry_policy.set_retry_on(retry_on);
    1386           0 :   route_retry_policy.mutable_per_try_timeout()->CopyFrom(
    1387           0 :       route_retry_policy.retry_back_off().max_interval());
    1388             : 
    1389           0 :   return route_retry_policy;
    1390           0 : }
    1391             : 
    1392         251 : bool Utility::isSafeRequest(const Http::RequestHeaderMap& request_headers) {
    1393         251 :   absl::string_view method = request_headers.getMethodValue();
    1394         251 :   return method == Http::Headers::get().MethodValues.Get ||
    1395         251 :          method == Http::Headers::get().MethodValues.Head ||
    1396         251 :          method == Http::Headers::get().MethodValues.Options ||
    1397         251 :          method == Http::Headers::get().MethodValues.Trace;
    1398         251 : }
    1399             : 
    1400           0 : Http::Code Utility::maybeRequestTimeoutCode(bool remote_decode_complete) {
    1401           0 :   return remote_decode_complete ? Http::Code::GatewayTimeout
    1402             :                                 // Http::Code::RequestTimeout is more expensive because HTTP1 client
    1403             :                                 // cannot use the connection any more.
    1404           0 :                                 : Http::Code::RequestTimeout;
    1405           0 : }
    1406             : 
    1407         738 : bool Utility::schemeIsValid(const absl::string_view scheme) {
    1408         738 :   return schemeIsHttp(scheme) || schemeIsHttps(scheme);
    1409         738 : }
    1410             : 
    1411         738 : bool Utility::schemeIsHttp(const absl::string_view scheme) {
    1412         738 :   if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.handle_uppercase_scheme")) {
    1413           0 :     return scheme == Headers::get().SchemeValues.Http;
    1414           0 :   }
    1415         738 :   return absl::EqualsIgnoreCase(scheme, Headers::get().SchemeValues.Http);
    1416         738 : }
    1417             : 
    1418         336 : bool Utility::schemeIsHttps(const absl::string_view scheme) {
    1419         336 :   if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.handle_uppercase_scheme")) {
    1420           0 :     return scheme == Headers::get().SchemeValues.Https;
    1421           0 :   }
    1422         336 :   return absl::EqualsIgnoreCase(scheme, Headers::get().SchemeValues.Https);
    1423         336 : }
    1424             : 
    1425             : std::string Utility::newUri(::Envoy::OptRef<const Utility::RedirectConfig> redirect_config,
    1426         202 :                             const Http::RequestHeaderMap& headers) {
    1427         202 :   absl::string_view final_scheme;
    1428         202 :   absl::string_view final_host;
    1429         202 :   absl::string_view final_port;
    1430         202 :   absl::string_view final_path;
    1431             : 
    1432         202 :   if (redirect_config.has_value() && !redirect_config->scheme_redirect_.empty()) {
    1433           0 :     final_scheme = redirect_config->scheme_redirect_;
    1434         202 :   } else if (redirect_config.has_value() && redirect_config->https_redirect_) {
    1435           0 :     final_scheme = Http::Headers::get().SchemeValues.Https;
    1436         202 :   } else {
    1437             :     // Serve the redirect URL based on the scheme of the original URL, not the
    1438             :     // security of the underlying connection.
    1439         202 :     final_scheme = headers.getSchemeValue();
    1440         202 :   }
    1441             : 
    1442         202 :   if (redirect_config.has_value() && !redirect_config->port_redirect_.empty()) {
    1443           0 :     final_port = redirect_config->port_redirect_;
    1444         202 :   } else {
    1445         202 :     final_port = "";
    1446         202 :   }
    1447             : 
    1448         202 :   if (redirect_config.has_value() && !redirect_config->host_redirect_.empty()) {
    1449           0 :     final_host = redirect_config->host_redirect_;
    1450         202 :   } else {
    1451         202 :     ASSERT(headers.Host());
    1452         202 :     final_host = processRequestHost(headers, final_scheme, final_port);
    1453         202 :   }
    1454             : 
    1455         202 :   std::string final_path_value;
    1456         202 :   if (redirect_config.has_value() && !redirect_config->path_redirect_.empty()) {
    1457             :     // The path_redirect query string, if any, takes precedence over the request's query string,
    1458             :     // and it will not be stripped regardless of `strip_query`.
    1459           0 :     if (redirect_config->path_redirect_has_query_) {
    1460           0 :       final_path = redirect_config->path_redirect_;
    1461           0 :     } else {
    1462           0 :       const absl::string_view current_path = headers.getPathValue();
    1463           0 :       const size_t path_end = current_path.find('?');
    1464           0 :       const bool current_path_has_query = path_end != absl::string_view::npos;
    1465           0 :       if (current_path_has_query) {
    1466           0 :         final_path_value = redirect_config->path_redirect_;
    1467           0 :         final_path_value.append(current_path.data() + path_end, current_path.length() - path_end);
    1468           0 :         final_path = final_path_value;
    1469           0 :       } else {
    1470           0 :         final_path = redirect_config->path_redirect_;
    1471           0 :       }
    1472           0 :     }
    1473         202 :   } else {
    1474         202 :     final_path = headers.getPathValue();
    1475         202 :   }
    1476             : 
    1477         202 :   if (!absl::StartsWith(final_path, "/")) {
    1478           0 :     final_path_value = absl::StrCat("/", final_path);
    1479           0 :     final_path = final_path_value;
    1480           0 :   }
    1481             : 
    1482         202 :   if (redirect_config.has_value() && !redirect_config->path_redirect_has_query_ &&
    1483         202 :       redirect_config->strip_query_) {
    1484           0 :     const size_t path_end = final_path.find('?');
    1485           0 :     if (path_end != absl::string_view::npos) {
    1486           0 :       final_path = final_path.substr(0, path_end);
    1487           0 :     }
    1488           0 :   }
    1489             : 
    1490         202 :   return fmt::format("{}://{}{}{}", final_scheme, final_host, final_port, final_path);
    1491         202 : }
    1492             : 
    1493           0 : bool Utility::isValidRefererValue(absl::string_view value) {
    1494             : 
    1495             :   // First, we try to parse it as an absolute URL and
    1496             :   // ensure that there is no fragment or userinfo.
    1497             :   // If that fails, we can just parse it as a relative reference
    1498             :   // and expect no fragment
    1499             :   // NOTE: "about:blank" is incorrectly rejected here, because
    1500             :   // url.initialize uses http_parser_parse_url, which requires
    1501             :   // a host to be present if there is a schema.
    1502           0 :   Utility::Url url;
    1503             : 
    1504           0 :   if (!Runtime::runtimeFeatureEnabled(
    1505           0 :           "envoy.reloadable_features.http_allow_partial_urls_in_referer")) {
    1506           0 :     if (url.initialize(value, false)) {
    1507           0 :       return true;
    1508           0 :     }
    1509           0 :     return false;
    1510           0 :   }
    1511             : 
    1512           0 :   if (url.initialize(value, false)) {
    1513           0 :     return !(url.containsFragment() || url.containsUserinfo());
    1514           0 :   }
    1515             : 
    1516           0 :   bool seen_slash = false;
    1517             : 
    1518           0 :   for (char c : value) {
    1519           0 :     switch (c) {
    1520           0 :     case ':':
    1521           0 :       if (!seen_slash) {
    1522             :         // First path segment cannot contain ':'
    1523             :         // https://www.rfc-editor.org/rfc/rfc3986#section-3.3
    1524           0 :         return false;
    1525           0 :       }
    1526           0 :       continue;
    1527           0 :     case '/':
    1528           0 :       seen_slash = true;
    1529           0 :       continue;
    1530           0 :     default:
    1531           0 :       if (!testCharInTable(kUriQueryAndFragmentCharTable, c)) {
    1532           0 :         return false;
    1533           0 :       }
    1534           0 :     }
    1535           0 :   }
    1536             : 
    1537           0 :   return true;
    1538           0 : }
    1539             : 
    1540             : } // namespace Http
    1541             : } // namespace Envoy

Generated by: LCOV version 1.15