Adjusts internal OgHttp2Session state when sending SETTINGS_INITIAL_WINDOW_SIZE to the peer.

This implements the final piece of handling SETTINGS_INITIAL_WINDOW_SIZE.

PiperOrigin-RevId: 429386425
diff --git a/http2/adapter/nghttp2_adapter_test.cc b/http2/adapter/nghttp2_adapter_test.cc
index 5791692..0a7526c 100644
--- a/http2/adapter/nghttp2_adapter_test.cc
+++ b/http2/adapter/nghttp2_adapter_test.cc
@@ -295,6 +295,83 @@
             kInitialFlowControlWindowSize + 20000);
 }
 
+TEST(NgHttp2AdapterTest, AckOfSettingInitialWindowSizeAffectsWindow) {
+  DataSavingVisitor visitor;
+  auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+
+  testing::InSequence s;
+
+  const std::vector<Header> headers =
+      ToHeaders({{":method", "GET"},
+                 {":scheme", "http"},
+                 {":authority", "example.com"},
+                 {":path", "/this/is/request/one"}});
+  const int32_t stream_id1 = adapter->SubmitRequest(headers, nullptr, nullptr);
+
+  EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+  EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+  int result = adapter->Send();
+  EXPECT_EQ(0, result);
+
+  const std::string initial_frames =
+      TestFrameSequence().ServerPreface().Serialize();
+  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x0));
+  EXPECT_CALL(visitor, OnSettingsStart());
+  EXPECT_CALL(visitor, OnSettingsEnd());
+
+  int64_t parse_result = adapter->ProcessBytes(initial_frames);
+  EXPECT_EQ(initial_frames.size(), static_cast<size_t>(parse_result));
+
+  EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1),
+            kInitialFlowControlWindowSize);
+  adapter->SubmitSettings({{INITIAL_WINDOW_SIZE, 80000u}});
+  // No update for the first stream, yet.
+  EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1),
+            kInitialFlowControlWindowSize);
+
+  // Ack of server's initial settings.
+  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+  // Outbound SETTINGS containing INITIAL_WINDOW_SIZE.
+  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0));
+  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0));
+
+  result = adapter->Send();
+  EXPECT_EQ(0, result);
+
+  // Still no update, as a SETTINGS ack has not yet been received.
+  EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1),
+            kInitialFlowControlWindowSize);
+
+  const std::string settings_ack =
+      TestFrameSequence().SettingsAck().Serialize();
+
+  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x1));
+  EXPECT_CALL(visitor, OnSettingsAck);
+
+  parse_result = adapter->ProcessBytes(settings_ack);
+  EXPECT_EQ(settings_ack.size(), static_cast<size_t>(parse_result));
+
+  // Stream window has been updated.
+  EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1), 80000);
+
+  const std::vector<Header> headers2 =
+      ToHeaders({{":method", "GET"},
+                 {":scheme", "http"},
+                 {":authority", "example.com"},
+                 {":path", "/this/is/request/two"}});
+  const int32_t stream_id2 = adapter->SubmitRequest(headers, nullptr, nullptr);
+
+  EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, 0x5));
+  EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, 0x5, 0));
+  result = adapter->Send();
+  EXPECT_EQ(0, result);
+
+  EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id2), 80000);
+}
+
 TEST(NgHttp2AdapterTest, ClientRejects100HeadersWithFin) {
   DataSavingVisitor visitor;
   auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
diff --git a/http2/adapter/oghttp2_adapter_test.cc b/http2/adapter/oghttp2_adapter_test.cc
index c00601e..0dc53ce 100644
--- a/http2/adapter/oghttp2_adapter_test.cc
+++ b/http2/adapter/oghttp2_adapter_test.cc
@@ -379,6 +379,91 @@
             kInitialFlowControlWindowSize + 20000);
 }
 
