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