-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
local rate limit: add new rate_limits support to the filter (#36099)
Commit Message: local rate limit: add new rate_limits api to the filter's api Additional Description: In the previous local rate limit, the [rate_limits](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-virtualhost-rate-limits) field of route is used to generate the descriptor entries. Then the generated entries will be used to match a token bucket which is configured in the filter configs (route level, vhost level, etc). However, it make the configuration very complex, and cannot cover some common scenarios easily. For example, give a specific virtual host X and a special route Y that under this virtual host X. We want to provides a virtual host level rate limit for the specific virtual host X, and a route level rate limit for the specific route Y. We hope the configuration of virtual host could works for all routes except the Y. For most filters, this requirement could be achieved by getting the most specific filter config and applying it. But for the local rate limit, thing become very complex. Because the rate limit configuration is split into `rate_limits` field of route and the filter config. The local rate limit need to handle these relationship carefully. This PR try to simplify it. Risk Level: low. Testing: n/a. Docs Changes: n/a. Release Notes: n/a. Platform Specific Features: n/a. --------- Signed-off-by: wangbaiping <[email protected]> Signed-off-by: code <[email protected]> Co-authored-by: Matt Klein <[email protected]>
- Loading branch information
1 parent
33679e4
commit e486663
Showing
17 changed files
with
1,594 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
load( | ||
"//bazel:envoy_build_system.bzl", | ||
"envoy_cc_library", | ||
"envoy_extension_package", | ||
) | ||
|
||
licenses(["notice"]) # Apache 2 | ||
|
||
envoy_extension_package() | ||
|
||
envoy_cc_library( | ||
name = "ratelimit_config_lib", | ||
srcs = ["ratelimit_config.cc"], | ||
hdrs = ["ratelimit_config.h"], | ||
deps = [ | ||
"//envoy/ratelimit:ratelimit_interface", | ||
"//source/common/router:router_ratelimit_lib", | ||
"@com_google_absl//absl/container:inlined_vector", | ||
"@com_google_absl//absl/strings", | ||
"@envoy_api//envoy/config/route/v3:pkg_cc_proto", | ||
], | ||
) |
143 changes: 143 additions & 0 deletions
143
source/extensions/filters/common/ratelimit_config/ratelimit_config.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
#include "source/extensions/filters/common/ratelimit_config/ratelimit_config.h" | ||
|
||
#include "source/common/config/utility.h" | ||
#include "source/common/http/matching/data_impl.h" | ||
#include "source/common/matcher/matcher.h" | ||
|
||
namespace Envoy { | ||
namespace Extensions { | ||
namespace Filters { | ||
namespace Common { | ||
namespace RateLimit { | ||
|
||
RateLimitPolicy::RateLimitPolicy(const ProtoRateLimit& config, | ||
Server::Configuration::CommonFactoryContext& context, | ||
absl::Status& creation_status, bool no_limit) { | ||
if (config.has_stage() || !config.disable_key().empty()) { | ||
creation_status = | ||
absl::InvalidArgumentError("'stage' field and 'disable_key' field are not supported"); | ||
return; | ||
} | ||
|
||
if (config.has_limit()) { | ||
if (no_limit) { | ||
creation_status = absl::InvalidArgumentError("'limit' field is not supported"); | ||
return; | ||
} | ||
} | ||
|
||
for (const ProtoRateLimit::Action& action : config.actions()) { | ||
switch (action.action_specifier_case()) { | ||
case ProtoRateLimit::Action::ActionSpecifierCase::kSourceCluster: | ||
actions_.emplace_back(new Envoy::Router::SourceClusterAction()); | ||
break; | ||
case ProtoRateLimit::Action::ActionSpecifierCase::kDestinationCluster: | ||
actions_.emplace_back(new Envoy::Router::DestinationClusterAction()); | ||
break; | ||
case ProtoRateLimit::Action::ActionSpecifierCase::kRequestHeaders: | ||
actions_.emplace_back(new Envoy::Router::RequestHeadersAction(action.request_headers())); | ||
break; | ||
case ProtoRateLimit::Action::ActionSpecifierCase::kRemoteAddress: | ||
actions_.emplace_back(new Envoy::Router::RemoteAddressAction()); | ||
break; | ||
case ProtoRateLimit::Action::ActionSpecifierCase::kGenericKey: | ||
actions_.emplace_back(new Envoy::Router::GenericKeyAction(action.generic_key())); | ||
break; | ||
case ProtoRateLimit::Action::ActionSpecifierCase::kMetadata: | ||
actions_.emplace_back(new Envoy::Router::MetaDataAction(action.metadata())); | ||
break; | ||
case ProtoRateLimit::Action::ActionSpecifierCase::kHeaderValueMatch: | ||
actions_.emplace_back( | ||
new Envoy::Router::HeaderValueMatchAction(action.header_value_match(), context)); | ||
break; | ||
case ProtoRateLimit::Action::ActionSpecifierCase::kExtension: { | ||
ProtobufMessage::ValidationVisitor& validator = context.messageValidationVisitor(); | ||
auto* factory = | ||
Envoy::Config::Utility::getFactory<Envoy::RateLimit::DescriptorProducerFactory>( | ||
action.extension()); | ||
if (!factory) { | ||
// If no descriptor extension is found, fallback to using HTTP matcher | ||
// input functions. Note that if the same extension name or type was | ||
// dual registered as an extension descriptor and an HTTP matcher input | ||
// function, the descriptor extension takes priority. | ||
Router::RateLimitDescriptorValidationVisitor validation_visitor; | ||
Matcher::MatchInputFactory<Http::HttpMatchingData> input_factory(validator, | ||
validation_visitor); | ||
Matcher::DataInputFactoryCb<Http::HttpMatchingData> data_input_cb = | ||
input_factory.createDataInput(action.extension()); | ||
actions_.emplace_back(std::make_unique<Router::MatchInputRateLimitDescriptor>( | ||
action.extension().name(), data_input_cb())); | ||
break; | ||
} | ||
auto message = Envoy::Config::Utility::translateAnyToFactoryConfig( | ||
action.extension().typed_config(), validator, *factory); | ||
Envoy::RateLimit::DescriptorProducerPtr producer = | ||
factory->createDescriptorProducerFromProto(*message, context); | ||
if (producer) { | ||
actions_.emplace_back(std::move(producer)); | ||
} else { | ||
creation_status = absl::InvalidArgumentError( | ||
absl::StrCat("Rate limit descriptor extension failed: ", action.extension().name())); | ||
return; | ||
} | ||
break; | ||
} | ||
case ProtoRateLimit::Action::ActionSpecifierCase::kMaskedRemoteAddress: | ||
actions_.emplace_back(new Router::MaskedRemoteAddressAction(action.masked_remote_address())); | ||
break; | ||
case ProtoRateLimit::Action::ActionSpecifierCase::kQueryParameterValueMatch: | ||
actions_.emplace_back(new Router::QueryParameterValueMatchAction( | ||
action.query_parameter_value_match(), context)); | ||
break; | ||
default: | ||
creation_status = absl::InvalidArgumentError(fmt::format( | ||
"Unsupported rate limit action: {}", static_cast<int>(action.action_specifier_case()))); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
void RateLimitPolicy::populateDescriptors(const Http::RequestHeaderMap& headers, | ||
const StreamInfo::StreamInfo& stream_info, | ||
const std::string& local_service_cluster, | ||
RateLimitDescriptors& descriptors) const { | ||
Envoy::RateLimit::LocalDescriptor descriptor; | ||
for (const Envoy::RateLimit::DescriptorProducerPtr& action : actions_) { | ||
Envoy::RateLimit::DescriptorEntry entry; | ||
if (!action->populateDescriptor(entry, local_service_cluster, headers, stream_info)) { | ||
return; | ||
} | ||
if (!entry.key_.empty()) { | ||
descriptor.entries_.emplace_back(std::move(entry)); | ||
} | ||
} | ||
descriptors.emplace_back(std::move(descriptor)); | ||
} | ||
|
||
RateLimitConfig::RateLimitConfig(const Protobuf::RepeatedPtrField<ProtoRateLimit>& configs, | ||
Server::Configuration::CommonFactoryContext& context, | ||
absl::Status& creation_status, bool no_limit) { | ||
for (const ProtoRateLimit& config : configs) { | ||
auto descriptor_generator = | ||
std::make_unique<RateLimitPolicy>(config, context, creation_status, no_limit); | ||
if (!creation_status.ok()) { | ||
return; | ||
} | ||
rate_limit_policies_.emplace_back(std::move(descriptor_generator)); | ||
} | ||
} | ||
|
||
void RateLimitConfig::populateDescriptors(const Http::RequestHeaderMap& headers, | ||
const StreamInfo::StreamInfo& stream_info, | ||
const std::string& local_service_cluster, | ||
RateLimitDescriptors& descriptors) const { | ||
for (const auto& generator : rate_limit_policies_) { | ||
generator->populateDescriptors(headers, stream_info, local_service_cluster, descriptors); | ||
} | ||
} | ||
|
||
} // namespace RateLimit | ||
} // namespace Common | ||
} // namespace Filters | ||
} // namespace Extensions | ||
} // namespace Envoy |
Oops, something went wrong.