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