Allows QuicSpdyClientSessionBase to take an QuicSession::Visitor instance to propagate server preferred address signal via a new interface OnServerPreferredAddressAvailable().

Move initiation of server preferred address validation from the client session to QUIC client.

Client only change. The new interface is a no-op on the server side.

PiperOrigin-RevId: 501579060
diff --git a/quiche/quic/core/http/quic_spdy_client_session.cc b/quiche/quic/core/http/quic_spdy_client_session.cc
index b6fa05d..a30e10c 100644
--- a/quiche/quic/core/http/quic_spdy_client_session.cc
+++ b/quiche/quic/core/http/quic_spdy_client_session.cc
@@ -26,7 +26,15 @@
     QuicConnection* connection, const QuicServerId& server_id,
     QuicCryptoClientConfig* crypto_config,
     QuicClientPushPromiseIndex* push_promise_index)
-    : QuicSpdyClientSessionBase(connection, push_promise_index, config,
+    : QuicSpdyClientSession(config, supported_versions, connection, nullptr,
+                            server_id, crypto_config, push_promise_index) {}
+
+QuicSpdyClientSession::QuicSpdyClientSession(
+    const QuicConfig& config, const ParsedQuicVersionVector& supported_versions,
+    QuicConnection* connection, QuicSession::Visitor* visitor,
+    const QuicServerId& server_id, QuicCryptoClientConfig* crypto_config,
+    QuicClientPushPromiseIndex* push_promise_index)
+    : QuicSpdyClientSessionBase(connection, visitor, push_promise_index, config,
                                 supported_versions),
       server_id_(server_id),
       crypto_config_(crypto_config),
diff --git a/quiche/quic/core/http/quic_spdy_client_session.h b/quiche/quic/core/http/quic_spdy_client_session.h
index df97adc..083baba 100644
--- a/quiche/quic/core/http/quic_spdy_client_session.h
+++ b/quiche/quic/core/http/quic_spdy_client_session.h
@@ -31,6 +31,15 @@
                         const QuicServerId& server_id,
                         QuicCryptoClientConfig* crypto_config,
                         QuicClientPushPromiseIndex* push_promise_index);
+
+  QuicSpdyClientSession(const QuicConfig& config,
+                        const ParsedQuicVersionVector& supported_versions,
+                        QuicConnection* connection,
+                        QuicSession::Visitor* visitor,
+                        const QuicServerId& server_id,
+                        QuicCryptoClientConfig* crypto_config,
+                        QuicClientPushPromiseIndex* push_promise_index);
+
   QuicSpdyClientSession(const QuicSpdyClientSession&) = delete;
   QuicSpdyClientSession& operator=(const QuicSpdyClientSession&) = delete;
   ~QuicSpdyClientSession() override;
diff --git a/quiche/quic/core/http/quic_spdy_client_session_base.cc b/quiche/quic/core/http/quic_spdy_client_session_base.cc
index 3b38b1e..adc3348 100644
--- a/quiche/quic/core/http/quic_spdy_client_session_base.cc
+++ b/quiche/quic/core/http/quic_spdy_client_session_base.cc
@@ -17,9 +17,10 @@
 namespace quic {
 
 QuicSpdyClientSessionBase::QuicSpdyClientSessionBase(
-    QuicConnection* connection, QuicClientPushPromiseIndex* push_promise_index,
-    const QuicConfig& config, const ParsedQuicVersionVector& supported_versions)
-    : QuicSpdySession(connection, nullptr, config, supported_versions),
+    QuicConnection* connection, QuicSession::Visitor* visitor,
+    QuicClientPushPromiseIndex* push_promise_index, const QuicConfig& config,
+    const ParsedQuicVersionVector& supported_versions)
+    : QuicSpdySession(connection, visitor, config, supported_versions),
       push_promise_index_(push_promise_index),
       largest_promised_stream_id_(
           QuicUtils::GetInvalidStreamId(connection->transport_version())) {}
