gfe-relnote: In QUIC, break QuicStream::CloseRWSide -> QuicSession::CloseStream -> QuicStream::OnClose -> CloseRWSide loop. Protected by gfe2_reloadable_flag_quic_break_session_stream_close_loop.

With this change, a stream can be closed by 4 methods:
1) QuicSession::CloseStream (will soon be removed)
2) QuicSession::ResetStream
3) QuicStream::Reset
4) QuicStream::OnStreamReset
And also make the code path consistent. All -> QuicStream::CloseReadSide/CloseWriteSide -> QuicSession::OnStreamClosed.

PiperOrigin-RevId: 307032801
Change-Id: I1a2f49ddb94cc9642ce8c8fcb514f5adb928d045
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index a4d6cd7..4d02c80 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -2027,7 +2027,11 @@
   // Transmit the cancel, and ensure the connection is torn down properly.
   SetPacketLossPercentage(0);
   QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
-  session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0);
+  if (session->break_close_loop()) {
+    session->ResetStream(stream_id, QUIC_STREAM_CANCELLED, 0);
+  } else {
+    session->SendRstStream(stream_id, QUIC_STREAM_CANCELLED, 0);
+  }
 
   // WaitForEvents waits 50ms and returns true if there are outstanding
   // requests.
diff --git a/quic/core/http/quic_client_promised_info_test.cc b/quic/core/http/quic_client_promised_info_test.cc
index 27d9fe8..381160b 100644
--- a/quic/core/http/quic_client_promised_info_test.cc
+++ b/quic/core/http/quic_client_promised_info_test.cc
@@ -227,7 +227,9 @@
   EXPECT_CALL(*connection_, SendControlFrame(_));
   EXPECT_CALL(*connection_,
               OnStreamReset(promise_id_, QUIC_PROMISE_VARY_MISMATCH));
-  EXPECT_CALL(session_, CloseStream(promise_id_));
+  if (!session_.break_close_loop()) {
+    EXPECT_CALL(session_, CloseStream(promise_id_));
+  }
 
   promised->HandleClientRequest(client_request_, &delegate);
 }
@@ -303,7 +305,9 @@
   session_.GetOrCreateStream(promise_id_);
 
   // Cancel the promised stream.
-  EXPECT_CALL(session_, CloseStream(promise_id_));
+  if (!session_.break_close_loop()) {
+    EXPECT_CALL(session_, CloseStream(promise_id_));
+  }
   EXPECT_CALL(*connection_, SendControlFrame(_));
   EXPECT_CALL(*connection_, OnStreamReset(promise_id_, QUIC_STREAM_CANCELLED));
   promised->Cancel();
@@ -327,11 +331,17 @@
   promise_stream->OnStreamHeaderList(false, headers.uncompressed_header_bytes(),
                                      headers);
 
-  EXPECT_CALL(session_, CloseStream(promise_id_));
+  if (!session_.break_close_loop()) {
+    EXPECT_CALL(session_, CloseStream(promise_id_));
+  }
   EXPECT_CALL(*connection_, SendControlFrame(_));
   EXPECT_CALL(*connection_,
               OnStreamReset(promise_id_, QUIC_STREAM_PEER_GOING_AWAY));
-  session_.SendRstStream(promise_id_, QUIC_STREAM_PEER_GOING_AWAY, 0);
+  if (session_.break_close_loop()) {
+    session_.ResetStream(promise_id_, QUIC_STREAM_PEER_GOING_AWAY, 0);
+  } else {
+    session_.SendRstStream(promise_id_, QUIC_STREAM_PEER_GOING_AWAY, 0);
+  }
 
   // Now initiate rendezvous.
   TestPushPromiseDelegate delegate(/*match=*/true);
