Pass ParsedClientHello to QuicDispatcher::CreateQuicSession. This makes it easier to pass CHLO information into CreateQuicSession, which is responsible for creating a QUIC session.

The information I'd like to add into ParsedClientHello, in a follow up CL, are
- Whether TLS resumption is attempted
- Whether Early Data is attempted
- The address token from the last packet of a CHLO, which will contain CachedNetworkParameters after cl/405481550.

PiperOrigin-RevId: 406887508
diff --git a/quic/core/quic_buffered_packet_store.cc b/quic/core/quic_buffered_packet_store.cc
index f235e45..291434f 100644
--- a/quic/core/quic_buffered_packet_store.cc
+++ b/quic/core/quic_buffered_packet_store.cc
@@ -83,22 +83,16 @@
 }
 
 EnqueuePacketResult QuicBufferedPacketStore::EnqueuePacket(
-    QuicConnectionId connection_id,
-    bool ietf_quic,
-    const QuicReceivedPacket& packet,
-    QuicSocketAddress self_address,
-    QuicSocketAddress peer_address,
-    bool is_chlo,
-    const std::vector<std::string>& alpns,
-    const absl::string_view sni,
-    const ParsedQuicVersion& version) {
+    QuicConnectionId connection_id, bool ietf_quic,
+    const QuicReceivedPacket& packet, QuicSocketAddress self_address,
+    QuicSocketAddress peer_address, const ParsedQuicVersion& version,
+    absl::optional<ParsedClientHello> parsed_chlo) {
+  const bool is_chlo = parsed_chlo.has_value();
   QUIC_BUG_IF(quic_bug_12410_1, !GetQuicFlag(FLAGS_quic_allow_chlo_buffering))
       << "Shouldn't buffer packets if disabled via flag.";
   QUIC_BUG_IF(quic_bug_12410_2,
               is_chlo && connections_with_chlo_.contains(connection_id))
       << "Shouldn't buffer duplicated CHLO on connection " << connection_id;
-  QUIC_BUG_IF(quic_bug_12410_3, !is_chlo && !alpns.empty())
-      << "Shouldn't have an ALPN defined for a non-CHLO packet.";
   QUIC_BUG_IF(quic_bug_12410_4, is_chlo && !version.IsKnown())
       << "Should have version for CHLO packet.";
 
@@ -143,8 +137,7 @@
     // Add CHLO to the beginning of buffered packets so that it can be delivered
     // first later.
     queue.buffered_packets.push_front(std::move(new_entry));
-    queue.alpns = alpns;
-    queue.sni = std::string(sni);
+    queue.parsed_chlo = std::move(parsed_chlo);
     connections_with_chlo_[connection_id] = false;  // Dummy value.
     // Set the version of buffered packets of this connection on CHLO.
     queue.version = version;
@@ -248,8 +241,11 @@
   connections_with_chlo_.pop_front();
 
   BufferedPacketList packets = DeliverPackets(*connection_id);
-  QUICHE_DCHECK(!packets.buffered_packets.empty())
-      << "Try to deliver connectons without CHLO";
+  QUICHE_DCHECK(!packets.buffered_packets.empty() &&
+                packets.parsed_chlo.has_value())
+      << "Try to deliver connectons without CHLO. # packets:"
+      << packets.buffered_packets.size()
+      << ", has_parsed_chlo:" << packets.parsed_chlo.has_value();
   return packets;
 }
 
diff --git a/quic/core/quic_buffered_packet_store.h b/quic/core/quic_buffered_packet_store.h
index 5ba0a25..b363802 100644
--- a/quic/core/quic_buffered_packet_store.h
+++ b/quic/core/quic_buffered_packet_store.h
@@ -67,9 +67,8 @@
 
     std::list<BufferedPacket> buffered_packets;
     QuicTime creation_time;
-    // The ALPNs from the CHLO, if found.
-    std::vector<std::string> alpns;
-    std::string sni;
+    // |parsed_chlo| is set iff the entire CHLO has been received.
+    absl::optional<ParsedClientHello> parsed_chlo;
     // Indicating whether this is an IETF QUIC connection.
     bool ietf_quic;
     // If buffered_packets contains the CHLO, it is the version of the CHLO.
@@ -101,18 +100,14 @@
 
   QuicBufferedPacketStore& operator=(const QuicBufferedPacketStore&) = delete;
 
-  // Adds a copy of packet into packet queue for given connection.
-  // TODO(danzh): Consider to split this method to EnqueueChlo() and
-  // EnqueueDataPacket().
-  EnqueuePacketResult EnqueuePacket(QuicConnectionId connection_id,
-                                    bool ietf_quic,
-                                    const QuicReceivedPacket& packet,
-                                    QuicSocketAddress self_address,
-                                    QuicSocketAddress peer_address,
-                                    bool is_chlo,
-                                    const std::vector<std::string>& alpns,
-                                    const absl::string_view sni,
-                                    const ParsedQuicVersion& version);
+  // Adds a copy of packet into the packet queue for given connection. If the
+  // packet is the last one of the CHLO, |parsed_chlo| will contain a parsed
+  // version of the CHLO.
+  EnqueuePacketResult EnqueuePacket(
+      QuicConnectionId connection_id, bool ietf_quic,
+      const QuicReceivedPacket& packet, QuicSocketAddress self_address,
+      QuicSocketAddress peer_address, const ParsedQuicVersion& version,
+      absl::optional<ParsedClientHello> parsed_chlo);
 
   // Returns true if there are any packets buffered for |connection_id|.
   bool HasBufferedPackets(QuicConnectionId connection_id) const;
