Make ALPN an array in QuicBufferedPacketStore

This is required to support QUIC+TLS where
ALPN is encoded as a list instead of a
single value.

gfe-relnote: refactor, no behavior change, not flag protected
PiperOrigin-RevId: 307922725
Change-Id: I08bbb0dc6db9264d17a64f569cf459ca590787c9
diff --git a/quic/core/quic_buffered_packet_store.cc b/quic/core/quic_buffered_packet_store.cc
index e363461..24ca6ef 100644
--- a/quic/core/quic_buffered_packet_store.cc
+++ b/quic/core/quic_buffered_packet_store.cc
@@ -138,7 +138,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.alpn = alpn;
+    queue.alpns = {alpn};
     connections_with_chlo_[connection_id] = false;  // Dummy value.
     // Set the version of buffered packets of this connection on CHLO.
     queue.version = version;
diff --git a/quic/core/quic_buffered_packet_store.h b/quic/core/quic_buffered_packet_store.h
index 52d957e..3d9290e 100644
--- a/quic/core/quic_buffered_packet_store.h
+++ b/quic/core/quic_buffered_packet_store.h
@@ -66,8 +66,8 @@
 
     std::list<BufferedPacket> buffered_packets;
     QuicTime creation_time;
-    // The alpn from the CHLO, if one was found.
-    std::string alpn;
+    // The ALPNs from the CHLO, if found.
+    std::vector<std::string> alpns;
     // Indicating whether this is an IETF QUIC connection.
     bool ietf_quic;
     // If buffered_packets contains the CHLO, it is the version of the CHLO.
diff --git a/quic/core/quic_buffered_packet_store_test.cc b/quic/core/quic_buffered_packet_store_test.cc
index dbf6c16..1b1544b 100644
--- a/quic/core/quic_buffered_packet_store_test.cc
+++ b/quic/core/quic_buffered_packet_store_test.cc
@@ -80,7 +80,7 @@
   const std::list<BufferedPacket>& queue = packets.buffered_packets;
   ASSERT_EQ(1u, queue.size());
   // The alpn should be ignored for non-chlo packets.
-  ASSERT_EQ("", packets.alpn);
+  ASSERT_TRUE(packets.alpns.empty());
   // 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.
@@ -415,7 +415,8 @@
   EXPECT_TRUE(store_.HasBufferedPackets(connection_id_2));
   auto packets = store_.DeliverPackets(connection_id_2);
   EXPECT_EQ(1u, packets.buffered_packets.size());
-  EXPECT_EQ("h3", packets.alpn);
+  ASSERT_EQ(1u, packets.alpns.size());
+  EXPECT_EQ("h3", packets.alpns[0]);
   // 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 c3261d8..36932c3 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -180,7 +180,7 @@
   QuicTimeWaitListManager* time_wait_list_manager_;
 };
 
-// Class which extracts the ALPN from a CHLO packet.
+// Class which extracts the ALPN from a QUIC_CRYPTO CHLO packet.
 class ChloAlpnExtractor : public ChloExtractor::Delegate {
  public:
   void OnChlo(QuicTransportVersion /*version*/,
@@ -522,6 +522,23 @@
   }
 }
 
