Add support for CreateContextForMultiPortPath running async.

This change modifies CreateContextForMultiPortPath to now take an std::function as an argument, and instead of returning an std::unique_ptr<QuicPathValidationContext> the unique_ptr is now passed into the std::function.

There is no behavioral change in how CreateContextForMultiPortPath or MaybeCreateMultiPortPath run.

This change is necessary for Chrome's implementation of CreateContextForMultiPortPath as it will need to be asynchronous. There is a WIP CL out for this change on the chrome side here: https://chromium-review.googlesource.com/c/chromium/src/+/4500184. This CL will need to be uploaded when these current changes are eventually merged into Chrome.

PiperOrigin-RevId: 529106195
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 42a4cf9..9fd036f 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -4095,18 +4095,21 @@
       kMaxNumMultiPortPaths) {
     return;
   }
-  auto path_context = visitor_->CreateContextForMultiPortPath();
-  if (!path_context) {
-    return;
-  }
-  auto multi_port_validation_result_delegate =
-      std::make_unique<MultiPortPathValidationResultDelegate>(this);
-  multi_port_probing_alarm_->Cancel();
-  multi_port_path_context_ = nullptr;
-  multi_port_stats_->num_multi_port_paths_created++;
-  ValidatePath(std::move(path_context),
-               std::move(multi_port_validation_result_delegate),
-               PathValidationReason::kMultiPort);
+
+  visitor_->CreateContextForMultiPortPath(
+      [this](std::unique_ptr<QuicPathValidationContext> path_context) {
+        if (!path_context) {
+          return;
+        }
+        auto multi_port_validation_result_delegate =
+            std::make_unique<MultiPortPathValidationResultDelegate>(this);
+        multi_port_probing_alarm_->Cancel();
+        multi_port_path_context_ = nullptr;
+        multi_port_stats_->num_multi_port_paths_created++;
+        ValidatePath(std::move(path_context),
+                     std::move(multi_port_validation_result_delegate),
+                     PathValidationReason::kMultiPort);
+      });
 }
 
 void QuicConnection::SendOrQueuePacket(SerializedPacket packet) {
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index de10b88..09c1eed 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -237,9 +237,14 @@
   // When bandwidth update alarms.
   virtual void OnBandwidthUpdateTimeout() = 0;
 
-  // Returns context needed for the connection to probe on the alternative path.
-  virtual std::unique_ptr<QuicPathValidationContext>
-  CreateContextForMultiPortPath() = 0;
+  // Runs |create_context| with context needed for the connection to probe on
+  // the alternative path. The callback must be called exactly once. May run
+  // |create_context| synchronously or asynchronously. If |create_context| is
+  // run asynchronously, it must be called on the same thread as QuicConnection
+  // is not thread safe.
+  virtual void CreateContextForMultiPortPath(
+      std::function<void(std::unique_ptr<QuicPathValidationContext>)>
+          create_context) = 0;
 
   // Migrate to the multi-port path which is identified by |context|.
   virtual void MigrateToMultiPortPath(
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index 8fa8d2f..64a0358 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -13382,10 +13382,11 @@
       QuicUtils::GenerateStatelessResetToken(frame.connection_id);
   frame.retire_prior_to = 0u;
   frame.sequence_number = 1u;
-  EXPECT_CALL(visitor_, CreateContextForMultiPortPath())
-      .WillRepeatedly(Return(
-          testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
-              kNewSelfAddress, connection_.peer_address(), &new_writer))));
+  EXPECT_CALL(visitor_, CreateContextForMultiPortPath)
+      .WillRepeatedly(testing::WithArgs<0>([&](auto&& fn) {
+        fn(std::move(std::make_unique<TestQuicPathValidationContext>(
+            kNewSelfAddress, connection_.peer_address(), &new_writer)));
+      }));
   connection_.OnNewConnectionIdFrame(frame);
   EXPECT_TRUE(connection_.HasPendingPathValidation());
   EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
@@ -13517,10 +13518,11 @@
       QuicUtils::GenerateStatelessResetToken(frame.connection_id);
   frame.retire_prior_to = 0u;
   frame.sequence_number = 1u;
-  EXPECT_CALL(visitor_, CreateContextForMultiPortPath())
-      .WillRepeatedly(Return(
-          testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
-              kNewSelfAddress, connection_.peer_address(), &new_writer))));
+  EXPECT_CALL(visitor_, CreateContextForMultiPortPath)
+      .WillRepeatedly(testing::WithArgs<0>([&](auto&& fn) {
+        fn(std::move(std::make_unique<TestQuicPathValidationContext>(
+            kNewSelfAddress, connection_.peer_address(), &new_writer)));
+      }));
   EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame));
   EXPECT_TRUE(connection_.HasPendingPathValidation());
   EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
