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

            
3
#include <cstdint>
4
#include <memory>
5
#include <string>
6
#include <vector>
7

            
8
#include "envoy/config/core/v3/base.pb.h"
9
#include "envoy/config/route/v3/route_components.pb.h"
10

            
11
#include "source/common/common/assert.h"
12
#include "source/common/common/empty_string.h"
13
#include "source/common/config/metadata.h"
14
#include "source/common/config/utility.h"
15
#include "source/common/protobuf/utility.h"
16
#include "source/common/runtime/runtime_features.h"
17

            
18
namespace Envoy {
19
namespace Router {
20

            
21
namespace {
22
bool populateDescriptor(const std::vector<RateLimit::DescriptorProducerPtr>& actions,
23
                        std::vector<RateLimit::DescriptorEntry>& descriptor_entries,
24
                        const std::string& local_service_cluster,
25
138
                        const Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo& info) {
26
138
  bool result = true;
27
146
  for (const RateLimit::DescriptorProducerPtr& action : actions) {
28
146
    RateLimit::DescriptorEntry descriptor_entry;
29
146
    result = result &&
30
146
             action->populateDescriptor(descriptor_entry, local_service_cluster, headers, info);
31
146
    if (!result) {
32
20
      break;
33
20
    }
34
126
    if (!descriptor_entry.key_.empty()) {
35
121
      descriptor_entries.push_back(descriptor_entry);
36
121
    }
37
126
  }
38
138
  return result;
39
138
}
40

            
41
} // namespace
42

            
43
// Ratelimit::DescriptorProducer
44
bool MatchInputRateLimitDescriptor::populateDescriptor(RateLimit::DescriptorEntry& descriptor_entry,
45
                                                       const std::string&,
46
                                                       const Http::RequestHeaderMap& headers,
47
6
                                                       const StreamInfo::StreamInfo& info) const {
48
6
  Http::Matching::HttpMatchingDataImpl data(info);
49
6
  data.onRequestHeaders(headers);
50
6
  auto result = data_input_->get(data);
51
6
  auto string_data = result.stringData();
52
6
  if (!string_data) {
53
2
    return false;
54
2
  }
55
4
  if (!string_data->empty()) {
56
2
    descriptor_entry = {descriptor_key_, std::string(*string_data)};
57
2
  }
58
4
  return true;
59
6
}
60

            
61
bool DynamicMetadataRateLimitOverride::populateOverride(
62
4
    RateLimit::Descriptor& descriptor, const envoy::config::core::v3::Metadata* metadata) const {
63
4
  const Protobuf::Value& metadata_value =
64
4
      Envoy::Config::Metadata::metadataValue(metadata, metadata_key_);
65
4
  if (metadata_value.kind_case() != Protobuf::Value::kStructValue) {
66
2
    return false;
67
2
  }
68

            
69
2
  const auto& override_value = metadata_value.struct_value().fields();
70
2
  const auto& limit_it = override_value.find("requests_per_unit");
71
2
  const auto& unit_it = override_value.find("unit");
72
2
  if (limit_it != override_value.end() &&
73
2
      limit_it->second.kind_case() == Protobuf::Value::kNumberValue &&
74
2
      unit_it != override_value.end() &&
75
2
      unit_it->second.kind_case() == Protobuf::Value::kStringValue) {
76
2
    envoy::type::v3::RateLimitUnit unit;
77
2
    if (envoy::type::v3::RateLimitUnit_Parse(unit_it->second.string_value(), &unit)) {
78
1
      descriptor.limit_.emplace(RateLimit::RateLimitOverride{
79
1
          static_cast<uint32_t>(limit_it->second.number_value()), unit});
80
1
      return true;
81
1
    }
82
2
  }
83
1
  return false;
84
2
}
85

            
86
bool SourceClusterAction::populateDescriptor(RateLimit::DescriptorEntry& descriptor_entry,
87
                                             const std::string& local_service_cluster,
88
                                             const Http::RequestHeaderMap&,
89
6
                                             const StreamInfo::StreamInfo&) const {
90
6
  descriptor_entry = {"source_cluster", local_service_cluster};
91
6
  return true;
92
6
}
93

            
94
bool DestinationClusterAction::populateDescriptor(RateLimit::DescriptorEntry& descriptor_entry,
95
                                                  const std::string&, const Http::RequestHeaderMap&,
96
61
                                                  const StreamInfo::StreamInfo& info) const {
97
61
  if (info.route() == nullptr || info.route()->routeEntry() == nullptr) {
98
1
    return false;
99
1
  }
100
60
  descriptor_entry = {"destination_cluster", info.route()->routeEntry()->clusterName()};
101
60
  return true;
102
61
}
103

            
104
bool RequestHeadersAction::populateDescriptor(RateLimit::DescriptorEntry& descriptor_entry,
105
                                              const std::string&,
106
                                              const Http::RequestHeaderMap& headers,
107
88
                                              const StreamInfo::StreamInfo&) const {
108
88
  const auto header_value = headers.get(header_name_);
109

            
110
  // If header is not present in the request and if skip_if_absent is true skip this descriptor,
111
  // while calling rate limiting service. If skip_if_absent is false, do not call rate limiting
112
  // service.
113
88
  if (header_value.empty()) {
114
6
    return skip_if_absent_;
115
6
  }
116
  // TODO(https://github.com/envoyproxy/envoy/issues/13454): Potentially populate all header values.
117
82
  descriptor_entry = {descriptor_key_, std::string(header_value[0]->value().getStringView())};
118
82
  return true;
119
88
}
120

            
121
bool RemoteAddressAction::populateDescriptor(RateLimit::DescriptorEntry& descriptor_entry,
122
                                             const std::string&, const Http::RequestHeaderMap&,
123
15
                                             const StreamInfo::StreamInfo& info) const {
124
15
  const Network::Address::InstanceConstSharedPtr& remote_address =
125
15
      info.downstreamAddressProvider().remoteAddress();
126
15
  if (remote_address->type() != Network::Address::Type::Ip) {
127
2
    return false;
128
2
  }
129

            
130
13
  descriptor_entry = {"remote_address", remote_address->ip()->addressAsString()};
131

            
132
13
  return true;
133
15
}
134

            
135
bool MaskedRemoteAddressAction::populateDescriptor(RateLimit::DescriptorEntry& descriptor_entry,
136
                                                   const std::string&,
137
                                                   const Http::RequestHeaderMap&,
138
8
                                                   const StreamInfo::StreamInfo& info) const {
139
8
  const Network::Address::InstanceConstSharedPtr& remote_address =
140
8
      info.downstreamAddressProvider().remoteAddress();
141
8
  if (remote_address->type() != Network::Address::Type::Ip) {
142
    return false;
143
  }
144

            
145
8
  uint32_t mask_len = v4_prefix_mask_len_;
146
8
  if (remote_address->ip()->version() == Network::Address::IpVersion::v6) {
147
4
    mask_len = v6_prefix_mask_len_;
148
4
  }
149

            
150
  // TODO: increase the efficiency, avoid string transform back and forth
151
  // Note: we don't do validity checking for CIDR range here because we know
152
  // from addressAsString this is a valid address.
153
8
  Network::Address::CidrRange cidr_entry =
154
8
      *Network::Address::CidrRange::create(remote_address->ip()->addressAsString(), mask_len);
155
8
  descriptor_entry = {"masked_remote_address", cidr_entry.asString()};
156

            
157
8
  return true;
158
8
}
159

            
160
GenericKeyAction::GenericKeyAction(
161
    const envoy::config::route::v3::RateLimit::Action::GenericKey& action,
162
    std::unique_ptr<Formatter::FormatterImpl> formatter)
163
39
    : descriptor_value_(action.descriptor_value()),
164
39
      descriptor_key_(!action.descriptor_key().empty() ? action.descriptor_key() : "generic_key"),
165
39
      default_value_(action.default_value()), descriptor_formatter_(std::move(formatter)) {}
166

            
167
bool GenericKeyAction::populateDescriptor(RateLimit::DescriptorEntry& descriptor_entry,
168
                                          const std::string&, const Http::RequestHeaderMap& headers,
169
37
                                          const StreamInfo::StreamInfo& info) const {
170
37
  if (descriptor_formatter_ == nullptr) {
171
19
    descriptor_entry = {descriptor_key_, descriptor_value_};
172
19
    return true;
173
19
  }
174

            
175
18
  const std::string formatted_value = descriptor_formatter_->format({&headers}, info);
176
18
  if (!formatted_value.empty()) {
177
11
    descriptor_entry = {descriptor_key_, formatted_value};
178
11
  } else if (!default_value_.empty()) {
179
4
    descriptor_entry = {descriptor_key_, default_value_};
180
4
  } else {
181
    // If formatting resulted in empty string and no default_value, skip this descriptor
182
3
    return false;
183
3
  }
184
15
  return true;
185
18
}
186

            
187
MetaDataAction::MetaDataAction(const envoy::config::route::v3::RateLimit::Action::MetaData& action)
188
18
    : metadata_key_(action.metadata_key()), descriptor_key_(action.descriptor_key()),
189
18
      default_value_(action.default_value()), source_(action.source()),
190
18
      skip_if_absent_(action.skip_if_absent()) {}
191

            
192
MetaDataAction::MetaDataAction(
193
    const envoy::config::route::v3::RateLimit::Action::DynamicMetaData& action)
194
1
    : metadata_key_(action.metadata_key()), descriptor_key_(action.descriptor_key()),
195
1
      default_value_(action.default_value()),
196
1
      source_(envoy::config::route::v3::RateLimit::Action::MetaData::DYNAMIC),
197
1
      skip_if_absent_(false) {}
198

            
199
bool MetaDataAction::populateDescriptor(RateLimit::DescriptorEntry& descriptor_entry,
200
                                        const std::string&, const Http::RequestHeaderMap&,
201
19
                                        const StreamInfo::StreamInfo& info) const {
202
19
  const envoy::config::core::v3::Metadata* metadata_source;
203

            
204
19
  switch (source_) {
205
    PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
206
17
  case envoy::config::route::v3::RateLimit::Action::MetaData::DYNAMIC:
207
17
    metadata_source = &info.dynamicMetadata();
208
17
    break;
209
2
  case envoy::config::route::v3::RateLimit::Action::MetaData::ROUTE_ENTRY:
210
2
    metadata_source = &info.route()->metadata();
211
2
    break;
212
19
  }
213

            
214
19
  const std::string metadata_string_value =
215
19
      Envoy::Config::Metadata::metadataValue(metadata_source, metadata_key_).string_value();
216

            
217
19
  if (!metadata_string_value.empty()) {
218
7
    descriptor_entry = {descriptor_key_, metadata_string_value};
219
7
    return true;
220
12
  } else if (metadata_string_value.empty() && !default_value_.empty()) {
221
2
    descriptor_entry = {descriptor_key_, default_value_};
222
2
    return true;
223
2
  }
224

            
225
  // If the metadata key is not present and no default value is set, skip this
226
  // descriptor if skip_if_absent is true. If skip_if_absent is false, do not
227
  // call rate limiting service.
228
10
  return skip_if_absent_;
229
19
}
230

            
231
QueryParametersAction::QueryParametersAction(
232
    const envoy::config::route::v3::RateLimit::Action::QueryParameters& action)
233
10
    : query_param_name_(action.query_parameter_name()),
234
10
      descriptor_key_(!action.descriptor_key().empty() ? action.descriptor_key() : "query_param"),
235
10
      skip_if_absent_(action.skip_if_absent()) {}
236

            
237
bool QueryParametersAction::populateDescriptor(RateLimit::DescriptorEntry& descriptor_entry,
238
                                               const std::string&,
239
                                               const Http::RequestHeaderMap& headers,
240
10
                                               const StreamInfo::StreamInfo&) const {
241
10
  Http::Utility::QueryParamsMulti query_parameters =
242
10
      Http::Utility::QueryParamsMulti::parseAndDecodeQueryString(headers.getPathValue());
243

            
244
10
  const absl::optional<std::string> query_param_value =
245
10
      query_parameters.getFirstValue(query_param_name_);
246

            
247
  // If query parameter is not present and ``skip_if_absent`` is ``true``, skip this descriptor.
248
  // If ``skip_if_absent`` is ``false``, do not call rate limiting service.
249
10
  if (!query_param_value.has_value()) {
250
4
    return skip_if_absent_;
251
4
  }
252

            
253
6
  descriptor_entry = {descriptor_key_, query_param_value.value()};
254
6
  return true;
255
10
}
256

            
257
HeaderValueMatchAction::HeaderValueMatchAction(
258
    const envoy::config::route::v3::RateLimit::Action::HeaderValueMatch& action,
259
    Server::Configuration::CommonFactoryContext& context,
260
    std::unique_ptr<Formatter::FormatterImpl> formatter)
261
30
    : descriptor_value_(action.descriptor_value()),
262
30
      descriptor_key_(!action.descriptor_key().empty() ? action.descriptor_key() : "header_match"),
263
30
      default_value_(action.default_value()),
264
30
      expect_match_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(action, expect_match, true)),
265
30
      action_headers_(Http::HeaderUtility::buildHeaderDataVector(action.headers(), context)),
266
30
      descriptor_formatter_(std::move(formatter)) {}
267

            
268
bool HeaderValueMatchAction::populateDescriptor(RateLimit::DescriptorEntry& descriptor_entry,
269
                                                const std::string&,
270
                                                const Http::RequestHeaderMap& headers,
271
762
                                                const StreamInfo::StreamInfo& info) const {
272
762
  if (expect_match_ != Http::HeaderUtility::matchHeaders(headers, action_headers_)) {
273
734
    return false;
274
734
  }
275

            
276
28
  if (descriptor_formatter_ == nullptr) {
277
15
    descriptor_entry = {descriptor_key_, descriptor_value_};
278
15
    return true;
279
15
  }
280

            
281
13
  const std::string formatted_value = descriptor_formatter_->format({&headers}, info);
282
13
  if (!formatted_value.empty()) {
283
9
    descriptor_entry = {descriptor_key_, formatted_value};
284
9
  } else if (!default_value_.empty()) {
285
3
    descriptor_entry = {descriptor_key_, default_value_};
286
3
  } else {
287
    // If formatting resulted in empty string and no default_value, skip this descriptor
288
1
    return false;
289
1
  }
290
12
  return true;
291
13
}
292

            
293
QueryParameterValueMatchAction::QueryParameterValueMatchAction(
294
    const envoy::config::route::v3::RateLimit::Action::QueryParameterValueMatch& action,
295
    Server::Configuration::CommonFactoryContext& context,
296
    std::unique_ptr<Formatter::FormatterImpl> formatter)
297
25
    : descriptor_value_(action.descriptor_value()),
298
25
      descriptor_key_(!action.descriptor_key().empty() ? action.descriptor_key() : "query_match"),
299
25
      default_value_(action.default_value()),
300
25
      expect_match_(PROTOBUF_GET_WRAPPED_OR_DEFAULT(action, expect_match, true)),
301
      action_query_parameters_(
302
25
          buildQueryParameterMatcherVector(action.query_parameters(), context)),
303
25
      descriptor_formatter_(std::move(formatter)) {}
304

            
305
bool QueryParameterValueMatchAction::populateDescriptor(
306
    RateLimit::DescriptorEntry& descriptor_entry, const std::string&,
307
25
    const Http::RequestHeaderMap& headers, const StreamInfo::StreamInfo& info) const {
308
25
  Http::Utility::QueryParamsMulti query_parameters =
309
25
      Http::Utility::QueryParamsMulti::parseAndDecodeQueryString(headers.getPathValue());
310
25
  if (expect_match_ !=
311
25
      ConfigUtility::matchQueryParams(query_parameters, action_query_parameters_)) {
312
4
    return false;
313
4
  }
314

            
315
21
  if (descriptor_formatter_ == nullptr) {
316
8
    descriptor_entry = {descriptor_key_, descriptor_value_};
317
8
    return true;
318
8
  }
319

            
320
13
  const std::string formatted_value = descriptor_formatter_->format({&headers}, info);
321
13
  if (!formatted_value.empty()) {
322
9
    descriptor_entry = {descriptor_key_, formatted_value};
323
9
  } else if (!default_value_.empty()) {
324
3
    descriptor_entry = {descriptor_key_, default_value_};
325
3
  } else {
326
    // If formatting resulted in empty string and no default_value, skip this descriptor
327
1
    return false;
328
1
  }
329
12
  return true;
330
13
}
331

            
332
std::vector<ConfigUtility::QueryParameterMatcherPtr>
333
QueryParameterValueMatchAction::buildQueryParameterMatcherVector(
334
    const Protobuf::RepeatedPtrField<envoy::config::route::v3::QueryParameterMatcher>&
335
        query_parameters,
336
25
    Server::Configuration::CommonFactoryContext& context) {
337
25
  std::vector<ConfigUtility::QueryParameterMatcherPtr> ret;
338
25
  ret.reserve(query_parameters.size());
339
25
  for (const auto& query_parameter : query_parameters) {
340
25
    ret.emplace_back(
341
25
        std::make_unique<ConfigUtility::QueryParameterMatcher>(query_parameter, context));
342
25
  }
343
25
  return ret;
344
25
}
345

            
346
RemoteAddressMatchAction::RemoteAddressMatchAction(
347
    const envoy::config::route::v3::RateLimit::Action::RemoteAddressMatch& action,
348
    Server::Configuration::CommonFactoryContext&)
349
7
    : descriptor_key_(!action.descriptor_key().empty() ? action.descriptor_key()
350
7
                                                       : "remote_address_match"),
351
7
      default_value_(action.default_value()),
352
7
      ip_list_(Network::Address::IpList::create(action.address_matcher().ranges()).value()),
353
7
      invert_match_(action.address_matcher().invert_match()),
354
7
      descriptor_formatter_(
355
7
          Formatter::FormatterImpl::create(action.descriptor_value(), true).value()) {}
356

            
357
bool RemoteAddressMatchAction::populateDescriptor(RateLimit::DescriptorEntry& descriptor_entry,
358
                                                  const std::string&,
359
                                                  const Http::RequestHeaderMap& headers,
360
7
                                                  const StreamInfo::StreamInfo& info) const {
361
  // Check if remote address matches the address matcher
362
7
  const Network::Address::InstanceConstSharedPtr& remote_address =
363
7
      info.downstreamAddressProvider().remoteAddress();
364
7
  if (remote_address->type() != Network::Address::Type::Ip) {
365
    return false;
366
  }
367

            
368
7
  const bool matches = ip_list_->contains(*remote_address);
369
7
  const bool should_apply = invert_match_ ? !matches : matches;
370
7
  if (!should_apply) {
371
2
    return false;
372
2
  }
373

            
374
  // Format the descriptor value
375
5
  const std::string formatted_value = descriptor_formatter_->format({&headers}, info);
376
5
  if (!formatted_value.empty()) {
377
4
    descriptor_entry = {descriptor_key_, formatted_value};
378
4
  } else if (!default_value_.empty()) {
379
1
    descriptor_entry = {descriptor_key_, default_value_};
380
1
  } else {
381
    // If formatting resulted in empty string and no default_value, skip this descriptor
382
    return false;
383
  }
384
5
  return true;
385
5
}
386

            
387
RateLimitPolicyEntryImpl::RateLimitPolicyEntryImpl(
388
    const envoy::config::route::v3::RateLimit& config,
389
    Server::Configuration::CommonFactoryContext& context, absl::Status& creation_status)
390
147
    : disable_key_(config.disable_key()),
391
147
      stage_(static_cast<uint64_t>(PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, stage, 0))),
