Skip to content

Commit 17defa5

Browse files
authored
Backport: router: fix a bug where header mutations may not be processed properly (#40856) (#40904)
Commit Message: router: fix a bug where header mutations may not be processed properly Additional Description: The #39534 introduced a bug where the `response_headers_to_add`` may be processed multiple times for local responses from the router filter. The sendLocalReply method will call the `finalizeResponseHeaders()` and the #39534 updated the code and make the `finalizeResponseHeaders()` be called in the modify_headers_ callback. This finally resulted in this problem. Risk Level: low. Testing: integration. Docs Changes: n/a. Release Notes: added. Platform Specific Features: n/a. Signed-off-by: WangBaiping <[email protected]> Signed-off-by: code <[email protected]>
1 parent 21c88a1 commit 17defa5

File tree

4 files changed

+44
-7
lines changed

4 files changed

+44
-7
lines changed

changelogs/current.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ minor_behavior_changes:
88

99
bug_fixes:
1010
# *Changes expected to improve the state of the world and are unlikely to have negative effects*
11+
- area: http
12+
change: |
13+
Fixed a bug where the ``response_headers_to_add`` may be processed multiple times for the local responses from
14+
the router filter.
1115
1216
removed_config_or_runtime:
1317
# *Normally occurs at the end of the* :ref:`deprecation period <deprecated>`

source/common/router/router.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,8 +455,6 @@ Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers,
455455
modify_headers_from_upstream_lb_(headers);
456456
}
457457

458-
route_entry_->finalizeResponseHeaders(headers, callbacks_->streamInfo());
459-
460458
if (attempt_count_ == 0 || !route_entry_->includeAttemptCountInResponse()) {
461459
return;
462460
}
@@ -1758,6 +1756,7 @@ void Filter::onUpstreamHeaders(uint64_t response_code, Http::ResponseHeaderMapPt
17581756

17591757
// Modify response headers after we have set the final upstream info because we may need to
17601758
// modify the headers based on the upstream host.
1759+
route_entry_->finalizeResponseHeaders(*headers, callbacks_->streamInfo());
17611760
modify_headers_(*headers);
17621761

17631762
if (end_stream) {

test/common/router/router_test.cc

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,6 @@ TEST_F(RouterTest, NoHost) {
811811
EXPECT_CALL(callbacks_, encodeData(_, true));
812812
EXPECT_CALL(callbacks_.stream_info_,
813813
setResponseFlag(StreamInfo::CoreResponseFlag::NoHealthyUpstream));
814-
EXPECT_CALL(callbacks_.route_->route_entry_, finalizeResponseHeaders(_, _));
815814

816815
Http::TestRequestHeaderMapImpl headers;
817816
HttpTestUtility::addDefaultHeaders(headers);
@@ -846,7 +845,6 @@ TEST_F(RouterTest, MaintenanceMode) {
846845
EXPECT_CALL(callbacks_, encodeData(_, true));
847846
EXPECT_CALL(callbacks_.stream_info_,
848847
setResponseFlag(StreamInfo::CoreResponseFlag::UpstreamOverflow));
849-
EXPECT_CALL(callbacks_.route_->route_entry_, finalizeResponseHeaders(_, _));
850848

851849
Http::TestRequestHeaderMapImpl headers;
852850
HttpTestUtility::addDefaultHeaders(headers);
@@ -894,7 +892,6 @@ TEST_F(RouterTest, DropOverloadDropped) {
894892
EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), false));
895893
EXPECT_CALL(callbacks_, encodeData(_, true));
896894
EXPECT_CALL(callbacks_.stream_info_, setResponseFlag(StreamInfo::CoreResponseFlag::DropOverLoad));
897-
EXPECT_CALL(callbacks_.route_->route_entry_, finalizeResponseHeaders(_, _));
898895

899896
Http::TestRequestHeaderMapImpl headers;
900897
HttpTestUtility::addDefaultHeaders(headers);
@@ -1226,7 +1223,6 @@ TEST_F(RouterTest, AllDebugConfig) {
12261223
Http::TestResponseHeaderMapImpl response_headers{{":status", "204"},
12271224
{"x-envoy-not-forwarded", "true"}};
12281225
EXPECT_CALL(callbacks_, encodeHeaders_(HeaderMapEqualRef(&response_headers), true));
1229-
EXPECT_CALL(callbacks_.route_->route_entry_, finalizeResponseHeaders(_, _));
12301226

12311227
Http::TestRequestHeaderMapImpl headers;
12321228
HttpTestUtility::addDefaultHeaders(headers);
@@ -3332,7 +3328,6 @@ TEST_F(RouterTest, RetryNoneHealthy) {
33323328
EXPECT_CALL(callbacks_, encodeData(_, true));
33333329
EXPECT_CALL(callbacks_.stream_info_,
33343330
setResponseFlag(StreamInfo::CoreResponseFlag::NoHealthyUpstream));
3335-
EXPECT_CALL(callbacks_.route_->route_entry_, finalizeResponseHeaders(_, _));
33363331
router_->retry_state_->callback_();
33373332
EXPECT_TRUE(verifyHostUpstreamStats(0, 1));
33383333
// Pool failure for the first try, so only 1 upstream request was made.

test/integration/header_integration_test.cc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,45 @@ TEST_P(HeaderIntegrationTest, TestDynamicHeaders) {
10531053
});
10541054
}
10551055

1056+
TEST_P(HeaderIntegrationTest, TestResponseHeadersOnlyBeHandledOnce) {
1057+
initializeFilter(HeaderMode::Append, false);
1058+
registerTestServerPorts({"http"});
1059+
codec_client_ = makeHttpConnection(makeClientConnection(lookupPort("http")));
1060+
1061+
auto encoder_decoder = codec_client_->startRequest(Http::TestRequestHeaderMapImpl{
1062+
{":method", "POST"},
1063+
{":path", "/vhost-and-route"},
1064+
{":scheme", "http"},
1065+
{":authority", "vhost-headers.com"},
1066+
});
1067+
auto response = std::move(encoder_decoder.second);
1068+
1069+
ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
1070+
1071+
ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));
1072+
ASSERT_TRUE(upstream_request_->waitForHeadersComplete());
1073+
ASSERT_TRUE(fake_upstream_connection_->close());
1074+
ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
1075+
ASSERT_TRUE(response->waitForEndStream());
1076+
1077+
if (downstream_protocol_ == Http::CodecType::HTTP1) {
1078+
ASSERT_TRUE(codec_client_->waitForDisconnect());
1079+
} else {
1080+
codec_client_->close();
1081+
}
1082+
1083+
EXPECT_FALSE(upstream_request_->complete());
1084+
EXPECT_EQ(0U, upstream_request_->bodyLength());
1085+
1086+
EXPECT_TRUE(response->complete());
1087+
1088+
Http::TestResponseHeaderMapImpl response_headers{response->headers()};
1089+
EXPECT_EQ(1, response_headers.get(Http::LowerCaseString("x-route-response")).size());
1090+
EXPECT_EQ("route", response_headers.get_("x-route-response"));
1091+
EXPECT_EQ(1, response_headers.get(Http::LowerCaseString("x-vhost-response")).size());
1092+
EXPECT_EQ("vhost", response_headers.get_("x-vhost-response"));
1093+
}
1094+
10561095
// Validates that XFF gets properly parsed.
10571096
TEST_P(HeaderIntegrationTest, TestXFFParsing) {
10581097
initializeFilter(HeaderMode::Replace, false);

0 commit comments

Comments
 (0)