@@ -13555,10 +13557,11 @@
         QuicUtils::GenerateStatelessResetToken(frame.connection_id);
     frame.retire_prior_to = 0u;
     frame.sequence_number = i + 2;
-    EXPECT_CALL(visitor_, CreateContextForMultiPortPath())
-        .WillRepeatedly(Return(
-            testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
-                kNewSelfAddress, connection_.peer_address(), &new_writer))));
+    EXPECT_CALL(visitor_, CreateContextForMultiPortPath)
+        .WillRepeatedly(testing::WithArgs<0>([&](auto&& fn) {
+          fn(std::move(std::make_unique<TestQuicPathValidationContext>(
+              kNewSelfAddress, connection_.peer_address(), &new_writer)));
+        }));
     EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame));
     EXPECT_TRUE(connection_.HasPendingPathValidation());
     EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
@@ -13626,10 +13629,11 @@
       QuicUtils::GenerateStatelessResetToken(frame.connection_id);
   frame.retire_prior_to = 0u;
   frame.sequence_number = 1u;
-  EXPECT_CALL(visitor_, CreateContextForMultiPortPath())
-      .WillRepeatedly(Return(
-          testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
-              kNewSelfAddress, connection_.peer_address(), &new_writer))));
+  EXPECT_CALL(visitor_, CreateContextForMultiPortPath)
+      .WillRepeatedly(testing::WithArgs<0>([&](auto&& fn) {
+        fn(std::move(std::make_unique<TestQuicPathValidationContext>(
+            kNewSelfAddress, connection_.peer_address(), &new_writer)));
+      }));
   EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame));
   EXPECT_TRUE(connection_.HasPendingPathValidation());
   EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
@@ -13695,10 +13699,11 @@
       QuicUtils::GenerateStatelessResetToken(frame.connection_id);
   frame.retire_prior_to = 0u;
   frame.sequence_number = 1u;
-  EXPECT_CALL(visitor_, CreateContextForMultiPortPath())
-      .WillRepeatedly(Return(
-          testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
-              kNewSelfAddress, connection_.peer_address(), &new_writer))));
+  EXPECT_CALL(visitor_, CreateContextForMultiPortPath)
+      .WillRepeatedly(testing::WithArgs<0>([&](auto&& fn) {
+        fn(std::move(std::make_unique<TestQuicPathValidationContext>(
+            kNewSelfAddress, connection_.peer_address(), &new_writer)));
+      }));
   EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame));
   EXPECT_TRUE(connection_.HasPendingPathValidation());
   EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
@@ -13772,10 +13777,11 @@
       QuicUtils::GenerateStatelessResetToken(frame.connection_id);
   frame.retire_prior_to = 0u;
   frame.sequence_number = 1u;
-  EXPECT_CALL(visitor_, CreateContextForMultiPortPath())
-      .WillRepeatedly(Return(
-          testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
-              kNewSelfAddress, connection_.peer_address(), &new_writer))));
+  EXPECT_CALL(visitor_, CreateContextForMultiPortPath)
+      .WillRepeatedly(testing::WithArgs<0>([&](auto&& fn) {
+        fn(std::move(std::make_unique<TestQuicPathValidationContext>(
+            kNewSelfAddress, connection_.peer_address(), &new_writer)));
+      }));
   EXPECT_TRUE(connection_.OnNewConnectionIdFrame(frame));
   EXPECT_TRUE(connection_.HasPendingPathValidation());
   EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
@@ -16842,10 +16848,11 @@
       QuicUtils::GenerateStatelessResetToken(frame.connection_id);
   frame.retire_prior_to = 0u;
   frame.sequence_number = 2u;
-  EXPECT_CALL(visitor_, CreateContextForMultiPortPath())
-      .WillOnce(Return(
-          testing::ByMove(std::make_unique<TestQuicPathValidationContext>(
-              kNewSelfAddress2, connection_.peer_address(), &new_writer2))));
+  EXPECT_CALL(visitor_, CreateContextForMultiPortPath)
+      .WillOnce(testing::WithArgs<0>([&](auto&& fn) {
+        fn(std::move(std::make_unique<TestQuicPathValidationContext>(
+            kNewSelfAddress2, connection_.peer_address(), &new_writer2)));
+      }));
   connection_.OnNewConnectionIdFrame(frame);
   EXPECT_TRUE(connection_.HasPendingPathValidation());
   EXPECT_EQ(1u, new_writer.path_challenge_frames().size());
diff --git a/quiche/quic/core/quic_session.h b/quiche/quic/core/quic_session.h
index e1c1932..1034a3c 100644
--- a/quiche/quic/core/quic_session.h
+++ b/quiche/quic/core/quic_session.h
@@ -181,9 +181,10 @@
   bool ValidateToken(absl::string_view token) override;
   bool MaybeSendAddressToken() override;
   void OnBandwidthUpdateTimeout() override {}