diff --git a/quiche/quic/core/http/quic_spdy_client_session_base.h b/quiche/quic/core/http/quic_spdy_client_session_base.h
index 04a0d76..39746b6 100644
--- a/quiche/quic/core/http/quic_spdy_client_session_base.h
+++ b/quiche/quic/core/http/quic_spdy_client_session_base.h
@@ -40,6 +40,7 @@
   // Takes ownership of |connection|. Caller retains ownership of
   // |promised_by_url|.
   QuicSpdyClientSessionBase(QuicConnection* connection,
+                            QuicSession::Visitor* visitor,
                             QuicClientPushPromiseIndex* push_promise_index,
                             const QuicConfig& config,
                             const ParsedQuicVersionVector& supported_versions);
diff --git a/quiche/quic/core/quic_dispatcher.h b/quiche/quic/core/quic_dispatcher.h
index 4c3e278..24602b3 100644
--- a/quiche/quic/core/quic_dispatcher.h
+++ b/quiche/quic/core/quic_dispatcher.h
@@ -116,6 +116,11 @@
   void OnConnectionIdRetired(
       const QuicConnectionId& server_connection_id) override;
 
+  void OnServerPreferredAddressAvailable(
+      const QuicSocketAddress& /*server_preferred_address*/) override {
+    QUICHE_DCHECK(false);
+  }
+
   // QuicTimeWaitListManager::Visitor interface implementation
   // Called whenever the time wait list manager adds a new connection to the
   // time-wait list.
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc
index 208c01b..7aaa65b 100644
--- a/quiche/quic/core/quic_session.cc
+++ b/quiche/quic/core/quic_session.cc
@@ -27,6 +27,7 @@
 #include "quiche/quic/platform/api/quic_logging.h"
 #include "quiche/quic/platform/api/quic_server_stats.h"
 #include "quiche/quic/platform/api/quic_stack_trace.h"
+#include "quiche/common/platform/api/quiche_logging.h"
 #include "quiche/common/quiche_text_utils.h"
 
 namespace quic {
@@ -2694,5 +2695,13 @@
   return valid;
 }
 
+void QuicSession::OnServerPreferredAddressAvailable(
+    const QuicSocketAddress& server_preferred_address) {
+  QUICHE_DCHECK_EQ(perspective_, Perspective::IS_CLIENT);
+  if (visitor_ != nullptr) {
+    visitor_->OnServerPreferredAddressAvailable(server_preferred_address);
+  }
+}
+
 #undef ENDPOINT  // undef for jumbo builds
 }  // namespace quic
diff --git a/quiche/quic/core/quic_session.h b/quiche/quic/core/quic_session.h
index bf9531a..ec52225 100644
--- a/quiche/quic/core/quic_session.h
+++ b/quiche/quic/core/quic_session.h
@@ -68,8 +68,11 @@
       public QuicControlFrameManager::DelegateInterface {
  public:
   // An interface from the session to the entity owning the session.
-  // This lets the session notify its owner (the Dispatcher) when the connection
-  // is closed, blocked, or added/removed from the time-wait list.
+  // This lets the session notify its owner when the connection
+  // is closed, blocked, etc.
+  // TODO(danzh): split this visitor to separate visitors for client and server
+  // respectively as not all methods in this class are interesting to both
+  // perspectives.
   class QUIC_EXPORT_PRIVATE Visitor {
    public:
     virtual ~Visitor() {}
@@ -98,6 +101,9 @@
     // Called when a ConnectionId has been retired.
     virtual void OnConnectionIdRetired(
         const QuicConnectionId& server_connection_id) = 0;
+
+    virtual void OnServerPreferredAddressAvailable(
+        const QuicSocketAddress& /*server_preferred_address*/) = 0;
   };
 
   // Does not take ownership of |connection| or |visitor|.
@@ -180,7 +186,7 @@
     return nullptr;
   }
   void OnServerPreferredAddressAvailable(
-      const QuicSocketAddress& /*server_preferred_address*/) override {}
+      const QuicSocketAddress& /*server_preferred_address*/) override;
 
   // QuicStreamFrameDataProducer
   WriteStreamDataResult WriteStreamData(QuicStreamId id,
diff --git a/quiche/quic/test_tools/mock_quic_session_visitor.h b/quiche/quic/test_tools/mock_quic_session_visitor.h
index 61dc5d5..ab230b4 100644
--- a/quiche/quic/test_tools/mock_quic_session_visitor.h
+++ b/quiche/quic/test_tools/mock_quic_session_visitor.h
@@ -35,6 +35,8 @@
               (const quic::QuicConnectionId& server_connection_id), (override));
   MOCK_METHOD(void, OnConnectionAddedToTimeWaitList,
               (QuicConnectionId connection_id), (override));
