Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions docs/configuration/http_conn_man/route_config/rate_limits.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ Actions

type
*(required, string)* The type of rate limit action to perform. The currently supported action
types are *source_cluster*, *destination_cluster* , *request_headers*, *remote_address* and
*generic_key*.
types are *source_cluster*, *destination_cluster* , *request_headers*, *remote_address*,
*generic_key* and *header_value_match*.

Source Cluster
^^^^^^^^^^^^^^
Expand Down Expand Up @@ -141,6 +141,31 @@ The following descriptor entry is appended to the descriptor:

* ("generic_key", "<descriptor_value>")

Header Value Match
^^^^^^^^^^^^^^^^^^

.. code-block:: json

{
"type": "header_value_match",
"descriptor_value" : "...",
"headers" : []
}


descriptor_value
*(required, string)* The value to use in the descriptor entry.

:ref:`headers<config_http_conn_man_route_table_route_headers>`
*(required, array)* Specifies a set of headers that the rate limit action should match on. The
action will check the request's headers against all the specified headers in the config. A match
will happen if all the headers in the config are present in the request with the same values (or
based on presence if the ``value`` field is not in the config).

The following descriptor entry is appended to the descriptor if the request matches the headers
specified in the action config:

* ("header_match", "<descriptor_value>")

.. _config_http_conn_man_route_table_rate_limit_composing_actions:

Expand Down
21 changes: 10 additions & 11 deletions docs/configuration/http_conn_man/route_config/route.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ auto_host_rewrite
*(optional, boolean)* Indicates that during forwarding, the host header will be swapped with the
hostname of the upstream host chosen by the cluster manager. This option is applicable only when
the destination cluster for a route is of type *strict_dns* or *logical_dns*. Setting this to true
with other cluster types has no effect. *auto_host_rewrite* and *host_rewrite* are mutually exclusive
with other cluster types has no effect. *auto_host_rewrite* and *host_rewrite* are mutually exclusive
options. Only one can be specified.

.. _config_http_conn_man_route_table_route_case_sensitive:
Expand Down Expand Up @@ -137,7 +137,10 @@ priority
<arch_overview_http_routing_priority>`.

:ref:`headers <config_http_conn_man_route_table_route_headers>`
*(optional, array)* Specifies a set of headers that the route should match on.
*(optional, array)* Specifies a set of headers that the route should match on. The router will
check the request's headers against all the specified headers in the route config. A match will
happen if all the headers in the route are present in the request with the same values (or based
on presence if the ``value`` field is not in the config).

.. _config_http_conn_man_route_table_route_rate_limits:

Expand Down Expand Up @@ -238,13 +241,13 @@ runtime_key
Headers
-------

The router can match a request to a route based on headers specified in the route config.

.. code-block:: json

[
{"name": "...", "value": "...", "regex": "..."}
]
{
"name": "...",
"value": "...",
"regex": "..."
}

name
*(required, string)* Specifies the name of the header in the request.
Expand All @@ -258,10 +261,6 @@ regex
expression or not. Defaults to false. The regex grammar used in the value field
is defined `here <http://en.cppreference.com/w/cpp/regex/ecmascript>`_.

The router will check the request's headers against all the specified
headers in the route config. A match will happen if all the headers in the route are present in
the request with the same values (or based on presence if the ``value`` field is not in the config).

.. attention::

Internally, Envoy always uses the HTTP/2 *:authority* header to represent the HTTP/1 *Host*
Expand Down
45 changes: 8 additions & 37 deletions docs/configuration/http_filters/fault_filter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,14 @@ upstream_cluster:
cluster that the filter should match on. Fault injection will be
restricted to requests bound to the specific upstream cluster.

:ref:`headers <config_http_filters_fault_injection_headers>`
*(optional, array)* Specifies a set of headers that the filter should match on.
:ref:`headers <config_http_conn_man_route_table_route_headers>`
*(optional, array)* Specifies a set of headers that the filter should match on. The fault
injection filter can be applied selectively to requests that match a set of headers specified in
the fault filter config. The chances of actual fault injection further depend on the values of
*abort_percent* and *fixed_delay_percent* parameters.The filter will check the request's headers
against all the specified headers in the filter config. A match will happen if all the headers in
the config are present in the request with the same values (or based on presence if the ``value``
field is not in the config).

The abort and delay blocks can be omitted. If they are not specified in the
configuration file, their respective values will be obtained from the
Expand Down Expand Up @@ -134,41 +140,6 @@ http.fault.delay.fixed_duration_ms
is missing from both the runtime and the config, no delays will be
injected.

.. _config_http_filters_fault_injection_headers:

Headers
-------

The fault injection filter can be applied selectively to requests that
match a set of headers specified in the fault filter config. The chances of
actual fault injection further depend on the values of *abort_percent* and
*fixed_delay_percent* parameters. Each element of the array in the
*headers* field should be in the following format:

