LCOV - code coverage report
Current view: top level - source/common/router - config_impl.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 921 1537 59.9 %
Date: 2024-01-05 06:35:25 Functions: 78 129 60.5 %

          Line data    Source code
       1             : #include "source/common/router/config_impl.h"
       2             : 
       3             : #include <algorithm>
       4             : #include <chrono>
       5             : #include <cstdint>
       6             : #include <map>
       7             : #include <memory>
       8             : #include <string>
       9             : #include <vector>
      10             : 
      11             : #include "envoy/config/common/matcher/v3/matcher.pb.h"
      12             : #include "envoy/config/common/matcher/v3/matcher.pb.validate.h"
      13             : #include "envoy/config/core/v3/base.pb.h"
      14             : #include "envoy/config/route/v3/route.pb.h"
      15             : #include "envoy/config/route/v3/route_components.pb.h"
      16             : #include "envoy/http/header_map.h"
      17             : #include "envoy/runtime/runtime.h"
      18             : #include "envoy/type/matcher/v3/http_inputs.pb.h"
      19             : #include "envoy/type/matcher/v3/http_inputs.pb.validate.h"
      20             : #include "envoy/type/matcher/v3/string.pb.h"
      21             : #include "envoy/type/v3/percent.pb.h"
      22             : #include "envoy/upstream/cluster_manager.h"
      23             : #include "envoy/upstream/upstream.h"
      24             : 
      25             : #include "source/common/common/assert.h"
      26             : #include "source/common/common/empty_string.h"
      27             : #include "source/common/common/fmt.h"
      28             : #include "source/common/common/hash.h"
      29             : #include "source/common/common/logger.h"
      30             : #include "source/common/common/regex.h"
      31             : #include "source/common/common/utility.h"
      32             : #include "source/common/config/metadata.h"
      33             : #include "source/common/config/utility.h"
      34             : #include "source/common/config/well_known_names.h"
      35             : #include "source/common/http/header_utility.h"
      36             : #include "source/common/http/headers.h"
      37             : #include "source/common/http/matching/data_impl.h"
      38             : #include "source/common/http/path_utility.h"
      39             : #include "source/common/http/utility.h"
      40             : #include "source/common/matcher/matcher.h"
      41             : #include "source/common/protobuf/protobuf.h"
      42             : #include "source/common/protobuf/utility.h"
      43             : #include "source/common/router/context_impl.h"
      44             : #include "source/common/router/reset_header_parser.h"
      45             : #include "source/common/router/retry_state_impl.h"
      46             : #include "source/common/runtime/runtime_features.h"
      47             : #include "source/common/tracing/custom_tag_impl.h"
      48             : #include "source/common/tracing/http_tracer_impl.h"
      49             : #include "source/common/upstream/retry_factory.h"
      50             : #include "source/extensions/early_data/default_early_data_policy.h"
      51             : #include "source/extensions/path/match/uri_template/uri_template_match.h"
      52             : #include "source/extensions/path/rewrite/uri_template/uri_template_rewrite.h"
      53             : 
      54             : #include "absl/strings/match.h"
      55             : 
      56             : namespace Envoy {
      57             : namespace Router {
      58             : namespace {
      59             : 
      60             : constexpr uint32_t DEFAULT_MAX_DIRECT_RESPONSE_BODY_SIZE_BYTES = 4096;
      61             : 
      62           0 : void mergeTransforms(Http::HeaderTransforms& dest, const Http::HeaderTransforms& src) {
      63           0 :   dest.headers_to_append_or_add.insert(dest.headers_to_append_or_add.end(),
      64           0 :                                        src.headers_to_append_or_add.begin(),
      65           0 :                                        src.headers_to_append_or_add.end());
      66           0 :   dest.headers_to_overwrite_or_add.insert(dest.headers_to_overwrite_or_add.end(),
      67           0 :                                           src.headers_to_overwrite_or_add.begin(),
      68           0 :                                           src.headers_to_overwrite_or_add.end());
      69           0 :   dest.headers_to_add_if_absent.insert(dest.headers_to_add_if_absent.end(),
      70           0 :                                        src.headers_to_add_if_absent.begin(),
      71           0 :                                        src.headers_to_add_if_absent.end());
      72           0 :   dest.headers_to_remove.insert(dest.headers_to_remove.end(), src.headers_to_remove.begin(),
      73           0 :                                 src.headers_to_remove.end());
      74           0 : }
      75             : 
      76             : RouteEntryImplBaseConstSharedPtr createAndValidateRoute(
      77             :     const envoy::config::route::v3::Route& route_config, const CommonVirtualHostSharedPtr& vhost,
      78             :     Server::Configuration::ServerFactoryContext& factory_context,
      79             :     ProtobufMessage::ValidationVisitor& validator,
      80        1684 :     const absl::optional<Upstream::ClusterManager::ClusterInfoMaps>& validation_clusters) {
      81             : 
      82        1684 :   RouteEntryImplBaseConstSharedPtr route;
      83        1684 :   switch (route_config.match().path_specifier_case()) {
      84        1322 :   case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPrefix:
      85        1322 :     route = std::make_shared<PrefixRouteEntryImpl>(vhost, route_config, factory_context, validator);
      86        1322 :     break;
      87         147 :   case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPath:
      88         147 :     route = std::make_shared<PathRouteEntryImpl>(vhost, route_config, factory_context, validator);
      89         147 :     break;
      90         119 :   case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kSafeRegex:
      91         119 :     route = std::make_shared<RegexRouteEntryImpl>(vhost, route_config, factory_context, validator);
      92         119 :     break;
      93           0 :   case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kConnectMatcher:
      94           0 :     route =
      95           0 :         std::make_shared<ConnectRouteEntryImpl>(vhost, route_config, factory_context, validator);
      96           0 :     break;
      97          36 :   case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPathSeparatedPrefix:
      98          36 :     route = std::make_shared<PathSeparatedPrefixRouteEntryImpl>(vhost, route_config,
      99          36 :                                                                 factory_context, validator);
     100          36 :     break;
     101          60 :   case envoy::config::route::v3::RouteMatch::PathSpecifierCase::kPathMatchPolicy:
     102          60 :     route = std::make_shared<UriTemplateMatcherRouteEntryImpl>(vhost, route_config, factory_context,
     103          60 :                                                                validator);
     104          60 :     break;
     105           0 :   case envoy::config::route::v3::RouteMatch::PathSpecifierCase::PATH_SPECIFIER_NOT_SET:
     106           0 :     break; // throw the error below.
     107        1684 :   }
     108        1643 :   if (!route) {
     109           0 :     throwEnvoyExceptionOrPanic("Invalid route config");
     110           0 :   }
     111             : 
     112        1643 :   if (validation_clusters.has_value()) {
     113        1605 :     route->validateClusters(*validation_clusters);
     114        1605 :     for (const auto& shadow_policy : route->shadowPolicies()) {
     115           0 :       if (!shadow_policy->cluster().empty()) {
     116           0 :         ASSERT(shadow_policy->clusterHeader().get().empty());
     117           0 :         if (!validation_clusters->hasCluster(shadow_policy->cluster())) {
     118           0 :           throwEnvoyExceptionOrPanic(
     119           0 :               fmt::format("route: unknown shadow cluster '{}'", shadow_policy->cluster()));
     120           0 :         }
     121           0 :       }
     122           0 :     }
     123        1605 :   }
     124             : 
     125        1643 :   return route;
     126        1643 : }
     127             : 
     128             : class RouteActionValidationVisitor
     129             :     : public Matcher::MatchTreeValidationVisitor<Http::HttpMatchingData> {
     130             : public:
     131             :   absl::Status performDataInputValidation(const Matcher::DataInputFactory<Http::HttpMatchingData>&,
     132           0 :                                           absl::string_view type_url) override {
     133           0 :     static std::string request_header_input_name = TypeUtil::descriptorFullNameToTypeUrl(
     134           0 :         createReflectableMessage(
     135           0 :             envoy::type::matcher::v3::HttpRequestHeaderMatchInput::default_instance())
     136           0 :             ->GetDescriptor()
     137           0 :             ->full_name());
     138           0 :     if (type_url == request_header_input_name) {
     139           0 :       return absl::OkStatus();
     140           0 :     }
     141             : 
     142           0 :     return absl::InvalidArgumentError(
     143           0 :         fmt::format("Route table can only match on request headers, saw {}", type_url));
     144           0 :   }
     145             : };
     146             : 
     147             : const envoy::config::route::v3::WeightedCluster::ClusterWeight& validateWeightedClusterSpecifier(
     148         198 :     const envoy::config::route::v3::WeightedCluster::ClusterWeight& cluster) {
     149         198 :   if (!cluster.name().empty() && !cluster.cluster_header().empty()) {
     150           0 :     throwEnvoyExceptionOrPanic("Only one of name or cluster_header can be specified");
     151         198 :   } else if (cluster.name().empty() && cluster.cluster_header().empty()) {
     152           0 :     throwEnvoyExceptionOrPanic("At least one of name or cluster_header need to be specified");
     153           0 :   }
     154         198 :   return cluster;
     155         198 : }
     156             : 
     157             : // Returns a vector of header parsers, sorted by specificity. The `specificity_ascend` parameter
     158             : // specifies whether the returned parsers will be sorted from least specific to most specific
     159             : // (global connection manager level header parser, virtual host level header parser and finally
     160             : // route-level parser.) or the reverse.
     161             : absl::InlinedVector<const HeaderParser*, 3>
     162             : getHeaderParsers(const HeaderParser* global_route_config_header_parser,
     163             :                  const HeaderParser* vhost_header_parser, const HeaderParser* route_header_parser,
     164         531 :                  bool specificity_ascend) {
     165         531 :   if (specificity_ascend) {
     166             :     // Sorted from least to most specific: global connection manager level headers, virtual host
     167             :     // level headers and finally route-level headers.
     168           1 :     return {global_route_config_header_parser, vhost_header_parser, route_header_parser};
     169         530 :   } else {
     170             :     // Sorted from most to least specific.
     171         530 :     return {route_header_parser, vhost_header_parser, global_route_config_header_parser};
     172         530 :   }
     173         531 : }
     174             : 
     175             : // If the implementation of a cluster specifier plugin is not provided in current Envoy and the
     176             : // plugin is set to optional, then this null plugin will be used as a placeholder.
     177             : class NullClusterSpecifierPlugin : public ClusterSpecifierPlugin {
     178             : public:
     179           0 :   RouteConstSharedPtr route(RouteConstSharedPtr, const Http::RequestHeaderMap&) const override {
     180           0 :     return nullptr;
     181           0 :   }
     182             : };
     183             : 
     184             : ClusterSpecifierPluginSharedPtr
     185             : getClusterSpecifierPluginByTheProto(const envoy::config::route::v3::ClusterSpecifierPlugin& plugin,
     186             :                                     ProtobufMessage::ValidationVisitor& validator,
     187           6 :                                     Server::Configuration::ServerFactoryContext& factory_context) {
     188           6 :   auto* factory =
     189           6 :       Envoy::Config::Utility::getFactory<ClusterSpecifierPluginFactoryConfig>(plugin.extension());
     190           6 :   if (factory == nullptr) {
     191           6 :     if (plugin.is_optional()) {
     192           0 :       return std::make_shared<NullClusterSpecifierPlugin>();
     193           0 :     }
     194           6 :     throwEnvoyExceptionOrPanic(
     195           6 :         fmt::format("Didn't find a registered implementation for '{}' with type URL: '{}'",
     196           6 :                     plugin.extension().name(),
     197           6 :                     Envoy::Config::Utility::getFactoryType(plugin.extension().typed_config())));
     198           6 :   }
     199           0 :   ASSERT(factory != nullptr);
     200           0 :   auto config =
     201           0 :       Envoy::Config::Utility::translateToFactoryConfig(plugin.extension(), validator, *factory);
     202           0 :   return factory->createClusterSpecifierPlugin(*config, factory_context);
     203           6 : }
     204             : 
     205             : ::Envoy::Http::Utility::RedirectConfig
     206         480 : createRedirectConfig(const envoy::config::route::v3::Route& route) {
     207         480 :   ::Envoy::Http::Utility::RedirectConfig redirect_config{
     208         480 :       route.redirect().scheme_redirect(),
     209         480 :       route.redirect().host_redirect(),
     210         480 :       route.redirect().port_redirect() ? ":" + std::to_string(route.redirect().port_redirect())
     211         480 :                                        : "",
     212         480 :       route.redirect().path_redirect(),
     213         480 :       route.redirect().prefix_rewrite(),
     214         480 :       route.redirect().has_regex_rewrite() ? route.redirect().regex_rewrite().substitution() : "",
     215         480 :       route.redirect().has_regex_rewrite()
     216         480 :           ? Regex::Utility::parseRegex(route.redirect().regex_rewrite().pattern())
     217         480 :           : nullptr,
     218         480 :       route.redirect().path_redirect().find('?') != absl::string_view::npos,
     219         480 :       route.redirect().https_redirect(),
     220         480 :       route.redirect().strip_query()};
     221         480 :   if (route.redirect().has_regex_rewrite()) {
     222          64 :     ASSERT(redirect_config.prefix_rewrite_redirect_.empty());
     223          64 :   }
     224         480 :   return redirect_config;
     225         480 : }
     226             : 
     227             : } // namespace
     228             : 
     229         196 : const std::string& OriginalConnectPort::key() {
     230         196 :   CONSTRUCT_ON_FIRST_USE(std::string, "envoy.router.original_connect_port");
     231         196 : }
     232             : 
     233           0 : std::string SslRedirector::newUri(const Http::RequestHeaderMap& headers) const {
     234           0 :   return Http::Utility::createSslRedirectPath(headers);
     235           0 : }
     236             : 
     237             : HedgePolicyImpl::HedgePolicyImpl(const envoy::config::route::v3::HedgePolicy& hedge_policy)
     238             :     : initial_requests_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(hedge_policy, initial_requests, 1)),
     239             :       additional_request_chance_(hedge_policy.additional_request_chance()),
     240          36 :       hedge_on_per_try_timeout_(hedge_policy.hedge_on_per_try_timeout()) {}
     241             : 
     242           6 : HedgePolicyImpl::HedgePolicyImpl() : initial_requests_(1), hedge_on_per_try_timeout_(false) {}
     243             : 
     244             : RetryPolicyImpl::RetryPolicyImpl(const envoy::config::route::v3::RetryPolicy& retry_policy,
     245             :                                  ProtobufMessage::ValidationVisitor& validation_visitor,
     246             :                                  Upstream::RetryExtensionFactoryContext& factory_context)
     247             :     : retriable_headers_(
     248             :           Http::HeaderUtility::buildHeaderMatcherVector(retry_policy.retriable_headers())),
     249             :       retriable_request_headers_(
     250             :           Http::HeaderUtility::buildHeaderMatcherVector(retry_policy.retriable_request_headers())),
     251          88 :       validation_visitor_(&validation_visitor) {
     252          88 :   per_try_timeout_ =
     253          88 :       std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(retry_policy, per_try_timeout, 0));
     254          88 :   per_try_idle_timeout_ =
     255          88 :       std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(retry_policy, per_try_idle_timeout, 0));
     256          88 :   num_retries_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(retry_policy, num_retries, 1);
     257          88 :   retry_on_ = RetryStateImpl::parseRetryOn(retry_policy.retry_on()).first;
     258          88 :   retry_on_ |= RetryStateImpl::parseRetryGrpcOn(retry_policy.retry_on()).first;
     259             : 
     260          88 :   for (const auto& host_predicate : retry_policy.retry_host_predicate()) {
     261           2 :     auto& factory = Envoy::Config::Utility::getAndCheckFactory<Upstream::RetryHostPredicateFactory>(
     262           2 :         host_predicate);
     263           2 :     auto config = Envoy::Config::Utility::translateToFactoryConfig(host_predicate,
     264           2 :                                                                    validation_visitor, factory);
     265           2 :     retry_host_predicate_configs_.emplace_back(factory, std::move(config));
     266           2 :   }
     267             : 
     268          88 :   const auto& retry_priority = retry_policy.retry_priority();
     269          88 :   if (!retry_priority.name().empty()) {
     270           0 :     auto& factory =
     271           0 :         Envoy::Config::Utility::getAndCheckFactory<Upstream::RetryPriorityFactory>(retry_priority);
     272           0 :     retry_priority_config_ =
     273           0 :         std::make_pair(&factory, Envoy::Config::Utility::translateToFactoryConfig(
     274           0 :                                      retry_priority, validation_visitor, factory));
     275           0 :   }
     276             : 
     277          88 :   for (const auto& options_predicate : retry_policy.retry_options_predicates()) {
     278          26 :     auto& factory =
     279          26 :         Envoy::Config::Utility::getAndCheckFactory<Upstream::RetryOptionsPredicateFactory>(
     280          26 :             options_predicate);
     281          26 :     retry_options_predicates_.emplace_back(
     282          26 :         factory.createOptionsPredicate(*Envoy::Config::Utility::translateToFactoryConfig(
     283          26 :                                            options_predicate, validation_visitor, factory),
     284          26 :                                        factory_context));
     285          26 :   }
     286             : 
     287          88 :   auto host_selection_attempts = retry_policy.host_selection_retry_max_attempts();
     288          88 :   if (host_selection_attempts) {
     289           0 :     host_selection_attempts_ = host_selection_attempts;
     290           0 :   }
     291             : 
     292          88 :   for (auto code : retry_policy.retriable_status_codes()) {
     293           4 :     retriable_status_codes_.emplace_back(code);
     294           4 :   }
     295             : 
     296          88 :   if (retry_policy.has_retry_back_off()) {
     297          16 :     base_interval_ = std::chrono::milliseconds(
     298          16 :         PROTOBUF_GET_MS_REQUIRED(retry_policy.retry_back_off(), base_interval));
     299          16 :     if ((*base_interval_).count() < 1) {
     300           0 :       base_interval_ = std::chrono::milliseconds(1);
     301           0 :     }
     302             : 
     303          16 :     max_interval_ = PROTOBUF_GET_OPTIONAL_MS(retry_policy.retry_back_off(), max_interval);
     304          16 :     if (max_interval_) {
     305             :       // Apply the same rounding to max interval in case both are set to sub-millisecond values.
     306           0 :       if ((*max_interval_).count() < 1) {
     307           0 :         max_interval_ = std::chrono::milliseconds(1);
     308           0 :       }
     309             : 
     310           0 :       if ((*max_interval_).count() < (*base_interval_).count()) {
     311           0 :         throwEnvoyExceptionOrPanic(
     312           0 :             "retry_policy.max_interval must greater than or equal to the base_interval");
     313           0 :       }
     314           0 :     }
     315          16 :   }
     316             : 
     317          88 :   if (retry_policy.has_rate_limited_retry_back_off()) {
     318           0 :     reset_headers_ = ResetHeaderParserImpl::buildResetHeaderParserVector(
     319           0 :         retry_policy.rate_limited_retry_back_off().reset_headers());
     320             : 
     321           0 :     absl::optional<std::chrono::milliseconds> reset_max_interval =
     322           0 :         PROTOBUF_GET_OPTIONAL_MS(retry_policy.rate_limited_retry_back_off(), max_interval);
     323           0 :     if (reset_max_interval.has_value()) {
     324           0 :       std::chrono::milliseconds max_interval = reset_max_interval.value();
     325           0 :       if (max_interval.count() < 1) {
     326           0 :         max_interval = std::chrono::milliseconds(1);
     327           0 :       }
     328           0 :       reset_max_interval_ = max_interval;
     329           0 :     }
     330           0 :   }
     331          88 : }
     332             : 
     333           0 : std::vector<Upstream::RetryHostPredicateSharedPtr> RetryPolicyImpl::retryHostPredicates() const {
     334           0 :   std::vector<Upstream::RetryHostPredicateSharedPtr> predicates;
     335           0 :   predicates.reserve(retry_host_predicate_configs_.size());
     336           0 :   for (const auto& config : retry_host_predicate_configs_) {
     337           0 :     predicates.emplace_back(config.first.createHostPredicate(*config.second, num_retries_));
     338           0 :   }
     339             : 
     340           0 :   return predicates;
     341           0 : }
     342             : 
     343           0 : Upstream::RetryPrioritySharedPtr RetryPolicyImpl::retryPriority() const {
     344           0 :   if (retry_priority_config_.first == nullptr) {
     345           0 :     return nullptr;
     346           0 :   }
     347             : 
     348           0 :   return retry_priority_config_.first->createRetryPriority(*retry_priority_config_.second,
     349           0 :                                                            *validation_visitor_, num_retries_);
     350           0 : }
     351             : 
     352             : InternalRedirectPolicyImpl::InternalRedirectPolicyImpl(
     353             :     const envoy::config::route::v3::InternalRedirectPolicy& policy_config,
     354             :     ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name)
     355             :     : current_route_name_(current_route_name),
     356             :       redirect_response_codes_(buildRedirectResponseCodes(policy_config)),
     357             :       max_internal_redirects_(
     358             :           PROTOBUF_GET_WRAPPED_OR_DEFAULT(policy_config, max_internal_redirects, 1)),
     359           9 :       enabled_(true), allow_cross_scheme_redirect_(policy_config.allow_cross_scheme_redirect()) {
     360           9 :   for (const auto& predicate : policy_config.predicates()) {
     361           1 :     auto& factory =
     362           1 :         Envoy::Config::Utility::getAndCheckFactory<InternalRedirectPredicateFactory>(predicate);
     363           1 :     auto config = factory.createEmptyConfigProto();
     364           1 :     Envoy::Config::Utility::translateOpaqueConfig(predicate.typed_config(), validator, *config);
     365           1 :     predicate_factories_.emplace_back(&factory, std::move(config));
     366           1 :   }
     367           9 :   for (const auto& header : policy_config.response_headers_to_copy()) {
     368           4 :     if (!Http::HeaderUtility::isModifiableHeader(header)) {
     369           0 :       throwEnvoyExceptionOrPanic(":-prefixed headers or Hosts may not be specified here.");
     370           0 :     }
     371           4 :     response_headers_to_copy_.emplace_back(header);
     372           4 :   }
     373           9 : }
     374             : 
     375           0 : std::vector<InternalRedirectPredicateSharedPtr> InternalRedirectPolicyImpl::predicates() const {
     376           0 :   std::vector<InternalRedirectPredicateSharedPtr> predicates;
     377           0 :   predicates.reserve(predicate_factories_.size());
     378           0 :   for (const auto& predicate_factory : predicate_factories_) {
     379           0 :     predicates.emplace_back(predicate_factory.first->createInternalRedirectPredicate(
     380           0 :         *predicate_factory.second, current_route_name_));
     381           0 :   }
     382           0 :   return predicates;
     383           0 : }
     384             : 
     385             : const std::vector<Http::LowerCaseString>&
     386           0 : InternalRedirectPolicyImpl::responseHeadersToCopy() const {
     387           0 :   return response_headers_to_copy_;
     388           0 : }
     389             : 
     390             : absl::flat_hash_set<Http::Code> InternalRedirectPolicyImpl::buildRedirectResponseCodes(
     391           9 :     const envoy::config::route::v3::InternalRedirectPolicy& policy_config) const {
     392           9 :   if (policy_config.redirect_response_codes_size() == 0) {
     393           5 :     return absl::flat_hash_set<Http::Code>{Http::Code::Found};
     394           5 :   }
     395           4 :   absl::flat_hash_set<Http::Code> ret;
     396           4 :   std::for_each(policy_config.redirect_response_codes().begin(),
     397          12 :                 policy_config.redirect_response_codes().end(), [&ret](uint32_t response_code) {
     398          12 :                   const absl::flat_hash_set<uint32_t> valid_redirect_response_code = {301, 302, 303,
     399          12 :                                                                                       307, 308};
     400          12 :                   if (valid_redirect_response_code.contains(response_code)) {
     401           6 :                     ret.insert(static_cast<Http::Code>(response_code));
     402           6 :                   }
     403          12 :                 });
     404           4 :   return ret;
     405           9 : }
     406             : 
     407             : void validateMirrorClusterSpecifier(
     408          56 :     const envoy::config::route::v3::RouteAction::RequestMirrorPolicy& config) {
     409          56 :   if (!config.cluster().empty() && !config.cluster_header().empty()) {
     410           0 :     throwEnvoyExceptionOrPanic(fmt::format("Only one of cluster '{}' or cluster_header '{}' "
     411           0 :                                            "in request mirror policy can be specified",
     412           0 :                                            config.cluster(), config.cluster_header()));
     413          56 :   } else if (config.cluster().empty() && config.cluster_header().empty()) {
     414             :     // For shadow policies with `cluster_header_`, we only verify that this field is not
     415             :     // empty because the cluster name is not set yet at config time.
     416           0 :     throwEnvoyExceptionOrPanic(
     417           0 :         "Exactly one of cluster or cluster_header in request mirror policy need to be specified");
     418           0 :   }
     419          56 : }
     420             : 
     421             : ShadowPolicyImpl::ShadowPolicyImpl(const RequestMirrorPolicy& config)
     422          56 :     : cluster_(config.cluster()), cluster_header_(config.cluster_header()) {
     423          56 :   validateMirrorClusterSpecifier(config);
     424             : 
     425          56 :   if (config.has_runtime_fraction()) {
     426           0 :     runtime_key_ = config.runtime_fraction().runtime_key();
     427           0 :     default_value_ = config.runtime_fraction().default_value();
     428          56 :   } else {
     429             :     // If there is no runtime fraction specified, the default is 100% sampled. By leaving
     430             :     // runtime_key_ empty and forcing the default to 100% this will yield the expected behavior.
     431          56 :     default_value_.set_numerator(100);
     432          56 :     default_value_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
     433          56 :   }
     434          56 :   trace_sampled_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, trace_sampled, true);
     435          56 : }
     436             : 
     437             : DecoratorImpl::DecoratorImpl(const envoy::config::route::v3::Decorator& decorator)
     438             :     : operation_(decorator.operation()),
     439          44 :       propagate_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(decorator, propagate, true)) {}
     440             : 
     441           0 : void DecoratorImpl::apply(Tracing::Span& span) const {
     442           0 :   if (!operation_.empty()) {
     443           0 :     span.setOperation(operation_);
     444           0 :   }
     445           0 : }
     446             : 
     447           0 : const std::string& DecoratorImpl::getOperation() const { return operation_; }
     448             : 
     449           0 : bool DecoratorImpl::propagate() const { return propagate_; }
     450             : 
     451           6 : RouteTracingImpl::RouteTracingImpl(const envoy::config::route::v3::Tracing& tracing) {
     452           6 :   if (!tracing.has_client_sampling()) {
     453           0 :     client_sampling_.set_numerator(100);
     454           0 :     client_sampling_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
     455           6 :   } else {
     456           6 :     client_sampling_ = tracing.client_sampling();
     457           6 :   }
     458           6 :   if (!tracing.has_random_sampling()) {
     459           6 :     random_sampling_.set_numerator(100);
     460           6 :     random_sampling_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
     461           6 :   } else {
     462           0 :     random_sampling_ = tracing.random_sampling();
     463           0 :   }
     464           6 :   if (!tracing.has_overall_sampling()) {
     465           6 :     overall_sampling_.set_numerator(100);
     466           6 :     overall_sampling_.set_denominator(envoy::type::v3::FractionalPercent::HUNDRED);
     467           6 :   } else {
     468           0 :     overall_sampling_ = tracing.overall_sampling();
     469           0 :   }
     470           6 :   for (const auto& tag : tracing.custom_tags()) {
     471           0 :     custom_tags_.emplace(tag.tag(), Tracing::CustomTagUtility::createCustomTag(tag));
     472           0 :   }
     473           6 : }
     474             : 
     475           0 : const envoy::type::v3::FractionalPercent& RouteTracingImpl::getClientSampling() const {
     476           0 :   return client_sampling_;
     477           0 : }
     478             : 
     479           0 : const envoy::type::v3::FractionalPercent& RouteTracingImpl::getRandomSampling() const {
     480           0 :   return random_sampling_;
     481           0 : }
     482             : 
     483           0 : const envoy::type::v3::FractionalPercent& RouteTracingImpl::getOverallSampling() const {
     484           0 :   return overall_sampling_;
     485           0 : }
     486           0 : const Tracing::CustomTagMap& RouteTracingImpl::getCustomTags() const { return custom_tags_; }
     487             : 
     488             : RouteEntryImplBase::RouteEntryImplBase(const CommonVirtualHostSharedPtr& vhost,
     489             :                                        const envoy::config::route::v3::Route& route,
     490             :                                        Server::Configuration::ServerFactoryContext& factory_context,
     491             :                                        ProtobufMessage::ValidationVisitor& validator)
     492             :     : prefix_rewrite_(route.route().prefix_rewrite()),
     493             :       path_matcher_(buildPathMatcher(route, validator)),
     494             :       path_rewriter_(buildPathRewriter(route, validator)),
     495             :       host_rewrite_(route.route().host_rewrite_literal()), vhost_(vhost),
     496             :       auto_host_rewrite_header_(!route.route().host_rewrite_header().empty()
     497             :                                     ? absl::optional<Http::LowerCaseString>(Http::LowerCaseString(
     498             :                                           route.route().host_rewrite_header()))
     499             :                                     : absl::nullopt),
     500             :       host_rewrite_path_regex_(
     501             :           route.route().has_host_rewrite_path_regex()
     502             :               ? Regex::Utility::parseRegex(route.route().host_rewrite_path_regex().pattern())
     503             :               : nullptr),
     504             :       host_rewrite_path_regex_substitution_(
     505             :           route.route().has_host_rewrite_path_regex()
     506             :               ? route.route().host_rewrite_path_regex().substitution()
     507             :               : ""),
     508             :       cluster_name_(route.route().cluster()), cluster_header_name_(route.route().cluster_header()),
     509             :       timeout_(PROTOBUF_GET_MS_OR_DEFAULT(route.route(), timeout, DEFAULT_ROUTE_TIMEOUT_MS)),
     510             :       optional_timeouts_(buildOptionalTimeouts(route.route())), loader_(factory_context.runtime()),
     511             :       runtime_(loadRuntimeData(route.match())),
     512             :       redirect_config_(route.has_redirect()
     513             :                            ? std::make_unique<::Envoy::Http::Utility::RedirectConfig>(
     514             :                                  createRedirectConfig(route))
     515             :                            : nullptr),
     516             :       hedge_policy_(buildHedgePolicy(vhost->hedgePolicy(), route.route())),
     517             :       retry_policy_(
     518             :           buildRetryPolicy(vhost->retryPolicy(), route.route(), validator, factory_context)),
     519             :       internal_redirect_policy_(
     520             :           buildInternalRedirectPolicy(route.route(), validator, route.name())),
     521             :       config_headers_(Http::HeaderUtility::buildHeaderDataVector(route.match().headers())),
     522             :       dynamic_metadata_(route.match().dynamic_metadata().begin(),
     523             :                         route.match().dynamic_metadata().end()),
     524             :       opaque_config_(parseOpaqueConfig(route)), decorator_(parseDecorator(route)),
     525             :       route_tracing_(parseRouteTracing(route)),
     526             :       per_filter_configs_(route.typed_per_filter_config(), factory_context, validator),
     527             :       route_name_(route.name()), time_source_(factory_context.mainThreadDispatcher().timeSource()),
     528             :       retry_shadow_buffer_limit_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
     529             :           route, per_request_buffer_limit_bytes, vhost->retryShadowBufferLimit())),
     530             :       direct_response_code_(ConfigUtility::parseDirectResponseCode(route)),
     531             :       cluster_not_found_response_code_(ConfigUtility::parseClusterNotFoundResponseCode(
     532             :           route.route().cluster_not_found_response_code())),
     533             :       priority_(ConfigUtility::parsePriority(route.route().priority())),
     534             :       auto_host_rewrite_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(route.route(), auto_host_rewrite, false)),
     535             :       append_xfh_(route.route().append_x_forwarded_host()),
     536             :       using_new_timeouts_(route.route().has_max_stream_duration()),
     537             :       match_grpc_(route.match().has_grpc()),
     538        1684 :       case_sensitive_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(route.match(), case_sensitive, true)) {
     539        1684 :   auto body_or_error = ConfigUtility::parseDirectResponseBody(
     540        1684 :       route, factory_context.api(), vhost_->globalRouteConfig().maxDirectResponseBodySizeBytes());
     541        1684 :   THROW_IF_STATUS_NOT_OK(body_or_error, throw);
     542        1684 :   direct_response_body_ = body_or_error.value();
     543        1684 :   if (!route.request_headers_to_add().empty() || !route.request_headers_to_remove().empty()) {
     544          23 :     request_headers_parser_ =
     545          23 :         HeaderParser::configure(route.request_headers_to_add(), route.request_headers_to_remove());
     546          23 :   }
     547        1684 :   if (!route.response_headers_to_add().empty() || !route.response_headers_to_remove().empty()) {
     548          18 :     response_headers_parser_ = HeaderParser::configure(route.response_headers_to_add(),
     549          18 :                                                        route.response_headers_to_remove());
     550          18 :   }
     551        1684 :   if (route.has_metadata()) {
     552          42 :     metadata_ = std::make_unique<RouteMetadataPack>(route.metadata());
     553          42 :   }
     554        1684 :   if (route.route().has_metadata_match()) {
     555          16 :     const auto filter_it = route.route().metadata_match().filter_metadata().find(
     556          16 :         Envoy::Config::MetadataFilters::get().ENVOY_LB);
     557          16 :     if (filter_it != route.route().metadata_match().filter_metadata().end()) {
     558          16 :       metadata_match_criteria_ = std::make_unique<MetadataMatchCriteriaImpl>(filter_it->second);
     559          16 :     }
     560          16 :   }
     561             : 
     562        1684 :   shadow_policies_.reserve(route.route().request_mirror_policies().size());
     563        1684 :   for (const auto& mirror_policy_config : route.route().request_mirror_policies()) {
     564          32 :     shadow_policies_.push_back(std::make_shared<ShadowPolicyImpl>(mirror_policy_config));
     565          32 :   }
     566             : 
     567             :   // Inherit policies from the virtual host, which might be from the route config.
     568        1684 :   if (shadow_policies_.empty()) {
     569        1617 :     shadow_policies_ = vhost->shadowPolicies();
     570        1617 :   }
     571             : 
     572             :   // If this is a weighted_cluster, we create N internal route entries
     573             :   // (called WeightedClusterEntry), such that each object is a simple
     574             :   // single cluster, pointing back to the parent. Metadata criteria
     575             :   // from the weighted cluster (if any) are merged with and override
     576             :   // the criteria from the route.
     577        1684 :   if (route.route().cluster_specifier_case() ==
     578        1684 :       envoy::config::route::v3::RouteAction::ClusterSpecifierCase::kWeightedClusters) {
     579          86 :     uint64_t total_weight = 0UL;
     580          86 :     const std::string& runtime_key_prefix = route.route().weighted_clusters().runtime_key_prefix();
     581             : 
     582          86 :     std::vector<WeightedClusterEntrySharedPtr> weighted_clusters;
     583          86 :     weighted_clusters.reserve(route.route().weighted_clusters().clusters().size());
     584         198 :     for (const auto& cluster : route.route().weighted_clusters().clusters()) {
     585         198 :       auto cluster_entry = std::make_unique<WeightedClusterEntry>(
     586         198 :           this, runtime_key_prefix + "." + cluster.name(), factory_context, validator, cluster);
     587         198 :       weighted_clusters.emplace_back(std::move(cluster_entry));
     588         198 :       total_weight += weighted_clusters.back()->clusterWeight();
     589         198 :       if (total_weight > std::numeric_limits<uint32_t>::max()) {
     590           0 :         throwEnvoyExceptionOrPanic(
     591           0 :             fmt::format("The sum of weights of all weighted clusters of route {} exceeds {}",
     592           0 :                         route_name_, std::numeric_limits<uint32_t>::max()));
     593           0 :       }
     594         198 :     }
     595             : 
     596             :     // Reject the config if the total_weight of all clusters is 0.
     597          86 :     if (total_weight == 0) {
     598           0 :       throwEnvoyExceptionOrPanic("Sum of weights in the weighted_cluster must be greater than 0.");
     599           0 :     }
     600             : 
     601          86 :     weighted_clusters_config_ =
     602          86 :         std::make_unique<WeightedClustersConfig>(std::move(weighted_clusters), total_weight,
     603          86 :                                                  route.route().weighted_clusters().header_name());
     604             : 
     605        1598 :   } else if (route.route().cluster_specifier_case() ==
     606        1598 :              envoy::config::route::v3::RouteAction::ClusterSpecifierCase::
     607        1598 :                  kInlineClusterSpecifierPlugin) {
     608           2 :     cluster_specifier_plugin_ = getClusterSpecifierPluginByTheProto(
     609           2 :         route.route().inline_cluster_specifier_plugin(), validator, factory_context);
     610        1596 :   } else if (route.route().has_cluster_specifier_plugin()) {
     611           0 :     cluster_specifier_plugin_ = vhost_->globalRouteConfig().clusterSpecifierPlugin(
     612           0 :         route.route().cluster_specifier_plugin());
     613           0 :   }
     614             : 
     615        1684 :   for (const auto& query_parameter : route.match().query_parameters()) {
     616          58 :     config_query_parameters_.push_back(
     617          58 :         std::make_unique<ConfigUtility::QueryParameterMatcher>(query_parameter));
     618          58 :   }
     619             : 
     620        1684 :   if (!route.route().hash_policy().empty()) {
     621         122 :     hash_policy_ = std::make_unique<Http::HashPolicyImpl>(route.route().hash_policy());
     622         122 :   }
     623             : 
     624        1684 :   if (route.match().has_tls_context()) {
     625          30 :     tls_context_match_criteria_ =
     626          30 :         std::make_unique<TlsContextMatchCriteriaImpl>(route.match().tls_context());
     627          30 :   }
     628             : 
     629        1684 :   if (!route.route().rate_limits().empty()) {
     630           4 :     rate_limit_policy_ =
     631           4 :         std::make_unique<RateLimitPolicyImpl>(route.route().rate_limits(), factory_context);
     632           4 :   }
     633             : 
     634             :   // Returns true if include_vh_rate_limits is explicitly set to true otherwise it defaults to false
     635             :   // which is similar to VhRateLimitOptions::Override and will only use virtual host rate limits if
     636             :   // the route is empty
     637        1684 :   include_vh_rate_limits_ =
     638        1684 :       PROTOBUF_GET_WRAPPED_OR_DEFAULT(route.route(), include_vh_rate_limits, false);
     639             : 
     640        1684 :   if (route.route().has_cors()) {
     641           2 :     cors_policy_ =
     642           2 :         std::make_unique<CorsPolicyImpl>(route.route().cors(), factory_context.runtime());
     643           2 :   }
     644        1684 :   for (const auto& upgrade_config : route.route().upgrade_configs()) {
     645           4 :     const bool enabled = upgrade_config.has_enabled() ? upgrade_config.enabled().value() : true;
     646           4 :     const bool success =
     647           4 :         upgrade_map_
     648           4 :             .emplace(std::make_pair(
     649           4 :                 Envoy::Http::LowerCaseString(upgrade_config.upgrade_type()).get(), enabled))
     650           4 :             .second;
     651           4 :     if (!success) {
     652           0 :       throwEnvoyExceptionOrPanic(absl::StrCat("Duplicate upgrade ", upgrade_config.upgrade_type()));
     653           0 :     }
     654           4 :     if (absl::EqualsIgnoreCase(upgrade_config.upgrade_type(),
     655           4 :                                Http::Headers::get().MethodValues.Connect) ||
     656           4 :         (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.enable_connect_udp_support") &&
     657           4 :          absl::EqualsIgnoreCase(upgrade_config.upgrade_type(),
     658           4 :                                 Http::Headers::get().UpgradeValues.ConnectUdp))) {
     659           0 :       connect_config_ = std::make_unique<ConnectConfig>(upgrade_config.connect_config());
     660           4 :     } else if (upgrade_config.has_connect_config()) {
     661           0 :       throwEnvoyExceptionOrPanic(absl::StrCat("Non-CONNECT upgrade type ",
     662           0 :                                               upgrade_config.upgrade_type(), " has ConnectConfig"));
     663           0 :     }
     664           4 :   }
     665             : 
     666        1684 :   int num_rewrite_polices = 0;
     667        1684 :   if (path_rewriter_ != nullptr) {
     668          44 :     ++num_rewrite_polices;
     669          44 :   }
     670             : 
     671        1684 :   if (!prefix_rewrite_.empty()) {
     672         166 :     ++num_rewrite_polices;
     673         166 :   }
     674             : 
     675        1684 :   if (route.route().has_regex_rewrite()) {
     676           0 :     ++num_rewrite_polices;
     677           0 :   }
     678             : 
     679        1684 :   if (num_rewrite_polices > 1) {
     680           0 :     throwEnvoyExceptionOrPanic(
     681           0 :         "Specify only one of prefix_rewrite, regex_rewrite or path_rewrite_policy");
     682           0 :   }
     683             : 
     684        1684 :   if (!prefix_rewrite_.empty() && path_matcher_ != nullptr) {
     685           0 :     throwEnvoyExceptionOrPanic("Cannot use prefix_rewrite with matcher extension");
     686           0 :   }
     687             : 
     688        1684 :   if (route.route().has_regex_rewrite()) {
     689           0 :     auto rewrite_spec = route.route().regex_rewrite();
     690           0 :     regex_rewrite_ = Regex::Utility::parseRegex(rewrite_spec.pattern());
     691           0 :     regex_rewrite_substitution_ = rewrite_spec.substitution();
     692           0 :   }
     693             : 
     694        1684 :   if (path_rewriter_ != nullptr) {
     695          44 :     absl::Status compatible_status = path_rewriter_->isCompatiblePathMatcher(path_matcher_);
     696          44 :     if (!compatible_status.ok()) {
     697           0 :       throwEnvoyExceptionOrPanic(std::string(compatible_status.message()));
     698           0 :     }
     699          44 :   }
     700             : 
     701        1684 :   if (redirect_config_ != nullptr && redirect_config_->path_redirect_has_query_ &&
     702        1684 :       redirect_config_->strip_query_) {
     703          22 :     ENVOY_LOG(warn,
     704          22 :               "`strip_query` is set to true, but `path_redirect` contains query string and it will "
     705          22 :               "not be stripped: {}",
     706          22 :               redirect_config_->path_redirect_);
     707          22 :   }
     708        1684 :   if (!route.stat_prefix().empty()) {
     709           5 :     route_stats_context_ = std::make_unique<RouteStatsContextImpl>(
     710           5 :         factory_context.scope(), factory_context.routerContext().routeStatNames(),
     711           5 :         vhost->statName(), route.stat_prefix());
     712           5 :   }
     713             : 
     714        1684 :   if (route.route().has_early_data_policy()) {
     715           0 :     auto& factory = Envoy::Config::Utility::getAndCheckFactory<EarlyDataPolicyFactory>(
     716           0 :         route.route().early_data_policy());
     717           0 :     auto message = Envoy::Config::Utility::translateToFactoryConfig(
     718           0 :         route.route().early_data_policy(), validator, factory);
     719           0 :     early_data_policy_ = factory.createEarlyDataPolicy(*message);
     720        1684 :   } else {
     721        1684 :     early_data_policy_ = std::make_unique<DefaultEarlyDataPolicy>(/*allow_safe_request*/ true);
     722        1684 :   }
     723        1684 : }
     724             : 
     725         674 : bool RouteEntryImplBase::evaluateRuntimeMatch(const uint64_t random_value) const {
     726         674 :   return runtime_ == nullptr
     727         674 :              ? true
     728         674 :              : loader_.snapshot().featureEnabled(runtime_->fractional_runtime_key_,
     729           0 :                                                  runtime_->fractional_runtime_default_,
     730           0 :                                                  random_value);
     731         674 : }
     732             : 
     733             : absl::string_view
     734         674 : RouteEntryImplBase::sanitizePathBeforePathMatching(const absl::string_view path) const {
     735         674 :   absl::string_view ret = path;
     736         674 :   if (vhost_->globalRouteConfig().ignorePathParametersInPathMatching()) {
     737           1 :     auto pos = ret.find_first_of(';');
     738           1 :     if (pos != absl::string_view::npos) {
     739           1 :       ret.remove_suffix(ret.length() - pos);
     740           1 :     }
     741           1 :   }
     742         674 :   return ret;
     743         674 : }
     744             : 
     745         674 : bool RouteEntryImplBase::evaluateTlsContextMatch(const StreamInfo::StreamInfo& stream_info) const {
     746         674 :   bool matches = true;
     747             : 
     748         674 :   if (!tlsContextMatchCriteria()) {
     749         674 :     return matches;
     750         674 :   }
     751             : 
     752           0 :   const TlsContextMatchCriteria& criteria = *tlsContextMatchCriteria();
     753             : 
     754           0 :   if (criteria.presented().has_value()) {
     755           0 :     const bool peer_presented =
     756           0 :         stream_info.downstreamAddressProvider().sslConnection() &&
     757           0 :         stream_info.downstreamAddressProvider().sslConnection()->peerCertificatePresented();
     758           0 :     matches &= criteria.presented().value() == peer_presented;
     759           0 :   }
     760             : 
     761           0 :   if (criteria.validated().has_value()) {
     762           0 :     const bool peer_validated =
     763           0 :         stream_info.downstreamAddressProvider().sslConnection() &&
     764           0 :         stream_info.downstreamAddressProvider().sslConnection()->peerCertificateValidated();
     765           0 :     matches &= criteria.validated().value() == peer_validated;
     766           0 :   }
     767             : 
     768           0 :   return matches;
     769         674 : }
     770             : 
     771             : bool RouteEntryImplBase::matchRoute(const Http::RequestHeaderMap& headers,
     772             :                                     const StreamInfo::StreamInfo& stream_info,
     773         674 :                                     uint64_t random_value) const {
     774         674 :   bool matches = true;
     775             : 
     776         674 :   matches &= evaluateRuntimeMatch(random_value);
     777         674 :   if (!matches) {
     778             :     // No need to waste further cycles calculating a route match.
     779           0 :     return false;
     780           0 :   }
     781             : 
     782         674 :   if (match_grpc_) {
     783           0 :     matches &= Grpc::Common::isGrpcRequestHeaders(headers);
     784           0 :     if (!matches) {
     785           0 :       return false;
     786           0 :     }
     787           0 :   }
     788             : 
     789         674 :   matches &= Http::HeaderUtility::matchHeaders(headers, config_headers_);
     790         674 :   if (!matches) {
     791           0 :     return false;
     792           0 :   }
     793         674 :   if (!config_query_parameters_.empty()) {
     794           0 :     auto query_parameters =
     795           0 :         Http::Utility::QueryParamsMulti::parseQueryString(headers.getPathValue());
     796           0 :     matches &= ConfigUtility::matchQueryParams(query_parameters, config_query_parameters_);
     797           0 :     if (!matches) {
     798           0 :       return false;
     799           0 :     }
     800           0 :   }
     801             : 
     802         674 :   matches &= evaluateTlsContextMatch(stream_info);
     803             : 
     804         674 :   for (const auto& m : dynamic_metadata_) {
     805           0 :     if (!matches) {
     806             :       // No need to check anymore as all dynamic metadata matchers must match for a match to occur.
     807           0 :       break;
     808           0 :     }
     809           0 :     matches &= m.match(stream_info.dynamicMetadata());
     810           0 :   }
     811             : 
     812         674 :   return matches;
     813         674 : }
     814             : 
     815         368 : const std::string& RouteEntryImplBase::clusterName() const { return cluster_name_; }
     816             : 
     817             : void RouteEntryImplBase::finalizeRequestHeaders(Http::RequestHeaderMap& headers,
     818             :                                                 const StreamInfo::StreamInfo& stream_info,
     819         196 :                                                 bool insert_envoy_original_path) const {
     820         196 :   for (const HeaderParser* header_parser : getRequestHeaderParsers(
     821         588 :            /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
     822             :     // Later evaluated header parser wins.
     823         588 :     header_parser->evaluateHeaders(headers, stream_info);
     824         588 :   }
     825             : 
     826             :   // Restore the port if this was a CONNECT request.
     827             :   // Note this will restore the port for HTTP/2 CONNECT-upgrades as well as as HTTP/1.1 style
     828             :   // CONNECT requests.
     829         196 :   if (Http::HeaderUtility::getPortStart(headers.getHostValue()) == absl::string_view::npos) {
     830         196 :     if (auto typed_state = stream_info.filterState().getDataReadOnly<OriginalConnectPort>(
     831         196 :             OriginalConnectPort::key());
     832         196 :         typed_state != nullptr) {
     833           0 :       headers.setHost(absl::StrCat(headers.getHostValue(), ":", typed_state->value()));
     834           0 :     }
     835         196 :   }
     836             : 
     837         196 :   if (!host_rewrite_.empty()) {
     838           0 :     Http::Utility::updateAuthority(headers, host_rewrite_, append_xfh_);
     839         196 :   } else if (auto_host_rewrite_header_) {
     840           0 :     const auto header = headers.get(*auto_host_rewrite_header_);
     841           0 :     if (!header.empty()) {
     842             :       // This is an implicitly untrusted header, so per the API documentation only the first
     843             :       // value is used.
     844           0 :       const absl::string_view header_value = header[0]->value().getStringView();
     845           0 :       if (!header_value.empty()) {
     846           0 :         Http::Utility::updateAuthority(headers, header_value, append_xfh_);
     847           0 :       }
     848           0 :     }
     849         196 :   } else if (host_rewrite_path_regex_ != nullptr) {
     850           0 :     const std::string path(headers.getPathValue());
     851           0 :     absl::string_view just_path(Http::PathUtil::removeQueryAndFragment(path));
     852           0 :     Http::Utility::updateAuthority(
     853           0 :         headers,
     854           0 :         host_rewrite_path_regex_->replaceAll(just_path, host_rewrite_path_regex_substitution_),
     855           0 :         append_xfh_);
     856           0 :   }
     857             : 
     858             :   // Handle path rewrite
     859         196 :   absl::optional<std::string> container;
     860         196 :   if (!getPathRewrite(headers, container).empty() || regex_rewrite_ != nullptr ||
     861         196 :       path_rewriter_ != nullptr) {
     862           2 :     rewritePathHeader(headers, insert_envoy_original_path);
     863           2 :   }
     864         196 : }
     865             : 
     866             : void RouteEntryImplBase::finalizeResponseHeaders(Http::ResponseHeaderMap& headers,
     867         335 :                                                  const StreamInfo::StreamInfo& stream_info) const {
     868         335 :   for (const HeaderParser* header_parser : getResponseHeaderParsers(
     869        1005 :            /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
     870             :     // Later evaluated header parser wins.
     871        1005 :     header_parser->evaluateHeaders(headers, {stream_info.getRequestHeaders(), &headers},
     872        1005 :                                    stream_info);
     873        1005 :   }
     874         335 : }
     875             : 
     876             : Http::HeaderTransforms
     877             : RouteEntryImplBase::responseHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
     878           0 :                                              bool do_formatting) const {
     879           0 :   Http::HeaderTransforms transforms;
     880           0 :   for (const HeaderParser* header_parser : getResponseHeaderParsers(
     881           0 :            /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
     882             :     // Later evaluated header parser wins.
     883           0 :     mergeTransforms(transforms, header_parser->getHeaderTransforms(stream_info, do_formatting));
     884           0 :   }
     885           0 :   return transforms;
     886           0 : }
     887             : 
     888             : Http::HeaderTransforms
     889             : RouteEntryImplBase::requestHeaderTransforms(const StreamInfo::StreamInfo& stream_info,
     890           0 :                                             bool do_formatting) const {
     891           0 :   Http::HeaderTransforms transforms;
     892           0 :   for (const HeaderParser* header_parser : getRequestHeaderParsers(
     893           0 :            /*specificity_ascend=*/vhost_->globalRouteConfig().mostSpecificHeaderMutationsWins())) {
     894             :     // Later evaluated header parser wins.
     895           0 :     mergeTransforms(transforms, header_parser->getHeaderTransforms(stream_info, do_formatting));
     896           0 :   }
     897           0 :   return transforms;
     898           0 : }
     899             : 
     900             : absl::InlinedVector<const HeaderParser*, 3>
     901         196 : RouteEntryImplBase::getRequestHeaderParsers(bool specificity_ascend) const {
     902         196 :   return getHeaderParsers(&vhost_->globalRouteConfig().requestHeaderParser(),
     903         196 :                           &vhost_->requestHeaderParser(), &requestHeaderParser(),
     904         196 :                           specificity_ascend);
     905         196 : }
     906             : 
     907             : absl::InlinedVector<const HeaderParser*, 3>
     908         335 : RouteEntryImplBase::getResponseHeaderParsers(bool specificity_ascend) const {
     909         335 :   return getHeaderParsers(&vhost_->globalRouteConfig().responseHeaderParser(),
     910         335 :                           &vhost_->responseHeaderParser(), &responseHeaderParser(),
     911         335 :                           specificity_ascend);
     912         335 : }
     913             : 
     914             : std::unique_ptr<const RouteEntryImplBase::RuntimeData>
     915        1684 : RouteEntryImplBase::loadRuntimeData(const envoy::config::route::v3::RouteMatch& route_match) {
     916        1684 :   if (route_match.has_runtime_fraction()) {
     917          16 :     auto runtime_data = std::make_unique<RouteEntryImplBase::RuntimeData>();
     918          16 :     runtime_data->fractional_runtime_default_ = route_match.runtime_fraction().default_value();
     919          16 :     runtime_data->fractional_runtime_key_ = route_match.runtime_fraction().runtime_key();
     920          16 :     return runtime_data;
     921          16 :   }
     922        1668 :   return nullptr;
     923        1684 : }
     924             : 
     925             : const std::string&
     926             : RouteEntryImplBase::getPathRewrite(const Http::RequestHeaderMap& headers,
     927         400 :                                    absl::optional<std::string>& container) const {
     928             :   // Just use the prefix rewrite if this isn't a redirect.
     929         400 :   if (!isRedirect()) {
     930         400 :     return prefix_rewrite_;
     931         400 :   }
     932             : 
     933             :   // Return the regex rewrite substitution for redirects, if set.
     934             :   // redirect_config_ is known to not be nullptr here, because of the isRedirect check above.
     935           0 :   ASSERT(redirect_config_ != nullptr);
     936           0 :   if (redirect_config_->regex_rewrite_redirect_ != nullptr) {
     937             :     // Copy just the path and rewrite it using the regex.
     938             :     //
     939             :     // Store the result in the output container, and return a reference to the underlying string.
     940           0 :     auto just_path(Http::PathUtil::removeQueryAndFragment(headers.getPathValue()));
     941           0 :     container = redirect_config_->regex_rewrite_redirect_->replaceAll(
     942           0 :         just_path, redirect_config_->regex_rewrite_redirect_substitution_);
     943             : 
     944           0 :     return container.value();
     945           0 :   }
     946             : 
     947             :   // Otherwise, return the prefix rewrite used for redirects.
     948           0 :   return redirect_config_->prefix_rewrite_redirect_;
     949           0 : }
     950             : 
     951             : void RouteEntryImplBase::finalizePathHeader(Http::RequestHeaderMap& headers,
     952             :                                             absl::string_view matched_path,
     953         204 :                                             bool insert_envoy_original_path) const {
     954         204 :   absl::optional<std::string> new_path =
     955         204 :       currentUrlPathAfterRewriteWithMatchedPath(headers, matched_path);
     956         204 :   if (!new_path.has_value()) {
     957             :     // There are no rewrites configured. Just return.
     958         202 :     return;
     959         202 :   }
     960             : 
     961           2 :   if (insert_envoy_original_path) {
     962           2 :     headers.setEnvoyOriginalPath(headers.getPathValue());
     963           2 :   }
     964             : 
     965           2 :   headers.setPath(new_path.value());
     966           2 : }
     967             : 
     968             : // currentUrlPathAfterRewriteWithMatchedPath does the "standard" path rewriting, meaning that it
     969             : // handles the "prefix_rewrite" and "regex_rewrite" route actions, only one of
     970             : // which can be specified. The "matched_path" argument applies only to the
     971             : // prefix rewriting, and describes the portion of the path (excluding query
     972             : // parameters) that should be replaced by the rewrite. A "regex_rewrite"
     973             : // applies to the entire path (excluding query parameters), regardless of what
     974             : // portion was matched.
     975             : absl::optional<std::string> RouteEntryImplBase::currentUrlPathAfterRewriteWithMatchedPath(
     976         204 :     const Http::RequestHeaderMap& headers, absl::string_view matched_path) const {
     977         204 :   absl::optional<std::string> container;
     978         204 :   const auto& rewrite = getPathRewrite(headers, container);
     979         204 :   if (rewrite.empty() && regex_rewrite_ == nullptr && path_rewriter_ == nullptr) {
     980             :     // There are no rewrites configured.
     981         202 :     return {};
     982         202 :   }
     983             : 
     984             :   // TODO(perf): can we avoid the string copy for the common case?
     985           2 :   std::string path(headers.getPathValue());
     986           2 :   if (!rewrite.empty()) {
     987           2 :     if (redirect_config_ != nullptr && redirect_config_->regex_rewrite_redirect_ != nullptr) {
     988             :       // As the rewrite constant may contain the result of a regex rewrite for a redirect, we must
     989             :       // replace the full path if this is the case. This is because the matched path does not need
     990             :       // to correspond to the full path, e.g. in the case of prefix matches.
     991           0 :       auto just_path(Http::PathUtil::removeQueryAndFragment(path));
     992           0 :       return path.replace(0, just_path.size(), rewrite);
     993           0 :     }
     994           2 :     ASSERT(case_sensitive() ? absl::StartsWith(path, matched_path)
     995           2 :                             : absl::StartsWithIgnoreCase(path, matched_path));
     996           2 :     return path.replace(0, matched_path.size(), rewrite);
     997           2 :   }
     998             : 
     999           0 :   if (regex_rewrite_ != nullptr) {
    1000             :     // Replace the entire path, but preserve the query parameters
    1001           0 :     auto just_path(Http::PathUtil::removeQueryAndFragment(path));
    1002           0 :     return path.replace(0, just_path.size(),
    1003           0 :                         regex_rewrite_->replaceAll(just_path, regex_rewrite_substitution_));
    1004           0 :   }
    1005             : 
    1006           0 :   if (path_rewriter_ != nullptr) {
    1007           0 :     absl::string_view just_path(Http::PathUtil::removeQueryAndFragment(headers.getPathValue()));
    1008             : 
    1009           0 :     absl::StatusOr<std::string> new_path = path_rewriter_->rewritePath(just_path, matched_path);
    1010             : 
    1011             :     // if rewrite fails return old path.
    1012           0 :     if (!new_path.ok()) {
    1013           0 :       return std::string(headers.getPathValue());
    1014           0 :     }
    1015           0 :     return path.replace(0, just_path.size(), new_path.value());
    1016           0 :   }
    1017             : 
    1018             :   // There are no rewrites configured.
    1019           0 :   return {};
    1020           0 : }
    1021             : 
    1022         202 : std::string RouteEntryImplBase::newUri(const Http::RequestHeaderMap& headers) const {
    1023         202 :   ASSERT(isDirectResponse());
    1024         202 :   return ::Envoy::Http::Utility::newUri(
    1025         202 :       ::Envoy::makeOptRefFromPtr(
    1026         202 :           const_cast<const ::Envoy::Http::Utility::RedirectConfig*>(redirect_config_.get())),
    1027         202 :       headers);
    1028         202 : }
    1029             : 
    1030             : std::multimap<std::string, std::string>
    1031        1655 : RouteEntryImplBase::parseOpaqueConfig(const envoy::config::route::v3::Route& route) {
    1032        1655 :   std::multimap<std::string, std::string> ret;
    1033        1655 :   if (route.has_metadata()) {
    1034          42 :     auto filter_metadata = route.metadata().filter_metadata().find("envoy.filters.http.router");
    1035          42 :     if (filter_metadata == route.metadata().filter_metadata().end()) {
    1036          40 :       return ret;
    1037          40 :     }
    1038           4 :     for (const auto& it : filter_metadata->second.fields()) {
    1039           4 :       if (it.second.kind_case() == ProtobufWkt::Value::kStringValue) {
    1040           4 :         ret.emplace(it.first, it.second.string_value());
    1041           4 :       }
    1042           4 :     }
    1043           2 :   }
    1044        1615 :   return ret;
    1045        1655 : }
    1046             : 
    1047             : std::unique_ptr<HedgePolicyImpl> RouteEntryImplBase::buildHedgePolicy(
    1048             :     HedgePolicyConstOptRef vhost_hedge_policy,
    1049        1684 :     const envoy::config::route::v3::RouteAction& route_config) const {
    1050             :   // Route specific policy wins, if available.
    1051        1684 :   if (route_config.has_hedge_policy()) {
    1052          36 :     return std::make_unique<HedgePolicyImpl>(route_config.hedge_policy());
    1053          36 :   }
    1054             : 
    1055             :   // If not, we fall back to the virtual host policy if there is one.
    1056        1648 :   if (vhost_hedge_policy.has_value()) {
    1057           0 :     return std::make_unique<HedgePolicyImpl>(*vhost_hedge_policy);
    1058           0 :   }
    1059             : 
    1060             :   // Otherwise, an empty policy will do.
    1061        1648 :   return nullptr;
    1062        1648 : }
    1063             : 
    1064             : std::unique_ptr<RetryPolicyImpl> RouteEntryImplBase::buildRetryPolicy(
    1065             :     RetryPolicyConstOptRef vhost_retry_policy,
    1066             :     const envoy::config::route::v3::RouteAction& route_config,
    1067             :     ProtobufMessage::ValidationVisitor& validation_visitor,
    1068        1684 :     Server::Configuration::ServerFactoryContext& factory_context) const {
    1069        1684 :   Upstream::RetryExtensionFactoryContextImpl retry_factory_context(
    1070        1684 :       factory_context.singletonManager());
    1071             :   // Route specific policy wins, if available.
    1072        1684 :   if (route_config.has_retry_policy()) {
    1073          88 :     return std::make_unique<RetryPolicyImpl>(route_config.retry_policy(), validation_visitor,
    1074          88 :                                              retry_factory_context);
    1075          88 :   }
    1076             : 
    1077             :   // If not, we fallback to the virtual host policy if there is one.
    1078        1596 :   if (vhost_retry_policy.has_value()) {
    1079           0 :     return std::make_unique<RetryPolicyImpl>(*vhost_retry_policy, validation_visitor,
    1080           0 :                                              retry_factory_context);
    1081           0 :   }
    1082             : 
    1083             :   // Otherwise, an empty policy will do.
    1084        1596 :   return nullptr;
    1085        1596 : }
    1086             : 
    1087             : std::unique_ptr<InternalRedirectPolicyImpl> RouteEntryImplBase::buildInternalRedirectPolicy(
    1088             :     const envoy::config::route::v3::RouteAction& route_config,
    1089        1656 :     ProtobufMessage::ValidationVisitor& validator, absl::string_view current_route_name) const {
    1090        1656 :   if (route_config.has_internal_redirect_policy()) {
    1091           9 :     return std::make_unique<InternalRedirectPolicyImpl>(route_config.internal_redirect_policy(),
    1092           9 :                                                         validator, current_route_name);
    1093           9 :   }
    1094        1647 :   envoy::config::route::v3::InternalRedirectPolicy policy_config;
    1095        1647 :   switch (route_config.internal_redirect_action()) {
    1096           0 :   case envoy::config::route::v3::RouteAction::HANDLE_INTERNAL_REDIRECT:
    1097           0 :     break;
    1098        1647 :   case envoy::config::route::v3::RouteAction::PASS_THROUGH_INTERNAL_REDIRECT:
    1099        1647 :     FALLTHRU;
    1100        1647 :   default:
    1101        1647 :     return nullptr;
    1102        1647 :   }
    1103           0 :   if (route_config.has_max_internal_redirects()) {
    1104           0 :     *policy_config.mutable_max_internal_redirects() = route_config.max_internal_redirects();
    1105           0 :   }
    1106           0 :   return std::make_unique<InternalRedirectPolicyImpl>(policy_config, validator, current_route_name);
    1107        1647 : }
    1108             : 
    1109             : RouteEntryImplBase::OptionalTimeouts RouteEntryImplBase::buildOptionalTimeouts(
    1110        1684 :     const envoy::config::route::v3::RouteAction& route) const {
    1111             :   // Calculate how many values are actually set, to initialize `OptionalTimeouts` packed_struct,
    1112             :   // avoiding memory re-allocation on each set() call.
    1113        1684 :   int num_timeouts_set = route.has_idle_timeout() ? 1 : 0;
    1114        1684 :   num_timeouts_set += route.has_max_grpc_timeout() ? 1 : 0;
    1115        1684 :   num_timeouts_set += route.has_grpc_timeout_offset() ? 1 : 0;
    1116        1684 :   if (route.has_max_stream_duration()) {
    1117           1 :     num_timeouts_set += route.max_stream_duration().has_max_stream_duration() ? 1 : 0;
    1118           1 :     num_timeouts_set += route.max_stream_duration().has_grpc_timeout_header_max() ? 1 : 0;
    1119           1 :     num_timeouts_set += route.max_stream_duration().has_grpc_timeout_header_offset() ? 1 : 0;
    1120           1 :   }
    1121        1684 :   OptionalTimeouts timeouts(num_timeouts_set);
    1122        1684 :   if (route.has_idle_timeout()) {
    1123           4 :     timeouts.set<OptionalTimeoutNames::IdleTimeout>(
    1124           4 :         std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(route, idle_timeout)));
    1125           4 :   }
    1126        1684 :   if (route.has_max_grpc_timeout()) {
    1127           8 :     timeouts.set<OptionalTimeoutNames::MaxGrpcTimeout>(
    1128           8 :         std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(route, max_grpc_timeout)));
    1129           8 :   }
    1130        1684 :   if (route.has_grpc_timeout_offset()) {
    1131           8 :     timeouts.set<OptionalTimeoutNames::GrpcTimeoutOffset>(
    1132           8 :         std::chrono::milliseconds(PROTOBUF_GET_MS_REQUIRED(route, grpc_timeout_offset)));
    1133           8 :   }
    1134        1684 :   if (route.has_max_stream_duration()) {
    1135           1 :     if (route.max_stream_duration().has_max_stream_duration()) {
    1136           0 :       timeouts.set<OptionalTimeoutNames::MaxStreamDuration>(std::chrono::milliseconds(
    1137           0 :           PROTOBUF_GET_MS_REQUIRED(route.max_stream_duration(), max_stream_duration)));
    1138           0 :     }
    1139           1 :     if (route.max_stream_duration().has_grpc_timeout_header_max()) {
    1140           0 :       timeouts.set<OptionalTimeoutNames::GrpcTimeoutHeaderMax>(std::chrono::milliseconds(
    1141           0 :           PROTOBUF_GET_MS_REQUIRED(route.max_stream_duration(), grpc_timeout_header_max)));
    1142           0 :     }
    1143           1 :     if (route.max_stream_duration().has_grpc_timeout_header_offset()) {
    1144           0 :       timeouts.set<OptionalTimeoutNames::GrpcTimeoutHeaderOffset>(std::chrono::milliseconds(
    1145           0 :           PROTOBUF_GET_MS_REQUIRED(route.max_stream_duration(), grpc_timeout_header_offset)));
    1146           0 :     }
    1147           1 :   }
    1148        1684 :   return timeouts;
    1149        1684 : }
    1150             : 
    1151             : PathRewriterSharedPtr
    1152             : RouteEntryImplBase::buildPathRewriter(envoy::config::route::v3::Route route,
    1153        1684 :                                       ProtobufMessage::ValidationVisitor& validator) const {
    1154        1684 :   if (!route.route().has_path_rewrite_policy()) {
    1155        1640 :     return nullptr;
    1156        1640 :   }
    1157             : 
    1158          44 :   auto& factory = Envoy::Config::Utility::getAndCheckFactory<PathRewriterFactory>(
    1159          44 :       route.route().path_rewrite_policy());
    1160             : 
    1161          44 :   ProtobufTypes::MessagePtr config = Envoy::Config::Utility::translateAnyToFactoryConfig(
    1162          44 :       route.route().path_rewrite_policy().typed_config(), validator, factory);
    1163             : 
    1164          44 :   absl::StatusOr<PathRewriterSharedPtr> rewriter = factory.createPathRewriter(*config);
    1165             : 
    1166          44 :   if (!rewriter.ok()) {
    1167           0 :     throwEnvoyExceptionOrPanic(std::string(rewriter.status().message()));
    1168           0 :   }
    1169             : 
    1170          44 :   return rewriter.value();
    1171          44 : }
    1172             : 
    1173             : PathMatcherSharedPtr
    1174             : RouteEntryImplBase::buildPathMatcher(envoy::config::route::v3::Route route,
    1175        1684 :                                      ProtobufMessage::ValidationVisitor& validator) const {
    1176        1684 :   if (!route.match().has_path_match_policy()) {
    1177        1624 :     return nullptr;
    1178        1624 :   }
    1179          60 :   auto& factory = Envoy::Config::Utility::getAndCheckFactory<PathMatcherFactory>(
    1180          60 :       route.match().path_match_policy());
    1181             : 
    1182          60 :   ProtobufTypes::MessagePtr config = Envoy::Config::Utility::translateAnyToFactoryConfig(
    1183          60 :       route.match().path_match_policy().typed_config(), validator, factory);
    1184             : 
    1185          60 :   absl::StatusOr<PathMatcherSharedPtr> matcher = factory.createPathMatcher(*config);
    1186             : 
    1187          60 :   if (!matcher.ok()) {
    1188           0 :     throwEnvoyExceptionOrPanic(std::string(matcher.status().message()));
    1189           0 :   }
    1190             : 
    1191          60 :   return matcher.value();
    1192          60 : }
    1193             : 
    1194        1655 : DecoratorConstPtr RouteEntryImplBase::parseDecorator(const envoy::config::route::v3::Route& route) {
    1195        1655 :   DecoratorConstPtr ret;
    1196        1655 :   if (route.has_decorator()) {
    1197          44 :     ret = DecoratorConstPtr(new DecoratorImpl(route.decorator()));
    1198          44 :   }
    1199        1655 :   return ret;
    1200        1655 : }
    1201             : 
    1202             : RouteTracingConstPtr
    1203        1655 : RouteEntryImplBase::parseRouteTracing(const envoy::config::route::v3::Route& route) {
    1204        1655 :   RouteTracingConstPtr ret;
    1205        1655 :   if (route.has_tracing()) {
    1206           6 :     ret = RouteTracingConstPtr(new RouteTracingImpl(route.tracing()));
    1207           6 :   }
    1208        1655 :   return ret;
    1209        1655 : }
    1210             : 
    1211         385 : const DirectResponseEntry* RouteEntryImplBase::directResponseEntry() const {
    1212             :   // A route for a request can exclusively be a route entry, a direct response entry,
    1213             :   // or a redirect entry.
    1214         385 :   if (isDirectResponse()) {
    1215         202 :     return this;
    1216         385 :   } else {
    1217         183 :     return nullptr;
    1218         183 :   }
    1219         385 : }
    1220             : 
    1221        2242 : const RouteEntry* RouteEntryImplBase::routeEntry() const {
    1222             :   // A route for a request can exclusively be a route entry, a direct response entry,
    1223             :   // or a redirect entry.
    1224        2242 :   if (isDirectResponse()) {
    1225         876 :     return nullptr;
    1226        2238 :   } else {
    1227        1366 :     return this;
    1228        1366 :   }
    1229        2242 : }
    1230             : 
    1231             : RouteConstSharedPtr RouteEntryImplBase::pickClusterViaClusterHeader(
    1232             :     const Http::LowerCaseString& cluster_header_name, const Http::HeaderMap& headers,
    1233          11 :     const RouteEntryAndRoute* route_selector_override) const {
    1234          11 :   const auto entry = headers.get(cluster_header_name);
    1235          11 :   std::string final_cluster_name;
    1236          11 :   if (!entry.empty()) {
    1237             :     // This is an implicitly untrusted header, so per the API documentation only
    1238             :     // the first value is used.
    1239           6 :     final_cluster_name = std::string(entry[0]->value().getStringView());
    1240           6 :   }
    1241             : 
    1242          11 :   return std::make_shared<DynamicRouteEntry>(route_selector_override
    1243          11 :                                                  ? route_selector_override
    1244          11 :                                                  : static_cast<const RouteEntryAndRoute*>(this),
    1245          11 :                                              shared_from_this(), final_cluster_name);
    1246          11 : }
    1247             : 
    1248             : RouteConstSharedPtr RouteEntryImplBase::clusterEntry(const Http::RequestHeaderMap& headers,
    1249         468 :                                                      uint64_t random_value) const {
    1250             :   // Gets the route object chosen from the list of weighted clusters
    1251             :   // (if there is one) or returns self.
    1252         468 :   if (weighted_clusters_config_ == nullptr) {
    1253         466 :     if (!cluster_name_.empty() || isDirectResponse()) {
    1254         457 :       return shared_from_this();
    1255         457 :     } else if (!cluster_header_name_.get().empty()) {
    1256           9 :       return pickClusterViaClusterHeader(cluster_header_name_, headers,
    1257           9 :                                          /*route_selector_override=*/nullptr);
    1258           9 :     } else {
    1259             :       // TODO(wbpcode): make the cluster header or weighted clusters an implementation of the
    1260             :       // cluster specifier plugin.
    1261           0 :       ASSERT(cluster_specifier_plugin_ != nullptr);
    1262           0 :       return cluster_specifier_plugin_->route(shared_from_this(), headers);
    1263           0 :     }
    1264         466 :   }
    1265           2 :   return pickWeightedCluster(headers, random_value, true);
    1266         468 : }
    1267             : 
    1268             : RouteConstSharedPtr RouteEntryImplBase::pickWeightedCluster(const Http::HeaderMap& headers,
    1269             :                                                             const uint64_t random_value,
    1270           2 :                                                             const bool ignore_overflow) const {
    1271           2 :   absl::optional<uint64_t> random_value_from_header;
    1272             :   // Retrieve the random value from the header if corresponding header name is specified.
    1273             :   // weighted_clusters_config_ is known not to be nullptr here. If it were, pickWeightedCluster
    1274             :   // would not be called.
    1275           2 :   ASSERT(weighted_clusters_config_ != nullptr);
    1276           2 :   if (!weighted_clusters_config_->random_value_header_name_.empty()) {
    1277           0 :     const auto header_value = headers.get(
    1278           0 :         Envoy::Http::LowerCaseString(weighted_clusters_config_->random_value_header_name_));
    1279           0 :     if (!header_value.empty() && header_value.size() == 1) {
    1280             :       // We expect single-valued header here, otherwise it will potentially cause inconsistent
    1281             :       // weighted cluster picking throughout the process because different values are used to
    1282             :       // compute the selected value. So, we treat multi-valued header as invalid input and fall back
    1283             :       // to use internally generated random number.
    1284           0 :       uint64_t random_value = 0;
    1285           0 :       if (absl::SimpleAtoi(header_value[0]->value().getStringView(), &random_value)) {
    1286           0 :         random_value_from_header = random_value;
    1287           0 :       }
    1288           0 :     }
    1289             : 
    1290           0 :     if (!random_value_from_header.has_value()) {
    1291             :       // Random value should be found here. But if it is not set due to some errors, log the
    1292             :       // information and fallback to the random value that is set by stream id.
    1293           0 :       ENVOY_LOG(debug, "The random value can not be found from the header and it will fall back to "
    1294           0 :                        "the value that is set by stream id");
    1295           0 :     }
    1296           0 :   }
    1297             : 
    1298           2 :   const uint64_t selected_value =
    1299           2 :       (random_value_from_header.has_value() ? random_value_from_header.value() : random_value) %
    1300           2 :       weighted_clusters_config_->total_cluster_weight_;
    1301           2 :   uint64_t begin = 0;
    1302           2 :   uint64_t end = 0;
    1303             : 
    1304             :   // Find the right cluster to route to based on the interval in which
    1305             :   // the selected value falls. The intervals are determined as
    1306             :   // [0, cluster1_weight), [cluster1_weight, cluster1_weight+cluster2_weight),..
    1307           2 :   for (const WeightedClusterEntrySharedPtr& cluster :
    1308           2 :        weighted_clusters_config_->weighted_clusters_) {
    1309           2 :     end = begin + cluster->clusterWeight();
    1310           2 :     if (!ignore_overflow) {
    1311             :       // end > total_cluster_weight: This case can only occur with Runtimes,
    1312             :       // when the user specifies invalid weights such that
    1313             :       // sum(weights) > total_cluster_weight.
    1314           0 :       ASSERT(end <= weighted_clusters_config_->total_cluster_weight_);
    1315           0 :     }
    1316             : 
    1317           2 :     if (selected_value >= begin && selected_value < end) {
    1318           2 :       if (!cluster->clusterHeaderName().get().empty() &&
    1319           2 :           !headers.get(cluster->clusterHeaderName()).empty()) {
    1320           2 :         return pickClusterViaClusterHeader(cluster->clusterHeaderName(), headers,
    1321           2 :                                            static_cast<RouteEntryAndRoute*>(cluster.get()));
    1322           2 :       }
    1323             :       // The WeightedClusterEntry does not contain reference to the RouteEntryImplBase to
    1324             :       // avoid circular reference. To ensure that the RouteEntryImplBase is not destructed
    1325             :       // before the WeightedClusterEntry, additional wrapper is used to hold the reference
    1326             :       // to the RouteEntryImplBase.
    1327           0 :       return std::make_shared<DynamicRouteEntry>(cluster.get(), shared_from_this(),
    1328           0 :                                                  cluster->clusterName());
    1329           2 :     }
    1330           0 :     begin = end;
    1331           0 :   }
    1332             : 
    1333           0 :   PANIC("unexpected");
    1334           0 : }
    1335             : 
    1336             : void RouteEntryImplBase::validateClusters(
    1337        1605 :     const Upstream::ClusterManager::ClusterInfoMaps& cluster_info_maps) const {
    1338        1605 :   if (isDirectResponse()) {
    1339         519 :     return;
    1340         519 :   }
    1341             : 
    1342             :   // Currently, we verify that the cluster exists in the CM if we have an explicit cluster or
    1343             :   // weighted cluster rule. We obviously do not verify a cluster_header rule. This means that
    1344             :   // trying to use all CDS clusters with a static route table will not work. In the upcoming RDS
    1345             :   // change we will make it so that dynamically loaded route tables do *not* perform CM checks.
    1346             :   // In the future we might decide to also have a config option that turns off checks for static
    1347             :   // route tables. This would enable the all CDS with static route table case.
    1348        1086 :   if (!cluster_name_.empty()) {
    1349         985 :     if (!cluster_info_maps.hasCluster(cluster_name_)) {
    1350         948 :       throwEnvoyExceptionOrPanic(fmt::format("route: unknown cluster '{}'", cluster_name_));
    1351         948 :     }
    1352         985 :   } else if (weighted_clusters_config_ != nullptr) {
    1353          86 :     for (const WeightedClusterEntrySharedPtr& cluster :
    1354          94 :          weighted_clusters_config_->weighted_clusters_) {
    1355          94 :       if (!cluster->clusterName().empty()) {
    1356          84 :         if (!cluster_info_maps.hasCluster(cluster->clusterName())) {
    1357          84 :           throwEnvoyExceptionOrPanic(
    1358          84 :               fmt::format("route: unknown weighted cluster '{}'", cluster->clusterName()));
    1359          84 :         }
    1360          84 :       }
    1361             :       // For weighted clusters with `cluster_header_name`, we only verify that this field is
    1362             :       // not empty because the cluster name is not set yet at config time (hence the validation
    1363             :       // here).
    1364          10 :       else if (cluster->clusterHeaderName().get().empty()) {
    1365           0 :         throwEnvoyExceptionOrPanic("route: unknown weighted cluster with no cluster_header field");
    1366           0 :       }
    1367          94 :     }
    1368          86 :   }
    1369        1086 : }
    1370             : 
    1371         427 : absl::optional<bool> RouteEntryImplBase::filterDisabled(absl::string_view config_name) const {
    1372         427 :   absl::optional<bool> result = per_filter_configs_.disabled(config_name);
    1373         427 :   if (result.has_value()) {
    1374           0 :     return result.value();
    1375           0 :   }
    1376         427 :   return vhost_->filterDisabled(config_name);
    1377         427 : }
    1378             : 
    1379             : void RouteEntryImplBase::traversePerFilterConfig(
    1380             :     const std::string& filter_name,
    1381          40 :     std::function<void(const Router::RouteSpecificFilterConfig&)> cb) const {
    1382          40 :   vhost_->traversePerFilterConfig(filter_name, cb);
    1383             : 
    1384          40 :   auto maybe_route_config = per_filter_configs_.get(filter_name);
    1385          40 :   if (maybe_route_config != nullptr) {
    1386           0 :     cb(*maybe_route_config);
    1387           0 :   }
    1388          40 : }
    1389             : 
    1390           0 : const envoy::config::core::v3::Metadata& RouteEntryImplBase::metadata() const {
    1391           0 :   return metadata_ != nullptr ? metadata_->proto_metadata_
    1392           0 :                               : DefaultRouteMetadataPack::get().proto_metadata_;
    1393           0 : }
    1394           0 : const Envoy::Config::TypedMetadata& RouteEntryImplBase::typedMetadata() const {
    1395           0 :   return metadata_ != nullptr ? metadata_->typed_metadata_
    1396           0 :                               : DefaultRouteMetadataPack::get().typed_metadata_;
    1397           0 : }
    1398             : 
    1399             : RouteEntryImplBase::WeightedClusterEntry::WeightedClusterEntry(
    1400             :     const RouteEntryImplBase* parent, const std::string& runtime_key,
    1401             :     Server::Configuration::ServerFactoryContext& factory_context,
    1402             :     ProtobufMessage::ValidationVisitor& validator,
    1403             :     const envoy::config::route::v3::WeightedCluster::ClusterWeight& cluster)
    1404             :     : DynamicRouteEntry(parent, nullptr, validateWeightedClusterSpecifier(cluster).name()),
    1405             :       runtime_key_(runtime_key), loader_(factory_context.runtime()),
    1406             :       cluster_weight_(PROTOBUF_GET_WRAPPED_REQUIRED(cluster, weight)),
    1407             :       per_filter_configs_(cluster.typed_per_filter_config(), factory_context, validator),
    1408             :       host_rewrite_(cluster.host_rewrite_literal()),
    1409         198 :       cluster_header_name_(cluster.cluster_header()) {
    1410         198 :   if (!cluster.request_headers_to_add().empty() || !cluster.request_headers_to_remove().empty()) {
    1411          16 :     request_headers_parser_ = HeaderParser::configure(cluster.request_headers_to_add(),
    1412          16 :                                                       cluster.request_headers_to_remove());
    1413          16 :   }
    1414         198 :   if (!cluster.response_headers_to_add().empty() || !cluster.response_headers_to_remove().empty()) {
    1415          16 :     response_headers_parser_ = HeaderParser::configure(cluster.response_headers_to_add(),
    1416          16 :                                                        cluster.response_headers_to_remove());
    1417          16 :   }
    1418             : 
    1419         198 :   if (cluster.has_metadata_match()) {
    1420          16 :     const auto filter_it = cluster.metadata_match().filter_metadata().find(
    1421          16 :         Envoy::Config::MetadataFilters::get().ENVOY_LB);
    1422          16 :     if (filter_it != cluster.metadata_match().filter_metadata().end()) {
    1423          16 :       if (parent->metadata_match_criteria_) {
    1424          16 :         cluster_metadata_match_criteria_ =
    1425          16 :             parent->metadata_match_criteria_->mergeMatchCriteria(filter_it->second);
    1426          16 :       } else {
    1427           0 :         cluster_metadata_match_criteria_ =
    1428           0 :             std::make_unique<MetadataMatchCriteriaImpl>(filter_it->second);
    1429           0 :       }
    1430          16 :     }
    1431          16 :   }
    1432         198 : }
    1433             : 
    1434             : Http::HeaderTransforms RouteEntryImplBase::WeightedClusterEntry::requestHeaderTransforms(
    1435           0 :     const StreamInfo::StreamInfo& stream_info, bool do_formatting) const {
    1436           0 :   auto transforms = requestHeaderParser().getHeaderTransforms(stream_info, do_formatting);
    1437           0 :   mergeTransforms(transforms,
    1438           0 :                   DynamicRouteEntry::requestHeaderTransforms(stream_info, do_formatting));
    1439           0 :   return transforms;
    1440           0 : }
    1441             : 
    1442             : Http::HeaderTransforms RouteEntryImplBase::WeightedClusterEntry::responseHeaderTransforms(
    1443           0 :     const StreamInfo::StreamInfo& stream_info, bool do_formatting) const {
    1444           0 :   auto transforms = responseHeaderParser().getHeaderTransforms(stream_info, do_formatting);
    1445           0 :   mergeTransforms(transforms,
    1446           0 :                   DynamicRouteEntry::responseHeaderTransforms(stream_info, do_formatting));
    1447           0 :   return transforms;
    1448           0 : }
    1449             : 
    1450             : void RouteEntryImplBase::WeightedClusterEntry::traversePerFilterConfig(
    1451             :     const std::string& filter_name,
    1452           0 :     std::function<void(const Router::RouteSpecificFilterConfig&)> cb) const {
    1453           0 :   DynamicRouteEntry::traversePerFilterConfig(filter_name, cb);
    1454             : 
    1455           0 :   const auto* cfg = per_filter_configs_.get(filter_name);
    1456           0 :   if (cfg) {
    1457           0 :     cb(*cfg);
    1458           0 :   }
    1459           0 : }
    1460             : 
    1461             : UriTemplateMatcherRouteEntryImpl::UriTemplateMatcherRouteEntryImpl(
    1462             :     const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
    1463             :     Server::Configuration::ServerFactoryContext& factory_context,
    1464             :     ProtobufMessage::ValidationVisitor& validator)
    1465             :     : RouteEntryImplBase(vhost, route, factory_context, validator),
    1466          60 :       uri_template_(path_matcher_->uriTemplate()){};
    1467             : 
    1468             : void UriTemplateMatcherRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
    1469           0 :                                                          bool insert_envoy_original_path) const {
    1470           0 :   finalizePathHeader(headers, path_matcher_->uriTemplate(), insert_envoy_original_path);
    1471           0 : }
    1472             : 
    1473             : absl::optional<std::string> UriTemplateMatcherRouteEntryImpl::currentUrlPathAfterRewrite(
    1474           0 :     const Http::RequestHeaderMap& headers) const {
    1475           0 :   return currentUrlPathAfterRewriteWithMatchedPath(headers, path_matcher_->uriTemplate());
    1476           0 : }
    1477             : 
    1478             : RouteConstSharedPtr
    1479             : UriTemplateMatcherRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
    1480             :                                           const StreamInfo::StreamInfo& stream_info,
    1481           0 :                                           uint64_t random_value) const {
    1482           0 :   if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value) &&
    1483           0 :       path_matcher_->match(headers.getPathValue())) {
    1484           0 :     return clusterEntry(headers, random_value);
    1485           0 :   }
    1486           0 :   return nullptr;
    1487           0 : }
    1488             : 
    1489             : PrefixRouteEntryImpl::PrefixRouteEntryImpl(
    1490             :     const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
    1491             :     Server::Configuration::ServerFactoryContext& factory_context,
    1492             :     ProtobufMessage::ValidationVisitor& validator)
    1493             :     : RouteEntryImplBase(vhost, route, factory_context, validator),
    1494             :       path_matcher_(
    1495        1322 :           Matchers::PathMatcher::createPrefix(route.match().prefix(), !case_sensitive())) {}
    1496             : 
    1497             : void PrefixRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
    1498         203 :                                              bool insert_envoy_original_path) const {
    1499         203 :   finalizePathHeader(headers, matcher(), insert_envoy_original_path);
    1500         203 : }
    1501             : 
    1502             : absl::optional<std::string>
    1503           0 : PrefixRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers) const {
    1504           0 :   return currentUrlPathAfterRewriteWithMatchedPath(headers, matcher());
    1505           0 : }
    1506             : 
    1507             : RouteConstSharedPtr PrefixRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
    1508             :                                                   const StreamInfo::StreamInfo& stream_info,
    1509         549 :                                                   uint64_t random_value) const {
    1510         549 :   if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value) &&
    1511         549 :       path_matcher_->match(sanitizePathBeforePathMatching(headers.getPathValue()))) {
    1512         439 :     return clusterEntry(headers, random_value);
    1513         439 :   }
    1514         110 :   return nullptr;
    1515         549 : }
    1516             : 
    1517             : PathRouteEntryImpl::PathRouteEntryImpl(const CommonVirtualHostSharedPtr& vhost,
    1518             :                                        const envoy::config::route::v3::Route& route,
    1519             :                                        Server::Configuration::ServerFactoryContext& factory_context,
    1520             :                                        ProtobufMessage::ValidationVisitor& validator)
    1521             :     : RouteEntryImplBase(vhost, route, factory_context, validator),
    1522         147 :       path_matcher_(Matchers::PathMatcher::createExact(route.match().path(), !case_sensitive())) {}
    1523             : 
    1524             : void PathRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
    1525           0 :                                            bool insert_envoy_original_path) const {
    1526           0 :   finalizePathHeader(headers, matcher(), insert_envoy_original_path);
    1527           0 : }
    1528             : 
    1529             : absl::optional<std::string>
    1530           0 : PathRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers) const {
    1531           0 :   return currentUrlPathAfterRewriteWithMatchedPath(headers, matcher());
    1532           0 : }
    1533             : 
    1534             : RouteConstSharedPtr PathRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
    1535             :                                                 const StreamInfo::StreamInfo& stream_info,
    1536          82 :                                                 uint64_t random_value) const {
    1537          82 :   if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value) &&
    1538          82 :       path_matcher_->match(sanitizePathBeforePathMatching(headers.getPathValue()))) {
    1539          18 :     return clusterEntry(headers, random_value);
    1540          18 :   }
    1541             : 
    1542          64 :   return nullptr;
    1543          82 : }
    1544             : 
    1545             : RegexRouteEntryImpl::RegexRouteEntryImpl(
    1546             :     const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
    1547             :     Server::Configuration::ServerFactoryContext& factory_context,
    1548             :     ProtobufMessage::ValidationVisitor& validator)
    1549             :     : RouteEntryImplBase(vhost, route, factory_context, validator),
    1550         119 :       path_matcher_(Matchers::PathMatcher::createSafeRegex(route.match().safe_regex())) {
    1551         119 :   ASSERT(route.match().path_specifier_case() ==
    1552         119 :          envoy::config::route::v3::RouteMatch::PathSpecifierCase::kSafeRegex);
    1553         119 : }
    1554             : 
    1555             : void RegexRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
    1556           1 :                                             bool insert_envoy_original_path) const {
    1557           1 :   absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
    1558             :   // TODO(yuval-k): This ASSERT can happen if the path was changed by a filter without clearing
    1559             :   // the route cache. We should consider if ASSERT-ing is the desired behavior in this case.
    1560           1 :   ASSERT(path_matcher_->match(sanitizePathBeforePathMatching(path)));
    1561           1 :   finalizePathHeader(headers, path, insert_envoy_original_path);
    1562           1 : }
    1563             : 
    1564             : absl::optional<std::string>
    1565           0 : RegexRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers) const {
    1566           0 :   const absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
    1567           0 :   return currentUrlPathAfterRewriteWithMatchedPath(headers, path);
    1568           0 : }
    1569             : 
    1570             : RouteConstSharedPtr RegexRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
    1571             :                                                  const StreamInfo::StreamInfo& stream_info,
    1572          43 :                                                  uint64_t random_value) const {
    1573          43 :   if (RouteEntryImplBase::matchRoute(headers, stream_info, random_value)) {
    1574          43 :     if (path_matcher_->match(sanitizePathBeforePathMatching(headers.getPathValue()))) {
    1575          11 :       return clusterEntry(headers, random_value);
    1576          11 :     }
    1577          43 :   }
    1578          32 :   return nullptr;
    1579          43 : }
    1580             : 
    1581             : ConnectRouteEntryImpl::ConnectRouteEntryImpl(
    1582             :     const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
    1583             :     Server::Configuration::ServerFactoryContext& factory_context,
    1584             :     ProtobufMessage::ValidationVisitor& validator)
    1585           0 :     : RouteEntryImplBase(vhost, route, factory_context, validator) {}
    1586             : 
    1587             : void ConnectRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
    1588           0 :                                               bool insert_envoy_original_path) const {
    1589           0 :   const absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
    1590           0 :   finalizePathHeader(headers, path, insert_envoy_original_path);
    1591           0 : }
    1592             : 
    1593             : absl::optional<std::string>
    1594           0 : ConnectRouteEntryImpl::currentUrlPathAfterRewrite(const Http::RequestHeaderMap& headers) const {
    1595           0 :   const absl::string_view path = Http::PathUtil::removeQueryAndFragment(headers.getPathValue());
    1596           0 :   return currentUrlPathAfterRewriteWithMatchedPath(headers, path);
    1597           0 : }
    1598             : 
    1599             : RouteConstSharedPtr ConnectRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
    1600             :                                                    const StreamInfo::StreamInfo& stream_info,
    1601           0 :                                                    uint64_t random_value) const {
    1602           0 :   if ((Http::HeaderUtility::isConnect(headers) ||
    1603           0 :        (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.enable_connect_udp_support") &&
    1604           0 :         Http::HeaderUtility::isConnectUdpRequest(headers))) &&
    1605           0 :       RouteEntryImplBase::matchRoute(headers, stream_info, random_value)) {
    1606           0 :     return clusterEntry(headers, random_value);
    1607           0 :   }
    1608           0 :   return nullptr;
    1609           0 : }
    1610             : 
    1611             : PathSeparatedPrefixRouteEntryImpl::PathSeparatedPrefixRouteEntryImpl(
    1612             :     const CommonVirtualHostSharedPtr& vhost, const envoy::config::route::v3::Route& route,
    1613             :     Server::Configuration::ServerFactoryContext& factory_context,
    1614             :     ProtobufMessage::ValidationVisitor& validator)
    1615             :     : RouteEntryImplBase(vhost, route, factory_context, validator),
    1616             :       path_matcher_(Matchers::PathMatcher::createPrefix(route.match().path_separated_prefix(),
    1617          36 :                                                         !case_sensitive())) {}
    1618             : 
    1619             : void PathSeparatedPrefixRouteEntryImpl::rewritePathHeader(Http::RequestHeaderMap& headers,
    1620           0 :                                                           bool insert_envoy_original_path) const {
    1621           0 :   finalizePathHeader(headers, matcher(), insert_envoy_original_path);
    1622           0 : }
    1623             : 
    1624             : absl::optional<std::string> PathSeparatedPrefixRouteEntryImpl::currentUrlPathAfterRewrite(
    1625           0 :     const Http::RequestHeaderMap& headers) const {
    1626           0 :   return currentUrlPathAfterRewriteWithMatchedPath(headers, matcher());
    1627           0 : }
    1628             : 
    1629             : RouteConstSharedPtr
    1630             : PathSeparatedPrefixRouteEntryImpl::matches(const Http::RequestHeaderMap& headers,
    1631             :                                            const StreamInfo::StreamInfo& stream_info,
    1632           0 :                                            uint64_t random_value) const {
    1633           0 :   if (!RouteEntryImplBase::matchRoute(headers, stream_info, random_value)) {
    1634           0 :     return nullptr;
    1635           0 :   }
    1636           0 :   absl::string_view sanitized_path = sanitizePathBeforePathMatching(
    1637           0 :       Http::PathUtil::removeQueryAndFragment(headers.getPathValue()));
    1638           0 :   const size_t sanitized_size = sanitized_path.size();
    1639           0 :   const size_t matcher_size = matcher().size();
    1640           0 :   if (sanitized_size >= matcher_size && path_matcher_->match(sanitized_path) &&
    1641           0 :       (sanitized_size == matcher_size || sanitized_path[matcher_size] == '/')) {
    1642           0 :     return clusterEntry(headers, random_value);
    1643           0 :   }
    1644           0 :   return nullptr;
    1645           0 : }
    1646             : 
    1647             : CommonVirtualHostImpl::CommonVirtualHostImpl(
    1648             :     const envoy::config::route::v3::VirtualHost& virtual_host,
    1649             :     const CommonConfigSharedPtr& global_route_config,
    1650             :     Server::Configuration::ServerFactoryContext& factory_context, Stats::Scope& scope,
    1651             :     ProtobufMessage::ValidationVisitor& validator)
    1652             :     : stat_name_storage_(virtual_host.name(), factory_context.scope().symbolTable()),
    1653             :       global_route_config_(global_route_config),
    1654             :       per_filter_configs_(virtual_host.typed_per_filter_config(), factory_context, validator),
    1655             :       retry_shadow_buffer_limit_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(
    1656             :           virtual_host, per_request_buffer_limit_bytes, std::numeric_limits<uint32_t>::max())),
    1657             :       include_attempt_count_in_request_(virtual_host.include_request_attempt_count()),
    1658             :       include_attempt_count_in_response_(virtual_host.include_attempt_count_in_response()),
    1659        1272 :       include_is_timeout_retry_header_(virtual_host.include_is_timeout_retry_header()) {
    1660        1272 :   if (!virtual_host.request_headers_to_add().empty() ||
    1661        1272 :       !virtual_host.request_headers_to_remove().empty()) {
    1662          23 :     request_headers_parser_ = HeaderParser::configure(virtual_host.request_headers_to_add(),
    1663          23 :                                                       virtual_host.request_headers_to_remove());
    1664          23 :   }
    1665        1272 :   if (!virtual_host.response_headers_to_add().empty() ||
    1666        1272 :       !virtual_host.response_headers_to_remove().empty()) {
    1667          16 :     response_headers_parser_ = HeaderParser::configure(virtual_host.response_headers_to_add(),
    1668          16 :                                                        virtual_host.response_headers_to_remove());
    1669          16 :   }
    1670             : 
    1671             :   // Retry and Hedge policies must be set before routes, since they may use them.
    1672        1272 :   if (virtual_host.has_retry_policy()) {
    1673          26 :     retry_policy_ = std::make_unique<envoy::config::route::v3::RetryPolicy>();
    1674          26 :     retry_policy_->CopyFrom(virtual_host.retry_policy());
    1675          26 :   }
    1676        1272 :   if (virtual_host.has_hedge_policy()) {
    1677          18 :     hedge_policy_ = std::make_unique<envoy::config::route::v3::HedgePolicy>();
    1678          18 :     hedge_policy_->CopyFrom(virtual_host.hedge_policy());
    1679          18 :   }
    1680             : 
    1681        1272 :   if (!virtual_host.rate_limits().empty()) {
    1682           0 :     rate_limit_policy_ =
    1683           0 :         std::make_unique<RateLimitPolicyImpl>(virtual_host.rate_limits(), factory_context);
    1684           0 :   }
    1685             : 
    1686        1272 :   shadow_policies_.reserve(virtual_host.request_mirror_policies().size());
    1687        1272 :   for (const auto& mirror_policy_config : virtual_host.request_mirror_policies()) {
    1688          16 :     shadow_policies_.push_back(std::make_shared<ShadowPolicyImpl>(mirror_policy_config));
    1689          16 :   }
    1690             : 
    1691             :   // Inherit policies from the global config.
    1692        1272 :   if (shadow_policies_.empty()) {
    1693        1254 :     shadow_policies_ = global_route_config_->shadowPolicies();
    1694        1254 :   }
    1695             : 
    1696        1272 :   if (virtual_host.has_matcher() && !virtual_host.routes().empty()) {
    1697           0 :     throwEnvoyExceptionOrPanic("cannot set both matcher and routes on virtual host");
    1698           0 :   }
    1699             : 
    1700        1272 :   if (!virtual_host.virtual_clusters().empty()) {
    1701           0 :     vcluster_scope_ = Stats::Utility::scopeFromStatNames(
    1702           0 :         scope, {stat_name_storage_.statName(),
    1703           0 :                 factory_context.routerContext().virtualClusterStatNames().vcluster_});
    1704           0 :     virtual_cluster_catch_all_ = std::make_unique<CatchAllVirtualCluster>(
    1705           0 :         *vcluster_scope_, factory_context.routerContext().virtualClusterStatNames());
    1706           0 :     for (const auto& virtual_cluster : virtual_host.virtual_clusters()) {
    1707           0 :       virtual_clusters_.push_back(
    1708           0 :           VirtualClusterEntry(virtual_cluster, *vcluster_scope_,
    1709           0 :                               factory_context.routerContext().virtualClusterStatNames()));
    1710           0 :     }
    1711           0 :   }
    1712             : 
    1713        1272 :   if (virtual_host.has_cors()) {
    1714           2 :     cors_policy_ = std::make_unique<CorsPolicyImpl>(virtual_host.cors(), factory_context.runtime());
    1715           2 :   }
    1716             : 
    1717        1272 :   if (virtual_host.has_metadata()) {
    1718           8 :     metadata_ = std::make_unique<RouteMetadataPack>(virtual_host.metadata());
    1719           8 :   }
    1720        1272 : }
    1721             : 
    1722             : CommonVirtualHostImpl::VirtualClusterEntry::VirtualClusterEntry(
    1723             :     const envoy::config::route::v3::VirtualCluster& virtual_cluster, Stats::Scope& scope,
    1724             :     const VirtualClusterStatNames& stat_names)
    1725             :     : StatNameProvider(virtual_cluster.name(), scope.symbolTable()),
    1726             :       VirtualClusterBase(virtual_cluster.name(), stat_name_storage_.statName(),
    1727           0 :                          scope.scopeFromStatName(stat_name_storage_.statName()), stat_names) {
    1728           0 :   if (virtual_cluster.headers().empty()) {
    1729           0 :     throwEnvoyExceptionOrPanic("virtual clusters must define 'headers'");
    1730           0 :   }
    1731             : 
    1732           0 :   ASSERT(!virtual_cluster.headers().empty());
    1733           0 :   headers_ = Http::HeaderUtility::buildHeaderDataVector(virtual_cluster.headers());
    1734           0 : }
    1735             : 
    1736           0 : const CommonConfig& CommonVirtualHostImpl::routeConfig() const { return *global_route_config_; }
    1737             : 
    1738         427 : absl::optional<bool> CommonVirtualHostImpl::filterDisabled(absl::string_view config_name) const {
    1739         427 :   absl::optional<bool> result = per_filter_configs_.disabled(config_name);
    1740         427 :   if (result.has_value()) {
    1741           0 :     return result.value();
    1742           0 :   }
    1743         427 :   return global_route_config_->filterDisabled(config_name);
    1744         427 : }
    1745             : 
    1746             : const RouteSpecificFilterConfig*
    1747           0 : CommonVirtualHostImpl::mostSpecificPerFilterConfig(const std::string& name) const {
    1748           0 :   auto* per_filter_config = per_filter_configs_.get(name);
    1749           0 :   return per_filter_config != nullptr ? per_filter_config
    1750           0 :                                       : global_route_config_->perFilterConfig(name);
    1751           0 : }
    1752             : void CommonVirtualHostImpl::traversePerFilterConfig(
    1753             :     const std::string& filter_name,
    1754          40 :     std::function<void(const Router::RouteSpecificFilterConfig&)> cb) const {
    1755             :   // Parent first.
    1756          40 :   if (auto* maybe_rc_config = global_route_config_->perFilterConfig(filter_name);
    1757          40 :       maybe_rc_config != nullptr) {
    1758           0 :     cb(*maybe_rc_config);
    1759           0 :   }
    1760          40 :   if (auto* maybe_vhost_config = per_filter_configs_.get(filter_name);
    1761          40 :       maybe_vhost_config != nullptr) {
    1762           0 :     cb(*maybe_vhost_config);
    1763           0 :   }
    1764          40 : }
    1765             : 
    1766           0 : const envoy::config::core::v3::Metadata& CommonVirtualHostImpl::metadata() const {
    1767           0 :   return metadata_ != nullptr ? metadata_->proto_metadata_
    1768           0 :                               : DefaultRouteMetadataPack::get().proto_metadata_;
    1769           0 : }
    1770           0 : const Envoy::Config::TypedMetadata& CommonVirtualHostImpl::typedMetadata() const {
    1771           0 :   return metadata_ != nullptr ? metadata_->typed_metadata_
    1772           0 :                               : DefaultRouteMetadataPack::get().typed_metadata_;
    1773           0 : }
    1774             : 
    1775             : VirtualHostImpl::VirtualHostImpl(
    1776             :     const envoy::config::route::v3::VirtualHost& virtual_host,
    1777             :     const CommonConfigSharedPtr& global_route_config,
    1778             :     Server::Configuration::ServerFactoryContext& factory_context, Stats::Scope& scope,
    1779             :     ProtobufMessage::ValidationVisitor& validator,
    1780        1272 :     const absl::optional<Upstream::ClusterManager::ClusterInfoMaps>& validation_clusters) {
    1781             : 
    1782        1272 :   shared_virtual_host_ = std::make_shared<CommonVirtualHostImpl>(virtual_host, global_route_config,
    1783        1272 :                                                                  factory_context, scope, validator);
    1784             : 
    1785        1272 :   switch (virtual_host.require_tls()) {
    1786           0 :     PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
    1787        1156 :   case envoy::config::route::v3::VirtualHost::NONE:
    1788        1156 :     ssl_requirements_ = SslRequirements::None;
    1789        1156 :     break;
    1790           1 :   case envoy::config::route::v3::VirtualHost::EXTERNAL_ONLY:
    1791           1 :     ssl_requirements_ = SslRequirements::ExternalOnly;
    1792           1 :     break;
    1793         113 :   case envoy::config::route::v3::VirtualHost::ALL:
    1794         113 :     ssl_requirements_ = SslRequirements::All;
    1795         113 :     break;
    1796        1272 :   }
    1797             : 
    1798        1270 :   if (virtual_host.has_matcher()) {
    1799           3 :     RouteActionContext context{shared_virtual_host_, factory_context};
    1800           3 :     RouteActionValidationVisitor validation_visitor;
    1801           3 :     Matcher::MatchTreeFactory<Http::HttpMatchingData, RouteActionContext> factory(
    1802           3 :         context, factory_context, validation_visitor);
    1803             : 
    1804           3 :     matcher_ = factory.create(virtual_host.matcher())();
    1805             : 
    1806           3 :     if (!validation_visitor.errors().empty()) {
    1807             :       // TODO(snowp): Output all violations.
    1808           0 :       throwEnvoyExceptionOrPanic(
    1809           0 :           fmt::format("requirement violation while creating route match tree: {}",
    1810           0 :                       validation_visitor.errors()[0]));
    1811           0 :     }
    1812        1267 :   } else {
    1813        1682 :     for (const auto& route : virtual_host.routes()) {
    1814        1682 :       routes_.emplace_back(createAndValidateRoute(route, shared_virtual_host_, factory_context,
    1815        1682 :                                                   validator, validation_clusters));
    1816        1682 :     }
    1817        1267 :   }
    1818        1270 : }
    1819             : 
    1820             : const std::shared_ptr<const SslRedirectRoute> VirtualHostImpl::SSL_REDIRECT_ROUTE{
    1821             :     new SslRedirectRoute()};
    1822             : 
    1823             : RouteConstSharedPtr VirtualHostImpl::getRouteFromRoutes(
    1824             :     const RouteCallback& cb, const Http::RequestHeaderMap& headers,
    1825             :     const StreamInfo::StreamInfo& stream_info, uint64_t random_value,
    1826         468 :     absl::Span<const RouteEntryImplBaseConstSharedPtr> routes) const {
    1827         674 :   for (auto route = routes.begin(); route != routes.end(); ++route) {
    1828         674 :     if (!headers.Path() && !(*route)->supportsPathlessHeaders()) {
    1829           0 :       continue;
    1830           0 :     }
    1831             : 
    1832         674 :     RouteConstSharedPtr route_entry = (*route)->matches(headers, stream_info, random_value);
    1833         674 :     if (route_entry == nullptr) {
    1834         206 :       continue;
    1835         206 :     }
    1836             : 
    1837         468 :     if (cb == nullptr) {
    1838         468 :       return route_entry;
    1839         468 :     }
    1840             : 
    1841           0 :     RouteEvalStatus eval_status = (std::next(route) == routes.end())
    1842           0 :                                       ? RouteEvalStatus::NoMoreRoutes
    1843           0 :                                       : RouteEvalStatus::HasMoreRoutes;
    1844           0 :     RouteMatchStatus match_status = cb(route_entry, eval_status);
    1845           0 :     if (match_status == RouteMatchStatus::Accept) {
    1846           0 :       return route_entry;
    1847           0 :     }
    1848           0 :     if (match_status == RouteMatchStatus::Continue &&
    1849           0 :         eval_status == RouteEvalStatus::NoMoreRoutes) {
    1850           0 :       ENVOY_LOG(debug,
    1851           0 :                 "return null when route match status is Continue but there is no more routes");
    1852           0 :       return nullptr;
    1853           0 :     }
    1854           0 :   }
    1855             : 
    1856           0 :   ENVOY_LOG(debug, "route was resolved but final route list did not match incoming request");
    1857           0 :   return nullptr;
    1858         468 : }
    1859             : 
    1860             : RouteConstSharedPtr VirtualHostImpl::getRouteFromEntries(const RouteCallback& cb,
    1861             :                                                          const Http::RequestHeaderMap& headers,
    1862             :                                                          const StreamInfo::StreamInfo& stream_info,
    1863         475 :                                                          uint64_t random_value) const {
    1864             :   // In the rare case that X-Forwarded-Proto and scheme disagree (say http URL over an HTTPS
    1865             :   // connection), force a redirect based on underlying protocol, rather than URL
    1866             :   // scheme, so don't force a redirect for a http:// url served over a TLS
    1867             :   // connection.
    1868         475 :   const absl::string_view scheme = headers.getForwardedProtoValue();
    1869         475 :   if (scheme.empty()) {
    1870             :     // No scheme header. This normally only happens when ActiveStream::decodeHeaders
    1871             :     // bails early (as it rejects a request), or a buggy filter removes the :scheme header.
    1872           7 :     return nullptr;
    1873           7 :   }
    1874             : 
    1875             :   // First check for ssl redirect.
    1876         468 :   if (ssl_requirements_ == SslRequirements::All && scheme != "https") {
    1877           0 :     return SSL_REDIRECT_ROUTE;
    1878         468 :   } else if (ssl_requirements_ == SslRequirements::ExternalOnly && scheme != "https" &&
    1879         468 :              !Http::HeaderUtility::isEnvoyInternalRequest(headers)) {
    1880           0 :     return SSL_REDIRECT_ROUTE;
    1881           0 :   }
    1882             : 
    1883         468 :   if (matcher_) {
    1884           2 :     Http::Matching::HttpMatchingDataImpl data(stream_info);
    1885           2 :     data.onRequestHeaders(headers);
    1886             : 
    1887           2 :     auto match = Matcher::evaluateMatch<Http::HttpMatchingData>(*matcher_, data);
    1888             : 
    1889           2 :     if (match.result_) {
    1890           2 :       const auto result = match.result_();
    1891           2 :       if (result->typeUrl() == RouteMatchAction::staticTypeUrl()) {
    1892           2 :         const RouteMatchAction& route_action = result->getTyped<RouteMatchAction>();
    1893             : 
    1894           2 :         return getRouteFromRoutes(cb, headers, stream_info, random_value, {route_action.route()});
    1895           2 :       } else if (result->typeUrl() == RouteListMatchAction::staticTypeUrl()) {
    1896           0 :         const RouteListMatchAction& action = result->getTyped<RouteListMatchAction>();
    1897             : 
    1898           0 :         return getRouteFromRoutes(cb, headers, stream_info, random_value, action.routes());
    1899           0 :       }
    1900           0 :       PANIC("Action in router matcher should be Route or RouteList");
    1901           0 :     }
    1902             : 
    1903           0 :     ENVOY_LOG(debug, "failed to match incoming request: {}", static_cast<int>(match.match_state_));
    1904             : 
    1905           0 :     return nullptr;
    1906           2 :   }
    1907             : 
    1908             :   // Check for a route that matches the request.
    1909         466 :   return getRouteFromRoutes(cb, headers, stream_info, random_value, routes_);
    1910         468 : }
    1911             : 
    1912             : const VirtualHostImpl* RouteMatcher::findWildcardVirtualHost(
    1913             :     absl::string_view host, const RouteMatcher::WildcardVirtualHosts& wildcard_virtual_hosts,
    1914           0 :     RouteMatcher::SubstringFunction substring_function) const {
    1915             :   // We do a longest wildcard match against the host that's passed in
    1916             :   // (e.g. "foo-bar.baz.com" should match "*-bar.baz.com" before matching "*.baz.com" for suffix
    1917             :   // wildcards). This is done by scanning the length => wildcards map looking for every wildcard
    1918             :   // whose size is < length.
    1919           0 :   for (const auto& iter : wildcard_virtual_hosts) {
    1920           0 :     const uint32_t wildcard_length = iter.first;
    1921           0 :     const auto& wildcard_map = iter.second;
    1922             :     // >= because *.foo.com shouldn't match .foo.com.
    1923           0 :     if (wildcard_length >= host.size()) {
    1924           0 :       continue;
    1925           0 :     }
    1926           0 :     const auto match = wildcard_map.find(substring_function(host, wildcard_length));
    1927           0 :     if (match != wildcard_map.end()) {
    1928           0 :       return match->second.get();
    1929           0 :     }
    1930           0 :   }
    1931           0 :   return nullptr;
    1932           0 : }
    1933             : 
    1934             : RouteMatcher::RouteMatcher(const envoy::config::route::v3::RouteConfiguration& route_config,
    1935             :                            const CommonConfigSharedPtr& global_route_config,
    1936             :                            Server::Configuration::ServerFactoryContext& factory_context,
    1937             :                            ProtobufMessage::ValidationVisitor& validator, bool validate_clusters)
    1938             :     : vhost_scope_(factory_context.scope().scopeFromStatName(
    1939             :           factory_context.routerContext().virtualClusterStatNames().vhost_)),
    1940        1278 :       ignore_port_in_host_matching_(route_config.ignore_port_in_host_matching()) {
    1941        1278 :   absl::optional<Upstream::ClusterManager::ClusterInfoMaps> validation_clusters;
    1942        1278 :   if (validate_clusters) {
    1943        1242 :     validation_clusters = factory_context.clusterManager().clusters();
    1944        1242 :   }
    1945        1278 :   for (const auto& virtual_host_config : route_config.virtual_hosts()) {
    1946        1272 :     VirtualHostSharedPtr virtual_host =
    1947        1272 :         std::make_shared<VirtualHostImpl>(virtual_host_config, global_route_config, factory_context,
    1948        1272 :                                           *vhost_scope_, validator, validation_clusters);
    1949        1272 :     for (const std::string& domain_name : virtual_host_config.domains()) {
    1950         196 :       const Http::LowerCaseString lower_case_domain_name(domain_name);
    1951         196 :       absl::string_view domain = lower_case_domain_name;
    1952         196 :       bool duplicate_found = false;
    1953         196 :       if ("*" == domain) {
    1954         117 :         if (default_virtual_host_) {
    1955           0 :           throwEnvoyExceptionOrPanic(fmt::format(
    1956           0 :               "Only a single wildcard domain is permitted in route {}", route_config.name()));
    1957           0 :         }
    1958         117 :         default_virtual_host_ = virtual_host;
    1959         185 :       } else if (!domain.empty() && '*' == domain[0]) {
    1960           0 :         duplicate_found = !wildcard_virtual_host_suffixes_[domain.size() - 1]
    1961           0 :                                .emplace(domain.substr(1), virtual_host)
    1962           0 :                                .second;
    1963          79 :       } else if (!domain.empty() && '*' == domain[domain.size() - 1]) {
    1964           0 :         duplicate_found = !wildcard_virtual_host_prefixes_[domain.size() - 1]
    1965           0 :                                .emplace(domain.substr(0, domain.size() - 1), virtual_host)
    1966           0 :                                .second;
    1967          79 :       } else {
    1968          79 :         duplicate_found = !virtual_hosts_.emplace(domain, virtual_host).second;
    1969          79 :       }
    1970         196 :       if (duplicate_found) {
    1971           0 :         throwEnvoyExceptionOrPanic(
    1972           0 :             fmt::format("Only unique values for domains are permitted. Duplicate "
    1973           0 :                         "entry of domain {} in route {}",
    1974           0 :                         domain, route_config.name()));
    1975           0 :       }
    1976         196 :     }
    1977        1272 :   }
    1978        1278 : }
    1979             : 
    1980         485 : const VirtualHostImpl* RouteMatcher::findVirtualHost(const Http::RequestHeaderMap& headers) const {
    1981             :   // Fast path the case where we only have a default virtual host.
    1982         485 :   if (virtual_hosts_.empty() && wildcard_virtual_host_suffixes_.empty() &&
    1983         485 :       wildcard_virtual_host_prefixes_.empty()) {
    1984         406 :     return default_virtual_host_.get();
    1985         406 :   }
    1986             : 
    1987             :   // There may be no authority in early reply paths in the HTTP connection manager.
    1988          79 :   if (headers.Host() == nullptr) {
    1989           0 :     return nullptr;
    1990           0 :   }
    1991             : 
    1992             :   // If 'ignore_port_in_host_matching' is set, ignore the port number in the host header(if any).
    1993          79 :   absl::string_view host_header_value = headers.getHostValue();
    1994          79 :   if (ignorePortInHostMatching()) {
    1995           0 :     if (const absl::string_view::size_type port_start =
    1996           0 :             Http::HeaderUtility::getPortStart(host_header_value);
    1997           0 :         port_start != absl::string_view::npos) {
    1998           0 :       host_header_value = host_header_value.substr(0, port_start);
    1999           0 :     }
    2000           0 :   }
    2001             :   // TODO (@rshriram) Match Origin header in WebSocket
    2002             :   // request with VHost, using wildcard match
    2003             :   // Lower-case the value of the host header, as hostnames are case insensitive.
    2004          79 :   const std::string host = absl::AsciiStrToLower(host_header_value);
    2005          79 :   const auto iter = virtual_hosts_.find(host);
    2006          79 :   if (iter != virtual_hosts_.end()) {
    2007          71 :     return iter->second.get();
    2008          71 :   }
    2009           8 :   if (!wildcard_virtual_host_suffixes_.empty()) {
    2010           0 :     const VirtualHostImpl* vhost = findWildcardVirtualHost(
    2011           0 :         host, wildcard_virtual_host_suffixes_,
    2012           0 :         [](absl::string_view h, int l) -> absl::string_view { return h.substr(h.size() - l); });
    2013           0 :     if (vhost != nullptr) {
    2014           0 :       return vhost;
    2015           0 :     }
    2016           0 :   }
    2017           8 :   if (!wildcard_virtual_host_prefixes_.empty()) {
    2018           0 :     const VirtualHostImpl* vhost = findWildcardVirtualHost(
    2019           0 :         host, wildcard_virtual_host_prefixes_,
    2020           0 :         [](absl::string_view h, int l) -> absl::string_view { return h.substr(0, l); });
    2021           0 :     if (vhost != nullptr) {
    2022           0 :       return vhost;
    2023           0 :     }
    2024           0 :   }
    2025           8 :   return default_virtual_host_.get();
    2026           8 : }
    2027             : 
    2028             : RouteConstSharedPtr RouteMatcher::route(const RouteCallback& cb,
    2029             :                                         const Http::RequestHeaderMap& headers,
    2030             :                                         const StreamInfo::StreamInfo& stream_info,
    2031         485 :                                         uint64_t random_value) const {
    2032         485 :   const VirtualHostImpl* virtual_host = findVirtualHost(headers);
    2033         485 :   if (virtual_host) {
    2034         475 :     return virtual_host->getRouteFromEntries(cb, headers, stream_info, random_value);
    2035         475 :   } else {
    2036          10 :     return nullptr;
    2037          10 :   }
    2038         485 : }
    2039             : 
    2040             : const SslRedirector SslRedirectRoute::SSL_REDIRECTOR;
    2041             : const envoy::config::core::v3::Metadata SslRedirectRoute::metadata_;
    2042             : const Envoy::Config::TypedMetadataImpl<Envoy::Config::TypedMetadataFactory>
    2043             :     SslRedirectRoute::typed_metadata_({});
    2044             : 
    2045             : const VirtualCluster*
    2046         183 : CommonVirtualHostImpl::virtualClusterFromEntries(const Http::HeaderMap& headers) const {
    2047         183 :   for (const VirtualClusterEntry& entry : virtual_clusters_) {
    2048           0 :     if (Http::HeaderUtility::matchHeaders(headers, entry.headers_)) {
    2049           0 :       return &entry;
    2050           0 :     }
    2051           0 :   }
    2052             : 
    2053         183 :   if (!virtual_clusters_.empty()) {
    2054           0 :     return virtual_cluster_catch_all_.get();
    2055           0 :   }
    2056             : 
    2057         183 :   return nullptr;
    2058         183 : }
    2059             : 
    2060             : CommonConfigImpl::CommonConfigImpl(const envoy::config::route::v3::RouteConfiguration& config,
    2061             :                                    Server::Configuration::ServerFactoryContext& factory_context,
    2062             :                                    ProtobufMessage::ValidationVisitor& validator)
    2063             :     : name_(config.name()), symbol_table_(factory_context.scope().symbolTable()),
    2064             :       per_filter_configs_(config.typed_per_filter_config(), factory_context, validator),
    2065             :       max_direct_response_body_size_bytes_(
    2066             :           PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, max_direct_response_body_size_bytes,
    2067             :                                           DEFAULT_MAX_DIRECT_RESPONSE_BODY_SIZE_BYTES)),
    2068             :       uses_vhds_(config.has_vhds()),
    2069             :       most_specific_header_mutations_wins_(config.most_specific_header_mutations_wins()),
    2070        1294 :       ignore_path_parameters_in_path_matching_(config.ignore_path_parameters_in_path_matching()) {
    2071        1294 :   if (!config.request_mirror_policies().empty()) {
    2072           8 :     shadow_policies_.reserve(config.request_mirror_policies().size());
    2073           8 :     for (const auto& mirror_policy_config : config.request_mirror_policies()) {
    2074           8 :       shadow_policies_.push_back(std::make_shared<ShadowPolicyImpl>(mirror_policy_config));
    2075           8 :     }
    2076           8 :   }
    2077             : 
    2078             :   // Initialize all cluster specifier plugins before creating route matcher. Because the route may
    2079             :   // reference it by name.
    2080        1294 :   for (const auto& plugin_proto : config.cluster_specifier_plugins()) {
    2081           4 :     auto plugin = getClusterSpecifierPluginByTheProto(plugin_proto, validator, factory_context);
    2082           4 :     cluster_specifier_plugins_.emplace(plugin_proto.extension().name(), std::move(plugin));
    2083           4 :   }
    2084             : 
    2085        1294 :   for (const std::string& header : config.internal_only_headers()) {
    2086          22 :     internal_only_headers_.push_back(Http::LowerCaseString(header));
    2087          22 :   }
    2088             : 
    2089        1294 :   if (!config.request_headers_to_add().empty() || !config.request_headers_to_remove().empty()) {
    2090          26 :     request_headers_parser_ = HeaderParser::configure(config.request_headers_to_add(),
    2091          26 :                                                       config.request_headers_to_remove());
    2092          26 :   }
    2093        1294 :   if (!config.response_headers_to_add().empty() || !config.response_headers_to_remove().empty()) {
    2094          61 :     response_headers_parser_ = HeaderParser::configure(config.response_headers_to_add(),
    2095          61 :                                                        config.response_headers_to_remove());
    2096          61 :   }
    2097             : 
    2098        1294 :   if (config.has_metadata()) {
    2099           8 :     metadata_ = std::make_unique<RouteMetadataPack>(config.metadata());
    2100           8 :   }
    2101        1294 : }
    2102             : 
    2103             : ClusterSpecifierPluginSharedPtr
    2104           0 : CommonConfigImpl::clusterSpecifierPlugin(absl::string_view provider) const {
    2105           0 :   auto iter = cluster_specifier_plugins_.find(provider);
    2106           0 :   if (iter == cluster_specifier_plugins_.end() || iter->second == nullptr) {
    2107           0 :     throwEnvoyExceptionOrPanic(
    2108           0 :         fmt::format("Unknown cluster specifier plugin name: {} is used in the route", provider));
    2109           0 :   }
    2110           0 :   return iter->second;
    2111           0 : }
    2112             : 
    2113           0 : const envoy::config::core::v3::Metadata& CommonConfigImpl::metadata() const {
    2114           0 :   return metadata_ != nullptr ? metadata_->proto_metadata_
    2115           0 :                               : DefaultRouteMetadataPack::get().proto_metadata_;
    2116           0 : }
    2117           0 : const Envoy::Config::TypedMetadata& CommonConfigImpl::typedMetadata() const {
    2118           0 :   return metadata_ != nullptr ? metadata_->typed_metadata_
    2119           0 :                               : DefaultRouteMetadataPack::get().typed_metadata_;
    2120           0 : }
    2121             : 
    2122             : ConfigImpl::ConfigImpl(const envoy::config::route::v3::RouteConfiguration& config,
    2123             :                        Server::Configuration::ServerFactoryContext& factory_context,
    2124             :                        ProtobufMessage::ValidationVisitor& validator,
    2125        1294 :                        bool validate_clusters_default) {
    2126             : 
    2127        1294 :   shared_config_ = std::make_shared<CommonConfigImpl>(config, factory_context, validator);
    2128             : 
    2129        1294 :   route_matcher_ = std::make_unique<RouteMatcher>(
    2130        1294 :       config, shared_config_, factory_context, validator,
    2131        1294 :       PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, validate_clusters, validate_clusters_default));
    2132        1294 : }
    2133             : 
    2134             : RouteConstSharedPtr ConfigImpl::route(const RouteCallback& cb,
    2135             :                                       const Http::RequestHeaderMap& headers,
    2136             :                                       const StreamInfo::StreamInfo& stream_info,
    2137         485 :                                       uint64_t random_value) const {
    2138         485 :   return route_matcher_->route(cb, headers, stream_info, random_value);
    2139         485 : }
    2140             : 
    2141           0 : const envoy::config::core::v3::Metadata& NullConfigImpl::metadata() const {
    2142           0 :   return DefaultRouteMetadataPack::get().proto_metadata_;
    2143           0 : }
    2144           0 : const Envoy::Config::TypedMetadata& NullConfigImpl::typedMetadata() const {
    2145           0 :   return DefaultRouteMetadataPack::get().typed_metadata_;
    2146           0 : }
    2147             : 
    2148             : RouteSpecificFilterConfigConstSharedPtr PerFilterConfigs::createRouteSpecificFilterConfig(
    2149             :     const std::string& name, const ProtobufWkt::Any& typed_config, bool is_optional,
    2150             :     Server::Configuration::ServerFactoryContext& factory_context,
    2151          34 :     ProtobufMessage::ValidationVisitor& validator) {
    2152          34 :   Server::Configuration::NamedHttpFilterConfigFactory* factory =
    2153          34 :       Envoy::Config::Utility::getFactoryByType<Server::Configuration::NamedHttpFilterConfigFactory>(
    2154          34 :           typed_config);
    2155          34 :   if (factory == nullptr) {
    2156          34 :     if (is_optional) {
    2157          12 :       ENVOY_LOG(warn,
    2158          12 :                 "Can't find a registered implementation for http filter '{}' with type URL: '{}'",
    2159          12 :                 name, Envoy::Config::Utility::getFactoryType(typed_config));
    2160          12 :       return nullptr;
    2161          22 :     } else {
    2162          22 :       throwEnvoyExceptionOrPanic(
    2163          22 :           fmt::format("Didn't find a registered implementation for '{}' with type URL: '{}'", name,
    2164          22 :                       Envoy::Config::Utility::getFactoryType(typed_config)));
    2165          22 :     }
    2166          34 :   }
    2167             : 
    2168           0 :   ProtobufTypes::MessagePtr proto_config = factory->createEmptyRouteConfigProto();
    2169           0 :   Envoy::Config::Utility::translateOpaqueConfig(typed_config, validator, *proto_config);
    2170           0 :   auto object = factory->createRouteSpecificFilterConfig(*proto_config, factory_context, validator);
    2171           0 :   if (object == nullptr) {
    2172           0 :     if (is_optional) {
    2173           0 :       ENVOY_LOG(
    2174           0 :           debug,
    2175           0 :           "The filter {} doesn't support virtual host or route specific configurations, and it is "
    2176           0 :           "optional, so ignore it.",
    2177           0 :           name);
    2178           0 :     } else {
    2179           0 :       throwEnvoyExceptionOrPanic(fmt::format(
    2180           0 :           "The filter {} doesn't support virtual host or route specific configurations", name));
    2181           0 :     }
    2182           0 :   }
    2183           0 :   return object;
    2184           0 : }
    2185             : 
    2186             : PerFilterConfigs::PerFilterConfigs(
    2187             :     const Protobuf::Map<std::string, ProtobufWkt::Any>& typed_configs,
    2188             :     Server::Configuration::ServerFactoryContext& factory_context,
    2189        4419 :     ProtobufMessage::ValidationVisitor& validator) {
    2190             : 
    2191        4419 :   std::string filter_config_type =
    2192        4419 :       envoy::config::route::v3::FilterConfig::default_instance().GetTypeName();
    2193             : 
    2194        4419 :   for (const auto& per_filter_config : typed_configs) {
    2195          54 :     const std::string& name = per_filter_config.first;
    2196          54 :     RouteSpecificFilterConfigConstSharedPtr config;
    2197             : 
    2198          54 :     if (TypeUtil::typeUrlToDescriptorFullName(per_filter_config.second.type_url()) ==
    2199          54 :         filter_config_type) {
    2200          32 :       envoy::config::route::v3::FilterConfig filter_config;
    2201          32 :       Envoy::Config::Utility::translateOpaqueConfig(per_filter_config.second, validator,
    2202          32 :                                                     filter_config);
    2203             : 
    2204             :       // The filter is marked as disabled explicitly and the config is ignored directly.
    2205          32 :       if (filter_config.disabled()) {
    2206          20 :         configs_.emplace(name, FilterConfig{nullptr, true});
    2207          20 :         continue;
    2208          20 :       }
    2209             : 
    2210             :       // If the field `config` is not configured, we treat it as configuration error.
    2211          12 :       if (!filter_config.has_config()) {
    2212           0 :         throwEnvoyExceptionOrPanic(
    2213           0 :             fmt::format("Empty route/virtual host per filter configuration for {} filter", name));
    2214           0 :       }
    2215             : 
    2216             :       // If the field `config` is configured but is empty, we treat the filter is enabled
    2217             :       // explicitly.
    2218          12 :       if (filter_config.config().type_url().empty()) {
    2219           0 :         configs_.emplace(name, FilterConfig{nullptr, false});
    2220           0 :         continue;
    2221           0 :       }
    2222             : 
    2223          12 :       config = createRouteSpecificFilterConfig(
    2224          12 :           name, filter_config.config(), filter_config.is_optional(), factory_context, validator);
    2225          22 :     } else {
    2226          22 :       config = createRouteSpecificFilterConfig(name, per_filter_config.second, false,
    2227          22 :                                                factory_context, validator);
    2228          22 :     }
    2229             : 
    2230             :     // If a filter is explicitly configured we treat it as enabled.
    2231             :     // The config may be nullptr because the filter could be optional.
    2232          34 :     configs_.emplace(name, FilterConfig{std::move(config), false});
    2233          34 :   }
    2234        4419 : }
    2235             : 
    2236         120 : const RouteSpecificFilterConfig* PerFilterConfigs::get(const std::string& name) const {
    2237         120 :   auto it = configs_.find(name);
    2238         120 :   return it == configs_.end() ? nullptr : it->second.config_.get();
    2239         120 : }
    2240             : 
    2241        1281 : absl::optional<bool> PerFilterConfigs::disabled(absl::string_view name) const {
    2242             :   // Quick exit if there are no configs.
    2243        1281 :   if (configs_.empty()) {
    2244        1281 :     return absl::nullopt;
    2245        1281 :   }
    2246             : 
    2247           0 :   const auto it = configs_.find(name);
    2248           0 :   return it != configs_.end() ? absl::optional<bool>{it->second.disabled_} : absl::nullopt;
    2249        1281 : }
    2250             : 
    2251             : Matcher::ActionFactoryCb RouteMatchActionFactory::createActionFactoryCb(
    2252             :     const Protobuf::Message& config, RouteActionContext& context,
    2253           3 :     ProtobufMessage::ValidationVisitor& validation_visitor) {
    2254           3 :   const auto& route_config =
    2255           3 :       MessageUtil::downcastAndValidate<const envoy::config::route::v3::Route&>(config,
    2256           3 :                                                                                validation_visitor);
    2257           3 :   auto route = createAndValidateRoute(route_config, context.vhost, context.factory_context,
    2258           3 :                                       validation_visitor, absl::nullopt);
    2259             : 
    2260           3 :   return [route]() { return std::make_unique<RouteMatchAction>(route); };
    2261           3 : }
    2262             : REGISTER_FACTORY(RouteMatchActionFactory, Matcher::ActionFactory<RouteActionContext>);
    2263             : 
    2264             : Matcher::ActionFactoryCb RouteListMatchActionFactory::createActionFactoryCb(
    2265             :     const Protobuf::Message& config, RouteActionContext& context,
    2266           0 :     ProtobufMessage::ValidationVisitor& validation_visitor) {
    2267           0 :   const auto& route_config =
    2268           0 :       MessageUtil::downcastAndValidate<const envoy::config::route::v3::RouteList&>(
    2269           0 :           config, validation_visitor);
    2270             : 
    2271           0 :   std::vector<RouteEntryImplBaseConstSharedPtr> routes;
    2272           0 :   for (const auto& route : route_config.routes()) {
    2273           0 :     routes.emplace_back(createAndValidateRoute(route, context.vhost, context.factory_context,
    2274           0 :                                                validation_visitor, absl::nullopt));
    2275           0 :   }
    2276           0 :   return [routes]() { return std::make_unique<RouteListMatchAction>(routes); };
    2277           0 : }
    2278             : REGISTER_FACTORY(RouteListMatchActionFactory, Matcher::ActionFactory<RouteActionContext>);
    2279             : 
    2280             : } // namespace Router
    2281             : } // namespace Envoy

Generated by: LCOV version 1.15