Return transport parameters from TlsChloExtractor

Protected by FLAGS_quic_reloadable_flag_quic_parse_transport_parameters_from_chlo.

PiperOrigin-RevId: 704748800
diff --git a/quiche/common/quiche_feature_flags_list.h b/quiche/common/quiche_feature_flags_list.h
index b5047ca..1c2ddd7 100755
--- a/quiche/common/quiche_feature_flags_list.h
+++ b/quiche/common/quiche_feature_flags_list.h
@@ -51,6 +51,7 @@
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_optimize_qpack_blocking_manager, false, false, "If true, optimize qpack_blocking_manager for CPU efficiency.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_pacing_remove_non_initial_burst, false, false, "If true, remove the non-initial burst in QUIC PacingSender.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_parse_cert_compression_algos_from_chlo, true, true, "If true, parse offered cert compression algorithms from received CHLOs.")
+QUICHE_FLAG(bool, quiche_reloadable_flag_quic_parse_transport_parameters_from_chlo, false, false, "If true, parse QUIC transport parameters from received CHLOs.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_priority_respect_incremental, false, false, "If true, respect the incremental parameter of each stream in QuicWriteBlockedList.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_require_handshake_confirmation, true, true, "If true, require handshake confirmation for QUIC connections, functionally disabling 0-rtt handshakes.")
 QUICHE_FLAG(bool, quiche_reloadable_flag_quic_send_placeholder_ticket_when_encrypt_ticket_fails, true, true, "If true, when TicketCrypter fails to encrypt a session ticket, quic::TlsServerHandshaker will send a placeholder ticket, instead of an empty one, to the client.")
diff --git a/quiche/quic/core/tls_chlo_extractor.cc b/quiche/quic/core/tls_chlo_extractor.cc
index 7466642..356338c 100644
--- a/quiche/quic/core/tls_chlo_extractor.cc
+++ b/quiche/quic/core/tls_chlo_extractor.cc
@@ -108,6 +108,20 @@
   return cert_compression_algos;
 }
 
+std::vector<uint8_t> GetTransportParameters(
+    const SSL_CLIENT_HELLO* client_hello) {
+  const uint8_t* transport_params_data;
+  size_t transport_params_len;
+  int rv = SSL_early_callback_ctx_extension_get(
+      client_hello, TLSEXT_TYPE_quic_transport_parameters,
+      &transport_params_data, &transport_params_len);
+  if (rv != 1) {
+    return {};
+  }
+  return std::vector<uint8_t>(transport_params_data,
+                              transport_params_data + transport_params_len);
+}
+
 }  // namespace
 
 TlsChloExtractor::TlsChloExtractor()
@@ -423,6 +437,12 @@
     }
   }
 
+  if (GetQuicReloadableFlag(quic_parse_transport_parameters_from_chlo)) {
+    QUIC_RELOADABLE_FLAG_COUNT(quic_parse_transport_parameters_from_chlo);
+
+    transport_params_ = GetTransportParameters(client_hello);
+  }
+
   // Update our state now that we've parsed a full CHLO.
   if (state_ == State::kInitial) {
     state_ = State::kParsedFullSinglePacketChlo;
diff --git a/quiche/quic/core/tls_chlo_extractor.h b/quiche/quic/core/tls_chlo_extractor.h
index 8ded967..297063c 100644
--- a/quiche/quic/core/tls_chlo_extractor.h
+++ b/quiche/quic/core/tls_chlo_extractor.h
@@ -57,6 +57,9 @@
   const std::vector<uint16_t>& cert_compression_algos() const {
     return cert_compression_algos_;
   }
+  absl::Span<const uint8_t> transport_params() const {
+    return transport_params_;
+  }
   absl::Span<const uint8_t> client_hello_bytes() const {
     return client_hello_bytes_;
   }
@@ -282,6 +285,9 @@
   // If set, contains the TLS alert that caused an unrecoverable error, which is
   // an AlertDescription value defined in go/rfc/8446#appendix-B.2.
   std::optional<uint8_t> tls_alert_;
+  // QUIC Transport Parameters, if present in the CHLO.
+  // Not populated for draft29.
+  std::vector<uint8_t> transport_params_;
   // Exact TLS message bytes.
   std::vector<uint8_t> client_hello_bytes_;
 };
diff --git a/quiche/quic/core/tls_chlo_extractor_test.cc b/quiche/quic/core/tls_chlo_extractor_test.cc
index 2bc2044..64e0ec9 100644
--- a/quiche/quic/core/tls_chlo_extractor_test.cc
+++ b/quiche/quic/core/tls_chlo_extractor_test.cc
@@ -284,6 +284,25 @@
   }
 }
 
+TEST_P(TlsChloExtractorTest, TlsExtensionInfo_QuicTransportParameters) {
+  Initialize();
+  EXPECT_EQ(packets_.size(), 1u);
+  IngestPackets();
+  ValidateChloDetails();
+
+  if (GetQuicReloadableFlag(quic_parse_transport_parameters_from_chlo)) {
+    // RFC QUIC has transport parameters, drafts doesn't.
+    if (version_ == ParsedQuicVersion::RFCv1() ||
+        version_ == ParsedQuicVersion::RFCv2()) {
+      EXPECT_FALSE(tls_chlo_extractor_->transport_params().empty());
+    } else {
+      EXPECT_TRUE(tls_chlo_extractor_->transport_params().empty());
+    }
+  } else {
+    EXPECT_TRUE(tls_chlo_extractor_->transport_params().empty());
+  }
+}
+
 TEST_P(TlsChloExtractorTest, MultiPacket) {
   IncreaseSizeOfChlo();
   Initialize();