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