+TEST(OgHttp2AdapterTest, AckOfSettingInitialWindowSizeAffectsWindow) {
+  DataSavingVisitor visitor;
+  OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
+  auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+  testing::InSequence s;
+
+  const std::vector<Header> headers =
+      ToHeaders({{":method", "GET"},
+                 {":scheme", "http"},
+                 {":authority", "example.com"},
+                 {":path", "/this/is/request/one"}});
+  const int32_t stream_id1 = adapter->SubmitRequest(headers, nullptr, nullptr);
+
+  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+  EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+  EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+  int result = adapter->Send();
+  EXPECT_EQ(0, result);
+
+  const std::string initial_frames =
+      TestFrameSequence()
+          .ServerPreface()
+          .SettingsAck()  // Ack of the client's initial settings.
+          .Serialize();
+  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x0));
+  EXPECT_CALL(visitor, OnSettingsStart());
+  EXPECT_CALL(visitor, OnSettingsEnd());
+  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x1));
+  EXPECT_CALL(visitor, OnSettingsAck);
+
+  int64_t parse_result = adapter->ProcessBytes(initial_frames);
+  EXPECT_EQ(initial_frames.size(), static_cast<size_t>(parse_result));
+
+  EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1),
+            kInitialFlowControlWindowSize);
+  adapter->SubmitSettings({{INITIAL_WINDOW_SIZE, 80000u}});
+  // No update for the first stream, yet.
+  EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1),
+            kInitialFlowControlWindowSize);
+
+  // Ack of server's initial settings.
+  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+  // Outbound SETTINGS containing INITIAL_WINDOW_SIZE.
+  EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0));
+  EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0));
+
+  result = adapter->Send();
+  EXPECT_EQ(0, result);
+
+  // Still no update, as a SETTINGS ack has not yet been received.
+  EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1),
+            kInitialFlowControlWindowSize);
+
+  const std::string settings_ack =
+      TestFrameSequence().SettingsAck().Serialize();
+
+  EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0x1));
+  EXPECT_CALL(visitor, OnSettingsAck);
+
+  parse_result = adapter->ProcessBytes(settings_ack);
+  EXPECT_EQ(settings_ack.size(), static_cast<size_t>(parse_result));
+
+  // Stream window has been updated.
+  EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id1), 80000);
+
+  const std::vector<Header> headers2 =
+      ToHeaders({{":method", "GET"},
+                 {":scheme", "http"},
+                 {":authority", "example.com"},
+                 {":path", "/this/is/request/two"}});
+  const int32_t stream_id2 = adapter->SubmitRequest(headers, nullptr, nullptr);
+
+  EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, 0x5));
+  EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, 0x5, 0));
+  result = adapter->Send();
+  EXPECT_EQ(0, result);
+
+  EXPECT_EQ(adapter->GetStreamReceiveWindowSize(stream_id2), 80000);
+}
+
 TEST(OgHttp2AdapterTest, ClientRejects100HeadersWithFin) {
   DataSavingVisitor visitor;
   OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
diff --git a/http2/adapter/oghttp2_session.cc b/http2/adapter/oghttp2_session.cc
index 563344b..45fecd9 100644
--- a/http2/adapter/oghttp2_session.cc
+++ b/http2/adapter/oghttp2_session.cc
@@ -524,7 +524,9 @@
     return;
   }
 
-  RunOnExit r;
+  const bool is_non_ack_settings = IsNonAckSettings(*frame);
+  MaybeSetupPreface(is_non_ack_settings);
+
   if (frame->frame_type() == spdy::SpdyFrameType::GOAWAY) {
     queued_goaway_ = true;
     if (latched_error_) {
@@ -544,6 +546,9 @@
     UpdateReceiveWindow(
         frame->stream_id(),
         reinterpret_cast<spdy::SpdyWindowUpdateIR&>(*frame).delta());
+  } else if (is_non_ack_settings) {
+    HandleOutboundSettings(
+        *reinterpret_cast<spdy::SpdySettingsIR*>(frame.get()));
   }
   if (frame->stream_id() != 0) {
     auto result = queued_frames_.insert({frame->stream_id(), 1});
@@ -567,7 +572,7 @@
     return kSendError;
   }
 
-  MaybeSetupPreface();
+  MaybeSetupPreface(/*sending_outbound_settings=*/false);
 
   SendResult continue_writing = SendQueuedFrames();
   if (queued_immediate_goaway_) {
@@ -1236,7 +1241,7 @@
             Http2VisitorInterface::ConnectionError::kFlowControlError);
         return;
       } else {
-        UpdateInitialWindowSize(value);
+        UpdateStreamSendWindowSizes(value);
       }
       break;
     default:
