Double the initial congestion window in QuicSentPacketManager

This new behavior is gated on the QuicTag IW2X and a new reloadable flag, quic_allow_client_enabled_2x_initial_cwnd.

Protected by FLAGS_quic_reloadable_flag_quic_allow_client_enabled_2x_initial_cwnd.

PiperOrigin-RevId: 773849231
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h
index 12ffbdd..1e52ff0 100755
--- a/quiche/common/quiche_feature_flags_list.h
+++ b/quiche/common/quiche_feature_flags_list.h
@@ -12,6 +12,7 @@
 QUICHE_FLAG(bool, quiche_reloadable_flag_enable_tls_trust_anchor_ids, true, true, "When true, QUIC client and server will support TLS Trust Anchor IDs.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_act_upon_invalid_header, true, true, "If true, reject or send error response code upon receiving invalid request or response headers.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_add_stream_info_to_idle_close_detail, false, true, "If true, include stream information in idle timeout connection close detail.")
+QUICHE_FLAG(bool, quiche_reloadable_flag_quic_allow_client_enabled_2x_initial_cwnd, false, false, "Doubles the initial congestion window for QUIC connections when initiated by the client")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_allow_client_enabled_bbr_v2, true, true, "If true, allow client to enable BBRv2 on server via connection option 'B2ON'.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_bbr2_extra_acked_window, false, true, "When true, the BBR4 copt sets the extra_acked window to 20 RTTs and BBR5 sets it to 40 RTTs.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_bbr2_probe_two_rounds, true, true, "When true, the BB2U copt causes BBR2 to wait two rounds with out draining the queue before exiting PROBE_UP and BB2S has the same effect in STARTUP.")
diff --git a/quiche/quic/core/crypto/crypto_protocol.h b/quiche/quic/core/crypto/crypto_protocol.h
index 244321f..9056c47 100644
--- a/quiche/quic/core/crypto/crypto_protocol.h
+++ b/quiche/quic/core/crypto/crypto_protocol.h
@@ -151,6 +151,7 @@
 DEFINE_STATIC_QUIC_TAG(IW10);  // Force ICWND to 10
 DEFINE_STATIC_QUIC_TAG(IW20);  // Force ICWND to 20
 DEFINE_STATIC_QUIC_TAG(IW50);  // Force ICWND to 50
+DEFINE_STATIC_QUIC_TAG(IW2X);  // Force ICWND to 2x its default value.
 DEFINE_STATIC_QUIC_TAG(B2ON);  // Enable BBRv2
 DEFINE_STATIC_QUIC_TAG(B2NA);  // For BBRv2, do not add ack
                                // height to queueing threshold
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc
index 7b4ae68..ec8d3a4 100644
--- a/quiche/quic/core/http/end_to_end_test.cc
+++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -3890,6 +3890,30 @@
   server_thread_->Resume();
 }
 
+TEST_P(EndToEndTest, NegotiatedDoubledInitialCongestionWindow) {
+  SetQuicReloadableFlag(quic_allow_client_enabled_2x_initial_cwnd, true);
+  client_extra_copts_.push_back(kIW2X);
+
+  ASSERT_TRUE(Initialize());
+
+  // Values are exchanged during crypto handshake, so wait for that to finish.
+  EXPECT_TRUE(client_->client()->WaitForOneRttKeysAvailable());
+  server_thread_->WaitForCryptoHandshakeConfirmed();
+  server_thread_->Pause();
+  QuicConnection* server_connection = GetServerConnection();
+  ASSERT_NE(server_connection, nullptr);
+  EXPECT_EQ(
+      server_connection->sent_packet_manager().initial_congestion_window(),
+      kInitialCongestionWindow * 2);
+  server_thread_->Resume();
+
+  QuicConnection* client_connection = GetClientConnection();
+  ASSERT_NE(client_connection, nullptr);
+  EXPECT_EQ(
+      client_connection->sent_packet_manager().initial_congestion_window(),
+      kInitialCongestionWindow);
+}
+
 TEST_P(EndToEndTest, DifferentFlowControlWindows) {
   // Client and server can set different initial flow control receive windows.
   // These are sent in CHLO/SHLO. Tests that these values are exchanged properly
diff --git a/quiche/quic/core/quic_sent_packet_manager.cc b/quiche/quic/core/quic_sent_packet_manager.cc
index ae905a5..2c60d23 100644
--- a/quiche/quic/core/quic_sent_packet_manager.cc
+++ b/quiche/quic/core/quic_sent_packet_manager.cc
@@ -187,6 +187,13 @@
     initial_congestion_window_ = 50;
     send_algorithm_->SetInitialCongestionWindowInPackets(50);
   }
