1
#pragma once
2

            
3
#include <cstdint>
4
#include <string>
5

            
6
#include "envoy/common/random_generator.h"
7
#include "envoy/event/timer.h"
8
#include "envoy/http/codec.h"
9
#include "envoy/http/header_map.h"
10
#include "envoy/router/context.h"
11
#include "envoy/router/router.h"
12
#include "envoy/runtime/runtime.h"
13
#include "envoy/stream_info/stream_info.h"
14
#include "envoy/upstream/upstream.h"
15

            
16
#include "source/common/common/backoff_strategy.h"
17
#include "source/common/http/header_utility.h"
18

            
19
#include "absl/strings/string_view.h"
20
#include "absl/types/optional.h"
21

            
22
namespace Envoy {
23
namespace Router {
24

            
25
/**
26
 * Wraps retry state for the router.
27
 */
28
class RetryStateImpl : public RetryState {
29
public:
30
  static std::unique_ptr<RetryStateImpl>
31
  create(const RetryPolicy& route_policy, Http::RequestHeaderMap& request_headers,
32
         const Upstream::ClusterInfo& cluster, const VirtualCluster* vcluster,
33
         RouteStatsContextOptRef route_stats_context,
34
         Server::Configuration::CommonFactoryContext& context, Event::Dispatcher& dispatcher,
35
         Upstream::ResourcePriority priority);
36
  ~RetryStateImpl() override;
37

            
38
  /**
39
   * Returns the RetryPolicy extracted from the x-envoy-retry-on header.
40
   * @param config is the value of the header.
41
   * @return std::pair<uint32_t, bool> the uint32_t is a bitset representing the
42
   *         valid retry policies in @param config. The bool is TRUE iff all the
43
   *         policies specified in @param config are valid.
44
   */
45
  static std::pair<uint32_t, bool> parseRetryOn(absl::string_view config);
46

            
47
  /**
48
   * Returns the RetryPolicy extracted from the x-envoy-retry-grpc-on header.
49
   * @param config is the value of the header.
50
   * @return std::pair<uint32_t, bool> the uint32_t is a bitset representing the
51
   *         valid retry policies in @param config. The bool is TRUE iff all the
52
   *         policies specified in @param config are valid.
53
   */
54
  static std::pair<uint32_t, bool> parseRetryGrpcOn(absl::string_view retry_grpc_on_header);
55

            
56
  // Router::RetryState
57
1517
  bool enabled() override { return retry_on_ != 0; }
58
  absl::optional<std::chrono::milliseconds>
59
  parseResetInterval(const Http::ResponseHeaderMap& response_headers) const override;
60
  RetryStatus shouldRetryHeaders(const Http::ResponseHeaderMap& response_headers,
61
                                 const Http::RequestHeaderMap& original_request,
62
                                 DoRetryHeaderCallback callback) override;
63
  // Returns if the retry policy would retry the passed headers and how. Does not
64
  // take into account circuit breaking or remaining tries.
65
  RetryDecision wouldRetryFromHeaders(const Http::ResponseHeaderMap& response_headers,
66
                                      const Http::RequestHeaderMap& original_request,
67
                                      bool& disable_early_data) override;
68
  RetryStatus shouldRetryReset(Http::StreamResetReason reset_reason, Http3Used http3_used,
69
                               DoRetryResetCallback callback,
70
                               bool upstream_request_started) override;
71
  RetryStatus shouldHedgeRetryPerTryTimeout(DoRetryCallback callback) override;
72

            
73
3183
  void onHostAttempted(Upstream::HostDescriptionConstSharedPtr host) override {
74
3183
    std::for_each(retry_host_predicates_.begin(), retry_host_predicates_.end(),
75
3183
                  [&host](auto predicate) { predicate->onHostAttempted(host); });
76
3183
    if (retry_priority_) {
77
18
      retry_priority_->onHostAttempted(host);
78
18
    }
79
3183
  }
80

            
81
207
  bool shouldSelectAnotherHost(const Upstream::Host& host) override {
82
207
    return std::any_of(
83
207
        retry_host_predicates_.begin(), retry_host_predicates_.end(),
84
207
        [&host](auto predicate) { return predicate->shouldSelectAnotherHost(host); });
85
207
  }
86

            
87
  const Upstream::HealthyAndDegradedLoad& priorityLoadForRetry(
88
      StreamInfo::StreamInfo* stream_info, const Upstream::PrioritySet& priority_set,
89
      const Upstream::HealthyAndDegradedLoad& original_priority_load,
90
207
      const Upstream::RetryPriority::PriorityMappingFunc& priority_mapping_func) override {
91
207
    if (!retry_priority_) {
92
198
      return original_priority_load;
93
198
    }
94
9
    return retry_priority_->determinePriorityLoad(stream_info, priority_set, original_priority_load,
95
9
                                                  priority_mapping_func);
96
207
  }
97

            
98
200
  uint32_t hostSelectionMaxAttempts() const override { return host_selection_max_attempts_; }
99

            
100
3169
  bool isAutomaticallyConfiguredForHttp3() const { return auto_configured_for_http3_; }
101

            
102
private:
103
  RetryStateImpl(const RetryPolicy& route_policy, Http::RequestHeaderMap& request_headers,
104
                 const Upstream::ClusterInfo& cluster, const VirtualCluster* vcluster,
105
                 RouteStatsContextOptRef route_stats_context,
106
                 Server::Configuration::CommonFactoryContext& context,
107
                 Event::Dispatcher& dispatcher, Upstream::ResourcePriority priority,
108
                 bool auto_configured_for_http3);
109

            
110
  void enableBackoffTimer();
111
  void resetRetry();
112
  // Returns if the retry policy would retry the reset and how. Does not
113
  // take into account circuit breaking or remaining tries.
114
  // disable_http3: populated to tell the caller whether to disable http3 or not when the return
115
  // value indicates retry.
116
  RetryDecision wouldRetryFromReset(const Http::StreamResetReason reset_reason,
117
                                    Http3Used http3_used, bool& disable_http3,
118
                                    bool upstream_request_started);
119
  RetryStatus shouldRetry(RetryDecision would_retry, DoRetryCallback callback);
120

            
121
  const Upstream::ClusterInfo& cluster_;
122
  const VirtualCluster* vcluster_;
123
  RouteStatsContextOptRef route_stats_context_;
124
  Runtime::Loader& runtime_;
125
  Random::RandomGenerator& random_;
126
  Event::Dispatcher& dispatcher_;
127
  TimeSource& time_source_;
128
  DoRetryCallback backoff_callback_;
129
  Event::SchedulableCallbackPtr next_loop_callback_;
130
  Event::TimerPtr retry_timer_;
131
  BackOffStrategyPtr backoff_strategy_;
132
  BackOffStrategyPtr ratelimited_backoff_strategy_{};
133
  std::vector<Upstream::RetryHostPredicateSharedPtr> retry_host_predicates_;
134
  Upstream::RetryPrioritySharedPtr retry_priority_;
135
  std::vector<uint32_t> retriable_status_codes_;
136
  std::vector<Http::HeaderMatcherSharedPtr> retriable_headers_;
137
  std::vector<ResetHeaderParserSharedPtr> reset_headers_{};
138
  std::chrono::milliseconds reset_max_interval_{};
139

            
140
  // Keep small members (bools, enums and int32s) at the end of class, to reduce alignment overhead.
141
  uint32_t retry_on_{};
142
  uint32_t retries_remaining_{};
143
  uint32_t host_selection_max_attempts_;
144
  Upstream::ResourcePriority priority_;
145
  const bool auto_configured_for_http3_{};
146
};
147

            
148
} // namespace Router
149
} // namespace Envoy