392
147
      apply_on_stream_done_(config.apply_on_stream_done()),
393
147
      x_ratelimit_option_(config.x_ratelimit_option()) {
394
147
  actions_.reserve(config.actions().size());
395
156
  for (const auto& action : config.actions()) {
396
156
    switch (action.action_specifier_case()) {
397
3
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kSourceCluster:
398
3
      actions_.emplace_back(new SourceClusterAction());
399
3
      break;
400
40
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kDestinationCluster:
401
40
      actions_.emplace_back(new DestinationClusterAction());
402
40
      break;
403
5
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kQueryParameters:
404
5
      actions_.emplace_back(new QueryParametersAction(action.query_parameters()));
405
5
      break;
406
6
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kRequestHeaders:
407
6
      actions_.emplace_back(new RequestHeadersAction(action.request_headers()));
408
6
      break;
409
14
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kRemoteAddress:
410
14
      actions_.emplace_back(new RemoteAddressAction());
411
14
      break;
412
22
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kGenericKey: {
413
22
      const auto& generic_key = action.generic_key();
414
      // Legacy behavior: use the descriptor_value as a literal string without any formatter
415
      // parsing or substitution.
416
22
      if (!Runtime::runtimeFeatureEnabled(
417
22
              "envoy.reloadable_features.enable_formatter_for_ratelimit_action_descriptor_value")) {
418
12
        actions_.emplace_back(new GenericKeyAction(action.generic_key()));
419
12
        break;
420
12
      }
421
10
      auto formatter_or_error =
422
10
          Formatter::FormatterImpl::create(generic_key.descriptor_value(), true);
423
10
      SET_AND_RETURN_IF_NOT_OK(formatter_or_error.status(), creation_status);
424
10
      actions_.emplace_back(
425
10
          new GenericKeyAction(action.generic_key(), std::move(formatter_or_error.value())));
426
10
      break;
427
10
    }
428
1
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kDynamicMetadata:
429
1
      actions_.emplace_back(new MetaDataAction(action.dynamic_metadata()));
430
1
      break;
431
9
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kMetadata:
432
9
      actions_.emplace_back(new MetaDataAction(action.metadata()));
433
9
      break;
434
14
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kHeaderValueMatch: {
435
14
      const auto& header_value_match = action.header_value_match();
436
      // Legacy behavior: use the descriptor_value as a literal string without any formatter
437
      // parsing or substitution.
438
14
      if (!Runtime::runtimeFeatureEnabled(
439
14
              "envoy.reloadable_features.enable_formatter_for_ratelimit_action_descriptor_value")) {
440
7
        actions_.emplace_back(new HeaderValueMatchAction(action.header_value_match(), context));
441
7
        break;
442
7
      }
443
7
      auto formatter_or_error =
444
7
          Formatter::FormatterImpl::create(header_value_match.descriptor_value(), true);
445
7
      SET_AND_RETURN_IF_NOT_OK(formatter_or_error.status(), creation_status);
446
7
      actions_.emplace_back(new HeaderValueMatchAction(action.header_value_match(), context,
447
7
                                                       std::move(formatter_or_error.value())));
448
7
      break;
449
7
    }
450
20
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kExtension: {
451
20
      ProtobufMessage::ValidationVisitor& validator = context.messageValidationVisitor();
452
20
      auto* factory = Envoy::Config::Utility::getFactory<RateLimit::DescriptorProducerFactory>(
453
20
          action.extension());
454
20
      if (!factory) {
455
        // If no descriptor extension is found, fallback to using HTTP matcher
456
        // input functions. Note that if the same extension name or type was
457
        // dual registered as an extension descriptor and an HTTP matcher input
458
        // function, the descriptor extension takes priority.
459
4
        RateLimitDescriptorValidationVisitor validation_visitor;
460
4
        Matcher::MatchInputFactory<Http::HttpMatchingData> input_factory(validator,
461
4
                                                                         validation_visitor);
462
4
        Matcher::DataInputFactoryCb<Http::HttpMatchingData> data_input_cb =
463
4
            input_factory.createDataInput(action.extension());
464
4
        actions_.emplace_back(std::make_unique<MatchInputRateLimitDescriptor>(
465
4
            action.extension().name(), data_input_cb()));
466
4
        break;
467
4
      }
468
16
      auto message = Envoy::Config::Utility::translateAnyToFactoryConfig(
469
16
          action.extension().typed_config(), validator, *factory);
470
16
      absl::StatusOr<RateLimit::DescriptorProducerPtr> producer_or =
471
16
          factory->createDescriptorProducerFromProto(*message, context);
472
16
      SET_AND_RETURN_IF_NOT_OK(producer_or.status(), creation_status);
473
11
      actions_.emplace_back(std::move(producer_or.value()));
474
11
      break;
475
16
    }
476
4
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kMaskedRemoteAddress:
477
4
      actions_.emplace_back(new MaskedRemoteAddressAction(action.masked_remote_address()));
478
4
      break;
479
13
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::
480
13
        kQueryParameterValueMatch: {
481
13
      const auto& query_parameter_value_match = action.query_parameter_value_match();
482
      // Legacy behavior: use the descriptor_value as a literal string without any formatter
483
      // parsing or substitution.
484
13
      if (!Runtime::runtimeFeatureEnabled(
485
13
              "envoy.reloadable_features.enable_formatter_for_ratelimit_action_descriptor_value")) {
486
6
        actions_.emplace_back(
487
6
            new QueryParameterValueMatchAction(action.query_parameter_value_match(), context));
488
6
        break;
489
6
      }
490
7
      auto formatter_or_error =
491
7
          Formatter::FormatterImpl::create(query_parameter_value_match.descriptor_value(), true);
492
7
      SET_AND_RETURN_IF_NOT_OK(formatter_or_error.status(), creation_status);
493
7
      actions_.emplace_back(new QueryParameterValueMatchAction(
494
7
          action.query_parameter_value_match(), context, std::move(formatter_or_error.value())));
495
7
      break;
496
7
    }
497
5
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::kRemoteAddressMatch:
498
5
      actions_.emplace_back(new RemoteAddressMatchAction(action.remote_address_match(), context));
499
5
      break;
500
    case envoy::config::route::v3::RateLimit::Action::ActionSpecifierCase::ACTION_SPECIFIER_NOT_SET:
501
      PANIC_DUE_TO_CORRUPT_ENUM;
502
156
    }
503
156
  }
504
141
  if (config.has_limit()) {
505
4
    switch (config.limit().override_specifier_case()) {
506
4
    case envoy::config::route::v3::RateLimit_Override::OverrideSpecifierCase::kDynamicMetadata:
507
4
      limit_override_.emplace(
508
4
          new DynamicMetadataRateLimitOverride(config.limit().dynamic_metadata()));
509
4
      break;
510
    case envoy::config::route::v3::RateLimit_Override::OverrideSpecifierCase::
511
        OVERRIDE_SPECIFIER_NOT_SET:
512
      PANIC_DUE_TO_CORRUPT_ENUM;
513
4
    }
514
4
  }
515
141
}
516

            
517
void RateLimitPolicyEntryImpl::populateDescriptors(std::vector<RateLimit::Descriptor>& descriptors,
518
                                                   const std::string& local_service_cluster,
519
                                                   const Http::RequestHeaderMap& headers,
520
138
                                                   const StreamInfo::StreamInfo& info) const {
521
138
  RateLimit::Descriptor descriptor;
522
138
  const bool result =
523
138
      populateDescriptor(actions_, descriptor.entries_, local_service_cluster, headers, info);
524

            
525
138
  if (limit_override_) {
526
4
    limit_override_.value()->populateOverride(descriptor, &info.dynamicMetadata());
527
4
  }
528

            
529
138
  if (result) {
530
118
    descriptor.x_ratelimit_option_ = x_ratelimit_option_;
531
118
    descriptors.emplace_back(descriptor);
532
118
  }
533
138
}
534

            
535
void RateLimitPolicyEntryImpl::populateLocalDescriptors(
536
    std::vector<Envoy::RateLimit::LocalDescriptor>& descriptors,
537
    const std::string& local_service_cluster, const Http::RequestHeaderMap& headers,
538
    const StreamInfo::StreamInfo& info) const {
539
  RateLimit::LocalDescriptor descriptor({});
540
  const bool result =
541
      populateDescriptor(actions_, descriptor.entries_, local_service_cluster, headers, info);
542
  if (result) {
543
    descriptors.emplace_back(descriptor);
544
  }
545
}
546

            
547
namespace {
548
// Initializes the ``RateLimitPolicyImpl::rate_limit_entries_`` vector.
549
std::vector<RateLimitPolicyEntryImpl> initRateLimitEntries(
550
    const Protobuf::RepeatedPtrField<envoy::config::route::v3::RateLimit>& rate_limits,
551
46
    Server::Configuration::CommonFactoryContext& context, absl::Status& creation_status) {
552
46
  std::vector<RateLimitPolicyEntryImpl> entries;
553
46
  entries.reserve(rate_limits.size());
554
48
  for (const auto& rate_limit : rate_limits) {
555
48
    entries.emplace_back(rate_limit, context, creation_status);
556
    // If the entry is invalid, the function returns no policy entries, and
557
    // changes the creation_status to the error.
558
48
    if (!creation_status.ok()) {
559
      return {};
560
    }
561
48
  }
562
46
  return entries;
563
46
}
564

            
565
// The maximum stage number supported. This value should match the maximum stage number in
566
// Json::Schema::HTTP_RATE_LIMITS_CONFIGURATION_SCHEMA and
567
// Json::Schema::RATE_LIMIT_HTTP_FILTER_SCHEMA from common/json/config_schemas.cc.
568
static constexpr uint64_t MAX_STAGE_NUMBER = 10UL;
569

            
570
// Initializes the ``RateLimitPolicyImpl::rate_limit_entries_reference_`` vector.
571
std::vector<std::vector<std::reference_wrapper<const RateLimitPolicyEntry>>>
572
46
initRateLimitEntriesReference(const std::vector<RateLimitPolicyEntryImpl>& rate_limit_entries) {
573
46
  std::vector<std::vector<std::reference_wrapper<const RateLimitPolicyEntry>>> references(
574
46
      MAX_STAGE_NUMBER + 1);
575
48
  for (const auto& entry : rate_limit_entries) {
576
48
    const uint64_t stage = entry.stage();
577
    // The stage value is validated by PGV.
578
48
    ASSERT(stage < references.size());
579
48
    references[stage].emplace_back(std::ref(entry));
580
48
  }
581
46
  return references;
582
46
}
583
} // namespace
584

            
585
RateLimitPolicyImpl::RateLimitPolicyImpl(
586
    const Protobuf::RepeatedPtrField<envoy::config::route::v3::RateLimit>& rate_limits,
587
    Server::Configuration::CommonFactoryContext& context, absl::Status& creation_status)
588
46
    : rate_limit_entries_(initRateLimitEntries(rate_limits, context, creation_status)),
589
46
      rate_limit_entries_reference_(initRateLimitEntriesReference(rate_limit_entries_)) {}
590

            
591
const std::vector<std::reference_wrapper<const Router::RateLimitPolicyEntry>>&
592
85
RateLimitPolicyImpl::getApplicableRateLimit(uint64_t stage) const {
593
85
  ASSERT(stage < rate_limit_entries_reference_.size());
594
85
  return rate_limit_entries_reference_[stage];
595
85
}
596

            
597
} // namespace Router
598
} // namespace Envoy