diff --git a/quic/core/quic_buffered_packet_store_test.cc b/quic/core/quic_buffered_packet_store_test.cc
index c5d11f2..7e51751 100644
--- a/quic/core/quic_buffered_packet_store_test.cc
+++ b/quic/core/quic_buffered_packet_store_test.cc
@@ -22,6 +22,10 @@
 namespace test {
 namespace {
 
+const absl::optional<ParsedClientHello> kNoParsedChlo;
+const absl::optional<ParsedClientHello> kDefaultParsedChlo =
+    absl::make_optional<ParsedClientHello>();
+
 using BufferedPacket = QuicBufferedPacketStore::BufferedPacket;
 using BufferedPacketList = QuicBufferedPacketStore::BufferedPacketList;
 using EnqueuePacketResult = QuicBufferedPacketStore::EnqueuePacketResult;
@@ -70,13 +74,12 @@
 TEST_F(QuicBufferedPacketStoreTest, SimpleEnqueueAndDeliverPacket) {
   QuicConnectionId connection_id = TestConnectionId(1);
   store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                       peer_address_, false, {}, "", invalid_version_);
+                       peer_address_, invalid_version_, kNoParsedChlo);
   EXPECT_TRUE(store_.HasBufferedPackets(connection_id));
   auto packets = store_.DeliverPackets(connection_id);
   const std::list<BufferedPacket>& queue = packets.buffered_packets;
   ASSERT_EQ(1u, queue.size());
-  // The alpn should be ignored for non-chlo packets.
-  ASSERT_TRUE(packets.alpns.empty());
+  ASSERT_FALSE(packets.parsed_chlo.has_value());
   // There is no valid version because CHLO has not arrived.
   EXPECT_EQ(invalid_version_, packets.version);
   // Check content of the only packet in the queue.
@@ -93,9 +96,9 @@
   QuicSocketAddress addr_with_new_port(QuicIpAddress::Any4(), 256);
   QuicConnectionId connection_id = TestConnectionId(1);
   store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                       peer_address_, false, {}, "", invalid_version_);
+                       peer_address_, invalid_version_, kNoParsedChlo);
   store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                       addr_with_new_port, false, {}, "", invalid_version_);
+                       addr_with_new_port, invalid_version_, kNoParsedChlo);
   std::list<BufferedPacket> queue =
       store_.DeliverPackets(connection_id).buffered_packets;
   ASSERT_EQ(2u, queue.size());
@@ -110,9 +113,9 @@
   for (uint64_t conn_id = 1; conn_id <= num_connections; ++conn_id) {
     QuicConnectionId connection_id = TestConnectionId(conn_id);
     store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                         peer_address_, false, {}, "", invalid_version_);
+                         peer_address_, invalid_version_, kNoParsedChlo);
     store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                         peer_address_, false, {}, "", invalid_version_);
+                         peer_address_, invalid_version_, kNoParsedChlo);
   }
 
   // Deliver packets in reversed order.
@@ -132,14 +135,15 @@
   QuicConnectionId connection_id = TestConnectionId(1);
   // Arrived CHLO packet shouldn't affect how many non-CHLO pacekts store can
   // keep.
-  EXPECT_EQ(QuicBufferedPacketStore::SUCCESS,
-            store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                                 peer_address_, true, {}, "", valid_version_));
+  EXPECT_EQ(
+      QuicBufferedPacketStore::SUCCESS,
+      store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+                           peer_address_, valid_version_, kDefaultParsedChlo));
   for (size_t i = 1; i <= num_packets; ++i) {
     // Only first |kDefaultMaxUndecryptablePackets packets| will be buffered.
     EnqueuePacketResult result =
         store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                             peer_address_, false, {}, "", invalid_version_);
+                             peer_address_, invalid_version_, kNoParsedChlo);
     if (i <= kDefaultMaxUndecryptablePackets) {
       EXPECT_EQ(EnqueuePacketResult::SUCCESS, result);
     } else {
@@ -161,7 +165,7 @@
     QuicConnectionId connection_id = TestConnectionId(conn_id);
     EnqueuePacketResult result =
         store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                             peer_address_, false, {}, "", invalid_version_);
