Skip to main content

moqtap_codec/
error.rs

1/// Maximum control message payload length: 2^16 - 1 bytes.
2pub const MAX_MESSAGE_LENGTH: usize = 65535;
3/// Maximum reason phrase length: 1024 bytes.
4pub const MAX_REASON_PHRASE_LENGTH: usize = 1024;
5/// Maximum GOAWAY new session URI length: 8192 bytes.
6pub const MAX_GOAWAY_URI_LENGTH: usize = 8192;
7/// Maximum full track name length: 4096 bytes.
8pub const MAX_FULL_TRACK_NAME_LENGTH: usize = 4096;
9/// Maximum track namespace tuple size: 32 elements.
10pub const MAX_NAMESPACE_TUPLE_SIZE: usize = 32;
11
12/// Errors produced during MoQT message encoding and decoding.
13///
14/// # Adding a variant
15///
16/// This enum is deliberately **not** `#[non_exhaustive]`, so that a session-close
17/// table matching it exhaustively fails to compile until a new variant has been
18/// placed on each of the drafts — either among the rules that draft
19/// answers with a close or among the ones it names and does not. A wildcard arm
20/// would make those fourteen decisions silently, all in the direction of "no
21/// rule", and a missing arm and a deliberate exclusion look identical from
22/// inside such a table.
23#[derive(Debug, thiserror::Error, PartialEq, Eq, Clone)]
24pub enum CodecError {
25    /// Unknown or unsupported message type identifier.
26    #[error("unknown message type: 0x{0:x}")]
27    UnknownMessageType(u64),
28    /// Not enough bytes in the buffer to complete decoding.
29    #[error("insufficient bytes")]
30    UnexpectedEnd,
31    /// Control message payload exceeds [`MAX_MESSAGE_LENGTH`].
32    #[error("message too long: {0} bytes (max {MAX_MESSAGE_LENGTH})")]
33    MessageTooLong(usize),
34    /// Variable-length integer encoding/decoding error.
35    #[error("varint error: {0}")]
36    VarInt(#[from] crate::varint::VarIntError),
37    /// Key-value pair encoding/decoding error.
38    #[error("kvp error: {0}")]
39    Kvp(#[from] crate::kvp::KvpError),
40    /// A decoded field value is not valid for its type.
41    #[error("invalid field value")]
42    InvalidField,
43    /// Namespace tuple element count is outside the range the caller allows.
44    ///
45    /// The upper bound is [`MAX_NAMESPACE_TUPLE_SIZE`] everywhere. The lower
46    /// bound is per-draft, and three wordings carry two rules. Drafts 07
47    /// through 14 define a Track Namespace as "an ordered N-tuple of bytes
48    /// where N can be between 1 and 32"; drafts 15 and 16 rename the elements
49    /// to Track Namespace Fields and keep the 1; drafts 17 and later lower it
50    /// to 0. So an empty tuple is refused only through draft-16.
51    #[error("namespace tuple size {0} is not allowed here (max {MAX_NAMESPACE_TUPLE_SIZE})")]
52    InvalidNamespaceTupleSize(usize),
53    /// A Track Namespace Field was declared with a length of zero.
54    ///
55    /// Drafts 16 through 19 all carry the same sentence in Section 2.4.1: "Each
56    /// Track Namespace Field Value MUST contain at least one byte. If an
57    /// endpoint receives a Track Namespace Field with a Track Namespace Field
58    /// Length of 0, it MUST close the session with a PROTOCOL_VIOLATION."
59    #[error("track namespace field is empty; each field must contain at least one byte")]
60    EmptyNamespaceField,
61    /// Full track name exceeds [`MAX_FULL_TRACK_NAME_LENGTH`].
62    #[error("track namespace or full track name exceeds {MAX_FULL_TRACK_NAME_LENGTH} bytes")]
63    TrackNameTooLong,
64    /// A requested range ends before it starts.
65    ///
66    /// Every draft states it, in the wording its fields have at the time.
67    /// Draft-07 Section 6.4 and Section 6.7: "EndGroup and EndObject MUST
68    /// specify the same or a later object than StartGroup and StartObject".
69    /// Draft-08 Section 7.4 through draft-14 Section 9.7, for the AbsoluteRange
70    /// filter: "End Group MUST specify the same or a larger Group than
71    /// specified in Start". Draft-12 Section 8.16 onwards, for FETCH: "End
72    /// Location MUST specify the same or a larger Location than Start
73    /// Location", which drafts 14 and later qualify with "for Standalone and
74    /// Absolute Joining Fetches".
75    ///
76    /// The two ends are reported as they appear on the wire, before the
77    /// draft's plus-one adjustments are undone, so the numbers here are the
78    /// ones a peer would read out of the frame.
79    #[error("range from group {0} object {1} ends at group {2} object {3}, which is earlier")]
80    InvalidRange(u64, u64, u64, u64),
81    /// A parameter's value is not the shape the parameter's own type implies.
82    ///
83    /// Drafts 07 through 10 state it once each, in the section that gives the
84    /// Parameter format: "If a receiver understands a parameter type, and the
85    /// parameter length implied by that type does not match the Parameter Length
86    /// field, the receiver MUST terminate the session with error code 'Parameter
87    /// Length Mismatch'." Each of those drafts assigns that code its own number
88    /// in the session termination registry. Drafts 11 and later drop the
89    /// sentence along with the Parameter framing it describes.
90    ///
91    /// The parameter reported is the one whose value disagreed with its type.
92    /// Which namespace the type is read in matters: a setup 0x02 is a
93    /// MAX_SUBSCRIBE_ID integer and a version-specific 0x02 is an
94    /// AUTHORIZATION INFO string.
95    #[error("parameter type {0} carries a value of the wrong length for its type")]
96    ParameterLengthMismatch(u64),
97    /// A key-value pair's value is not the serialization its own Type defines.
98    ///
99    /// Drafts 11 through 19 state it once each, in the section that gives the
100    /// Key-Value-Pair format. Drafts 14 and 15 word it: "If a receiver
101    /// understands a Type, and the following Value or Length/Value does not
102    /// match the serialization defined by that Type, the receiver MUST
103    /// terminate the session with error code KEY_VALUE_FORMATTING_ERROR".
104    /// Drafts 16 through 19 say close where those two say terminate, and
105    /// drafts 11 through 13 spell the code 'Key-Value Formatting Error'. Every
106    /// one of the nine assigns it a number in the session termination
107    /// registry.
108    ///
109    /// This is the successor to [`CodecError::ParameterLengthMismatch`], which
110    /// drafts 07 through 10 state about the Parameter framing they had instead.
111    /// The two never overlap: no draft states both, and the earlier rule is
112    /// about a declared length disagreeing with a type while this one is about
113    /// the bytes themselves.
114    ///
115    /// The rule is conditional on understanding the Type, so it reaches only the
116    /// types a draft defines a serialization for. A parameter this codec cannot
117    /// name carries bytes no rule here describes, and refusing it would close
118    /// sessions over extensions the drafts leave room for.
119    ///
120    /// `key` is the parameter type as the frame spelled it, in the namespace it
121    /// was read in — AUTHORIZATION TOKEN is 0x01 on draft-11 and 0x03 on drafts
122    /// 12 through 19, and a setup 0x01 is a PATH on draft-11 rather than a token
123    /// at all. `detail` says which way the value failed to match, because the
124    /// serialization is a structure rather than a length and "malformed" alone
125    /// leaves the reader to re-derive it.
126    #[error(
127        "key-value pair of type {key} does not match the serialization that type defines: {detail}"
128    )]
129    KeyValueFormatting {
130        /// The parameter type whose value did not match, as the frame spelled
131        /// it.
132        key: u64,
133        /// How the value failed to match the serialization.
134        detail: &'static str,
135    },
136    /// A control message carried a Message Parameter whose type its draft does
137    /// not define.
138    ///
139    /// Drafts 16, 17, 18 and 19 state it in the paragraph that introduces
140    /// parameters: "All Message Parameters MUST be defined in the negotiated
141    /// version of MOQT or negotiated via Setup Options. An endpoint that
142    /// receives an unknown Message Parameter MUST close the session with
143    /// PROTOCOL_VIOLATION." Drafts 17 and later add the reasoning — "Because the
144    /// receiver has to understand every Message Parameter, there is no need for
145    /// a mechanism to skip unknown parameters" — and draft-19 draws the
146    /// consequence for the framing: "Because unknown parameters cannot be
147    /// skipped, the block is bounded by a parameter count rather than a length."
148    /// Drafts 07 through 15 are the other way round and this must not reach
149    /// them. They say "Receivers MUST allow duplicates of unknown parameters",
150    /// which presumes unknown parameters arrive and are carried; refusing one
151    /// there would close a session over an extension those drafts leave room
152    /// for. Draft-16 is where the sentence narrows to *unknown **Setup**
153    /// Parameters*, in the same paragraph that adds the close — one edit, both
154    /// halves.
155    ///
156    /// Setup Parameters keep the older behaviour on every draft, which is why
157    /// this is about one namespace and not both. Drafts 16 through 19 all say a
158    /// receiver ignores an unrecognised Setup Option or Setup Parameter, so a
159    /// type unknown in that namespace is carried and only a type unknown in the
160    /// message namespace ends the session.
161    #[error("message parameter type {0} is not one this draft defines")]
162    UnknownMessageParameter(u64),
163    /// A Message Parameter appeared in a message type its own definition does
164    /// not name.
165    ///
166    /// Drafts 17, 18 and 19 only, and the split is the whole reason this is a
167    /// variant rather than a shared rule. All the drafts state the first
168    /// half the same way and ten of them state the opposite consequence.
169    /// Draft-19 Section 10.2.1, draft-18 Section 10.2.1 and draft-17 Section
170    /// 9.3.1: "Each Message Parameter definition indicates the message types in
171    /// which it can appear. If it appears in some other type of message, the
172    /// receiving endpoint MUST close the connection with a PROTOCOL_VIOLATION."
173    /// Draft-16 Section 9.2.2 and, under the older name Version Specific
174    /// Parameters, drafts 07 through 15, end the same sentence "it MUST be
175    /// ignored". Raising this on any of those ten would close a session over
176    /// traffic they oblige an endpoint to tolerate, so the decoder never does.
177    ///
178    /// What each draft's own text settles, and what it leaves open:
179    ///
180    /// * A parameter's scope is the set of message types named as the
181    ///   destination of its "MAY appear in" sentence, with trailing and
182    ///   parenthesised qualifiers read as describing one of those destinations
183    ///   rather than adding another. Draft-17's LARGEST_OBJECT "MAY appear in
184    ///   SUBSCRIBE_OK, PUBLISH or in REQUEST_OK (in response to REQUEST_UPDATE
185    ///   or TRACK_STATUS)" scopes three message types, not five, which is how
186    ///   drafts 18 and 19 write the same rule once the responses have names of
187    ///   their own.
188    /// * Half the response names are one wire type. Draft-19 Section 10.5:
189    ///   "This document uses the shorthand PUBLISH_OK, REQUEST_UPDATE_OK,
190    ///   TRACK_STATUS_OK, SUBSCRIBE_NAMESPACE_OK, and PUBLISH_NAMESPACE_OK to
191    ///   refer to a REQUEST_OK sent in response to the corresponding request
192    ///   type." Which one a given REQUEST_OK is depends on the request its
193    ///   Request ID answers, which is session state and not in the frame, so
194    ///   every name that resolves to REQUEST_OK widens the same set and a
195    ///   REQUEST_OK is held only to their union.
196    ///
197    /// Both readings err toward carrying the parameter, which is the direction
198    /// a rule that ends sessions should be wrong in.
199    ///
200    /// `key` is the absolute parameter type after any delta encoding is
201    /// resolved, and `message_type` is the type id off the wire, so a log names
202    /// the pair the sender actually wrote.
203    #[error("message parameter type {key} may not appear in message type {message_type}")]
204    ParameterOutOfScope {
205        /// The parameter type that was out of scope, absolute.
206        key: u64,
207        /// The message type it arrived in, as its id on the wire.
208        message_type: u64,
209    },
210    /// A Message Parameter carried a value outside the range its type allows.
211    ///
212    /// Drafts 15 through 19 give several parameters a range and answer anything
213    /// outside it with a close. GROUP_ORDER: "The allowed values are Ascending
214    /// (0x1) or Descending (0x2). If an endpoint receives a value outside this
215    /// range, it MUST close the session with PROTOCOL_VIOLATION." FORWARD says
216    /// the same of 0 and 1. Drafts 15 and 16 add SUBSCRIBER_PRIORITY — "The
217    /// range is restricted to 0-255" — which drafts 17 and later do not need,
218    /// having made the parameter a uint8 so that nothing outside the range can
219    /// be spelled. Draft-15 alone carries DYNAMIC_GROUPS as a Message Parameter,
220    /// where "Values larger than 1 are a Protocol Violation"; draft-16 moved it
221    /// into the extension header namespace, which this codec carries as opaque
222    /// bytes.
223    ///
224    /// Not draft-15's PUBLISHER_PRIORITY. It says "Priorities above 255 are
225    /// invalid" and stops there, naming no consequence, where every parameter
226    /// above states one in the next clause. The contrast is within one section,
227    /// so the omission is the draft's and not an oversight to be read past.
228    ///
229    /// Nor the Group Order and Forward *fields* of drafts 07 through 14, which
230    /// are a different serialization and are answered separately — Forward by
231    /// [`CodecError::InvalidForward`], Group Order by nothing, for the reason
232    /// given there.
233    ///
234    /// `key` is the absolute parameter type, after any delta encoding is
235    /// resolved, so a log names the type the sender meant rather than the
236    /// increment it wrote.
237    #[error(
238        "message parameter type {key} carries value {value}, which is outside the range it allows"
239    )]
240    ParameterValueOutOfRange {
241        /// The parameter type whose value was out of range, absolute.
242        key: u64,
243        /// The value as it arrived.
244        value: u64,
245    },
246    /// A Track Extension or Track Property carried a value outside the range its
247    /// type allows.
248    ///
249    /// A separate namespace from [`CodecError::ParameterValueOutOfRange`], and
250    /// separate for a reason rather than for tidiness: the two registries assign
251    /// the same numbers to different things. Type 0x22 is the GROUP_ORDER
252    /// Message Parameter and it is also DEFAULT_PUBLISHER_GROUP_ORDER, which is
253    /// a property of the track rather than a preference expressed by one
254    /// subscriber. Type 0x30 is DYNAMIC_GROUPS in the second namespace and is
255    /// unassigned in the first from draft-16 on. A single variant covering both
256    /// would name a type without saying which table to read it in.
257    ///
258    /// Draft-16 states the rules of the extension header namespace and drafts
259    /// 17, 18 and 19 restate them of the Track Property namespace that replaced
260    /// it. Two types restrict their range and answer anything outside it with a
261    /// close. DEFAULT_PUBLISHER_GROUP_ORDER: "The allowed values are Ascending
262    /// (0x1) or Descending (0x2). If an endpoint receives a value outside this
263    /// range, it MUST close the session with PROTOCOL_VIOLATION." DYNAMIC_GROUPS:
264    /// "The allowed values are 0 or 1... If an endpoint receives a value larger
265    /// than 1, it MUST close the session with PROTOCOL_VIOLATION."
266    ///
267    /// Draft-16 adds a third, and only draft-16: "DELIVERY_TIMEOUT, if present,
268    /// MUST contain a value greater than 0. If an endpoint receives a
269    /// DELIVERY_TIMEOUT equal to 0 it MUST close the session with
270    /// PROTOCOL_VIOLATION." Draft-17 renamed the type to
271    /// OBJECT_DELIVERY_TIMEOUT and states no range for it, so the rule ends
272    /// where the name does.
273    ///
274    /// Not DEFAULT_PUBLISHER_PRIORITY, on any of the four. "Priorities above 255
275    /// are invalid" names no consequence, in a section where its neighbours
276    /// name one in the next clause. The same contrast excludes draft-15's
277    /// PUBLISHER_PRIORITY from [`CodecError::ParameterValueOutOfRange`], and it
278    /// is the draft's distinction both times.
279    ///
280    /// `key` is the absolute type, after any delta encoding is resolved.
281    #[error(
282        "track property type {key} carries value {value}, which is outside the range it allows"
283    )]
284    TrackPropertyValueOutOfRange {
285        /// The extension or property type whose value was out of range,
286        /// absolute.
287        key: u64,
288        /// The value as it arrived.
289        value: u64,
290    },
291    /// A Forward field carried a value other than zero or one.
292    ///
293    /// Drafts 11 through 14 carry the field, and state the rule in two wordings.
294    /// SUBSCRIBE and SUBSCRIBE_UPDATE, on all four: "Forward: If 1, Objects
295    /// matching the subscription are forwarded to the subscriber. If 0, Objects
296    /// are not forwarded to the subscriber. Any other value is a protocol error
297    /// and MUST terminate the session with a Protocol Violation". PUBLISH, added
298    /// in draft-12: "Any value other than 0 or 1 is a Protocol Violation."
299    /// Draft-14 spells both codes PROTOCOL_VIOLATION and is otherwise unchanged.
300    ///
301    /// PUBLISH_OK, also from draft-12, is the one site that names no
302    /// consequence: "Forward: The Forward State for this subscription, either 0
303    /// (don't forward) or 1 (forward)." It is reported here all the same. The
304    /// field carries the same two values in every message that has one, three of
305    /// the four sites state the close outright, and the sentence enumerates 0
306    /// and 1 without giving a third value any meaning — so this is the same rule
307    /// stated shorter, not a permission. Reading an omission the other way is
308    /// how a wrong claim gets in: on Group Order, drafts 12 through 14 do state
309    /// the 0x0 rule for SUBSCRIBE_OK, PUBLISH and FETCH_OK, so a reading that has
310    /// them silent about it is wrong about all three.
311    ///
312    /// Drafts 07 through 10 have no such field. Drafts 15 and later carry
313    /// forwarding as the FORWARD parameter instead, under the same rule but a
314    /// different serialization; that form is
315    /// [`CodecError::ParameterValueOutOfRange`].
316    #[error("forward field carries {0}, which is neither zero nor one")]
317    InvalidForward(u8),
318    /// A subscription filter names a Filter Type no draft in its range assigns.
319    ///
320    /// All the drafts state the rule and they do not state the same
321    /// consequence. Drafts 07 through 13: "A filter type other than the above
322    /// MUST be treated as error", which names no code and no close. Draft-14:
323    /// "An endpoint that receives a filter type other than the above MUST be
324    /// close the session with PROTOCOL_VIOLATION", the typo being the draft's.
325    /// Drafts 15 through 19 say the same without the typo. So the same value in
326    /// the same place is a refused message on the first seven drafts and a
327    /// session close on the last six, and only the per-draft session table can
328    /// tell them apart.
329    ///
330    /// The assigned set is not constant either. Drafts 07 and 08 assign 0x1 as
331    /// Latest Group, drafts 09 and 10 withdraw it and list three types, and
332    /// drafts 11 and later reinstate 0x1 as Next Group Start — a different
333    /// meaning at the same number. A decoder that accepts the union would read a
334    /// draft-09 SUBSCRIBE the draft requires it to reject.
335    ///
336    /// The serialization moves as well. Drafts 07 through 14 carry the Filter
337    /// Type as a field of SUBSCRIBE and its relatives; drafts 15 and later carry
338    /// it as the first field inside the length-prefixed filter parameter, which
339    /// is a place a reader of the field has to know to look.
340    #[error("filter type {0} is not one this draft assigns")]
341    InvalidFilterType(u64),
342    /// A FETCH names a Fetch Type no draft in its range assigns.
343    ///
344    /// The sentence next to the Filter Type one, and it moves the same way.
345    /// Drafts 08 through 13: "A Fetch Type other than 0x1, 0x2 or 0x3 MUST be
346    /// treated as an error", naming no code and no close — 0x3 being absent from
347    /// the sentence on drafts 08, 09 and 10, which assign only two types.
348    /// Draft-14: "An endpoint that receives a Fetch Type other than 0x1, 0x2 or
349    /// 0x3 MUST be close the session with a PROTOCOL_VIOLATION", carrying the
350    /// same missing word as its Filter Type sentence. Drafts 15 through 19 say it
351    /// without the typo. Draft-07 has no FETCH at all.
352    ///
353    /// Like the Filter Type, the value decides which fields follow it: a
354    /// Standalone fetch carries a Track Namespace, a Track Name and a range, and
355    /// a joining fetch carries a Request ID and an offset. A reader that cannot
356    /// name the type cannot find the end of the message, which is why the later
357    /// drafts answer it with a close rather than by ignoring the field.
358    #[error("fetch type {0} is not one this draft assigns")]
359    InvalidFetchType(u64),
360    /// A subscription filter parameter's value is not a filter.
361    ///
362    /// Drafts 15 and 16 state it of this parameter directly: "If the length of
363    /// the Subscription Filter does not match the parameter length, the publisher
364    /// MUST close the session with PROTOCOL_VIOLATION." Drafts 17 through 19
365    /// drop that sentence and leave the general one, which every draft from 15
366    /// on also carries: "If a receiver understands a Type, and the following
367    /// Value or Length/Value does not match the serialization defined by that
368    /// Type, the receiver MUST close the session with error code
369    /// KEY_VALUE_FORMATTING_ERROR."
370    ///
371    /// Two sentences, two codes, one malformation — which is why this is a
372    /// variant of its own rather than reported as
373    /// [`CodecError::KeyValueFormatting`]. A filter three bytes long inside a
374    /// four-byte parameter ends a draft-16 session with PROTOCOL_VIOLATION and a
375    /// draft-17 session with KEY_VALUE_FORMATTING_ERROR, and the session tables
376    /// are where that difference belongs.
377    ///
378    /// A Filter Type outside the assigned set is not this: the filter's own
379    /// section names PROTOCOL_VIOLATION for that on all five drafts, and it is
380    /// [`CodecError::InvalidFilterType`].
381    ///
382    /// `detail` says which way the value failed, because "malformed" alone
383    /// leaves the reader to work out whether the filter ran short or the
384    /// parameter ran long.
385    #[error("subscription filter parameter is not a filter: {detail}")]
386    SubscriptionFilterMalformed {
387        /// How the value failed to be a filter.
388        detail: &'static str,
389    },
390    /// An AbsoluteRange filter's End Group Delta carries the range past the end
391    /// of the number space.
392    ///
393    /// Drafts 17 and later replaced the absolute End Group with a delta measured
394    /// from the Start Location's Group, which makes the last group in range a
395    /// sum rather than a field. Drafts 18 and 19 answer the sum leaving the
396    /// range: "Otherwise, the last Group ID to be delivered will be the Group ID
397    /// in Start Location plus the End Group Delta. If the resulting Group ID
398    /// would be greater than 2^64 - 1, the endpoint MUST close the session with
399    /// a PROTOCOL_VIOLATION."
400    ///
401    /// Draft-17 introduced the delta and states no such sentence, so it is
402    /// absent from that draft's session table. Drafts 15 and 16 write the End
403    /// Group out in full and can express nothing to overflow.
404    #[error(
405        "filter start group {start_group} plus end group delta {delta} leaves the 64-bit range"
406    )]
407    FilterEndGroupOverflow {
408        /// The Group ID of the filter's Start Location.
409        start_group: u64,
410        /// The End Group Delta as the filter carried it.
411        delta: u64,
412    },
413    /// An end-of-track object states an Object ID other than zero.
414    ///
415    /// Drafts 08, 09 and 10 describe Object Status 0x5 as "end of Track. GroupID
416    /// is one greater than the largest group produced in this track and the
417    /// ObjectId is zero", and continue: "An object with this status that has a
418    /// Group ID less than or equal to any other Group ID, or an Object ID other
419    /// than zero, is a protocol error, and the receiver MUST terminate the
420    /// session." The Group ID half needs the largest group seen on the track and
421    /// is not settleable from one header; the Object ID half is, and is what this
422    /// reports. Draft-07 assigns no 0x5 and drafts 11 and later drop the
423    /// sentence.
424    #[error("end-of-track object states object id {0}; an end of track ends at object zero")]
425    EndOfTrackObjectId(u64),
426    /// A delta-encoded key would exceed 2^64 - 1 once the delta is added to the
427    /// previous key.
428    ///
429    /// Drafts 16, 17, 18 and 19 all say, in Section 1.4.3: "The previous Type
430    /// value plus the Delta Type MUST NOT be greater than 2^64 - 1. If a Delta
431    /// Type is received that would be too large, the Session MUST be closed
432    /// with a PROTOCOL_VIOLATION." Delta encoding arrives with draft-16;
433    /// drafts 15 and earlier write absolute types and cannot reach this.
434    #[error("delta-encoded key {0} + {1} exceeds 2^64 - 1")]
435    KeyDeltaOverflow(u64, u64),
436    /// A caller asked to encode a parameter list that is not in ascending order
437    /// by type.
438    ///
439    /// Drafts 17, 18 and 19 require it in as many words: "Parameters MUST be
440    /// serialized in ascending order by Type." Draft-16 delta-encodes types
441    /// without stating that sentence, but the constraint is the same there and
442    /// is structural rather than stated: the delta is an unsigned difference
443    /// from the previous type, so a descending pair has no representation at
444    /// all. Encoding one anyway wraps the subtraction and emits a nine-byte
445    /// delta the peer resolves to an unrelated key, which is why draft-16
446    /// reports this too.
447    #[error("parameter type {1} follows {0}; parameters must be in ascending order by type")]
448    ParametersOutOfOrder(u64, u64),
449    /// The same parameter type appears twice in one message, and its definition
450    /// does not allow that.
451    ///
452    /// Every draft from 07 to 20 states the sender's half: "Senders MUST NOT
453    /// repeat the same Parameter Type in a message" — drafts 11 and later
454    /// adding "unless the parameter definition explicitly allows multiple
455    /// instances of that type to be sent in a single message." The receiver's
456    /// half is a SHOULD, and from draft-11 it carries a limit that makes the
457    /// rule asymmetric: "Receivers MUST allow duplicates of unknown
458    /// parameters." A receiver may therefore refuse a repeat only of a type its
459    /// own draft names, while a sender may repeat nothing it is not granted.
460    ///
461    /// One type is granted repeats, from draft-11 onward: AUTHORIZATION TOKEN,
462    /// numbered 0x01 on draft-11 and 0x03 on drafts 12 through 19. It is not
463    /// reported here. Drafts 07 through 10 state neither the exemption clause
464    /// nor the unknown-duplicates sentence, so on those four the rule is
465    /// symmetric and every repeat is refused in both directions.
466    ///
467    /// Setup Parameters and Version Specific Parameters are separate
468    /// namespaces that assign different meanings to the same number, so the
469    /// exemption is per namespace: on draft-11, 0x01 is the repeatable
470    /// AUTHORIZATION TOKEN in a SUBSCRIBE and the non-repeatable PATH in a
471    /// CLIENT_SETUP.
472    #[error("parameter type {0} appears more than once")]
473    DuplicateParameter(u64),
474    /// A delta-encoded Object ID would exceed 2^64 - 1 once the delta is added
475    /// to the previous Object ID on the same stream.
476    ///
477    /// Drafts 18 through 20 Section 11.4.2, and draft-21 Section 11.3.1: "The
478    /// Object ID Delta + 1 is added to the previous Object ID in the Subgroup
479    /// stream if there was one... If the resulting Object ID would be greater
480    /// than 2^64 - 1, the endpoint MUST close the session with a
481    /// PROTOCOL_VIOLATION."
482    ///
483    /// Drafts 14 through 17 Section 10.4.2 describe the same arithmetic and
484    /// state no consequence, so on those four this is a decode failure and
485    /// nothing more. The delta encoding does not exist before draft-14, which
486    /// is why the range starts there rather than at the first draft with a
487    /// subgroup stream.
488    ///
489    /// Distinct from [`CodecError::InvalidField`], which is too coarse for this
490    /// rule: a caller could not tell the wrap from a dozen unrelated
491    /// malformations, and so could not act on the rule.
492    #[error("object id {0} + {1} + 1 exceeds 2^64 - 1")]
493    ObjectIdOverflow(u64, u64),
494    /// An Object with Object Status 'Object Does Not Exist' carries extension
495    /// headers.
496    ///
497    /// Drafts 11 through 14 state it once each — draft-11 Section 9.1.1.2,
498    /// drafts 12 and 13 Section 9.2.1.2, draft-14 Section 10.2.1.2 — and the
499    /// first three word it: "Any Object may have extension headers except those
500    /// with Object Status 'Object Does Not Exist'. If an endpoint receives a
501    /// non-existent Object containing extension headers it MUST close the
502    /// session with a Protocol Violation." Draft-14 states the same sentence
503    /// with the code spelled PROTOCOL_VIOLATION.
504    ///
505    /// The rule names one status and no others, so extensions beside End of
506    /// Group or End of Track are legal on those four drafts and are not
507    /// reported here. Drafts 15 and later replaced this narrow form with the
508    /// general one — extensions, later properties, are permitted only beside
509    /// Normal — which is a different rule with a different subject and is
510    /// reported by the carriers' own `extensions_permitted` predicates.
511    ///
512    /// Drafts 07 through 10 state neither form.
513    ///
514    /// All three carriers that announce a status reach this, in both
515    /// directions: an object on a subgroup stream, an object on a fetch stream,
516    /// and a status datagram. A plain datagram has no status field and is the
517    /// one carrier that cannot break the rule.
518    ///
519    /// Distinct from [`CodecError::InvalidField`], which is too coarse for this
520    /// rule: a caller could not tell it from a dozen unrelated malformations, so
521    /// a session could not be closed over it without closing sessions the drafts
522    /// do not ask to be closed.
523    #[error("object with status 'object does not exist' carries {0} bytes of extension headers")]
524    ExtensionsOnNonExistentObject(usize),
525    /// An object arrived carrying a payload the draft gives it no room for.
526    ///
527    /// All the drafts state the rule, in two phrasings. Drafts 07 through
528    /// 18 say it of the status code — draft-07 Section 7.1.1.1, drafts 08 and
529    /// 09 Section 8.1.1.1, drafts 10 and 11 Section 9.1.1.1, drafts 12 and 13
530    /// Section 9.2.1.1, drafts 14 through 17 Section 10.2.1.1, draft-18 Section
531    /// 11.2.1.1: "Any object with a status code other than zero MUST have an
532    /// empty payload." Drafts 19 and 20 state it of a registry instead, both in
533    /// their Section 11.2.1.1: "An Object MUST have an empty payload unless its
534    /// Object Status value is registered as permitting a payload in the Object
535    /// Status registry (Section 15.9). Of the values defined in this document,
536    /// only Normal (0x0) permits a payload." The two agree on every status those
537    /// documents define and differ in what a later one may add.
538    ///
539    /// `detail` separates the two ways an object can break it, because they are
540    /// different mistakes and the second is the one a decoder can be fooled by:
541    ///
542    /// * The framing already said there would be no payload. A datagram whose
543    ///   Type sets the STATUS bit carries a status in the payload's place, so
544    ///   bytes after it are not a short payload or an odd one — they are bytes
545    ///   the frame does not define. This bites hardest at status Normal, whose
546    ///   status alone would report a payload as permitted, and a caller that
547    ///   treats what is left as the payload hands the application content the
548    ///   publisher never framed as content.
549    /// * The status forbids one. The registry rule above, reached where a
550    ///   caller holds a status and a payload together and the framing has not
551    ///   already ruled one of them out.
552    ///
553    /// **No draft turns this into a close.** The sentence is a MUST on the
554    /// sender with no receiver action named, and the "SHOULD be treated as a
555    /// protocol error" beside it belongs to the neighbouring rule about
556    /// unassigned status values. So every draft's session-close table places it
557    /// among the rules they state and do not end a session over — which is a
558    /// decision this variant makes visible, and one
559    /// [`CodecError::InvalidField`] was making by accident.
560    ///
561    /// Distinct from that variant for the reason
562    /// [`CodecError::ExtensionsOnNonExistentObject`] is: an error a caller
563    /// cannot name is one no test can assert and no log can explain.
564    #[error("object with status {status} carries {len} bytes of payload; {detail}")]
565    PayloadNotPermitted {
566        /// The object's status as it arrived on the wire.
567        status: u64,
568        /// How many bytes followed it.
569        len: usize,
570        /// Which of the two rules the bytes broke.
571        detail: &'static str,
572    },
573    /// A request message's Required Request ID Delta names a dependency below
574    /// zero.
575    ///
576    /// Draft-17 Section 9.2: "An endpoint MUST close the session with
577    /// INVALID_REQUIRED_REQUEST_ID if it receives a delta where 2 × Required
578    /// Request ID Delta exceeds the Request ID." Draft-18 removed the field.
579    #[error("required request id delta {1} is too large for request id {0}")]
580    InvalidRequiredRequestIdDelta(u64, u64),
581    /// A unidirectional stream announced a type its draft's stream table does
582    /// not assign.
583    ///
584    /// Every draft from 07 to 20 requires the session to end for this, in one
585    /// of two phrasings. Draft-07 and drafts 17 through 19 say "An endpoint
586    /// that receives an unknown stream type MUST close the session"; drafts 08
587    /// through 16 fold the streams and the datagrams into one sentence, "an
588    /// unknown stream or datagram type", whose second half is
589    /// [`CodecError::UnknownDatagramType`]. No draft in the range is silent,
590    /// and neither phrasing is a hint that the other draft's rule is weaker.
591    ///
592    /// What the tables assign moves across the range, so the set this reports
593    /// on is per-draft rather than shared. Draft-07 has a single table covering
594    /// streams and datagrams together, which is why an OBJECT_DATAGRAM type at
595    /// the head of a draft-07 stream is *not* unknown; drafts 08 onward split
596    /// them into two tables with independent numbering; drafts 17 through 19
597    /// add SETUP, and drafts 18 and 19 add PADDING, both of which are assigned
598    /// stream types that a subgroup reader must refuse without reporting this.
599    ///
600    /// A stream announcing an assigned type this reader cannot read is not this
601    /// error: the value is one the draft defines, and the disagreement is with
602    /// the caller rather than with the draft. Those stay
603    /// [`CodecError::InvalidField`]. The distinction is the whole point of the
604    /// variant — reporting an assigned type as unknown closes sessions over
605    /// streams the draft permits, which is the more costly way to be wrong.
606    #[error("stream type {0} is not one this draft assigns")]
607    UnknownStreamType(u64),
608    /// A datagram announced a type its draft's datagram table does not assign.
609    ///
610    /// The datagram half of the rule above, and stated by all the drafts
611    /// for the same reason: drafts 08 through 16 name streams and datagrams in
612    /// one sentence, and drafts 17, 18 and 19 give the datagrams their own —
613    /// "An endpoint that receives an unknown datagram type MUST close the
614    /// session." Draft-07 alone numbers its datagrams in the stream table, so
615    /// its datagram types are reported by [`CodecError::UnknownStreamType`] and
616    /// this variant is never produced there.
617    ///
618    /// Separate from the stream variant because the two number spaces are
619    /// separate from draft-08 on: 0x05 is FETCH_HEADER on a stream and an
620    /// assigned OBJECT_DATAGRAM form in several drafts' datagram tables, so a
621    /// single variant could not say which table had been consulted.
622    #[error("datagram type {0} is not one this draft assigns")]
623    UnknownDatagramType(u64),
624    /// A subgroup stream header's Type was inside the form its draft defines,
625    /// but held a combination the draft separately names as invalid.
626    ///
627    /// Distinct from [`CodecError::UnknownStreamType`], which reports a value
628    /// no table assigns. Drafts 16 through 21 describe the subgroup Type as a
629    /// bit field rather than as a list of code points, and then rule
630    /// combinations out *within* the form: on all six a SUBGROUP_ID_MODE of
631    /// 0b11, and from draft-20 also a Type that leaves the subgroup form's own
632    /// bit clear or that runs past the one-byte flags space. The enclosing
633    /// form is assigned, so calling these unknown would misname them; the
634    /// drafts call them invalid and require a close with PROTOCOL_VIOLATION.
635    ///
636    /// Separate from [`CodecError::InvalidDatagramTypeValue`] because the
637    /// drafts state the two as separate sentences in separate sections — "If
638    /// an endpoint receives a stream header with any of these" against "If an
639    /// endpoint receives a datagram with any of these". A single variant
640    /// spanning both could not say which of the two sentences a frame broke,
641    /// and `raw` cannot be made to say it either: the two Type spaces overlap,
642    /// so deriving the namespace from the number would be this crate guessing
643    /// which table had been consulted. A consumer that publishes the rule
644    /// beside the refusal would then be right on half the frames and wrong on
645    /// the other half, with nothing in the row to say which.
646    ///
647    /// `detail` names which combination was seen, because the rule is a list
648    /// rather than a single condition and a log that says only "invalid" leaves
649    /// the reader to re-derive the bits.
650    #[error("stream header type {raw:#x} is one this draft names as invalid: {detail}")]
651    InvalidStreamTypeValue {
652        /// The Type value as it arrived, before any narrowing to a byte.
653        raw: u64,
654        /// Which of the draft's lists it fell into.
655        detail: &'static str,
656    },
657    /// A datagram's Type was inside the form its draft defines, but held a
658    /// combination the draft separately names as invalid.
659    ///
660    /// The datagram half of [`CodecError::InvalidStreamTypeValue`], which
661    /// carries the reason the two are separate variants rather than one.
662    /// Drafts 16 through 21 rule out a Type asking to be both an object status
663    /// and an end-of-group marker; drafts 20 and 21 add the bit reserved for a
664    /// datagram, and any other bit their form leaves unspecified.
665    ///
666    /// `detail` names which combination was seen, for the reason the stream
667    /// variant gives.
668    #[error("datagram type {raw:#x} is one this draft names as invalid: {detail}")]
669    InvalidDatagramTypeValue {
670        /// The Type value as it arrived, before any narrowing to a byte.
671        raw: u64,
672        /// Which of the draft's lists it fell into.
673        detail: &'static str,
674    },
675    /// A ContentExists field carried a value other than zero or one.
676    ///
677    /// Drafts 07 through 13 all state it in the same words: "Content Exists: 1
678    /// if an object has been published on this track, 0 if not. If 0, then the
679    /// Largest Group ID and Largest Object ID fields will not be present. Any
680    /// other value is a protocol error and MUST terminate the session with a
681    /// Protocol Violation". Draft-14 states the same sentence with the code
682    /// spelled PROTOCOL_VIOLATION. Draft-07 states it for SUBSCRIBE_OK
683    /// in Section 6.15 and SUBSCRIBE_DONE in Section 6.19; drafts 08 through 11
684    /// keep the SUBSCRIBE_OK site alone; drafts 12, 13 and 14 add PUBLISH.
685    /// Draft-15 removed the field, replacing it with the presence or absence of
686    /// a LARGEST_OBJECT parameter, so no draft from 15 on has one.
687    ///
688    /// The field is a single byte and it decides whether two more follow, so a
689    /// value that is neither leaves a reader with no way to know where the
690    /// message ends.
691    #[error("content exists field carries {0}, which is neither zero nor one")]
692    InvalidContentExists(u8),
693    /// A control message's declared Length disagrees with the fields it
694    /// carries.
695    ///
696    /// All the drafts state it in the same paragraph that gives the
697    /// message type registry, and only the code changes: drafts 07 through 10
698    /// say "If the length does not match the length of the message content, the
699    /// receiver MUST close the session", naming no code; drafts 11 through 18
700    /// name the Message Payload and answer with PROTOCOL_VIOLATION; draft-19
701    /// renames the field to Message Body and keeps the code.
702    ///
703    /// Both directions are the same rule and both are reported here. Fields that
704    /// stop short leave bytes unread — a trailing field the sender wrote and
705    /// this reader does not know about, or one it read at the wrong width.
706    /// Fields that run past the end wanted more bytes than the Length allowed,
707    /// which is the same disagreement seen from the other side.
708    /// The second case is why this cannot be left as
709    /// [`CodecError::UnexpectedEnd`]. That variant means *the message is still
710    /// arriving* everywhere else, and a reader loops on it rather than closing;
711    /// inside a buffer already bounded by the declared Length there is nothing
712    /// left to arrive, so running out there means something else entirely.
713    ///
714    /// Distinct from [`CodecError::InvalidField`], which is too coarse for the
715    /// leftover-bytes case: a caller could not tell it from a dozen unrelated
716    /// malformations, so a session could not be closed over it.
717    #[error("control message declares {declared} bytes of payload; {detail}")]
718    ControlMessageLengthMismatch {
719        /// The Length field the sender wrote.
720        declared: usize,
721        /// Which way the two disagreed, in words, because the useful number is
722        /// different in each direction and neither is knowable in the other.
723        detail: &'static str,
724    },
725    /// Reason phrase exceeds [`MAX_REASON_PHRASE_LENGTH`].
726    #[error("reason phrase exceeds {MAX_REASON_PHRASE_LENGTH} bytes")]
727    ReasonPhraseTooLong,
728    /// GOAWAY URI exceeds [`MAX_GOAWAY_URI_LENGTH`].
729    #[error("GOAWAY URI exceeds {MAX_GOAWAY_URI_LENGTH} bytes")]
730    GoAwayUriTooLong,
731    /// Draft not implemented or not enabled via feature flag.
732    #[error("unsupported draft: {0}")]
733    UnsupportedDraft(String),
734}
735
736impl CodecError {
737    /// Whether more bytes might complete this decode.
738    ///
739    /// A reader that owns a growing buffer asks this to tell *the input has
740    /// not all arrived* from *the input is wrong*: `true` means fill the
741    /// buffer and decode again, `false` means the bytes are what they are and
742    /// no amount of waiting improves them.
743    ///
744    /// # Why this is a predicate and not a variant test
745    ///
746    /// Running out of bytes has four spellings here, because a decode can run
747    /// out inside a nested decoder that reports in its own error type and each
748    /// one names the condition after itself:
749    ///
750    /// * [`CodecError::UnexpectedEnd`], from a field this module read;
751    /// * [`CodecError::VarInt`] carrying [`VarIntError::UnexpectedEnd`], from a
752    ///   varint whose length prefix promised bytes the buffer did not hold;
753    /// * [`CodecError::Kvp`] carrying [`KvpError::UnexpectedEnd`], and
754    /// * the same wrapped one level deeper as
755    ///   [`KvpError::VarInt`]`(`[`VarIntError::UnexpectedEnd`]`)`, from a
756    ///   key-value pair that ran out in its length or in its value.
757    ///
758    /// All four mean the same thing to a caller and only the first announces
759    /// it in the variant name. That is not a hypothetical: a reader matching
760    /// the first alone treated a subgroup object whose leading varint had not
761    /// arrived yet as a malformed stream, and reported `insufficient bytes for
762    /// varint decoding` for an object that was merely still in flight. The
763    /// condition is one fact about a buffer, so it is answered in one place
764    /// rather than re-derived by every reader that has to know it.
765    ///
766    /// # What is deliberately not here
767    ///
768    /// [`CodecError::ControlMessageLengthMismatch`] is the case that looks
769    /// like this one and is its opposite, and its own documentation says why:
770    /// inside a buffer already bounded by a declared Length there is nothing
771    /// left to arrive, so running out there is a malformation and waiting for
772    /// more would be waiting forever.
773    ///
774    /// [`VarIntError::UnexpectedEnd`]: crate::varint::VarIntError::UnexpectedEnd
775    /// [`KvpError::UnexpectedEnd`]: crate::kvp::KvpError::UnexpectedEnd
776    /// [`KvpError::VarInt`]: crate::kvp::KvpError::VarInt
777    pub fn is_incomplete(&self) -> bool {
778        use crate::kvp::KvpError;
779        use crate::varint::VarIntError;
780        matches!(
781            self,
782            CodecError::UnexpectedEnd
783                | CodecError::VarInt(VarIntError::UnexpectedEnd)
784                | CodecError::Kvp(KvpError::UnexpectedEnd)
785                | CodecError::Kvp(KvpError::VarInt(VarIntError::UnexpectedEnd))
786        )
787    }
788}