Add --quic_versions to QuicToyServer and improve version parsing This CL improves our version parsing code to allow using ALPN and parse a list, and uses that in a new --quic_versions flag on QuicToyServer. Note that ParseQuicVersionString is only used in tests and toy code. gfe-relnote: n/a, test-only PiperOrigin-RevId: 299245199 Change-Id: I33c0f73b6a094de0dba86b69bd598a24ee162872
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc index a7450aa..b5fc585 100644 --- a/quic/core/quic_versions.cc +++ b/quic/core/quic_versions.cc
@@ -263,7 +263,8 @@ return UnsupportedQuicVersion(); } -ParsedQuicVersion ParseQuicVersionString(std::string version_string) { +ParsedQuicVersion ParseQuicVersionString( + quiche::QuicheStringPiece version_string) { if (version_string.empty()) { return UnsupportedQuicVersion(); } @@ -271,12 +272,26 @@ if (quiche::QuicheTextUtils::StringToInt(version_string, &quic_version_number) && quic_version_number > 0) { - return ParsedQuicVersion( - PROTOCOL_QUIC_CRYPTO, - static_cast<QuicTransportVersion>(quic_version_number)); + QuicTransportVersion transport_version = + static_cast<QuicTransportVersion>(quic_version_number); + bool transport_version_is_supported = false; + for (QuicTransportVersion transport_vers : SupportedTransportVersions()) { + if (transport_vers == transport_version) { + transport_version_is_supported = true; + break; + } + } + if (!transport_version_is_supported || + !ParsedQuicVersionIsValid(PROTOCOL_QUIC_CRYPTO, transport_version)) { + return UnsupportedQuicVersion(); + } + return ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, transport_version); } for (const ParsedQuicVersion& version : AllSupportedVersions()) { - if (version_string == ParsedQuicVersionToString(version)) { + if (version_string == ParsedQuicVersionToString(version) || + version_string == AlpnForVersion(version) || + (version.handshake_protocol == PROTOCOL_QUIC_CRYPTO && + version_string == QuicVersionToString(version.transport_version))) { return version; } } @@ -286,6 +301,25 @@ return UnsupportedQuicVersion(); } +ParsedQuicVersionVector ParseQuicVersionVectorString( + quiche::QuicheStringPiece versions_string) { + ParsedQuicVersionVector versions; + std::vector<quiche::QuicheStringPiece> version_strings = + quiche::QuicheTextUtils::Split(versions_string, ','); + for (quiche::QuicheStringPiece version_string : version_strings) { + quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace( + &version_string); + ParsedQuicVersion version = ParseQuicVersionString(version_string); + if (version.transport_version == QUIC_VERSION_UNSUPPORTED || + std::find(versions.begin(), versions.end(), version) != + versions.end()) { + continue; + } + versions.push_back(version); + } + return versions; +} + QuicTransportVersionVector AllSupportedTransportVersions() { constexpr auto supported_transport_versions = SupportedTransportVersions(); QuicTransportVersionVector supported_versions(
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h index bb539b1..e07e24a 100644 --- a/quic/core/quic_versions.h +++ b/quic/core/quic_versions.h
@@ -21,6 +21,7 @@ #include "net/third_party/quiche/src/quic/core/quic_tag.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_export.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" namespace quic { @@ -399,10 +400,17 @@ QUIC_EXPORT_PRIVATE ParsedQuicVersion ParseQuicVersionLabel(QuicVersionLabel version_label); -// Parses a QUIC version string such as "Q043" or "T099". -// Also supports parsing numbers such as "44". +// Parses a QUIC version string such as "Q043" or "T050". Also supports parsing +// ALPN such as "h3-25" or "h3-Q050". For PROTOCOL_QUIC_CRYPTO versions, also +// supports parsing numbers such as "46". QUIC_EXPORT_PRIVATE ParsedQuicVersion -ParseQuicVersionString(std::string version_string); +ParseQuicVersionString(quiche::QuicheStringPiece version_string); + +// Parses a comma-separated list of QUIC version strings. Supports parsing by +// label, ALPN and numbers for PROTOCOL_QUIC_CRYPTO. Skips unknown versions. +// For example: "h3-25,Q050,46". +QUIC_EXPORT_PRIVATE ParsedQuicVersionVector +ParseQuicVersionVectorString(quiche::QuicheStringPiece versions_string); // Constructs a QuicVersionLabel from the provided ParsedQuicVersion. QUIC_EXPORT_PRIVATE QuicVersionLabel
diff --git a/quic/core/quic_versions_test.cc b/quic/core/quic_versions_test.cc index 59d7b09..851619f 100644 --- a/quic/core/quic_versions_test.cc +++ b/quic/core/quic_versions_test.cc
@@ -15,7 +15,9 @@ namespace test { namespace { -using testing::_; +using ::testing::_; +using ::testing::ElementsAre; +using ::testing::IsEmpty; class QuicVersionsTest : public QuicTest { protected: @@ -152,19 +154,110 @@ EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_43), ParseQuicVersionString("Q043")); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), + ParseQuicVersionString("QUIC_VERSION_46")); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), + ParseQuicVersionString("46")); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46), ParseQuicVersionString("Q046")); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_48), ParseQuicVersionString("Q048")); EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50), ParseQuicVersionString("Q050")); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50), + ParseQuicVersionString("50")); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50), + ParseQuicVersionString("h3-Q050")); EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("")); EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("Q 46")); EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("Q046 ")); + EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("99")); + EXPECT_EQ(UnsupportedQuicVersion(), ParseQuicVersionString("70")); - // Test a TLS version: EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50), ParseQuicVersionString("T050")); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_50), + ParseQuicVersionString("h3-T050")); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27), + ParseQuicVersionString("ff00001b")); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_27), + ParseQuicVersionString("h3-27")); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25), + ParseQuicVersionString("ff000019")); + EXPECT_EQ(ParsedQuicVersion(PROTOCOL_TLS1_3, QUIC_VERSION_IETF_DRAFT_25), + ParseQuicVersionString("h3-25")); +} + +TEST_F(QuicVersionsTest, ParseQuicVersionVectorString) { + ParsedQuicVersion version_q046(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_46); + ParsedQuicVersion version_q050(PROTOCOL_QUIC_CRYPTO, QUIC_VERSION_50); + ParsedQuicVersion version_t050(PROTOCOL_TLS1_3, QUIC_VERSION_50); + ParsedQuicVersion version_draft_25(PROTOCOL_TLS1_3, + QUIC_VERSION_IETF_DRAFT_25); + ParsedQuicVersion version_draft_27(PROTOCOL_TLS1_3, + QUIC_VERSION_IETF_DRAFT_27); + + EXPECT_THAT(ParseQuicVersionVectorString(""), IsEmpty()); + + EXPECT_THAT(ParseQuicVersionVectorString("QUIC_VERSION_50"), + ElementsAre(version_q050)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-Q050"), + ElementsAre(version_q050)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-T050"), + ElementsAre(version_t050)); + + EXPECT_THAT(ParseQuicVersionVectorString("h3-25, h3-27"), + ElementsAre(version_draft_25, version_draft_27)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-25,h3-27"), + ElementsAre(version_draft_25, version_draft_27)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-25,h3-27,h3-25"), + ElementsAre(version_draft_25, version_draft_27)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-25,h3-27, h3-25"), + ElementsAre(version_draft_25, version_draft_27)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-27,h3-25"), + ElementsAre(version_draft_27, version_draft_25)); + + EXPECT_THAT(ParseQuicVersionVectorString("h3-27,50"), + ElementsAre(version_draft_27, version_q050)); + + EXPECT_THAT(ParseQuicVersionVectorString("h3-Q050, h3-T050"), + ElementsAre(version_q050, version_t050)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-T050, h3-Q050"), + ElementsAre(version_t050, version_q050)); + EXPECT_THAT(ParseQuicVersionVectorString("QUIC_VERSION_50,h3-T050"), + ElementsAre(version_q050, version_t050)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-T050,QUIC_VERSION_50"), + ElementsAre(version_t050, version_q050)); + EXPECT_THAT(ParseQuicVersionVectorString("QUIC_VERSION_50, h3-T050"), + ElementsAre(version_q050, version_t050)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-T050, QUIC_VERSION_50"), + ElementsAre(version_t050, version_q050)); + + EXPECT_THAT(ParseQuicVersionVectorString("QUIC_VERSION_50,QUIC_VERSION_46"), + ElementsAre(version_q050, version_q046)); + EXPECT_THAT(ParseQuicVersionVectorString("QUIC_VERSION_46,QUIC_VERSION_50"), + ElementsAre(version_q046, version_q050)); + + // Regression test for https://crbug.com/1044952. + EXPECT_THAT(ParseQuicVersionVectorString("QUIC_VERSION_50, QUIC_VERSION_50"), + ElementsAre(version_q050)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-Q050, h3-Q050"), + ElementsAre(version_q050)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-T050, h3-T050"), + ElementsAre(version_t050)); + EXPECT_THAT(ParseQuicVersionVectorString("h3-Q050, QUIC_VERSION_50"), + ElementsAre(version_q050)); + EXPECT_THAT(ParseQuicVersionVectorString( + "QUIC_VERSION_50, h3-Q050, QUIC_VERSION_50, h3-Q050"), + ElementsAre(version_q050)); + EXPECT_THAT(ParseQuicVersionVectorString("QUIC_VERSION_50, h3-T050, h3-Q050"), + ElementsAre(version_q050, version_t050)); + + EXPECT_THAT(ParseQuicVersionVectorString("99"), IsEmpty()); + EXPECT_THAT(ParseQuicVersionVectorString("70"), IsEmpty()); + EXPECT_THAT(ParseQuicVersionVectorString("h3-01"), IsEmpty()); + EXPECT_THAT(ParseQuicVersionVectorString("h3-01,h3-25"), + ElementsAre(version_draft_25)); } TEST_F(QuicVersionsTest, CreateQuicVersionLabel) {
diff --git a/quic/tools/quic_toy_client.cc b/quic/tools/quic_toy_client.cc index 8a9532d..63a0671 100644 --- a/quic/tools/quic_toy_client.cc +++ b/quic/tools/quic_toy_client.cc
@@ -184,30 +184,29 @@ quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions(); - std::string quic_version_string = GetQuicFlag(FLAGS_quic_version); if (GetQuicFlag(FLAGS_quic_ietf_draft)) { quic::QuicVersionInitializeSupportForIetfDraft(); versions = {}; for (const ParsedQuicVersion& version : AllSupportedVersions()) { - // Find the first version that supports IETF QUIC. if (version.HasIetfQuicFrames() && version.handshake_protocol == quic::PROTOCOL_TLS1_3) { - versions = {version}; - break; + versions.push_back(version); } } - CHECK_EQ(versions.size(), 1u); - quic::QuicEnableVersion(versions[0]); + } - } else if (!quic_version_string.empty()) { - quic::ParsedQuicVersion parsed_quic_version = - quic::ParseQuicVersionString(quic_version_string); - if (parsed_quic_version.transport_version == - quic::QUIC_VERSION_UNSUPPORTED) { - return 1; - } - versions = {parsed_quic_version}; - quic::QuicEnableVersion(parsed_quic_version); + std::string quic_version_string = GetQuicFlag(FLAGS_quic_version); + if (!quic_version_string.empty()) { + versions = quic::ParseQuicVersionVectorString(quic_version_string); + } + + if (versions.empty()) { + std::cerr << "No known version selected." << std::endl; + return 1; + } + + for (const quic::ParsedQuicVersion& version : versions) { + quic::QuicEnableVersion(version); } if (GetQuicFlag(FLAGS_force_version_negotiation)) {
diff --git a/quic/tools/quic_toy_server.cc b/quic/tools/quic_toy_server.cc index a23e7d9..20ee151 100644 --- a/quic/tools/quic_toy_server.cc +++ b/quic/tools/quic_toy_server.cc
@@ -36,8 +36,15 @@ DEFINE_QUIC_COMMAND_LINE_FLAG(bool, quic_ietf_draft, false, - "Use the IETF draft version. This also enables " - "required internal QUIC flags."); + "Only enable IETF draft versions. This also " + "enables required internal QUIC flags."); + +DEFINE_QUIC_COMMAND_LINE_FLAG( + std::string, + quic_versions, + "", + "QUIC versions to enable, e.g. \"h3-25,h3-27\". If not set, then all " + "available versions are enabled."); namespace quic { @@ -73,6 +80,13 @@ } else { supported_versions = AllSupportedVersions(); } + std::string versions_string = GetQuicFlag(FLAGS_quic_versions); + if (!versions_string.empty()) { + supported_versions = ParseQuicVersionVectorString(versions_string); + } + if (supported_versions.empty()) { + return 1; + } for (const auto& version : supported_versions) { QuicEnableVersion(version); }