1 // Copyright (c) 2023 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Structured data for message types in draft-ietf-moq-transport-02.
6
7 #ifndef QUICHE_QUIC_MOQT_MOQT_MESSAGES_H_
8 #define QUICHE_QUIC_MOQT_MOQT_MESSAGES_H_
9
10 #include <cstddef>
11 #include <cstdint>
12 #include <optional>
13 #include <string>
14 #include <utility>
15 #include <vector>
16
17 #include "absl/strings/string_view.h"
18 #include "quiche/quic/core/quic_time.h"
19 #include "quiche/quic/core/quic_types.h"
20 #include "quiche/quic/core/quic_versions.h"
21 #include "quiche/common/platform/api/quiche_export.h"
22
23 namespace moqt {
24
GetMoqtSupportedQuicVersions()25 inline constexpr quic::ParsedQuicVersionVector GetMoqtSupportedQuicVersions() {
26 return quic::ParsedQuicVersionVector{quic::ParsedQuicVersion::RFCv1()};
27 }
28
29 enum class MoqtVersion : uint64_t {
30 kDraft03 = 0xff000003,
31 kUnrecognizedVersionForTests = 0xfe0000ff,
32 };
33
34 struct QUICHE_EXPORT MoqtSessionParameters {
35 // TODO: support multiple versions.
36 // TODO: support roles other than PubSub.
37 MoqtVersion version;
38 quic::Perspective perspective;
39 bool using_webtrans;
40 std::string path;
41 bool deliver_partial_objects;
42 };
43
44 // The maximum length of a message, excluding any OBJECT payload. This prevents
45 // DoS attack via forcing the parser to buffer a large message (OBJECT payloads
46 // are not buffered by the parser).
47 inline constexpr size_t kMaxMessageHeaderSize = 2048;
48
49 enum class QUICHE_EXPORT MoqtMessageType : uint64_t {
50 kObjectStream = 0x00,
51 kObjectDatagram = 0x01,
52 kSubscribe = 0x03,
53 kSubscribeOk = 0x04,
54 kSubscribeError = 0x05,
55 kAnnounce = 0x06,
56 kAnnounceOk = 0x7,
57 kAnnounceError = 0x08,
58 kUnannounce = 0x09,
59 kUnsubscribe = 0x0a,
60 kSubscribeDone = 0x0b,
61 kGoAway = 0x10,
62 kClientSetup = 0x40,
63 kServerSetup = 0x41,
64 kStreamHeaderTrack = 0x50,
65 kStreamHeaderGroup = 0x51,
66 };
67
68 enum class QUICHE_EXPORT MoqtError : uint64_t {
69 kNoError = 0x0,
70 kInternalError = 0x1,
71 kUnauthorized = 0x2,
72 kProtocolViolation = 0x3,
73 kDuplicateTrackAlias = 0x4,
74 kParameterLengthMismatch = 0x5,
75 kGoawayTimeout = 0x10,
76 };
77
78 enum class QUICHE_EXPORT MoqtRole : uint64_t {
79 kPublisher = 0x1,
80 kSubscriber = 0x2,
81 kPubSub = 0x3,
82 kRoleMax = 0x3,
83 };
84
85 enum class QUICHE_EXPORT MoqtSetupParameter : uint64_t {
86 kRole = 0x0,
87 kPath = 0x1,
88 };
89
90 enum class QUICHE_EXPORT MoqtTrackRequestParameter : uint64_t {
91 kAuthorizationInfo = 0x2,
92 };
93
94 // TODO: those are non-standard; add standard error codes once those exist, see
95 // <https://github.com/moq-wg/moq-transport/issues/393>.
96 enum class MoqtAnnounceErrorCode : uint64_t {
97 kInternalError = 0,
98 kAnnounceNotSupported = 1,
99 };
100
101 struct MoqtAnnounceErrorReason {
102 MoqtAnnounceErrorCode error_code;
103 std::string reason_phrase;
104 };
105
106 struct FullTrackName {
107 std::string track_namespace;
108 std::string track_name;
FullTrackNameFullTrackName109 FullTrackName(absl::string_view ns, absl::string_view name)
110 : track_namespace(ns), track_name(name) {}
111 bool operator==(const FullTrackName& other) const {
112 return track_namespace == other.track_namespace &&
113 track_name == other.track_name;
114 }
115 bool operator<(const FullTrackName& other) const {
116 return track_namespace < other.track_namespace ||
117 (track_namespace == other.track_namespace &&
118 track_name < other.track_name);
119 }
120 FullTrackName& operator=(FullTrackName other) {
121 track_namespace = other.track_namespace;
122 track_name = other.track_name;
123 return *this;
124 }
125 template <typename H>
126 friend H AbslHashValue(H h, const FullTrackName& m);
127 };
128
129 template <typename H>
AbslHashValue(H h,const FullTrackName & m)130 H AbslHashValue(H h, const FullTrackName& m) {
131 return H::combine(std::move(h), m.track_namespace, m.track_name);
132 }
133
134 // These are absolute sequence numbers.
135 struct FullSequence {
136 uint64_t group = 0;
137 uint64_t object = 0;
138 bool operator==(const FullSequence& other) const {
139 return group == other.group && object == other.object;
140 }
141 bool operator<(const FullSequence& other) const {
142 return group < other.group ||
143 (group == other.group && object < other.object);
144 }
145 bool operator<=(const FullSequence& other) const {
146 return (group < other.group ||
147 (group == other.group && object <= other.object));
148 }
149 FullSequence& operator=(FullSequence other) {
150 group = other.group;
151 object = other.object;
152 return *this;
153 }
154 template <typename H>
155 friend H AbslHashValue(H h, const FullSequence& m);
156 };
157
158 template <typename H>
AbslHashValue(H h,const FullSequence & m)159 H AbslHashValue(H h, const FullSequence& m) {
160 return H::combine(std::move(h), m.group, m.object);
161 }
162
163 struct QUICHE_EXPORT MoqtClientSetup {
164 std::vector<MoqtVersion> supported_versions;
165 std::optional<MoqtRole> role;
166 std::optional<std::string> path;
167 };
168
169 struct QUICHE_EXPORT MoqtServerSetup {
170 MoqtVersion selected_version;
171 std::optional<MoqtRole> role;
172 };
173
174 // These codes do not appear on the wire.
175 enum class QUICHE_EXPORT MoqtForwardingPreference : uint8_t {
176 kTrack = 0x0,
177 kGroup = 0x1,
178 kObject = 0x2,
179 kDatagram = 0x3,
180 };
181
182 // The data contained in every Object message, although the message type
183 // implies some of the values. |payload_length| has no value if the length
184 // is unknown (because it runs to the end of the stream.)
185 struct QUICHE_EXPORT MoqtObject {
186 uint64_t subscribe_id;
187 uint64_t track_alias;
188 uint64_t group_id;
189 uint64_t object_id;
190 uint64_t object_send_order;
191 MoqtForwardingPreference forwarding_preference;
192 std::optional<uint64_t> payload_length;
193 };
194
195 enum class QUICHE_EXPORT MoqtSubscribeLocationMode : uint64_t {
196 kNone = 0x0,
197 kAbsolute = 0x1,
198 kRelativePrevious = 0x2,
199 kRelativeNext = 0x3,
200 };
201
202 // kNone: std::optional<MoqtSubscribeLocation> is nullopt.
203 // kAbsolute: absolute = true
204 // kRelativePrevious: absolute is false; relative_value is negative
205 // kRelativeNext: absolute is true; relative_value is positive
206 struct QUICHE_EXPORT MoqtSubscribeLocation {
MoqtSubscribeLocationMoqtSubscribeLocation207 MoqtSubscribeLocation(bool is_absolute, uint64_t abs)
208 : absolute(is_absolute), absolute_value(abs) {}
MoqtSubscribeLocationMoqtSubscribeLocation209 MoqtSubscribeLocation(bool is_absolute, int64_t rel)
210 : absolute(is_absolute), relative_value(rel) {}
211 bool absolute;
212 union {
213 uint64_t absolute_value;
214 int64_t relative_value;
215 };
216 bool operator==(const MoqtSubscribeLocation& other) const {
217 return absolute == other.absolute &&
218 ((absolute && absolute_value == other.absolute_value) ||
219 (!absolute && relative_value == other.relative_value));
220 }
221 };
222
GetModeForSubscribeLocation(const std::optional<MoqtSubscribeLocation> & location)223 inline MoqtSubscribeLocationMode GetModeForSubscribeLocation(
224 const std::optional<MoqtSubscribeLocation>& location) {
225 if (!location.has_value()) {
226 return MoqtSubscribeLocationMode::kNone;
227 }
228 if (location->absolute) {
229 return MoqtSubscribeLocationMode::kAbsolute;
230 }
231 return location->relative_value >= 0
232 ? MoqtSubscribeLocationMode::kRelativeNext
233 : MoqtSubscribeLocationMode::kRelativePrevious;
234 }
235
236 struct QUICHE_EXPORT MoqtSubscribe {
237 uint64_t subscribe_id;
238 uint64_t track_alias;
239 std::string track_namespace;
240 std::string track_name;
241 // If the mode is kNone, the these are std::nullopt.
242 std::optional<MoqtSubscribeLocation> start_group;
243 std::optional<MoqtSubscribeLocation> start_object;
244 std::optional<MoqtSubscribeLocation> end_group;
245 std::optional<MoqtSubscribeLocation> end_object;
246 std::optional<std::string> authorization_info;
247 };
248
249 struct QUICHE_EXPORT MoqtSubscribeOk {
250 uint64_t subscribe_id;
251 // The message uses ms, but expires is in us.
252 quic::QuicTimeDelta expires = quic::QuicTimeDelta::FromMilliseconds(0);
253 // If ContextExists on the wire is zero, largest_id has no value.
254 std::optional<FullSequence> largest_id;
255 };
256
257 enum class QUICHE_EXPORT SubscribeErrorCode : uint64_t {
258 kInternalError = 0x0,
259 kInvalidRange = 0x1,
260 kRetryTrackAlias = 0x2,
261 };
262
263 struct QUICHE_EXPORT MoqtSubscribeError {
264 uint64_t subscribe_id;
265 SubscribeErrorCode error_code;
266 std::string reason_phrase;
267 uint64_t track_alias;
268 };
269
270 struct QUICHE_EXPORT MoqtUnsubscribe {
271 uint64_t subscribe_id;
272 };
273
274 enum class QUICHE_EXPORT SubscribeDoneCode : uint64_t {
275 kUnsubscribed = 0x0,
276 kInternalError = 0x1,
277 kUnauthorized = 0x2,
278 kTrackEnded = 0x3,
279 kSubscriptionEnded = 0x4,
280 kGoingAway = 0x5,
281 kExpired = 0x6,
282 };
283
284 struct QUICHE_EXPORT MoqtSubscribeDone {
285 uint64_t subscribe_id;
286 uint64_t status_code;
287 std::string reason_phrase;
288 std::optional<FullSequence> final_id;
289 };
290
291 struct QUICHE_EXPORT MoqtAnnounce {
292 std::string track_namespace;
293 std::optional<std::string> authorization_info;
294 };
295
296 struct QUICHE_EXPORT MoqtAnnounceOk {
297 std::string track_namespace;
298 };
299
300 struct QUICHE_EXPORT MoqtAnnounceError {
301 std::string track_namespace;
302 MoqtAnnounceErrorCode error_code;
303 std::string reason_phrase;
304 };
305
306 struct QUICHE_EXPORT MoqtUnannounce {
307 std::string track_namespace;
308 };
309
310 struct QUICHE_EXPORT MoqtGoAway {
311 std::string new_session_uri;
312 };
313
314 std::string MoqtMessageTypeToString(MoqtMessageType message_type);
315
316 std::string MoqtForwardingPreferenceToString(
317 MoqtForwardingPreference preference);
318
319 MoqtForwardingPreference GetForwardingPreference(MoqtMessageType type);
320
321 MoqtMessageType GetMessageTypeForForwardingPreference(
322 MoqtForwardingPreference preference);
323
324 } // namespace moqt
325
326 #endif // QUICHE_QUIC_MOQT_MOQT_MESSAGES_H_
327