diff --git a/api/envoy/api/v2/core/health_check.proto b/api/envoy/api/v2/core/health_check.proto index ad35ca61536a0..199a5b0dc1892 100644 --- a/api/envoy/api/v2/core/health_check.proto +++ b/api/envoy/api/v2/core/health_check.proto @@ -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 @@ -100,6 +101,10 @@ message HealthCheck { // `. 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; } diff --git a/api/envoy/api/v2/rds.proto b/api/envoy/api/v2/rds.proto index 00ac0145b301a..f02735e22f701 100644 --- a/api/envoy/api/v2/rds.proto +++ b/api/envoy/api/v2/rds.proto @@ -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 @@ -76,6 +77,10 @@ message RouteConfiguration { // `. 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 diff --git a/api/envoy/api/v2/route/route.proto b/api/envoy/api/v2/route/route.proto index 8755338c55aea..d2adc062f3869 100644 --- a/api/envoy/api/v2/route/route.proto +++ b/api/envoy/api/v2/route/route.proto @@ -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. @@ -81,6 +82,10 @@ message VirtualHost { // `. 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 @@ -113,6 +118,7 @@ message VirtualHost { // // Envoy supports routing on HTTP method via :ref:`header matching // `. +// [#comment:next free field: 13] message Route { // Route matching parameters. RouteMatch match = 1 [(validate.rules).message.required = true, (gogoproto.nullable) = false]; @@ -157,6 +163,10 @@ message Route { // `. 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 @@ -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 @@ -204,6 +215,10 @@ message WeightedCluster { // `. 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 diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index a9562aaf25237..6ecdc64c73402 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -53,6 +53,8 @@ Version history request headers `. * http: :ref:`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 + `. * listeners: added the ability to match :ref:`FilterChain ` using :ref:`destination_port ` and :ref:`prefix_ranges `. diff --git a/source/common/router/config_impl.cc b/source/common/router/config_impl.cc index 7931a54afd829..0ca62855d1bce 100644 --- a/source/common/router/config_impl.cc +++ b/source/common/router/config_impl.cc @@ -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)), @@ -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) { @@ -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) { @@ -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()); } diff --git a/source/common/upstream/health_checker_impl.cc b/source/common/upstream/health_checker_impl.cc index a4f0f8714d740..22f179b76f1f3 100644 --- a/source/common/upstream/health_checker_impl.cc +++ b/source/common/upstream/health_checker_impl.cc @@ -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(); diff --git a/test/common/router/config_impl_test.cc b/test/common/router/config_impl_test.cc index b063d6408bf79..219e9c57a7a5d 100644 --- a/test/common/router/config_impl_test.cc +++ b/test/common/router/config_impl_test.cc @@ -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) { @@ -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 @@ -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: @@ -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: @@ -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 factory_context; @@ -797,10 +808,15 @@ 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. @@ -808,8 +824,31 @@ name: foo 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")); } } } diff --git a/test/common/router/header_formatter_test.cc b/test/common/router/header_formatter_test.cc index 9c9048e6194e0..ece062a3fd0e9 100644 --- a/test/common/router/header_formatter_test.cc +++ b/test/common/router/header_formatter_test.cc @@ -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 request_info; absl::optional protocol = Envoy::Http::Protocol::Http11; ON_CALL(request_info, protocol()).WillByDefault(ReturnPointee(&protocol)); @@ -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) { diff --git a/test/common/upstream/health_checker_impl_test.cc b/test/common/upstream/health_checker_impl_test.cc index 5b7f6f3ab34e4..ae3b339c883ac 100644 --- a/test/common/upstream/health_checker_impl_test.cc +++ b/test/common/upstream/health_checker_impl_test.cc @@ -351,6 +351,30 @@ class HttpHealthCheckerImplTest : public testing::Test { }); } + void setupServiceValidationWithoutUserAgent() { + std::string yaml = R"EOF( + timeout: 1s + interval: 1s + interval_jitter: 1s + unhealthy_threshold: 2 + healthy_threshold: 2 + http_health_check: + service_name: locations + path: /healthcheck + host: "www.envoyproxy.io" + # The following entry removes the default "user-agent" header. + request_headers_to_remove: ["user-agent"] + )EOF"; + + health_checker_.reset(new TestHttpHealthCheckerImpl(*cluster_, parseHealthCheckFromV2Yaml(yaml), + dispatcher_, runtime_, random_, + HealthCheckEventLoggerPtr(event_logger_))); + health_checker_->addHostCheckCompleteCb( + [this](HostSharedPtr host, HealthTransition changed_state) -> void { + onHostStatus(host, changed_state); + }); + } + void expectSessionCreate(const HostWithHealthCheckMap& health_check_map) { // Expectations are in LIFO order. TestSessionPtr new_test_session(new TestSession()); @@ -794,6 +818,46 @@ TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheckWithAdditionalHeaders) { test_sessions_[0]->interval_timer_->callback_(); } +TEST_F(HttpHealthCheckerImplTest, SuccessServiceCheckWithoutUserAgent) { + setupServiceValidationWithoutUserAgent(); + // requires non-empty `service_name` in config. + EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) + .WillOnce(Return(true)); + + EXPECT_CALL(*this, onHostStatus(_, HealthTransition::Unchanged)).Times(1); + auto metadata = TestUtility::parseYaml( + R"EOF( + filter_metadata: + namespace: + key: value + )EOF"); + + std::string current_start_time; + cluster_->prioritySet().getMockHostSet(0)->hosts_ = { + makeTestHost(cluster_->info_, "tcp://127.0.0.1:80", metadata)}; + cluster_->info_->stats().upstream_cx_total_.inc(); + expectSessionCreate(); + expectStreamCreate(0); + EXPECT_CALL(*test_sessions_[0]->timeout_timer_, enableTimer(_)); + EXPECT_CALL(test_sessions_[0]->request_encoder_, encodeHeaders(_, true)) + .WillRepeatedly(Invoke( + [&](const Http::HeaderMap& headers, bool) { EXPECT_EQ(headers.UserAgent(), nullptr); })); + health_checker_->start(); + + EXPECT_CALL(runtime_.snapshot_, getInteger("health_check.max_interval", _)); + EXPECT_CALL(runtime_.snapshot_, getInteger("health_check.min_interval", _)) + .WillOnce(Return(45000)); + EXPECT_CALL(*test_sessions_[0]->interval_timer_, enableTimer(std::chrono::milliseconds(45000))); + EXPECT_CALL(*test_sessions_[0]->timeout_timer_, disableTimer()); + absl::optional health_checked_cluster("locations-production-iad"); + respond(0, "200", false, true, false, health_checked_cluster); + EXPECT_TRUE(cluster_->prioritySet().getMockHostSet(0)->hosts_[0]->healthy()); + + EXPECT_CALL(*test_sessions_[0]->timeout_timer_, enableTimer(_)); + expectStreamCreate(0); + test_sessions_[0]->interval_timer_->callback_(); +} + TEST_F(HttpHealthCheckerImplTest, ServiceDoesNotMatchFail) { setupServiceValidationHC(); EXPECT_CALL(runtime_.snapshot_, featureEnabled("health_check.verify_cluster", 100)) diff --git a/test/integration/header_integration_test.cc b/test/integration/header_integration_test.cc index 29eaf276d9716..a1f5fe7b51cc7 100644 --- a/test/integration/header_integration_test.cc +++ b/test/integration/header_integration_test.cc @@ -51,6 +51,7 @@ stat_prefix: header_test - header: key: "x-vhost-request" value: "vhost" + request_headers_to_remove: ["x-vhost-request-remove"] response_headers_to_add: - header: key: "x-vhost-response" @@ -60,28 +61,30 @@ stat_prefix: header_test - match: { prefix: "/vhost-only" } route: { cluster: "cluster_0" } - match: { prefix: "/vhost-and-route" } + request_headers_to_add: + - header: + key: "x-route-request" + value: "route" + request_headers_to_remove: ["x-route-request-remove"] + response_headers_to_add: + - header: + key: "x-route-response" + value: "route" + response_headers_to_remove: ["x-route-response-remove"] route: cluster: cluster_0 - request_headers_to_add: - - header: - key: "x-route-request" - value: "route" - response_headers_to_add: - - header: - key: "x-route-response" - value: "route" - response_headers_to_remove: ["x-route-response-remove"] - match: { prefix: "/vhost-route-and-weighted-clusters" } + request_headers_to_add: + - header: + key: "x-route-request" + value: "route" + request_headers_to_remove: ["x-route-request-remove"] + response_headers_to_add: + - header: + key: "x-route-response" + value: "route" + response_headers_to_remove: ["x-route-response-remove"] route: - request_headers_to_add: - - header: - key: "x-route-request" - value: "route" - response_headers_to_add: - - header: - key: "x-route-response" - value: "route" - response_headers_to_remove: ["x-route-response-remove"] weighted_clusters: clusters: - name: cluster_0 @@ -90,6 +93,7 @@ stat_prefix: header_test - header: key: "x-weighted-cluster-request" value: "weighted-cluster-1" + request_headers_to_remove: ["x-weighted-cluster-request-remove"] response_headers_to_add: - header: key: "x-weighted-cluster-response" @@ -99,17 +103,18 @@ stat_prefix: header_test domains: ["route-headers.com"] routes: - match: { prefix: "/route-only" } + request_headers_to_add: + - header: + key: "x-route-request" + value: "route" + request_headers_to_remove: ["x-route-request-remove"] + response_headers_to_add: + - header: + key: "x-route-response" + value: "route" + response_headers_to_remove: ["x-route-response-remove"] route: cluster: cluster_0 - request_headers_to_add: - - header: - key: "x-route-request" - value: "route" - response_headers_to_add: - - header: - key: "x-route-response" - value: "route" - response_headers_to_remove: ["x-route-response-remove"] - name: xff-headers domains: ["xff-headers.com"] routes: @@ -259,6 +264,7 @@ class HeaderIntegrationTest route_config->add_response_headers_to_remove("x-routeconfig-response-remove"); addHeader(route_config->mutable_request_headers_to_add(), "x-routeconfig-request", "routeconfig", append); + route_config->add_request_headers_to_remove("x-routeconfig-request-remove"); } if (use_eds_) { @@ -271,14 +277,14 @@ class HeaderIntegrationTest addHeader(vhost.mutable_response_headers_to_add(), "x-vhost-dynamic", "vhost:%UPSTREAM_METADATA([\"test.namespace\", \"key\"])%", append); - for (auto& rte : *vhost.mutable_routes()) { - if (rte.has_route()) { - auto* mutable_rte = rte.mutable_route(); - addHeader(mutable_rte->mutable_response_headers_to_add(), "x-route-dynamic", - "route:%UPSTREAM_METADATA([\"test.namespace\", \"key\"])%", append); + for (auto& route : *vhost.mutable_routes()) { + addHeader(route.mutable_response_headers_to_add(), "x-route-dynamic", + "route:%UPSTREAM_METADATA([\"test.namespace\", \"key\"])%", append); - if (mutable_rte->has_weighted_clusters()) { - for (auto& c : *mutable_rte->mutable_weighted_clusters()->mutable_clusters()) { + if (route.has_route()) { + auto* route_action = route.mutable_route(); + if (route_action->has_weighted_clusters()) { + for (auto& c : *route_action->mutable_weighted_clusters()->mutable_clusters()) { addHeader(c.mutable_response_headers_to_add(), "x-weighted-cluster-dynamic", "weighted:%UPSTREAM_METADATA([\"test.namespace\", \"key\"])%", append); @@ -299,15 +305,18 @@ class HeaderIntegrationTest disableHeaderValueOptionAppend(*vhost.mutable_request_headers_to_add()); disableHeaderValueOptionAppend(*vhost.mutable_response_headers_to_add()); - for (auto& rte : *vhost.mutable_routes()) { - if (rte.has_route()) { - auto* mutable_rte = rte.mutable_route(); + for (auto& route : *vhost.mutable_routes()) { + disableHeaderValueOptionAppend(*route.mutable_request_headers_to_add()); + disableHeaderValueOptionAppend(*route.mutable_response_headers_to_add()); + + if (route.has_route()) { + auto* route_action = route.mutable_route(); - disableHeaderValueOptionAppend(*mutable_rte->mutable_request_headers_to_add()); - disableHeaderValueOptionAppend(*mutable_rte->mutable_response_headers_to_add()); + disableHeaderValueOptionAppend(*route_action->mutable_request_headers_to_add()); + disableHeaderValueOptionAppend(*route_action->mutable_response_headers_to_add()); - if (mutable_rte->has_weighted_clusters()) { - for (auto& c : *mutable_rte->mutable_weighted_clusters()->mutable_clusters()) { + if (route_action->has_weighted_clusters()) { + for (auto& c : *route_action->mutable_weighted_clusters()->mutable_clusters()) { disableHeaderValueOptionAppend(*c.mutable_request_headers_to_add()); disableHeaderValueOptionAppend(*c.mutable_response_headers_to_add()); } @@ -457,6 +466,7 @@ TEST_P(HeaderIntegrationTest, TestVirtualHostAppendHeaderManipulation) { {":scheme", "http"}, {":authority", "vhost-headers.com"}, {"x-vhost-request", "downstream"}, + {"x-vhost-request-remove", "downstream"}, }, Http::TestHeaderMapImpl{ {":authority", "vhost-headers.com"}, @@ -524,6 +534,7 @@ TEST_P(HeaderIntegrationTest, TestRouteAppendHeaderManipulation) { {":scheme", "http"}, {":authority", "route-headers.com"}, {"x-route-request", "downstream"}, + {"x-route-request-remove", "downstream"}, }, Http::TestHeaderMapImpl{ {":authority", "route-headers.com"}, @@ -557,6 +568,7 @@ TEST_P(HeaderIntegrationTest, TestRouteReplaceHeaderManipulation) { {":scheme", "http"}, {":authority", "route-headers.com"}, {"x-route-request", "downstream"}, + {"x-route-request-remove", "downstream"}, {"x-unmodified", "downstream"}, }, Http::TestHeaderMapImpl{ @@ -592,7 +604,9 @@ TEST_P(HeaderIntegrationTest, TestVirtualHostAndRouteAppendHeaderManipulation) { {":scheme", "http"}, {":authority", "vhost-headers.com"}, {"x-vhost-request", "downstream"}, + {"x-vhost-request-remove", "downstream"}, {"x-route-request", "downstream"}, + {"x-route-request-remove", "downstream"}, }, Http::TestHeaderMapImpl{ {":authority", "vhost-headers.com"}, @@ -671,8 +685,11 @@ TEST_P(HeaderIntegrationTest, TestRouteConfigVirtualHostAndRouteAppendHeaderMani {":scheme", "http"}, {":authority", "vhost-headers.com"}, {"x-routeconfig-request", "downstream"}, + {"x-routeconfig-request-remove", "downstream"}, {"x-vhost-request", "downstream"}, + {"x-vhost-request-remove", "downstream"}, {"x-route-request", "downstream"}, + {"x-route-request-remove", "downstream"}, }, Http::TestHeaderMapImpl{ {":authority", "vhost-headers.com"}, @@ -762,9 +779,13 @@ TEST_P(HeaderIntegrationTest, TestRouteConfigVirtualHostRouteAndClusterAppendHea {":scheme", "http"}, {":authority", "vhost-headers.com"}, {"x-routeconfig-request", "downstream"}, + {"x-routeconfig-request-remove", "downstream"}, {"x-vhost-request", "downstream"}, + {"x-vhost-request-remove", "downstream"}, {"x-route-request", "downstream"}, + {"x-route-request-remove", "downstream"}, {"x-weighted-cluster-request", "downstream"}, + {"x-weighted-cluster-request-remove", "downstream"}, }, Http::TestHeaderMapImpl{ {":authority", "vhost-headers.com"},