Redirect WindowUpdate frame to pending streams if they are present, and closes connection when a WindowUpdate frame is received on a READ_UNIDIRECTIONAL stream.

We determined that at least in the foreseeable future, pending streams will only be READ_UNIDIRECTIONAL, so they will close connection when receives WindowUpdate frame.

gfe-relnote: protected by gfe2_reloadable_flag_quic_no_window_update_on_read_only_stream.
PiperOrigin-RevId: 256041719
Change-Id: Ie7caf100a890f770bb1863d6479d66a5e0820e2a
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index cf95e1d..f13eec4 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -155,6 +155,8 @@
     RETURN_STRING_LITERAL(QUIC_HTTP_DECODER_ERROR);
     RETURN_STRING_LITERAL(QUIC_STALE_CONNECTION_CANCELLED);
     RETURN_STRING_LITERAL(QUIC_IETF_GQUIC_ERROR_MISSING);
+    RETURN_STRING_LITERAL(
+        QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM);
 
     RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
     // Intentionally have no default case, so we'll break the build
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index 34019de..abc666d 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -331,8 +331,11 @@
   // not available in a received CONNECTION_CLOSE frame.
   QUIC_IETF_GQUIC_ERROR_MISSING = 122,
 
+  // Received WindowUpdate on a READ_UNIDIRECTIONAL stream.
+  QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM = 123,
+
   // No error. Used as bound while iterating.
-  QUIC_LAST_ERROR = 123,
+  QUIC_LAST_ERROR = 124,
 };
 // QuicErrorCodes is encoded as a single octet on-the-wire.
 static_assert(static_cast<int>(QUIC_LAST_ERROR) <=
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index d7aa91e..829b3d3 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -528,6 +528,18 @@
     flow_controller_.UpdateSendWindowOffset(frame.byte_offset);
     return;
   }
+
+  if (VersionHasIetfQuicFrames(connection_->transport_version()) &&
+      QuicUtils::GetStreamType(stream_id, perspective(),
+                               IsIncomingStream(stream_id)) ==
+          READ_UNIDIRECTIONAL) {
+    connection()->CloseConnection(
+        QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM,
+        "WindowUpdateFrame received on READ_UNIDIRECTIONAL stream.",
+        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    return;
+  }
+
   QuicStream* stream = GetOrCreateStream(stream_id);
   if (stream != nullptr) {
     stream->OnWindowUpdateFrame(frame);
@@ -1279,6 +1291,7 @@
 }
 
 QuicStream* QuicSession::GetOrCreateStream(const QuicStreamId stream_id) {
+  DCHECK(!QuicContainsKey(pending_stream_map_, stream_id));
   if (eliminate_static_stream_map_ &&
       QuicUtils::IsCryptoStreamId(connection_->transport_version(),
                                   stream_id)) {
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 5ea8633..6188b05 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -1766,6 +1766,27 @@
   EXPECT_EQ(0, session_.num_incoming_streams_created());
 }
 
+TEST_P(QuicSessionTestServer, PendingStreamOnWindowUpdate) {
+  if (!VersionHasIetfQuicFrames(transport_version())) {
+    return;
+  }
+
+  session_.set_uses_pending_streams(true);
+  QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId(
+      transport_version(), Perspective::IS_CLIENT);
+  QuicStreamFrame data1(stream_id, true, 10, QuicStringPiece("HT"));
+  session_.OnStreamFrame(data1);
+  EXPECT_EQ(0, session_.num_incoming_streams_created());
+  QuicWindowUpdateFrame window_update_frame(kInvalidControlFrameId, stream_id,
+                                            0);
+  EXPECT_CALL(
+      *connection_,
+      CloseConnection(
+          QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM,
+          "WindowUpdateFrame received on READ_UNIDIRECTIONAL stream.", _));
+  session_.OnWindowUpdateFrame(window_update_frame);
+}
+
 TEST_P(QuicSessionTestServer, DrainingStreamsDoNotCountAsOpened) {
   // Verify that a draining stream (which has received a FIN but not consumed
   // it) does not count against the open quota (because it is closed from the
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index b0699d4..8ef4717 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -737,6 +737,15 @@
 }
 
 void QuicStream::OnWindowUpdateFrame(const QuicWindowUpdateFrame& frame) {
+  if (GetQuicReloadableFlag(quic_no_window_update_on_read_only_stream) &&
+      type_ == READ_UNIDIRECTIONAL) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_no_window_update_on_read_only_stream);
+    CloseConnectionWithDetails(
+        QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM,
+        "WindowUpdateFrame received on READ_UNIDIRECTIONAL stream.");
+    return;
+  }
+
   if (flow_controller_->UpdateSendWindowOffset(frame.byte_offset)) {
     // Let session unblock this stream.
     session_->MarkConnectionLevelWriteBlocked(id_);
diff --git a/quic/core/quic_stream_test.cc b/quic/core/quic_stream_test.cc
index 45fae55..ee9676f 100644
--- a/quic/core/quic_stream_test.cc
+++ b/quic/core/quic_stream_test.cc
@@ -1693,6 +1693,23 @@
   EXPECT_TRUE(stream_->write_side_closed());
 }
 
+TEST_P(QuicStreamTest, WindowUpdateForReadOnlyStream) {
+  SetQuicReloadableFlag(quic_no_window_update_on_read_only_stream, true);
+  Initialize();
+
+  QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId(
+      connection_->transport_version(), Perspective::IS_CLIENT);
+  TestStream stream(stream_id, session_.get(), READ_UNIDIRECTIONAL);
+  QuicWindowUpdateFrame window_update_frame(kInvalidControlFrameId, stream_id,
+                                            0);
+  EXPECT_CALL(
+      *connection_,
+      CloseConnection(
+          QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM,
+          "WindowUpdateFrame received on READ_UNIDIRECTIONAL stream.", _));
+  stream.OnWindowUpdateFrame(window_update_frame);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic