Adds tests that demonstrate oghttp2's complete lack of reaction to flow control violations. PiperOrigin-RevId: 428596022
diff --git a/http2/adapter/nghttp2_adapter_test.cc b/http2/adapter/nghttp2_adapter_test.cc index 5825fdf..5ff0020 100644 --- a/http2/adapter/nghttp2_adapter_test.cc +++ b/http2/adapter/nghttp2_adapter_test.cc
@@ -2960,6 +2960,147 @@ EXPECT_FALSE(adapter->want_write()); } +TEST(NgHttp2AdapterTest, ClientDisobeysConnectionFlowControl) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}, + {"accept", "some bogus value!"}}, + /*fin=*/false) + // 70000 bytes of data + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(4464, 'a')) + .Serialize(); + + testing::InSequence s; + // Client 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).Times(5); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + // No further frame data or headers are delivered. + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); + + EXPECT_TRUE(adapter->want_write()); + + // No SETTINGS ack is written. + EXPECT_CALL(visitor, OnBeforeFrameSent(GOAWAY, 0, _, 0x0)); + EXPECT_CALL( + visitor, + OnFrameSent(GOAWAY, 0, _, 0x0, + static_cast<int>(Http2ErrorCode::FLOW_CONTROL_ERROR))); + + int send_result = adapter->Send(); + EXPECT_EQ(0, send_result); + EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::GOAWAY})); +} + +TEST(NgHttp2AdapterTest, ClientDisobeysStreamFlowControl) { + DataSavingVisitor visitor; + auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}, + {"accept", "some bogus value!"}}, + /*fin=*/false) + .Serialize(); + const std::string more_frames = TestFrameSequence() + // 70000 bytes of data + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(4464, 'a')) + .Serialize(); + + testing::InSequence s; + // Client 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).Times(5); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + + int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); + + adapter->SubmitWindowUpdate(0, 20000); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(WINDOW_UPDATE, 0, 4, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(WINDOW_UPDATE, 0, 4, 0x0, 0)); + + int send_result = adapter->Send(); + EXPECT_EQ(0, send_result); + EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::SETTINGS, + SpdyFrameType::WINDOW_UPDATE})); + visitor.Clear(); + + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + // No further frame data or headers for stream 1 are delivered. + + result = adapter->ProcessBytes(more_frames); + EXPECT_EQ(more_frames.size(), static_cast<size_t>(result)); + + EXPECT_TRUE(adapter->want_write()); + EXPECT_CALL(visitor, OnBeforeFrameSent(RST_STREAM, 1, 4, 0x0)); + EXPECT_CALL( + visitor, + OnFrameSent(RST_STREAM, 1, 4, 0x0, + static_cast<int>(Http2ErrorCode::FLOW_CONTROL_ERROR))); + EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::FLOW_CONTROL_ERROR)); + + send_result = adapter->Send(); + EXPECT_EQ(0, send_result); + EXPECT_THAT(visitor.data(), EqualsFrames({SpdyFrameType::RST_STREAM})); +} + TEST(NgHttp2AdapterTest, ServerErrorWhileHandlingHeaders) { DataSavingVisitor visitor; auto adapter = NgHttp2Adapter::CreateServerAdapter(visitor);
diff --git a/http2/adapter/oghttp2_adapter_test.cc b/http2/adapter/oghttp2_adapter_test.cc index 02e46fd..2e37880 100644 --- a/http2/adapter/oghttp2_adapter_test.cc +++ b/http2/adapter/oghttp2_adapter_test.cc
@@ -3827,6 +3827,145 @@ } } +TEST(OgHttp2AdapterTest, ClientDisobeysConnectionFlowControl) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}, + {"accept", "some bogus value!"}}, + /*fin=*/false) + // 70000 bytes of data + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(4464, 'a')) + .Serialize(); + + testing::InSequence s; + // Client 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).Times(5); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 4464, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 4464)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + + const int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); + + EXPECT_TRUE(adapter->want_write()); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + + int send_result = adapter->Send(); + EXPECT_EQ(0, send_result); + EXPECT_THAT(visitor.data(), + EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS})); +} + +TEST(OgHttp2AdapterTest, ClientDisobeysStreamFlowControl) { + DataSavingVisitor visitor; + OgHttp2Adapter::Options options{.perspective = Perspective::kServer}; + auto adapter = OgHttp2Adapter::Create(visitor, options); + + const std::string frames = TestFrameSequence() + .ClientPreface() + .Headers(1, + {{":method", "POST"}, + {":scheme", "https"}, + {":authority", "example.com"}, + {":path", "/this/is/request/one"}, + {"accept", "some bogus value!"}}, + /*fin=*/false) + .Serialize(); + const std::string more_frames = TestFrameSequence() + // 70000 bytes of data + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(16384, 'a')) + .Data(1, std::string(4464, 'a')) + .Serialize(); + + testing::InSequence s; + // Client 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).Times(5); + EXPECT_CALL(visitor, OnEndHeadersForStream(1)); + + int64_t result = adapter->ProcessBytes(frames); + EXPECT_EQ(frames.size(), static_cast<size_t>(result)); + + adapter->SubmitWindowUpdate(0, 20000); + + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 0, 0x1)); + EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 0, 0x1, 0)); + EXPECT_CALL(visitor, OnBeforeFrameSent(WINDOW_UPDATE, 0, 4, 0x0)); + EXPECT_CALL(visitor, OnFrameSent(WINDOW_UPDATE, 0, 4, 0x0, 0)); + + int send_result = adapter->Send(); + EXPECT_EQ(0, send_result); + EXPECT_THAT(visitor.data(), + EqualsFrames({SpdyFrameType::SETTINGS, SpdyFrameType::SETTINGS, + SpdyFrameType::WINDOW_UPDATE})); + + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 16384, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 16384)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + EXPECT_CALL(visitor, OnFrameHeader(1, 4464, DATA, 0x0)); + EXPECT_CALL(visitor, OnBeginDataForStream(1, 4464)); + EXPECT_CALL(visitor, OnDataForStream(1, _)); + + result = adapter->ProcessBytes(more_frames); + EXPECT_EQ(more_frames.size(), static_cast<size_t>(result)); + + EXPECT_FALSE(adapter->want_write()); +} + TEST(OgHttp2AdapterTest, ServerErrorWhileHandlingHeaders) { DataSavingVisitor visitor; OgHttp2Adapter::Options options{.perspective = Perspective::kServer};