diff --git a/quic/core/http/quic_spdy_client_session_base.cc b/quic/core/http/quic_spdy_client_session_base.cc
index 2655259..63de57c 100644
--- a/quic/core/http/quic_spdy_client_session_base.cc
+++ b/quic/core/http/quic_spdy_client_session_base.cc
@@ -204,7 +204,11 @@
     QuicStreamId id,
     QuicRstStreamErrorCode error_code) {
   DCHECK(QuicUtils::IsServerInitiatedStreamId(transport_version(), id));
-  SendRstStream(id, error_code, 0);
+  if (break_close_loop()) {
+    ResetStream(id, error_code, 0);
+  } else {
+    SendRstStream(id, error_code, 0);
+  }
   if (!IsOpenStream(id) && !IsClosedStream(id)) {
     MaybeIncreaseLargestPeerStreamId(id);
   }
@@ -218,6 +222,14 @@
   }
 }
 
+void QuicSpdyClientSessionBase::OnStreamClosed(QuicStreamId stream_id) {
+  DCHECK(break_close_loop());
+  QuicSpdySession::OnStreamClosed(stream_id);
+  if (!VersionUsesHttp3(transport_version())) {
+    headers_stream()->MaybeReleaseSequencerBuffer();
+  }
+}
+
 bool QuicSpdyClientSessionBase::ShouldReleaseHeadersStreamSequencerBuffer() {
   return !HasActiveRequestStreams() && promised_by_id_.empty();
 }
diff --git a/quic/core/http/quic_spdy_client_session_base.h b/quic/core/http/quic_spdy_client_session_base.h
index 9d702e3..282a8cf 100644
--- a/quic/core/http/quic_spdy_client_session_base.h
+++ b/quic/core/http/quic_spdy_client_session_base.h
@@ -105,6 +105,9 @@
   // Release headers stream's sequencer buffer if it's empty.
   void CloseStreamInner(QuicStreamId stream_id, bool rst_sent) override;
 
+  // Release headers stream's sequencer buffer if it's empty.
+  void OnStreamClosed(QuicStreamId stream_id) override;
+
   // Returns true if there are no active requests and no promised streams.
   bool ShouldReleaseHeadersStreamSequencerBuffer() override;
 
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc
index 65dd43a..dc72a73 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -354,7 +354,11 @@
       .Times(AtLeast(1))
       .WillRepeatedly(Invoke(&ClearControlFrame));
   EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(1);
-  session_->SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0);
+  if (session_->break_close_loop()) {
+    session_->ResetStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0);
+  } else {
+    session_->SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0);
+  }
 
   // A new stream cannot be created as the reset stream still counts as an open
   // outgoing stream until closed by the server.
@@ -406,7 +410,11 @@
       .Times(AtLeast(1))
       .WillRepeatedly(Invoke(&ClearControlFrame));
   EXPECT_CALL(*connection_, OnStreamReset(_, _)).Times(1);
-  session_->SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0);
+  if (session_->break_close_loop()) {
+    session_->ResetStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0);
+  } else {
+    session_->SendRstStream(stream_id, QUIC_STREAM_PEER_GOING_AWAY, 0);
+  }
 
   // The stream receives trailers with final byte offset, but the header value
   // is non-numeric and should be treated as malformed.
@@ -861,7 +869,12 @@
   EXPECT_CALL(*connection_, SendControlFrame(_));
   EXPECT_CALL(*connection_,
               OnStreamReset(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY));
-  session_->SendRstStream(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY, 0);
+  if (session_->break_close_loop()) {
+    session_->ResetStream(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY, 0);
+  } else {
+    session_->SendRstStream(promised_stream_id_, QUIC_STREAM_PEER_GOING_AWAY,
+                            0);
+  }
   QuicClientPromisedInfo* promised =
       session_->GetPromisedById(promised_stream_id_);
   EXPECT_NE(promised, nullptr);
diff --git a/quic/core/http/quic_spdy_stream_test.cc b/quic/core/http/quic_spdy_stream_test.cc
index f46d914..9db9f22 100644
--- a/quic/core/http/quic_spdy_stream_test.cc
+++ b/quic/core/http/quic_spdy_stream_test.cc
@@ -317,6 +317,7 @@
         &helper_, &alarm_factory_, perspective, SupportedVersions(GetParam()));
     session_ = std::make_unique<StrictMock<TestSession>>(connection_);
     session_->Initialize();
+    connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
     ON_CALL(*session_, WritevData(_, _, _, _, _, _))
         .WillByDefault(
             Invoke(session_.get(), &MockQuicSpdySession::ConsumeData));