Close read side but not write side in QuicSpdyStream::OnStreamReset().

This bug was found by ClusterFuzz at https://crbug.com/1177662.

Protected by FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset.

PiperOrigin-RevId: 357252071
Change-Id: Ie672d4d877fa44c2fd11530d6e73a600a7cfd11e
diff --git a/quic/core/http/quic_spdy_stream.cc b/quic/core/http/quic_spdy_stream.cc
index aa5f7bf..83905b5 100644
--- a/quic/core/http/quic_spdy_stream.cc
+++ b/quic/core/http/quic_spdy_stream.cc
@@ -725,6 +725,9 @@
 }
 
 void QuicSpdyStream::OnStreamReset(const QuicRstStreamFrame& frame) {
+  // TODO(bnc): Merge the two blocks below when both
+  // quic_abort_qpack_on_stream_reset and quic_fix_on_stream_reset are
+  // deprecated.
   if (frame.error_code != QUIC_STREAM_NO_ERROR) {
     if (VersionUsesHttp3(transport_version()) && !fin_received() &&
         spdy_session_->qpack_decoder()) {
@@ -739,6 +742,18 @@
     return;
   }
 
+  if (GetQuicReloadableFlag(quic_fix_on_stream_reset) &&
+      VersionUsesHttp3(transport_version())) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_fix_on_stream_reset);
+    if (!fin_received() && spdy_session_->qpack_decoder()) {
+      spdy_session_->qpack_decoder()->OnStreamReset(id());
+      qpack_decoded_headers_accumulator_.reset();
+    }
+
+    QuicStream::OnStreamReset(frame);
+    return;
+  }
+
   QUIC_DVLOG(1) << ENDPOINT
                 << "Received QUIC_STREAM_NO_ERROR, not discarding response";
   set_rst_received(true);
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index 06f779e..0e35c28 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -1142,10 +1142,19 @@
   Initialize(kShouldProcessData);
   ProcessHeaders(false, headers_);
 
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).Times(AnyNumber());
+
   stream_->OnStreamReset(QuicRstStreamFrame(
       kInvalidControlFrameId, stream_->id(), QUIC_STREAM_NO_ERROR, 0));
-  EXPECT_TRUE(stream_->write_side_closed());
-  EXPECT_FALSE(stream_->reading_stopped());
+
+  if (GetQuicReloadableFlag(quic_fix_on_stream_reset) && UsesHttp3()) {
+    // RESET_STREAM should close the read side but not the write side.
+    EXPECT_TRUE(stream_->read_side_closed());
+    EXPECT_FALSE(stream_->write_side_closed());
+  } else {
+    EXPECT_TRUE(stream_->write_side_closed());
+    EXPECT_FALSE(stream_->reading_stopped());
+  }
 }
 
 // Tests that on if the peer sends too much data (i.e. violates the flow control
@@ -3029,6 +3038,39 @@
   EXPECT_EQ(headers_frame_payload_length, write_headers_return_value);
 }
 
+// Regression test for https://crbug.com/1177662.
+// RESET_STREAM with QUIC_STREAM_NO_ERROR should not be treated in a special
+// way: it should close the read side but not the write side.
+TEST_P(QuicSpdyStreamTest, TwoResetStreamFrames) {
+  if (!UsesHttp3()) {
+    return;
+  }
+
+  Initialize(kShouldProcessData);
+
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).Times(AnyNumber());
+
+  QuicRstStreamFrame rst_frame1(kInvalidControlFrameId, stream_->id(),
+                                QUIC_STREAM_CANCELLED, /* bytes_written = */ 0);
+  stream_->OnStreamReset(rst_frame1);
+  EXPECT_TRUE(stream_->read_side_closed());
+  EXPECT_FALSE(stream_->write_side_closed());
+
+  QuicRstStreamFrame rst_frame2(kInvalidControlFrameId, stream_->id(),
+                                QUIC_STREAM_NO_ERROR, /* bytes_written = */ 0);
+  if (GetQuicReloadableFlag(quic_fix_on_stream_reset)) {
+    stream_->OnStreamReset(rst_frame2);
+    EXPECT_TRUE(stream_->read_side_closed());
+    EXPECT_FALSE(stream_->write_side_closed());
+  } else {
+    EXPECT_CALL(*session_, MaybeSendRstStreamFrame(
+                               stream_->id(), QUIC_RST_ACKNOWLEDGEMENT, _));
+    EXPECT_QUIC_BUG(
+        stream_->OnStreamReset(rst_frame2),
+        "The stream should've already sent RST in response to STOP_SENDING");
+  }
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 6289379..6f809a7 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -44,6 +44,7 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_enable_version_rfcv1, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_control_frames, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_encrypted_goaway, true)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_on_stream_reset, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_fix_willing_and_able_to_write2, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_goaway_with_max_stream_id, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_parse_accept_ch_frame, true)