Use detailed error code when client cached 0-RTT stream/flow control limit is reduced by server.

Protected by quic_enable_zero_rtt_for_tls

PiperOrigin-RevId: 316531305
Change-Id: Ia9bf39307b4e19b345605cbd8a13ed4874e7b3b8
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 5043f1f..b697457 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -1027,7 +1027,7 @@
         max_streams <
             v99_streamid_manager_.outgoing_bidirectional_stream_count()) {
       connection_->CloseConnection(
-          QUIC_INTERNAL_ERROR,
+          QUIC_ZERO_RTT_UNRETRANSMITTABLE,
           quiche::QuicheStrCat(
               "Server rejected 0-RTT, aborting because new bidirectional "
               "initial stream limit ",
@@ -1043,8 +1043,12 @@
         max_streams <
             v99_streamid_manager_.max_outgoing_bidirectional_streams()) {
       connection_->CloseConnection(
-          QUIC_MAX_STREAMS_ERROR,
+          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 bidirectional limit ", max_streams,
               " decreases the current limit: ",
               v99_streamid_manager_.max_outgoing_bidirectional_streams()),
@@ -1065,7 +1069,7 @@
         max_streams <
             v99_streamid_manager_.outgoing_unidirectional_stream_count()) {
       connection_->CloseConnection(
-          QUIC_INTERNAL_ERROR,
+          QUIC_ZERO_RTT_UNRETRANSMITTABLE,
           quiche::QuicheStrCat(
               "Server rejected 0-RTT, aborting because new unidirectional "
               "initial stream limit ",
@@ -1078,8 +1082,12 @@
     if (max_streams <
         v99_streamid_manager_.max_outgoing_unidirectional_streams()) {
       connection_->CloseConnection(
-          QUIC_MAX_STREAMS_ERROR,
+          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 unidirectional limit ", max_streams,
               " decreases the current limit: ",
               v99_streamid_manager_.max_outgoing_unidirectional_streams()),
@@ -1321,16 +1329,7 @@
     }
     QUIC_DVLOG(1) << ENDPOINT << "Informing unidirectional stream " << id
                   << " of new stream flow control window " << new_window;
-    if (was_zero_rtt_rejected_ &&
-        new_window < kv.second->flow_controller()->bytes_sent()) {
-      connection_->CloseConnection(
-          QUIC_INTERNAL_ERROR,
-          quiche::QuicheStrCat(
-              "Server rejected 0-RTT, aborting because new stream max data ",
-              new_window, " for stream ", kv.first,
-              " is less than currently used: ",
-              kv.second->flow_controller()->bytes_sent()),
-          ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    if (!ValidateStreamFlowControlLimit(new_window, kv.second.get())) {
       return;
     }
     if (!kv.second->ConfigSendWindowOffset(new_window)) {
@@ -1357,16 +1356,7 @@
     }
     QUIC_DVLOG(1) << ENDPOINT << "Informing outgoing bidirectional stream "
                   << id << " of new stream flow control window " << new_window;
-    if (was_zero_rtt_rejected_ &&
-        new_window < kv.second->flow_controller()->bytes_sent()) {
-      connection_->CloseConnection(
-          QUIC_INTERNAL_ERROR,
-          quiche::QuicheStrCat(
-              "Server rejected 0-RTT, aborting because new stream max data ",
-              new_window, " for stream ", kv.first,
-              " is less than currently used: ",
-              kv.second->flow_controller()->bytes_sent()),
-          ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    if (!ValidateStreamFlowControlLimit(new_window, kv.second.get())) {
       return;
     }
     if (!kv.second->ConfigSendWindowOffset(new_window)) {
@@ -1393,16 +1383,7 @@
     }
     QUIC_DVLOG(1) << ENDPOINT << "Informing incoming bidirectional stream "
                   << id << " of new stream flow control window " << new_window;
-    if (was_zero_rtt_rejected_ &&
-        new_window < kv.second->flow_controller()->bytes_sent()) {
-      connection_->CloseConnection(
-          QUIC_INTERNAL_ERROR,
-          quiche::QuicheStrCat(
-              "Server rejected 0-RTT, aborting because new stream max data ",
-              new_window, " for stream ", kv.first,
-              " is less than currently used: ",
-              kv.second->flow_controller()->bytes_sent()),
-          ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    if (!ValidateStreamFlowControlLimit(new_window, kv.second.get())) {
       return;
     }
     if (!kv.second->ConfigSendWindowOffset(new_window)) {
@@ -1411,6 +1392,41 @@
   }
 }
 
+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;
 
@@ -1422,7 +1438,7 @@
         ", which is below currently used: ", flow_controller_.bytes_sent());
     QUIC_LOG(ERROR) << error_details;
     connection_->CloseConnection(
-        QUIC_INTERNAL_ERROR, error_details,
+        QUIC_ZERO_RTT_UNRETRANSMITTABLE, error_details,
         ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
     return;
   }
@@ -1442,12 +1458,15 @@
     // The client receives a lower limit than remembered, violating
     // https://tools.ietf.org/html/draft-ietf-quic-transport-27#section-7.3.1
     std::string error_details = quiche::QuicheStrCat(
-        "Peer sent us an invalid session flow control send window: ",
-        new_window, ", below current: ", flow_controller_.send_window_offset());
+        was_zero_rtt_rejected_ ? "Server rejected 0-RTT, aborting because "
+                               : "",
+        "new session max data ", new_window,
+        " decreases current limit: ", flow_controller_.send_window_offset());
     QUIC_LOG(ERROR) << error_details;
     connection_->CloseConnection(
-        QUIC_FLOW_CONTROL_INVALID_WINDOW, error_details,
-        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+        was_zero_rtt_rejected_ ? QUIC_ZERO_RTT_REJECTION_LIMIT_REDUCED
+                               : QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED,
+        error_details, ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
     return;
   }