Add `unknown_transfer_encoding` and `multiple_transfer_encoding_keys` defects and integration tests for `validate_transfer_encoding` flag. Verifies the behavior of invalid `Transfer-Encoding` headers (multiple headers, unknown encodings, and identity encoding) under the `balsa_validate_transfer_encoding` flag. **Invalid Requests (Multiple TE Keys / Unknown TE Value):** Ensures that requests with multiple Transfer-Encoding keys or unknown values are handled correctly. - Guard is ON: Rejection happens early in BalsaFrame (`400 Bad Request`) with details `http1.codec_error`. - Guard is OFF: Request bypasses Balsa's strict check, but is caught and rejected later by Envoy's core codec (`400 Bad Request`) with details `http1.invalid_transfer_encoding`. - Note on Defects: The newly added defects are NOT exported to Access Logs in either case because Balsa's early bailout prevents the visitor callbacks that copy defects to FilterState. **Identity Transfer Encoding:** Ensures that `Transfer-Encoding: identity` is allowed by BalsaFrame but ultimately rejected by Envoy's core codec as an invalid transfer encoding (`400 Bad Request`) regardless of Guard state as Envoy only allows `chunked` as the valid `Transfer-Encoding` value. **Invalid Responses (Multiple TE Keys / Unknown TE Value):** Ensures that responses from backends/upstreams with multiple TE keys or unknown values are handled correctly. - Guard is ON: Response is rejected with `502` by BalsaFrame. - Guard is OFF: Response is allowed by BalsaFrame early check, but Envoy core codec handles validation and rejects it later (`502`). PiperOrigin-RevId: 970595413
diff --git a/quiche/balsa/balsa_frame.cc b/quiche/balsa/balsa_frame.cc index af17add..f2fdba4 100644 --- a/quiche/balsa/balsa_frame.cc +++ b/quiche/balsa/balsa_frame.cc
@@ -737,7 +737,6 @@ headers_->transfer_encoding_is_chunked_ = false; return; } - if (http_validation_policy().validate_transfer_encoding) { HandleError(BalsaFrameEnums::UNKNOWN_TRANSFER_ENCODING); } @@ -870,11 +869,12 @@ continue; } if (absl::EqualsIgnoreCase(key, kTransferEncoding)) { - if (http_validation_policy().validate_transfer_encoding && - transfer_encoding_idx != 0) { - QUICHE_CODE_COUNT(multiple_transfer_encoding_keys_rejected); - HandleError(BalsaFrameEnums::MULTIPLE_TRANSFER_ENCODING_KEYS); - return; + if (transfer_encoding_idx != 0) { + if (http_validation_policy().validate_transfer_encoding) { + QUICHE_CODE_COUNT(multiple_transfer_encoding_keys_rejected); + HandleError(BalsaFrameEnums::MULTIPLE_TRANSFER_ENCODING_KEYS); + return; + } } transfer_encoding_idx = i + 1; }
diff --git a/quiche/balsa/balsa_frame_test.cc b/quiche/balsa/balsa_frame_test.cc index 962ec61..602379e 100644 --- a/quiche/balsa/balsa_frame_test.cc +++ b/quiche/balsa/balsa_frame_test.cc
@@ -3775,6 +3775,7 @@ EXPECT_TRUE(balsa_frame_.Error()); EXPECT_EQ(BalsaFrameEnums::MULTIPLE_TRANSFER_ENCODING_KEYS, balsa_frame_.ErrorCode()); + EXPECT_TRUE(balsa_frame_.protocol_defects().multiple_transfer_encoding_keys); } TEST_F(HTTPBalsaFrameTest, AcceptTwoTransferEncodingHeaders) { @@ -3793,6 +3794,7 @@ EXPECT_FALSE(balsa_frame_.Error()); EXPECT_EQ(BalsaFrameEnums::BALSA_NO_ERROR, balsa_frame_.ErrorCode()); + EXPECT_TRUE(balsa_frame_.protocol_defects().multiple_transfer_encoding_keys); } TEST_F(HTTPBalsaFrameTest, TwoTransferEncodingTokensIsAnError) { @@ -3806,6 +3808,7 @@ EXPECT_TRUE(balsa_frame_.Error()); EXPECT_EQ(BalsaFrameEnums::UNKNOWN_TRANSFER_ENCODING, balsa_frame_.ErrorCode()); + EXPECT_TRUE(balsa_frame_.protocol_defects().unknown_transfer_encoding); } TEST_F(HTTPBalsaFrameTest, AcceptTwoTransferEncodingTokens) { @@ -3823,6 +3826,7 @@ EXPECT_FALSE(balsa_frame_.Error()); EXPECT_EQ(BalsaFrameEnums::BALSA_NO_ERROR, balsa_frame_.ErrorCode()); + EXPECT_TRUE(balsa_frame_.protocol_defects().unknown_transfer_encoding); } TEST_F(HTTPBalsaFrameTest, UnknownTransferEncodingTokenIsAnError) { @@ -3836,6 +3840,7 @@ EXPECT_TRUE(balsa_frame_.Error()); EXPECT_EQ(BalsaFrameEnums::UNKNOWN_TRANSFER_ENCODING, balsa_frame_.ErrorCode()); + EXPECT_TRUE(balsa_frame_.protocol_defects().unknown_transfer_encoding); } TEST_F(HTTPBalsaFrameTest, AcceptUnknownTransferEncodingToken) { @@ -3853,6 +3858,7 @@ EXPECT_FALSE(balsa_frame_.Error()); EXPECT_EQ(BalsaFrameEnums::BALSA_NO_ERROR, balsa_frame_.ErrorCode()); + EXPECT_TRUE(balsa_frame_.protocol_defects().unknown_transfer_encoding); } TEST_F(HTTPBalsaFrameTest, MissingContentLength) {