Skip to content

Commit

Permalink
Expose fault.http.abort.http_status setting via HTTTP header (#10294)
Browse files Browse the repository at this point in the history
The partial implementation of #10254. Adding a support for http header responsible for injecting faults - aborting requests with x-envoy-fault-abort-request HTTP header set.

Risk Level: low, new feature.
Testing: Added
Docs Changes: Added
Release Notes: Added

Signed-off-by: Rafal Augustyniak <raugustyniak@lyft.com>
  • Loading branch information
Augustyniak authored Mar 11, 2020
1 parent a22b937 commit 55971b2
Show file tree
Hide file tree
Showing 18 changed files with 316 additions and 76 deletions.
4 changes: 2 additions & 2 deletions api/envoy/config/filter/fault/v2/fault.proto
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ message FaultDelay {
}

// Fault delays are controlled via an HTTP header (if applicable). See the
// :ref:`http fault filter <config_http_filters_fault_injection_http_header>` documentation for
// :ref:`HTTP fault filter <config_http_filters_fault_injection_http_header>` documentation for
// more information.
message HeaderDelay {
}
Expand Down Expand Up @@ -65,7 +65,7 @@ message FaultRateLimit {
}

// Rate limits are controlled via an HTTP header (if applicable). See the
// :ref:`http fault filter <config_http_filters_fault_injection_http_header>` documentation for
// :ref:`HTTP fault filter <config_http_filters_fault_injection_http_header>` documentation for
// more information.
message HeaderLimit {
}
Expand Down
9 changes: 9 additions & 0 deletions api/envoy/config/filter/http/fault/v2/fault.proto
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,22 @@ option (udpa.annotations.file_migrate).move_to_package = "envoy.extensions.filte
// [#extension: envoy.filters.http.fault]

message FaultAbort {
// Fault aborts are controlled via an HTTP header (if applicable). See the
// :ref:`HTTP fault filter <config_http_filters_fault_injection_http_header>` documentation for
// more information.
message HeaderAbort {
}

reserved 1;

oneof error_type {
option (validate.required) = true;

// HTTP status code to use to abort the HTTP request.
uint32 http_status = 2 [(validate.rules).uint32 = {lt: 600 gte: 200}];

// Fault aborts are controlled via an HTTP header (if applicable).
HeaderAbort header_abort = 4;
}

// The percentage of requests/operations/connections that will be aborted with the error code
Expand Down
4 changes: 2 additions & 2 deletions api/envoy/extensions/filters/common/fault/v3/fault.proto
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ message FaultDelay {
}

// Fault delays are controlled via an HTTP header (if applicable). See the
// :ref:`http fault filter <config_http_filters_fault_injection_http_header>` documentation for
// :ref:`HTTP fault filter <config_http_filters_fault_injection_http_header>` documentation for
// more information.
message HeaderDelay {
option (udpa.annotations.versioning).previous_message_type =
Expand Down Expand Up @@ -75,7 +75,7 @@ message FaultRateLimit {
}

// Rate limits are controlled via an HTTP header (if applicable). See the
// :ref:`http fault filter <config_http_filters_fault_injection_http_header>` documentation for
// :ref:`HTTP fault filter <config_http_filters_fault_injection_http_header>` documentation for
// more information.
message HeaderLimit {
option (udpa.annotations.versioning).previous_message_type =
Expand Down
11 changes: 11 additions & 0 deletions api/envoy/extensions/filters/http/fault/v3/fault.proto
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,24 @@ message FaultAbort {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.filter.http.fault.v2.FaultAbort";

// Fault aborts are controlled via an HTTP header (if applicable). See the
// :ref:`HTTP fault filter <config_http_filters_fault_injection_http_header>` documentation for
// more information.
message HeaderAbort {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.filter.http.fault.v2.FaultAbort.HeaderAbort";
}

reserved 1;

oneof error_type {
option (validate.required) = true;

// HTTP status code to use to abort the HTTP request.
uint32 http_status = 2 [(validate.rules).uint32 = {lt: 600 gte: 200}];

// Fault aborts are controlled via an HTTP header (if applicable).
HeaderAbort header_abort = 4;
}

// The percentage of requests/operations/connections that will be aborted with the error code
Expand Down
14 changes: 13 additions & 1 deletion docs/root/configuration/http/http_filters/fault_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,18 @@ The fault filter has the capability to allow fault configuration to be specified
This is useful in certain scenarios in which it is desired to allow the client to specify its own
fault configuration. The currently supported header controls are:

* Request abort configuration via the *x-envoy-fault-abort-request* header. The header value
should be an integer that specifies the HTTP status code to return in response to a request
and must be in the range [200, 600). In order for the header to work, :ref:`header_abort
<envoy_api_field_config.filter.http.fault.v2.FaultAbort.header_abort>` needs to be set.
* Request delay configuration via the *x-envoy-fault-delay-request* header. The header value
should be an integer that specifies the number of milliseconds to throttle the latency for.
In order for the header to work, :ref:`header_delay
<envoy_api_field_config.filter.fault.v2.FaultDelay.header_delay>` needs to be set.
* Response rate limit configuration via the *x-envoy-fault-throughput-response* header. The
header value should be an integer that specified the limit in KiB/s and must be > 0.
header value should be an integer that specified the limit in KiB/s and must be > 0. In order
for the header to work, :ref:`header_limit
<envoy_api_field_config.filter.fault.v2.FaultRateLimit.header_limit>` needs to be set.

.. attention::

Expand All @@ -57,6 +65,10 @@ options:
typed_config:
"@type": type.googleapis.com/envoy.config.filter.http.fault.v2.HTTPFault
max_active_faults: 100
abort:
header_abort: {}
percentage:
numerator: 100
delay:
header_delay: {}
percentage:
Expand Down
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Version history
of extension names is available in the :ref:`deprecated <deprecated>` documentation.
* ext_authz: disabled the use of lowercase string matcher for headers matching in HTTP-based `ext_authz`.
Can be reverted temporarily by setting runtime feature `envoy.reloadable_features.ext_authz_http_service_enable_case_sensitive_string_matcher` to false.
* fault: added support for controlling abort faults with :ref:`HTTP header fault configuration <config_http_filters_fault_injection_http_header>` to the HTTP fault filter.
* http: added HTTP/1.1 flood protection. Can be temporarily disabled using the runtime feature `envoy.reloadable_features.http1_flood_protection`
* http: fixing a bug in HTTP/1.0 responses where Connection: keep-alive was not appended for connections which were kept alive.
* http: fixed a bug that could send extra METADATA frames and underflow memory when encoding METADATA frames on a connection that was dispatching data.
Expand Down
4 changes: 2 additions & 2 deletions generated_api_shadow/envoy/config/filter/fault/v2/fault.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions source/extensions/filters/common/fault/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ envoy_cc_library(
hdrs = ["fault_config.h"],
deps = [
"//include/envoy/http:header_map_interface",
"//source/common/http:codes_lib",
"//source/common/http:headers_lib",
"//source/common/protobuf:utility_lib",
"@envoy_api//envoy/extensions/filters/common/fault/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/filters/http/fault/v3:pkg_cc_proto",
"@envoy_api//envoy/type/v3:pkg_cc_proto",
],
)
35 changes: 35 additions & 0 deletions source/extensions/filters/common/fault/fault_config.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "extensions/filters/common/fault/fault_config.h"

#include "envoy/extensions/filters/common/fault/v3/fault.pb.h"
#include "envoy/extensions/filters/http/fault/v3/fault.pb.h"

#include "common/protobuf/utility.h"

Expand All @@ -10,6 +11,40 @@ namespace Filters {
namespace Common {
namespace Fault {

FaultAbortConfig::FaultAbortConfig(
const envoy::extensions::filters::http::fault::v3::FaultAbort& abort_config)
: percentage_(abort_config.percentage()) {
switch (abort_config.error_type_case()) {
case envoy::extensions::filters::http::fault::v3::FaultAbort::ErrorTypeCase::kHttpStatus:
provider_ = std::make_unique<FixedAbortProvider>(abort_config.http_status());
break;
case envoy::extensions::filters::http::fault::v3::FaultAbort::ErrorTypeCase::kHeaderAbort:
provider_ = std::make_unique<HeaderAbortProvider>();
break;
case envoy::extensions::filters::http::fault::v3::FaultAbort::ErrorTypeCase::ERROR_TYPE_NOT_SET:
NOT_REACHED_GCOVR_EXCL_LINE;
}
}

absl::optional<Http::Code>
FaultAbortConfig::HeaderAbortProvider::statusCode(const Http::HeaderEntry* header) const {
absl::optional<Http::Code> ret;
if (header == nullptr) {
return ret;
}

uint64_t code;
if (!absl::SimpleAtoi(header->value().getStringView(), &code)) {
return ret;
}

if (code >= 200 && code < 600) {
ret = static_cast<Http::Code>(code);
}

return ret;
}

FaultDelayConfig::FaultDelayConfig(
const envoy::extensions::filters::common::fault::v3::FaultDelay& delay_config)
: percentage_(delay_config.percentage()) {
Expand Down
52 changes: 52 additions & 0 deletions source/extensions/filters/common/fault/fault_config.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#pragma once

#include "envoy/extensions/filters/common/fault/v3/fault.pb.h"
#include "envoy/extensions/filters/http/fault/v3/fault.pb.h"
#include "envoy/http/header_map.h"
#include "envoy/type/v3/percent.pb.h"

#include "common/http/codes.h"
#include "common/http/headers.h"
#include "common/singleton/const_singleton.h"

Expand All @@ -20,10 +22,60 @@ class HeaderNameValues {
const Http::LowerCaseString DelayRequest{absl::StrCat(prefix(), "-fault-delay-request")};
const Http::LowerCaseString ThroughputResponse{
absl::StrCat(prefix(), "-fault-throughput-response")};
const Http::LowerCaseString AbortRequest{absl::StrCat(prefix(), "-fault-abort-request")};
};

using HeaderNames = ConstSingleton<HeaderNameValues>;

class FaultAbortConfig {
public:
FaultAbortConfig(const envoy::extensions::filters::http::fault::v3::FaultAbort& abort_config);

const envoy::type::v3::FractionalPercent& percentage() const { return percentage_; }
absl::optional<Http::Code> statusCode(const Http::HeaderEntry* header) const {
return provider_->statusCode(header);
}

private:
// Abstract abort provider.
class AbortProvider {
public:
virtual ~AbortProvider() = default;

// Return the HTTP status code to use. Optionally passed an HTTP header that may contain the
// HTTP status code depending on the provider implementation.
virtual absl::optional<Http::Code> statusCode(const Http::HeaderEntry* header) const PURE;
};

// Delay provider that uses a fixed abort status code.
class FixedAbortProvider : public AbortProvider {
public:
FixedAbortProvider(uint64_t status_code) : status_code_(status_code) {}

// AbortProvider
absl::optional<Http::Code> statusCode(const Http::HeaderEntry*) const override {
return static_cast<Http::Code>(status_code_);
}

private:
const uint64_t status_code_;
};

// Abort provider the reads a status code from an HTTP header.
class HeaderAbortProvider : public AbortProvider {
public:
// AbortProvider
absl::optional<Http::Code> statusCode(const Http::HeaderEntry* header) const override;
};

using AbortProviderPtr = std::unique_ptr<AbortProvider>;

AbortProviderPtr provider_;
const envoy::type::v3::FractionalPercent percentage_;
};

using FaultAbortConfigPtr = std::unique_ptr<FaultAbortConfig>;

/**
* Generic configuration for a delay fault.
*/
Expand Down
Loading

0 comments on commit 55971b2

Please sign in to comment.