+  MOCK_METHOD(void, OnServerPreferredAddressAvailable,
+              (const QuicSocketAddress& server_preferred_address), (override));
 };
 
 class MockQuicCryptoServerStreamHelper
diff --git a/quiche/quic/test_tools/quic_test_utils.cc b/quiche/quic/test_tools/quic_test_utils.cc
index 62c8f69..1a63557 100644
--- a/quiche/quic/test_tools/quic_test_utils.cc
+++ b/quiche/quic/test_tools/quic_test_utils.cc
@@ -738,8 +738,8 @@
     QuicConnection* connection, const QuicConfig& config,
     const ParsedQuicVersionVector& supported_versions,
     const QuicServerId& server_id, QuicCryptoClientConfig* crypto_config)
-    : QuicSpdyClientSessionBase(connection, &push_promise_index_, config,
-                                supported_versions) {
+    : QuicSpdyClientSessionBase(connection, nullptr, &push_promise_index_,
+                                config, supported_versions) {
   // TODO(b/153726130): Consider adding SetServerApplicationStateForResumption
   // calls in tests and set |has_application_state| to true.
   crypto_stream_ = std::make_unique<QuicCryptoClientStream>(
diff --git a/quiche/quic/tools/quic_client_base.cc b/quiche/quic/tools/quic_client_base.cc
index de77089..02b56a4 100644
--- a/quiche/quic/tools/quic_client_base.cc
+++ b/quiche/quic/tools/quic_client_base.cc
@@ -18,15 +18,20 @@
 
 namespace quic {
 
+namespace {
+
 // Implements the basic feature of a result delegate for path validation for
 // connection migration. If the validation succeeds, migrate to the alternative
 // path. Otherwise, stay on the current path.
 class QuicClientSocketMigrationValidationResultDelegate
     : public QuicPathValidator::ResultDelegate {
  public:
-  QuicClientSocketMigrationValidationResultDelegate(QuicClientBase* client)
+  explicit QuicClientSocketMigrationValidationResultDelegate(
+      QuicClientBase* client)
       : QuicPathValidator::ResultDelegate(), client_(client) {}
 
+  virtual ~QuicClientSocketMigrationValidationResultDelegate() = default;
+
   // QuicPathValidator::ResultDelegate
   // Overridden to start migration and takes the ownership of the writer in the
   // context.
@@ -53,10 +58,33 @@
         /*is_multi_port=*/false, *context);
   }
 
+ protected:
+  QuicClientBase* client() { return client_; }
+
  private:
   QuicClientBase* client_;
 };
 
+class ServerPreferredAddressResultDelegateWithWriter
+    : public QuicClientSocketMigrationValidationResultDelegate {
+ public:
+  ServerPreferredAddressResultDelegateWithWriter(QuicClientBase* client)
+      : QuicClientSocketMigrationValidationResultDelegate(client) {}
+
+  // Overridden to transfer the ownership of the new writer.
+  void OnPathValidationSuccess(
+      std::unique_ptr<QuicPathValidationContext> context,
+      QuicTime /*start_time*/) override {
+    client()->session()->connection()->OnServerPreferredAddressValidated(
+        *context, false);
+    auto migration_context = std::unique_ptr<PathMigrationContext>(
+        static_cast<PathMigrationContext*>(context.release()));
+    client()->set_writer(migration_context->ReleaseWriter());
+  }
+};
+
+}  // namespace
+
 QuicClientBase::NetworkHelper::~NetworkHelper() = default;
 
 QuicClientBase::QuicClientBase(
@@ -494,4 +522,23 @@
       std::move(result_delegate));
 }
 
+void QuicClientBase::OnServerPreferredAddressAvailable(
+    const QuicSocketAddress& server_preferred_address) {
+  const auto self_address = session_->self_address();
+  if (network_helper_ == nullptr ||
+      !network_helper_->CreateUDPSocketAndBind(server_preferred_address,
+                                               self_address.host(), 0)) {
+    return;
+  }
+  QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
+  if (writer == nullptr) {
+    return;
+  }
+  session()->ValidatePath(
+      std::make_unique<PathMigrationContext>(
+          std::unique_ptr<QuicPacketWriter>(writer),
+          network_helper_->GetLatestClientAddress(), server_preferred_address),
+      std::make_unique<ServerPreferredAddressResultDelegateWithWriter>(this));
+}
+
 }  // namespace quic
