Stop processing data in QuicReceiveControlStream::OnDataAvailable() if connection is closed. gfe-relnote: n/a, change to QUIC v99-only class. PiperOrigin-RevId: 258206302 Change-Id: Ic80bc7f417a7c5e6bebd30c301ed9b20e11b0b21
diff --git a/quic/core/http/quic_receive_control_stream.cc b/quic/core/http/quic_receive_control_stream.cc index 9e227f4..ee92720 100644 --- a/quic/core/http/quic_receive_control_stream.cc +++ b/quic/core/http/quic_receive_control_stream.cc
@@ -154,7 +154,8 @@ void QuicReceiveControlStream::OnDataAvailable() { iovec iov; - while (!reading_stopped() && decoder_.error() == QUIC_NO_ERROR) { + while (session()->connection()->connected() && !reading_stopped() && + decoder_.error() == QUIC_NO_ERROR) { DCHECK_GE(sequencer_offset_, sequencer()->NumBytesConsumed()); if (!sequencer()->PeekRegion(sequencer_offset_, &iov)) { break;
diff --git a/quic/core/http/quic_receive_control_stream_test.cc b/quic/core/http/quic_receive_control_stream_test.cc index 8a0af4f..49c31d1 100644 --- a/quic/core/http/quic_receive_control_stream_test.cc +++ b/quic/core/http/quic_receive_control_stream_test.cc
@@ -7,6 +7,7 @@ #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quiche/src/quic/test_tools/quic_spdy_session_peer.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_stream_peer.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" namespace quic { @@ -83,8 +84,9 @@ std::string EncodeSettings(const SettingsFrame& settings) { HttpEncoder encoder; std::unique_ptr<char[]> buffer; - auto header_length = encoder.SerializeSettingsFrame(settings, &buffer); - return std::string(buffer.get(), header_length); + QuicByteCount settings_frame_length = + encoder.SerializeSettingsFrame(settings, &buffer); + return std::string(buffer.get(), settings_frame_length); } std::string PriorityFrame(const PriorityFrame& frame) { @@ -95,6 +97,11 @@ return std::string(priority_buffer.get(), priority_frame_length); } + QuicStreamOffset NumBytesConsumed() { + return QuicStreamPeer::sequencer(receive_control_stream_.get()) + ->NumBytesConsumed(); + } + MockQuicConnectionHelper helper_; MockAlarmFactory alarm_factory_; StrictMock<MockQuicConnection>* connection_; @@ -210,6 +217,43 @@ receive_control_stream_->OnStreamFrame(frame); } +// Regression test for https://crbug.com/982648. +// QuicReceiveControlStream::OnDataAvailable() must stop processing input as +// soon as OnSettingsFrameStart() is called by HttpDecoder for the second frame. +TEST_P(QuicReceiveControlStreamTest, StopProcessingIfConnectionClosed) { + SettingsFrame settings; + // Reserved identifiers, must be ignored. + settings.values[0x21] = 100; + settings.values[0x40] = 200; + + std::string settings_frame = EncodeSettings(settings); + + EXPECT_EQ(0u, NumBytesConsumed()); + + // Receive first SETTINGS frame. + receive_control_stream_->OnStreamFrame( + QuicStreamFrame(receive_control_stream_->id(), /* fin = */ false, + /* offset = */ 0, settings_frame)); + + // First SETTINGS frame is consumed. + EXPECT_EQ(settings_frame.size(), NumBytesConsumed()); + + // Second SETTINGS frame causes the connection to be closed. + EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)) + .WillOnce( + Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); + EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _)); + EXPECT_CALL(session_, OnConnectionClosed(_, _)); + + // Receive second SETTINGS frame. + receive_control_stream_->OnStreamFrame( + QuicStreamFrame(receive_control_stream_->id(), /* fin = */ false, + /* offset = */ settings_frame.size(), settings_frame)); + + // No new data is consumed. + EXPECT_EQ(settings_frame.size(), NumBytesConsumed()); +} + } // namespace } // namespace test } // namespace quic