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 thirteen 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 thirteen 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 thirteen 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 /// what put a wrong claim about Group Order into this codec: drafts 12
309 /// through 14 do state the 0x0 rule for SUBSCRIBE_OK, PUBLISH and FETCH_OK,
310 /// and a doc comment here asserted for a while that they did not.
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 thirteen 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, where
339 /// nothing had been reading it at all.
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 19 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 /// Draft-18 Section 11.4.2 and draft-19 Section 11.4.2: "The Object ID
478 /// Delta + 1 is added to the previous Object ID in the Subgroup stream if
479 /// there was one... If the resulting Object ID would be greater than
480 /// 2^64 - 1, the endpoint MUST close the session with a
481 /// PROTOCOL_VIOLATION." Draft-17
482 /// Section 10.4.2 describes the same arithmetic and states no consequence,
483 /// so on that draft this is a decode failure and nothing more.
484 ///
485 /// Distinct from [`CodecError::InvalidField`], which the object reader
486 /// previously answered with: a caller could not tell the wrap from a dozen
487 /// unrelated malformations, and so could not act on the rule.
488 #[error("object id {0} + {1} + 1 exceeds 2^64 - 1")]
489 ObjectIdOverflow(u64, u64),
490 /// An Object with Object Status 'Object Does Not Exist' carries extension
491 /// headers.
492 ///
493 /// Drafts 11 through 14 state it once each — draft-11 Section 9.1.1.2,
494 /// drafts 12 and 13 Section 9.2.1.2, draft-14 Section 10.2.1.2 — and the
495 /// first three word it: "Any Object may have extension headers except those
496 /// with Object Status 'Object Does Not Exist'. If an endpoint receives a
497 /// non-existent Object containing extension headers it MUST close the
498 /// session with a Protocol Violation." Draft-14 states the same sentence
499 /// with the code spelled PROTOCOL_VIOLATION.
500 ///
501 /// The rule names one status and no others, so extensions beside End of
502 /// Group or End of Track are legal on those four drafts and are not
503 /// reported here. Drafts 15 and later replaced this narrow form with the
504 /// general one — extensions, later properties, are permitted only beside
505 /// Normal — which is a different rule with a different subject and is
506 /// reported by the carriers' own `extensions_permitted` predicates.
507 ///
508 /// Drafts 07 through 10 state neither form.
509 ///
510 /// All three carriers that announce a status reach this, in both
511 /// directions: an object on a subgroup stream, an object on a fetch stream,
512 /// and a status datagram. A plain datagram has no status field and is the
513 /// one carrier that cannot break the rule.
514 ///
515 /// Distinct from [`CodecError::InvalidField`], which these four drafts
516 /// previously answered with: a caller could not tell this rule from a dozen
517 /// unrelated malformations, so a session could not be closed over it
518 /// without closing sessions the drafts do not ask to be closed.
519 #[error("object with status 'object does not exist' carries {0} bytes of extension headers")]
520 ExtensionsOnNonExistentObject(usize),
521 /// An object arrived carrying a payload the draft gives it no room for.
522 ///
523 /// All thirteen drafts state the rule, in two phrasings. Drafts 07 through
524 /// 18 say it of the status code — draft-07 Section 7.1.1.1, drafts 08 and
525 /// 09 Section 8.1.1.1, drafts 10 and 11 Section 9.1.1.1, drafts 12 and 13
526 /// Section 9.2.1.1, drafts 14 through 17 Section 10.2.1.1, draft-18 Section
527 /// 11.2.1.1: "Any object with a status code other than zero MUST have an
528 /// empty payload." Draft-19 Section 11.2.1.1 states it of a registry
529 /// instead: "An Object MUST have an empty payload unless its Object Status
530 /// value is registered as permitting a payload in the Object Status
531 /// registry (Section 15.9). Of the values defined in this document, only
532 /// Normal (0x0) permits a payload." The two agree on every status those
533 /// documents define and differ in what a later one may add.
534 ///
535 /// `detail` separates the two ways an object can break it, because they are
536 /// different mistakes and the second is the one a decoder can be fooled by:
537 ///
538 /// * The framing already said there would be no payload. A datagram whose
539 /// Type sets the STATUS bit carries a status in the payload's place, so
540 /// bytes after it are not a short payload or an odd one — they are bytes
541 /// the frame does not define. This bites hardest at status Normal, whose
542 /// status alone would report a payload as permitted, and a caller that
543 /// treats what is left as the payload hands the application content the
544 /// publisher never framed as content.
545 /// * The status forbids one. The registry rule above, reached where a
546 /// caller holds a status and a payload together and the framing has not
547 /// already ruled one of them out.
548 ///
549 /// **No draft turns this into a close.** The sentence is a MUST on the
550 /// sender with no receiver action named, and the "SHOULD be treated as a
551 /// protocol error" beside it belongs to the neighbouring rule about
552 /// unassigned status values. So all thirteen session-close tables place it
553 /// among the rules they state and do not end a session over — which is a
554 /// decision this variant makes visible, and one
555 /// [`CodecError::InvalidField`] was making by accident.
556 ///
557 /// Distinct from that variant for the reason
558 /// [`CodecError::ExtensionsOnNonExistentObject`] is: an error a caller
559 /// cannot name is one no test can assert and no log can explain.
560 #[error("object with status {status} carries {len} bytes of payload; {detail}")]
561 PayloadNotPermitted {
562 /// The object's status as it arrived on the wire.
563 status: u64,
564 /// How many bytes followed it.
565 len: usize,
566 /// Which of the two rules the bytes broke.
567 detail: &'static str,
568 },
569 /// A request message's Required Request ID Delta names a dependency below
570 /// zero.
571 ///
572 /// Draft-17 Section 9.2: "An endpoint MUST close the session with
573 /// INVALID_REQUIRED_REQUEST_ID if it receives a delta where 2 × Required
574 /// Request ID Delta exceeds the Request ID." Draft-18 removed the field.
575 #[error("required request id delta {1} is too large for request id {0}")]
576 InvalidRequiredRequestIdDelta(u64, u64),
577 /// A unidirectional stream announced a type its draft's stream table does
578 /// not assign.
579 ///
580 /// Every draft from 07 to 19 requires the session to end for this, in one
581 /// of two phrasings. Draft-07 and drafts 17 through 19 say "An endpoint
582 /// that receives an unknown stream type MUST close the session"; drafts 08
583 /// through 16 fold the streams and the datagrams into one sentence, "an
584 /// unknown stream or datagram type", whose second half is
585 /// [`CodecError::UnknownDatagramType`]. No draft in the range is silent,
586 /// and neither phrasing is a hint that the other draft's rule is weaker.
587 ///
588 /// What the tables assign moves across the range, so the set this reports
589 /// on is per-draft rather than shared. Draft-07 has a single table covering
590 /// streams and datagrams together, which is why an OBJECT_DATAGRAM type at
591 /// the head of a draft-07 stream is *not* unknown; drafts 08 onward split
592 /// them into two tables with independent numbering; drafts 17 through 19
593 /// add SETUP, and drafts 18 and 19 add PADDING, both of which are assigned
594 /// stream types that a subgroup reader must refuse without reporting this.
595 ///
596 /// A stream announcing an assigned type this reader cannot read is not this
597 /// error: the value is one the draft defines, and the disagreement is with
598 /// the caller rather than with the draft. Those stay
599 /// [`CodecError::InvalidField`]. The distinction is the whole point of the
600 /// variant — reporting an assigned type as unknown closes sessions over
601 /// streams the draft permits, which is the more costly way to be wrong.
602 #[error("stream type {0} is not one this draft assigns")]
603 UnknownStreamType(u64),
604 /// A datagram announced a type its draft's datagram table does not assign.
605 ///
606 /// The datagram half of the rule above, and stated by all thirteen drafts
607 /// for the same reason: drafts 08 through 16 name streams and datagrams in
608 /// one sentence, and drafts 17, 18 and 19 give the datagrams their own —
609 /// "An endpoint that receives an unknown datagram type MUST close the
610 /// session." Draft-07 alone numbers its datagrams in the stream table, so
611 /// its datagram types are reported by [`CodecError::UnknownStreamType`] and
612 /// this variant is never produced there.
613 ///
614 /// Separate from the stream variant because the two number spaces are
615 /// separate from draft-08 on: 0x05 is FETCH_HEADER on a stream and an
616 /// assigned OBJECT_DATAGRAM form in several drafts' datagram tables, so a
617 /// single variant could not say which table had been consulted.
618 #[error("datagram type {0} is not one this draft assigns")]
619 UnknownDatagramType(u64),
620 /// A Type value inside the form its draft defines, but one the draft
621 /// separately names as invalid.
622 ///
623 /// Distinct from the two variants above, which report a value no table
624 /// assigns. Drafts 16 through 19 describe their subgroup and datagram Types
625 /// as bit fields rather than as a list of code points, and then rule out
626 /// particular bit combinations *within* the form — a subgroup Type whose
627 /// SUBGROUP_ID_MODE holds the reserved value, or a datagram Type asking to
628 /// be both an object status and an end-of-group marker. The enclosing form
629 /// is assigned, so calling these unknown would misname them; the drafts
630 /// call them invalid and require a close with PROTOCOL_VIOLATION.
631 ///
632 /// `detail` names which combination was seen, because the rule is a list
633 /// rather than a single condition and a log that says only "invalid" leaves
634 /// the reader to re-derive the bits.
635 #[error("type {raw:#x} is one this draft names as invalid: {detail}")]
636 InvalidTypeValue {
637 /// The Type value as it arrived, before any narrowing to a byte.
638 raw: u64,
639 /// Which of the draft's lists it fell into.
640 detail: &'static str,
641 },
642 /// A ContentExists field carried a value other than zero or one.
643 ///
644 /// Drafts 07 through 13 all state it in the same words: "Content Exists: 1
645 /// if an object has been published on this track, 0 if not. If 0, then the
646 /// Largest Group ID and Largest Object ID fields will not be present. Any
647 /// other value is a protocol error and MUST terminate the session with a
648 /// Protocol Violation". Draft-14 states the same sentence with the code
649 /// spelled PROTOCOL_VIOLATION. Draft-07 states it for SUBSCRIBE_OK
650 /// in Section 6.15 and SUBSCRIBE_DONE in Section 6.19; drafts 08 through 11
651 /// keep the SUBSCRIBE_OK site alone; drafts 12, 13 and 14 add PUBLISH.
652 /// Draft-15 removed the field, replacing it with the presence or absence of
653 /// a LARGEST_OBJECT parameter, so no draft from 15 on has one.
654 ///
655 /// The field is a single byte and it decides whether two more follow, so a
656 /// value that is neither leaves a reader with no way to know where the
657 /// message ends.
658 #[error("content exists field carries {0}, which is neither zero nor one")]
659 InvalidContentExists(u8),
660 /// A control message's declared Length disagrees with the fields it
661 /// carries.
662 ///
663 /// All thirteen drafts state it in the same paragraph that gives the
664 /// message type registry, and only the code changes: drafts 07 through 10
665 /// say "If the length does not match the length of the message content, the
666 /// receiver MUST close the session", naming no code; drafts 11 through 18
667 /// name the Message Payload and answer with PROTOCOL_VIOLATION; draft-19
668 /// renames the field to Message Body and keeps the code.
669 ///
670 /// Both directions are the same rule and both are reported here. Fields that
671 /// stop short leave bytes unread — a trailing field the sender wrote and
672 /// this reader does not know about, or one it read at the wrong width.
673 /// Fields that run past the end wanted more bytes than the Length allowed,
674 /// which is the same disagreement seen from the other side.
675 /// The second case is why this cannot be left as
676 /// [`CodecError::UnexpectedEnd`]. That variant means *the message is still
677 /// arriving* everywhere else, and a reader loops on it rather than closing;
678 /// inside a buffer already bounded by the declared Length there is nothing
679 /// left to arrive, so running out there means something else entirely.
680 ///
681 /// Distinct from [`CodecError::InvalidField`], which the leftover-bytes case
682 /// previously answered with: a caller could not tell it from a dozen
683 /// unrelated malformations, so a session could not be closed over it.
684 #[error("control message declares {declared} bytes of payload; {detail}")]
685 ControlMessageLengthMismatch {
686 /// The Length field the sender wrote.
687 declared: usize,
688 /// Which way the two disagreed, in words, because the useful number is
689 /// different in each direction and neither is knowable in the other.
690 detail: &'static str,
691 },
692 /// Reason phrase exceeds [`MAX_REASON_PHRASE_LENGTH`].
693 #[error("reason phrase exceeds {MAX_REASON_PHRASE_LENGTH} bytes")]
694 ReasonPhraseTooLong,
695 /// GOAWAY URI exceeds [`MAX_GOAWAY_URI_LENGTH`].
696 #[error("GOAWAY URI exceeds {MAX_GOAWAY_URI_LENGTH} bytes")]
697 GoAwayUriTooLong,
698 /// Draft not implemented or not enabled via feature flag.
699 #[error("unsupported draft: {0}")]
700 UnsupportedDraft(String),
701}