Move flow control config validation logic from QuicSession to individual QuicStream.

This prevents the session from accessing too much of stream's states.

No behavior change. not protected

PiperOrigin-RevId: 325467676
Change-Id: I80590b074f2fee31221cd5aefada8a58fc1c0d40
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index b2ac3ab..926038a 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -1397,7 +1397,8 @@
     }
     QUIC_DVLOG(1) << ENDPOINT << "Informing unidirectional stream " << id
                   << " of new stream flow control window " << new_window;
-    if (!ValidateStreamFlowControlLimit(new_window, kv.second.get())) {
+    if (!kv.second->ValidateFlowControlLimit(new_window,
+                                             was_zero_rtt_rejected_)) {
       return;
     }
     if (!kv.second->ConfigSendWindowOffset(new_window)) {
@@ -1430,7 +1431,8 @@
     }
     QUIC_DVLOG(1) << ENDPOINT << "Informing outgoing bidirectional stream "
                   << id << " of new stream flow control window " << new_window;
-    if (!ValidateStreamFlowControlLimit(new_window, kv.second.get())) {
+    if (!kv.second->ValidateFlowControlLimit(new_window,
+                                             was_zero_rtt_rejected_)) {
       return;
     }
     if (!kv.second->ConfigSendWindowOffset(new_window)) {
@@ -1463,7 +1465,8 @@
     }
     QUIC_DVLOG(1) << ENDPOINT << "Informing incoming bidirectional stream "
                   << id << " of new stream flow control window " << new_window;
-    if (!ValidateStreamFlowControlLimit(new_window, kv.second.get())) {
+    if (!kv.second->ValidateFlowControlLimit(new_window,
+                                             was_zero_rtt_rejected_)) {
       return;
     }
     if (!kv.second->ConfigSendWindowOffset(new_window)) {
@@ -1472,41 +1475,6 @@
   }
 }
 
-bool QuicSession::ValidateStreamFlowControlLimit(QuicStreamOffset new_window,
-                                                 const QuicStream* stream) {
-  if (was_zero_rtt_rejected_ &&
-      new_window < stream->flow_controller()->bytes_sent()) {
-    QUIC_BUG_IF(perspective() == Perspective::IS_SERVER)
-        << "Server should never receive configs twice.";
-    connection_->CloseConnection(
-        QUIC_ZERO_RTT_UNRETRANSMITTABLE,
-        quiche::QuicheStrCat(
-            "Server rejected 0-RTT, aborting because new stream max data ",
-            new_window, " for stream ", stream->id(),
-            " is less than currently used: ",
-            stream->flow_controller()->bytes_sent()),
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-    return false;
-  }
-
-  if (version().AllowsLowFlowControlLimits() &&
-      new_window < stream->flow_controller()->send_window_offset()) {
-    QUIC_BUG_IF(perspective() == Perspective::IS_SERVER)
-        << "Server should never receive configs twice.";
-    connection_->CloseConnection(
-        was_zero_rtt_rejected_ ? QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED
-                               : QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED,
-        quiche::QuicheStrCat(
-            was_zero_rtt_rejected_ ? "Server rejected 0-RTT, aborting because "
-                                   : "",
-            "new stream max data ", new_window, " decreases current limit: ",
-            stream->flow_controller()->send_window_offset()),
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
-    return false;
-  }
-  return true;
-}
-
 void QuicSession::OnNewSessionFlowControlWindow(QuicStreamOffset new_window) {
   QUIC_DVLOG(1) << ENDPOINT << "OnNewSessionFlowControlWindow " << new_window;
 
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 9bb05ff..54c3111 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -736,12 +736,6 @@
                                QuicRstStreamErrorCode error,
                                QuicStreamOffset bytes_written);
 
-  // Closes the connection and returns false if |new_window| is lower than
-  // |stream|'s current flow control window.
-  // Returns true otherwise.
-  bool ValidateStreamFlowControlLimit(QuicStreamOffset new_window,
-                                      const QuicStream* stream);
-
   // Sends a STOP_SENDING frame if the stream type allows.
   void MaybeSendStopSendingFrame(QuicStreamId id, QuicRstStreamErrorCode error);
 
diff --git a/quic/core/quic_stream.cc b/quic/core/quic_stream.cc
index ee3f983..b25a159 100644
--- a/quic/core/quic_stream.cc
+++ b/quic/core/quic_stream.cc
@@ -1247,6 +1247,37 @@
   }
 }
 
+bool QuicStream::ValidateFlowControlLimit(QuicStreamOffset new_window,
+                                          bool was_zero_rtt_rejected) {
+  if (was_zero_rtt_rejected && new_window < flow_controller_->bytes_sent()) {
+    QUIC_BUG_IF(perspective_ == Perspective::IS_SERVER)
+        << "Server streams' flow control should never be configured twice.";
+    OnUnrecoverableError(
+        QUIC_ZERO_RTT_UNRETRANSMITTABLE,
+        quiche::QuicheStrCat(
+            "Server rejected 0-RTT, aborting because new stream max data ",
+            new_window, " for stream ", id_,
+            " is less than currently used: ", flow_controller_->bytes_sent()));
+    return false;
+  }
+
+  if (VersionUsesHttp3(transport_version()) &&
+      new_window < flow_controller_->send_window_offset()) {
+    QUIC_BUG_IF(perspective_ == Perspective::IS_SERVER)
+        << "Server streams' flow control should never be configured twice.";
+    OnUnrecoverableError(
+        was_zero_rtt_rejected ? QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED
+                              : QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED,
+        quiche::QuicheStrCat(
+            was_zero_rtt_rejected ? "Server rejected 0-RTT, aborting because "
+                                  : "",
+            "new stream max data ", new_window, " decreases current limit: ",
+            flow_controller_->send_window_offset()));
+    return false;
+  }
+  return true;
+}
+
 bool QuicStream::MaybeSetTtl(QuicTime::Delta ttl) {
   if (is_static_) {
     QUIC_BUG << "Cannot set TTL of a static stream.";
diff --git a/quic/core/quic_stream.h b/quic/core/quic_stream.h
index 12b1192..8d7ce19 100644
--- a/quic/core/quic_stream.h
+++ b/quic/core/quic_stream.h
@@ -179,6 +179,12 @@
   // Send PRIORITY_UPDATE frame if application protocol supports it.
   virtual void MaybeSendPriorityUpdateFrame() {}
 
+  // Closes the connection and returns false if |new_window| is lower than
+  // stream's current flow control window.
+  // Returns true otherwise.
+  bool ValidateFlowControlLimit(QuicStreamOffset new_window,
+                                bool was_zero_rtt_rejected);
+
   // Sets |priority_| to priority.  This should only be called before bytes are
   // written to the server.  For a server stream, this is called when a
   // PRIORITY_UPDATE frame is received.  This calls