Skip to main content

moqtap_codec/draft10/
error_codes.rs

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