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