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