[quic]Add client connection option MPQM to control the migration when multi-port is enabled.

PiperOrigin-RevId: 561995255
diff --git a/quiche/quic/core/crypto/crypto_protocol.h b/quiche/quic/core/crypto/crypto_protocol.h
index 2725912..157e2d3 100644
--- a/quiche/quic/core/crypto/crypto_protocol.h
+++ b/quiche/quic/core/crypto/crypto_protocol.h
@@ -431,6 +431,8 @@
                                                  // INVALID_VERSION
 
 const QuicTag kMPQC = TAG('M', 'P', 'Q', 'C');   // Multi-port QUIC connection
+const QuicTag kMPQM = TAG('M', 'P', 'Q', 'M');   // Enable multi-port QUIC
+                                                 // migration
 
 // Client Hints triggers.
 const QuicTag kGWCH = TAG('G', 'W', 'C', 'H');
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc
index 58090a8..8cfa2c6 100644
--- a/quiche/quic/core/http/end_to_end_test.cc
+++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -5368,7 +5368,7 @@
 }
 
 TEST_P(EndToEndTest, ClientMultiPortConnection) {
-  client_config_.SetClientConnectionOptions(QuicTagVector{kMPQC});
+  client_config_.SetClientConnectionOptions(QuicTagVector{kMPQC, kMPQM});
   ASSERT_TRUE(Initialize());
   if (!version_.HasIetfQuicFrames()) {
     return;
@@ -5414,7 +5414,7 @@
 }
 
 TEST_P(EndToEndTest, ClientMultiPortMigrationOnPathDegrading) {
-  client_config_.SetClientConnectionOptions(QuicTagVector{kMPQC});
+  client_config_.SetClientConnectionOptions(QuicTagVector{kMPQC, kMPQM});
   ASSERT_TRUE(Initialize());
   if (!version_.HasIetfQuicFrames()) {
     return;
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 693da98..3a991d8 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -693,6 +693,9 @@
   if (perspective_ == Perspective::IS_CLIENT && version().HasIetfQuicFrames() &&
       config.HasClientRequestedIndependentOption(kMPQC, perspective_)) {
     multi_port_stats_ = std::make_unique<MultiPortStats>();
+    if (config.HasClientRequestedIndependentOption(kMPQM, perspective_)) {
+      multi_port_migration_enabled_ = true;
+    }
   }
 }
 
@@ -6232,7 +6235,7 @@
   is_path_degrading_ = true;
   visitor_->OnPathDegrading();
   stats_.num_path_degrading++;
-  if (multi_port_stats_) {
+  if (multi_port_stats_ && multi_port_migration_enabled_) {
     MaybeMigrateToMultiPortPath();
   }
 }
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index 053017e..60dab8c 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -2362,6 +2362,9 @@
 
   std::unique_ptr<MultiPortStats> multi_port_stats_;
 
+  // If true, connection will migrate to multi-port path upon path degrading.
+  bool multi_port_migration_enabled_ = false;
+
   // Client side only.
   bool active_migration_disabled_ = false;
 
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index 0e213fd..7ef137d 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -13182,12 +13182,22 @@
   EXPECT_EQ(kTestRTT,
             stats->rtt_stats_when_default_path_degrading.latest_rtt());
 
+  EXPECT_CALL(visitor_, OnForwardProgressMadeAfterPathDegrading());
+  QuicConnectionPeer::OnForwardProgressMade(&connection_);
+
   EXPECT_TRUE(connection_.GetMultiPortProbingAlarm()->IsSet());
   // Since there's already a scheduled probing alarm, manual calls won't have
   // any effect.
   connection_.MaybeProbeMultiPortPath();
   EXPECT_FALSE(connection_.HasPendingPathValidation());
 
+  // Since kMPQM is not set, migration shouldn't happen
+  EXPECT_CALL(visitor_, OnPathDegrading());
+  EXPECT_CALL(visitor_, MigrateToMultiPortPath(_)).Times(0);
+  connection_.OnPathDegradingDetected();
+  EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
+      &connection_, kNewSelfAddress, connection_.peer_address()));
+
   // Simulate the case where the path validation fails after retries.
   connection_.GetMultiPortProbingAlarm()->Fire();
   EXPECT_TRUE(connection_.HasPendingPathValidation());
@@ -13204,7 +13214,7 @@
   EXPECT_FALSE(connection_.HasPendingPathValidation());
   EXPECT_FALSE(QuicConnectionPeer::IsAlternativePath(
       &connection_, kNewSelfAddress, connection_.peer_address()));
-  EXPECT_EQ(1, connection_.GetStats().num_path_degrading);
+  EXPECT_EQ(2, connection_.GetStats().num_path_degrading);
   EXPECT_EQ(1, stats->num_multi_port_probe_failures_when_path_degrading);
   EXPECT_EQ(0, stats->num_multi_port_probe_failures_when_path_not_degrading);
 }
@@ -13488,7 +13498,7 @@
       .WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
   set_perspective(Perspective::IS_CLIENT);
   QuicConfig config;
-  config.SetClientConnectionOptions(QuicTagVector{kMPQC});
+  config.SetClientConnectionOptions(QuicTagVector{kMPQC, kMPQM});
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
   connection_.SetFromConfig(config);
   if (!version().HasIetfQuicFrames()) {
@@ -13566,7 +13576,7 @@
       .WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
   set_perspective(Perspective::IS_CLIENT);
   QuicConfig config;
-  config.SetClientConnectionOptions(QuicTagVector{kMPQC});
+  config.SetClientConnectionOptions(QuicTagVector{kMPQC, kMPQM});
   EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
   connection_.SetFromConfig(config);
   if (!version().HasIetfQuicFrames()) {
diff --git a/quiche/quic/test_tools/quic_connection_peer.cc b/quiche/quic/test_tools/quic_connection_peer.cc
index 8ac35d5..9dcb6a5 100644
--- a/quiche/quic/test_tools/quic_connection_peer.cc
+++ b/quiche/quic/test_tools/quic_connection_peer.cc
@@ -606,5 +606,10 @@
   connection->disable_ecn_codepoint_validation_ = true;
 }
 
+// static
+void QuicConnectionPeer::OnForwardProgressMade(QuicConnection* connection) {
+  connection->OnForwardProgressMade();
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quiche/quic/test_tools/quic_connection_peer.h b/quiche/quic/test_tools/quic_connection_peer.h
index 3c8e068..21e31ba 100644
--- a/quiche/quic/test_tools/quic_connection_peer.h
+++ b/quiche/quic/test_tools/quic_connection_peer.h
@@ -238,6 +238,8 @@
 
   // Overrides restrictions on sending ECN for test purposes.
   static void DisableEcnCodepointValidation(QuicConnection* connection);
+
+  static void OnForwardProgressMade(QuicConnection* connection);
 };
 
 }  // namespace test