+std::string QuicDispatcher::SelectAlpn(const std::vector<std::string>& alpns) {
+  if (alpns.empty()) {
+    return "";
+  }
+  if (alpns.size() > 1u) {
+    const std::vector<std::string>& supported_alpns =
+        version_manager_->GetSupportedAlpns();
+    for (const std::string& alpn : alpns) {
+      if (std::find(supported_alpns.begin(), supported_alpns.end(), alpn) !=
+          supported_alpns.end()) {
+        return alpn;
+      }
+    }
+  }
+  return alpns[0];
+}
+
 QuicDispatcher::QuicPacketFate QuicDispatcher::ValidityChecks(
     const ReceivedPacketInfo& packet_info) {
   if (!packet_info.version_flag) {
@@ -831,9 +848,10 @@
     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().peer_address,
-                          packet_list.alpn, packet_list.version);
+                          alpn, packet_list.version);
     if (original_connection_id != server_connection_id) {
       session->connection()->AddIncomingConnectionId(original_connection_id);
       session->connection()->InstallInitialCrypters(original_connection_id);
diff --git a/quic/core/quic_dispatcher.h b/quic/core/quic_dispatcher.h
index 1c1229a..849a8c9 100644
--- a/quic/core/quic_dispatcher.h
+++ b/quic/core/quic_dispatcher.h
@@ -300,6 +300,12 @@
   // Called if a packet from an unseen connection is reset or rejected.
   virtual void OnNewConnectionRejected() {}
 
+  // Selects the preferred ALPN from a vector of ALPNs.
+  // This runs through the list of ALPNs provided by the client and picks the
+  // first one it supports. If no supported versions are found, the first
+  // element of the vector is returned.
+  std::string SelectAlpn(const std::vector<std::string>& alpns);
+
  private:
   friend class test::QuicDispatcherPeer;
 
diff --git a/quic/core/quic_dispatcher_test.cc b/quic/core/quic_dispatcher_test.cc
index cc98335..fe5adb0 100644
--- a/quic/core/quic_dispatcher_test.cc
+++ b/quic/core/quic_dispatcher_test.cc
@@ -1412,6 +1412,17 @@
   ProcessFirstFlight(client_address, TestConnectionId(1));
 }
 
+TEST_P(QuicDispatcherTestOneVersion, SelectAlpn) {
+  EXPECT_EQ(QuicDispatcherPeer::SelectAlpn(dispatcher_.get(), {}), "");
+  EXPECT_EQ(QuicDispatcherPeer::SelectAlpn(dispatcher_.get(), {""}), "");
+  EXPECT_EQ(QuicDispatcherPeer::SelectAlpn(dispatcher_.get(), {"hq"}), "hq");
+  // Q033 is no longer supported but Q050 is.
+  QuicEnableVersion(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50));
+  EXPECT_EQ(
+      QuicDispatcherPeer::SelectAlpn(dispatcher_.get(), {"h3-Q033", "h3-Q050"}),
+      "h3-Q050");
+}
+
 // Verify the stopgap test: Packets with truncated connection IDs should be
 // dropped.
 class QuicDispatcherTestStrayPacketConnectionId
diff --git a/quic/core/quic_version_manager.cc b/quic/core/quic_version_manager.cc
index 105d23d..0a014be 100644
--- a/quic/core/quic_version_manager.cc
+++ b/quic/core/quic_version_manager.cc
@@ -50,6 +50,11 @@
   return filtered_supported_versions_with_quic_crypto_;
 }
 