+                             peer_address_, invalid_version_, kNoParsedChlo);
     if (conn_id <= kMaxConnectionsWithoutCHLO) {
       EXPECT_EQ(EnqueuePacketResult::SUCCESS, result);
     } else {
@@ -190,8 +194,8 @@
   for (uint64_t conn_id = 1; conn_id <= num_chlos; ++conn_id) {
     EXPECT_EQ(EnqueuePacketResult::SUCCESS,
               store_.EnqueuePacket(TestConnectionId(conn_id), false, packet_,
-                                   self_address_, peer_address_, true, {}, "",
-                                   valid_version_));
+                                   self_address_, peer_address_, valid_version_,
+                                   kDefaultParsedChlo));
   }
 
   // Send data packets on another |kMaxConnectionsWithoutCHLO| connections.
@@ -201,7 +205,7 @@
     QuicConnectionId connection_id = TestConnectionId(conn_id);
     EnqueuePacketResult result =
         store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                             peer_address_, true, {}, "", valid_version_);
+                             peer_address_, valid_version_, kDefaultParsedChlo);
     if (conn_id <= kDefaultMaxConnectionsInStore) {
       EXPECT_EQ(EnqueuePacketResult::SUCCESS, result);
     } else {
@@ -217,7 +221,7 @@
     EXPECT_EQ(
         EnqueuePacketResult::SUCCESS,
         store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                             peer_address_, false, {}, "", invalid_version_));
+                             peer_address_, invalid_version_, kNoParsedChlo));
   }
 
   // Buffer CHLOs on other connections till store is full.
@@ -226,7 +230,7 @@
     QuicConnectionId connection_id = TestConnectionId(i);
     EnqueuePacketResult rs =
         store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                             peer_address_, true, {}, "", valid_version_);
+                             peer_address_, valid_version_, kDefaultParsedChlo);
     if (i <= kDefaultMaxConnectionsInStore) {
       EXPECT_EQ(EnqueuePacketResult::SUCCESS, rs);
       EXPECT_TRUE(store_.HasChloForConnection(connection_id));
@@ -240,10 +244,11 @@
   // But buffering a CHLO belonging to a connection already has data packet
   // buffered in the store should success. This is the connection should be
   // delivered at last.
-  EXPECT_EQ(EnqueuePacketResult::SUCCESS,
-            store_.EnqueuePacket(
-                /*connection_id=*/TestConnectionId(1), false, packet_,
-                self_address_, peer_address_, true, {}, "", valid_version_));
+  EXPECT_EQ(
+      EnqueuePacketResult::SUCCESS,
+      store_.EnqueuePacket(
+          /*connection_id=*/TestConnectionId(1), false, packet_, self_address_,
+          peer_address_, valid_version_, kDefaultParsedChlo));
   EXPECT_TRUE(store_.HasChloForConnection(
       /*connection_id=*/TestConnectionId(1)));
 
@@ -271,15 +276,16 @@
 TEST_F(QuicBufferedPacketStoreTest, PacketQueueExpiredBeforeDelivery) {
   QuicConnectionId connection_id = TestConnectionId(1);
   store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                       peer_address_, false, {}, "", invalid_version_);
-  EXPECT_EQ(EnqueuePacketResult::SUCCESS,
-            store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                                 peer_address_, true, {}, "", valid_version_));
+                       peer_address_, invalid_version_, kNoParsedChlo);
+  EXPECT_EQ(
+      EnqueuePacketResult::SUCCESS,
+      store_.EnqueuePacket(connection_id, false, packet_, self_address_,
+                           peer_address_, valid_version_, kDefaultParsedChlo));
   QuicConnectionId connection_id2 = TestConnectionId(2);
   EXPECT_EQ(
       EnqueuePacketResult::SUCCESS,
       store_.EnqueuePacket(connection_id2, false, packet_, self_address_,
-                           peer_address_, false, {}, "", invalid_version_));
+                           peer_address_, invalid_version_, kNoParsedChlo));
 
   // CHLO on connection 3 arrives 1ms later.
   clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(1));
@@ -288,7 +294,8 @@
   // connections.
   QuicSocketAddress another_client_address(QuicIpAddress::Any4(), 255);
   store_.EnqueuePacket(connection_id3, false, packet_, self_address_,
-                       another_client_address, true, {}, "", valid_version_);
+                       another_client_address, valid_version_,
+                       kDefaultParsedChlo);
 
   // Advance clock to the time when connection 1 and 2 expires.
   clock_.AdvanceTime(
@@ -320,9 +327,9 @@
   // for them to expire.
   QuicConnectionId connection_id4 = TestConnectionId(4);
   store_.EnqueuePacket(connection_id4, false, packet_, self_address_,
-                       peer_address_, false, {}, "", invalid_version_);
+                       peer_address_, invalid_version_, kNoParsedChlo);
   store_.EnqueuePacket(connection_id4, false, packet_, self_address_,
-                       peer_address_, false, {}, "", invalid_version_);
+                       peer_address_, invalid_version_, kNoParsedChlo);
   clock_.AdvanceTime(
       QuicBufferedPacketStorePeer::expiration_alarm(&store_)->deadline() -
       clock_.ApproximateNow());
@@ -337,9 +344,9 @@
 
   // Enqueue some packets
   store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                       peer_address_, false, {}, "", invalid_version_);
