Support local vs remote flow control with TLS, and fix initial limits

This CL fixes a bug found during interop testing where we were failing to communicate with clients that do not send initial_max_stream_data_bidi_local. (When sent by clients, this is the transport parameter that governs the server-initiated streams which are not used by HTTP/3.) All of our tests sent the same value for _local and _remote so we did not notice that we had them backwards. This CL fixes the issue and adds tests to prevent regressions. This CL also fixes an issue where the initial limits were incorrectly set for versions that AllowsLowFlowControlLimits().

gfe-relnote: change QUIC flow control transport parameters, protected by disabled TLS flag.
PiperOrigin-RevId: 273843552
Change-Id: Id8bb3a59029ee9442ebf163fd658f38c47024950
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index cfe7b3f..f4e70d0 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -1042,12 +1042,30 @@
     stream_id_manager_.set_max_open_incoming_streams(max_incoming_streams);
   }
 
-  if (config_.HasReceivedInitialStreamFlowControlWindowBytes()) {
-    // Streams which were created before the SHLO was received (0-RTT
-    // requests) are now informed of the peer's initial flow control window.
-    OnNewStreamFlowControlWindow(
-        config_.ReceivedInitialStreamFlowControlWindowBytes());
+  if (connection_->version().handshake_protocol == PROTOCOL_TLS1_3) {
+    // When using IETF-style TLS transport parameters, inform existing streams
+    // of new flow-control limits.
+    if (config_.HasReceivedInitialMaxStreamDataBytesOutgoingBidirectional()) {
+      OnNewStreamOutgoingBidirectionalFlowControlWindow(
+          config_.ReceivedInitialMaxStreamDataBytesOutgoingBidirectional());
+    }
+    if (config_.HasReceivedInitialMaxStreamDataBytesIncomingBidirectional()) {
+      OnNewStreamIncomingBidirectionalFlowControlWindow(
+          config_.ReceivedInitialMaxStreamDataBytesIncomingBidirectional());
+    }
+    if (config_.HasReceivedInitialMaxStreamDataBytesUnidirectional()) {
+      OnNewStreamUnidirectionalFlowControlWindow(
+          config_.ReceivedInitialMaxStreamDataBytesUnidirectional());
+    }
+  } else {  // The version uses Google QUIC Crypto.
+    if (config_.HasReceivedInitialStreamFlowControlWindowBytes()) {
+      // Streams which were created before the SHLO was received (0-RTT
+      // requests) are now informed of the peer's initial flow control window.
+      OnNewStreamFlowControlWindow(
+          config_.ReceivedInitialStreamFlowControlWindowBytes());
+    }
   }
+
   if (config_.HasReceivedInitialSessionFlowControlWindowBytes()) {
     OnNewSessionFlowControlWindow(
         config_.ReceivedInitialSessionFlowControlWindowBytes());
@@ -1061,6 +1079,11 @@
     QuicConnection::ScopedPacketFlusher flusher(connection());
     v99_streamid_manager_.OnConfigNegotiated();
   }
+
+  // Ask flow controllers to try again since the config could have unblocked us.
+  if (connection_->version().AllowsLowFlowControlLimits()) {
+    OnCanWrite();
+  }
 }
 
 void QuicSession::AdjustInitialFlowControlWindows(size_t stream_window) {
@@ -1132,6 +1155,68 @@
   }
 }
 
+void QuicSession::OnNewStreamUnidirectionalFlowControlWindow(
+    QuicStreamOffset new_window) {
+  QUIC_DVLOG(1) << ENDPOINT << "OnNewStreamUnidirectionalFlowControlWindow "
+                << new_window;
+  // Inform all existing outgoing unidirectional streams about the new window.
+  for (auto const& kv : stream_map_) {
+    const QuicStreamId id = kv.first;
+    if (QuicUtils::IsBidirectionalStreamId(id)) {
+      continue;
+    }
+    if (!QuicUtils::IsOutgoingStreamId(connection_->version(), id,
+                                       perspective())) {
+      continue;
+    }
+    QUIC_DVLOG(1) << ENDPOINT << "Informing unidirectional stream " << id
+                  << " of new stream flow control window " << new_window;
+    kv.second->UpdateSendWindowOffset(new_window);
+  }
+}
+
+void QuicSession::OnNewStreamOutgoingBidirectionalFlowControlWindow(
+    QuicStreamOffset new_window) {
+  QUIC_DVLOG(1) << ENDPOINT
+                << "OnNewStreamOutgoingBidirectionalFlowControlWindow "
+                << new_window;
+  // Inform all existing outgoing bidirectional streams about the new window.
+  for (auto const& kv : stream_map_) {
+    const QuicStreamId id = kv.first;
+    if (!QuicUtils::IsBidirectionalStreamId(id)) {
+      continue;
+    }
+    if (!QuicUtils::IsOutgoingStreamId(connection_->version(), id,
+                                       perspective())) {
+      continue;
+    }
+    QUIC_DVLOG(1) << ENDPOINT << "Informing outgoing bidirectional stream "
+                  << id << " of new stream flow control window " << new_window;
+    kv.second->UpdateSendWindowOffset(new_window);
+  }
+}
+
+void QuicSession::OnNewStreamIncomingBidirectionalFlowControlWindow(
+    QuicStreamOffset new_window) {
+  QUIC_DVLOG(1) << ENDPOINT
+                << "OnNewStreamIncomingBidirectionalFlowControlWindow "
+                << new_window;
+  // Inform all existing incoming bidirectional streams about the new window.
+  for (auto const& kv : stream_map_) {
+    const QuicStreamId id = kv.first;
+    if (!QuicUtils::IsBidirectionalStreamId(id)) {
+      continue;
+    }
+    if (QuicUtils::IsOutgoingStreamId(connection_->version(), id,
+                                      perspective())) {
+      continue;
+    }
+    QUIC_DVLOG(1) << ENDPOINT << "Informing incoming bidirectional stream "
+                  << id << " of new stream flow control window " << new_window;
+    kv.second->UpdateSendWindowOffset(new_window);
+  }
+}
+
 void QuicSession::OnNewSessionFlowControlWindow(QuicStreamOffset new_window) {
   if (new_window < kMinimumFlowControlSendWindow &&
       !connection_->version().AllowsLowFlowControlLimits()) {