Move moqt::TrackNamespace and friends into a separate header. PiperOrigin-RevId: 852458341
diff --git a/build/source_list.bzl b/build/source_list.bzl index 88d0861..bde91f4 100644 --- a/build/source_list.bzl +++ b/build/source_list.bzl
@@ -1568,6 +1568,7 @@ "quic/moqt/moqt_framer.h", "quic/moqt/moqt_known_track_publisher.h", "quic/moqt/moqt_messages.h", + "quic/moqt/moqt_names.h", "quic/moqt/moqt_object.h", "quic/moqt/moqt_outgoing_queue.h", "quic/moqt/moqt_outstanding_objects.h", @@ -1597,6 +1598,7 @@ "quic/moqt/moqt_framer.cc", "quic/moqt/moqt_known_track_publisher.cc", "quic/moqt/moqt_messages.cc", + "quic/moqt/moqt_names.cc", "quic/moqt/moqt_object.cc", "quic/moqt/moqt_outgoing_queue.cc", "quic/moqt/moqt_outstanding_objects.cc", @@ -1623,6 +1625,7 @@ "quic/moqt/moqt_framer_test.cc", "quic/moqt/moqt_integration_test.cc", "quic/moqt/moqt_messages_test.cc", + "quic/moqt/moqt_names_test.cc", "quic/moqt/moqt_outgoing_queue_test.cc", "quic/moqt/moqt_outstanding_objects_test.cc", "quic/moqt/moqt_parser_fuzz_test.cc",
diff --git a/build/source_list.gni b/build/source_list.gni index 1ca3b89..42c0a86 100644 --- a/build/source_list.gni +++ b/build/source_list.gni
@@ -1572,6 +1572,7 @@ "src/quiche/quic/moqt/moqt_framer.h", "src/quiche/quic/moqt/moqt_known_track_publisher.h", "src/quiche/quic/moqt/moqt_messages.h", + "src/quiche/quic/moqt/moqt_names.h", "src/quiche/quic/moqt/moqt_object.h", "src/quiche/quic/moqt/moqt_outgoing_queue.h", "src/quiche/quic/moqt/moqt_outstanding_objects.h", @@ -1601,6 +1602,7 @@ "src/quiche/quic/moqt/moqt_framer.cc", "src/quiche/quic/moqt/moqt_known_track_publisher.cc", "src/quiche/quic/moqt/moqt_messages.cc", + "src/quiche/quic/moqt/moqt_names.cc", "src/quiche/quic/moqt/moqt_object.cc", "src/quiche/quic/moqt/moqt_outgoing_queue.cc", "src/quiche/quic/moqt/moqt_outstanding_objects.cc", @@ -1628,6 +1630,7 @@ "src/quiche/quic/moqt/moqt_framer_test.cc", "src/quiche/quic/moqt/moqt_integration_test.cc", "src/quiche/quic/moqt/moqt_messages_test.cc", + "src/quiche/quic/moqt/moqt_names_test.cc", "src/quiche/quic/moqt/moqt_outgoing_queue_test.cc", "src/quiche/quic/moqt/moqt_outstanding_objects_test.cc", "src/quiche/quic/moqt/moqt_parser_fuzz_test.cc",
diff --git a/build/source_list.json b/build/source_list.json index 1f8c1a7..da12550 100644 --- a/build/source_list.json +++ b/build/source_list.json
@@ -1571,6 +1571,7 @@ "quiche/quic/moqt/moqt_framer.h", "quiche/quic/moqt/moqt_known_track_publisher.h", "quiche/quic/moqt/moqt_messages.h", + "quiche/quic/moqt/moqt_names.h", "quiche/quic/moqt/moqt_object.h", "quiche/quic/moqt/moqt_outgoing_queue.h", "quiche/quic/moqt/moqt_outstanding_objects.h", @@ -1600,6 +1601,7 @@ "quiche/quic/moqt/moqt_framer.cc", "quiche/quic/moqt/moqt_known_track_publisher.cc", "quiche/quic/moqt/moqt_messages.cc", + "quiche/quic/moqt/moqt_names.cc", "quiche/quic/moqt/moqt_object.cc", "quiche/quic/moqt/moqt_outgoing_queue.cc", "quiche/quic/moqt/moqt_outstanding_objects.cc", @@ -1627,6 +1629,7 @@ "quiche/quic/moqt/moqt_framer_test.cc", "quiche/quic/moqt/moqt_integration_test.cc", "quiche/quic/moqt/moqt_messages_test.cc", + "quiche/quic/moqt/moqt_names_test.cc", "quiche/quic/moqt/moqt_outgoing_queue_test.cc", "quiche/quic/moqt/moqt_outstanding_objects_test.cc", "quiche/quic/moqt/moqt_parser_fuzz_test.cc",
diff --git a/quiche/quic/moqt/moqt_messages.cc b/quiche/quic/moqt/moqt_messages.cc index e449bcc..2283c6f 100644 --- a/quiche/quic/moqt/moqt_messages.cc +++ b/quiche/quic/moqt/moqt_messages.cc
@@ -14,11 +14,8 @@ #include "absl/algorithm/container.h" #include "absl/container/btree_map.h" #include "absl/status/status.h" -#include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" #include "absl/strings/string_view.h" -#include "absl/types/span.h" #include "quiche/quic/core/quic_types.h" #include "quiche/quic/platform/api/quic_bug_tracker.h" #include "quiche/common/platform/api/quiche_bug_tracker.h" @@ -318,62 +315,6 @@ return "Unknown preference " + std::to_string(static_cast<int>(preference)); } -TrackNamespace::TrackNamespace(absl::Span<const absl::string_view> elements) - : tuple_(elements.begin(), elements.end()) { - if (std::size(elements) > kMaxNamespaceElements) { - tuple_.clear(); - QUICHE_BUG(Moqt_namespace_too_large_01) - << "Constructing a namespace that is too large."; - return; - } - for (auto it : elements) { - length_ += it.size(); - if (length_ > kMaxFullTrackNameSize) { - tuple_.clear(); - QUICHE_BUG(Moqt_namespace_too_large_02) - << "Constructing a namespace that is too large."; - return; - } - } -} - -bool TrackNamespace::InNamespace(const TrackNamespace& other) const { - if (tuple_.size() < other.tuple_.size()) { - return false; - } - for (int i = 0; i < other.tuple_.size(); ++i) { - if (tuple_[i] != other.tuple_[i]) { - return false; - } - } - return true; -} - -void TrackNamespace::AddElement(absl::string_view element) { - if (!CanAddElement(element)) { - QUICHE_BUG(Moqt_namespace_too_large_03) - << "Constructing a namespace that is too large."; - return; - } - length_ += element.length(); - tuple_.push_back(std::string(element)); -} - -std::string TrackNamespace::ToString() const { - std::vector<std::string> bits; - bits.reserve(tuple_.size()); - for (absl::string_view raw_bit : tuple_) { - bits.push_back(absl::StrCat("\"", absl::CHexEscape(raw_bit), "\"")); - } - return absl::StrCat("{", absl::StrJoin(bits, "::"), "}"); -} - -void FullTrackName::set_name(absl::string_view name) { - QUIC_BUG_IF(Moqt_name_too_large_03, !CanAddName(name)) - << "Setting a name that is too large."; - name_ = name; -} - absl::Status MoqtStreamErrorToStatus(webtransport::StreamErrorCode error_code, absl::string_view reason_phrase) { switch (error_code) {
diff --git a/quiche/quic/moqt/moqt_messages.h b/quiche/quic/moqt/moqt_messages.h index 37061d0..2308763 100644 --- a/quiche/quic/moqt/moqt_messages.h +++ b/quiche/quic/moqt/moqt_messages.h
@@ -19,15 +19,13 @@ #include "absl/container/btree_map.h" #include "absl/status/status.h" -#include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" -#include "absl/types/span.h" #include "quiche/quic/core/quic_time.h" #include "quiche/quic/core/quic_types.h" #include "quiche/quic/core/quic_versions.h" +#include "quiche/quic/moqt/moqt_names.h" #include "quiche/quic/moqt/moqt_priority.h" -#include "quiche/common/platform/api/quiche_bug_tracker.h" #include "quiche/common/platform/api/quiche_export.h" #include "quiche/common/platform/api/quiche_logging.h" #include "quiche/common/quiche_callbacks.h" @@ -51,9 +49,6 @@ inline constexpr uint64_t kDefaultInitialMaxRequestId = 100; // TODO(martinduke): Implement an auth token cache. inline constexpr uint64_t kDefaultMaxAuthTokenCacheSize = 0; -inline constexpr uint64_t kMinNamespaceElements = 1; -inline constexpr uint64_t kMaxNamespaceElements = 32; -inline constexpr size_t kMaxFullTrackNameSize = 1024; inline constexpr uint64_t kMaxObjectId = quiche::kVarInt62MaxValue; enum AuthTokenType : uint64_t { @@ -399,114 +394,6 @@ using MoqtSubscribeErrorReason = MoqtRequestError; using MoqtPublishNamespaceErrorReason = MoqtSubscribeErrorReason; -class TrackNamespace { - public: - explicit TrackNamespace(absl::Span<const absl::string_view> elements); - explicit TrackNamespace( - std::initializer_list<const absl::string_view> elements) - : TrackNamespace(absl::Span<const absl::string_view>( - std::data(elements), std::size(elements))) {} - explicit TrackNamespace(absl::string_view ns) : TrackNamespace({ns}) {} - TrackNamespace() : TrackNamespace({}) {} - - bool IsValid() const { - return !tuple_.empty() && tuple_.size() <= kMaxNamespaceElements && - length_ <= kMaxFullTrackNameSize; - } - bool InNamespace(const TrackNamespace& other) const; - // Check if adding an element will exceed limits, without triggering a - // bug. Useful for the parser, which has to be robust to malformed data. - bool CanAddElement(absl::string_view element) { - return (tuple_.size() < kMaxNamespaceElements && - length_ + element.length() <= kMaxFullTrackNameSize); - } - void AddElement(absl::string_view element); - bool PopElement() { - if (tuple_.size() == 1) { - return false; - } - length_ -= tuple_.back().length(); - tuple_.pop_back(); - return true; - } - std::string ToString() const; - // Returns the number of elements in the tuple. - size_t number_of_elements() const { return tuple_.size(); } - // Returns the sum of the lengths of all elements in the tuple. - size_t total_length() const { return length_; } - - auto operator<=>(const TrackNamespace& other) const { - return std::lexicographical_compare_three_way( - tuple_.cbegin(), tuple_.cend(), other.tuple_.cbegin(), - other.tuple_.cend()); - } - bool operator==(const TrackNamespace&) const = default; - - const std::vector<std::string>& tuple() const { return tuple_; } - - template <typename H> - friend H AbslHashValue(H h, const TrackNamespace& m) { - return H::combine(std::move(h), m.tuple_); - } - template <typename Sink> - friend void AbslStringify(Sink& sink, const TrackNamespace& track_namespace) { - sink.Append(track_namespace.ToString()); - } - - private: - std::vector<std::string> tuple_; - size_t length_ = 0; // size in bytes. -}; - -class FullTrackName { - public: - FullTrackName(absl::string_view ns, absl::string_view name) - : namespace_(ns), name_(name) { - QUICHE_BUG_IF(Moqt_full_track_name_too_large_01, !IsValid()) - << "Constructing a Full Track Name that is too large."; - } - FullTrackName(TrackNamespace ns, absl::string_view name) - : namespace_(ns), name_(name) { - QUICHE_BUG_IF(Moqt_full_track_name_too_large_02, !IsValid()) - << "Constructing a Full Track Name that is too large."; - } - FullTrackName() = default; - - bool IsValid() const { - return namespace_.IsValid() && length() <= kMaxFullTrackNameSize; - } - const TrackNamespace& track_namespace() const { return namespace_; } - TrackNamespace& track_namespace() { return namespace_; } - absl::string_view name() const { return name_; } - void AddElement(absl::string_view element) { - return namespace_.AddElement(element); - } - std::string ToString() const { - return absl::StrCat(namespace_.ToString(), "::", name_); - } - // Check if the name will exceed limits, without triggering a bug. Useful for - // the parser, which has to be robust to malformed data. - bool CanAddName(absl::string_view name) { - return (namespace_.total_length() + name.length() <= kMaxFullTrackNameSize); - } - void set_name(absl::string_view name); - size_t length() const { return namespace_.total_length() + name_.length(); } - - auto operator<=>(const FullTrackName&) const = default; - template <typename H> - friend H AbslHashValue(H h, const FullTrackName& m) { - return H::combine(std::move(h), m.namespace_.tuple(), m.name_); - } - template <typename Sink> - friend void AbslStringify(Sink& sink, const FullTrackName& full_track_name) { - sink.Append(full_track_name.ToString()); - } - - private: - TrackNamespace namespace_; - std::string name_ = ""; -}; - // Location as defined in // https://moq-wg.github.io/moq-transport/draft-ietf-moq-transport.html#location-structure struct Location {
diff --git a/quiche/quic/moqt/moqt_messages_test.cc b/quiche/quic/moqt/moqt_messages_test.cc index 4372ffd..4597b0f 100644 --- a/quiche/quic/moqt/moqt_messages_test.cc +++ b/quiche/quic/moqt/moqt_messages_test.cc
@@ -5,107 +5,12 @@ #include "quiche/quic/moqt/moqt_messages.h" #include <optional> -#include <vector> -#include "absl/hash/hash.h" -#include "absl/strings/string_view.h" -#include "quiche/common/platform/api/quiche_expect_bug.h" #include "quiche/common/platform/api/quiche_test.h" namespace moqt::test { namespace { -TEST(MoqtMessagesTest, TrackNamespaceConstructors) { - TrackNamespace name1({"foo", "bar"}); - std::vector<absl::string_view> list = {"foo", "bar"}; - TrackNamespace name2(list); - EXPECT_EQ(name1, name2); - EXPECT_EQ(absl::HashOf(name1), absl::HashOf(name2)); -} - -TEST(MoqtMessagesTest, TrackNamespaceOrder) { - TrackNamespace name1({"a", "b"}); - TrackNamespace name2({"a", "b", "c"}); - TrackNamespace name3({"b", "a"}); - EXPECT_LT(name1, name2); - EXPECT_LT(name2, name3); - EXPECT_LT(name1, name3); -} - -TEST(MoqtMessagesTest, TrackNamespaceInNamespace) { - TrackNamespace name1({"a", "b"}); - TrackNamespace name2({"a", "b", "c"}); - TrackNamespace name3({"d", "b"}); - EXPECT_TRUE(name2.InNamespace(name1)); - EXPECT_FALSE(name1.InNamespace(name2)); - EXPECT_TRUE(name1.InNamespace(name1)); - EXPECT_FALSE(name2.InNamespace(name3)); -} - -TEST(MoqtMessagesTest, TrackNamespacePushPop) { - TrackNamespace name({"a"}); - TrackNamespace original = name; - name.AddElement("b"); - EXPECT_TRUE(name.InNamespace(original)); - EXPECT_FALSE(original.InNamespace(name)); - EXPECT_TRUE(name.PopElement()); - EXPECT_EQ(name, original); - EXPECT_FALSE(name.PopElement()); -} - -TEST(MoqtMessagesTest, TrackNamespaceToString) { - TrackNamespace name1({"a", "b"}); - EXPECT_EQ(name1.ToString(), R"({"a"::"b"})"); - - TrackNamespace name2({"\xff", "\x61"}); - EXPECT_EQ(name2.ToString(), R"({"\xff"::"a"})"); -} - -TEST(MoqtMessagesTest, FullTrackNameToString) { - FullTrackName name1(TrackNamespace{"a", "b"}, "c"); - EXPECT_EQ(name1.ToString(), R"({"a"::"b"}::c)"); -} - -TEST(MoqtMessagesTest, TooManyNamespaceElements) { - // 32 elements work. - TrackNamespace name1({"a", "b", "c", "d", "e", "f", "g", "h", - "i", "j", "k", "l", "m", "n", "o", "p", - "q", "r", "s", "t", "u", "v", "w", "x", - "y", "z", "aa", "bb", "cc", "dd", "ee", "ff"}); - EXPECT_TRUE(name1.IsValid()); - EXPECT_QUICHE_BUG(name1.AddElement("a"), - "Constructing a namespace that is too large."); - EXPECT_EQ(name1.number_of_elements(), kMaxNamespaceElements); - - // 33 elements fail, - TrackNamespace name2; - EXPECT_QUICHE_BUG( - name2 = TrackNamespace({"a", "b", "c", "d", "e", "f", "g", "h", "i", - "j", "k", "l", "m", "n", "o", "p", "q", "r", - "s", "t", "u", "v", "w", "x", "y", "z", "aa", - "bb", "cc", "dd", "ee", "ff", "gg"}), - "Constructing a namespace that is too large."); - EXPECT_FALSE(name2.IsValid()); -} - -TEST(MoqtMessagesTest, FullTrackNameTooLong) { - char raw_name[kMaxFullTrackNameSize + 1]; - absl::string_view track_namespace(raw_name, kMaxFullTrackNameSize); - // Adding an element takes it over the length limit. - TrackNamespace max_length_namespace({track_namespace}); - EXPECT_TRUE(max_length_namespace.IsValid()); - EXPECT_QUICHE_BUG(max_length_namespace.AddElement("f"), - "Constructing a namespace that is too large."); - // Constructing a FullTrackName where the name brings it over the length - // limit. - EXPECT_QUICHE_BUG(FullTrackName(max_length_namespace, "f"), - "Constructing a Full Track Name that is too large."); - // The namespace is too long by itself.. - absl::string_view big_namespace(raw_name, kMaxFullTrackNameSize + 1); - EXPECT_QUICHE_BUG(TrackNamespace({big_namespace}), - "Constructing a namespace that is too large."); -} - TEST(MoqtMessagesTest, MoqtDatagramType) { for (bool payload : {false, true}) { for (bool extension : {false, true}) {
diff --git a/quiche/quic/moqt/moqt_names.cc b/quiche/quic/moqt/moqt_names.cc new file mode 100644 index 0000000..510e740 --- /dev/null +++ b/quiche/quic/moqt/moqt_names.cc
@@ -0,0 +1,90 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "quiche/quic/moqt/moqt_names.h" + +#include <iterator> +#include <string> +#include <vector> + +#include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" +#include "quiche/common/platform/api/quiche_bug_tracker.h" + +namespace moqt { + +TrackNamespace::TrackNamespace(absl::Span<const absl::string_view> elements) + : tuple_(elements.begin(), elements.end()) { + if (std::size(elements) > kMaxNamespaceElements) { + tuple_.clear(); + QUICHE_BUG(Moqt_namespace_too_large_01) + << "Constructing a namespace that is too large."; + return; + } + for (auto it : elements) { + length_ += it.size(); + if (length_ > kMaxFullTrackNameSize) { + tuple_.clear(); + QUICHE_BUG(Moqt_namespace_too_large_02) + << "Constructing a namespace that is too large."; + return; + } + } +} + +bool TrackNamespace::InNamespace(const TrackNamespace& other) const { + if (tuple_.size() < other.tuple_.size()) { + return false; + } + for (int i = 0; i < other.tuple_.size(); ++i) { + if (tuple_[i] != other.tuple_[i]) { + return false; + } + } + return true; +} + +void TrackNamespace::AddElement(absl::string_view element) { + if (!CanAddElement(element)) { + QUICHE_BUG(Moqt_namespace_too_large_03) + << "Constructing a namespace that is too large."; + return; + } + length_ += element.length(); + tuple_.push_back(std::string(element)); +} + +std::string TrackNamespace::ToString() const { + std::vector<std::string> bits; + bits.reserve(tuple_.size()); + for (absl::string_view raw_bit : tuple_) { + bits.push_back(absl::StrCat("\"", absl::CHexEscape(raw_bit), "\"")); + } + return absl::StrCat("{", absl::StrJoin(bits, "::"), "}"); +} + +FullTrackName::FullTrackName(absl::string_view ns, absl::string_view name) + : namespace_(ns), name_(name) { + QUICHE_BUG_IF(Moqt_full_track_name_too_large_01, !IsValid()) + << "Constructing a Full Track Name that is too large."; +} +FullTrackName::FullTrackName(TrackNamespace ns, absl::string_view name) + : namespace_(ns), name_(name) { + QUICHE_BUG_IF(Moqt_full_track_name_too_large_02, !IsValid()) + << "Constructing a Full Track Name that is too large."; +} + +std::string FullTrackName::ToString() const { + return absl::StrCat(namespace_.ToString(), "::", name_); +} +void FullTrackName::set_name(absl::string_view name) { + QUICHE_BUG_IF(Moqt_name_too_large_03, !CanAddName(name)) + << "Setting a name that is too large."; + name_ = name; +} + +} // namespace moqt
diff --git a/quiche/quic/moqt/moqt_names.h b/quiche/quic/moqt/moqt_names.h new file mode 100644 index 0000000..0102eed --- /dev/null +++ b/quiche/quic/moqt/moqt_names.h
@@ -0,0 +1,126 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_MOQT_MOQT_NAMES_H_ +#define QUICHE_QUIC_MOQT_MOQT_NAMES_H_ + +#include <algorithm> +#include <cstddef> +#include <cstdint> +#include <cstring> +#include <initializer_list> +#include <string> +#include <vector> + +#include "absl/strings/string_view.h" +#include "absl/types/span.h" + +namespace moqt { + +// Protocol-specified limits on the length and structure of MoQT namespaces. +inline constexpr uint64_t kMinNamespaceElements = 1; +inline constexpr uint64_t kMaxNamespaceElements = 32; +inline constexpr size_t kMaxFullTrackNameSize = 1024; + +class TrackNamespace { + public: + explicit TrackNamespace(absl::Span<const absl::string_view> elements); + explicit TrackNamespace( + std::initializer_list<const absl::string_view> elements) + : TrackNamespace(absl::Span<const absl::string_view>( + std::data(elements), std::size(elements))) {} + explicit TrackNamespace(absl::string_view ns) : TrackNamespace({ns}) {} + TrackNamespace() : TrackNamespace({}) {} + + bool IsValid() const { + return !tuple_.empty() && tuple_.size() <= kMaxNamespaceElements && + length_ <= kMaxFullTrackNameSize; + } + bool InNamespace(const TrackNamespace& other) const; + // Check if adding an element will exceed limits, without triggering a + // bug. Useful for the parser, which has to be robust to malformed data. + bool CanAddElement(absl::string_view element) { + return (tuple_.size() < kMaxNamespaceElements && + length_ + element.length() <= kMaxFullTrackNameSize); + } + void AddElement(absl::string_view element); + bool PopElement() { + if (tuple_.size() == 1) { + return false; + } + length_ -= tuple_.back().length(); + tuple_.pop_back(); + return true; + } + std::string ToString() const; + // Returns the number of elements in the tuple. + size_t number_of_elements() const { return tuple_.size(); } + // Returns the sum of the lengths of all elements in the tuple. + size_t total_length() const { return length_; } + + auto operator<=>(const TrackNamespace& other) const { + return std::lexicographical_compare_three_way( + tuple_.cbegin(), tuple_.cend(), other.tuple_.cbegin(), + other.tuple_.cend()); + } + bool operator==(const TrackNamespace&) const = default; + + const std::vector<std::string>& tuple() const { return tuple_; } + + template <typename H> + friend H AbslHashValue(H h, const TrackNamespace& m) { + return H::combine(std::move(h), m.tuple_); + } + template <typename Sink> + friend void AbslStringify(Sink& sink, const TrackNamespace& track_namespace) { + sink.Append(track_namespace.ToString()); + } + + private: + std::vector<std::string> tuple_; + size_t length_ = 0; // size in bytes. +}; + +class FullTrackName { + public: + FullTrackName(absl::string_view ns, absl::string_view name); + FullTrackName(TrackNamespace ns, absl::string_view name); + FullTrackName() = default; + + bool IsValid() const { + return namespace_.IsValid() && length() <= kMaxFullTrackNameSize; + } + const TrackNamespace& track_namespace() const { return namespace_; } + TrackNamespace& track_namespace() { return namespace_; } + absl::string_view name() const { return name_; } + void AddElement(absl::string_view element) { + return namespace_.AddElement(element); + } + std::string ToString() const; + // Check if the name will exceed limits, without triggering a bug. Useful for + // the parser, which has to be robust to malformed data. + bool CanAddName(absl::string_view name) { + return (namespace_.total_length() + name.length() <= kMaxFullTrackNameSize); + } + void set_name(absl::string_view name); + size_t length() const { return namespace_.total_length() + name_.length(); } + + auto operator<=>(const FullTrackName&) const = default; + template <typename H> + friend H AbslHashValue(H h, const FullTrackName& m) { + return H::combine(std::move(h), m.namespace_.tuple(), m.name_); + } + template <typename Sink> + friend void AbslStringify(Sink& sink, const FullTrackName& full_track_name) { + sink.Append(full_track_name.ToString()); + } + + private: + TrackNamespace namespace_; + std::string name_ = ""; +}; + +} // namespace moqt + +#endif // QUICHE_QUIC_MOQT_MOQT_NAMES_H_
diff --git a/quiche/quic/moqt/moqt_names_test.cc b/quiche/quic/moqt/moqt_names_test.cc new file mode 100644 index 0000000..cffdaf0 --- /dev/null +++ b/quiche/quic/moqt/moqt_names_test.cc
@@ -0,0 +1,109 @@ +// Copyright 2024 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "quiche/quic/moqt/moqt_names.h" + +#include <vector> + +#include "absl/hash/hash.h" +#include "absl/strings/string_view.h" +#include "quiche/common/platform/api/quiche_expect_bug.h" +#include "quiche/common/platform/api/quiche_test.h" + +namespace moqt::test { +namespace { + +TEST(MoqtNamesTest, TrackNamespaceConstructors) { + TrackNamespace name1({"foo", "bar"}); + std::vector<absl::string_view> list = {"foo", "bar"}; + TrackNamespace name2(list); + EXPECT_EQ(name1, name2); + EXPECT_EQ(absl::HashOf(name1), absl::HashOf(name2)); +} + +TEST(MoqtNamesTest, TrackNamespaceOrder) { + TrackNamespace name1({"a", "b"}); + TrackNamespace name2({"a", "b", "c"}); + TrackNamespace name3({"b", "a"}); + EXPECT_LT(name1, name2); + EXPECT_LT(name2, name3); + EXPECT_LT(name1, name3); +} + +TEST(MoqtNamesTest, TrackNamespaceInNamespace) { + TrackNamespace name1({"a", "b"}); + TrackNamespace name2({"a", "b", "c"}); + TrackNamespace name3({"d", "b"}); + EXPECT_TRUE(name2.InNamespace(name1)); + EXPECT_FALSE(name1.InNamespace(name2)); + EXPECT_TRUE(name1.InNamespace(name1)); + EXPECT_FALSE(name2.InNamespace(name3)); +} + +TEST(MoqtNamesTest, TrackNamespacePushPop) { + TrackNamespace name({"a"}); + TrackNamespace original = name; + name.AddElement("b"); + EXPECT_TRUE(name.InNamespace(original)); + EXPECT_FALSE(original.InNamespace(name)); + EXPECT_TRUE(name.PopElement()); + EXPECT_EQ(name, original); + EXPECT_FALSE(name.PopElement()); +} + +TEST(MoqtNamesTest, TrackNamespaceToString) { + TrackNamespace name1({"a", "b"}); + EXPECT_EQ(name1.ToString(), R"({"a"::"b"})"); + + TrackNamespace name2({"\xff", "\x61"}); + EXPECT_EQ(name2.ToString(), R"({"\xff"::"a"})"); +} + +TEST(MoqtNamesTest, FullTrackNameToString) { + FullTrackName name1(TrackNamespace{"a", "b"}, "c"); + EXPECT_EQ(name1.ToString(), R"({"a"::"b"}::c)"); +} + +TEST(MoqtNamesTest, TooManyNamespaceElements) { + // 32 elements work. + TrackNamespace name1({"a", "b", "c", "d", "e", "f", "g", "h", + "i", "j", "k", "l", "m", "n", "o", "p", + "q", "r", "s", "t", "u", "v", "w", "x", + "y", "z", "aa", "bb", "cc", "dd", "ee", "ff"}); + EXPECT_TRUE(name1.IsValid()); + EXPECT_QUICHE_BUG(name1.AddElement("a"), + "Constructing a namespace that is too large."); + EXPECT_EQ(name1.number_of_elements(), kMaxNamespaceElements); + + // 33 elements fail, + TrackNamespace name2; + EXPECT_QUICHE_BUG( + name2 = TrackNamespace({"a", "b", "c", "d", "e", "f", "g", "h", "i", + "j", "k", "l", "m", "n", "o", "p", "q", "r", + "s", "t", "u", "v", "w", "x", "y", "z", "aa", + "bb", "cc", "dd", "ee", "ff", "gg"}), + "Constructing a namespace that is too large."); + EXPECT_FALSE(name2.IsValid()); +} + +TEST(MoqtNamesTest, FullTrackNameTooLong) { + char raw_name[kMaxFullTrackNameSize + 1]; + absl::string_view track_namespace(raw_name, kMaxFullTrackNameSize); + // Adding an element takes it over the length limit. + TrackNamespace max_length_namespace({track_namespace}); + EXPECT_TRUE(max_length_namespace.IsValid()); + EXPECT_QUICHE_BUG(max_length_namespace.AddElement("f"), + "Constructing a namespace that is too large."); + // Constructing a FullTrackName where the name brings it over the length + // limit. + EXPECT_QUICHE_BUG(FullTrackName(max_length_namespace, "f"), + "Constructing a Full Track Name that is too large."); + // The namespace is too long by itself.. + absl::string_view big_namespace(raw_name, kMaxFullTrackNameSize + 1); + EXPECT_QUICHE_BUG(TrackNamespace({big_namespace}), + "Constructing a namespace that is too large."); +} + +} // namespace +} // namespace moqt::test
diff --git a/quiche/quic/moqt/moqt_relay_track_publisher.cc b/quiche/quic/moqt/moqt_relay_track_publisher.cc index ff027db..5dfd187 100644 --- a/quiche/quic/moqt/moqt_relay_track_publisher.cc +++ b/quiche/quic/moqt/moqt_relay_track_publisher.cc
@@ -17,6 +17,7 @@ #include "quiche/quic/moqt/moqt_object.h" #include "quiche/quic/moqt/moqt_publisher.h" #include "quiche/quic/moqt/moqt_session_interface.h" +#include "quiche/common/platform/api/quiche_bug_tracker.h" #include "quiche/common/platform/api/quiche_logging.h" #include "quiche/common/quiche_callbacks.h" #include "quiche/common/quiche_mem_slice.h"
diff --git a/quiche/quic/moqt/relay_namespace_tree.h b/quiche/quic/moqt/relay_namespace_tree.h index 5047418..bdee98e 100644 --- a/quiche/quic/moqt/relay_namespace_tree.h +++ b/quiche/quic/moqt/relay_namespace_tree.h
@@ -16,6 +16,7 @@ #include "absl/strings/string_view.h" #include "quiche/quic/moqt/moqt_messages.h" #include "quiche/quic/moqt/moqt_session_interface.h" +#include "quiche/common/platform/api/quiche_bug_tracker.h" #include "quiche/common/quiche_weak_ptr.h" namespace moqt {