+                       peer_address_, invalid_version_, kNoParsedChlo);
   store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                       peer_address_, false, {}, "", invalid_version_);
+                       peer_address_, invalid_version_, kNoParsedChlo);
   EXPECT_TRUE(store_.HasBufferedPackets(connection_id));
   EXPECT_FALSE(store_.HasChlosBuffered());
 
@@ -363,11 +370,11 @@
 
   // Enqueue some packets, which include a CHLO
   store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                       peer_address_, false, {}, "", invalid_version_);
+                       peer_address_, invalid_version_, kNoParsedChlo);
   store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                       peer_address_, true, {}, "", valid_version_);
+                       peer_address_, valid_version_, kDefaultParsedChlo);
   store_.EnqueuePacket(connection_id, false, packet_, self_address_,
-                       peer_address_, false, {}, "", invalid_version_);
+                       peer_address_, invalid_version_, kNoParsedChlo);
   EXPECT_TRUE(store_.HasBufferedPackets(connection_id));
   EXPECT_TRUE(store_.HasChlosBuffered());
 
@@ -392,12 +399,15 @@
 
   // Enqueue some packets for two connection IDs
   store_.EnqueuePacket(connection_id_1, false, packet_, self_address_,
-                       peer_address_, false, {}, "", invalid_version_);
+                       peer_address_, invalid_version_, kNoParsedChlo);
   store_.EnqueuePacket(connection_id_1, false, packet_, self_address_,
-                       peer_address_, false, {}, "", invalid_version_);
+                       peer_address_, invalid_version_, kNoParsedChlo);
+
+  ParsedClientHello parsed_chlo;
+  parsed_chlo.alpns.push_back("h3");
+  parsed_chlo.sni = TestHostname();
   store_.EnqueuePacket(connection_id_2, false, packet_, self_address_,
-                       peer_address_, true, {"h3"}, TestHostname(),
-                       valid_version_);
+                       peer_address_, valid_version_, parsed_chlo);
   EXPECT_TRUE(store_.HasBufferedPackets(connection_id_1));
   EXPECT_TRUE(store_.HasBufferedPackets(connection_id_2));
   EXPECT_TRUE(store_.HasChlosBuffered());
@@ -414,9 +424,9 @@
   EXPECT_TRUE(store_.HasBufferedPackets(connection_id_2));
   auto packets = store_.DeliverPackets(connection_id_2);
   EXPECT_EQ(1u, packets.buffered_packets.size());
-  ASSERT_EQ(1u, packets.alpns.size());
-  EXPECT_EQ("h3", packets.alpns[0]);
-  EXPECT_EQ(TestHostname(), packets.sni);
+  ASSERT_EQ(1u, packets.parsed_chlo->alpns.size());
+  EXPECT_EQ("h3", packets.parsed_chlo->alpns[0]);
+  EXPECT_EQ(TestHostname(), packets.parsed_chlo->sni);
   // Since connection_id_2's chlo arrives, verify version is set.
   EXPECT_EQ(valid_version_, packets.version);
   EXPECT_TRUE(store_.HasChlosBuffered());
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index 2d3a260..1484b74 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -737,7 +737,7 @@
         return;
       }
 
-      ProcessChlo(*parsed_chlo, packet_info);
+      ProcessChlo(*std::move(parsed_chlo), packet_info);
       return;
     }
   }
@@ -773,7 +773,7 @@
   }
 }
 
