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