diff --git a/quiche/quic/tools/quic_client_base.h b/quiche/quic/tools/quic_client_base.h
index ac29482..c3ec302 100644
--- a/quiche/quic/tools/quic_client_base.h
+++ b/quiche/quic/tools/quic_client_base.h
@@ -52,7 +52,7 @@
 // Subclasses derived from this class are responsible for creating the
 // actual QuicSession instance, as well as defining functions that
 // create and run the underlying network transport.
-class QuicClientBase {
+class QuicClientBase : public QuicSession::Visitor {
  public:
   // An interface to various network events that the QuicClient will need to
   // interact with.
@@ -93,6 +93,26 @@
 
   virtual ~QuicClientBase();
 
+  // Implmenets QuicSession::Visitor
+  void OnConnectionClosed(QuicConnectionId /*server_connection_id*/,
+                          QuicErrorCode /*error*/,
+                          const std::string& /*error_details*/,
+                          ConnectionCloseSource /*source*/) override {}
+
+  void OnWriteBlocked(QuicBlockedWriterInterface* /*blocked_writer*/) override {
+  }
+  void OnRstStreamReceived(const QuicRstStreamFrame& /*frame*/) override {}
+  void OnStopSendingReceived(const QuicStopSendingFrame& /*frame*/) override {}
+  bool TryAddNewConnectionId(
+      const QuicConnectionId& /*server_connection_id*/,
+      const QuicConnectionId& /*new_connection_id*/) override {
+    return false;
+  }
+  void OnConnectionIdRetired(
+      const QuicConnectionId& /*server_connection_id*/) override {}
+  void OnServerPreferredAddressAvailable(
+      const QuicSocketAddress& server_preferred_address) override;
+
   // Initializes the client to create a connection. Should be called exactly
   // once before calling StartConnect or Connect. Returns true if the
   // initialization succeeds, false otherwise.
diff --git a/quiche/quic/tools/quic_default_client.cc b/quiche/quic/tools/quic_default_client.cc
index 0f43aa5..6a15fa9 100644
--- a/quiche/quic/tools/quic_default_client.cc
+++ b/quiche/quic/tools/quic_default_client.cc
@@ -86,8 +86,8 @@
     const ParsedQuicVersionVector& supported_versions,
     QuicConnection* connection) {
   return std::make_unique<QuicSimpleClientSession>(
-      *config(), supported_versions, connection, network_helper(), server_id(),
-      crypto_config(), push_promise_index(), drop_response_body(),
+      *config(), supported_versions, connection, this, network_helper(),
+      server_id(), crypto_config(), push_promise_index(), drop_response_body(),
       enable_web_transport());
 }
 
diff --git a/quiche/quic/tools/quic_simple_client_session.cc b/quiche/quic/tools/quic_simple_client_session.cc
index d6085d2..26e368b 100644
--- a/quiche/quic/tools/quic_simple_client_session.cc
+++ b/quiche/quic/tools/quic_simple_client_session.cc
@@ -10,43 +10,26 @@
 
 namespace quic {
 
-namespace {
-
-class ServerPreferredAddressResultDelegateWithWriter
-    : public QuicPathValidator::ResultDelegate {
- public:
-  explicit ServerPreferredAddressResultDelegateWithWriter(
-      QuicConnection* connection)
-      : connection_(connection) {}
-
-  // Overridden to transfer the ownership of the new writer.
-  void OnPathValidationSuccess(
-      std::unique_ptr<QuicPathValidationContext> /*context*/,
-      QuicTime /*start_time*/) override {
-    // TODO(danzh) hand over the ownership of the released writer to client and
-    // call the connection to migrate.
-  }
-
-  void OnPathValidationFailure(
-      std::unique_ptr<QuicPathValidationContext> context) override {
-    connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/false,
-                                                 *context);
-  }
-
- private:
-  QuicConnection* connection_ = nullptr;
-};
-
-}  // namespace
-
 QuicSimpleClientSession::QuicSimpleClientSession(
     const QuicConfig& config, const ParsedQuicVersionVector& supported_versions,
     QuicConnection* connection, QuicClientBase::NetworkHelper* network_helper,
     const QuicServerId& server_id, QuicCryptoClientConfig* crypto_config,
     QuicClientPushPromiseIndex* push_promise_index, bool drop_response_body,
     bool enable_web_transport)
-    : QuicSpdyClientSession(config, supported_versions, connection, server_id,
-                            crypto_config, push_promise_index),
+    : QuicSimpleClientSession(config, supported_versions, connection,
+                              /*visitor=*/nullptr, network_helper, server_id,
+                              crypto_config, push_promise_index,
+                              drop_response_body, enable_web_transport) {}
+
+QuicSimpleClientSession::QuicSimpleClientSession(
+    const QuicConfig& config, const ParsedQuicVersionVector& supported_versions,
+    QuicConnection* connection, QuicSession::Visitor* visitor,
+    QuicClientBase::NetworkHelper* network_helper,
+    const QuicServerId& server_id, QuicCryptoClientConfig* crypto_config,
+    QuicClientPushPromiseIndex* push_promise_index, bool drop_response_body,
+    bool enable_web_transport)
+    : QuicSpdyClientSession(config, supported_versions, connection, visitor,
+                            server_id, crypto_config, push_promise_index),
       network_helper_(network_helper),
       drop_response_body_(drop_response_body),
       enable_web_transport_(enable_web_transport) {}
@@ -87,24 +70,4 @@
       network_helper_->GetLatestClientAddress(), peer_address());
 }
 
