Enable getting and setting custom QUIC transport parameters. This is primarily for WebTransport (https://tools.ietf.org/html/draft-vvv-webtransport-quic-00#section-3.2), but may be useful for other purposes in the future. gfe-relnote: n/a (v99 only) PiperOrigin-RevId: 268811939 Change-Id: Icf4ba1657ffbca367d72110be86047052bd5dcdf
diff --git a/quic/core/crypto/transport_parameters.cc b/quic/core/crypto/transport_parameters.cc index 8e4fa40..27b1f98 100644 --- a/quic/core/crypto/transport_parameters.cc +++ b/quic/core/crypto/transport_parameters.cc
@@ -6,6 +6,7 @@ #include <cstdint> #include <cstring> +#include <forward_list> #include "third_party/boringssl/src/include/openssl/bytestring.h" #include "net/third_party/quiche/src/quic/core/crypto/crypto_framer.h" @@ -282,6 +283,10 @@ rv += " " + TransportParameterIdToString(kGoogleQuicParam); } rv += "]"; + for (const auto& kv : custom_parameters) { + rv += " 0x" + QuicTextUtils::Hex(static_cast<uint32_t>(kv.first)); + rv += "=" + QuicTextUtils::HexEncode(kv.second); + } return rv; } @@ -534,6 +539,23 @@ } } + auto custom_parameters = std::make_unique<CBB[]>(in.custom_parameters.size()); + int i = 0; + for (const auto& kv : in.custom_parameters) { + CBB* custom_parameter = &custom_parameters[i++]; + QUIC_BUG_IF(kv.first < 0xff00) << "custom_parameters should not be used " + "for non-private use parameters"; + if (!CBB_add_u16(¶ms, kv.first) || + !CBB_add_u16_length_prefixed(¶ms, custom_parameter) || + !CBB_add_bytes(custom_parameter, + reinterpret_cast<const uint8_t*>(kv.second.data()), + kv.second.size())) { + QUIC_BUG << "Failed to write custom parameter " + << static_cast<int>(kv.first); + return false; + } + } + if (!CBB_flush(cbb.get())) { QUIC_BUG << "Failed to flush CBB for " << in; return false; @@ -744,6 +766,10 @@ } } } break; + default: + out->custom_parameters[param_id] = std::string( + reinterpret_cast<const char*>(CBS_data(&value)), CBS_len(&value)); + break; } if (!parse_success) { return false;
diff --git a/quic/core/crypto/transport_parameters.h b/quic/core/crypto/transport_parameters.h index 95a01da..22dc252 100644 --- a/quic/core/crypto/transport_parameters.h +++ b/quic/core/crypto/transport_parameters.h
@@ -14,6 +14,7 @@ #include "net/third_party/quiche/src/quic/core/quic_data_writer.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" namespace quic { @@ -24,6 +25,8 @@ struct QUIC_EXPORT_PRIVATE TransportParameters { // The identifier used to differentiate transport parameters. enum TransportParameterId : uint16_t; + // A map used to specify custom parameters. + using ParameterMap = QuicUnorderedMap<TransportParameterId, std::string>; // Represents an individual QUIC transport parameter that only encodes a // variable length integer. Can only be created inside the constructor for // TransportParameters. @@ -174,6 +177,9 @@ // the specification. bool AreValid() const; + // Custom parameters that may be specific to application protocol. + ParameterMap custom_parameters; + // Allows easily logging transport parameters. std::string ToString() const; friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
diff --git a/quic/core/crypto/transport_parameters_test.cc b/quic/core/crypto/transport_parameters_test.cc index e782c38..ed75f45 100644 --- a/quic/core/crypto/transport_parameters_test.cc +++ b/quic/core/crypto/transport_parameters_test.cc
@@ -17,6 +17,10 @@ namespace quic { namespace test { namespace { + +using testing::Pair; +using testing::UnorderedElementsAre; + const ParsedQuicVersion kVersion(PROTOCOL_TLS1_3, QUIC_VERSION_99); const QuicVersionLabel kFakeVersionLabel = 0x01234567; const QuicVersionLabel kFakeVersionLabel2 = 0x89ABCDEF; @@ -47,6 +51,12 @@ kFakePreferredStatelessResetTokenData, kFakePreferredStatelessResetTokenData + sizeof(kFakeStatelessResetTokenData)); +const auto kCustomParameter1 = + static_cast<TransportParameters::TransportParameterId>(0xffcd); +const char* kCustomParameter1Value = "foo"; +const auto kCustomParameter2 = + static_cast<TransportParameters::TransportParameterId>(0xff34); +const char* kCustomParameter2Value = "bar"; QuicSocketAddress CreateFakeV4SocketAddress() { QuicIpAddress ipv4_address; @@ -101,6 +111,8 @@ orig_params.disable_migration = kFakeDisableMigration; orig_params.active_connection_id_limit.set_value( kFakeActiveConnectionIdLimit); + orig_params.custom_parameters[kCustomParameter1] = kCustomParameter1Value; + orig_params.custom_parameters[kCustomParameter2] = kCustomParameter2Value; std::vector<uint8_t> serialized; ASSERT_TRUE(SerializeTransportParameters(kVersion, orig_params, &serialized)); @@ -134,6 +146,10 @@ EXPECT_EQ(kFakeDisableMigration, new_params.disable_migration); EXPECT_EQ(kFakeActiveConnectionIdLimit, new_params.active_connection_id_limit.value()); + EXPECT_THAT( + new_params.custom_parameters, + UnorderedElementsAre(Pair(kCustomParameter1, kCustomParameter1Value), + Pair(kCustomParameter2, kCustomParameter2Value))); } TEST_F(TransportParametersTest, RoundTripServer) { @@ -205,6 +221,7 @@ new_params.preferred_address->stateless_reset_token); EXPECT_EQ(kFakeActiveConnectionIdLimit, new_params.active_connection_id_limit.value()); + EXPECT_EQ(0u, new_params.custom_parameters.size()); } TEST_F(TransportParametersTest, IsValid) {
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc index 1033b26..5845f78 100644 --- a/quic/core/http/end_to_end_test.cc +++ b/quic/core/http/end_to_end_test.cc
@@ -4172,6 +4172,24 @@ static_cast<QuicSpdySession*>(GetServerSession())->max_allowed_push_id()); } +TEST_P(EndToEndTest, CustomTransportParameters) { + if (GetParam().negotiated_version.handshake_protocol != PROTOCOL_TLS1_3) { + Initialize(); + return; + } + + constexpr auto kCustomParameter = + static_cast<TransportParameters::TransportParameterId>(0xff34); + client_config_.custom_transport_parameters_to_send()[kCustomParameter] = + "test"; + ASSERT_TRUE(Initialize()); + + EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed()); + EXPECT_EQ(server_config_.received_custom_transport_parameters().at( + kCustomParameter), + "test"); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/quic_config.cc b/quic/core/quic_config.cc index 6400b0e..e48d21a 100644 --- a/quic/core/quic_config.cc +++ b/quic/core/quic_config.cc
@@ -978,6 +978,7 @@ initial_round_trip_time_us_.ToHandshakeMessage( params->google_quic_params.get()); connection_options_.ToHandshakeMessage(params->google_quic_params.get()); + params->custom_parameters = custom_transport_parameters_to_send_; return true; } @@ -1085,6 +1086,8 @@ } } + received_custom_transport_parameters_ = params.custom_parameters; + *error_details = ""; return QUIC_NO_ERROR; }
diff --git a/quic/core/quic_config.h b/quic/core/quic_config.h index b9ebcc1..25522b3 100644 --- a/quic/core/quic_config.h +++ b/quic/core/quic_config.h
@@ -479,6 +479,14 @@ HelloType hello_type, std::string* error_details); + TransportParameters::ParameterMap& custom_transport_parameters_to_send() { + return custom_transport_parameters_to_send_; + } + const TransportParameters::ParameterMap& + received_custom_transport_parameters() const { + return received_custom_transport_parameters_; + } + private: friend class test::QuicConfigPeer; @@ -557,6 +565,11 @@ // deserializing the frame); the received exponent is the value the peer uses // to serialize frames and this node uses to deserialize them. QuicFixedUint32 ack_delay_exponent_; + + // Custom transport parameters that can be sent and received in the TLS + // handshake. + TransportParameters::ParameterMap custom_transport_parameters_to_send_; + TransportParameters::ParameterMap received_custom_transport_parameters_; }; } // namespace quic