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}