Skip to main content

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}