From a71bc8e857663a6665a37a645c58363ff1844d8a Mon Sep 17 00:00:00 2001 From: Tommy Wang Date: Wed, 3 Feb 2021 15:21:29 -0800 Subject: [PATCH] network: Enable the Kill Request filter to honor Route-level configuration. (#14929) Signed-off-by: Tommy Wang --- .../filters/http/kill_request/BUILD | 1 + .../http/kill_request/kill_request_config.h | 33 +++++++++++++++++ .../http/kill_request/kill_request_filter.cc | 23 ++++++++++++ .../http/kill_request/kill_request_filter.h | 32 +++++++++++++++-- .../filters/http/kill_request/BUILD | 2 ++ .../kill_request/kill_request_filter_test.cc | 35 +++++++++++++++++++ 6 files changed, 124 insertions(+), 2 deletions(-) diff --git a/source/extensions/filters/http/kill_request/BUILD b/source/extensions/filters/http/kill_request/BUILD index 6fa2cc297e89..404a84c96fa6 100644 --- a/source/extensions/filters/http/kill_request/BUILD +++ b/source/extensions/filters/http/kill_request/BUILD @@ -21,6 +21,7 @@ envoy_cc_library( "//source/common/http:header_utility_lib", "//source/common/http:headers_lib", "//source/common/protobuf:utility_lib", + "//source/extensions/filters/http:well_known_names", "@envoy_api//envoy/extensions/filters/http/kill_request/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/http/kill_request/kill_request_config.h b/source/extensions/filters/http/kill_request/kill_request_config.h index d7a6120e6b82..4596e032558b 100644 --- a/source/extensions/filters/http/kill_request/kill_request_config.h +++ b/source/extensions/filters/http/kill_request/kill_request_config.h @@ -25,6 +25,39 @@ class KillRequestFilterFactory const std::string& stats_prefix, Server::Configuration::FactoryContext& context) override; }; +/** + * Generic configuration for a kill request. + */ +class KillRequestConfig { +public: + KillRequestConfig( + const envoy::extensions::filters::http::kill_request::v3::KillRequest& + kill_request); + + envoy::type::v3::FractionalPercent probability( + const Http::RequestHeaderMap* request_headers) const { + return provider_->probability(request_headers); + } + +private: + // Abstract kill provider. + class KillProvider { + public: + virtual ~KillProvider() = default; + + // Return what probability of requests kill should be applied to. + virtual envoy::type::v3::FractionalPercent probability( + const Http::RequestHeaderMap* request_headers) const PURE; + }; + + using KillProviderPtr = std::unique_ptr; + + KillProviderPtr provider_; +}; + +using KillRequestConfigPtr = std::unique_ptr; +using KillRequestConfigSharedPtr = std::shared_ptr; + } // namespace KillRequest } // namespace HttpFilters } // namespace Extensions diff --git a/source/extensions/filters/http/kill_request/kill_request_filter.cc b/source/extensions/filters/http/kill_request/kill_request_filter.cc index 3ecd90e00cac..e879d4d6c286 100644 --- a/source/extensions/filters/http/kill_request/kill_request_filter.cc +++ b/source/extensions/filters/http/kill_request/kill_request_filter.cc @@ -1,14 +1,21 @@ #include "extensions/filters/http/kill_request/kill_request_filter.h" #include +#include #include "common/protobuf/utility.h" +#include "extensions/filters/http/well_known_names.h" namespace Envoy { namespace Extensions { namespace HttpFilters { namespace KillRequest { +using ::envoy::extensions::filters::http::kill_request::v3::KillRequest; + +KillSettings::KillSettings(const KillRequest& kill_request) + : kill_probability_(kill_request.probability()) {} + bool KillRequestFilter::isKillRequestEnabled() { return ProtobufPercentHelper::evaluateFractionalPercent(kill_request_.probability(), random_generator_.random()); @@ -29,6 +36,22 @@ Http::FilterHeadersStatus KillRequestFilter::decodeHeaders(Http::RequestHeaderMa return Http::FilterHeadersStatus::Continue; } + // Route-level configuration overrides filter-level configuration. + if (decoder_callbacks_->route() && + decoder_callbacks_->route()->routeEntry()) { + const std::string& name = + Extensions::HttpFilters::HttpFilterNames::get().KillRequest; + const auto* route_entry = decoder_callbacks_->route()->routeEntry(); + + const auto* per_route_kill_settings = + route_entry->mostSpecificPerFilterConfigTyped(name); + + if (per_route_kill_settings) { + envoy::type::v3::FractionalPercent probability = per_route_kill_settings->getProbability(); + kill_request_.set_allocated_probability(&probability); + } + } + if (is_kill_request && isKillRequestEnabled()) { // Crash Envoy. raise(SIGABRT); diff --git a/source/extensions/filters/http/kill_request/kill_request_filter.h b/source/extensions/filters/http/kill_request/kill_request_filter.h index 82243f819b7a..a1fab555620c 100644 --- a/source/extensions/filters/http/kill_request/kill_request_filter.h +++ b/source/extensions/filters/http/kill_request/kill_request_filter.h @@ -7,6 +7,7 @@ #include "envoy/http/filter.h" #include "envoy/http/header_map.h" +#include "common/http/header_utility.h" #include "common/http/headers.h" namespace Envoy { @@ -52,7 +53,10 @@ class KillRequestFilter : public Http::StreamFilter, Logger::Loggable& filterHeaders() const { + return kill_request_filter_headers_; + } + + const envoy::type::v3::FractionalPercent getProbability() const { + return std::move(kill_probability_); + } + +private: + envoy::type::v3::FractionalPercent kill_probability_; + const std::vector + kill_request_filter_headers_; }; } // namespace KillRequest diff --git a/test/extensions/filters/http/kill_request/BUILD b/test/extensions/filters/http/kill_request/BUILD index 31d3857c26ae..3ccf34a41dca 100644 --- a/test/extensions/filters/http/kill_request/BUILD +++ b/test/extensions/filters/http/kill_request/BUILD @@ -18,8 +18,10 @@ envoy_extension_cc_test( deps = [ "//include/envoy/http:metadata_interface", "//source/common/buffer:buffer_lib", + "//source/extensions/filters/http:well_known_names", "//source/extensions/filters/http/kill_request:kill_request_filter_lib", "//test/mocks:common_lib", + "//test/mocks/http:http_mocks", "//test/test_common:utility_lib", "@envoy_api//envoy/extensions/filters/http/kill_request/v3:pkg_cc_proto", "@envoy_api//envoy/type/v3:pkg_cc_proto", diff --git a/test/extensions/filters/http/kill_request/kill_request_filter_test.cc b/test/extensions/filters/http/kill_request/kill_request_filter_test.cc index af202dcfa1c6..846a53163618 100644 --- a/test/extensions/filters/http/kill_request/kill_request_filter_test.cc +++ b/test/extensions/filters/http/kill_request/kill_request_filter_test.cc @@ -4,9 +4,11 @@ #include "common/buffer/buffer_impl.h" +#include "extensions/filters/http/well_known_names.h" #include "extensions/filters/http/kill_request/kill_request_filter.h" #include "test/mocks/common.h" +#include "test/mocks/http/mocks.h" #include "test/test_common/utility.h" #include "gmock/gmock.h" @@ -18,6 +20,7 @@ namespace HttpFilters { namespace KillRequest { namespace { +using ::testing::AnyNumber; using ::testing::Return; class KillRequestFilterTest : public testing::Test { @@ -25,10 +28,17 @@ class KillRequestFilterTest : public testing::Test { void setUpTest(const envoy::extensions::filters::http::kill_request::v3::KillRequest& kill_request) { filter_ = std::make_unique(kill_request, random_generator_); + + filter_->setDecoderFilterCallbacks(decoder_filter_callbacks_); + EXPECT_CALL(decoder_filter_callbacks_.dispatcher_, pushTrackedObject(_)) + .Times(AnyNumber()); + EXPECT_CALL(decoder_filter_callbacks_.dispatcher_, popTrackedObject(_)) + .Times(AnyNumber()); } std::unique_ptr filter_; testing::NiceMock random_generator_; + testing::NiceMock decoder_filter_callbacks_; Http::TestRequestHeaderMapImpl request_headers_; }; @@ -74,6 +84,31 @@ TEST_F(KillRequestFilterTest, KillRequestDisabledWhenIsKillRequestEnabledReturns EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers_, false)); } + +TEST_F( + KillRequestFilterTest, + KillRequestDisabledWhenIsKillRequestEnabledReturnsTrueFromRouteLevelConfiguration) { + envoy::extensions::filters::http::kill_request::v3::KillRequest kill_request; + kill_request.mutable_probability()->set_numerator(0); + setUpTest(kill_request); + request_headers_.addCopy("x-envoy-kill-request", "true"); + + envoy::extensions::filters::http::kill_request::v3::KillRequest + route_level_kill_request; + route_level_kill_request.mutable_probability()->set_numerator(1); + route_level_kill_request.set_kill_request_header("x-custom-kill-request"); + + + KillSettings kill_settings = KillSettings(route_level_kill_request); + + ON_CALL(random_generator_, random()).WillByDefault(Return(0)); + ON_CALL(decoder_filter_callbacks_.route_->route_entry_, + perFilterConfig( + Extensions::HttpFilters::HttpFilterNames::get().KillRequest)) + .WillByDefault(Return(&kill_settings)); + EXPECT_DEATH(filter_->decodeHeaders(request_headers_, false), ""); +} + TEST_F(KillRequestFilterTest, KillRequestDisabledWhenHeaderIsMissing) { envoy::extensions::filters::http::kill_request::v3::KillRequest kill_request; kill_request.mutable_probability()->set_numerator(100);