Allow client to activate PragueCubic congestion control via connection option for experiment purposes.

At first, this option will be enabled on a local Chrome instance for basic lab experiments.

This option has no effect on the server, so it could be stripped from the client hello. @ianswett recommended I send it to remain consistent with the framework, for now.

see go/prague-cubic.

PiperOrigin-RevId: 688685081
diff --git a/quiche/quic/core/crypto/crypto_protocol.h b/quiche/quic/core/crypto/crypto_protocol.h
index 48e98d5..c9f697f 100644
--- a/quiche/quic/core/crypto/crypto_protocol.h
+++ b/quiche/quic/core/crypto/crypto_protocol.h
@@ -287,6 +287,9 @@
 
 const QuicTag kCRNT = TAG('C', 'R', 'N', 'T');
 
+const QuicTag kPRGC = TAG('P', 'R', 'G', 'C');   // Prague Cubic congestion
+                                                 // control (client-only)
+
 // Optional support of truncated Connection IDs.  If sent by a peer, the value
 // is the minimum number of bytes allowed for the connection ID sent to the
 // peer.
diff --git a/quiche/quic/core/quic_config_test.cc b/quiche/quic/core/quic_config_test.cc
index a157eaa..ea311e9 100644
--- a/quiche/quic/core/quic_config_test.cc
+++ b/quiche/quic/core/quic_config_test.cc
@@ -14,6 +14,7 @@
 #include "quiche/quic/core/quic_constants.h"
 #include "quiche/quic/core/quic_packets.h"
 #include "quiche/quic/core/quic_time.h"
+#include "quiche/quic/core/quic_types.h"
 #include "quiche/quic/core/quic_utils.h"
 #include "quiche/quic/platform/api/quic_expect_bug.h"
 #include "quiche/quic/platform/api/quic_flags.h"
@@ -335,9 +336,12 @@
   QuicConfig client_config;
   QuicTagVector copt;
   copt.push_back(kTBBR);
+  copt.push_back(kPRGC);
   client_config.SetConnectionOptionsToSend(copt);
   EXPECT_TRUE(client_config.HasClientSentConnectionOption(
       kTBBR, Perspective::IS_CLIENT));
+  EXPECT_TRUE(client_config.HasClientSentConnectionOption(
+      kPRGC, Perspective::IS_CLIENT));
 
   CryptoHandshakeMessage msg;
   client_config.ToHandshakeMessage(&msg, version_.transport_version);
@@ -349,9 +353,11 @@
   EXPECT_TRUE(config_.negotiated());
 
   EXPECT_TRUE(config_.HasReceivedConnectionOptions());
-  EXPECT_EQ(1u, config_.ReceivedConnectionOptions().size());
+  EXPECT_EQ(2u, config_.ReceivedConnectionOptions().size());
   EXPECT_TRUE(
       config_.HasClientSentConnectionOption(kTBBR, Perspective::IS_SERVER));
+  EXPECT_TRUE(
+      config_.HasClientSentConnectionOption(kPRGC, Perspective::IS_SERVER));
 }
 
 TEST_P(QuicConfigTest, DontSendClientConnectionOptions) {
diff --git a/quiche/quic/core/quic_sent_packet_manager.cc b/quiche/quic/core/quic_sent_packet_manager.cc
index e4f364c..94c228d 100644
--- a/quiche/quic/core/quic_sent_packet_manager.cc
+++ b/quiche/quic/core/quic_sent_packet_manager.cc
@@ -21,6 +21,7 @@
 #include "quiche/quic/core/quic_connection_stats.h"
 #include "quiche/quic/core/quic_constants.h"
 #include "quiche/quic/core/quic_packet_number.h"
+#include "quiche/quic/core/quic_tag.h"
 #include "quiche/quic/core/quic_transmission_info.h"
 #include "quiche/quic/core/quic_types.h"
 #include "quiche/quic/core/quic_utils.h"
@@ -136,6 +137,10 @@
   }
 
   // Configure congestion control.
+  if (perspective == Perspective::IS_CLIENT &&
+      config.HasClientRequestedIndependentOption(kPRGC, perspective)) {
+    SetSendAlgorithm(kPragueCubic);
+  }
   if (config.HasClientRequestedIndependentOption(kTBBR, perspective)) {
     SetSendAlgorithm(kBBR);
   }
@@ -240,7 +245,9 @@
   } else if (ContainsQuicTag(connection_options, kQBIC)) {
     cc_type = kCubicBytes;
   }
-
+  // This function is only used in server experiments, so do not apply the
+  // client-only PRGC tag.
+  QUICHE_DCHECK(unacked_packets_.perspective() == Perspective::IS_SERVER);
   if (cc_type.has_value()) {
     SetSendAlgorithm(*cc_type);
   }
diff --git a/quiche/quic/core/quic_sent_packet_manager_test.cc b/quiche/quic/core/quic_sent_packet_manager_test.cc
index 5e8c62b..d086c12 100644
--- a/quiche/quic/core/quic_sent_packet_manager_test.cc
+++ b/quiche/quic/core/quic_sent_packet_manager_test.cc
@@ -1246,6 +1246,15 @@
   manager_.SetFromConfig(config);
   EXPECT_EQ(kRenoBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
                             ->GetCongestionControlType());
+
+  options.clear();
+  options.push_back(kPRGC);
+  QuicConfigPeer::SetReceivedConnectionOptions(&config, options);
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  manager_.SetFromConfig(config);
+  // The server does nothing on kPRGC.
+  EXPECT_EQ(kRenoBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+                            ->GetCongestionControlType());
 }
 
 TEST_F(QuicSentPacketManagerTest, NegotiateClientCongestionControlFromOptions) {
@@ -1293,6 +1302,24 @@
   manager_.SetFromConfig(config);
   EXPECT_EQ(kRenoBytes, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
                             ->GetCongestionControlType());
+
+  options.clear();
+  options.push_back(kPRGC);
+  config.SetClientConnectionOptions(options);
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  manager_.SetFromConfig(config);
+  EXPECT_EQ(kPragueCubic, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+                              ->GetCongestionControlType());
+
+  // Test that kPRGC is overriden by other options.
+  options.clear();
+  options.push_back(kPRGC);
+  options.push_back(kTBBR);
+  config.SetClientConnectionOptions(options);
+  EXPECT_CALL(*network_change_visitor_, OnCongestionChange());
+  manager_.SetFromConfig(config);
+  EXPECT_EQ(kBBR, QuicSentPacketManagerPeer::GetSendAlgorithm(manager_)
+                      ->GetCongestionControlType());
 }
 
 TEST_F(QuicSentPacketManagerTest, UseInitialRoundTripTimeToSend) {