Skip to content

Commit

Permalink
network: Enable the Kill Request filter to honor Route-level configur…
Browse files Browse the repository at this point in the history
…ation. (envoyproxy#14929)

Signed-off-by: Tommy Wang <xiaobinwang@google.com>
  • Loading branch information
xiaobinxbw committed Feb 4, 2021
1 parent 4e1146b commit a71bc8e
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 2 deletions.
1 change: 1 addition & 0 deletions source/extensions/filters/http/kill_request/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
)
Expand Down
33 changes: 33 additions & 0 deletions source/extensions/filters/http/kill_request/kill_request_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<KillProvider>;

KillProviderPtr provider_;
};

using KillRequestConfigPtr = std::unique_ptr<KillRequestConfig>;
using KillRequestConfigSharedPtr = std::shared_ptr<KillRequestConfig>;

} // namespace KillRequest
} // namespace HttpFilters
} // namespace Extensions
Expand Down
23 changes: 23 additions & 0 deletions source/extensions/filters/http/kill_request/kill_request_filter.cc
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
#include "extensions/filters/http/kill_request/kill_request_filter.h"

#include <csignal>
#include <string>

#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());
Expand All @@ -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<KillSettings>(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);
Expand Down
32 changes: 30 additions & 2 deletions source/extensions/filters/http/kill_request/kill_request_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -52,7 +53,10 @@ class KillRequestFilter : public Http::StreamFilter, Logger::Loggable<Logger::Id
return Http::FilterTrailersStatus::Continue;
}

void setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks&) override {}
void setDecoderFilterCallbacks(
Http::StreamDecoderFilterCallbacks& callbacks) override {
decoder_callbacks_ = &callbacks;
}

// Http::StreamEncoderFilter
Http::FilterHeadersStatus encode100ContinueHeaders(Http::ResponseHeaderMap&) override {
Expand Down Expand Up @@ -82,8 +86,32 @@ class KillRequestFilter : public Http::StreamFilter, Logger::Loggable<Logger::Id
// equaling true.
bool isKillRequestEnabled();

const envoy::extensions::filters::http::kill_request::v3::KillRequest kill_request_;
envoy::extensions::filters::http::kill_request::v3::KillRequest kill_request_;
Random::RandomGenerator& random_generator_;
Http::StreamDecoderFilterCallbacks* decoder_callbacks_{};
};

/**
* Configuration for fault injection.
*/
class KillSettings : public Router::RouteSpecificFilterConfig {
public:
KillSettings(
const envoy::extensions::filters::http::kill_request::v3::KillRequest&
kill_request);

const std::vector<Http::HeaderUtility::HeaderDataPtr>& 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<Http::HeaderUtility::HeaderDataPtr>
kill_request_filter_headers_;
};

} // namespace KillRequest
Expand Down
2 changes: 2 additions & 0 deletions test/extensions/filters/http/kill_request/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -18,17 +20,25 @@ namespace HttpFilters {
namespace KillRequest {
namespace {

using ::testing::AnyNumber;
using ::testing::Return;

class KillRequestFilterTest : public testing::Test {
protected:
void
setUpTest(const envoy::extensions::filters::http::kill_request::v3::KillRequest& kill_request) {
filter_ = std::make_unique<KillRequestFilter>(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<KillRequestFilter> filter_;
testing::NiceMock<Random::MockRandomGenerator> random_generator_;
testing::NiceMock<Http::MockStreamDecoderFilterCallbacks> decoder_filter_callbacks_;
Http::TestRequestHeaderMapImpl request_headers_;
};

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit a71bc8e

Please sign in to comment.