Coverage Report

Created: 2023-11-12 09:30

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