Adds test cases for OgHttp2Adapter as a client receiving a response with Content-Length and padding. Demonstrates the problem seen in https://github.com/envoyproxy/envoy/issues/34693. PiperOrigin-RevId: 642720946
diff --git a/quiche/http2/adapter/oghttp2_adapter_test.cc b/quiche/http2/adapter/oghttp2_adapter_test.cc index a854e4d..0bafd9f 100644 --- a/quiche/http2/adapter/oghttp2_adapter_test.cc +++ b/quiche/http2/adapter/oghttp2_adapter_test.cc
@@ -1096,6 +1096,122 @@ SpdyFrameType::RST_STREAM})); } +TEST(OgHttp2AdapterTest, ClientHandlesResponseWithContentLengthAndPadding) { + TestVisitor visitor; + OgHttp2Adapter::Options options; + options.perspective = Perspective::kClient; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + testing::InSequence s; + + const std::vector<Header> headers1 = + ToHeaders({{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}}); + + const int32_t stream_id1 = + adapter->SubmitRequest(headers1, nullptr, true, nullptr); + ASSERT_GT(stream_id1, 0); + + const std::vector<Header> headers2 = + ToHeaders({{":method", "GET"}, + {":scheme", "http"}, + {":authority", "example.com"}, + {":path", "/this/is/request/two"}}); + + const int32_t stream_id2 = + adapter->SubmitRequest(headers2, nullptr, true, nullptr); + ASSERT_GT(stream_id2, stream_id1); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, + END_STREAM_FLAG | END_HEADERS_FLAG)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, + END_STREAM_FLAG | END_HEADERS_FLAG, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, + END_STREAM_FLAG | END_HEADERS_FLAG)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, + END_STREAM_FLAG | END_HEADERS_FLAG, 0)); + + int result = adapter->Send(); + EXPECT_EQ(0, result); + visitor.Clear(); + + // * Stream 1 sends a response with padding that exceeds the total + // Content-Length in the first DATA frame. + // * Stream 3 sends a response with padding that exceeds the total + // Content-Length in the 2nd frame of 3. + const std::string stream_frames = + TestFrameSequence() + .ServerPreface() + .Headers(1, {{":status", "200"}, {"content-length", "2"}}, + /*fin=*/false) + .Data(1, "hi", /*fin=*/true, /*padding_length=*/10) + .Headers(3, {{":status", "200"}, {"content-length", "24"}}, + /*fin=*/false) + .Data(3, "hi", false, 11) + .Data(3, " it's nice", false, 12) + .Data(3, " to meet you", true, 13) + .Serialize(); + + // Server preface (empty SETTINGS) + EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0)); + EXPECT_CALL(visitor, OnSettingsStart()); + EXPECT_CALL(visitor, OnSettingsEnd()); + + EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); + EXPECT_CALL(visitor, OnHeaderForStream(1, ":status", "200")); + EXPECT_CALL(visitor, OnHeaderForStream(1, "content-length", "2")); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, 2 + 10, DATA, 0x9)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 2 + 10)); + EXPECT_CALL(visitor, OnDataPaddingLength(1, 10)); + // BUG: no stream data passed through for stream 1 + EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); + EXPECT_CALL(visitor, OnHeaderForStream(3, ":status", "200")); + EXPECT_CALL(visitor, OnHeaderForStream(3, "content-length", "24")); + EXPECT_CALL(visitor, OnEndHeadersForStream(3)); + EXPECT_CALL(visitor, OnFrameHeader(3, 2 + 11, DATA, 0x8)); + EXPECT_CALL(visitor, OnBeginDataForStream(3, 2 + 11)); + EXPECT_CALL(visitor, OnDataPaddingLength(3, 11)); + EXPECT_CALL(visitor, OnDataForStream(3, "hi")); + EXPECT_CALL(visitor, OnFrameHeader(3, 10 + 12, DATA, 0x8)); + EXPECT_CALL(visitor, OnBeginDataForStream(3, 10 + 12)); + EXPECT_CALL(visitor, OnDataPaddingLength(3, 12)); + // BUG: no stream data passed through for stream 3, frame 2 of 3 + // BUG: no OnFrameHeader/OnBeginDataForStream calls for stream 3, frame 3 of 3 + EXPECT_CALL(visitor, OnDataPaddingLength(3, 13)); + + const int64_t stream_result = adapter->ProcessBytes(stream_frames); + EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, ACK_FLAG)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, ACK_FLAG, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, _, 0x0)); + EXPECT_CALL(visitor, + OnFrameSent(RST_STREAM, 1, _, 0x0, + static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR))); + EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); + EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 3, _, 0x0)); + EXPECT_CALL(visitor, + OnFrameSent(RST_STREAM, 3, _, 0x0, + static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR))); + EXPECT_CALL(visitor, OnCloseStream(3, Http2ErrorCode::HTTP2_NO_ERROR)); + + EXPECT_TRUE(adapter->want_write()); + result = adapter->Send(); + EXPECT_EQ(0, result); + EXPECT_THAT(visitor.data(), EqualsFrames({ + SpdyFrameType::SETTINGS, + SpdyFrameType::RST_STREAM, + SpdyFrameType::RST_STREAM, + })); +} + TEST(OgHttp2AdapterTest, ClientHandles204WithContent) { TestVisitor visitor; OgHttp2Adapter::Options options;