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
5 changes: 5 additions & 0 deletions api/envoy/api/v2/core/health_check.proto
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ message HealthCheck {
}
}

// [#comment:next free field: 9]
message HttpHealthCheck {
// The value of the host header in the HTTP health check request. If
// left empty (default value), the name of the cluster this health check is associated
Expand Down Expand Up @@ -100,6 +101,10 @@ message HealthCheck {
// <config_http_conn_man_headers_custom_request_headers>`.
repeated core.HeaderValueOption request_headers_to_add = 6;

// Specifies a list of HTTP headers that should be removed from each request that is sent to the
// health checked cluster.
repeated string request_headers_to_remove = 8;

// If set, health checks will be made using http/2.
bool use_http2 = 7;
}
Expand Down
5 changes: 5 additions & 0 deletions api/envoy/api/v2/rds.proto
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ service RouteDiscoveryService {
}
}

// [#comment:next free field: 9]
message RouteConfiguration {
// The name of the route configuration. For example, it might match
// :ref:`route_config_name
Expand Down Expand Up @@ -76,6 +77,10 @@ message RouteConfiguration {
// <config_http_conn_man_headers_custom_request_headers>`.
repeated core.HeaderValueOption request_headers_to_add = 6;

// Specifies a list of HTTP headers that should be removed from each request
// routed by the HTTP connection manager.
repeated string request_headers_to_remove = 8;

// An optional boolean that specifies whether the clusters that the route
// table refers to will be validated by the cluster manager. If set to true
// and a route refers to a non-existent cluster, the route table will not
Expand Down
15 changes: 15 additions & 0 deletions api/envoy/api/v2/route/route.proto
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ option (gogoproto.equal_all) = true;
// host header. This allows a single listener to service multiple top level domain path trees. Once
// a virtual host is selected based on the domain, the routes are processed in order to see which
// upstream cluster to route to or whether to perform a redirect.
// [#comment:next free field: 14]
message VirtualHost {
// The logical name of the virtual host. This is used when emitting certain
// statistics but is not relevant for routing.
Expand Down Expand Up @@ -81,6 +82,10 @@ message VirtualHost {
// <config_http_conn_man_headers_custom_request_headers>`.
repeated core.HeaderValueOption request_headers_to_add = 7;

// Specifies a list of HTTP headers that should be removed from each request
// handled by this virtual host.
repeated string request_headers_to_remove = 13;

// Specifies a list of HTTP headers that should be added to each response
// handled by this virtual host. Headers specified at this level are applied
// after headers from enclosed :ref:`envoy_api_msg_route.Route` and before headers from the
Expand Down Expand Up @@ -113,6 +118,7 @@ message VirtualHost {
//
// Envoy supports routing on HTTP method via :ref:`header matching
// <envoy_api_msg_route.HeaderMatcher>`.
// [#comment:next free field: 13]
message Route {
// Route matching parameters.
RouteMatch match = 1 [(validate.rules).message.required = true, (gogoproto.nullable) = false];
Expand Down Expand Up @@ -157,6 +163,10 @@ message Route {
// <config_http_conn_man_headers_custom_request_headers>`.
repeated core.HeaderValueOption request_headers_to_add = 9;

// Specifies a list of HTTP headers that should be removed from each request
// matching this route.
repeated string request_headers_to_remove = 12;

// Specifies a set of headers that will be added to responses to requests
// matching this route. Headers specified at this level are applied before
// headers from the enclosing :ref:`envoy_api_msg_route.VirtualHost` and
Expand All @@ -176,6 +186,7 @@ message Route {
// multiple upstream clusters along with weights that indicate the percentage of
// traffic to be forwarded to each cluster. The router selects an upstream cluster based on the
// weights.
// [#comment:next free field: 10]
message WeightedCluster {
message ClusterWeight {
// Name of the upstream cluster. The cluster must exist in the
Expand Down Expand Up @@ -204,6 +215,10 @@ message WeightedCluster {
// <config_http_conn_man_headers_custom_request_headers>`.
repeated core.HeaderValueOption request_headers_to_add = 4;

// Specifies a list of HTTP headers that should be removed from each request when
// this cluster is selected through the enclosing :ref:`envoy_api_msg_route.RouteAction`.
repeated string request_headers_to_remove = 9;

// Specifies a list of headers to be added to responses when this cluster is selected
// through the enclosing :ref:`envoy_api_msg_route.RouteAction`.
// Headers specified at this level are applied before headers from the enclosing
Expand Down
2 changes: 2 additions & 0 deletions docs/root/intro/version_history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Version history
request headers <config_http_conn_man_headers_custom_request_headers>`.
* http: :ref:`hpack_table_size <envoy_api_field_core.Http2ProtocolOptions.hpack_table_size>` now controls
dynamic table size of both: encoder and decoder.
* http: added support for removing request headers using :ref:`request_headers_to_remove
<envoy_api_field_route.Route.request_headers_to_remove>`.
* listeners: added the ability to match :ref:`FilterChain <envoy_api_msg_listener.FilterChain>` using
:ref:`destination_port <envoy_api_field_listener.FilterChainMatch.destination_port>` and
:ref:`prefix_ranges <envoy_api_field_listener.FilterChainMatch.prefix_ranges>`.
Expand Down
12 changes: 8 additions & 4 deletions source/common/router/config_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ RouteEntryImplBase::RouteEntryImplBase(const VirtualHostImpl& vhost,
HeaderParser::configure(route.route().request_headers_to_add())),
route_action_response_headers_parser_(HeaderParser::configure(
route.route().response_headers_to_add(), route.route().response_headers_to_remove())),
request_headers_parser_(HeaderParser::configure(route.request_headers_to_add())),
request_headers_parser_(HeaderParser::configure(route.request_headers_to_add(),
route.request_headers_to_remove())),
response_headers_parser_(HeaderParser::configure(route.response_headers_to_add(),
route.response_headers_to_remove())),
opaque_config_(parseOpaqueConfig(route)), decorator_(parseDecorator(route)),
Expand Down Expand Up @@ -578,7 +579,8 @@ RouteEntryImplBase::WeightedClusterEntry::WeightedClusterEntry(
: DynamicRouteEntry(parent, cluster.name()), runtime_key_(runtime_key),
loader_(factory_context.runtime()),
cluster_weight_(PROTOBUF_GET_WRAPPED_REQUIRED(cluster, weight)),
request_headers_parser_(HeaderParser::configure(cluster.request_headers_to_add())),
request_headers_parser_(HeaderParser::configure(cluster.request_headers_to_add(),
cluster.request_headers_to_remove())),
response_headers_parser_(HeaderParser::configure(cluster.response_headers_to_add(),
cluster.response_headers_to_remove())),
per_filter_configs_(cluster.per_filter_config(), factory_context) {
Expand Down Expand Up @@ -696,7 +698,8 @@ VirtualHostImpl::VirtualHostImpl(const envoy::api::v2::route::VirtualHost& virtu
bool validate_clusters)
: name_(virtual_host.name()), rate_limit_policy_(virtual_host.rate_limits()),
global_route_config_(global_route_config),
request_headers_parser_(HeaderParser::configure(virtual_host.request_headers_to_add())),
request_headers_parser_(HeaderParser::configure(virtual_host.request_headers_to_add(),
virtual_host.request_headers_to_remove())),
response_headers_parser_(HeaderParser::configure(virtual_host.response_headers_to_add(),
virtual_host.response_headers_to_remove())),
per_filter_configs_(virtual_host.per_filter_config(), factory_context) {
Expand Down Expand Up @@ -903,7 +906,8 @@ ConfigImpl::ConfigImpl(const envoy::api::v2::RouteConfiguration& config,
internal_only_headers_.push_back(Http::LowerCaseString(header));
}

request_headers_parser_ = HeaderParser::configure(config.request_headers_to_add());
request_headers_parser_ =
HeaderParser::configure(config.request_headers_to_add(), config.request_headers_to_remove());
response_headers_parser_ = HeaderParser::configure(config.response_headers_to_add(),
config.response_headers_to_remove());
}
Expand Down
3 changes: 2 additions & 1 deletion source/common/upstream/health_checker_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ HttpHealthCheckerImpl::HttpHealthCheckerImpl(const Cluster& cluster,
: HealthCheckerImplBase(cluster, config, dispatcher, runtime, random, std::move(event_logger)),
path_(config.http_health_check().path()), host_value_(config.http_health_check().host()),
request_headers_parser_(
Router::HeaderParser::configure(config.http_health_check().request_headers_to_add())),
Router::HeaderParser::configure(config.http_health_check().request_headers_to_add(),
config.http_health_check().request_headers_to_remove())),
codec_client_type_(codecClientType(config.http_health_check().use_http2())) {
if (!config.http_health_check().service_name().empty()) {
service_name_ = config.http_health_check().service_name();
Expand Down
43 changes: 41 additions & 2 deletions test/common/router/config_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ class TestConfigImpl : public ConfigImpl {

Http::TestHeaderMapImpl genHeaders(const std::string& host, const std::string& path,
const std::string& method) {
return Http::TestHeaderMapImpl{{":authority", host}, {":path", path}, {":method", method}};
return Http::TestHeaderMapImpl{{":authority", host}, {":path", path},
{":method", method}, {"x-safe", "safe"},
{"x-global-nope", "global"}, {"x-vhost-nope", "vhost"},
{"x-route-nope", "route"}};
}

envoy::api::v2::RouteConfiguration parseRouteConfigurationFromJson(const std::string& json_string) {
Expand Down Expand Up @@ -731,7 +734,7 @@ TEST(RouteMatcherTest, TestRequestHeadersToAddWithAppendFalse) {
name: foo
virtual_hosts:
- name: www2
domains: ["*"]
domains: ["www.lyft.com"]
request_headers_to_add:
- header:
key: x-global-header
Expand All @@ -741,6 +744,7 @@ name: foo
key: x-vhost-header
value: vhost-www2
append: false
request_headers_to_remove: ["x-vhost-nope"]
routes:
- match: { prefix: "/endpoint" }
request_headers_to_add:
Expand All @@ -756,6 +760,7 @@ name: foo
key: x-route-header
value: route-endpoint
append: false
request_headers_to_remove: ["x-route-nope"]
route:
cluster: www2
request_headers_to_add:
Expand All @@ -776,11 +781,17 @@ name: foo
append: false
- match: { prefix: "/" }
route: { cluster: www2 }
- name: default
domains: ["*"]
routes:
- match: { prefix: "/" }
route: { cluster: default }
request_headers_to_add:
- header:
key: x-global-header
value: global
append: false
request_headers_to_remove: ["x-global-nope"]
)EOF";

NiceMock<Server::Configuration::MockFactoryContext> factory_context;
Expand All @@ -797,19 +808,47 @@ name: foo
Http::TestHeaderMapImpl headers = genHeaders("www.lyft.com", "/endpoint", "GET");
const RouteEntry* route = config.route(headers, 0)->routeEntry();
route->finalizeRequestHeaders(headers, request_info, true);
// Added headers.
EXPECT_EQ("global", headers.get_("x-global-header"));
EXPECT_EQ("vhost-www2", headers.get_("x-vhost-header"));
EXPECT_EQ("route-endpoint", headers.get_("x-route-header"));
EXPECT_EQ("route-action-endpoint", headers.get_("x-route-action-header"));
// Removed headers.
EXPECT_FALSE(headers.has("x-global-nope"));
EXPECT_FALSE(headers.has("x-vhost-nope"));
EXPECT_FALSE(headers.has("x-route-nope"));
}

// Global overrides virtual host.
{
Http::TestHeaderMapImpl headers = genHeaders("www.lyft.com", "/", "GET");
const RouteEntry* route = config.route(headers, 0)->routeEntry();
route->finalizeRequestHeaders(headers, request_info, true);
// Added headers.
EXPECT_EQ("global", headers.get_("x-global-header"));
EXPECT_EQ("vhost-www2", headers.get_("x-vhost-header"));
EXPECT_FALSE(headers.has("x-route-header"));
EXPECT_FALSE(headers.has("x-route-action-header"));
// Removed headers.
EXPECT_FALSE(headers.has("x-global-nope"));
EXPECT_FALSE(headers.has("x-vhost-nope"));
EXPECT_TRUE(headers.has("x-route-nope"));
}

// Global only.
{
Http::TestHeaderMapImpl headers = genHeaders("www.example.com", "/", "GET");
const RouteEntry* route = config.route(headers, 0)->routeEntry();
route->finalizeRequestHeaders(headers, request_info, true);
// Added headers.
EXPECT_EQ("global", headers.get_("x-global-header"));
EXPECT_FALSE(headers.has("x-vhost-header"));
EXPECT_FALSE(headers.has("x-route-header"));
EXPECT_FALSE(headers.has("x-route-action-header"));
// Removed headers.
EXPECT_FALSE(headers.has("x-global-nope"));
EXPECT_TRUE(headers.has("x-vhost-nope"));
EXPECT_TRUE(headers.has("x-route-nope"));
}
}
}
Expand Down
65 changes: 35 additions & 30 deletions test/common/router/header_formatter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -534,39 +534,41 @@ TEST(HeaderParserTest, EvaluateCompoundHeaders) {
match: { prefix: "/new_endpoint" }
route:
cluster: www2
request_headers_to_add:
- header:
key: "x-prefix"
value: "prefix-%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
- header:
key: "x-suffix"
value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%-suffix"
- header:
key: "x-both"
value: "prefix-%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%-suffix"
- header:
key: "x-escaping-1"
value: "%%%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%%%"
- header:
key: "x-escaping-2"
value: "%%%%%%"
- header:
key: "x-multi"
value: "%PROTOCOL% from %DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
- header:
key: "x-multi-back-to-back"
value: "%PROTOCOL%%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
- header:
key: "x-metadata"
value: "%UPSTREAM_METADATA([\"namespace\", \"%key%\"])%"
- header:
key: "x-per-request"
value: "%PER_REQUEST_STATE(testing)%"
request_headers_to_add:
- header:
key: "x-prefix"
value: "prefix-%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
- header:
key: "x-suffix"
value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%-suffix"
- header:
key: "x-both"
value: "prefix-%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%-suffix"
- header:
key: "x-escaping-1"
value: "%%%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%%%"
- header:
key: "x-escaping-2"
value: "%%%%%%"
- header:
key: "x-multi"
value: "%PROTOCOL% from %DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
- header:
key: "x-multi-back-to-back"
value: "%PROTOCOL%%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
- header:
key: "x-metadata"
value: "%UPSTREAM_METADATA([\"namespace\", \"%key%\"])%"
- header:
key: "x-per-request"
value: "%PER_REQUEST_STATE(testing)%"
request_headers_to_remove: ["x-nope"]
)EOF";

const auto route = parseRouteFromV2Yaml(yaml);
HeaderParserPtr req_header_parser =
HeaderParser::configure(parseRouteFromV2Yaml(yaml).route().request_headers_to_add());
Http::TestHeaderMapImpl header_map{{":method", "POST"}};
HeaderParser::configure(route.request_headers_to_add(), route.request_headers_to_remove());
Http::TestHeaderMapImpl header_map{{":method", "POST"}, {"x-safe", "safe"}, {"x-nope", "nope"}};
NiceMock<Envoy::RequestInfo::MockRequestInfo> request_info;
absl::optional<Envoy::Http::Protocol> protocol = Envoy::Http::Protocol::Http11;
ON_CALL(request_info, protocol()).WillByDefault(ReturnPointee(&protocol));
Expand Down Expand Up @@ -618,6 +620,9 @@ match: { prefix: "/new_endpoint" }

EXPECT_TRUE(header_map.has("x-per-request"));
EXPECT_EQ("test_value", header_map.get_("x-per-request"));

EXPECT_TRUE(header_map.has("x-safe"));
EXPECT_FALSE(header_map.has("x-nope"));
}

TEST(HeaderParserTest, EvaluateHeadersWithAppendFalse) {
Expand Down
Loading