-void QuicSimpleClientSession::OnServerPreferredAddressAvailable(
-    const QuicSocketAddress& server_preferred_address) {
-  const auto self_address = connection()->self_address();
-  if (network_helper_ == nullptr ||
-      !network_helper_->CreateUDPSocketAndBind(server_preferred_address,
-                                               self_address.host(), 0)) {
-    return;
-  }
-  QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter();
-  if (writer == nullptr) {
-    return;
-  }
-  connection()->ValidatePath(
-      std::make_unique<PathMigrationContext>(
-          std::unique_ptr<QuicPacketWriter>(writer),
-          network_helper_->GetLatestClientAddress(), server_preferred_address),
-      std::make_unique<ServerPreferredAddressResultDelegateWithWriter>(
-          connection()));
-}
-
 }  // namespace quic
diff --git a/quiche/quic/tools/quic_simple_client_session.h b/quiche/quic/tools/quic_simple_client_session.h
index 4e06cc2..1beb062 100644
--- a/quiche/quic/tools/quic_simple_client_session.h
+++ b/quiche/quic/tools/quic_simple_client_session.h
@@ -22,13 +22,21 @@
                           QuicClientPushPromiseIndex* push_promise_index,
                           bool drop_response_body, bool enable_web_transport);
 
+  QuicSimpleClientSession(const QuicConfig& config,
+                          const ParsedQuicVersionVector& supported_versions,
+                          QuicConnection* connection,
+                          QuicSession::Visitor* visitor,
+                          QuicClientBase::NetworkHelper* network_helper,
+                          const QuicServerId& server_id,
+                          QuicCryptoClientConfig* crypto_config,
+                          QuicClientPushPromiseIndex* push_promise_index,
+                          bool drop_response_body, bool enable_web_transport);
+
   std::unique_ptr<QuicSpdyClientStream> CreateClientStream() override;
   bool ShouldNegotiateWebTransport() override;
   HttpDatagramSupport LocalHttpDatagramSupport() override;
   std::unique_ptr<QuicPathValidationContext> CreateContextForMultiPortPath()
       override;
-  void OnServerPreferredAddressAvailable(
-      const QuicSocketAddress& server_preferred_address) override;
   bool drop_response_body() const { return drop_response_body_; }
 
  private: