gfe-relnote: No longer send a RESET_STREAM in response to a STOP_SENDING if the stream is write closed.  Protected by disabled gfe2_reloadable_flag_quic_enable_version_99.

PiperOrigin-RevId: 293529515
Change-Id: I0a837b79a776233d7a21fb8f87dbb7e173241a26
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index b8de367..b63f729 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -221,9 +221,13 @@
 void QuicSession::OnStopSendingFrame(const QuicStopSendingFrame& frame) {
   // STOP_SENDING is in IETF QUIC only.
   DCHECK(VersionHasIetfQuicFrames(transport_version()));
+  DCHECK(QuicVersionUsesCryptoFrames(transport_version()));
 
   QuicStreamId stream_id = frame.stream_id;
   // If Stream ID is invalid then close the connection.
+  // TODO(ianswett): This check is redundant to checks for IsClosedStream,
+  // but removing it requires removing multiple DCHECKs.
+  // TODO(ianswett): Multiple QUIC_DVLOGs could be QUIC_PEER_BUGs.
   if (stream_id == QuicUtils::GetInvalidStreamId(transport_version())) {
     QUIC_DVLOG(1) << ENDPOINT
                   << "Received STOP_SENDING with invalid stream_id: "
@@ -234,6 +238,19 @@
     return;
   }
 
+  // If stream_id is READ_UNIDIRECTIONAL, close the connection.
+  if (QuicUtils::GetStreamType(stream_id, perspective(),
+                               IsIncomingStream(stream_id)) ==
+      READ_UNIDIRECTIONAL) {
+    QUIC_DVLOG(1) << ENDPOINT
+                  << "Received STOP_SENDING for a read-only stream_id: "
+                  << stream_id << ".";
+    connection()->CloseConnection(
+        QUIC_INVALID_STREAM_ID, "Received STOP_SENDING for a read-only stream",
+        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    return;
+  }
+
   if (visitor_) {
     visitor_->OnStopSendingReceived(frame);
   }
@@ -246,7 +263,9 @@
         << stream_id << " Ignoring.";
     return;
   }
-  // If stream is non-existent, close the connection
+  // If stream is non-existent, close the connection.
+  // TODO(b/148842616): IETF QUIC allows a STOP_SENDING to arrive before a
+  // STREAM frame for peer-intiated bidirectional steams
   StreamMap::iterator it = stream_map_.find(stream_id);
   if (it == stream_map_.end()) {
     QUIC_DVLOG(1) << ENDPOINT
@@ -260,10 +279,22 @@
   }
 
   QuicStream* stream = it->second.get();
+  // If the stream is null, it's an implementation error.
   if (stream == nullptr) {
     QUIC_BUG << ENDPOINT
              << "Received STOP_SENDING for NULL QuicStream, stream_id: "
              << stream_id << ". Ignoring.";
+    connection()->CloseConnection(
+        QUIC_INTERNAL_ERROR, "Received STOP_SENDING for a null stream",
+        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    return;
+  }
+
+  // Do not reset the sream if all data has been sent and acknowledged.
+  if (stream->write_side_closed() && !stream->IsWaitingForAcks()) {
+    QUIC_DVLOG(1) << ENDPOINT
+                  << "Ignoring STOP_SENDING for a write closed stream, id: "
+                  << stream_id;
     return;
   }