Support session-specific ALPNs for clients, and specifying multiple ALPNs.

gfe-relnote: n/a (no functional change)
PiperOrigin-RevId: 266192245
Change-Id: Id0921d36b2c32c7df92a4a528ec19d9a28b826b0
diff --git a/quic/core/tls_client_handshaker.cc b/quic/core/tls_client_handshaker.cc
index 8e2d2bb..a410f81 100644
--- a/quic/core/tls_client_handshaker.cc
+++ b/quic/core/tls_client_handshaker.cc
@@ -15,8 +15,6 @@
 
 namespace quic {
 
-std::string* quic_alpn_override_on_client_for_tests = nullptr;
-
 TlsClientHandshaker::ProofVerifierCallbackImpl::ProofVerifierCallbackImpl(
     TlsClientHandshaker* parent)
     : parent_(parent) {}
@@ -78,32 +76,10 @@
     return false;
   }
 
-  std::string alpn_string = AlpnForVersion(session()->connection()->version());
-  if (quic_alpn_override_on_client_for_tests != nullptr) {
-    alpn_string = *quic_alpn_override_on_client_for_tests;
-  }
-  if (alpn_string.length() > std::numeric_limits<uint8_t>::max()) {
-    QUIC_BUG << "ALPN too long: '" << alpn_string << "'";
-    CloseConnection(QUIC_HANDSHAKE_FAILED,
-                    "Client configured ALPN is too long");
+  if (!SetAlpn()) {
+    CloseConnection(QUIC_HANDSHAKE_FAILED, "Client failed to set ALPN");
     return false;
   }
-  const uint8_t alpn_length = alpn_string.length();
-  if (alpn_length > 0) {
-    // SSL_set_alpn_protos expects a sequence of one-byte-length-prefixed
-    // strings so we copy alpn_string to a new buffer that has the length
-    // in alpn[0].
-    uint8_t alpn[std::numeric_limits<uint8_t>::max() + 1];
-    alpn[0] = alpn_length;
-    memcpy(reinterpret_cast<char*>(alpn + 1), alpn_string.data(), alpn_length);
-    if (SSL_set_alpn_protos(ssl(), alpn,
-                            static_cast<unsigned>(alpn_length) + 1) != 0) {
-      QUIC_BUG << "Failed to set ALPN: '" << alpn_string << "'";
-      CloseConnection(QUIC_HANDSHAKE_FAILED, "Client failed to set ALPN");
-      return false;
-    }
-  }
-  QUIC_DLOG(INFO) << "Client using ALPN: '" << alpn_string << "'";
 
   // Set the Transport Parameters to send in the ClientHello
   if (!SetTransportParameters()) {
@@ -117,6 +93,46 @@
   return session()->connection()->connected();
 }
 
+static bool IsValidAlpn(const std::string& alpn_string) {
+  return alpn_string.length() <= std::numeric_limits<uint8_t>::max();
+}
+
+bool TlsClientHandshaker::SetAlpn() {
+  std::vector<std::string> alpns = session()->GetAlpnsToOffer();
+  if (alpns.empty()) {
+    if (allow_empty_alpn_for_tests_) {
+      return true;
+    }
+
+    QUIC_BUG << "ALPN missing";
+    return false;
+  }
+  if (!std::all_of(alpns.begin(), alpns.end(), IsValidAlpn)) {
+    QUIC_BUG << "ALPN too long";
+    return false;
+  }
+
+  // SSL_set_alpn_protos expects a sequence of one-byte-length-prefixed
+  // strings.
+  uint8_t alpn[1024];
+  QuicDataWriter alpn_writer(sizeof(alpn), reinterpret_cast<char*>(alpn));
+  bool success = true;
+  for (const std::string& alpn_string : alpns) {
+    success = success && alpn_writer.WriteUInt8(alpn_string.size()) &&
+              alpn_writer.WriteStringPiece(alpn_string);
+  }
+  success =
+      success && (SSL_set_alpn_protos(ssl(), alpn, alpn_writer.length()) == 0);
+  if (!success) {
+    QUIC_BUG << "Failed to set ALPN: "
+             << QuicTextUtils::HexDump(
+                    QuicStringPiece(alpn_writer.data(), alpn_writer.length()));
+    return false;
+  }
+  QUIC_DLOG(INFO) << "Client using ALPN: '" << alpns[0] << "'";
+  return true;
+}
+
 bool TlsClientHandshaker::SetTransportParameters() {
   TransportParameters params;
   params.perspective = Perspective::IS_CLIENT;