@@ -1497,19 +1502,18 @@
   }
 }
 
-void OgHttp2Session::MaybeSetupPreface() {
+void OgHttp2Session::MaybeSetupPreface(bool sending_outbound_settings) {
   if (!queued_preface_) {
+    queued_preface_ = true;
     if (!IsServerSession()) {
       buffered_data_.assign(spdy::kHttp2ConnectionHeaderPrefix,
                             spdy::kHttp2ConnectionHeaderPrefixSize);
     }
-    // First frame must be a non-ack SETTINGS.
-    if (frames_.empty() || !IsNonAckSettings(*frames_.front())) {
-      auto frame = PrepareSettingsFrame(GetInitialSettings());
-      HandleOutboundSettings(*frame);
-      frames_.push_front(std::move(frame));
+    if (!sending_outbound_settings) {
+      QUICHE_DCHECK(frames_.empty());
+      // First frame must be a non-ack SETTINGS.
+      EnqueueFrame(PrepareSettingsFrame(GetInitialSettings()));
     }
-    queued_preface_ = true;
   }
 }
 
@@ -1578,8 +1582,11 @@
             case HEADER_TABLE_SIZE:
               decoder_.GetHpackDecoder()->ApplyHeaderTableSizeSetting(value);
               break;
-            case ENABLE_PUSH:
             case INITIAL_WINDOW_SIZE:
+              UpdateStreamReceiveWindowSizes(value);
+              initial_stream_receive_window_ = value;
+              break;
+            case ENABLE_PUSH:
             case MAX_FRAME_SIZE:
             case MAX_HEADER_LIST_SIZE:
             case ENABLE_CONNECT_PROTOCOL:
@@ -1816,7 +1823,7 @@
   }
 }
 
-void OgHttp2Session::UpdateInitialWindowSize(uint32_t new_value) {
+void OgHttp2Session::UpdateStreamSendWindowSizes(uint32_t new_value) {
   const int32_t delta =
       static_cast<int32_t>(new_value) - initial_stream_send_window_;
   initial_stream_send_window_ = new_value;
@@ -1835,5 +1842,11 @@
   }
 }
 
+void OgHttp2Session::UpdateStreamReceiveWindowSizes(uint32_t new_value) {
+  for (auto& [stream_id, stream_state] : stream_map_) {
+    stream_state.window_manager.OnWindowSizeLimitChange(new_value);
+  }
+}
+
 }  // namespace adapter
 }  // namespace http2
diff --git a/http2/adapter/oghttp2_session.h b/http2/adapter/oghttp2_session.h
index 6f02d2c..a714825 100644
--- a/http2/adapter/oghttp2_session.h
+++ b/http2/adapter/oghttp2_session.h
@@ -289,8 +289,10 @@
 
   struct QUICHE_EXPORT_PRIVATE ProcessBytesResultVisitor;
 
-  // Queues the connection preface, if not already done.
-  void MaybeSetupPreface();
+  // Queues the connection preface, if not already done. If not
+  // `sending_outbound_settings` and the preface has not yet been queued, this
+  // method will generate and enqueue initial SETTINGS.
+  void MaybeSetupPreface(bool sending_outbound_settings);
 
   // Gets the settings to be sent in the initial SETTINGS frame sent as part of
   // the connection preface.
@@ -405,7 +407,13 @@
   // Invoked when sending a flow control window update to the peer.
   void UpdateReceiveWindow(Http2StreamId stream_id, int32_t delta);
 
-  void UpdateInitialWindowSize(uint32_t new_value);
+  // Updates stream send window accounting to respect the peer's advertised
+  // initial window setting.
+  void UpdateStreamSendWindowSizes(uint32_t new_value);
+
+  // Updates stream receive window managers to use the newly advertised stream
+  // initial window.
+  void UpdateStreamReceiveWindowSizes(uint32_t new_value);
 
   // Receives events when inbound frames are parsed.
   Http2VisitorInterface& visitor_;