Adds unit test cases covering the case where a content-length overshoot causes a PROTOCOL_ERROR.

Both nghttp2 and oghttp2 appear to treat this case the same.

PiperOrigin-RevId: 700101380
diff --git a/quiche/http2/adapter/nghttp2_adapter_test.cc b/quiche/http2/adapter/nghttp2_adapter_test.cc
index 0674441..99cab9e 100644
--- a/quiche/http2/adapter/nghttp2_adapter_test.cc
+++ b/quiche/http2/adapter/nghttp2_adapter_test.cc
@@ -7502,6 +7502,94 @@
                     SpdyFrameType::RST_STREAM}));
 }
 
+TEST(NgHttp2AdapterTest, ServerHandlesContentLengthMismatchWithDataPending) {
+  TestVisitor visitor;
+  auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
+  // Server preface
+  adapter->SubmitSettings({});
+
+  testing::InSequence s;
+
+  const std::string stream_frames =
+      TestFrameSequence()
+          .ClientPreface()
+          .Headers(1, {{":method", "GET"},
+                       {":scheme", "https"},
+                       {":authority", "example.com"},
+                       {":path", "/this/is/request/one"},
+                       {"content-length", "4"}})
+          .Data(1, "ok", /*fin=*/false)
+          .Serialize();
+
+  // Client preface (empty SETTINGS)
+  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+  EXPECT_CALL(visitor, OnSettingsStart());
+  EXPECT_CALL(visitor, OnSettingsEnd());
+
+  // Stream 1: content-length is smaller than actual data, but not yet
+  // Headers and the beginning of data is delivered to the visitor.
+  EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+  EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(5);
+  EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+  EXPECT_CALL(visitor, OnFrameHeader(1, 2, DATA, 0));
+  EXPECT_CALL(visitor, OnBeginDataForStream(1, 2));
+  EXPECT_CALL(visitor, OnDataForStream(1, _));
+
+  const int64_t stream_result = adapter->ProcessBytes(stream_frames);
+  EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result));
+
+  // Initial response data for stream 1.
+  visitor.AppendPayloadForStream(
+      1, "Here is some response data, and there will be more. ");
+  adapter->SubmitResponse(1, 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
+  EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
+  EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
+  EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 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}));
+
+  visitor.Clear();
+
+  // Final response data and fin for stream 1.
+  visitor.AppendPayloadForStream(1, "Last data!");
+  visitor.SetEndData(1, true);
+  adapter->ResumeStream(1);
+
+  const std::string client_fin =
+      TestFrameSequence().Data(1, "ay!", /*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));
+
+  const int64_t fin_result = adapter->ProcessBytes(client_fin);
+  ASSERT_EQ(client_fin.size(), fin_result);
+
+  // The library sends the RST_STREAM but not the end of data.
+  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::PROTOCOL_ERROR));
+  result = adapter->Send();
+  EXPECT_EQ(0, result);
+}
+
 TEST(NgHttp2AdapterTest, ServerHandlesAsteriskPathForOptions) {
   TestVisitor visitor;
   auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
diff --git a/quiche/http2/adapter/oghttp2_adapter_test.cc b/quiche/http2/adapter/oghttp2_adapter_test.cc
index 82980cb..bf11371 100644
--- a/quiche/http2/adapter/oghttp2_adapter_test.cc
+++ b/quiche/http2/adapter/oghttp2_adapter_test.cc
@@ -8279,6 +8279,94 @@
                     SpdyFrameType::RST_STREAM, SpdyFrameType::RST_STREAM}));
 }
 
+TEST(OgHttp2AdapterTest, ServerHandlesContentLengthMismatchWithDataPending) {
+  TestVisitor visitor;
+  OgHttp2Adapter::Options options;
+  options.perspective = Perspective::kServer;
+  auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+  testing::InSequence s;
+
+  const std::string stream_frames =
+      TestFrameSequence()
+          .ClientPreface()
+          .Headers(1, {{":method", "GET"},
+                       {":scheme", "https"},
+                       {":authority", "example.com"},
+                       {":path", "/this/is/request/one"},
+                       {"content-length", "4"}})
+          .Data(1, "ok", /*fin=*/false)
+          .Serialize();
+
+  // Client preface (empty SETTINGS)
+  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+  EXPECT_CALL(visitor, OnSettingsStart());
+  EXPECT_CALL(visitor, OnSettingsEnd());
+
+  // Stream 1: content-length is smaller than actual data, but not yet
+  // Headers and the beginning of data is delivered to the visitor.
+  EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 4));
+  EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+  EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(5);
+  EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+  EXPECT_CALL(visitor, OnFrameHeader(1, 2, DATA, 0));
+  EXPECT_CALL(visitor, OnBeginDataForStream(1, 2));
+  EXPECT_CALL(visitor, OnDataForStream(1, _));
+
+  const int64_t stream_result = adapter->ProcessBytes(stream_frames);
+  EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result));
+
+  // Initial response data for stream 1.
+  visitor.AppendPayloadForStream(
+      1, "Here is some response data, and there will be more. ");
+  adapter->SubmitResponse(1, 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
+  EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, 1, _, 0x4));
+  EXPECT_CALL(visitor, OnFrameSent(HEADERS, 1, _, 0x4, 0));
+  EXPECT_CALL(visitor, OnFrameSent(DATA, 1, _, 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}));
+
+  visitor.Clear();
+
+  // Final response data and fin for stream 1.
+  visitor.AppendPayloadForStream(1, "Last data!");
+  visitor.SetEndData(1, true);
+  adapter->ResumeStream(1);
+
+  const std::string client_fin =
+      TestFrameSequence().Data(1, "ay!", /*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));
+
+  const int64_t fin_result = adapter->ProcessBytes(client_fin);
+  ASSERT_EQ(client_fin.size(), fin_result);
+
+  // The library sends the RST_STREAM but not the end of data.
+  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));
+  result = adapter->Send();
+  EXPECT_EQ(0, result);
+}
+
 TEST(OgHttp2AdapterTest, ServerHandlesAsteriskPathForOptions) {
   TestVisitor visitor;
   OgHttp2Adapter::Options options;