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