Add a new OnParsedClientHelloReceived method to QuicConnection and QuicConnectionDebugVisitor.

This method will be called after the QuicSession is created and the ClientHello is parsed by the server. The QuicConnection will simply forward the parsed CHLO to the debug visitor. The base debug visitor implementation will do nothing.

I plan to use this in BDN to log the cert compression algorithms, and potentially other fields of the parsed CHLO.

PiperOrigin-RevId: 681963634
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 8ab8459..9c59090 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -1095,6 +1095,13 @@
   }
 }
 
+void QuicConnection::OnParsedClientHelloInfo(
+    const ParsedClientHello& client_hello) {
+  if (debug_visitor_ != nullptr) {
+    debug_visitor_->OnParsedClientHelloInfo(client_hello);
+  }
+}
+
 bool QuicConnection::HasPendingAcks() const { return ack_alarm().IsSet(); }
 
 void QuicConnection::OnUserAgentIdKnown(const std::string& /*user_agent_id*/) {
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index c76527b..0893193 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -472,6 +472,10 @@
   virtual void OnEncryptedClientHelloReceived(
       absl::string_view /*client_hello*/) {}
 
+  // Called after the QuicSession is created.
+  virtual void OnParsedClientHelloInfo(
+      const ParsedClientHello& /*client_hello*/) {}
+
   // Called by QuicConnection::SetMultiPacketClientHello.
   virtual void SetMultiPacketClientHello() {}
 };
@@ -1232,6 +1236,9 @@
   // Called after an ClientHelloInner is received and decrypted as a server.
   void OnEncryptedClientHelloReceived(absl::string_view client_hello) const;
 
+  // Called after the QuicSession is created.
+  virtual void OnParsedClientHelloInfo(const ParsedClientHello& client_hello);
+
   // Returns true if ack_alarm_ is set.
   bool HasPendingAcks() const;
 
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index a79d5ac..1c123ce 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -17594,6 +17594,19 @@
   connection_.OnResetStreamAtFrame(QuicResetStreamAtFrame(0, 0, 0, 20, 10));
 }
 
+TEST_P(QuicConnectionTest, OnParsedClientHelloInfoWithDebugVisitor) {
+  const ParsedClientHello parsed_chlo{.sni = "sni",
+                                      .uaid = "uiad",
+                                      .supported_groups = {1, 2, 3},
+                                      .cert_compression_algos = {4, 5, 6},
+                                      .alpns = {"h2", "http/1.1"},
+                                      .retry_token = "retry_token"};
+  MockQuicConnectionDebugVisitor debug_visitor;
+  connection_.set_debug_visitor(&debug_visitor);
+  EXPECT_CALL(debug_visitor, OnParsedClientHelloInfo(parsed_chlo)).Times(1);
+  connection_.OnParsedClientHelloInfo(parsed_chlo);
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quiche/quic/core/quic_dispatcher.cc b/quiche/quic/core/quic_dispatcher.cc
index 48c006c..877bb31 100644
--- a/quiche/quic/core/quic_dispatcher.cc
+++ b/quiche/quic/core/quic_dispatcher.cc
@@ -1417,6 +1417,9 @@
     session->connection()->SetOriginalDestinationConnectionId(
         original_connection_id);
   }
+
+  session->connection()->OnParsedClientHelloInfo(parsed_chlo);
+
   QUIC_DLOG(INFO) << "Created new session for " << *server_connection_id;
 
   auto insertion_result = reference_counted_session_map_.insert(std::make_pair(
diff --git a/quiche/quic/core/quic_dispatcher_test.cc b/quiche/quic/core/quic_dispatcher_test.cc
index 6a46ce3..c2ed31e 100644
--- a/quiche/quic/core/quic_dispatcher_test.cc
+++ b/quiche/quic/core/quic_dispatcher_test.cc
@@ -659,6 +659,9 @@
       .WillOnce(WithArg<2>(Invoke([this](const QuicEncryptedPacket& packet) {
         ValidatePacket(TestConnectionId(1), packet);
       })));
+  EXPECT_CALL(*reinterpret_cast<MockQuicConnection*>(session1_->connection()),
+              OnParsedClientHelloInfo(MatchParsedClientHello()))
+      .Times(1);
 
   ProcessFirstFlight(client_address, TestConnectionId(1));
 }
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h
index 0f26a3e..9e88108 100644
--- a/quiche/quic/test_tools/quic_test_utils.h
+++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -673,6 +673,7 @@
                const QuicSocketAddress&, const QuicSocketAddress&,
                QuicPacketWriter*),
               (override));
+  MOCK_METHOD(void, OnParsedClientHelloInfo, (const ParsedClientHello&), ());
 
   MOCK_METHOD(void, OnError, (QuicFramer*), (override));
   void QuicConnection_OnError(QuicFramer* framer) {
@@ -1393,6 +1394,7 @@
 
   MOCK_METHOD(void, OnZeroRttRejected, (int), (override));
   MOCK_METHOD(void, OnZeroRttPacketAcked, (), (override));
+  MOCK_METHOD(void, OnParsedClientHelloInfo, (const ParsedClientHello&), ());
 };
 
 class MockReceivedPacketManager : public QuicReceivedPacketManager {