moqtap_codec/draft08/error_codes.rs
1//! Error, status and termination code registries defined by
2//! draft-ietf-moq-transport-08.
3//!
4//! One enum per registry. Draft-08 predates the IANA registry tables of the
5//! later drafts: its tables carry only a code and a reason phrase, and assign no
6//! symbolic ALLCAPS names. Each variant name below is therefore UpperCamelCase
7//! derived from the reason phrase, not taken from the draft, and every variant's
8//! doc comment quotes that phrase verbatim in backticks so the mapping back to
9//! the draft stays checkable. Where the draft also defines a code in prose, that
10//! prose follows the phrase.
11//!
12//! `from_u64` returns `None` for any value the draft does not assign. A peer may
13//! legitimately send a code from a later draft or a private extension, and an
14//! unrecognized code must not be a decode failure at this layer.
15//!
16//! Draft-08 assigns no reserved-for-greasing ranges in these registries; every
17//! row is a single code point.
18//!
19//! Codes that share a spelling across registries do not always share a meaning,
20//! and draft-08 spellings do not always carry over to later drafts.
21//! `Unauthorized` is the clearest case: in
22//! [`SessionErrorCode`](crate::draft08::error_codes::SessionErrorCode) it
23//! reports a breach of a pre-negotiated agreement, which is not what the
24//! identically named code means in the request-scoped registries or in later
25//! drafts.
26
27/// Session termination codes (draft-08, section 3.5 "Termination").
28///
29/// Carried in the QUIC CONNECTION_CLOSE frame or the WebTransport
30/// CLOSE_WEBTRANSPORT_SESSION capsule. The draft assigns 0x0 through 0x6 and
31/// 0x10 through 0x12; 0x7 through 0xF are unassigned.
32///
33/// Section 3.5 defines every code in this registry in prose except
34/// `Parameter Length Mismatch` (0x5), which the table assigns but the prose
35/// list skips; that variant carries the reason phrase alone.
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37#[repr(u64)]
38pub enum SessionErrorCode {
39 /// `No Error` — The session is being terminated without an error.
40 NoError = 0x0,
41 /// `Internal Error` — An implementation specific error occurred.
42 InternalError = 0x1,
43 /// `Unauthorized` — The endpoint breached an agreement, which MAY have been
44 /// pre-negotiated by the application.
45 ///
46 /// This is a breach of agreement, not a failed authentication or a refused
47 /// credential.
48 Unauthorized = 0x2,
49 /// `Protocol Violation` — The remote endpoint performed an action that was
50 /// disallowed by the specification.
51 ProtocolViolation = 0x3,
52 /// `Duplicate Track Alias` — The endpoint attempted to use a Track Alias
53 /// that was already in use.
54 DuplicateTrackAlias = 0x4,
55 /// `Parameter Length Mismatch`
56 ///
57 /// Section 3.5 assigns this code in its table but does not define it in
58 /// prose, so the reason phrase above is the whole of the draft's
59 /// description.
60 ParameterLengthMismatch = 0x5,
61 /// `Too Many Subscribes` — The session was closed because the subscriber
62 /// used a Subscribe ID equal or larger than the current Maximum Subscribe
63 /// ID.
64 ///
65 /// This reports a Subscribe ID above the advertised maximum, not a count of
66 /// concurrent subscriptions.
67 TooManySubscribes = 0x6,
68 /// `GOAWAY Timeout` — The session was closed because the peer took too long
69 /// to close the session in response to a GOAWAY (Section 7.3) message. See
70 /// session migration (Section 3.6).
71 GoawayTimeout = 0x10,
72 /// `Control Message Timeout` — The session was closed because the peer took
73 /// too long to respond to a control message.
74 ControlMessageTimeout = 0x11,
75 /// `Data Stream Timeout` — The session was closed because the peer took too
76 /// long to send data expected on an open Data Stream (Section 8). This
77 /// includes fields of a stream header or an object header within a data
78 /// stream.
79 ///
80 /// Closing the session is only one of the responses the draft permits here.
81 /// Section 3.5: an endpoint that times out waiting for a new object header
82 /// on an open subgroup stream MAY send STOP_SENDING on that stream,
83 /// terminate the subscription, or close the session with an error.
84 DataStreamTimeout = 0x12,
85}
86
87/// ANNOUNCE_ERROR codes (draft-08, section 7.10 "ANNOUNCE_ERROR").
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89#[repr(u64)]
90pub enum AnnounceErrorCode {
91 /// `Internal Error`
92 InternalError = 0x0,
93 /// `Unauthorized`
94 Unauthorized = 0x1,
95 /// `Timeout`
96 Timeout = 0x2,
97 /// `Not Supported`
98 NotSupported = 0x3,
99 /// `Uninterested`
100 Uninterested = 0x4,
101}
102
103/// SUBSCRIBE_ERROR codes (draft-08, section 7.16 "SUBSCRIBE_ERROR").
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105#[repr(u64)]
106pub enum SubscribeErrorCode {
107 /// `Internal Error`
108 InternalError = 0x0,
109 /// `Unauthorized`
110 Unauthorized = 0x1,
111 /// `Timeout`
112 Timeout = 0x2,
113 /// `Not Supported`
114 NotSupported = 0x3,
115 /// `Track Does Not Exist`
116 TrackDoesNotExist = 0x4,
117 /// `Invalid Range`
118 InvalidRange = 0x5,
119 /// `Retry Track Alias`
120 ///
121 /// Not a plain failure. Section 7.16 states that when the Error Code is
122 /// `Retry Track Alias`, the subscriber SHOULD re-issue the SUBSCRIBE using
123 /// the Track Alias carried in the SUBSCRIBE_ERROR message instead of the one
124 /// it chose; if that Track Alias is already in use, the subscriber MUST
125 /// close the connection with `Duplicate Track Alias` (Section 3.5).
126 RetryTrackAlias = 0x6,
127}
128
129/// FETCH_ERROR codes (draft-08, section 7.18 "FETCH_ERROR").
130///
131/// The same first six code points as [`SubscribeErrorCode`]; this registry has
132/// no equivalent of `Retry Track Alias` (0x6).
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134#[repr(u64)]
135pub enum FetchErrorCode {
136 /// `Internal Error`
137 InternalError = 0x0,
138 /// `Unauthorized`
139 Unauthorized = 0x1,
140 /// `Timeout`
141 Timeout = 0x2,
142 /// `Not Supported`
143 NotSupported = 0x3,
144 /// `Track Does Not Exist`
145 TrackDoesNotExist = 0x4,
146 /// `Invalid Range`
147 InvalidRange = 0x5,
148}
149
150/// SUBSCRIBE_DONE status codes (draft-08, section 7.19 "SUBSCRIBE_DONE").
151///
152/// The draft calls these status codes rather than error codes. Section 7.19:
153/// "The Status Code indicates why the subscription ended, and whether it was an
154/// error." Both outcomes appear in the registry, so receiving one of these does
155/// not by itself mean the subscription failed.
156///
157/// Note that 0x2 is `Track Ended` here, whereas the four request-scoped
158/// registries in this draft assign `Timeout` to 0x2.
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160#[repr(u64)]
161pub enum SubscribeDoneStatusCode {
162 /// `Internal Error`
163 InternalError = 0x0,
164 /// `Unauthorized`
165 Unauthorized = 0x1,
166 /// `Track Ended`
167 TrackEnded = 0x2,
168 /// `Subscription Ended`
169 SubscriptionEnded = 0x3,
170 /// `Going Away`
171 GoingAway = 0x4,
172 /// `Expired`
173 Expired = 0x5,
174 /// `Too Far Behind`
175 TooFarBehind = 0x6,
176}
177
178/// SUBSCRIBE_ANNOUNCES_ERROR codes (draft-08, section 7.26
179/// "SUBSCRIBE_ANNOUNCES_ERROR").
180///
181/// This registry agrees with [`AnnounceErrorCode`] on 0x0 through 0x3 and then
182/// diverges: 0x4 is `Namespace Prefix Unknown` here and `Uninterested` there.
183/// The request-scoped registries in this draft are kept as separate types
184/// because 0x4 carries three different meanings across them.
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186#[repr(u64)]
187pub enum SubscribeAnnouncesErrorCode {
188 /// `Internal Error`
189 InternalError = 0x0,
190 /// `Unauthorized`
191 Unauthorized = 0x1,
192 /// `Timeout`
193 Timeout = 0x2,
194 /// `Not Supported`
195 NotSupported = 0x3,
196 /// `Namespace Prefix Unknown`
197 NamespacePrefixUnknown = 0x4,
198}
199
200impl SessionErrorCode {
201 /// Every session termination code draft-08 assigns, in ascending wire order.
202 ///
203 /// This is the set [`Self::from_u64`] accepts, written out so that it can
204 /// be enumerated: nothing can iterate an enum's variants, so a caller that
205 /// wants the registry has to be handed it. Writing it down is also what
206 /// lets a test state its claims about the registry itself rather than about
207 /// the range some sweep happens to reach.
208 pub const ALL: &[SessionErrorCode] = &[
209 SessionErrorCode::NoError,
210 SessionErrorCode::InternalError,
211 SessionErrorCode::Unauthorized,
212 SessionErrorCode::ProtocolViolation,
213 SessionErrorCode::DuplicateTrackAlias,
214 SessionErrorCode::ParameterLengthMismatch,
215 SessionErrorCode::TooManySubscribes,
216 SessionErrorCode::GoawayTimeout,
217 SessionErrorCode::ControlMessageTimeout,
218 SessionErrorCode::DataStreamTimeout,
219 ];
220
221 /// Convert a raw u64 to a `SessionErrorCode`, if valid.
222 pub fn from_u64(v: u64) -> Option<Self> {
223 match v {
224 0x0 => Some(SessionErrorCode::NoError),
225 0x1 => Some(SessionErrorCode::InternalError),
226 0x2 => Some(SessionErrorCode::Unauthorized),
227 0x3 => Some(SessionErrorCode::ProtocolViolation),
228 0x4 => Some(SessionErrorCode::DuplicateTrackAlias),
229 0x5 => Some(SessionErrorCode::ParameterLengthMismatch),
230 0x6 => Some(SessionErrorCode::TooManySubscribes),
231 0x10 => Some(SessionErrorCode::GoawayTimeout),
232 0x11 => Some(SessionErrorCode::ControlMessageTimeout),
233 0x12 => Some(SessionErrorCode::DataStreamTimeout),
234 _ => None,
235 }
236 }
237
238 /// Return the raw u64 value of this error code.
239 pub fn as_u64(self) -> u64 {
240 self as u64
241 }
242}
243
244impl AnnounceErrorCode {
245 /// Every ANNOUNCE_ERROR code draft-08 assigns, in ascending wire order.
246 ///
247 /// This is the set [`Self::from_u64`] accepts, written out so that it can
248 /// be enumerated: nothing can iterate an enum's variants, so a caller that
249 /// wants the registry has to be handed it. Writing it down is also what
250 /// lets a test state its claims about the registry itself rather than about
251 /// the range some sweep happens to reach.
252 pub const ALL: &[AnnounceErrorCode] = &[
253 AnnounceErrorCode::InternalError,
254 AnnounceErrorCode::Unauthorized,
255 AnnounceErrorCode::Timeout,
256 AnnounceErrorCode::NotSupported,
257 AnnounceErrorCode::Uninterested,
258 ];
259
260 /// Convert a raw u64 to an `AnnounceErrorCode`, if valid.
261 pub fn from_u64(v: u64) -> Option<Self> {
262 match v {
263 0x0 => Some(AnnounceErrorCode::InternalError),
264 0x1 => Some(AnnounceErrorCode::Unauthorized),
265 0x2 => Some(AnnounceErrorCode::Timeout),
266 0x3 => Some(AnnounceErrorCode::NotSupported),
267 0x4 => Some(AnnounceErrorCode::Uninterested),
268 _ => None,
269 }
270 }
271
272 /// Return the raw u64 value of this error code.
273 pub fn as_u64(self) -> u64 {
274 self as u64
275 }
276}
277
278impl SubscribeErrorCode {
279 /// Every SUBSCRIBE_ERROR code draft-08 assigns, in ascending wire order.
280 ///
281 /// This is the set [`Self::from_u64`] accepts, written out so that it can
282 /// be enumerated: nothing can iterate an enum's variants, so a caller that
283 /// wants the registry has to be handed it. Writing it down is also what
284 /// lets a test state its claims about the registry itself rather than about
285 /// the range some sweep happens to reach.
286 pub const ALL: &[SubscribeErrorCode] = &[
287 SubscribeErrorCode::InternalError,
288 SubscribeErrorCode::Unauthorized,
289 SubscribeErrorCode::Timeout,
290 SubscribeErrorCode::NotSupported,
291 SubscribeErrorCode::TrackDoesNotExist,
292 SubscribeErrorCode::InvalidRange,
293 SubscribeErrorCode::RetryTrackAlias,
294 ];
295
296 /// Convert a raw u64 to a `SubscribeErrorCode`, if valid.
297 pub fn from_u64(v: u64) -> Option<Self> {
298 match v {
299 0x0 => Some(SubscribeErrorCode::InternalError),
300 0x1 => Some(SubscribeErrorCode::Unauthorized),
301 0x2 => Some(SubscribeErrorCode::Timeout),
302 0x3 => Some(SubscribeErrorCode::NotSupported),
303 0x4 => Some(SubscribeErrorCode::TrackDoesNotExist),
304 0x5 => Some(SubscribeErrorCode::InvalidRange),
305 0x6 => Some(SubscribeErrorCode::RetryTrackAlias),
306 _ => None,
307 }
308 }
309
310 /// Return the raw u64 value of this error code.
311 pub fn as_u64(self) -> u64 {
312 self as u64
313 }
314}
315
316impl FetchErrorCode {
317 /// Every FETCH_ERROR code draft-08 assigns, in ascending wire order.
318 ///
319 /// This is the set [`Self::from_u64`] accepts, written out so that it can
320 /// be enumerated: nothing can iterate an enum's variants, so a caller that
321 /// wants the registry has to be handed it. Writing it down is also what
322 /// lets a test state its claims about the registry itself rather than about
323 /// the range some sweep happens to reach.
324 pub const ALL: &[FetchErrorCode] = &[
325 FetchErrorCode::InternalError,
326 FetchErrorCode::Unauthorized,
327 FetchErrorCode::Timeout,
328 FetchErrorCode::NotSupported,
329 FetchErrorCode::TrackDoesNotExist,
330 FetchErrorCode::InvalidRange,
331 ];
332
333 /// Convert a raw u64 to a `FetchErrorCode`, if valid.
334 pub fn from_u64(v: u64) -> Option<Self> {
335 match v {
336 0x0 => Some(FetchErrorCode::InternalError),
337 0x1 => Some(FetchErrorCode::Unauthorized),
338 0x2 => Some(FetchErrorCode::Timeout),
339 0x3 => Some(FetchErrorCode::NotSupported),
340 0x4 => Some(FetchErrorCode::TrackDoesNotExist),
341 0x5 => Some(FetchErrorCode::InvalidRange),
342 _ => None,
343 }
344 }
345
346 /// Return the raw u64 value of this error code.
347 pub fn as_u64(self) -> u64 {
348 self as u64
349 }
350}
351
352impl SubscribeDoneStatusCode {
353 /// Every SUBSCRIBE_DONE status code draft-08 assigns, in ascending wire order.
354 ///
355 /// This is the set [`Self::from_u64`] accepts, written out so that it can
356 /// be enumerated: nothing can iterate an enum's variants, so a caller that
357 /// wants the registry has to be handed it. Writing it down is also what
358 /// lets a test state its claims about the registry itself rather than about
359 /// the range some sweep happens to reach.
360 pub const ALL: &[SubscribeDoneStatusCode] = &[
361 SubscribeDoneStatusCode::InternalError,
362 SubscribeDoneStatusCode::Unauthorized,
363 SubscribeDoneStatusCode::TrackEnded,
364 SubscribeDoneStatusCode::SubscriptionEnded,
365 SubscribeDoneStatusCode::GoingAway,
366 SubscribeDoneStatusCode::Expired,
367 SubscribeDoneStatusCode::TooFarBehind,
368 ];
369
370 /// Convert a raw u64 to a `SubscribeDoneStatusCode`, if valid.
371 pub fn from_u64(v: u64) -> Option<Self> {
372 match v {
373 0x0 => Some(SubscribeDoneStatusCode::InternalError),
374 0x1 => Some(SubscribeDoneStatusCode::Unauthorized),
375 0x2 => Some(SubscribeDoneStatusCode::TrackEnded),
376 0x3 => Some(SubscribeDoneStatusCode::SubscriptionEnded),
377 0x4 => Some(SubscribeDoneStatusCode::GoingAway),
378 0x5 => Some(SubscribeDoneStatusCode::Expired),
379 0x6 => Some(SubscribeDoneStatusCode::TooFarBehind),
380 _ => None,
381 }
382 }
383
384 /// Return the raw u64 value of this status code.
385 pub fn as_u64(self) -> u64 {
386 self as u64
387 }
388}
389
390impl SubscribeAnnouncesErrorCode {
391 /// Every SUBSCRIBE_ANNOUNCES_ERROR code draft-08 assigns, in ascending wire order.
392 ///
393 /// This is the set [`Self::from_u64`] accepts, written out so that it can
394 /// be enumerated: nothing can iterate an enum's variants, so a caller that
395 /// wants the registry has to be handed it. Writing it down is also what
396 /// lets a test state its claims about the registry itself rather than about
397 /// the range some sweep happens to reach.
398 pub const ALL: &[SubscribeAnnouncesErrorCode] = &[
399 SubscribeAnnouncesErrorCode::InternalError,
400 SubscribeAnnouncesErrorCode::Unauthorized,
401 SubscribeAnnouncesErrorCode::Timeout,
402 SubscribeAnnouncesErrorCode::NotSupported,
403 SubscribeAnnouncesErrorCode::NamespacePrefixUnknown,
404 ];
405
406 /// Convert a raw u64 to a `SubscribeAnnouncesErrorCode`, if valid.
407 pub fn from_u64(v: u64) -> Option<Self> {
408 match v {
409 0x0 => Some(SubscribeAnnouncesErrorCode::InternalError),
410 0x1 => Some(SubscribeAnnouncesErrorCode::Unauthorized),
411 0x2 => Some(SubscribeAnnouncesErrorCode::Timeout),
412 0x3 => Some(SubscribeAnnouncesErrorCode::NotSupported),
413 0x4 => Some(SubscribeAnnouncesErrorCode::NamespacePrefixUnknown),
414 _ => None,
415 }
416 }
417
418 /// Return the raw u64 value of this error code.
419 pub fn as_u64(self) -> u64 {
420 self as u64
421 }
422}
423
424/// TRACK_STATUS Status Code values (draft-08, Section 7.24).
425///
426/// The draft defines these as a prose list rather than as a `Code`/`Reason`
427/// table, so they are named from the prose. It is stricter about this field
428/// than about the error registries above: the Status Code "MUST hold one of the
429/// following values" and "Any other value in the Status Code field is a
430/// malformed message", so [`TrackStatusCode::from_u64`] answering `None` is a
431/// decode failure rather than a merely unrecognised code.
432#[derive(Debug, Clone, Copy, PartialEq, Eq)]
433#[repr(u64)]
434pub enum TrackStatusCode {
435 /// The track is in progress, and subsequent fields contain the highest
436 /// group and object ID for that track.
437 InProgress = 0x00,
438 /// The track does not exist. Subsequent fields MUST be zero, and any other
439 /// value is a malformed message.
440 TrackDoesNotExist = 0x01,
441 /// The track has not yet begun. Subsequent fields MUST be zero, and any
442 /// other value is a malformed message.
443 NotYetBegun = 0x02,
444 /// The track has finished, so there is no live edge. Subsequent fields
445 /// contain the highest group and object ID known.
446 Finished = 0x03,
447 /// The publisher is a relay that cannot obtain the current track status
448 /// from upstream. Subsequent fields contain the largest group and object
449 /// ID known.
450 RelayStatusUnavailable = 0x04,
451}
452
453impl TrackStatusCode {
454 /// Convert a raw u64 to a `TrackStatusCode`, if this draft assigns it.
455 pub fn from_u64(v: u64) -> Option<Self> {
456 match v {
457 0x00 => Some(TrackStatusCode::InProgress),
458 0x01 => Some(TrackStatusCode::TrackDoesNotExist),
459 0x02 => Some(TrackStatusCode::NotYetBegun),
460 0x03 => Some(TrackStatusCode::Finished),
461 0x04 => Some(TrackStatusCode::RelayStatusUnavailable),
462 _ => None,
463 }
464 }
465
466 /// Whether this code requires the fields after it to be zero.
467 ///
468 /// Section 7.24 says of 0x01 "Subsequent fields MUST be zero, and any other
469 /// value is a malformed message", and the same of 0x02. The other three
470 /// codes describe those fields as carrying a real location, so they place
471 /// no requirement on them.
472 pub fn requires_zero_location(self) -> bool {
473 matches!(self, TrackStatusCode::TrackDoesNotExist | TrackStatusCode::NotYetBegun)
474 }
475
476 /// Return the raw u64 value of this status code.
477 pub fn as_u64(self) -> u64 {
478 self as u64
479 }
480}