1
#include "source/common/router/retry_policy_impl.h"
2

            
3
#include <memory>
4

            
5
#include "source/common/config/utility.h"
6
#include "source/common/router/reset_header_parser.h"
7
#include "source/common/router/retry_state_impl.h"
8
#include "source/common/upstream/retry_factory.h"
9

            
10
namespace Envoy {
11
namespace Router {
12

            
13
absl::StatusOr<std::shared_ptr<RetryPolicyImpl>>
14
RetryPolicyImpl::create(const envoy::config::route::v3::RetryPolicy& retry_policy,
15
                        ProtobufMessage::ValidationVisitor& validation_visitor,
16
229
                        Server::Configuration::CommonFactoryContext& common_context) {
17
229
  absl::Status creation_status = absl::OkStatus();
18
229
  auto ret = std::shared_ptr<RetryPolicyImpl>(
19
229
      new RetryPolicyImpl(retry_policy, validation_visitor, common_context, creation_status));
20
229
  RETURN_IF_NOT_OK(creation_status);
21
227
  return ret;
22
229
}
23

            
24
RetryPolicyConstSharedPtr RetryPolicyImpl::DefaultRetryPolicy = std::make_shared<RetryPolicyImpl>();
25

            
26
RetryPolicyImpl::RetryPolicyImpl(const envoy::config::route::v3::RetryPolicy& retry_policy,
27
                                 ProtobufMessage::ValidationVisitor& validation_visitor,
28
                                 Server::Configuration::CommonFactoryContext& common_context,
29
                                 absl::Status& creation_status)
30
229
    : retriable_headers_(Http::HeaderUtility::buildHeaderMatcherVector(
31
229
          retry_policy.retriable_headers(), common_context)),
32
229
      retriable_request_headers_(Http::HeaderUtility::buildHeaderMatcherVector(
33
229
          retry_policy.retriable_request_headers(), common_context)),
34
229
      validation_visitor_(&validation_visitor) {
35
229
  per_try_timeout_ =
36
229
      std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(retry_policy, per_try_timeout, 0));
37
229
  per_try_idle_timeout_ =
38
229
      std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(retry_policy, per_try_idle_timeout, 0));
39
229
  num_retries_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(retry_policy, num_retries, 1);
40
229
  retry_on_ = RetryStateImpl::parseRetryOn(retry_policy.retry_on()).first;
41
229
  retry_on_ |= RetryStateImpl::parseRetryGrpcOn(retry_policy.retry_on()).first;
42

            
43
229
  for (const auto& host_predicate : retry_policy.retry_host_predicate()) {
44
10
    auto& factory = Envoy::Config::Utility::getAndCheckFactory<Upstream::RetryHostPredicateFactory>(
45
10
        host_predicate);
46
10
    auto config = Envoy::Config::Utility::translateToFactoryConfig(host_predicate,
47
10
                                                                   validation_visitor, factory);
48
10
    retry_host_predicate_configs_.emplace_back(factory, std::move(config));
49
10
  }
50

            
51
229
  const auto& retry_priority = retry_policy.retry_priority();
52
229
  if (!retry_priority.name().empty()) {
53
20
    auto& factory =
54
20
        Envoy::Config::Utility::getAndCheckFactory<Upstream::RetryPriorityFactory>(retry_priority);
55
20
    retry_priority_config_ =
56
20
        std::make_pair(&factory, Envoy::Config::Utility::translateToFactoryConfig(
57
20
                                     retry_priority, validation_visitor, factory));
58
20
  }
59

            
60
229
  Upstream::RetryExtensionFactoryContextImpl factory_context(common_context.singletonManager());
61
229
  for (const auto& options_predicate : retry_policy.retry_options_predicates()) {
62
3
    auto& factory =
63
3
        Envoy::Config::Utility::getAndCheckFactory<Upstream::RetryOptionsPredicateFactory>(
64
3
            options_predicate);
65
3
    retry_options_predicates_.emplace_back(
66
3
        factory.createOptionsPredicate(*Envoy::Config::Utility::translateToFactoryConfig(
67
3
                                           options_predicate, validation_visitor, factory),
68
3
                                       factory_context));
69
3
  }
70

            
71
229
  auto host_selection_attempts = retry_policy.host_selection_retry_max_attempts();
72
229
  if (host_selection_attempts) {
73
    host_selection_attempts_ = host_selection_attempts;
74
  }
75

            
76
229
  for (auto code : retry_policy.retriable_status_codes()) {
77
8
    retriable_status_codes_.emplace_back(code);
78
8
  }
79

            
80
229
  if (retry_policy.has_retry_back_off()) {
81
82
    base_interval_ = std::chrono::milliseconds(
82
82
        PROTOBUF_GET_MS_REQUIRED(retry_policy.retry_back_off(), base_interval));
83
82
    if ((*base_interval_).count() < 1) {
84
1
      base_interval_ = std::chrono::milliseconds(1);
85
1
    }
86

            
87
82
    max_interval_ = PROTOBUF_GET_OPTIONAL_MS(retry_policy.retry_back_off(), max_interval);
88
82
    if (max_interval_) {
89
      // Apply the same rounding to max interval in case both are set to sub-millisecond values.
90
63
      if ((*max_interval_).count() < 1) {
91
1
        max_interval_ = std::chrono::milliseconds(1);
92
1
      }
93

            
94
63
      if ((*max_interval_).count() < (*base_interval_).count()) {
95
2
        creation_status = absl::InvalidArgumentError(
96
2
            "retry_policy.max_interval must greater than or equal to the base_interval");
97
2
        return;
98
2
      }
99
63
    }
100
82
  }
101

            
102
227
  if (retry_policy.has_rate_limited_retry_back_off()) {
103
4
    reset_headers_ = ResetHeaderParserImpl::buildResetHeaderParserVector(
104
4
        retry_policy.rate_limited_retry_back_off().reset_headers());
105

            
106
4
    absl::optional<std::chrono::milliseconds> reset_max_interval =
107
4
        PROTOBUF_GET_OPTIONAL_MS(retry_policy.rate_limited_retry_back_off(), max_interval);
108
4
    if (reset_max_interval.has_value()) {
109
4
      std::chrono::milliseconds max_interval = reset_max_interval.value();
110
4
      if (max_interval.count() < 1) {
111
1
        max_interval = std::chrono::milliseconds(1);
112
1
      }
113
4
      reset_max_interval_ = max_interval;
114
4
    }
115
4
  }
116
227
}
117

            
118
3171
std::vector<Upstream::RetryHostPredicateSharedPtr> RetryPolicyImpl::retryHostPredicates() const {
119
3171
  std::vector<Upstream::RetryHostPredicateSharedPtr> predicates;
120
3171
  predicates.reserve(retry_host_predicate_configs_.size());
121
3171
  for (const auto& config : retry_host_predicate_configs_) {
122
12
    predicates.emplace_back(config.first.createHostPredicate(*config.second, num_retries_));
123
12
  }
124

            
125
3171
  return predicates;
126
3171
}
127

            
128
3171
Upstream::RetryPrioritySharedPtr RetryPolicyImpl::retryPriority() const {
129
3171
  if (retry_priority_config_.first == nullptr) {
130
3160
    return nullptr;
131
3160
  }
132

            
133
11
  return retry_priority_config_.first->createRetryPriority(*retry_priority_config_.second,
134
11
                                                           *validation_visitor_, num_retries_);
135
3171
}
136

            
137
} // namespace Router
138
} // namespace Envoy