+const std::vector<std::string>& QuicVersionManager::GetSupportedAlpns() {
+  MaybeRefilterSupportedVersions();
+  return filtered_supported_alpns_;
+}
+
 void QuicVersionManager::MaybeRefilterSupportedVersions() {
   static_assert(SupportedVersions().size() == 8u,
                 "Supported versions out of sync");
@@ -89,7 +94,8 @@
       FilterSupportedVersions(allowed_supported_versions_);
   filtered_supported_versions_with_quic_crypto_.clear();
   filtered_transport_versions_.clear();
-  for (ParsedQuicVersion version : filtered_supported_versions_) {
+  filtered_supported_alpns_.clear();
+  for (const ParsedQuicVersion& version : filtered_supported_versions_) {
     auto transport_version = version.transport_version;
     if (std::find(filtered_transport_versions_.begin(),
                   filtered_transport_versions_.end(),
@@ -99,7 +105,12 @@
     if (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO) {
       filtered_supported_versions_with_quic_crypto_.push_back(version);
     }
+    filtered_supported_alpns_.emplace_back(AlpnForVersion(version));
   }
 }
 
+void QuicVersionManager::AddCustomAlpn(const std::string& alpn) {
+  filtered_supported_alpns_.push_back(alpn);
+}
+
 }  // namespace quic
diff --git a/quic/core/quic_version_manager.h b/quic/core/quic_version_manager.h
index 381cef4..c5111ed 100644
--- a/quic/core/quic_version_manager.h
+++ b/quic/core/quic_version_manager.h
@@ -29,6 +29,10 @@
   // Returns currently supported versions using QUIC crypto.
   const ParsedQuicVersionVector& GetSupportedVersionsWithQuicCrypto();
 
+  // Returns the list of supported ALPNs, based on the current supported
+  // versions and any custom additions by subclasses.
+  const std::vector<std::string>& GetSupportedAlpns();
+
  protected:
   // If the value of any reloadable flag is different from the cached value,
   // re-filter |filtered_supported_versions_| and update the cached flag values.
@@ -42,6 +46,10 @@
     return filtered_transport_versions_;
   }
 
+  // Mechanism for subclasses to add custom ALPNs to the supported list.
+  // Should be called in constructor and RefilterSupportedVersions.
+  void AddCustomAlpn(const std::string& alpn);
+
  private:
   // Cached value of reloadable flags.
   // quic_enable_version_draft_27 flag
@@ -72,6 +80,9 @@
   // |filtered_supported_versions_|. No guarantees are made that the same
   // transport version isn't repeated.
   QuicTransportVersionVector filtered_transport_versions_;
+  // Contains the list of ALPNs corresponding to filtered_supported_versions_
+  // with custom ALPNs added.
+  std::vector<std::string> filtered_supported_alpns_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_version_manager_test.cc b/quic/core/quic_version_manager_test.cc
index 9ecad57..3a8e98f 100644
--- a/quic/core/quic_version_manager_test.cc
+++ b/quic/core/quic_version_manager_test.cc
@@ -9,6 +9,8 @@
 #include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
 #include "net/third_party/quiche/src/common/platform/api/quiche_arraysize.h"
 
+using ::testing::ElementsAre;
+
 namespace quic {
 namespace test {
 namespace {
@@ -48,6 +50,9 @@
             manager.GetSupportedVersions());
   EXPECT_EQ(CurrentSupportedVersionsWithQuicCrypto(),
             manager.GetSupportedVersionsWithQuicCrypto());
+  EXPECT_THAT(
+      manager.GetSupportedAlpns(),
+      ElementsAre("h3-Q050", "h3-Q049", "h3-Q048", "h3-Q046", "h3-Q043"));
 
   SetQuicReloadableFlag(quic_enable_version_draft_27, true);
   expected_parsed_versions.insert(
@@ -60,6 +65,9 @@
             manager.GetSupportedVersions());
   EXPECT_EQ(CurrentSupportedVersionsWithQuicCrypto(),
             manager.GetSupportedVersionsWithQuicCrypto());
+  EXPECT_THAT(manager.GetSupportedAlpns(),
+              ElementsAre("h3-27", "h3-Q050", "h3-Q049", "h3-Q048", "h3-Q046",
+                          "h3-Q043"));
 
   SetQuicReloadableFlag(quic_enable_version_draft_25_v3, true);
   expected_parsed_versions.insert(
@@ -70,6 +78,9 @@
             manager.GetSupportedVersionsWithQuicCrypto().size());
   EXPECT_EQ(CurrentSupportedVersionsWithQuicCrypto(),
             manager.GetSupportedVersionsWithQuicCrypto());
+  EXPECT_THAT(manager.GetSupportedAlpns(),
+              ElementsAre("h3-27", "h3-25", "h3-Q050", "h3-Q049", "h3-Q048",
+                          "h3-Q046", "h3-Q043"));
 
   SetQuicReloadableFlag(quic_enable_version_t050_v2, true);
   expected_parsed_versions.insert(
@@ -82,6 +93,9 @@
             manager.GetSupportedVersions());
   EXPECT_EQ(CurrentSupportedVersionsWithQuicCrypto(),
             manager.GetSupportedVersionsWithQuicCrypto());
+  EXPECT_THAT(manager.GetSupportedAlpns(),
+              ElementsAre("h3-27", "h3-25", "h3-T050", "h3-Q050", "h3-Q049",
+                          "h3-Q048", "h3-Q046", "h3-Q043"));
 }
 
 }  // namespace