/proc/self/cwd/source/common/router/router.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include <chrono> |
4 | | #include <cstdint> |
5 | | #include <functional> |
6 | | #include <memory> |
7 | | #include <optional> |
8 | | #include <string> |
9 | | |
10 | | #include "envoy/common/random_generator.h" |
11 | | #include "envoy/extensions/filters/http/router/v3/router.pb.h" |
12 | | #include "envoy/http/codec.h" |
13 | | #include "envoy/http/codes.h" |
14 | | #include "envoy/http/filter.h" |
15 | | #include "envoy/http/filter_factory.h" |
16 | | #include "envoy/http/hash_policy.h" |
17 | | #include "envoy/http/stateful_session.h" |
18 | | #include "envoy/local_info/local_info.h" |
19 | | #include "envoy/router/router_filter_interface.h" |
20 | | #include "envoy/router/shadow_writer.h" |
21 | | #include "envoy/runtime/runtime.h" |
22 | | #include "envoy/server/factory_context.h" |
23 | | #include "envoy/server/filter_config.h" |
24 | | #include "envoy/stats/scope.h" |
25 | | #include "envoy/stats/stats_macros.h" |
26 | | #include "envoy/stream_info/stream_info.h" |
27 | | #include "envoy/upstream/cluster_manager.h" |
28 | | |
29 | | #include "source/common/access_log/access_log_impl.h" |
30 | | #include "source/common/buffer/watermark_buffer.h" |
31 | | #include "source/common/common/cleanup.h" |
32 | | #include "source/common/common/hash.h" |
33 | | #include "source/common/common/hex.h" |
34 | | #include "source/common/common/linked_object.h" |
35 | | #include "source/common/common/logger.h" |
36 | | #include "source/common/config/utility.h" |
37 | | #include "source/common/config/well_known_names.h" |
38 | | #include "source/common/http/filter_chain_helper.h" |
39 | | #include "source/common/http/utility.h" |
40 | | #include "source/common/router/config_impl.h" |
41 | | #include "source/common/router/context_impl.h" |
42 | | #include "source/common/router/upstream_request.h" |
43 | | #include "source/common/stats/symbol_table.h" |
44 | | #include "source/common/stream_info/stream_info_impl.h" |
45 | | #include "source/common/upstream/load_balancer_impl.h" |
46 | | #include "source/common/upstream/upstream_factory_context_impl.h" |
47 | | |
48 | | namespace Envoy { |
49 | | namespace Router { |
50 | | |
51 | | /** |
52 | | * Struct definition for all router filter stats. @see stats_macros.h |
53 | | */ |
54 | | MAKE_STATS_STRUCT(FilterStats, StatNames, ALL_ROUTER_STATS); |
55 | | |
56 | | /** |
57 | | * Router filter utilities split out for ease of testing. |
58 | | */ |
59 | | class FilterUtility { |
60 | | public: |
61 | | struct HedgingParams { |
62 | | bool hedge_on_per_try_timeout_ : 1; |
63 | | }; |
64 | | |
65 | | class StrictHeaderChecker { |
66 | | public: |
67 | | struct HeaderCheckResult { |
68 | | bool valid_ = true; |
69 | | const Http::HeaderEntry* entry_; |
70 | | }; |
71 | | |
72 | | /** |
73 | | * Determine whether a given header's value passes the strict validation |
74 | | * defined for that header. |
75 | | * @param headers supplies the headers from which to get the target header. |
76 | | * @param target_header is the header to be validated. |
77 | | * @return HeaderCheckResult containing the entry for @param target_header |
78 | | * and valid_ set to FALSE if @param target_header is set to an |
79 | | * invalid value. If @param target_header doesn't appear in |
80 | | * @param headers, return a result with valid_ set to TRUE. |
81 | | */ |
82 | | static const HeaderCheckResult checkHeader(Http::RequestHeaderMap& headers, |
83 | | const Http::LowerCaseString& target_header); |
84 | | |
85 | | using ParseRetryFlagsFunc = std::function<std::pair<uint32_t, bool>(absl::string_view)>; |
86 | | |
87 | | private: |
88 | | static HeaderCheckResult hasValidRetryFields(const Http::HeaderEntry* header_entry, |
89 | 0 | const ParseRetryFlagsFunc& parse_fn) { |
90 | 0 | HeaderCheckResult r; |
91 | 0 | if (header_entry) { |
92 | 0 | const auto flags_and_validity = parse_fn(header_entry->value().getStringView()); |
93 | 0 | r.valid_ = flags_and_validity.second; |
94 | 0 | r.entry_ = header_entry; |
95 | 0 | } |
96 | 0 | return r; |
97 | 0 | } |
98 | | |
99 | 0 | static HeaderCheckResult isInteger(const Http::HeaderEntry* header_entry) { |
100 | 0 | HeaderCheckResult r; |
101 | 0 | if (header_entry) { |
102 | 0 | uint64_t out; |
103 | 0 | r.valid_ = absl::SimpleAtoi(header_entry->value().getStringView(), &out); |
104 | 0 | r.entry_ = header_entry; |
105 | 0 | } |
106 | 0 | return r; |
107 | 0 | } |
108 | | }; |
109 | | |
110 | | /** |
111 | | * Returns response_time / timeout, as a percentage as [0, 100]. Returns 0 |
112 | | * if there is no timeout. |
113 | | * @param response_time supplies the response time thus far. |
114 | | * @param timeout supplies the timeout to get the percentage of. |
115 | | * @return the percentage of timeout [0, 100] for stats use. |
116 | | */ |
117 | | static uint64_t percentageOfTimeout(const std::chrono::milliseconds response_time, |
118 | | const std::chrono::milliseconds timeout); |
119 | | |
120 | | /** |
121 | | * Set the :scheme header using the best information available. In order this is |
122 | | * - existing scheme header if valid |
123 | | * - x-forwarded-proto header if valid |
124 | | * - security of downstream connection |
125 | | */ |
126 | | static void setUpstreamScheme(Http::RequestHeaderMap& headers, bool downstream_secure); |
127 | | |
128 | | /** |
129 | | * Determine whether a request should be shadowed. |
130 | | * @param policy supplies the route's shadow policy. |
131 | | * @param runtime supplies the runtime to lookup the shadow key in. |
132 | | * @param stable_random supplies the random number to use when determining whether shadowing |
133 | | * should take place. |
134 | | * @return TRUE if shadowing should take place. |
135 | | */ |
136 | | static bool shouldShadow(const ShadowPolicy& policy, Runtime::Loader& runtime, |
137 | | uint64_t stable_random); |
138 | | |
139 | | /** |
140 | | * Determine the final timeout to use based on the route as well as the request headers. |
141 | | * @param route supplies the request route. |
142 | | * @param request_headers supplies the request headers. |
143 | | * @param insert_envoy_expected_request_timeout_ms insert |
144 | | * x-envoy-expected-request-timeout-ms? |
145 | | * @param grpc_request tells if the request is a gRPC request. |
146 | | * @return TimeoutData for both the global and per try timeouts. |
147 | | */ |
148 | | static TimeoutData finalTimeout(const RouteEntry& route, Http::RequestHeaderMap& request_headers, |
149 | | bool insert_envoy_expected_request_timeout_ms, bool grpc_request, |
150 | | bool per_try_timeout_hedging_enabled, |
151 | | bool respect_expected_rq_timeout); |
152 | | |
153 | | /** |
154 | | * Set the x-envoy-expected-request-timeout-ms and grpc-timeout headers if needed. |
155 | | * @param elapsed_time time elapsed since completion of the downstream request |
156 | | * @param timeout final TimeoutData to use for the request |
157 | | * @param request_headers the request headers to modify |
158 | | * @param insert_envoy_expected_request_timeout_ms insert |
159 | | * x-envoy-expected-request-timeout-ms? |
160 | | * @param grpc_request tells if the request is a gRPC request. |
161 | | * @param per_try_timeout_headging_enabled is request hedging enabled? |
162 | | */ |
163 | | static void setTimeoutHeaders(uint64_t elapsed_time, const TimeoutData& timeout, |
164 | | const RouteEntry& route, Http::RequestHeaderMap& request_headers, |
165 | | bool insert_envoy_expected_request_timeout_ms, bool grpc_request, |
166 | | bool per_try_timeout_hedging_enabled); |
167 | | |
168 | | /** |
169 | | * Try to parse a header entry that may have a timeout field |
170 | | * |
171 | | * @param header_timeout_entry header entry which may contain a timeout value. |
172 | | * @return result timeout value from header. It will return nullopt if parse failed. |
173 | | */ |
174 | | static absl::optional<std::chrono::milliseconds> |
175 | | tryParseHeaderTimeout(const Http::HeaderEntry& header_timeout_entry); |
176 | | |
177 | | /** |
178 | | * Try to set global timeout. |
179 | | * |
180 | | * @param header_timeout_entry header entry which may contain a timeout value. |
181 | | * @param timeout timeout data to set from header timeout entry. |
182 | | */ |
183 | | static void trySetGlobalTimeout(const Http::HeaderEntry& header_timeout_entry, |
184 | | TimeoutData& timeout); |
185 | | |
186 | | /** |
187 | | * Determine the final hedging settings after applying randomized behavior. |
188 | | * @param route supplies the request route. |
189 | | * @param request_headers supplies the request headers. |
190 | | * @return HedgingParams the final parameters to use for request hedging. |
191 | | */ |
192 | | static HedgingParams finalHedgingParams(const RouteEntry& route, |
193 | | Http::RequestHeaderMap& request_headers); |
194 | | }; |
195 | | |
196 | | /** |
197 | | * Configuration for the router filter. |
198 | | */ |
199 | | class FilterConfig : Http::FilterChainFactory { |
200 | | public: |
201 | | FilterConfig(Stats::StatName stat_prefix, const LocalInfo::LocalInfo& local_info, |
202 | | Stats::Scope& scope, Upstream::ClusterManager& cm, Runtime::Loader& runtime, |
203 | | Random::RandomGenerator& random, ShadowWriterPtr&& shadow_writer, |
204 | | bool emit_dynamic_stats, bool start_child_span, bool suppress_envoy_headers, |
205 | | bool respect_expected_rq_timeout, bool suppress_grpc_request_failure_code_stats, |
206 | | bool flush_upstream_log_on_upstream_stream, |
207 | | const Protobuf::RepeatedPtrField<std::string>& strict_check_headers, |
208 | | TimeSource& time_source, Http::Context& http_context, |
209 | | Router::Context& router_context) |
210 | | : router_context_(router_context), scope_(scope), local_info_(local_info), cm_(cm), |
211 | | runtime_(runtime), default_stats_(router_context_.statNames(), scope_, stat_prefix), |
212 | | async_stats_(router_context_.statNames(), scope, http_context.asyncClientStatPrefix()), |
213 | | random_(random), emit_dynamic_stats_(emit_dynamic_stats), |
214 | | start_child_span_(start_child_span), suppress_envoy_headers_(suppress_envoy_headers), |
215 | | respect_expected_rq_timeout_(respect_expected_rq_timeout), |
216 | | suppress_grpc_request_failure_code_stats_(suppress_grpc_request_failure_code_stats), |
217 | | flush_upstream_log_on_upstream_stream_(flush_upstream_log_on_upstream_stream), |
218 | | http_context_(http_context), zone_name_(local_info_.zoneStatName()), |
219 | 3.51k | shadow_writer_(std::move(shadow_writer)), time_source_(time_source) { |
220 | 3.51k | if (!strict_check_headers.empty()) { |
221 | 4 | strict_check_headers_ = std::make_unique<HeaderVector>(); |
222 | 4 | for (const auto& header : strict_check_headers) { |
223 | 4 | strict_check_headers_->emplace_back(Http::LowerCaseString(header)); |
224 | 4 | } |
225 | 4 | } |
226 | 3.51k | } |
227 | | |
228 | | FilterConfig(Stats::StatName stat_prefix, Server::Configuration::FactoryContext& context, |
229 | | ShadowWriterPtr&& shadow_writer, |
230 | | const envoy::extensions::filters::http::router::v3::Router& config); |
231 | | |
232 | | bool createFilterChain( |
233 | | Http::FilterChainManager& manager, bool only_create_if_configured = false, |
234 | 2.40k | const Http::FilterChainOptions& options = Http::EmptyFilterChainOptions{}) const override { |
235 | | // Currently there is no default filter chain, so only_create_if_configured true doesn't make |
236 | | // sense. |
237 | 2.40k | ASSERT(!only_create_if_configured); |
238 | 2.40k | if (upstream_http_filter_factories_.empty()) { |
239 | 2.40k | return false; |
240 | 2.40k | } |
241 | 0 | Http::FilterChainUtility::createFilterChainForFactories(manager, options, |
242 | 0 | upstream_http_filter_factories_); |
243 | 0 | return true; |
244 | 2.40k | } |
245 | | |
246 | | bool createUpgradeFilterChain(absl::string_view, const UpgradeMap*, |
247 | 0 | Http::FilterChainManager&) const override { |
248 | | // Upgrade filter chains not yet supported for upstream HTTP filters. |
249 | 0 | return false; |
250 | 0 | } |
251 | | |
252 | | using HeaderVector = std::vector<Http::LowerCaseString>; |
253 | | using HeaderVectorPtr = std::unique_ptr<HeaderVector>; |
254 | | |
255 | 0 | ShadowWriter& shadowWriter() { return *shadow_writer_; } |
256 | 2.40k | TimeSource& timeSource() { return time_source_; } |
257 | | |
258 | | Router::Context& router_context_; |
259 | | Stats::Scope& scope_; |
260 | | const LocalInfo::LocalInfo& local_info_; |
261 | | Upstream::ClusterManager& cm_; |
262 | | Runtime::Loader& runtime_; |
263 | | FilterStats default_stats_; |
264 | | FilterStats async_stats_; |
265 | | Random::RandomGenerator& random_; |
266 | | const bool emit_dynamic_stats_; |
267 | | const bool start_child_span_; |
268 | | const bool suppress_envoy_headers_; |
269 | | const bool respect_expected_rq_timeout_; |
270 | | const bool suppress_grpc_request_failure_code_stats_; |
271 | | // TODO(xyu-stripe): Make this a bitset to keep cluster memory footprint down. |
272 | | HeaderVectorPtr strict_check_headers_; |
273 | | const bool flush_upstream_log_on_upstream_stream_; |
274 | | absl::optional<std::chrono::milliseconds> upstream_log_flush_interval_; |
275 | | std::list<AccessLog::InstanceSharedPtr> upstream_logs_; |
276 | | Http::Context& http_context_; |
277 | | Stats::StatName zone_name_; |
278 | | Stats::StatName empty_stat_name_; |
279 | | std::unique_ptr<Server::Configuration::UpstreamFactoryContext> upstream_ctx_; |
280 | | Http::FilterChainUtility::FilterFactoriesList upstream_http_filter_factories_; |
281 | | |
282 | | private: |
283 | | ShadowWriterPtr shadow_writer_; |
284 | | TimeSource& time_source_; |
285 | | }; |
286 | | |
287 | | using FilterConfigSharedPtr = std::shared_ptr<FilterConfig>; |
288 | | |
289 | | class UpstreamRequest; |
290 | | using UpstreamRequestPtr = std::unique_ptr<UpstreamRequest>; |
291 | | |
292 | | /** |
293 | | * Service routing filter. |
294 | | */ |
295 | | class Filter : Logger::Loggable<Logger::Id::router>, |
296 | | public Http::StreamDecoderFilter, |
297 | | public Upstream::LoadBalancerContextBase, |
298 | | public RouterFilterInterface { |
299 | | public: |
300 | | Filter(FilterConfig& config, FilterStats& stats) |
301 | | : config_(config), stats_(stats), grpc_request_(false), exclude_http_code_stats_(false), |
302 | | downstream_response_started_(false), downstream_end_stream_(false), is_retry_(false), |
303 | | request_buffer_overflowed_(false), streaming_shadows_(Runtime::runtimeFeatureEnabled( |
304 | 2.88k | "envoy.reloadable_features.streaming_shadow")) {} |
305 | | |
306 | | ~Filter() override; |
307 | | |
308 | | static StreamInfo::ResponseFlag |
309 | | streamResetReasonToResponseFlag(Http::StreamResetReason reset_reason); |
310 | | |
311 | | // Http::StreamFilterBase |
312 | | void onDestroy() override; |
313 | | |
314 | | // Http::StreamDecoderFilter |
315 | | Http::FilterHeadersStatus decodeHeaders(Http::RequestHeaderMap& headers, |
316 | | bool end_stream) override; |
317 | | Http::FilterDataStatus decodeData(Buffer::Instance& data, bool end_stream) override; |
318 | | Http::FilterTrailersStatus decodeTrailers(Http::RequestTrailerMap& trailers) override; |
319 | | Http::FilterMetadataStatus decodeMetadata(Http::MetadataMap& metadata_map) override; |
320 | | void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& callbacks) override; |
321 | | |
322 | | // Upstream::LoadBalancerContext |
323 | 0 | absl::optional<uint64_t> computeHashKey() override { |
324 | 0 | if (route_entry_ && downstream_headers_) { |
325 | 0 | auto hash_policy = route_entry_->hashPolicy(); |
326 | 0 | if (hash_policy) { |
327 | 0 | return hash_policy->generateHash( |
328 | 0 | callbacks_->streamInfo().downstreamAddressProvider().remoteAddress().get(), |
329 | 0 | *downstream_headers_, |
330 | 0 | [this](const std::string& key, const std::string& path, std::chrono::seconds max_age, |
331 | 0 | Http::CookieAttributeRefVector attributes) { |
332 | 0 | return addDownstreamSetCookie(key, path, max_age, attributes); |
333 | 0 | }, |
334 | 0 | callbacks_->streamInfo().filterState()); |
335 | 0 | } |
336 | 0 | } |
337 | 0 | return {}; |
338 | 0 | } |
339 | 0 | const Router::MetadataMatchCriteria* metadataMatchCriteria() override { |
340 | 0 | if (route_entry_) { |
341 | | // Have we been called before? If so, there's no need to recompute because |
342 | | // by the time this method is called for the first time, route_entry_ should |
343 | | // not change anymore. |
344 | 0 | if (metadata_match_ != nullptr) { |
345 | 0 | return metadata_match_.get(); |
346 | 0 | } |
347 | | |
348 | | // The request's metadata, if present, takes precedence over the route's. |
349 | 0 | const auto& request_metadata = callbacks_->streamInfo().dynamicMetadata().filter_metadata(); |
350 | 0 | const auto filter_it = request_metadata.find(Envoy::Config::MetadataFilters::get().ENVOY_LB); |
351 | 0 | if (filter_it != request_metadata.end()) { |
352 | 0 | if (route_entry_->metadataMatchCriteria() != nullptr) { |
353 | 0 | metadata_match_ = |
354 | 0 | route_entry_->metadataMatchCriteria()->mergeMatchCriteria(filter_it->second); |
355 | 0 | } else { |
356 | 0 | metadata_match_ = std::make_unique<Router::MetadataMatchCriteriaImpl>(filter_it->second); |
357 | 0 | } |
358 | 0 | return metadata_match_.get(); |
359 | 0 | } |
360 | 0 | return route_entry_->metadataMatchCriteria(); |
361 | 0 | } |
362 | 0 | return nullptr; |
363 | 0 | } |
364 | 6.06k | const Network::Connection* downstreamConnection() const override { |
365 | 6.06k | return callbacks_->connection().ptr(); |
366 | 6.06k | } |
367 | 0 | const StreamInfo::StreamInfo* requestStreamInfo() const override { |
368 | 0 | return &callbacks_->streamInfo(); |
369 | 0 | } |
370 | 0 | const Http::RequestHeaderMap* downstreamHeaders() const override { return downstream_headers_; } |
371 | | |
372 | 2.39k | bool shouldSelectAnotherHost(const Upstream::Host& host) override { |
373 | | // We only care about host selection when performing a retry, at which point we consult the |
374 | | // RetryState to see if we're configured to avoid certain hosts during retries. |
375 | 2.39k | if (!is_retry_) { |
376 | 2.39k | return false; |
377 | 2.39k | } |
378 | | |
379 | 0 | ASSERT(retry_state_); |
380 | 0 | return retry_state_->shouldSelectAnotherHost(host); |
381 | 0 | } |
382 | | |
383 | | const Upstream::HealthyAndDegradedLoad& determinePriorityLoad( |
384 | | const Upstream::PrioritySet& priority_set, |
385 | | const Upstream::HealthyAndDegradedLoad& original_priority_load, |
386 | 2.39k | const Upstream::RetryPriority::PriorityMappingFunc& priority_mapping_func) override { |
387 | | // We only modify the priority load on retries. |
388 | 2.39k | if (!is_retry_) { |
389 | 2.39k | return original_priority_load; |
390 | 2.39k | } |
391 | 0 | return retry_state_->priorityLoadForRetry(priority_set, original_priority_load, |
392 | 0 | priority_mapping_func); |
393 | 2.39k | } |
394 | | |
395 | 2.39k | uint32_t hostSelectionRetryCount() const override { |
396 | 2.39k | if (!is_retry_) { |
397 | 2.39k | return 1; |
398 | 2.39k | } |
399 | 0 | return retry_state_->hostSelectionMaxAttempts(); |
400 | 2.39k | } |
401 | | |
402 | 2.39k | Network::Socket::OptionsSharedPtr upstreamSocketOptions() const override { |
403 | 2.39k | return (upstream_options_ != nullptr) ? upstream_options_ |
404 | 2.39k | : callbacks_->getUpstreamSocketOptions(); |
405 | 2.39k | } |
406 | | |
407 | 4.38k | Network::TransportSocketOptionsConstSharedPtr upstreamTransportSocketOptions() const override { |
408 | 4.38k | return transport_socket_options_; |
409 | 4.38k | } |
410 | | |
411 | 2.39k | absl::optional<OverrideHost> overrideHostToSelect() const override { |
412 | 2.39k | if (is_retry_) { |
413 | 0 | return {}; |
414 | 0 | } |
415 | 2.39k | return callbacks_->upstreamOverrideHost(); |
416 | 2.39k | } |
417 | | |
418 | | /** |
419 | | * Set a computed cookie to be sent with the downstream headers. |
420 | | * @param key supplies the size of the cookie |
421 | | * @param max_age the lifetime of the cookie |
422 | | * @param path the path of the cookie, or "" |
423 | | * @return std::string the value of the new cookie |
424 | | */ |
425 | | std::string addDownstreamSetCookie(const std::string& key, const std::string& path, |
426 | | std::chrono::seconds max_age, |
427 | 0 | Http::CookieAttributeRefVector attributes) { |
428 | | // The cookie value should be the same per connection so that if multiple |
429 | | // streams race on the same path, they all receive the same cookie. |
430 | | // Since the downstream port is part of the hashed value, multiple HTTP1 |
431 | | // connections can receive different cookies if they race on requests. |
432 | 0 | std::string value; |
433 | 0 | const Network::Connection* conn = downstreamConnection(); |
434 | | // Need to check for null conn if this is ever used by Http::AsyncClient in the future. |
435 | 0 | value = conn->connectionInfoProvider().remoteAddress()->asString() + |
436 | 0 | conn->connectionInfoProvider().localAddress()->asString(); |
437 | |
|
438 | 0 | const std::string cookie_value = Hex::uint64ToHex(HashUtil::xxHash64(value)); |
439 | 0 | downstream_set_cookies_.emplace_back( |
440 | 0 | Http::Utility::makeSetCookieValue(key, cookie_value, path, max_age, true, attributes)); |
441 | 0 | return cookie_value; |
442 | 0 | } |
443 | | |
444 | | // RouterFilterInterface |
445 | | void onUpstream1xxHeaders(Http::ResponseHeaderMapPtr&& headers, |
446 | | UpstreamRequest& upstream_request) override; |
447 | | void onUpstreamHeaders(uint64_t response_code, Http::ResponseHeaderMapPtr&& headers, |
448 | | UpstreamRequest& upstream_request, bool end_stream) override; |
449 | | void onUpstreamData(Buffer::Instance& data, UpstreamRequest& upstream_request, |
450 | | bool end_stream) override; |
451 | | void onUpstreamTrailers(Http::ResponseTrailerMapPtr&& trailers, |
452 | | UpstreamRequest& upstream_request) override; |
453 | | void onUpstreamMetadata(Http::MetadataMapPtr&& metadata_map) override; |
454 | | void onUpstreamReset(Http::StreamResetReason reset_reason, absl::string_view transport_failure, |
455 | | UpstreamRequest& upstream_request) override; |
456 | | void onUpstreamHostSelected(Upstream::HostDescriptionConstSharedPtr host, |
457 | | bool pool_success) override; |
458 | | void onPerTryTimeout(UpstreamRequest& upstream_request) override; |
459 | | void onPerTryIdleTimeout(UpstreamRequest& upstream_request) override; |
460 | | void onStreamMaxDurationReached(UpstreamRequest& upstream_request) override; |
461 | 72.4k | Http::StreamDecoderFilterCallbacks* callbacks() override { return callbacks_; } |
462 | 15.0k | Upstream::ClusterInfoConstSharedPtr cluster() override { return cluster_; } |
463 | 12.8k | FilterConfig& config() override { return config_; } |
464 | 4.32k | TimeoutData timeout() override { return timeout_; } |
465 | 2.32k | absl::optional<std::chrono::milliseconds> dynamicMaxStreamDuration() const override { |
466 | 2.32k | return dynamic_max_stream_duration_; |
467 | 2.32k | } |
468 | 20.4k | Http::RequestHeaderMap* downstreamHeaders() override { return downstream_headers_; } |
469 | 12.9k | Http::RequestTrailerMap* downstreamTrailers() override { return downstream_trailers_; } |
470 | 1.89k | bool downstreamResponseStarted() const override { return downstream_response_started_; } |
471 | 2.32k | bool downstreamEndStream() const override { return downstream_end_stream_; } |
472 | 0 | uint32_t attemptCount() const override { return attempt_count_; } |
473 | 0 | const std::list<UpstreamRequestPtr>& upstreamRequests() const { return upstream_requests_; } |
474 | | |
475 | 0 | TimeSource& timeSource() { return config_.timeSource(); } |
476 | 0 | const Route* route() const { return route_.get(); } |
477 | 0 | const FilterStats& stats() { return stats_; } |
478 | | |
479 | | protected: |
480 | 0 | void setRetryShadowBufferLimit(uint32_t retry_shadow_buffer_limit) { |
481 | 0 | ASSERT(retry_shadow_buffer_limit_ > retry_shadow_buffer_limit); |
482 | 0 | retry_shadow_buffer_limit_ = retry_shadow_buffer_limit; |
483 | 0 | } |
484 | | |
485 | | private: |
486 | | friend class UpstreamRequest; |
487 | | |
488 | | enum class TimeoutRetry { Yes, No }; |
489 | | |
490 | | void onPerTryTimeoutCommon(UpstreamRequest& upstream_request, Stats::Counter& error_counter, |
491 | | const std::string& response_code_details); |
492 | | Stats::StatName upstreamZone(Upstream::HostDescriptionConstSharedPtr upstream_host); |
493 | | void chargeUpstreamCode(uint64_t response_status_code, |
494 | | const Http::ResponseHeaderMap& response_headers, |
495 | | Upstream::HostDescriptionConstSharedPtr upstream_host, bool dropped); |
496 | | void chargeUpstreamCode(Http::Code code, Upstream::HostDescriptionConstSharedPtr upstream_host, |
497 | | bool dropped); |
498 | | void chargeUpstreamAbort(Http::Code code, bool dropped, UpstreamRequest& upstream_request); |
499 | | void cleanup(); |
500 | | virtual RetryStatePtr |
501 | | createRetryState(const RetryPolicy& policy, Http::RequestHeaderMap& request_headers, |
502 | | const Upstream::ClusterInfo& cluster, const VirtualCluster* vcluster, |
503 | | RouteStatsContextOptRef route_stats_context, Runtime::Loader& runtime, |
504 | | Random::RandomGenerator& random, Event::Dispatcher& dispatcher, |
505 | | TimeSource& time_source, Upstream::ResourcePriority priority) PURE; |
506 | | |
507 | | std::unique_ptr<GenericConnPool> |
508 | | createConnPool(Upstream::ThreadLocalCluster& thread_local_cluster); |
509 | | UpstreamRequestPtr createUpstreamRequest(); |
510 | | absl::optional<absl::string_view> getShadowCluster(const ShadowPolicy& shadow_policy, |
511 | | const Http::HeaderMap& headers) const; |
512 | | |
513 | | void maybeDoShadowing(); |
514 | | bool maybeRetryReset(Http::StreamResetReason reset_reason, UpstreamRequest& upstream_request, |
515 | | TimeoutRetry is_timeout_retry); |
516 | | uint32_t numRequestsAwaitingHeaders(); |
517 | | void onGlobalTimeout(); |
518 | | void onRequestComplete(); |
519 | | void onResponseTimeout(); |
520 | | // Handle an upstream request aborted due to a local timeout. |
521 | | void onSoftPerTryTimeout(); |
522 | | void onSoftPerTryTimeout(UpstreamRequest& upstream_request); |
523 | | void onUpstreamTimeoutAbort(StreamInfo::ResponseFlag response_flag, absl::string_view details); |
524 | | // Handle an "aborted" upstream request, meaning we didn't see response |
525 | | // headers (e.g. due to a reset). Handles recording stats and responding |
526 | | // downstream if appropriate. |
527 | | void onUpstreamAbort(Http::Code code, StreamInfo::ResponseFlag response_flag, |
528 | | absl::string_view body, bool dropped, absl::string_view details); |
529 | | void onUpstreamComplete(UpstreamRequest& upstream_request); |
530 | | // Reset all in-flight upstream requests. |
531 | | void resetAll(); |
532 | | // Reset all in-flight upstream requests that do NOT match the passed argument. This is used |
533 | | // if a "good" response comes back and we return downstream, so there is no point in waiting |
534 | | // for the remaining upstream requests to return. |
535 | | void resetOtherUpstreams(UpstreamRequest& upstream_request); |
536 | | void sendNoHealthyUpstreamResponse(); |
537 | | bool setupRedirect(const Http::ResponseHeaderMap& headers); |
538 | | bool convertRequestHeadersForInternalRedirect(Http::RequestHeaderMap& downstream_headers, |
539 | | const Http::HeaderEntry& internal_redirect, |
540 | | uint64_t status_code); |
541 | | void updateOutlierDetection(Upstream::Outlier::Result result, UpstreamRequest& upstream_request, |
542 | | absl::optional<uint64_t> code); |
543 | | void doRetry(bool can_send_early_data, bool can_use_http3, TimeoutRetry is_timeout_retry); |
544 | | void runRetryOptionsPredicates(UpstreamRequest& retriable_request); |
545 | | // Called immediately after a non-5xx header is received from upstream, performs stats accounting |
546 | | // and handle difference between gRPC and non-gRPC requests. |
547 | | void handleNon5xxResponseHeaders(absl::optional<Grpc::Status::GrpcStatus> grpc_status, |
548 | | UpstreamRequest& upstream_request, bool end_stream, |
549 | | uint64_t grpc_to_http_status); |
550 | 3.36k | Http::Context& httpContext() { return config_.http_context_; } |
551 | | |
552 | | RetryStatePtr retry_state_; |
553 | | FilterConfig& config_; |
554 | | Http::StreamDecoderFilterCallbacks* callbacks_{}; |
555 | | RouteConstSharedPtr route_; |
556 | | const RouteEntry* route_entry_{}; |
557 | | Upstream::ClusterInfoConstSharedPtr cluster_; |
558 | | std::unique_ptr<Stats::StatNameDynamicStorage> alt_stat_prefix_; |
559 | | const VirtualCluster* request_vcluster_{}; |
560 | | RouteStatsContextOptRef route_stats_context_; |
561 | | Event::TimerPtr response_timeout_; |
562 | | TimeoutData timeout_; |
563 | | std::list<UpstreamRequestPtr> upstream_requests_; |
564 | | FilterStats stats_; |
565 | | // Tracks which upstream request "wins" and will have the corresponding |
566 | | // response forwarded downstream |
567 | | UpstreamRequest* final_upstream_request_ = nullptr; |
568 | | Http::RequestHeaderMap* downstream_headers_{}; |
569 | | Http::RequestTrailerMap* downstream_trailers_{}; |
570 | | MonotonicTime downstream_request_complete_time_; |
571 | | MetadataMatchCriteriaConstPtr metadata_match_; |
572 | | std::function<void(Http::ResponseHeaderMap&)> modify_headers_; |
573 | | std::vector<std::reference_wrapper<const ShadowPolicy>> active_shadow_policies_{}; |
574 | | std::unique_ptr<Http::RequestHeaderMap> shadow_headers_; |
575 | | std::unique_ptr<Http::RequestTrailerMap> shadow_trailers_; |
576 | | // The stream lifetime configured by request header. |
577 | | absl::optional<std::chrono::milliseconds> dynamic_max_stream_duration_; |
578 | | // list of cookies to add to upstream headers |
579 | | std::vector<std::string> downstream_set_cookies_; |
580 | | |
581 | | Network::TransportSocketOptionsConstSharedPtr transport_socket_options_; |
582 | | Network::Socket::OptionsSharedPtr upstream_options_; |
583 | | // Set of ongoing shadow streams which have not yet received end stream. |
584 | | absl::flat_hash_set<Http::AsyncClient::OngoingRequest*> shadow_streams_; |
585 | | |
586 | | // Keep small members (bools and enums) at the end of class, to reduce alignment overhead. |
587 | | uint32_t retry_shadow_buffer_limit_{std::numeric_limits<uint32_t>::max()}; |
588 | | uint32_t attempt_count_{1}; |
589 | | uint32_t pending_retries_{0}; |
590 | | Http::Code timeout_response_code_ = Http::Code::GatewayTimeout; |
591 | | FilterUtility::HedgingParams hedging_params_; |
592 | | bool grpc_request_ : 1; |
593 | | bool exclude_http_code_stats_ : 1; |
594 | | bool downstream_response_started_ : 1; |
595 | | bool downstream_end_stream_ : 1; |
596 | | bool is_retry_ : 1; |
597 | | bool include_attempt_count_in_request_ : 1; |
598 | | bool include_timeout_retry_header_in_request_ : 1; |
599 | | bool request_buffer_overflowed_ : 1; |
600 | | const bool streaming_shadows_ : 1; |
601 | | }; |
602 | | |
603 | | class ProdFilter : public Filter { |
604 | | public: |
605 | | using Filter::Filter; |
606 | | |
607 | | private: |
608 | | // Filter |
609 | | RetryStatePtr createRetryState(const RetryPolicy& policy, Http::RequestHeaderMap& request_headers, |
610 | | const Upstream::ClusterInfo& cluster, |
611 | | const VirtualCluster* vcluster, |
612 | | RouteStatsContextOptRef route_stats_context, |
613 | | Runtime::Loader& runtime, Random::RandomGenerator& random, |
614 | | Event::Dispatcher& dispatcher, TimeSource& time_source, |
615 | | Upstream::ResourcePriority priority) override; |
616 | | }; |
617 | | |
618 | | } // namespace Router |
619 | | } // namespace Envoy |