Skip to content

Commit 26ec87e

Browse files
test(ext_proc): add integration test for BUFFERED→FULL_DUPLEX_STREAMED mode override with empty chunked response body
Regression test for the fix in processor_state.cc that changed hasBufferedData() (length > 0) to bufferedData() (non-null check) in sendBufferedDataInStreamedMode. Without the fix, an empty upstream body arriving before the ext_proc header response caused the filter to hang because continueIfNecessary() was never called. The test verifies that when mode_override changes response_body_mode from BUFFERED to FULL_DUPLEX_STREAMED and the upstream sends a 0-byte DATA frame before ext_proc responds to the headers, the empty body is correctly forwarded to ext_proc and the downstream receives the response. Signed-off-by: Renuka Fernando <renukapiyumal@gmail.com>
1 parent cd90116 commit 26ec87e

1 file changed

Lines changed: 49 additions & 0 deletions

File tree

test/extensions/filters/http/ext_proc/ext_proc_full_duplex_integration_test.cc

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,55 @@ TEST_P(ExtProcIntegrationTest, TwoExtProcFiltersBothDuplexInBothDirectionWithTra
970970
EXPECT_THAT(response->headers(), ContainsHeader("x-new-header_1", "new_1"));
971971
}
972972

973+
// test for: mode_override changes response_body_mode from BUFFERED to
974+
// FULL_DUPLEX_STREAMED, but the upstream has already sent a chunked empty body (0-byte DATA frame
975+
// with end_stream=true) before the ext_proc server responds to the response headers.
976+
TEST_P(ExtProcIntegrationTest, ModeOverrideBufferedToFullDuplexChunkedEmptyResponseBody) {
977+
proto_config_.mutable_processing_mode()->set_response_body_mode(ProcessingMode::BUFFERED);
978+
proto_config_.set_allow_mode_override(true);
979+
initializeConfig();
980+
HttpIntegrationTest::initialize();
981+
982+
// GET request with no body.
983+
auto response = sendDownstreamRequest(absl::nullopt);
984+
985+
// ext_proc processes request headers (first gRPC message on the side stream).
986+
processRequestHeadersMessage(*grpc_upstreams_[0], true, absl::nullopt);
987+
988+
// Upstream connection: send response headers with end_stream=false (simulating chunked
989+
// transfer encoding), then immediately send an empty DATA frame with end_stream=true.
990+
// Both frames arrive *before* the ext_proc server responds to the response headers.
991+
ASSERT_TRUE(fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, fake_upstream_connection_));
992+
ASSERT_TRUE(fake_upstream_connection_->waitForNewStream(*dispatcher_, upstream_request_));
993+
ASSERT_TRUE(upstream_request_->waitForEndStream(*dispatcher_));
994+
upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}}, false);
995+
upstream_request_->encodeData(0, true);
996+
997+
// ext_proc now receives the response headers message. Respond with mode_override that changes
998+
// response_body_mode from BUFFERED to FULL_DUPLEX_STREAMED.
999+
processGenericMessage(
1000+
*grpc_upstreams_[0], false, [](const ProcessingRequest& req, ProcessingResponse& resp) {
1001+
EXPECT_TRUE(req.has_response_headers());
1002+
resp.mutable_response_headers();
1003+
resp.mutable_mode_override()->set_response_body_mode(ProcessingMode::FULL_DUPLEX_STREAMED);
1004+
return true;
1005+
});
1006+
1007+
processResponseBodyMessage(
1008+
*grpc_upstreams_[0], false, [](const HttpBody& body, BodyResponse& resp) {
1009+
EXPECT_TRUE(body.end_of_stream());
1010+
EXPECT_EQ(body.body().size(), 0);
1011+
auto* streamed_response =
1012+
resp.mutable_response()->mutable_body_mutation()->mutable_streamed_response();
1013+
streamed_response->set_body("");
1014+
streamed_response->set_end_of_stream(true);
1015+
return true;
1016+
});
1017+
1018+
// Downstream should receive the response without hanging.
1019+
verifyDownstreamResponse(*response, 200);
1020+
}
1021+
9731022
} // namespace ExternalProcessing
9741023
} // namespace HttpFilters
9751024
} // namespace Extensions

0 commit comments

Comments
 (0)