+  if (config.HasClientRequestedIndependentOption(kIW2X, perspective) &&
+      GetQuicReloadableFlag(quic_allow_client_enabled_2x_initial_cwnd)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_allow_client_enabled_2x_initial_cwnd);
+    initial_congestion_window_ *= 2;
+    send_algorithm_->SetInitialCongestionWindowInPackets(
+        initial_congestion_window_);
+  }
   if (config.HasClientRequestedIndependentOption(kBWS5, perspective)) {
     initial_congestion_window_ = 10;
     send_algorithm_->SetInitialCongestionWindowInPackets(10);
diff --git a/quiche/quic/core/quic_sent_packet_manager_test.cc b/quiche/quic/core/quic_sent_packet_manager_test.cc
index 0427cfb..d7c051b 100644
--- a/quiche/quic/core/quic_sent_packet_manager_test.cc
+++ b/quiche/quic/core/quic_sent_packet_manager_test.cc
@@ -2121,6 +2121,51 @@
   EXPECT_EQ(10u, manager_.initial_congestion_window());
 }
 
+TEST_F(QuicSentPacketManagerTest, ServerCongestionWindowDoubledWithIW2X) {
+  SetQuicReloadableFlag(quic_allow_client_enabled_2x_initial_cwnd, true);
+  QuicConfig config;
+  QuicConfigPeer::SetReceivedConnectionOptions(&config, {kIW2X});
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  EXPECT_CALL(*send_algorithm_, SetInitialCongestionWindowInPackets(
+                                    kInitialCongestionWindow * 2));
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  manager_.SetFromConfig(config);
+
+  EXPECT_EQ(manager_.initial_congestion_window(), kInitialCongestionWindow * 2);
+}
+
+TEST_F(QuicSentPacketManagerTest,
+       ServerCongestionWindowIsDefaultWithIW2XAndNoFlag) {
+  SetQuicReloadableFlag(quic_allow_client_enabled_2x_initial_cwnd, false);
+  QuicConfig config;
+  QuicConfigPeer::SetReceivedConnectionOptions(&config, {kIW2X});
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  EXPECT_CALL(*send_algorithm_, SetInitialCongestionWindowInPackets(_))
+      .Times(0);
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  manager_.SetFromConfig(config);
+
+  EXPECT_EQ(manager_.initial_congestion_window(), kInitialCongestionWindow);
+}
+
+TEST_F(QuicSentPacketManagerTest,
+       ClientCongestionWindowIsDefaultWithIW2XAndNoFlag) {
+  QuicSentPacketManagerPeer::SetPerspective(&manager_, Perspective::IS_CLIENT);
+  SetQuicReloadableFlag(quic_allow_client_enabled_2x_initial_cwnd, false);
+  QuicConfig config;
+  config.SetConnectionOptionsToSend({kIW2X});
+  config.SetClientConnectionOptions({});
+
+  EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+  EXPECT_CALL(*send_algorithm_,
+              SetInitialCongestionWindowInPackets(kInitialCongestionWindow * 2))
+      .Times(0);
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  manager_.SetFromConfig(config);
+
+  EXPECT_EQ(manager_.initial_congestion_window(), kInitialCongestionWindow);
+}
+
 TEST_F(QuicSentPacketManagerTest, ClientMultiplePacketNumberSpacePtoTimeout) {
   manager_.EnableMultiplePacketNumberSpacesSupport();
   EXPECT_CALL(*send_algorithm_, PacingRate(_))