Add helper functions to validate subprotocol names without serializing them. PiperOrigin-RevId: 758302644
diff --git a/quiche/web_transport/web_transport_headers.cc b/quiche/web_transport/web_transport_headers.cc index fc46536..2ae7bf1 100644 --- a/quiche/web_transport/web_transport_headers.cc +++ b/quiche/web_transport/web_transport_headers.cc
@@ -12,6 +12,7 @@ #include <vector> #include "absl/base/attributes.h" +#include "absl/container/flat_hash_set.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" @@ -106,6 +107,31 @@ return std::string(subprotocol); } +bool ValidateSubprotocolName(absl::string_view name) { + return !name.empty() && quiche::structured_headers::IsValidToken(name); +} + +template <typename S> +bool ValidateSubprotocolListBase(absl::Span<const S> list) { + absl::flat_hash_set<absl::string_view> examined; + for (const absl::string_view name : list) { + if (!ValidateSubprotocolName(name)) { + return false; + } + auto [it, added] = examined.insert(name); + if (!added) { + return false; + } + } + return true; +} +bool ValidateSubprotocolList(absl::Span<const absl::string_view> list) { + return ValidateSubprotocolListBase(list); +} +bool ValidateSubprotocolList(absl::Span<const std::string> list) { + return ValidateSubprotocolListBase(list); +} + absl::StatusOr<WebTransportInitHeader> ParseInitHeader( absl::string_view header) { std::optional<Dictionary> parsed =
diff --git a/quiche/web_transport/web_transport_headers.h b/quiche/web_transport/web_transport_headers.h index 9ed1674..897741e 100644 --- a/quiche/web_transport/web_transport_headers.h +++ b/quiche/web_transport/web_transport_headers.h
@@ -28,6 +28,10 @@ absl::string_view value); QUICHE_EXPORT absl::StatusOr<std::string> SerializeSubprotocolResponseHeader( absl::string_view subprotocol); +QUICHE_EXPORT bool ValidateSubprotocolName(absl::string_view name); +QUICHE_EXPORT bool ValidateSubprotocolList( + absl::Span<const absl::string_view> list); +QUICHE_EXPORT bool ValidateSubprotocolList(absl::Span<const std::string> list); inline constexpr absl::string_view kInitHeader = "WebTransport-Init";
diff --git a/quiche/web_transport/web_transport_headers_test.cc b/quiche/web_transport/web_transport_headers_test.cc index c41dc8d..d85e1f8 100644 --- a/quiche/web_transport/web_transport_headers_test.cc +++ b/quiche/web_transport/web_transport_headers_test.cc
@@ -4,7 +4,12 @@ #include "quiche/web_transport/web_transport_headers.h" +#include <initializer_list> +#include <string> +#include <vector> + #include "absl/status/status.h" +#include "absl/strings/string_view.h" #include "quiche/common/platform/api/quiche_test.h" #include "quiche/common/test_tools/quiche_test_utils.h" @@ -119,5 +124,28 @@ IsOkAndHolds("u=100, bl=200, br=400")); } +// Helper to sidestep the fact that calling ValidateSubprotocolList directly +// with an initializer list is ambiguous. +bool ValidateSubprotocolListHelper( + std::initializer_list<absl::string_view> list) { + return ValidateSubprotocolList(list); +} + +TEST(WebTransportHeaders, ValidateSubprotocolName) { + EXPECT_TRUE(ValidateSubprotocolName("test")); + EXPECT_FALSE(ValidateSubprotocolName("123")); + EXPECT_FALSE(ValidateSubprotocolName("")); + + EXPECT_TRUE(ValidateSubprotocolListHelper({})); + EXPECT_TRUE(ValidateSubprotocolListHelper({"a", "b", "c"})); + EXPECT_FALSE(ValidateSubprotocolListHelper({"a", "1", "c"})); + EXPECT_FALSE(ValidateSubprotocolListHelper({"a", "b", "a"})); + + std::vector<std::string> vec = {"a", "b"}; + EXPECT_TRUE(ValidateSubprotocolList(vec)); + vec.push_back("b"); + EXPECT_FALSE(ValidateSubprotocolList(vec)); +} + } // namespace } // namespace webtransport