Adds unit test cases covering the case where a content-length undershoot causes a PROTOCOL_ERROR. Both nghttp2 and oghttp2 appear to treat this case the same. PiperOrigin-RevId: 701960516
diff --git a/quiche/http2/adapter/nghttp2_adapter_test.cc b/quiche/http2/adapter/nghttp2_adapter_test.cc index 99cab9e..8eaff06 100644 --- a/quiche/http2/adapter/nghttp2_adapter_test.cc +++ b/quiche/http2/adapter/nghttp2_adapter_test.cc
@@ -7519,6 +7519,12 @@ {":path", "/this/is/request/one"}, {"content-length", "4"}}) .Data(1, "ok", /*fin=*/false) + .Headers(3, {{":method", "GET"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/three"}, + {"content-length", "4"}}) + .Data(3, "ok", /*fin=*/false) .Serialize(); // Client preface (empty SETTINGS) @@ -7526,7 +7532,7 @@ EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); - // Stream 1: content-length is smaller than actual data, but not yet + // Stream 1 // Headers and the beginning of data is delivered to the visitor. EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); @@ -7536,6 +7542,16 @@ EXPECT_CALL(visitor, OnBeginDataForStream(1, 2)); EXPECT_CALL(visitor, OnDataForStream(1, _)); + // Stream 3 + // Headers and the beginning of data is delivered to the visitor. + EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); + EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(5); + EXPECT_CALL(visitor, OnEndHeadersForStream(3)); + EXPECT_CALL(visitor, OnFrameHeader(3, 2, DATA, 0)); + EXPECT_CALL(visitor, OnBeginDataForStream(3, 2)); + EXPECT_CALL(visitor, OnDataForStream(3, _)); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); @@ -7544,23 +7560,34 @@ 1, "Here is some response data, and there will be more. "); adapter->SubmitResponse(1, ToHeaders({{":status", "200"}}), false); + // Initial response data for stream 3. + visitor.AppendPayloadForStream( + 3, "Here is some response data, and there will be more. "); + adapter->SubmitResponse(3, ToHeaders({{":status", "200"}}), false); + // Server preface (SETTINGS) EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); // SETTINGS ack EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, ACK_FLAG)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, ACK_FLAG, 0)); - // Stream 1, with some DATA + // Stream 1 HEADERS EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0)); + // Stream 3 HEADERS + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 3, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, 3, _, 0x4, 0)); + // DATA EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0)); + EXPECT_CALL(visitor, OnFrameSent(DATA, 3, _, 0x0, 0)); EXPECT_TRUE(adapter->want_write()); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, - SpdyFrameType::HEADERS, SpdyFrameType::DATA})); + SpdyFrameType::HEADERS, SpdyFrameType::HEADERS, + SpdyFrameType::DATA, SpdyFrameType::DATA})); visitor.Clear(); @@ -7569,14 +7596,28 @@ visitor.SetEndData(1, true); adapter->ResumeStream(1); - const std::string client_fin = - TestFrameSequence().Data(1, "ay!", /*fin=*/true).Serialize(); + // Final response data and fin for stream 3. + visitor.AppendPayloadForStream(3, "Last data!"); + visitor.SetEndData(3, true); + adapter->ResumeStream(3); + + // Stream 1: actual data overshoots the content-length from request headers. + // Stream 3: actual data undershoots the content-length from request headers. + const std::string client_fin = TestFrameSequence() + .Data(1, "ay!", /*fin=*/true) + .Data(3, "", /*fin=*/true) + .Serialize(); // The library does not deliver the actual data or fin from the client to the // visitor. EXPECT_CALL(visitor, OnFrameHeader(1, 3, DATA, 0x1)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 3)); + // The library does not deliver the actual data or fin from the client to the + // visitor. + EXPECT_CALL(visitor, OnFrameHeader(3, 0, DATA, 0x1)); + EXPECT_CALL(visitor, OnBeginDataForStream(3, 0)); + const int64_t fin_result = adapter->ProcessBytes(client_fin); ASSERT_EQ(client_fin.size(), fin_result); @@ -7586,6 +7627,13 @@ OnFrameSent(RST_STREAM, 1, _, 0x0, static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::PROTOCOL_ERROR)); + // The library sends the RST_STREAM but not the end of data. + 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::PROTOCOL_ERROR)); + result = adapter->Send(); EXPECT_EQ(0, result); }
diff --git a/quiche/http2/adapter/oghttp2_adapter_test.cc b/quiche/http2/adapter/oghttp2_adapter_test.cc index bf11371..d692c4b 100644 --- a/quiche/http2/adapter/oghttp2_adapter_test.cc +++ b/quiche/http2/adapter/oghttp2_adapter_test.cc
@@ -8296,6 +8296,12 @@ {":path", "/this/is/request/one"}, {"content-length", "4"}}) .Data(1, "ok", /*fin=*/false) + .Headers(3, {{":method", "GET"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/three"}, + {"content-length", "4"}}) + .Data(3, "ok", /*fin=*/false) .Serialize(); // Client preface (empty SETTINGS) @@ -8303,7 +8309,7 @@ EXPECT_CALL(visitor, OnSettingsStart()); EXPECT_CALL(visitor, OnSettingsEnd()); - // Stream 1: content-length is smaller than actual data, but not yet + // Stream 1 // Headers and the beginning of data is delivered to the visitor. EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4)); EXPECT_CALL(visitor, OnBeginHeadersForStream(1)); @@ -8313,6 +8319,16 @@ EXPECT_CALL(visitor, OnBeginDataForStream(1, 2)); EXPECT_CALL(visitor, OnDataForStream(1, _)); + // Stream 3 + // Headers and the beginning of data is delivered to the visitor. + EXPECT_CALL(visitor, OnFrameHeader(3, _, HEADERS, 4)); + EXPECT_CALL(visitor, OnBeginHeadersForStream(3)); + EXPECT_CALL(visitor, OnHeaderForStream(3, _, _)).Times(5); + EXPECT_CALL(visitor, OnEndHeadersForStream(3)); + EXPECT_CALL(visitor, OnFrameHeader(3, 2, DATA, 0)); + EXPECT_CALL(visitor, OnBeginDataForStream(3, 2)); + EXPECT_CALL(visitor, OnDataForStream(3, _)); + const int64_t stream_result = adapter->ProcessBytes(stream_frames); EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result)); @@ -8321,23 +8337,34 @@ 1, "Here is some response data, and there will be more. "); adapter->SubmitResponse(1, ToHeaders({{":status", "200"}}), false); + // Initial response data for stream 3. + visitor.AppendPayloadForStream( + 3, "Here is some response data, and there will be more. "); + adapter->SubmitResponse(3, ToHeaders({{":status", "200"}}), false); + // Server preface (SETTINGS) EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); // SETTINGS ack EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, ACK_FLAG)); EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, ACK_FLAG, 0)); - // Stream 1, with some DATA + // Stream 1 HEADERS EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4)); EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0)); + // Stream 3 HEADERS + EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 3, _, 0x4)); + EXPECT_CALL(visitor, OnFrameSent(HEADERS, 3, _, 0x4, 0)); + // DATA EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 0x0, 0)); + EXPECT_CALL(visitor, OnFrameSent(DATA, 3, _, 0x0, 0)); EXPECT_TRUE(adapter->want_write()); int result = adapter->Send(); EXPECT_EQ(0, result); EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, - SpdyFrameType::HEADERS, SpdyFrameType::DATA})); + SpdyFrameType::HEADERS, SpdyFrameType::HEADERS, + SpdyFrameType::DATA, SpdyFrameType::DATA})); visitor.Clear(); @@ -8346,14 +8373,28 @@ visitor.SetEndData(1, true); adapter->ResumeStream(1); - const std::string client_fin = - TestFrameSequence().Data(1, "ay!", /*fin=*/true).Serialize(); + // Final response data and fin for stream 3. + visitor.AppendPayloadForStream(3, "Last data!"); + visitor.SetEndData(3, true); + adapter->ResumeStream(3); + + // Stream 1: actual data overshoots the content-length from request headers. + // Stream 3: actual data undershoots the content-length from request headers. + const std::string client_fin = TestFrameSequence() + .Data(1, "ay!", /*fin=*/true) + .Data(3, "", /*fin=*/true) + .Serialize(); // The library does not deliver the actual data or fin from the client to the // visitor. EXPECT_CALL(visitor, OnFrameHeader(1, 3, DATA, 0x1)); EXPECT_CALL(visitor, OnBeginDataForStream(1, 3)); + // The library does not deliver the actual data or fin from the client to the + // visitor. + EXPECT_CALL(visitor, OnFrameHeader(3, 0, DATA, 0x1)); + EXPECT_CALL(visitor, OnBeginDataForStream(3, 0)); + const int64_t fin_result = adapter->ProcessBytes(client_fin); ASSERT_EQ(client_fin.size(), fin_result); @@ -8363,6 +8404,13 @@ OnFrameSent(RST_STREAM, 1, _, 0x0, static_cast<int>(Http2ErrorCode::PROTOCOL_ERROR))); EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR)); + // The library sends the RST_STREAM but not the end of data. + 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)); + result = adapter->Send(); EXPECT_EQ(0, result); }