-absl::optional<QuicDispatcher::ParsedClientHello>
+absl::optional<ParsedClientHello>
 QuicDispatcher::TryExtractChloOrBufferEarlyPacket(
     const ReceivedPacketInfo& packet_info) {
   if (packet_info.version.UsesTls()) {
@@ -1234,14 +1234,20 @@
     if (packets.empty()) {
       return;
     }
+    if (!packet_list.parsed_chlo.has_value()) {
+      QUIC_BUG(quic_dispatcher_no_parsed_chlo_in_buffered_packets)
+          << "Buffered connection has no CHLO. connection_id:"
+          << server_connection_id;
+      continue;
+    }
+    const ParsedClientHello& parsed_chlo = *packet_list.parsed_chlo;
     QuicConnectionId original_connection_id = server_connection_id;
     server_connection_id = MaybeReplaceServerConnectionId(server_connection_id,
                                                           packet_list.version);
-    std::string alpn = SelectAlpn(packet_list.alpns);
-    std::unique_ptr<QuicSession> session =
-        CreateQuicSession(server_connection_id, packets.front().self_address,
-                          packets.front().peer_address, alpn,
-                          packet_list.version, packet_list.sni);
+    std::string alpn = SelectAlpn(parsed_chlo.alpns);
+    std::unique_ptr<QuicSession> session = CreateQuicSession(
+        server_connection_id, packets.front().self_address,
+        packets.front().peer_address, alpn, packet_list.version, parsed_chlo);
     if (original_connection_id != server_connection_id) {
       session->connection()->SetOriginalDestinationConnectionId(
           original_connection_id);
@@ -1302,14 +1308,14 @@
   EnqueuePacketResult rs = buffered_packets_.EnqueuePacket(
       packet_info.destination_connection_id,
       packet_info.form != GOOGLE_QUIC_PACKET, packet_info.packet,
-      packet_info.self_address, packet_info.peer_address, /*is_chlo=*/false,
-      /*alpns=*/{}, /*sni=*/absl::string_view(), packet_info.version);
+      packet_info.self_address, packet_info.peer_address, packet_info.version,
+      /*parsed_chlo=*/absl::nullopt);
   if (rs != EnqueuePacketResult::SUCCESS) {
     OnBufferPacketFailure(rs, packet_info.destination_connection_id);
   }
 }
 
-void QuicDispatcher::ProcessChlo(const ParsedClientHello& parsed_chlo,
+void QuicDispatcher::ProcessChlo(ParsedClientHello parsed_chlo,
                                  ReceivedPacketInfo* packet_info) {
   if (!buffered_packets_.HasBufferedPackets(
           packet_info->destination_connection_id) &&
@@ -1325,8 +1331,7 @@
         packet_info->destination_connection_id,
         packet_info->form != GOOGLE_QUIC_PACKET, packet_info->packet,
         packet_info->self_address, packet_info->peer_address,
-        /*is_chlo=*/true, parsed_chlo.alpns, parsed_chlo.sni,
-        packet_info->version);
+        packet_info->version, std::move(parsed_chlo));
     if (rs != EnqueuePacketResult::SUCCESS) {
       OnBufferPacketFailure(rs, packet_info->destination_connection_id);
     }
@@ -1341,7 +1346,7 @@
   std::string alpn = SelectAlpn(parsed_chlo.alpns);
   std::unique_ptr<QuicSession> session = CreateQuicSession(
       packet_info->destination_connection_id, packet_info->self_address,
-      packet_info->peer_address, alpn, packet_info->version, parsed_chlo.sni);
+      packet_info->peer_address, alpn, packet_info->version, parsed_chlo);
   if (QUIC_PREDICT_FALSE(session == nullptr)) {
     QUIC_BUG(quic_bug_10287_8)
         << "CreateQuicSession returned nullptr for "
diff --git a/quic/core/quic_dispatcher.h b/quic/core/quic_dispatcher.h
index 1c4e53b..71e3914 100644
--- a/quic/core/quic_dispatcher.h
+++ b/quic/core/quic_dispatcher.h
@@ -169,22 +169,14 @@
   }
 
  protected:
-  // ParsedClientHello contains client hello information extracted from a fully
-  // received client hello.
-  struct QUIC_NO_EXPORT ParsedClientHello {
-    std::string sni;                 // QUIC crypto and TLS.
-    std::string uaid;                // QUIC crypto only.
-    std::vector<std::string> alpns;  // QUIC crypto and TLS.
-    std::string legacy_version_encapsulation_inner_packet;  // QUIC crypto only.
-  };
-
+  // Creates a QUIC session based on the given information.
+  // |alpn| is the selected ALPN from |parsed_chlo.alpns|.
   virtual std::unique_ptr<QuicSession> CreateQuicSession(
       QuicConnectionId server_connection_id,
       const QuicSocketAddress& self_address,
-      const QuicSocketAddress& peer_address,
-      absl::string_view alpn,
+      const QuicSocketAddress& peer_address, absl::string_view alpn,
       const ParsedQuicVersion& version,
-      absl::string_view sni) = 0;
+      const ParsedClientHello& parsed_chlo) = 0;
 
   // Tries to validate and dispatch packet based on available information.
   // Returns true if packet is dropped or successfully dispatched (e.g.,
@@ -255,7 +247,7 @@
   // Called when |packet_info| is the last received packet of the client hello.
   // |parsed_chlo| is the parsed version of the client hello. Creates a new
   // connection and delivers any buffered packets for that connection id.
-  void ProcessChlo(const ParsedClientHello& parsed_chlo,
+  void ProcessChlo(ParsedClientHello parsed_chlo,
                    ReceivedPacketInfo* packet_info);
 
   // Return true if dispatcher wants to destroy session outside of
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index 276f59c..1dfda6c 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -130,14 +130,12 @@
                        kQuicDefaultConnectionIdLength),
         random_(random) {}
 