.. code-block:: json

[
{"name": "...", "value": "...", "regex": "..."}
]

name
*(required, string)* Specifies the name of the header in the request.

value
*(optional, string)* Specifies the value of the header. If the value is
absent a request that has the *name* header will match, regardless of the
header's value.

regex
*(optional, boolean)* Specifies whether the header value is a regular expression
or not. Defaults to false. The regex grammar used in the value field
is defined `here <http://en.cppreference.com/w/cpp/regex/ecmascript>`_.

The filter will check the request's headers against all the specified
headers in the filter config. A match will happen if all the headers in the
config are present in the request with the same values (or based on
presence if the ``value`` field is not in the config).

Statistics
----------

Expand Down
1 change: 1 addition & 0 deletions source/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ add_library(
redis/conn_pool_impl.cc
redis/proxy_filter.cc
router/config_impl.cc
router/config_utility.cc
router/rds_impl.cc
router/retry_state_impl.cc
router/router.cc
Expand Down
5 changes: 1 addition & 4 deletions source/common/http/filter/fault_filter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,7 @@ FaultFilterConfig::FaultFilterConfig(const Json::Object& json_config, Runtime::L
if (json_config.hasObject("headers")) {
std::vector<Json::ObjectPtr> config_headers = json_config.getObjectArray("headers");
for (const Json::ObjectPtr& header_map : config_headers) {
// allow header value to be empty, allows matching to be only based on header presence.
fault_filter_headers_.emplace_back(Http::LowerCaseString(header_map->getString("name")),
header_map->getString("value", EMPTY_STRING),
header_map->getBoolean("regex", false));
fault_filter_headers_.push_back(*header_map);
}
}

Expand Down
54 changes: 37 additions & 17 deletions source/common/json/config_schemas.cc
Original file line number Diff line number Diff line change
Expand Up @@ -526,14 +526,7 @@ const std::string Json::Schema::ROUTE_ENTRY_CONFIGURATION_SCHEMA(R"EOF(
"type" : "array",
"minItems" : 1,
"items" : {
"type" : "object",
"properties" : {
"name" : {"type" : "string"},
"value" : {"type" : "string"},
"regex" : {"type" : "boolean"}
},
"required" : ["name"],
"additionalProperties" : false
"type" : "object"
}
},
"rate_limits" : {"type" : "array"},
Expand All @@ -550,6 +543,20 @@ const std::string Json::Schema::ROUTE_ENTRY_CONFIGURATION_SCHEMA(R"EOF(
}
)EOF");

const std::string Json::Schema::HEADER_DATA_CONFIGURATION_SCHEMA(R"EOF(
{
"$schema" : "http://json-schema.org/schema#",
"type" : "object",
"properties" : {
"name" : {"type" : "string"},
"value" : {"type" : "string"},
"regex" : {"type" : "boolean"}
},
"required" : ["name"],
"additionalProperties" : false
}
)EOF");

const std::string Json::Schema::HTTP_RATE_LIMITS_CONFIGURATION_SCHEMA(R"EOF(
{
"$schema": "http://json-schema.org/schema#",
Expand Down Expand Up @@ -611,6 +618,25 @@ const std::string Json::Schema::HTTP_RATE_LIMITS_CONFIGURATION_SCHEMA(R"EOF(
},
"required" : ["type", "descriptor_value"],
"additionalProperties" : false
},
"header_value_match" : {
"type" : "object",
"properties" : {
"type" : {
"type" : "string",
"enum" : ["header_value_match"]
},
"descriptor_value" : {"type" : "string"},
"headers" : {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also duplicated several times now. Can we de-dup and do a sub-schema check in the ConfigUtility code?

"type" : "array",
"minItems" : 1,
"items" : {
"type" : "object"
}
},
"required" : ["type", "descriptor_value", "headers"],
"additionalProperties" : false
}
}
},
"type" : "object",
Expand All @@ -626,7 +652,8 @@ const std::string Json::Schema::HTTP_RATE_LIMITS_CONFIGURATION_SCHEMA(R"EOF(
{"$ref" : "#/definitions/destination_cluster"},
{"$ref" : "#/definitions/request_headers"},
{"$ref" : "#/definitions/remote_address"},
{"$ref" : "#/definitions/generic_key"}
{"$ref" : "#/definitions/generic_key"},
{"$ref" : "#/definitions/header_value_match"}
]
}
}
Expand Down Expand Up @@ -697,14 +724,7 @@ const std::string Json::Schema::FAULT_HTTP_FILTER_SCHEMA(R"EOF(
"type" : "array",
"minItems" : 1,
"items" : {
"type" : "object",
"properties" : {
"name" : {"type" : "string"},
"value" : {"type" : "string"},
"regex" : {"type" : "boolean"}
},
"required" : ["name"],
"additionalProperties" : false
"type" : "object"
}
}
},
Expand Down
1 change: 1 addition & 0 deletions source/common/json/config_schemas.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class Schema {
static const std::string ROUTE_ENTRY_CONFIGURATION_SCHEMA;
static const std::string HTTP_RATE_LIMITS_CONFIGURATION_SCHEMA;
static const std::string RDS_CONFIGURATION_SCHEMA;
static const std::string HEADER_DATA_CONFIGURATION_SCHEMA;

// HTTP Filter Schemas
static const std::string BUFFER_HTTP_FILTER_SCHEMA;
Expand Down
17 changes: 17 additions & 0 deletions source/common/json/json_validator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include "envoy/json/json_object.h"

namespace Json {

/**
* Base class to inherit from to validate config schema before initializing member variables.
*/
class JsonValidator {
public:
JsonValidator(const Json::Object& config, const std::string& schema) {
config.validateSchema(schema);
}
};

} // Json
42 changes: 1 addition & 41 deletions source/common/router/config_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,41 +38,6 @@ ShadowPolicyImpl::ShadowPolicyImpl(const Json::Object& config) {
runtime_key_ = config.getObject("shadow")->getString("runtime_key", "");
}

Upstream::ResourcePriority ConfigUtility::parsePriority(const Json::Object& config) {
std::string priority_string = config.getString("priority", "default");
if (priority_string == "default") {
return Upstream::ResourcePriority::Default;
} else if (priority_string == "high") {
return Upstream::ResourcePriority::High;
} else {
throw EnvoyException(fmt::format("invalid resource priority '{}'", priority_string));
}
}

bool ConfigUtility::matchHeaders(const Http::HeaderMap& request_headers,
const std::vector<HeaderData>& config_headers) {
bool matches = true;

if (!config_headers.empty()) {
for (const HeaderData& cfg_header_data : config_headers) {
const Http::HeaderEntry* header = request_headers.get(cfg_header_data.name_);
if (cfg_header_data.value_.empty()) {
matches &= (header != nullptr);
} else if (!cfg_header_data.is_regex_) {
matches &= (header != nullptr) && (header->value() == cfg_header_data.value_.c_str());
} else {
matches &= (header != nullptr) &&
std::regex_match(header->value().c_str(), cfg_header_data.regex_pattern_);
}
if (!matches) {
break;
}
}
}

return matches;
}

HashPolicyImpl::HashPolicyImpl(const Json::Object& config)
: header_name_(config.getString("header_name")) {}

Expand Down Expand Up @@ -164,12 +129,7 @@ RouteEntryImplBase::RouteEntryImplBase(const VirtualHostImpl& vhost, const Json:
if (route.hasObject("headers")) {
std::vector<Json::ObjectPtr> config_headers = route.getObjectArray("headers");
for (const Json::ObjectPtr& header_map : config_headers) {
// allow header value to be empty, allows matching to be only based on header presence.
// Regex is an opt-in. Unless explicitly mentioned, we will use header values for exact string
// matches.
config_headers_.emplace_back(Http::LowerCaseString(header_map->getString("name")),
header_map->getString("value", EMPTY_STRING),
header_map->getBoolean("regex", false));
config_headers_.push_back(*header_map);
}
}

Expand Down
34 changes: 2 additions & 32 deletions source/common/router/config_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include "envoy/runtime/runtime.h"
#include "envoy/upstream/cluster_manager.h"

#include "common/router/config_utility.h"

namespace Router {

/**
Expand Down Expand Up @@ -48,38 +50,6 @@ class SslRedirectRoute : public Route {
static const SslRedirector SSL_REDIRECTOR;
};

/**
* Utility routines for loading route configuration and matching runtime request headers.
*/
class ConfigUtility {
public:
struct HeaderData {
HeaderData(const Http::LowerCaseString& name, const std::string& value, const bool is_regex)
: name_(name), value_(value), regex_pattern_(value_, std::regex::optimize),
is_regex_(is_regex) {}

const Http::LowerCaseString name_;
const std::string value_;
const std::regex regex_pattern_;
const bool is_regex_;
};

/**
* @return the resource priority parsed from JSON.
*/
static Upstream::ResourcePriority parsePriority(const Json::Object& config);

/**
* See if the specified headers are present in the request headers.
* @param headers supplies the list of headers to match
* @param request_headers supplies the list of request headers to compare against search_list
* @return true all the headers (and values) in the search_list set are found in the
* request_headers
*/
static bool matchHeaders(const Http::HeaderMap& headers,
const std::vector<HeaderData>& request_headers);
};

/**
* Holds all routing configuration for an entire virtual host.
*/
Expand Down
Loading