LCOV - code coverage report
Current view: top level - source/common/router - retry_state_impl.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 42 347 12.1 %
Date: 2024-01-05 06:35:25 Functions: 3 18 16.7 %

          Line data    Source code
       1             : #include "source/common/router/retry_state_impl.h"
       2             : 
       3             : #include <chrono>
       4             : #include <cstdint>
       5             : #include <string>
       6             : #include <vector>
       7             : 
       8             : #include "envoy/config/route/v3/route_components.pb.h"
       9             : 
      10             : #include "source/common/common/assert.h"
      11             : #include "source/common/common/utility.h"
      12             : #include "source/common/grpc/common.h"
      13             : #include "source/common/http/codes.h"
      14             : #include "source/common/http/headers.h"
      15             : #include "source/common/http/utility.h"
      16             : #include "source/common/runtime/runtime_features.h"
      17             : 
      18             : namespace Envoy {
      19             : namespace Router {
      20             : 
      21           0 : bool clusterSupportsHttp3AndTcpFallback(const Upstream::ClusterInfo& cluster) {
      22           0 :   return (cluster.features() & Upstream::ClusterInfo::Features::HTTP3) &&
      23             :          // USE_ALPN is only set when a TCP pool is also configured. Such cluster supports TCP
      24             :          // fallback.
      25           0 :          (cluster.features() & Upstream::ClusterInfo::Features::USE_ALPN);
      26           0 : }
      27             : 
      28             : std::unique_ptr<RetryStateImpl>
      29             : RetryStateImpl::create(const RetryPolicy& route_policy, Http::RequestHeaderMap& request_headers,
      30             :                        const Upstream::ClusterInfo& cluster, const VirtualCluster* vcluster,
      31             :                        RouteStatsContextOptRef route_stats_context, Runtime::Loader& runtime,
      32             :                        Random::RandomGenerator& random, Event::Dispatcher& dispatcher,
      33         251 :                        TimeSource& time_source, Upstream::ResourcePriority priority) {
      34         251 :   std::unique_ptr<RetryStateImpl> ret;
      35             : 
      36             :   // We short circuit here and do not bother with an allocation if there is no chance we will retry.
      37             :   // But for HTTP/3 0-RTT safe requests, which can be rejected because they are sent too early(425
      38             :   // response code), we want to give them a chance to retry as normal requests even though the retry
      39             :   // policy doesn't specify it. So always allocate retry state object.
      40         251 :   if (request_headers.EnvoyRetryOn() || request_headers.EnvoyRetryGrpcOn() ||
      41         251 :       route_policy.retryOn()) {
      42           0 :     ret.reset(new RetryStateImpl(route_policy, request_headers, cluster, vcluster,
      43           0 :                                  route_stats_context, runtime, random, dispatcher, time_source,
      44           0 :                                  priority, false));
      45         251 :   } else if ((cluster.features() & Upstream::ClusterInfo::Features::HTTP3) &&
      46         251 :              Http::Utility::isSafeRequest(request_headers)) {
      47           0 :     ret.reset(new RetryStateImpl(route_policy, request_headers, cluster, vcluster,
      48           0 :                                  route_stats_context, runtime, random, dispatcher, time_source,
      49           0 :                                  priority, true));
      50           0 :   }
      51             : 
      52             :   // Consume all retry related headers to avoid them being propagated to the upstream
      53         251 :   request_headers.removeEnvoyRetryOn();
      54         251 :   request_headers.removeEnvoyRetryGrpcOn();
      55         251 :   request_headers.removeEnvoyMaxRetries();
      56         251 :   request_headers.removeEnvoyHedgeOnPerTryTimeout();
      57         251 :   request_headers.removeEnvoyRetriableHeaderNames();
      58         251 :   request_headers.removeEnvoyRetriableStatusCodes();
      59         251 :   request_headers.removeEnvoyUpstreamRequestPerTryTimeoutMs();
      60             : 
      61         251 :   return ret;
      62         251 : }
      63             : 
      64             : RetryStateImpl::RetryStateImpl(const RetryPolicy& route_policy,
      65             :                                Http::RequestHeaderMap& request_headers,
      66             :                                const Upstream::ClusterInfo& cluster, const VirtualCluster* vcluster,
      67             :                                RouteStatsContextOptRef route_stats_context,
      68             :                                Runtime::Loader& runtime, Random::RandomGenerator& random,
      69             :                                Event::Dispatcher& dispatcher, TimeSource& time_source,
      70             :                                Upstream::ResourcePriority priority, bool auto_configured_for_http3)
      71             :     : cluster_(cluster), vcluster_(vcluster), route_stats_context_(route_stats_context),
      72             :       runtime_(runtime), random_(random), dispatcher_(dispatcher), time_source_(time_source),
      73             :       retry_host_predicates_(route_policy.retryHostPredicates()),
      74             :       retry_priority_(route_policy.retryPriority()),
      75             :       retriable_status_codes_(route_policy.retriableStatusCodes()),
      76             :       retriable_headers_(route_policy.retriableHeaders()),
      77             :       reset_headers_(route_policy.resetHeaders()),
      78             :       reset_max_interval_(route_policy.resetMaxInterval()), retry_on_(route_policy.retryOn()),
      79             :       retries_remaining_(route_policy.numRetries()), priority_(priority),
      80           0 :       auto_configured_for_http3_(auto_configured_for_http3) {
      81           0 :   if ((cluster.features() & Upstream::ClusterInfo::Features::HTTP3) &&
      82           0 :       Http::Utility::isSafeRequest(request_headers)) {
      83             :     // Because 0-RTT requests could be rejected because they are sent too early, and such requests
      84             :     // should always be retried, setup retry policy for 425 response code for all potential 0-RTT
      85             :     // requests even though the retry policy isn't configured to do so. Since 0-RTT safe requests
      86             :     // traditionally shouldn't have body, automatically retrying them will not cause extra
      87             :     // buffering. This will also enable retry if they are reset during connect.
      88           0 :     retry_on_ |= RetryPolicy::RETRY_ON_RETRIABLE_STATUS_CODES;
      89           0 :     retriable_status_codes_.push_back(static_cast<uint32_t>(Http::Code::TooEarly));
      90           0 :   }
      91           0 :   std::chrono::milliseconds base_interval(
      92           0 :       runtime_.snapshot().getInteger("upstream.base_retry_backoff_ms", 25));
      93           0 :   if (route_policy.baseInterval()) {
      94           0 :     base_interval = *route_policy.baseInterval();
      95           0 :   }
      96             : 
      97             :   // By default, cap the max interval to 10 times the base interval to ensure reasonable back-off
      98             :   // intervals.
      99           0 :   std::chrono::milliseconds max_interval = base_interval * 10;
     100           0 :   if (route_policy.maxInterval()) {
     101           0 :     max_interval = *route_policy.maxInterval();
     102           0 :   }
     103             : 
     104           0 :   backoff_strategy_ = std::make_unique<JitteredExponentialBackOffStrategy>(
     105           0 :       base_interval.count(), max_interval.count(), random_);
     106           0 :   host_selection_max_attempts_ = route_policy.hostSelectionMaxAttempts();
     107             : 
     108             :   // Merge in the headers.
     109           0 :   if (request_headers.EnvoyRetryOn()) {
     110           0 :     retry_on_ |= parseRetryOn(request_headers.getEnvoyRetryOnValue()).first;
     111           0 :   }
     112           0 :   if (request_headers.EnvoyRetryGrpcOn()) {
     113           0 :     retry_on_ |= parseRetryGrpcOn(request_headers.getEnvoyRetryGrpcOnValue()).first;
     114           0 :   }
     115             : 
     116           0 :   const auto& retriable_request_headers = route_policy.retriableRequestHeaders();
     117           0 :   if (!retriable_request_headers.empty()) {
     118             :     // If this route limits retries by request headers, make sure there is a match.
     119           0 :     bool request_header_match = false;
     120           0 :     for (const auto& retriable_header : retriable_request_headers) {
     121           0 :       if (retriable_header->matchesHeaders(request_headers)) {
     122           0 :         request_header_match = true;
     123           0 :         break;
     124           0 :       }
     125           0 :     }
     126             : 
     127           0 :     if (!request_header_match) {
     128           0 :       retry_on_ = 0;
     129           0 :     }
     130           0 :   }
     131           0 :   if (retry_on_ != 0 && request_headers.EnvoyMaxRetries()) {
     132           0 :     uint64_t temp;
     133           0 :     if (absl::SimpleAtoi(request_headers.getEnvoyMaxRetriesValue(), &temp)) {
     134             :       // The max retries header takes precedence if set.
     135           0 :       retries_remaining_ = temp;
     136           0 :     }
     137           0 :   }
     138             : 
     139           0 :   if (request_headers.EnvoyRetriableStatusCodes()) {
     140           0 :     for (const auto& code :
     141           0 :          StringUtil::splitToken(request_headers.getEnvoyRetriableStatusCodesValue(), ",")) {
     142           0 :       unsigned int out;
     143           0 :       if (absl::SimpleAtoi(code, &out)) {
     144           0 :         retriable_status_codes_.emplace_back(out);
     145           0 :       }
     146           0 :     }
     147           0 :   }
     148             : 
     149           0 :   if (request_headers.EnvoyRetriableHeaderNames()) {
     150             :     // Retriable headers in the configuration are specified via HeaderMatcher.
     151             :     // Giving the same flexibility via request header would require the user
     152             :     // to provide HeaderMatcher serialized into a string. To avoid this extra
     153             :     // complexity we only support name-only header matchers via request
     154             :     // header. Anything more sophisticated needs to be provided via config.
     155           0 :     for (const auto& header_name : StringUtil::splitToken(
     156           0 :              request_headers.EnvoyRetriableHeaderNames()->value().getStringView(), ",")) {
     157           0 :       envoy::config::route::v3::HeaderMatcher header_matcher;
     158           0 :       header_matcher.set_name(std::string(absl::StripAsciiWhitespace(header_name)));
     159           0 :       retriable_headers_.emplace_back(
     160           0 :           std::make_shared<Http::HeaderUtility::HeaderData>(header_matcher));
     161           0 :     }
     162           0 :   }
     163           0 : }
     164             : 
     165           0 : RetryStateImpl::~RetryStateImpl() { resetRetry(); }
     166             : 
     167           0 : void RetryStateImpl::enableBackoffTimer() {
     168           0 :   if (!retry_timer_) {
     169           0 :     retry_timer_ = dispatcher_.createTimer([this]() -> void { backoff_callback_(); });
     170           0 :   }
     171             : 
     172           0 :   if (ratelimited_backoff_strategy_ != nullptr) {
     173             :     // If we have a backoff strategy based on rate limit feedback from the response we use it.
     174           0 :     retry_timer_->enableTimer(
     175           0 :         std::chrono::milliseconds(ratelimited_backoff_strategy_->nextBackOffMs()));
     176             : 
     177             :     // The strategy is only valid for the response that sent the ratelimit reset header and cannot
     178             :     // be reused.
     179           0 :     ratelimited_backoff_strategy_.reset();
     180             : 
     181           0 :     cluster_.trafficStats()->upstream_rq_retry_backoff_ratelimited_.inc();
     182             : 
     183           0 :   } else {
     184             :     // Otherwise we use a fully jittered exponential backoff algorithm.
     185           0 :     retry_timer_->enableTimer(std::chrono::milliseconds(backoff_strategy_->nextBackOffMs()));
     186             : 
     187           0 :     cluster_.trafficStats()->upstream_rq_retry_backoff_exponential_.inc();
     188           0 :   }
     189           0 : }
     190             : 
     191          88 : std::pair<uint32_t, bool> RetryStateImpl::parseRetryOn(absl::string_view config) {
     192          88 :   uint32_t ret = 0;
     193          88 :   bool all_fields_valid = true;
     194          88 :   for (const auto& retry_on : StringUtil::splitToken(config, ",", false, true)) {
     195          66 :     if (retry_on == Http::Headers::get().EnvoyRetryOnValues._5xx) {
     196           0 :       ret |= RetryPolicy::RETRY_ON_5XX;
     197          66 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnValues.GatewayError) {
     198           0 :       ret |= RetryPolicy::RETRY_ON_GATEWAY_ERROR;
     199          66 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnValues.ConnectFailure) {
     200          66 :       ret |= RetryPolicy::RETRY_ON_CONNECT_FAILURE;
     201          66 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnValues.EnvoyRateLimited) {
     202           0 :       ret |= RetryPolicy::RETRY_ON_ENVOY_RATE_LIMITED;
     203           0 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnValues.Retriable4xx) {
     204           0 :       ret |= RetryPolicy::RETRY_ON_RETRIABLE_4XX;
     205           0 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnValues.RefusedStream) {
     206           0 :       ret |= RetryPolicy::RETRY_ON_REFUSED_STREAM;
     207           0 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnValues.RetriableStatusCodes) {
     208           0 :       ret |= RetryPolicy::RETRY_ON_RETRIABLE_STATUS_CODES;
     209           0 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnValues.RetriableHeaders) {
     210           0 :       ret |= RetryPolicy::RETRY_ON_RETRIABLE_HEADERS;
     211           0 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnValues.Reset) {
     212           0 :       ret |= RetryPolicy::RETRY_ON_RESET;
     213           0 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnValues.Http3PostConnectFailure) {
     214           0 :       ret |= RetryPolicy::RETRY_ON_HTTP3_POST_CONNECT_FAILURE;
     215           0 :     } else {
     216           0 :       all_fields_valid = false;
     217           0 :     }
     218          66 :   }
     219             : 
     220          88 :   return {ret, all_fields_valid};
     221          88 : }
     222             : 
     223          88 : std::pair<uint32_t, bool> RetryStateImpl::parseRetryGrpcOn(absl::string_view retry_grpc_on_header) {
     224          88 :   uint32_t ret = 0;
     225          88 :   bool all_fields_valid = true;
     226          88 :   for (const auto& retry_on : StringUtil::splitToken(retry_grpc_on_header, ",", false, true)) {
     227          66 :     if (retry_on == Http::Headers::get().EnvoyRetryOnGrpcValues.Cancelled) {
     228           0 :       ret |= RetryPolicy::RETRY_ON_GRPC_CANCELLED;
     229          66 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnGrpcValues.DeadlineExceeded) {
     230           0 :       ret |= RetryPolicy::RETRY_ON_GRPC_DEADLINE_EXCEEDED;
     231          66 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnGrpcValues.ResourceExhausted) {
     232           0 :       ret |= RetryPolicy::RETRY_ON_GRPC_RESOURCE_EXHAUSTED;
     233          66 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnGrpcValues.Unavailable) {
     234           0 :       ret |= RetryPolicy::RETRY_ON_GRPC_UNAVAILABLE;
     235          66 :     } else if (retry_on == Http::Headers::get().EnvoyRetryOnGrpcValues.Internal) {
     236           0 :       ret |= RetryPolicy::RETRY_ON_GRPC_INTERNAL;
     237          66 :     } else {
     238          66 :       all_fields_valid = false;
     239          66 :     }
     240          66 :   }
     241             : 
     242          88 :   return {ret, all_fields_valid};
     243          88 : }
     244             : 
     245             : absl::optional<std::chrono::milliseconds>
     246           0 : RetryStateImpl::parseResetInterval(const Http::ResponseHeaderMap& response_headers) const {
     247           0 :   for (const auto& reset_header : reset_headers_) {
     248           0 :     const auto& interval = reset_header->parseInterval(time_source_, response_headers);
     249           0 :     if (interval.has_value() && interval.value() <= reset_max_interval_) {
     250           0 :       return interval;
     251           0 :     }
     252           0 :   }
     253             : 
     254           0 :   return absl::nullopt;
     255           0 : }
     256             : 
     257           0 : void RetryStateImpl::resetRetry() {
     258           0 :   if (backoff_callback_ != nullptr) {
     259           0 :     cluster_.resourceManager(priority_).retries().dec();
     260           0 :     backoff_callback_ = nullptr;
     261           0 :   }
     262           0 :   if (next_loop_callback_ != nullptr) {
     263           0 :     cluster_.resourceManager(priority_).retries().dec();
     264           0 :     next_loop_callback_ = nullptr;
     265           0 :   }
     266           0 : }
     267             : 
     268           0 : RetryStatus RetryStateImpl::shouldRetry(RetryDecision would_retry, DoRetryCallback callback) {
     269             :   // If a callback is armed from a previous shouldRetry and we don't need to
     270             :   // retry this particular request, we can infer that we did a retry earlier
     271             :   // and it was successful.
     272           0 :   if ((backoff_callback_ || next_loop_callback_) && would_retry == RetryDecision::NoRetry) {
     273           0 :     cluster_.trafficStats()->upstream_rq_retry_success_.inc();
     274           0 :     if (vcluster_) {
     275           0 :       vcluster_->stats().upstream_rq_retry_success_.inc();
     276           0 :     }
     277           0 :     if (route_stats_context_.has_value()) {
     278           0 :       route_stats_context_->stats().upstream_rq_retry_success_.inc();
     279           0 :     }
     280           0 :   }
     281             : 
     282           0 :   resetRetry();
     283             : 
     284           0 :   if (would_retry == RetryDecision::NoRetry) {
     285           0 :     return RetryStatus::No;
     286           0 :   }
     287             : 
     288             :   // The request has exhausted the number of retries allotted to it by the retry policy configured
     289             :   // (or the x-envoy-max-retries header).
     290           0 :   if (retries_remaining_ == 0) {
     291           0 :     cluster_.trafficStats()->upstream_rq_retry_limit_exceeded_.inc();
     292           0 :     if (vcluster_) {
     293           0 :       vcluster_->stats().upstream_rq_retry_limit_exceeded_.inc();
     294           0 :     }
     295           0 :     if (route_stats_context_.has_value()) {
     296           0 :       route_stats_context_->stats().upstream_rq_retry_limit_exceeded_.inc();
     297           0 :     }
     298           0 :     return RetryStatus::NoRetryLimitExceeded;
     299           0 :   }
     300             : 
     301           0 :   retries_remaining_--;
     302             : 
     303           0 :   if (!cluster_.resourceManager(priority_).retries().canCreate()) {
     304           0 :     cluster_.trafficStats()->upstream_rq_retry_overflow_.inc();
     305           0 :     if (vcluster_) {
     306           0 :       vcluster_->stats().upstream_rq_retry_overflow_.inc();
     307           0 :     }
     308           0 :     if (route_stats_context_.has_value()) {
     309           0 :       route_stats_context_->stats().upstream_rq_retry_overflow_.inc();
     310           0 :     }
     311           0 :     return RetryStatus::NoOverflow;
     312           0 :   }
     313             : 
     314           0 :   if (!runtime_.snapshot().featureEnabled("upstream.use_retry", 100)) {
     315           0 :     return RetryStatus::No;
     316           0 :   }
     317             : 
     318           0 :   ASSERT(!backoff_callback_ && !next_loop_callback_);
     319           0 :   cluster_.resourceManager(priority_).retries().inc();
     320           0 :   cluster_.trafficStats()->upstream_rq_retry_.inc();
     321           0 :   if (vcluster_) {
     322           0 :     vcluster_->stats().upstream_rq_retry_.inc();
     323           0 :   }
     324           0 :   if (route_stats_context_.has_value()) {
     325           0 :     route_stats_context_->stats().upstream_rq_retry_.inc();
     326           0 :   }
     327           0 :   if (would_retry == RetryDecision::RetryWithBackoff) {
     328           0 :     backoff_callback_ = callback;
     329           0 :     enableBackoffTimer();
     330           0 :   } else {
     331           0 :     next_loop_callback_ = dispatcher_.createSchedulableCallback(callback);
     332           0 :     next_loop_callback_->scheduleCallbackNextIteration();
     333           0 :   }
     334           0 :   return RetryStatus::Yes;
     335           0 : }
     336             : 
     337             : RetryStatus RetryStateImpl::shouldRetryHeaders(const Http::ResponseHeaderMap& response_headers,
     338             :                                                const Http::RequestHeaderMap& original_request,
     339           0 :                                                DoRetryHeaderCallback callback) {
     340             :   // This may be overridden in wouldRetryFromHeaders().
     341           0 :   bool disable_early_data = false;
     342           0 :   const RetryDecision retry_decision =
     343           0 :       wouldRetryFromHeaders(response_headers, original_request, disable_early_data);
     344             : 
     345             :   // Yes, we will retry based on the headers - try to parse a rate limited reset interval from the
     346             :   // response.
     347           0 :   if (retry_decision == RetryDecision::RetryWithBackoff && !reset_headers_.empty()) {
     348           0 :     const auto backoff_interval = parseResetInterval(response_headers);
     349           0 :     if (backoff_interval.has_value() && (backoff_interval.value().count() > 1L)) {
     350           0 :       ratelimited_backoff_strategy_ = std::make_unique<JitteredLowerBoundBackOffStrategy>(
     351           0 :           backoff_interval.value().count(), random_);
     352           0 :     }
     353           0 :   }
     354             : 
     355           0 :   return shouldRetry(retry_decision,
     356           0 :                      [disable_early_data, callback]() { callback(disable_early_data); });
     357           0 : }
     358             : 
     359             : RetryStatus RetryStateImpl::shouldRetryReset(Http::StreamResetReason reset_reason,
     360           0 :                                              Http3Used http3_used, DoRetryResetCallback callback) {
     361             : 
     362             :   // Following wouldRetryFromReset() may override the value.
     363           0 :   bool disable_http3 = false;
     364           0 :   const RetryDecision retry_decision = wouldRetryFromReset(reset_reason, http3_used, disable_http3);
     365           0 :   return shouldRetry(retry_decision, [disable_http3, callback]() { callback(disable_http3); });
     366           0 : }
     367             : 
     368           0 : RetryStatus RetryStateImpl::shouldHedgeRetryPerTryTimeout(DoRetryCallback callback) {
     369             :   // A hedged retry on per try timeout is always retried if there are retries
     370             :   // left. NOTE: this is a bit different than non-hedged per try timeouts which
     371             :   // are only retried if the applicable retry policy specifies either
     372             :   // RETRY_ON_5XX or RETRY_ON_GATEWAY_ERROR. This is because these types of
     373             :   // retries are associated with a stream reset which is analogous to a gateway
     374             :   // error. When hedging on per try timeout is enabled, however, there is no
     375             :   // stream reset.
     376           0 :   return shouldRetry(RetryState::RetryDecision::RetryWithBackoff, callback);
     377           0 : }
     378             : 
     379             : RetryState::RetryDecision
     380             : RetryStateImpl::wouldRetryFromHeaders(const Http::ResponseHeaderMap& response_headers,
     381             :                                       const Http::RequestHeaderMap& original_request,
     382           0 :                                       bool& disable_early_data) {
     383             :   // A response that contains the x-envoy-ratelimited header comes from an upstream envoy.
     384             :   // We retry these only when the envoy-ratelimited policy is in effect.
     385           0 :   if (response_headers.EnvoyRateLimited() != nullptr) {
     386           0 :     return (retry_on_ & RetryPolicy::RETRY_ON_ENVOY_RATE_LIMITED) ? RetryDecision::RetryWithBackoff
     387           0 :                                                                   : RetryDecision::NoRetry;
     388           0 :   }
     389             : 
     390           0 :   uint64_t response_status = Http::Utility::getResponseStatus(response_headers);
     391             : 
     392           0 :   if (retry_on_ & RetryPolicy::RETRY_ON_5XX) {
     393           0 :     if (Http::CodeUtility::is5xx(response_status)) {
     394           0 :       return RetryDecision::RetryWithBackoff;
     395           0 :     }
     396           0 :   }
     397             : 
     398           0 :   if (retry_on_ & RetryPolicy::RETRY_ON_GATEWAY_ERROR) {
     399           0 :     if (Http::CodeUtility::isGatewayError(response_status)) {
     400           0 :       return RetryDecision::RetryWithBackoff;
     401           0 :     }
     402           0 :   }
     403             : 
     404           0 :   if ((retry_on_ & RetryPolicy::RETRY_ON_RETRIABLE_4XX)) {
     405           0 :     Http::Code code = static_cast<Http::Code>(response_status);
     406           0 :     if (code == Http::Code::Conflict) {
     407           0 :       return RetryDecision::RetryWithBackoff;
     408           0 :     }
     409           0 :   }
     410             : 
     411           0 :   if ((retry_on_ & RetryPolicy::RETRY_ON_RETRIABLE_STATUS_CODES)) {
     412           0 :     for (auto code : retriable_status_codes_) {
     413           0 :       if (response_status == code) {
     414           0 :         if (static_cast<Http::Code>(code) != Http::Code::TooEarly) {
     415           0 :           return RetryDecision::RetryWithBackoff;
     416           0 :         }
     417           0 :         if (original_request.get(Http::Headers::get().EarlyData).empty()) {
     418             :           // Retry if the downstream request wasn't received as early data. Otherwise, regardless if
     419             :           // the request was sent as early data in upstream or not, don't retry. Instead, forward
     420             :           // the response to downstream.
     421           0 :           disable_early_data = true;
     422           0 :           return RetryDecision::RetryImmediately;
     423           0 :         }
     424           0 :       }
     425           0 :     }
     426           0 :   }
     427             : 
     428           0 :   if (retry_on_ & RetryPolicy::RETRY_ON_RETRIABLE_HEADERS) {
     429           0 :     for (const auto& retriable_header : retriable_headers_) {
     430           0 :       if (retriable_header->matchesHeaders(response_headers)) {
     431           0 :         return RetryDecision::RetryWithBackoff;
     432           0 :       }
     433           0 :     }
     434           0 :   }
     435             : 
     436           0 :   if (retry_on_ &
     437           0 :       (RetryPolicy::RETRY_ON_GRPC_CANCELLED | RetryPolicy::RETRY_ON_GRPC_DEADLINE_EXCEEDED |
     438           0 :        RetryPolicy::RETRY_ON_GRPC_RESOURCE_EXHAUSTED | RetryPolicy::RETRY_ON_GRPC_UNAVAILABLE |
     439           0 :        RetryPolicy::RETRY_ON_GRPC_INTERNAL)) {
     440           0 :     absl::optional<Grpc::Status::GrpcStatus> status = Grpc::Common::getGrpcStatus(response_headers);
     441           0 :     if (status) {
     442           0 :       if ((status.value() == Grpc::Status::Canceled &&
     443           0 :            (retry_on_ & RetryPolicy::RETRY_ON_GRPC_CANCELLED)) ||
     444           0 :           (status.value() == Grpc::Status::DeadlineExceeded &&
     445           0 :            (retry_on_ & RetryPolicy::RETRY_ON_GRPC_DEADLINE_EXCEEDED)) ||
     446           0 :           (status.value() == Grpc::Status::ResourceExhausted &&
     447           0 :            (retry_on_ & RetryPolicy::RETRY_ON_GRPC_RESOURCE_EXHAUSTED)) ||
     448           0 :           (status.value() == Grpc::Status::Unavailable &&
     449           0 :            (retry_on_ & RetryPolicy::RETRY_ON_GRPC_UNAVAILABLE)) ||
     450           0 :           (status.value() == Grpc::Status::Internal &&
     451           0 :            (retry_on_ & RetryPolicy::RETRY_ON_GRPC_INTERNAL))) {
     452           0 :         return RetryDecision::RetryWithBackoff;
     453           0 :       }
     454           0 :     }
     455           0 :   }
     456             : 
     457           0 :   return RetryDecision::NoRetry;
     458           0 : }
     459             : 
     460             : RetryState::RetryDecision
     461             : RetryStateImpl::wouldRetryFromReset(const Http::StreamResetReason reset_reason,
     462           0 :                                     Http3Used http3_used, bool& disable_http3) {
     463           0 :   ASSERT(!disable_http3);
     464             :   // First check "never retry" conditions so we can short circuit (we never
     465             :   // retry if the reset reason is overflow).
     466           0 :   if (reset_reason == Http::StreamResetReason::Overflow) {
     467           0 :     return RetryDecision::NoRetry;
     468           0 :   }
     469             : 
     470           0 :   if (reset_reason == Http::StreamResetReason::LocalConnectionFailure ||
     471           0 :       reset_reason == Http::StreamResetReason::RemoteConnectionFailure ||
     472           0 :       reset_reason == Http::StreamResetReason::ConnectionTimeout) {
     473           0 :     if (http3_used != Http3Used::Unknown && clusterSupportsHttp3AndTcpFallback(cluster_)) {
     474             :       // Already got request encoder, so this must be a 0-RTT handshake failure. Retry
     475             :       // immediately.
     476             :       // TODO(danzh) consider making the retry configurable.
     477           0 :       ASSERT(http3_used == Http3Used::Yes,
     478           0 :              "0-RTT was attempted on non-Quic connection and failed.");
     479           0 :       return RetryDecision::RetryImmediately;
     480           0 :     }
     481           0 :     if ((retry_on_ & RetryPolicy::RETRY_ON_CONNECT_FAILURE)) {
     482             :       // This is a pool failure.
     483           0 :       return RetryDecision::RetryWithBackoff;
     484           0 :     }
     485           0 :   } else if (http3_used == Http3Used::Yes && clusterSupportsHttp3AndTcpFallback(cluster_) &&
     486           0 :              (retry_on_ & RetryPolicy::RETRY_ON_HTTP3_POST_CONNECT_FAILURE)) {
     487             :     // Retry any post-handshake failure immediately with http3 disabled if the
     488             :     // failed request was sent over Http/3.
     489           0 :     disable_http3 = true;
     490           0 :     return RetryDecision::RetryImmediately;
     491           0 :   }
     492             : 
     493           0 :   if (retry_on_ & RetryPolicy::RETRY_ON_RESET) {
     494           0 :     return RetryDecision::RetryWithBackoff;
     495           0 :   }
     496             : 
     497           0 :   if (retry_on_ & (RetryPolicy::RETRY_ON_5XX | RetryPolicy::RETRY_ON_GATEWAY_ERROR)) {
     498             :     // Currently we count an upstream reset as a "5xx" (since it will result in
     499             :     // one). With RETRY_ON_RESET we may eventually remove these policies.
     500           0 :     return RetryDecision::RetryWithBackoff;
     501           0 :   }
     502             : 
     503           0 :   if ((retry_on_ & RetryPolicy::RETRY_ON_REFUSED_STREAM) &&
     504           0 :       reset_reason == Http::StreamResetReason::RemoteRefusedStreamReset) {
     505           0 :     return RetryDecision::RetryWithBackoff;
     506           0 :   }
     507             : 
     508           0 :   return RetryDecision::NoRetry;
     509           0 : }
     510             : 
     511             : } // namespace Router
     512             : } // namespace Envoy

Generated by: LCOV version 1.15