-  std::unique_ptr<QuicPathValidationContext> CreateContextForMultiPortPath()
-      override {
-    return nullptr;
+  void CreateContextForMultiPortPath(
+      std::function<void(std::unique_ptr<QuicPathValidationContext>)>
+          create_context) override {
+    create_context(nullptr);
   }
   void MigrateToMultiPortPath(
       std::unique_ptr<QuicPathValidationContext> /*context*/) override {}
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h
index a60de26..c21107c 100644
--- a/quiche/quic/test_tools/quic_test_utils.h
+++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -504,8 +504,9 @@
   MOCK_METHOD(void, BeforeConnectionCloseSent, (), (override));
   MOCK_METHOD(bool, ValidateToken, (absl::string_view), (override));
   MOCK_METHOD(bool, MaybeSendAddressToken, (), (override));
-  MOCK_METHOD(std::unique_ptr<QuicPathValidationContext>,
-              CreateContextForMultiPortPath, (), (override));
+  MOCK_METHOD(void, CreateContextForMultiPortPath,
+              (std::function<void(std::unique_ptr<QuicPathValidationContext>)>),
+              (override));
   MOCK_METHOD(void, MigrateToMultiPortPath,
               (std::unique_ptr<QuicPathValidationContext>), (override));
   MOCK_METHOD(void, OnServerPreferredAddressAvailable,
diff --git a/quiche/quic/test_tools/simulator/quic_endpoint.h b/quiche/quic/test_tools/simulator/quic_endpoint.h
index 6be7bbb..29b72c5 100644
--- a/quiche/quic/test_tools/simulator/quic_endpoint.h
+++ b/quiche/quic/test_tools/simulator/quic_endpoint.h
@@ -105,9 +105,10 @@
   bool ValidateToken(absl::string_view /*token*/) override { return true; }
   bool MaybeSendAddressToken() override { return false; }
   void OnBandwidthUpdateTimeout() override {}
-  std::unique_ptr<QuicPathValidationContext> CreateContextForMultiPortPath()
-      override {
-    return nullptr;
+  void CreateContextForMultiPortPath(
+      std::function<void(std::unique_ptr<QuicPathValidationContext>)>
+          create_context) override {
+    create_context(nullptr);
   }
   void MigrateToMultiPortPath(
       std::unique_ptr<QuicPathValidationContext> /*context*/) override {}
diff --git a/quiche/quic/tools/quic_simple_client_session.cc b/quiche/quic/tools/quic_simple_client_session.cc
index 23601ad..eaaa921 100644
--- a/quiche/quic/tools/quic_simple_client_session.cc
+++ b/quiche/quic/tools/quic_simple_client_session.cc
@@ -50,24 +50,28 @@
                                : HttpDatagramSupport::kNone;
 }
 
-std::unique_ptr<QuicPathValidationContext>
-QuicSimpleClientSession::CreateContextForMultiPortPath() {
+void QuicSimpleClientSession::CreateContextForMultiPortPath(
+    std::function<void(std::unique_ptr<QuicPathValidationContext>)>
+        create_context) {
   if (!network_helper_ || connection()->multi_port_stats() == nullptr) {
-    return nullptr;
+    create_context(nullptr);
+    return;
   }
   auto self_address = connection()->self_address();
   auto server_address = connection()->peer_address();
   if (!network_helper_->CreateUDPSocketAndBind(
           server_address, self_address.host(), self_address.port() + 1)) {
-    return nullptr;
+    create_context(nullptr);
+    return;
   }
   QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
   if (writer == nullptr) {
-    return nullptr;
+    create_context(nullptr);
+    return;
   }
-  return std::make_unique<PathMigrationContext>(
+  create_context(std::make_unique<PathMigrationContext>(
       std::unique_ptr<QuicPacketWriter>(writer),
-      network_helper_->GetLatestClientAddress(), peer_address());
+      network_helper_->GetLatestClientAddress(), peer_address()));
 }
 
 void QuicSimpleClientSession::MigrateToMultiPortPath(
diff --git a/quiche/quic/tools/quic_simple_client_session.h b/quiche/quic/tools/quic_simple_client_session.h
index 1770c9f..b172afc 100644
--- a/quiche/quic/tools/quic_simple_client_session.h
+++ b/quiche/quic/tools/quic_simple_client_session.h
@@ -35,8 +35,9 @@
   std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override;
   bool ShouldNegotiateWebTransport() override;
   HttpDatagramSupport LocalHttpDatagramSupport() override;
-  std::unique_ptr<QuicPathValidationContext> CreateContextForMultiPortPath()
-      override;
+  void CreateContextForMultiPortPath(
+      std::function<void(std::unique_ptr<QuicPathValidationContext>)>
+          create_context) override;
   void MigrateToMultiPortPath(
       std::unique_ptr<QuicPathValidationContext> context) override;
   bool drop_response_body() const { return drop_response_body_; }