-  MOCK_METHOD(std::unique_ptr<QuicSession>,
-              CreateQuicSession,
+  MOCK_METHOD(std::unique_ptr<QuicSession>, CreateQuicSession,
               (QuicConnectionId connection_id,
                const QuicSocketAddress& self_address,
-               const QuicSocketAddress& peer_address,
-               absl::string_view alpn,
-               const quic::ParsedQuicVersion& version,
-               absl::string_view sni),
+               const QuicSocketAddress& peer_address, absl::string_view alpn,
+               const ParsedQuicVersion& version,
+               const ParsedClientHello& parsed_chlo),
               (override));
 
   MOCK_METHOD(bool,
@@ -458,6 +456,13 @@
 
   std::string ExpectedAlpn() { return ExpectedAlpnForVersion(version_); }
 
+  ParsedClientHello ParsedClientHelloForTest() {
+    ParsedClientHello parsed_chlo;
+    parsed_chlo.alpns = {ExpectedAlpn()};
+    parsed_chlo.sni = TestHostname();
+    return parsed_chlo;
+  }
+
   void MarkSession1Deleted() { session1_ = nullptr; }
 
   void VerifyVersionSupported(ParsedQuicVersion version) {
@@ -588,9 +593,10 @@
       << "No session should be created before the rest of the CHLO arrives.";
 
   // Processing the second packet should create the new session.
-  EXPECT_CALL(*dispatcher_,
-              CreateQuicSession(server_connection_id, _, client_address,
-                                Eq(ExpectedAlpn()), _, TestHostname()))
+  EXPECT_CALL(
+      *dispatcher_,
+      CreateQuicSession(server_connection_id, _, client_address,
+                        Eq(ExpectedAlpn()), _, Eq(ParsedClientHelloForTest())))
       .WillOnce(Return(ByMove(CreateSession(
           dispatcher_.get(), config_, server_connection_id, client_address,
           &mock_helper_, &mock_alarm_factory_, &crypto_config_,
@@ -679,9 +685,10 @@
 TEST_P(QuicDispatcherTestAllVersions, ProcessPackets) {
   QuicSocketAddress client_address(QuicIpAddress::Loopback4(), 1);
 
-  EXPECT_CALL(*dispatcher_,
-              CreateQuicSession(TestConnectionId(1), _, client_address,
-                                Eq(ExpectedAlpn()), _, TestHostname()))
+  EXPECT_CALL(
+      *dispatcher_,
+      CreateQuicSession(TestConnectionId(1), _, client_address,
+                        Eq(ExpectedAlpn()), _, Eq(ParsedClientHelloForTest())))
       .WillOnce(Return(ByMove(CreateSession(
           dispatcher_.get(), config_, TestConnectionId(1), client_address,
           &mock_helper_, &mock_alarm_factory_, &crypto_config_,
@@ -696,9 +703,10 @@
                   ReceivedPacketInfoConnectionIdEquals(TestConnectionId(1))));
   ProcessFirstFlight(client_address, TestConnectionId(1));
 
-  EXPECT_CALL(*dispatcher_,
-              CreateQuicSession(TestConnectionId(2), _, client_address,
-                                Eq(ExpectedAlpn()), _, TestHostname()))
+  EXPECT_CALL(
+      *dispatcher_,
+      CreateQuicSession(TestConnectionId(2), _, client_address,
+                        Eq(ExpectedAlpn()), _, Eq(ParsedClientHelloForTest())))
       .WillOnce(Return(ByMove(CreateSession(
           dispatcher_.get(), config_, TestConnectionId(2), client_address,
           &mock_helper_, &mock_alarm_factory_, &crypto_config_,
@@ -2407,7 +2415,7 @@
   // buffered should be delivered to the session.
   EXPECT_CALL(*dispatcher_,
               CreateQuicSession(conn_id, _, client_addr_, Eq(ExpectedAlpn()), _,
-                                TestHostname()))
+                                Eq(ParsedClientHelloForTest())))
       .WillOnce(Return(ByMove(CreateSession(
           dispatcher_.get(), config_, conn_id, client_addr_, &mock_helper_,
           &mock_alarm_factory_, &crypto_config_,
@@ -2611,7 +2619,8 @@
     if (conn_id <= kMaxNumSessionsToCreate) {
       EXPECT_CALL(*dispatcher_,
                   CreateQuicSession(TestConnectionId(conn_id), _, client_addr_,
-                                    Eq(ExpectedAlpn()), _, TestHostname()))
+                                    Eq(ExpectedAlpn()), _,
+                                    Eq(ParsedClientHelloForTest())))
           .WillOnce(Return(ByMove(CreateSession(
               dispatcher_.get(), config_, TestConnectionId(conn_id),
               client_addr_, &mock_helper_, &mock_alarm_factory_,
@@ -2646,7 +2655,8 @@
        ++conn_id) {
     EXPECT_CALL(*dispatcher_,
                 CreateQuicSession(TestConnectionId(conn_id), _, client_addr_,
-                                  Eq(ExpectedAlpn()), _, TestHostname()))
+                                  Eq(ExpectedAlpn()), _,
+                                  Eq(ParsedClientHelloForTest())))
         .WillOnce(Return(ByMove(CreateSession(
             dispatcher_.get(), config_, TestConnectionId(conn_id), client_addr_,
             &mock_helper_, &mock_alarm_factory_, &crypto_config_,
diff --git a/quic/core/quic_types.cc b/quic/core/quic_types.cc
index f578bb4..8c155c4 100644
--- a/quic/core/quic_types.cc
+++ b/quic/core/quic_types.cc
@@ -8,6 +8,7 @@
 
 #include "absl/strings/str_cat.h"
 #include "quic/core/quic_error_codes.h"
+#include "common/print_elements.h"
 
 namespace quic {
 
@@ -382,6 +383,21 @@
   return os;
 }
 
+bool operator==(const ParsedClientHello& a, const ParsedClientHello& b) {
+  return a.sni == b.sni && a.uaid == b.uaid && a.alpns == b.alpns &&
+         a.legacy_version_encapsulation_inner_packet ==
+             b.legacy_version_encapsulation_inner_packet;
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const ParsedClientHello& parsed_chlo) {
+  os << "{ sni:" << parsed_chlo.sni << ", uaid:" << parsed_chlo.uaid
+     << ", alpns:" << quiche::PrintElements(parsed_chlo.alpns)
+     << ", len(inner_packet):"
+     << parsed_chlo.legacy_version_encapsulation_inner_packet.size() << " }";
+  return os;
+}
+
 #undef RETURN_STRING_LITERAL  // undef for jumbo builds
 
 }  // namespace quic
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h
index edfa78b..b0f32cd 100644
--- a/quic/core/quic_types.h
+++ b/quic/core/quic_types.h
@@ -848,6 +848,21 @@
   absl::optional<absl::InlinedVector<uint16_t, 8>> signing_algorithm_prefs;
 };
 
+// ParsedClientHello contains client hello information extracted from a fully
+// received client hello.
+struct QUIC_NO_EXPORT ParsedClientHello {
+  std::string sni;                 // QUIC crypto and TLS.
+  std::string uaid;                // QUIC crypto only.
+  std::vector<std::string> alpns;  // QUIC crypto and TLS.
+  std::string legacy_version_encapsulation_inner_packet;  // QUIC crypto only.
+};
+
+QUIC_EXPORT_PRIVATE bool operator==(const ParsedClientHello& a,
+                                    const ParsedClientHello& b);
+
+QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+    std::ostream& os, const ParsedClientHello& parsed_chlo);
+
 }  // namespace quic
 
 #endif  // QUICHE_QUIC_CORE_QUIC_TYPES_H_
diff --git a/quic/masque/masque_dispatcher.cc b/quic/masque/masque_dispatcher.cc
index 5729d54..c14e03d 100644
--- a/quic/masque/masque_dispatcher.cc
+++ b/quic/masque/masque_dispatcher.cc
@@ -31,12 +31,10 @@
       masque_server_backend_(masque_server_backend) {}
 
 std::unique_ptr<QuicSession> MasqueDispatcher::CreateQuicSession(
-    QuicConnectionId connection_id,
-    const QuicSocketAddress& self_address,
-    const QuicSocketAddress& peer_address,
-    absl::string_view /*alpn*/,
+    QuicConnectionId connection_id, const QuicSocketAddress& self_address,
+    const QuicSocketAddress& peer_address, absl::string_view /*alpn*/,
     const ParsedQuicVersion& version,
-    absl::string_view /*sni*/) {
+    const ParsedClientHello& /*parsed_chlo*/) {
   // The MasqueServerSession takes ownership of |connection| below.
   QuicConnection* connection =
       new QuicConnection(connection_id, self_address, peer_address, helper(),
diff --git a/quic/masque/masque_dispatcher.h b/quic/masque/masque_dispatcher.h
index 368219e..35c5e0f 100644
--- a/quic/masque/masque_dispatcher.h
+++ b/quic/masque/masque_dispatcher.h
@@ -38,12 +38,10 @@
 
   // From QuicSimpleDispatcher.
   std::unique_ptr<QuicSession> CreateQuicSession(
-      QuicConnectionId connection_id,
-      const QuicSocketAddress& self_address,
-      const QuicSocketAddress& peer_address,
-      absl::string_view alpn,
+      QuicConnectionId connection_id, const QuicSocketAddress& self_address,
+      const QuicSocketAddress& peer_address, absl::string_view alpn,
       const ParsedQuicVersion& version,
-      absl::string_view sni) override;
+      const quic::ParsedClientHello& parsed_chlo) override;
 
   bool OnFailedToDispatchPacket(const ReceivedPacketInfo& packet_info) override;
 
diff --git a/quic/qbone/qbone_client_test.cc b/quic/qbone/qbone_client_test.cc
index 91bb82d..371a8b6 100644
--- a/quic/qbone/qbone_client_test.cc
+++ b/quic/qbone/qbone_client_test.cc
@@ -123,12 +123,10 @@
         writer_(writer) {}
 
   std::unique_ptr<QuicSession> CreateQuicSession(
-      QuicConnectionId id,
-      const QuicSocketAddress& self_address,
-      const QuicSocketAddress& peer_address,
-      absl::string_view alpn,
-      const quic::ParsedQuicVersion& version,
-      absl::string_view sni) override {
+      QuicConnectionId id, const QuicSocketAddress& self_address,
+      const QuicSocketAddress& peer_address, absl::string_view alpn,
+      const ParsedQuicVersion& version,
+      const ParsedClientHello& /*parsed_chlo*/) override {
     QUICHE_CHECK_EQ(alpn, "qbone");
     QuicConnection* connection = new QuicConnection(
         id, self_address, peer_address, helper(), alarm_factory(), writer(),
diff --git a/quic/test_tools/quic_test_server.cc b/quic/test_tools/quic_test_server.cc
index 020a34c..1d85805 100644
--- a/quic/test_tools/quic_test_server.cc
+++ b/quic/test_tools/quic_test_server.cc
@@ -96,7 +96,8 @@
   std::unique_ptr<QuicSession> CreateQuicSession(
       QuicConnectionId id, const QuicSocketAddress& self_address,
       const QuicSocketAddress& peer_address, absl::string_view /*alpn*/,
-      const ParsedQuicVersion& version, absl::string_view /*sni*/) override {
+      const ParsedQuicVersion& version,
+      const ParsedClientHello& /*parsed_chlo*/) override {
     QuicReaderMutexLock lock(&factory_lock_);
     // The QuicServerSessionBase takes ownership of |connection| below.
     QuicConnection* connection = new QuicConnection(
diff --git a/quic/tools/quic_simple_dispatcher.cc b/quic/tools/quic_simple_dispatcher.cc
index aa9a9c1..7864ce7 100644
--- a/quic/tools/quic_simple_dispatcher.cc
+++ b/quic/tools/quic_simple_dispatcher.cc
@@ -49,12 +49,10 @@
 }
 
 std::unique_ptr<QuicSession> QuicSimpleDispatcher::CreateQuicSession(
-    QuicConnectionId connection_id,
-    const QuicSocketAddress& self_address,
-    const QuicSocketAddress& peer_address,
-    absl::string_view /*alpn*/,
+    QuicConnectionId connection_id, const QuicSocketAddress& self_address,
+    const QuicSocketAddress& peer_address, absl::string_view /*alpn*/,
     const ParsedQuicVersion& version,
-    absl::string_view /*sni*/) {
+    const ParsedClientHello& /*parsed_chlo*/) {
   // The QuicServerSessionBase takes ownership of |connection| below.
   QuicConnection* connection =
       new QuicConnection(connection_id, self_address, peer_address, helper(),
diff --git a/quic/tools/quic_simple_dispatcher.h b/quic/tools/quic_simple_dispatcher.h
index c790f21..ce9d680 100644
--- a/quic/tools/quic_simple_dispatcher.h
+++ b/quic/tools/quic_simple_dispatcher.h
@@ -32,12 +32,10 @@
 
  protected:
   std::unique_ptr<QuicSession> CreateQuicSession(
-      QuicConnectionId connection_id,
-      const QuicSocketAddress& self_address,
-      const QuicSocketAddress& peer_address,
-      absl::string_view alpn,
+      QuicConnectionId connection_id, const QuicSocketAddress& self_address,
+      const QuicSocketAddress& peer_address, absl::string_view alpn,
       const ParsedQuicVersion& version,
-      absl::string_view sni) override;
+      const ParsedClientHello& parsed_chlo) override;
 
   QuicSimpleServerBackend* server_backend() {
     return quic_simple_server_backend_;
diff --git a/quic/tools/quic_transport_simple_server_dispatcher.cc b/quic/tools/quic_transport_simple_server_dispatcher.cc
index 25730ed..b767434 100644
--- a/quic/tools/quic_transport_simple_server_dispatcher.cc
+++ b/quic/tools/quic_transport_simple_server_dispatcher.cc
@@ -37,10 +37,9 @@
 QuicTransportSimpleServerDispatcher::CreateQuicSession(
     QuicConnectionId server_connection_id,
     const QuicSocketAddress& self_address,
-    const QuicSocketAddress& peer_address,
-    absl::string_view /*alpn*/,
+    const QuicSocketAddress& peer_address, absl::string_view /*alpn*/,
     const ParsedQuicVersion& version,
-    absl::string_view /*sni*/) {
+    const ParsedClientHello& /*parsed_chlo*/) {
   auto connection = std::make_unique<QuicConnection>(
       server_connection_id, self_address, peer_address, helper(),
       alarm_factory(), writer(),
diff --git a/quic/tools/quic_transport_simple_server_dispatcher.h b/quic/tools/quic_transport_simple_server_dispatcher.h
index bb38596..d8761f6 100644
--- a/quic/tools/quic_transport_simple_server_dispatcher.h
+++ b/quic/tools/quic_transport_simple_server_dispatcher.h
@@ -30,10 +30,9 @@
   std::unique_ptr<QuicSession> CreateQuicSession(
       QuicConnectionId server_connection_id,
       const QuicSocketAddress& self_address,
-      const QuicSocketAddress& peer_address,
-      absl::string_view alpn,
+      const QuicSocketAddress& peer_address, absl::string_view alpn,
       const ParsedQuicVersion& version,
-      absl::string_view sni) override;
+      const ParsedClientHello& parsed_chlo) override;
 
   std::vector<url::Origin> accepted_origins_;
 };