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