Skip to main content

moqtap_codec/draft18/
message.rs

1//! Draft-18 control message encoding and decoding.
2//!
3//! Key differences from draft-17:
4//! - `Required Request ID Delta` field removed from every request message.
5//! - SUBSCRIBE_NAMESPACE renumbered to 0x50 and `subscribe_options` removed.
6//! - New SUBSCRIBE_TRACKS message (0x51); FORWARD parameter belongs here.
7//! - PUBLISH_OK collapsed into REQUEST_OK (0x07); REQUEST_OK gains a trailing
8//!   Track Properties block (length implicit from message length).
9//! - GOAWAY gains an optional `request_id` (control stream only).
10//! - REQUEST_ERROR gains REDIRECT (0x34) carrying a Redirect structure
11//!   (connect_uri, track_namespace, track_name) appended after reason_phrase.
12//! - PUBLISH_DONE status codes 0x5/0x6 swapped: 0x5 = TOO_FAR_BEHIND,
13//!   0x6 = EXPIRED.
14//! - DELIVERY_TIMEOUT (0x02) renamed to OBJECT_DELIVERY_TIMEOUT;
15//!   new SUBGROUP_DELIVERY_TIMEOUT (0x06) and FILL_TIMEOUT (0x0A).
16//! - New TRACK_NAMESPACE_PREFIX parameter (0x34) for REQUEST_UPDATE, carrying
17//!   a bare Track Namespace (Section 2.4.1) with no length ahead of it.
18
19use crate::auth_token::{AuthorizationToken, AUTH_TOKEN_PARAMETER};
20use crate::error::MAX_FULL_TRACK_NAME_LENGTH;
21pub use crate::error::{
22    CodecError, MAX_GOAWAY_URI_LENGTH, MAX_MESSAGE_LENGTH, MAX_NAMESPACE_TUPLE_SIZE,
23    MAX_REASON_PHRASE_LENGTH,
24};
25use crate::kvp::{KeyValuePair, KvpError, KvpValue, MAX_KVP_VALUE_LEN};
26use crate::subscription_filter::{SubscriptionFilter, SUBSCRIPTION_FILTER_PARAMETER};
27use crate::types::check_location_range;
28use crate::types::*;
29use crate::varint::{Moqt18 as Wire, VarInt};
30use bytes::{Buf, BufMut};
31
32// ============================================================
33// Parameter encoding helpers for draft-18
34// ============================================================
35
36/// How a parameter value is encoded on the wire.
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38enum ParamEncoding {
39    /// Bare varint.
40    Varint,
41    /// Single byte (uint8).
42    Uint8,
43    /// Two consecutive varints (group, object).
44    Location,
45    /// Length-prefixed bytes.
46    LengthPrefixed,
47    /// A Track Namespace as defined in draft-18 Section 2.4.1: a varint field
48    /// count followed by that many length-prefixed fields.
49    ///
50    /// Not one of the four value encodings draft-18 Section 10.2 lists. A
51    /// parameter definition is free to name an encoding from elsewhere in the
52    /// document, and TRACK_NAMESPACE_PREFIX does exactly that; the field count
53    /// is the only length the wire carries.
54    TrackNamespaceValue,
55}
56
57fn param_encoding(key: u64) -> Option<ParamEncoding> {
58    match key {
59        // 0x02 = OBJECT_DELIVERY_TIMEOUT (DELIVERY_TIMEOUT on drafts 11-17)
60        // 0x04 = RENDEZVOUS_TIMEOUT (draft-18 Section 10.2.6). Not
61        //        MAX_CACHE_DURATION: that is Property Type 0x04 in the
62        //        separate Properties registry (Section 15.8), a different
63        //        namespace that happens to reuse the number.
64        // 0x06 = SUBGROUP_DELIVERY_TIMEOUT (new in draft-18)
65        // 0x08 = EXPIRES
66        // 0x0A = FILL_TIMEOUT (new in draft-18, FETCH only)
67        // 0x32 = NEW_GROUP_REQUEST
68        0x02 | 0x04 | 0x06 | 0x08 | 0x0A | 0x32 => Some(ParamEncoding::Varint),
69        // 0x10 = FORWARD, 0x20 = SUBSCRIBER_PRIORITY, 0x22 = GROUP_ORDER
70        0x10 | 0x20 | 0x22 => Some(ParamEncoding::Uint8),
71        // 0x09 = LARGEST_OBJECT. Draft-18 Section 10.2.11: "The LARGEST_OBJECT
72        //        parameter (Parameter Type 0x9) is a Location." A Location is
73        //        two consecutive varints (Section 10.2), with no length ahead
74        //        of them.
75        0x09 => Some(ParamEncoding::Location),
76        // 0x34 = TRACK_NAMESPACE_PREFIX (new in draft-18). Section 10.2.14:
77        //        it "uses the Track Namespace encoding described in
78        //        Section 2.4.1".
79        0x34 => Some(ParamEncoding::TrackNamespaceValue),
80        // 0x03 = AUTHORIZATION_TOKEN
81        // 0x21 = SUBSCRIPTION_FILTER
82        0x03 | 0x21 => Some(ParamEncoding::LengthPrefixed),
83        _ => None,
84    }
85}
86
87/// The one parameter type draft-18 lets a message carry more than once.
88///
89/// Section 10.2.2: "The AUTHORIZATION TOKEN parameter MAY be repeated within a
90/// message as long as the combination of Token Type and Token Value are unique
91/// after resolving any aliases." Every other type is subject to the blanket rule
92/// in Section 10.2.
93const AUTHORIZATION_TOKEN: u64 = 0x03;
94
95/// Whether `value` is inside the range draft-18 allows for a uint8-valued
96/// parameter.
97///
98/// Two of the three uint8 parameters restrict their range and say the receiver
99/// MUST close the session with PROTOCOL_VIOLATION on anything outside it:
100/// GROUP_ORDER allows only Ascending (0x1) and Descending (0x2) (Section
101/// 10.2.8), and FORWARD allows only 0 and 1 (Section 10.2.12).
102/// SUBSCRIBER_PRIORITY (Section 10.2.7) uses the whole 0-255 range, so it has no
103/// entry here.
104///
105/// Range-checking on decode is what makes the values usable: an application
106/// that tests `group_order == 2` for descending would otherwise treat 7 as
107/// neither ascending nor descending and carry on.
108fn uint8_value_in_range(key: u64, value: u8) -> bool {
109    match key {
110        // FORWARD (0x10)
111        0x10 => value <= 1,
112        // GROUP_ORDER (0x22)
113        0x22 => value == 1 || value == 2,
114        _ => true,
115    }
116}
117
118/// Add a delta to the previous delta-encoded key.
119///
120/// Draft-18 Section 1.4.3: "The previous Type value plus the Delta Type MUST NOT
121/// be greater than 2^64 - 1. If a Delta Type is received that would be too
122/// large, the Session MUST be closed with a PROTOCOL_VIOLATION." Section 10.2
123/// repeats it for Message Parameters. MoQT varints span the whole 64-bit range,
124/// so a peer can drive the sum past the end: a debug build panicked on the
125/// addition and a release build wrapped the key and reported the parameter under
126/// a type its sender never wrote.
127fn add_delta(prev_key: u64, delta: u64) -> Result<u64, CodecError> {
128    prev_key.checked_add(delta).ok_or(CodecError::KeyDeltaOverflow(prev_key, delta))
129}
130
131/// Hold a namespace-plus-name pair to the Full Track Name cap.
132///
133/// Draft-18 Section 2.4.1: "The maximum total length of a Full Track Name is
134/// 4,096 bytes. The length of a Full Track Name is computed as the sum of the
135/// Track Namespace Field Length fields and the Track Name Length field... If an
136/// endpoint receives a Track Namespace or a Full Track Name exceeding 4,096
137/// bytes, it MUST close the session with a PROTOCOL_VIOLATION."
138///
139/// The namespace half of that sentence is enforced inside the namespace decoder,
140/// which is the only place that sees a namespace with no name beside it. This is
141/// the other half, and it has to live where the two are decoded together: a
142/// namespace at 4,000 bytes and a name at 500 are each legal alone.
143///
144/// A control message can be 65,535 bytes, so without this a peer can hand the
145/// application a Full Track Name sixteen times the permitted size, and two
146/// relays that disagree about whether it was legal disagree about cache
147/// identity.
148fn check_full_track_name(namespace: &TrackNamespace, track_name: &[u8]) -> Result<(), CodecError> {
149    let total = namespace.field_bytes_len().saturating_add(track_name.len());
150    if total > MAX_FULL_TRACK_NAME_LENGTH {
151        return Err(CodecError::TrackNameTooLong);
152    }
153    Ok(())
154}
155
156/// Hold every AUTHORIZATION TOKEN parameter to the Token structure it names.
157///
158/// Section 10.2.2: "If the Token structure cannot be decoded, the receiver
159/// MUST close the Session with KEY_VALUE_FORMATTING_ERROR." That is the answer
160/// Section 1.4.3 gives for any Type whose value does not match the
161/// serialization that Type defines; the Token is the one structure this draft
162/// spells out, and the only parameter value in it that is more than opaque
163/// bytes.
164///
165/// Both namespaces carry the type on this draft, and both reach here.
166///
167/// A type this draft cannot name is left alone. The rule is conditional on the
168/// receiver understanding the Type, and an extension's parameter carries bytes
169/// no rule here describes.
170fn check_authorization_tokens(parameters: &[KeyValuePair]) -> Result<(), CodecError> {
171    for parameter in parameters {
172        let key = parameter.key.into_inner();
173        if key != AUTH_TOKEN_PARAMETER {
174            continue;
175        }
176        match &parameter.value {
177            KvpValue::Bytes(value) => {
178                AuthorizationToken::decode_moqt::<Wire>(key, value)?;
179            }
180            // Unreachable from the decoder, which picks the shape from the
181            // type and finds this one length-prefixed. A caller that built the
182            // pair in memory can still get here, and it is the same rule: the
183            // value is not the serialization the type defines.
184            KvpValue::Varint(_) => {
185                return Err(CodecError::KeyValueFormatting {
186                    key,
187                    detail: "its value is a bare varint where the type defines a Token structure",
188                });
189            }
190        }
191    }
192    Ok(())
193}
194
195/// Hold every SUBSCRIPTION_FILTER parameter to the filter structure it names.
196///
197/// Section 5.1.2: "An endpoint that receives a filter type other than the above
198/// MUST close the session with PROTOCOL_VIOLATION." Section 10.2.9: "The
199/// SUBSCRIPTION_FILTER parameter (Parameter Type 0x21) uses length-prefixed
200/// encoding... It is a Subscription Filter."
201///
202/// Drafts 15 and 16 stated the length rule of this parameter directly, at
203/// draft-16 Section 9.2.2.5 — "If the length of the Subscription Filter does
204/// not match the parameter length, the publisher MUST close the session with
205/// PROTOCOL_VIOLATION." Draft-17 dropped that sentence, and what answers the
206/// same malformation here is the general rule of Section 1.4.3, which names
207/// KEY_VALUE_FORMATTING_ERROR. Same malformation, different code, and the
208/// session table is where the two part.
209///
210/// The End Group is a delta, and this draft is the first to say what happens
211/// when resolving it leaves the number space: "the last Group ID to be delivered
212/// will be the Group ID in Start Location plus the End Group Delta. If the
213/// resulting Group ID would be greater than 2^64 - 1, the endpoint MUST close
214/// the session with a PROTOCOL_VIOLATION." That is why the sum is taken here and
215/// not left to the caller — draft-17, which introduced the delta and states no
216/// such sentence, does not take it.
217///
218/// The filter is otherwise decoded and discarded. What is kept is the refusal —
219/// the value stays on the parameter as the bytes that arrived, so a caller reads
220/// it through [`SubscriptionFilter::decode_moqt`] when it wants the filter
221/// rather than the frame.
222fn check_subscription_filters(parameters: &[KeyValuePair]) -> Result<(), CodecError> {
223    for parameter in parameters {
224        if parameter.key.into_inner() != SUBSCRIPTION_FILTER_PARAMETER {
225            continue;
226        }
227        match &parameter.value {
228            KvpValue::Bytes(value) => {
229                SubscriptionFilter::decode_moqt::<Wire>(value)?.last_group()?;
230            }
231            // Unreachable from the decoder, which picks the shape from the type
232            // and finds this one length-prefixed. A caller that built the pair
233            // in memory can still get here, and it is the same rule.
234            KvpValue::Varint(_) => {
235                return Err(CodecError::SubscriptionFilterMalformed {
236                    detail: "its value is a bare varint where the type defines a filter",
237                });
238            }
239        }
240    }
241    Ok(())
242}
243
244/// Decode a count-prefixed list of parameters with delta-encoded types.
245fn decode_parameters(buf: &mut impl Buf) -> Result<Vec<KeyValuePair>, CodecError> {
246    let count = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
247    let mut params = crate::types::reserve_bounded(count, buf);
248    let mut prev_key: u64 = 0;
249
250    for i in 0..count {
251        let delta = VarInt::decode_moqt::<Wire>(buf)?.into_inner();
252        let abs_key = add_delta(prev_key, delta)?;
253        // Types ascend, so a repeat is always a zero delta against the
254        // parameter before it. Draft-18 Section 10.2: "Receivers SHOULD check
255        // that there are no unexpected duplicate parameters and close the
256        // session with PROTOCOL_VIOLATION if found." Downstream code that scans
257        // the list for a key takes whichever copy it meets first, so two
258        // implementations reading one frame can pick opposite values.
259        if i > 0 && delta == 0 && abs_key != AUTHORIZATION_TOKEN {
260            return Err(CodecError::DuplicateParameter(abs_key));
261        }
262        prev_key = abs_key;
263
264        // Section 10.2: "All Message Parameters MUST be defined in the
265        // negotiated version of MOQT or negotiated via Setup Options. An
266        // endpoint that receives an unknown Message Parameter MUST close the
267        // session with PROTOCOL_VIOLATION. Because the receiver has to
268        // understand every Message Parameter, there is no need for a mechanism
269        // to skip unknown parameters." Because unknown parameters
270        // cannot be skipped, the block is bounded by a parameter count rather
271        // than a length.
272        //
273        // The table this consults is the registry's, so a type it cannot name
274        // is one this draft does not define. Reporting it as an ordinary
275        // malformation, which is what it did before, left the rule enforced
276        // against the frame and invisible to the session.
277        let encoding =
278            param_encoding(abs_key).ok_or(CodecError::UnknownMessageParameter(abs_key))?;
279
280        let value = match encoding {
281            ParamEncoding::Varint => {
282                let v = VarInt::decode_moqt::<Wire>(buf)?;
283                KvpValue::Varint(v)
284            }
285            ParamEncoding::Uint8 => {
286                if buf.remaining() < 1 {
287                    return Err(CodecError::UnexpectedEnd);
288                }
289                let byte = buf.get_u8();
290                if !uint8_value_in_range(abs_key, byte) {
291                    return Err(CodecError::ParameterValueOutOfRange {
292                        key: abs_key,
293                        value: byte as u64,
294                    });
295                }
296                KvpValue::Varint(VarInt::from_u64_moqt(byte as u64))
297            }
298            ParamEncoding::Location => {
299                let group = VarInt::decode_moqt::<Wire>(buf)?;
300                let object = VarInt::decode_moqt::<Wire>(buf)?;
301                let mut encoded = Vec::new();
302                group.encode_moqt::<Wire>(&mut encoded);
303                object.encode_moqt::<Wire>(&mut encoded);
304                KvpValue::Bytes(encoded)
305            }
306            ParamEncoding::LengthPrefixed => {
307                let len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
308                let data = read_bytes(buf, len)?;
309                KvpValue::Bytes(data)
310            }
311            ParamEncoding::TrackNamespaceValue => {
312                // A prefix of zero fields is legal: Section 2.4.1 puts a Track
313                // Namespace at "between 0 and 32 Track Namespace Fields", and
314                // an empty prefix matches every namespace.
315                let ns = TrackNamespace::decode_allow_empty_moqt::<Wire>(buf)?;
316                let mut encoded = Vec::new();
317                ns.encode_moqt::<Wire>(&mut encoded);
318                KvpValue::Bytes(encoded)
319            }
320        };
321
322        params.push(KeyValuePair { key: VarInt::from_u64_moqt(abs_key), value });
323    }
324    check_authorization_tokens(&params)?;
325    check_subscription_filters(&params)?;
326    Ok(params)
327}
328
329/// Whether `bytes` is exactly the wire form of a Location — two consecutive
330/// varints and nothing after them.
331///
332/// `decode_parameters` builds this value by reading two varints and
333/// re-serialising them, so every value it produces satisfies this. A value
334/// built in memory need not, and the encode arm writes these bytes verbatim
335/// because a Location carries no length of its own. Without this check a
336/// caller could hand over one varint, or three, and the codec would put a
337/// frame on the wire that its own decoder answers with an error.
338fn is_location_value(bytes: &[u8]) -> bool {
339    let mut buf = bytes;
340    VarInt::decode_moqt::<Wire>(&mut buf).is_ok()
341        && VarInt::decode_moqt::<Wire>(&mut buf).is_ok()
342        && !buf.has_remaining()
343}
344
345/// Whether `bytes` is exactly the wire form of a Track Namespace, with
346/// nothing after it. The same reasoning as [`is_location_value`]: the value
347/// goes out verbatim, so it has to be something this draft can read back.
348fn is_track_namespace_value(bytes: &[u8]) -> bool {
349    let mut buf = bytes;
350    TrackNamespace::decode_allow_empty_moqt::<Wire>(&mut buf).is_ok() && !buf.has_remaining()
351}
352
353/// Encode a count-prefixed list of parameters with delta-encoded types.
354///
355/// Errors on every list [`decode_parameters`] would refuse, so the two
356/// directions accept the same set of frames. Three things are refused, and each
357/// of them is a frame this codec would otherwise emit and then decline to read
358/// back:
359///
360/// * A list not in ascending order by type. The delta is a difference, so a
361///   descending pair wraps the subtraction into a nine-byte delta the peer
362///   resolves to an unrelated key.
363/// * A repeated type, except AUTHORIZATION_TOKEN (Section 10.2.2).
364/// * A uint8-valued parameter whose value does not fit one octet or lies
365///   outside the range its definition allows. Truncating instead is the worse
366///   outcome: GROUP_ORDER 258 goes out as the byte 0x02, a well-formed
367///   Descending indistinguishable on the wire from one the caller meant.
368/// * A value under a type that defines a structure which is not that structure:
369///   a Token, and a filter. Each is a value the receiver must close the session
370///   over, so writing one is not a way to send it — the sender's first sign of
371///   trouble would be the session going.
372fn encode_parameters(params: &[KeyValuePair], buf: &mut impl BufMut) -> Result<(), CodecError> {
373    check_authorization_tokens(params)?;
374    check_subscription_filters(params)?;
375    VarInt::from_usize(params.len()).encode_moqt::<Wire>(buf);
376    let mut prev_key: u64 = 0;
377
378    for (i, p) in params.iter().enumerate() {
379        let abs_key = p.key.into_inner();
380        let delta = abs_key
381            .checked_sub(prev_key)
382            .ok_or(CodecError::ParametersOutOfOrder(prev_key, abs_key))?;
383        if i > 0 && delta == 0 && abs_key != AUTHORIZATION_TOKEN {
384            return Err(CodecError::DuplicateParameter(abs_key));
385        }
386        prev_key = abs_key;
387        VarInt::from_u64_moqt(delta).encode_moqt::<Wire>(buf);
388
389        // The same maximum the decoder below applies, and the same one this
390        // draft's Setup Option encoder has always applied: "The maximum length
391        // of a value is 2^16-1 bytes. If an endpoint receives a length larger
392        // than the maximum, it MUST close the session with a PROTOCOL_VIOLATION."
393        // A value past it is one the peer must end the session over, so writing
394        // it is not a way to send it.
395        //
396        // Hoisted above the shape table rather than repeated inside it: a
397        // Location is bytes as well, and one past the maximum is not a Location.
398        if let KvpValue::Bytes(b) = &p.value {
399            if b.len() > MAX_KVP_VALUE_LEN {
400                return Err(KvpError::ValueTooLong(b.len()).into());
401            }
402        }
403
404        let encoding = param_encoding(abs_key);
405        match (&p.value, encoding) {
406            (KvpValue::Varint(v), Some(ParamEncoding::Varint)) => {
407                v.encode_moqt::<Wire>(buf);
408            }
409            (KvpValue::Varint(v), Some(ParamEncoding::Uint8)) => {
410                let raw = v.into_inner();
411                let byte = u8::try_from(raw).map_err(|_| CodecError::InvalidField)?;
412                if !uint8_value_in_range(abs_key, byte) {
413                    return Err(CodecError::ParameterValueOutOfRange {
414                        key: abs_key,
415                        value: byte as u64,
416                    });
417                }
418                buf.put_u8(byte);
419            }
420            // Both values are already stored in their own wire form — two
421            // varints for a Location, a field count and its fields for a Track
422            // Namespace — so they go out as they are. Adding a length here is
423            // the bug these arms exist to avoid.
424            (KvpValue::Bytes(b), Some(ParamEncoding::Location)) => {
425                if !is_location_value(b) {
426                    return Err(CodecError::InvalidField);
427                }
428                buf.put_slice(b);
429            }
430            (KvpValue::Bytes(b), Some(ParamEncoding::TrackNamespaceValue)) => {
431                if !is_track_namespace_value(b) {
432                    return Err(CodecError::InvalidField);
433                }
434                buf.put_slice(b);
435            }
436            (KvpValue::Bytes(b), Some(ParamEncoding::LengthPrefixed)) => {
437                VarInt::from_usize(b.len()).encode_moqt::<Wire>(buf);
438                buf.put_slice(b);
439            }
440            _ => {
441                // Fallback: encode as KVP even/odd
442                match &p.value {
443                    KvpValue::Varint(v) => v.encode_moqt::<Wire>(buf),
444                    KvpValue::Bytes(b) => {
445                        VarInt::from_usize(b.len()).encode_moqt::<Wire>(buf);
446                        buf.put_slice(b);
447                    }
448                }
449            }
450        }
451    }
452    Ok(())
453}
454
455/// Decode delta-encoded KVPs with even/odd convention (for setup options
456/// and track properties). Read until buffer is exhausted.
457fn decode_kvp_delta(buf: &mut impl Buf) -> Result<Vec<KeyValuePair>, CodecError> {
458    let mut pairs = Vec::new();
459    let mut prev_key: u64 = 0;
460
461    while buf.has_remaining() {
462        let delta = VarInt::decode_moqt::<Wire>(buf)?.into_inner();
463        let abs_key = add_delta(prev_key, delta)?;
464        prev_key = abs_key;
465
466        let value = if abs_key.is_multiple_of(2) {
467            let v = VarInt::decode_moqt::<Wire>(buf)?;
468            KvpValue::Varint(v)
469        } else {
470            let len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
471            // Draft-18 Section 1.4.3: "The maximum length of a value is 2^16-1
472            // bytes. If an endpoint receives a length larger than the maximum,
473            // it MUST close the session with a PROTOCOL_VIOLATION." The
474            // standalone `KeyValuePair::decode` already enforces this; stating
475            // it here too means the two readers of the same wire shape answer
476            // the same way, rather than this one leaning on the caller having
477            // clipped the buffer to a control message first.
478            if len > MAX_KVP_VALUE_LEN {
479                return Err(KvpError::ValueTooLong(len).into());
480            }
481            let data = read_bytes(buf, len)?;
482            KvpValue::Bytes(data)
483        };
484
485        pairs.push(KeyValuePair { key: VarInt::from_u64_moqt(abs_key), value });
486    }
487    Ok(pairs)
488}
489
490/// Encode delta-encoded KVPs with even/odd convention.
491///
492/// Refuses a list that is not in ascending order by type, for the same reason
493/// [`encode_parameters`] does: the delta is a difference, and a descending pair
494/// wraps it into a nine-byte delta the peer resolves to an unrelated key.
495fn encode_kvp_delta(pairs: &[KeyValuePair], buf: &mut impl BufMut) -> Result<(), CodecError> {
496    let mut prev_key: u64 = 0;
497    for p in pairs {
498        let abs_key = p.key.into_inner();
499        let delta = abs_key
500            .checked_sub(prev_key)
501            .ok_or(CodecError::ParametersOutOfOrder(prev_key, abs_key))?;
502        prev_key = abs_key;
503        VarInt::from_u64_moqt(delta).encode_moqt::<Wire>(buf);
504        match &p.value {
505            KvpValue::Varint(v) => v.encode_moqt::<Wire>(buf),
506            KvpValue::Bytes(b) => {
507                if b.len() > MAX_KVP_VALUE_LEN {
508                    return Err(KvpError::ValueTooLong(b.len()).into());
509                }
510                VarInt::from_usize(b.len()).encode_moqt::<Wire>(buf);
511                buf.put_slice(b);
512            }
513        }
514    }
515    Ok(())
516}
517
518/// Immutable Properties, Property Type 0xB.
519///
520/// Section 12.7: Immutable Properties are "a Track or Object Property that
521/// contains a sequence of Key-Value-Pairs (see Figure 2) that are themselves
522/// Track or Object Properties, respectively". The Type is odd, so its value is
523/// length-prefixed bytes, and those bytes are another delta-typed run starting
524/// from 0.
525const IMMUTABLE_PROPERTIES: u64 = 0x0B;
526
527/// Whether `value` is inside the range draft-18 allows for a Track Property
528/// type that restricts one.
529///
530/// Two types do, and each answers anything outside its range with a session
531/// close. DEFAULT_PUBLISHER_GROUP_ORDER (0x22), Section 12.5: "The allowed
532/// values are Ascending (0x1) or Descending (0x2). If an endpoint receives a
533/// value outside this range, it MUST close the session with
534/// PROTOCOL_VIOLATION." DYNAMIC_GROUPS (0x30), Section 12.6: "The allowed
535/// values are 0 or 1... If an endpoint receives a value larger than 1, it MUST
536/// close the session with PROTOCOL_VIOLATION."
537///
538/// Both are Track Properties, so the list they arrive in is the one carried by
539/// a control message rather than the properties on an object.
540///
541/// DEFAULT_PUBLISHER_PRIORITY (0x0E) is not here. Section 12.4 says
542/// "Priorities above 255 are invalid" and stops, where the two above name a
543/// consequence in the next clause. A range stated without one is not a close.
544///
545/// The numbers belong to the Property registry and not the Message Parameter
546/// one. Type 0x22 is GROUP_ORDER as a parameter and
547/// DEFAULT_PUBLISHER_GROUP_ORDER as a property, and the two happen to permit the
548/// same pair of values while meaning different things — one subscriber's
549/// preference against a property of the track. Reading either table for the
550/// other's types would be right by accident here and wrong at the next entry.
551fn track_property_value_in_range(key: u64, value: u64) -> bool {
552    match key {
553        // DEFAULT_PUBLISHER_GROUP_ORDER (0x22)
554        0x22 => value == 1 || value == 2,
555        // DYNAMIC_GROUPS (0x30)
556        0x30 => value <= 1,
557        _ => true,
558    }
559}
560
561/// Refuse a Track Property whose value falls outside the range its type allows,
562/// wherever in the list it is carried.
563///
564/// # Inside Immutable Properties as well as beside them
565///
566/// The list is walked one level down through Immutable Properties, whose
567/// contents Section 12.7 defines as properties themselves. The draft asks for
568/// this in as many words: "When looking for the value of a property, processors
569/// MUST search both the mutable properties and the contents of Immutable
570/// Properties." A check applied only to the outer list is one a peer opts out of
571/// by moving a pair inside the block, and the block is where an Original
572/// Publisher puts what a relay must not rewrite — which is where a track's group
573/// order and dynamic-group support belong.
574///
575/// Bytes under 0xB that do not parse as a Key-Value-Pair run are left alone
576/// rather than refused. Section 12.7 says relays "MAY decode and view the
577/// Properties in the Key-Value-Pairs", which is a permission and not a
578/// requirement, so a block this codec cannot read is carried to the caller
579/// intact instead of ending the session.
580fn check_track_property_values(properties: &[KeyValuePair]) -> Result<(), CodecError> {
581    for property in properties {
582        let key = property.key.into_inner();
583        match &property.value {
584            KvpValue::Varint(value) => {
585                let value = value.into_inner();
586                if !track_property_value_in_range(key, value) {
587                    return Err(CodecError::TrackPropertyValueOutOfRange { key, value });
588                }
589            }
590            KvpValue::Bytes(bytes) if key == IMMUTABLE_PROPERTIES => {
591                let mut inner = &bytes[..];
592                // A block that is not a Key-Value-Pair run is skipped rather
593                // than refused. See the note above: reading inside it is a
594                // permission, so one that cannot be read is carried.
595                //
596                // Skipped means this block and only this block. The rule the
597                // draft states here is about the block whose pairs will not
598                // parse, and says nothing about its neighbours; ending the
599                // whole walk would let a peer keep an out-of-range property
600                // from being looked at by putting an unparseable block in
601                // front of it.
602                if let Ok(nested) = decode_kvp_delta(&mut inner) {
603                    check_track_property_values(&nested)?;
604                }
605            }
606            KvpValue::Bytes(_) => {}
607        }
608    }
609    Ok(())
610}
611
612/// Decode the Track Properties that fill the tail of a control message.
613///
614/// [`decode_kvp_delta`] with the Property registry's value rules applied. The
615/// two are separate because that function also reads Setup Options, which are a
616/// third namespace numbering its entries independently of this one.
617fn decode_track_properties(buf: &mut impl Buf) -> Result<Vec<KeyValuePair>, CodecError> {
618    let properties = decode_kvp_delta(buf)?;
619    check_track_property_values(&properties)?;
620    Ok(properties)
621}
622
623/// Encode a control message's Track Properties.
624///
625/// Held to the same value ranges as the decoder. A value this codec refuses to
626/// read is one it must not write: the peer that receives it is required to close
627/// the session, so the sender's first sign of trouble would be the session
628/// going.
629fn encode_track_properties(
630    properties: &[KeyValuePair],
631    buf: &mut impl BufMut,
632) -> Result<(), CodecError> {
633    check_track_property_values(properties)?;
634    encode_kvp_delta(properties, buf)
635}
636
637/// The Setup Option types this draft defines.
638///
639/// Section 10.3.1 assigns PATH, AUTHORIZATION TOKEN, MAX_AUTH_TOKEN_CACHE_SIZE, AUTHORITY and
640/// MOQT_IMPLEMENTATION.
641///
642/// The list exists for one rule and one direction. Section 10.3: "Receivers
643/// MUST allow duplicates of unknown Setup Options." A receiver may therefore
644/// refuse a repeat only of a type it can name, and an option outside this list
645/// is one an extension defined and this codec has no business closing a session
646/// over. Nothing else reads it - unknown options are still decoded and carried,
647/// as "Receivers MUST ignore unrecognized Setup Options" requires.
648const KNOWN_SETUP_OPTIONS: &[u64] = &[0x01, 0x03, 0x04, 0x05, 0x07];
649
650/// The one Setup Option whose definition allows more than one instance.
651///
652/// Section 10.3.1.4: "The AUTHORIZATION TOKEN Setup Option (Option Type 0x03)
653/// is functionally equivalent to the AUTHORIZATION TOKEN message parameter...
654/// The endpoint can specify one or more tokens in SETUP that the peer can use to
655/// authorize MOQT session establishment." That is the "unless the option
656/// definition explicitly allows multiple instances" carve-out, and it is the
657/// only one on this draft.
658const REPEATABLE_SETUP_OPTION: u64 = 0x03;
659
660/// Decode the Setup Options of a SETUP message.
661///
662/// Section 10.3: "Senders MUST NOT repeat the same Option Type in a message
663/// unless the option definition explicitly allows multiple instances. Receivers
664/// MUST allow duplicates of unknown Setup Options."
665///
666/// The second sentence is why this is not the mirror of
667/// [`encode_setup_options`]: a repeat of a type this draft names is refused, and
668/// a repeat of any other type is carried. Types ascend and are delta-encoded, so
669/// a repeat is always a zero delta against the option before it.
670fn decode_setup_options(buf: &mut impl Buf) -> Result<Vec<KeyValuePair>, CodecError> {
671    let options = decode_kvp_delta(buf)?;
672    for (i, option) in options.iter().enumerate() {
673        let key = option.key.into_inner();
674        if key == REPEATABLE_SETUP_OPTION || !KNOWN_SETUP_OPTIONS.contains(&key) {
675            continue;
676        }
677        if options[..i].iter().any(|earlier| earlier.key == option.key) {
678            return Err(CodecError::DuplicateParameter(key));
679        }
680    }
681    check_authorization_tokens(&options)?;
682    Ok(options)
683}
684
685/// Encode the Setup Options of a SETUP message.
686///
687/// The sender's half of the same sentence, and it is the wider half: "Senders
688/// MUST NOT repeat the same Option Type in a message" names no exception for
689/// types the sender does not recognise, so every repeat is refused here except
690/// the one the draft allows. A caller holding an option this codec has never
691/// heard of still may not send it twice.
692///
693/// The token is in this namespace as well, and is held to its structure here for
694/// the reason [`encode_parameters`] gives.
695fn encode_setup_options(options: &[KeyValuePair], buf: &mut impl BufMut) -> Result<(), CodecError> {
696    check_authorization_tokens(options)?;
697    for (i, option) in options.iter().enumerate() {
698        if option.key.into_inner() == REPEATABLE_SETUP_OPTION {
699            continue;
700        }
701        if options[..i].iter().any(|earlier| earlier.key == option.key) {
702            return Err(CodecError::DuplicateParameter(option.key.into_inner()));
703        }
704    }
705    encode_kvp_delta(options, buf)
706}
707
708// ============================================================
709// Message Types
710// ============================================================
711
712#[derive(Debug, Clone, Copy, PartialEq, Eq)]
713#[repr(u64)]
714pub enum MessageType {
715    RequestUpdate = 0x02,
716    Subscribe = 0x03,
717    SubscribeOk = 0x04,
718    RequestError = 0x05,
719    PublishNamespace = 0x06,
720    /// REQUEST_OK (0x07). PUBLISH_OK is now an alias of this type.
721    RequestOk = 0x07,
722    Namespace = 0x08,
723    PublishDone = 0x0B,
724    TrackStatus = 0x0D,
725    NamespaceDone = 0x0E,
726    PublishBlocked = 0x0F,
727    GoAway = 0x10,
728    Fetch = 0x16,
729    FetchOk = 0x18,
730    Publish = 0x1D,
731    /// SUBSCRIBE_NAMESPACE (renumbered to 0x50 in draft-18).
732    SubscribeNamespace = 0x50,
733    /// SUBSCRIBE_TRACKS (new message in draft-18).
734    SubscribeTracks = 0x51,
735    Setup = 0x2F00,
736}
737
738impl MessageType {
739    pub fn from_id(id: u64) -> Option<Self> {
740        match id {
741            0x02 => Some(MessageType::RequestUpdate),
742            0x03 => Some(MessageType::Subscribe),
743            0x04 => Some(MessageType::SubscribeOk),
744            0x05 => Some(MessageType::RequestError),
745            0x06 => Some(MessageType::PublishNamespace),
746            0x07 => Some(MessageType::RequestOk),
747            0x08 => Some(MessageType::Namespace),
748            0x0B => Some(MessageType::PublishDone),
749            0x0D => Some(MessageType::TrackStatus),
750            0x0E => Some(MessageType::NamespaceDone),
751            0x0F => Some(MessageType::PublishBlocked),
752            0x10 => Some(MessageType::GoAway),
753            0x16 => Some(MessageType::Fetch),
754            0x18 => Some(MessageType::FetchOk),
755            0x1D => Some(MessageType::Publish),
756            0x50 => Some(MessageType::SubscribeNamespace),
757            0x51 => Some(MessageType::SubscribeTracks),
758            0x2F00 => Some(MessageType::Setup),
759            _ => None,
760        }
761    }
762
763    pub fn id(&self) -> u64 {
764        *self as u64
765    }
766
767    /// This type's name in the shared vector corpus: the `message_type` its
768    /// draft's `codec/messages/*.json` files carry, in `snake_case`.
769    pub fn name(&self) -> &'static str {
770        match self {
771            MessageType::RequestUpdate => "request_update",
772            MessageType::Subscribe => "subscribe",
773            MessageType::SubscribeOk => "subscribe_ok",
774            MessageType::RequestError => "request_error",
775            MessageType::PublishNamespace => "publish_namespace",
776            MessageType::RequestOk => "request_ok",
777            MessageType::Namespace => "namespace",
778            MessageType::PublishDone => "publish_done",
779            MessageType::TrackStatus => "track_status",
780            MessageType::NamespaceDone => "namespace_done",
781            MessageType::PublishBlocked => "publish_blocked",
782            MessageType::GoAway => "goaway",
783            MessageType::Fetch => "fetch",
784            MessageType::FetchOk => "fetch_ok",
785            MessageType::Publish => "publish",
786            MessageType::SubscribeNamespace => "subscribe_namespace",
787            MessageType::SubscribeTracks => "subscribe_tracks",
788            MessageType::Setup => "setup",
789        }
790    }
791}
792
793// ============================================================
794// Session Lifecycle Messages
795// ============================================================
796
797/// Unified SETUP (0x2F00).
798#[derive(Debug, Clone, PartialEq, Eq)]
799pub struct Setup {
800    pub options: Vec<KeyValuePair>,
801}
802
803/// GOAWAY (0x10). Sent on the control stream (with `request_id`) or on an
804/// individual request stream (without `request_id`).
805#[derive(Debug, Clone, PartialEq, Eq)]
806pub struct GoAway {
807    pub new_session_uri: Vec<u8>,
808    pub timeout: VarInt,
809    /// Present only when sent on the control stream — identifies the
810    /// smallest peer Request ID that may not have been processed.
811    pub request_id: Option<VarInt>,
812}
813
814// ============================================================
815// Consolidated Response Messages
816// ============================================================
817
818/// REQUEST_OK (0x07). Used as a generic OK response and as the alias for
819/// PUBLISH_OK / REQUEST_UPDATE_OK / TRACK_STATUS_OK / SUBSCRIBE_NAMESPACE_OK
820/// / PUBLISH_NAMESPACE_OK.
821///
822/// `track_properties` is only populated for TRACK_STATUS_OK; for every
823/// other shape it MUST be empty (length implicit from the message length).
824#[derive(Debug, Clone, PartialEq, Eq)]
825pub struct RequestOk {
826    pub parameters: Vec<KeyValuePair>,
827    pub track_properties: Vec<KeyValuePair>,
828}
829
830/// Optional Redirect structure carried in REQUEST_ERROR with code 0x34.
831#[derive(Debug, Clone, PartialEq, Eq)]
832pub struct Redirect {
833    pub connect_uri: Vec<u8>,
834    pub track_namespace: TrackNamespace,
835    pub track_name: Vec<u8>,
836}
837
838/// REQUEST_ERROR (0x05). Adds an optional Redirect structure when
839/// `error_code` is REDIRECT (0x34).
840#[derive(Debug, Clone, PartialEq, Eq)]
841pub struct RequestError {
842    pub error_code: VarInt,
843    pub retry_interval: VarInt,
844    pub reason_phrase: Vec<u8>,
845    pub redirect: Option<Redirect>,
846}
847
848/// REQUEST_ERROR error codes that gain dedicated meaning in draft-18.
849pub mod request_error_codes {
850    /// New in draft-18: a Mandatory Track Property the receiver does not
851    /// understand.
852    pub const UNSUPPORTED_EXTENSION: u64 = 0x33;
853    /// New in draft-18: response carries a [`super::Redirect`] structure.
854    pub const REDIRECT: u64 = 0x34;
855}
856
857// ============================================================
858// Subscribe Messages
859// ============================================================
860
861#[derive(Debug, Clone, PartialEq, Eq)]
862pub struct Subscribe {
863    pub request_id: VarInt,
864    pub track_namespace: TrackNamespace,
865    pub track_name: Vec<u8>,
866    pub parameters: Vec<KeyValuePair>,
867}
868
869/// SUBSCRIBE_OK (0x04).
870#[derive(Debug, Clone, PartialEq, Eq)]
871pub struct SubscribeOk {
872    pub track_alias: VarInt,
873    pub parameters: Vec<KeyValuePair>,
874    pub track_properties: Vec<KeyValuePair>,
875}
876
877#[derive(Debug, Clone, PartialEq, Eq)]
878pub struct RequestUpdate {
879    pub request_id: VarInt,
880    pub parameters: Vec<KeyValuePair>,
881}
882
883// ============================================================
884// Publish Messages
885// ============================================================
886
887#[derive(Debug, Clone, PartialEq, Eq)]
888pub struct Publish {
889    pub request_id: VarInt,
890    pub track_namespace: TrackNamespace,
891    pub track_name: Vec<u8>,
892    pub track_alias: VarInt,
893    pub parameters: Vec<KeyValuePair>,
894    pub track_properties: Vec<KeyValuePair>,
895}
896
897/// PUBLISH_DONE (0x0B). Status codes 0x5/0x6 are swapped vs draft-17.
898#[derive(Debug, Clone, PartialEq, Eq)]
899pub struct PublishDone {
900    pub status_code: VarInt,
901    pub stream_count: VarInt,
902    pub reason_phrase: Vec<u8>,
903}
904
905/// Numeric values for the [`PublishDone::status_code`] field.
906pub mod publish_done_codes {
907    /// Draft-18: TOO_FAR_BEHIND is 0x05 (was 0x06 in draft-17).
908    pub const TOO_FAR_BEHIND: u64 = 0x05;
909    /// Draft-18: EXPIRED is 0x06 (was 0x05 in draft-17).
910    pub const EXPIRED: u64 = 0x06;
911}
912
913// ============================================================
914// Publish Namespace Messages
915// ============================================================
916
917#[derive(Debug, Clone, PartialEq, Eq)]
918pub struct PublishNamespace {
919    pub request_id: VarInt,
920    pub track_namespace: TrackNamespace,
921    pub parameters: Vec<KeyValuePair>,
922}
923
924// ============================================================
925// Namespace Messages
926// ============================================================
927
928#[derive(Debug, Clone, PartialEq, Eq)]
929pub struct Namespace {
930    pub namespace_suffix: TrackNamespace,
931}
932
933#[derive(Debug, Clone, PartialEq, Eq)]
934pub struct NamespaceDone {
935    pub namespace_suffix: TrackNamespace,
936}
937
938// ============================================================
939// Subscribe Namespace / Tracks Messages
940// ============================================================
941
942/// SUBSCRIBE_NAMESPACE (0x50). Subscribes to NAMESPACE / NAMESPACE_DONE
943/// advertisements for namespaces matching `namespace_prefix`. The
944/// `subscribe_options` byte from draft-17 is removed; namespace subscriptions
945/// only produce NAMESPACE / NAMESPACE_DONE.
946#[derive(Debug, Clone, PartialEq, Eq)]
947pub struct SubscribeNamespace {
948    pub request_id: VarInt,
949    pub namespace_prefix: TrackNamespace,
950    pub parameters: Vec<KeyValuePair>,
951}
952
953/// SUBSCRIBE_TRACKS (0x51, new in draft-18). Subscribes to PUBLISH messages
954/// for tracks whose namespace matches `namespace_prefix`. Carries the FORWARD
955/// parameter (Section 10.2.12), which on drafts 15 through 17 may appear on
956/// SUBSCRIBE_NAMESPACE instead.
957#[derive(Debug, Clone, PartialEq, Eq)]
958pub struct SubscribeTracks {
959    pub request_id: VarInt,
960    pub namespace_prefix: TrackNamespace,
961    pub parameters: Vec<KeyValuePair>,
962}
963
964// ============================================================
965// Track Status Messages
966// ============================================================
967
968#[derive(Debug, Clone, PartialEq, Eq)]
969pub struct TrackStatus {
970    pub request_id: VarInt,
971    pub track_namespace: TrackNamespace,
972    pub track_name: Vec<u8>,
973    pub parameters: Vec<KeyValuePair>,
974}
975
976// ============================================================
977// Fetch Messages
978// ============================================================
979
980#[derive(Debug, Clone, Copy, PartialEq, Eq)]
981#[repr(u64)]
982pub enum FetchType {
983    Standalone = 1,
984    RelativeJoining = 2,
985    AbsoluteJoining = 3,
986}
987
988impl FetchType {
989    pub fn from_u64(v: u64) -> Option<Self> {
990        match v {
991            1 => Some(FetchType::Standalone),
992            2 => Some(FetchType::RelativeJoining),
993            3 => Some(FetchType::AbsoluteJoining),
994            _ => None,
995        }
996    }
997}
998
999#[derive(Debug, Clone, PartialEq, Eq)]
1000pub struct Fetch {
1001    pub request_id: VarInt,
1002    pub fetch_type: FetchType,
1003    pub fetch_payload: FetchPayload,
1004    pub parameters: Vec<KeyValuePair>,
1005}
1006
1007#[derive(Debug, Clone, PartialEq, Eq)]
1008pub enum FetchPayload {
1009    Standalone {
1010        track_namespace: TrackNamespace,
1011        track_name: Vec<u8>,
1012        start_group: VarInt,
1013        start_object: VarInt,
1014        end_group: VarInt,
1015        end_object: VarInt,
1016    },
1017    Joining {
1018        joining_request_id: VarInt,
1019        joining_start: VarInt,
1020    },
1021}
1022
1023/// FETCH_OK (0x18). `end_of_track` is uint8.
1024#[derive(Debug, Clone, PartialEq, Eq)]
1025pub struct FetchOk {
1026    pub end_of_track: u8,
1027    pub end_group: VarInt,
1028    pub end_object: VarInt,
1029    pub parameters: Vec<KeyValuePair>,
1030    pub track_properties: Vec<KeyValuePair>,
1031}
1032
1033// ============================================================
1034// Publish Blocked
1035// ============================================================
1036
1037#[derive(Debug, Clone, PartialEq, Eq)]
1038pub struct PublishBlocked {
1039    pub namespace_suffix: TrackNamespace,
1040    pub track_name: Vec<u8>,
1041}
1042
1043// ============================================================
1044// Unified Message Enum
1045// ============================================================
1046
1047#[derive(Debug, Clone, PartialEq, Eq)]
1048pub enum ControlMessage {
1049    Setup(Setup),
1050    GoAway(GoAway),
1051    RequestOk(RequestOk),
1052    RequestError(RequestError),
1053    Subscribe(Subscribe),
1054    SubscribeOk(SubscribeOk),
1055    RequestUpdate(RequestUpdate),
1056    Publish(Publish),
1057    PublishDone(PublishDone),
1058    PublishNamespace(PublishNamespace),
1059    Namespace(Namespace),
1060    NamespaceDone(NamespaceDone),
1061    SubscribeNamespace(SubscribeNamespace),
1062    SubscribeTracks(SubscribeTracks),
1063    TrackStatus(TrackStatus),
1064    Fetch(Fetch),
1065    FetchOk(FetchOk),
1066    PublishBlocked(PublishBlocked),
1067}
1068
1069/// Refuse a FETCH whose range ends before it starts.
1070///
1071/// Section 10.12.3: "Fetch specifies an inclusive range of Objects starting at
1072/// Start Location and ending at End Location. End Location MUST specify the
1073/// same or a larger Location than Start Location for Standalone and Absolute Joining Fetches." A Joining Fetch names
1074/// no explicit range - it is computed from the subscription it joins - so only
1075/// a standalone range is checked here.
1076///
1077/// SUBSCRIBE is not checked here, and needs no check: this draft's
1078/// AbsoluteRange filter carries an End Group Delta measured from the start
1079/// location rather than an absolute End Group, so an end before the start
1080/// has no encoding.
1081///
1082/// Applied on both sides. A range that ends before it starts selects nothing,
1083/// and the peer's only recourse is an error response or a session close, so
1084/// writing one is not a way to ask for anything.
1085fn check_ranges(message: &ControlMessage) -> Result<(), CodecError> {
1086    match message {
1087        ControlMessage::Fetch(m) => match &m.fetch_payload {
1088            FetchPayload::Standalone {
1089                start_group, start_object, end_group, end_object, ..
1090            } => check_location_range(
1091                start_group.into_inner(),
1092                start_object.into_inner(),
1093                end_group.into_inner(),
1094                end_object.into_inner(),
1095            ),
1096            FetchPayload::Joining { .. } => Ok(()),
1097        },
1098        _ => Ok(()),
1099    }
1100}
1101
1102/// Refuse a message whose discriminator disagrees with the fields beside it.
1103///
1104/// Two draft-18 messages carry a field that says which of the following fields
1105/// are on the wire: FETCH's Fetch Type, and REQUEST_ERROR's Error Code, whose
1106/// REDIRECT value (0x34) is what puts the Redirect structure on the wire. This
1107/// codec holds the alternatives in an enum and an `Option`, so a value can say
1108/// one thing in its discriminator and another in its body, and the two sides of
1109/// the codec resolve that differently — the encoder writes whatever the body
1110/// holds, and the decoder reads whatever the discriminator announces.
1111///
1112/// The result is a message that does not survive its own round trip:
1113///
1114/// - A FETCH whose type says Standalone and whose body is a joining pair
1115///   encodes to a joining request id and a joining start where a Track
1116///   Namespace and a Track Name belong, and comes back as a Standalone fetch of
1117///   a track named after two integers — or, more often, as an error, which at
1118///   least is honest. The two joining types share one body shape, so the check
1119///   is between Standalone and everything else rather than one arm per type.
1120/// - A REQUEST_ERROR with code REDIRECT and no Redirect body encodes to a
1121///   message that ends where the decoder expects a Connect URI length, so the
1122///   peer reads the redirect out of whatever follows or runs off the end. The
1123///   mirror case is quieter and no better: a Redirect body under any other
1124///   error code is written out and then skipped by a decoder that was never
1125///   told to look for it, so the sender believes it redirected a peer that
1126///   never saw a redirect.
1127///
1128/// Refusing at the encoder keeps the two readings from ever diverging on the
1129/// wire.
1130fn check_discriminators(message: &ControlMessage) -> Result<(), CodecError> {
1131    match message {
1132        ControlMessage::Fetch(m) => {
1133            let body_is_standalone = matches!(m.fetch_payload, FetchPayload::Standalone { .. });
1134            if body_is_standalone != (m.fetch_type == FetchType::Standalone) {
1135                return Err(CodecError::InvalidField);
1136            }
1137        }
1138        ControlMessage::RequestError(m) => {
1139            let code_is_redirect = m.error_code.into_inner() == request_error_codes::REDIRECT;
1140            if code_is_redirect != m.redirect.is_some() {
1141                return Err(CodecError::InvalidField);
1142            }
1143        }
1144        _ => {}
1145    }
1146    Ok(())
1147}
1148
1149/// Whether draft-18 lets Message Parameter `key` appear in `message`.
1150///
1151/// Section 10.2.1: "Each Message Parameter definition indicates the message
1152/// types in which it can appear. If it appears in some other type of message,
1153/// the receiving endpoint MUST close the connection with a PROTOCOL_VIOLATION."
1154/// One arm per entry in the Message Parameters registry (Section 15.7),
1155/// carrying the message types that entry's own subsection names.
1156///
1157/// Five of the names are one wire type. Section 10.5: "This document uses the
1158/// shorthand PUBLISH_OK, REQUEST_UPDATE_OK, TRACK_STATUS_OK,
1159/// SUBSCRIBE_NAMESPACE_OK, and PUBLISH_NAMESPACE_OK to refer to a REQUEST_OK
1160/// sent in response to the corresponding request type." Which one a given
1161/// REQUEST_OK is depends on the request its Request ID answers, which is
1162/// session state and not in the frame, so each of those names widens the same
1163/// arm and a REQUEST_OK is held to their union. Draft-17 needed none of this:
1164/// there PUBLISH_OK is its own message type, and the two drafts' tables differ
1165/// accordingly.
1166///
1167/// Where a name is qualified — "REQUEST_UPDATE (for a subscription)",
1168/// "REQUEST_UPDATE for a SUBSCRIBE_NAMESPACE or SUBSCRIBE_TRACKS request" — the
1169/// qualifier says which instance of the destination is meant rather than naming
1170/// another, and settling it needs the same session state, so the message type
1171/// alone decides.
1172///
1173/// FETCH_OK has no arm in the table below, and that is the draft's doing rather
1174/// than an omission here: Section 10.13 gives it a Parameters field and no
1175/// parameter definition names it, so every type this draft defines is "some
1176/// other type of message" there.
1177///
1178/// The table decides scope only. A type this draft does not define has no scope
1179/// to be outside of and is answered by [`CodecError::UnknownMessageParameter`],
1180/// which is why the final arm carries rather than refuses.
1181fn parameter_in_scope(key: u64, message: MessageType) -> bool {
1182    use MessageType as M;
1183    match key {
1184        // Section 10.2.4 OBJECT_DELIVERY_TIMEOUT: "It MAY appear in a
1185        // PUBLISH_OK, SUBSCRIBE, or REQUEST_UPDATE message."
1186        0x02 => matches!(message, M::RequestOk | M::Subscribe | M::RequestUpdate),
1187        // Section 10.2.2 AUTHORIZATION TOKEN: "It MAY appear in a PUBLISH,
1188        // SUBSCRIBE, REQUEST_UPDATE, SUBSCRIBE_NAMESPACE, SUBSCRIBE_TRACKS,
1189        // PUBLISH_NAMESPACE, TRACK_STATUS or FETCH message."
1190        0x03 => matches!(
1191            message,
1192            M::Publish
1193                | M::Subscribe
1194                | M::RequestUpdate
1195                | M::SubscribeNamespace
1196                | M::SubscribeTracks
1197                | M::PublishNamespace
1198                | M::TrackStatus
1199                | M::Fetch
1200        ),
1201        // Section 10.2.6 RENDEZVOUS TIMEOUT: it "MAY appear in a SUBSCRIBE
1202        // message".
1203        0x04 => matches!(message, M::Subscribe),
1204        // Section 10.2.3 SUBGROUP_DELIVERY_TIMEOUT: "It MAY appear in a
1205        // PUBLISH_OK, SUBSCRIBE, or REQUEST_UPDATE message."
1206        0x06 => matches!(message, M::RequestOk | M::Subscribe | M::RequestUpdate),
1207        // Section 10.2.10 EXPIRES: "It MAY appear in SUBSCRIBE_OK, PUBLISH,
1208        // PUBLISH_OK, or REQUEST_UPDATE_OK."
1209        0x08 => matches!(message, M::SubscribeOk | M::Publish | M::RequestOk),
1210        // Section 10.2.11 LARGEST OBJECT: "It MAY appear in SUBSCRIBE_OK,
1211        // PUBLISH, REQUEST_UPDATE_OK, or TRACK_STATUS_OK."
1212        0x09 => matches!(message, M::SubscribeOk | M::Publish | M::RequestOk),
1213        // Section 10.2.5 FILL TIMEOUT: it "MAY appear in a FETCH message".
1214        0x0A => matches!(message, M::Fetch),
1215        // Section 10.2.12 FORWARD: "It MAY appear in SUBSCRIBE, REQUEST_UPDATE
1216        // (for a subscription), PUBLISH, PUBLISH_OK and SUBSCRIBE_TRACKS."
1217        0x10 => matches!(
1218            message,
1219            M::Subscribe | M::RequestUpdate | M::Publish | M::RequestOk | M::SubscribeTracks
1220        ),
1221        // Section 10.2.7 SUBSCRIBER PRIORITY: "It MAY appear in a SUBSCRIBE,
1222        // FETCH, REQUEST_UPDATE (for a subscription or FETCH), or PUBLISH_OK
1223        // message."
1224        0x20 => matches!(message, M::Subscribe | M::Fetch | M::RequestUpdate | M::RequestOk),
1225        // Section 10.2.9 SUBSCRIPTION FILTER: "It MAY appear in a SUBSCRIBE,
1226        // PUBLISH_OK or REQUEST_UPDATE (for a subscription) message."
1227        0x21 => matches!(message, M::Subscribe | M::RequestOk | M::RequestUpdate),
1228        // Section 10.2.8 GROUP ORDER: "It MAY appear in a SUBSCRIBE,
1229        // PUBLISH_OK, or FETCH."
1230        0x22 => matches!(message, M::Subscribe | M::RequestOk | M::Fetch),
1231        // Section 10.2.13 NEW GROUP REQUEST: "It MAY appear in PUBLISH_OK,
1232        // SUBSCRIBE or REQUEST_UPDATE for a subscription."
1233        0x32 => matches!(message, M::RequestOk | M::Subscribe | M::RequestUpdate),
1234        // Section 10.2.14 TRACK_NAMESPACE_PREFIX: "It MAY appear in
1235        // REQUEST_UPDATE for a SUBSCRIBE_NAMESPACE or SUBSCRIBE_TRACKS
1236        // request." The two named there are the request being updated, not two
1237        // more places the parameter may be written.
1238        0x34 => matches!(message, M::RequestUpdate),
1239        _ => true,
1240    }
1241}
1242
1243/// Refuse a message carrying a Message Parameter its own definition does not
1244/// place there.
1245///
1246/// Section 10.2.1 answers this with a close, which the drafts below do not.
1247/// Draft-16 Section 9.2.2, and drafts 07 through 15 under the older name
1248/// Version Specific Parameters, end the same sentence "it MUST be ignored" —
1249/// so this check belongs to drafts 17, 18 and 19 and to no draft before them.
1250///
1251/// Applied on both sides. A parameter outside its scope is one the peer must
1252/// close the session over, so writing one is a way to end a session rather than
1253/// a way to ask for anything.
1254fn check_parameter_scope(message: &ControlMessage) -> Result<(), CodecError> {
1255    let parameters = match message {
1256        ControlMessage::RequestOk(m) => &m.parameters,
1257        ControlMessage::Subscribe(m) => &m.parameters,
1258        ControlMessage::SubscribeOk(m) => &m.parameters,
1259        ControlMessage::RequestUpdate(m) => &m.parameters,
1260        ControlMessage::Publish(m) => &m.parameters,
1261        ControlMessage::PublishNamespace(m) => &m.parameters,
1262        ControlMessage::SubscribeNamespace(m) => &m.parameters,
1263        ControlMessage::SubscribeTracks(m) => &m.parameters,
1264        ControlMessage::TrackStatus(m) => &m.parameters,
1265        ControlMessage::Fetch(m) => &m.parameters,
1266        ControlMessage::FetchOk(m) => &m.parameters,
1267        // No Message Parameters field. SETUP is named here rather than left to
1268        // a wildcard because the draft says why it can never have one: Section
1269        // 10.2.1 notes that "since Setup Options use a separate namespace, it
1270        // is impossible for Message Parameters to appear in Setup messages",
1271        // and this codec keeps the two namespaces in separate fields.
1272        ControlMessage::Setup(_)
1273        | ControlMessage::GoAway(_)
1274        | ControlMessage::RequestError(_)
1275        | ControlMessage::PublishDone(_)
1276        | ControlMessage::Namespace(_)
1277        | ControlMessage::NamespaceDone(_)
1278        | ControlMessage::PublishBlocked(_) => return Ok(()),
1279    };
1280
1281    let message_type = message.message_type();
1282    for parameter in parameters {
1283        let key = parameter.key.into_inner();
1284        if !parameter_in_scope(key, message_type) {
1285            return Err(CodecError::ParameterOutOfScope { key, message_type: message_type.id() });
1286        }
1287    }
1288    Ok(())
1289}
1290
1291impl ControlMessage {
1292    pub fn encode(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
1293        check_discriminators(self)?;
1294        check_ranges(self)?;
1295        check_parameter_scope(self)?;
1296        let mut payload = Vec::with_capacity(256);
1297        self.encode_payload(&mut payload)?;
1298
1299        if payload.len() > MAX_MESSAGE_LENGTH {
1300            return Err(CodecError::MessageTooLong(payload.len()));
1301        }
1302
1303        let msg_type = self.message_type();
1304        VarInt::from_usize(msg_type.id() as usize).encode_moqt::<Wire>(buf);
1305        // Draft-18: 16-bit length (big-endian)
1306        buf.put_u16(payload.len() as u16);
1307        buf.put_slice(&payload);
1308        Ok(())
1309    }
1310
1311    pub fn decode(buf: &mut impl Buf) -> Result<Self, CodecError> {
1312        let type_id = VarInt::decode_moqt::<Wire>(buf)?.into_inner();
1313        let msg_type =
1314            MessageType::from_id(type_id).ok_or(CodecError::UnknownMessageType(type_id))?;
1315        // Draft-18: 16-bit length (big-endian)
1316        if buf.remaining() < 2 {
1317            return Err(CodecError::UnexpectedEnd);
1318        }
1319        let payload_len = buf.get_u16() as usize;
1320        if buf.remaining() < payload_len {
1321            return Err(CodecError::UnexpectedEnd);
1322        }
1323        let payload_bytes = buf.copy_to_bytes(payload_len);
1324        let mut payload = &payload_bytes[..];
1325        let msg = match Self::decode_payload(msg_type, &mut payload) {
1326            Ok(msg) => msg,
1327            // The fields wanted more bytes than the Length allowed. This buffer
1328            // is already bounded by that Length, so running out inside it cannot
1329            // mean the message is still arriving - which is what the same error
1330            // means everywhere else, and why a reader loops on it rather than
1331            // closing. Here there is nothing left to arrive.
1332            Err(
1333                CodecError::UnexpectedEnd
1334                | CodecError::Kvp(crate::kvp::KvpError::UnexpectedEnd)
1335                | CodecError::Kvp(crate::kvp::KvpError::VarInt(
1336                    crate::varint::VarIntError::UnexpectedEnd,
1337                ))
1338                | CodecError::VarInt(crate::varint::VarIntError::UnexpectedEnd),
1339            ) => {
1340                return Err(CodecError::ControlMessageLengthMismatch {
1341                    declared: payload_len,
1342                    detail: "its fields ran past the end",
1343                });
1344            }
1345            Err(e) => return Err(e),
1346        };
1347        check_ranges(&msg)?;
1348        check_parameter_scope(&msg)?;
1349        // The declared length is part of the message, not a hint. Bytes left over
1350        // after the fields have been read mean the sender and this reader disagree
1351        // about the shape of the message, and guessing which of the two is right
1352        // is how a trailing field gets silently dropped.
1353        if payload.has_remaining() {
1354            return Err(CodecError::ControlMessageLengthMismatch {
1355                declared: payload_len,
1356                detail: "its fields left bytes unread",
1357            });
1358        }
1359        Ok(msg)
1360    }
1361
1362    fn encode_payload(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
1363        match self {
1364            ControlMessage::Setup(m) => {
1365                encode_setup_options(&m.options, buf)?;
1366            }
1367            ControlMessage::GoAway(m) => {
1368                if m.new_session_uri.len() > MAX_GOAWAY_URI_LENGTH {
1369                    return Err(CodecError::GoAwayUriTooLong);
1370                }
1371                VarInt::from_usize(m.new_session_uri.len()).encode_moqt::<Wire>(buf);
1372                buf.put_slice(&m.new_session_uri);
1373                m.timeout.encode_moqt::<Wire>(buf);
1374                if let Some(rid) = &m.request_id {
1375                    rid.encode_moqt::<Wire>(buf);
1376                }
1377            }
1378            ControlMessage::RequestOk(m) => {
1379                encode_parameters(&m.parameters, buf)?;
1380                encode_track_properties(&m.track_properties, buf)?;
1381            }
1382            ControlMessage::RequestError(m) => {
1383                if m.reason_phrase.len() > MAX_REASON_PHRASE_LENGTH {
1384                    return Err(CodecError::ReasonPhraseTooLong);
1385                }
1386                m.error_code.encode_moqt::<Wire>(buf);
1387                m.retry_interval.encode_moqt::<Wire>(buf);
1388                VarInt::from_usize(m.reason_phrase.len()).encode_moqt::<Wire>(buf);
1389                buf.put_slice(&m.reason_phrase);
1390                if let Some(r) = &m.redirect {
1391                    r.track_namespace.validate_moqt()?;
1392                    check_full_track_name(&r.track_namespace, &r.track_name)?;
1393                    VarInt::from_usize(r.connect_uri.len()).encode_moqt::<Wire>(buf);
1394                    buf.put_slice(&r.connect_uri);
1395                    r.track_namespace.encode_moqt::<Wire>(buf);
1396                    VarInt::from_usize(r.track_name.len()).encode_moqt::<Wire>(buf);
1397                    buf.put_slice(&r.track_name);
1398                }
1399            }
1400            ControlMessage::Subscribe(m) => {
1401                m.track_namespace.validate_moqt()?;
1402                check_full_track_name(&m.track_namespace, &m.track_name)?;
1403                m.request_id.encode_moqt::<Wire>(buf);
1404                m.track_namespace.encode_moqt::<Wire>(buf);
1405                VarInt::from_usize(m.track_name.len()).encode_moqt::<Wire>(buf);
1406                buf.put_slice(&m.track_name);
1407                encode_parameters(&m.parameters, buf)?;
1408            }
1409            ControlMessage::SubscribeOk(m) => {
1410                m.track_alias.encode_moqt::<Wire>(buf);
1411                encode_parameters(&m.parameters, buf)?;
1412                encode_track_properties(&m.track_properties, buf)?;
1413            }
1414            ControlMessage::RequestUpdate(m) => {
1415                m.request_id.encode_moqt::<Wire>(buf);
1416                encode_parameters(&m.parameters, buf)?;
1417            }
1418            ControlMessage::Publish(m) => {
1419                m.track_namespace.validate_moqt()?;
1420                check_full_track_name(&m.track_namespace, &m.track_name)?;
1421                m.request_id.encode_moqt::<Wire>(buf);
1422                m.track_namespace.encode_moqt::<Wire>(buf);
1423                VarInt::from_usize(m.track_name.len()).encode_moqt::<Wire>(buf);
1424                buf.put_slice(&m.track_name);
1425                m.track_alias.encode_moqt::<Wire>(buf);
1426                encode_parameters(&m.parameters, buf)?;
1427                encode_track_properties(&m.track_properties, buf)?;
1428            }
1429            ControlMessage::PublishDone(m) => {
1430                if m.reason_phrase.len() > MAX_REASON_PHRASE_LENGTH {
1431                    return Err(CodecError::ReasonPhraseTooLong);
1432                }
1433                m.status_code.encode_moqt::<Wire>(buf);
1434                m.stream_count.encode_moqt::<Wire>(buf);
1435                VarInt::from_usize(m.reason_phrase.len()).encode_moqt::<Wire>(buf);
1436                buf.put_slice(&m.reason_phrase);
1437            }
1438            ControlMessage::PublishNamespace(m) => {
1439                m.track_namespace.validate_moqt()?;
1440                m.request_id.encode_moqt::<Wire>(buf);
1441                m.track_namespace.encode_moqt::<Wire>(buf);
1442                encode_parameters(&m.parameters, buf)?;
1443            }
1444            ControlMessage::Namespace(m) => {
1445                m.namespace_suffix.validate_moqt()?;
1446                m.namespace_suffix.encode_moqt::<Wire>(buf);
1447            }
1448            ControlMessage::NamespaceDone(m) => {
1449                m.namespace_suffix.validate_moqt()?;
1450                m.namespace_suffix.encode_moqt::<Wire>(buf);
1451            }
1452            ControlMessage::SubscribeNamespace(m) => {
1453                m.namespace_prefix.validate_moqt()?;
1454                m.request_id.encode_moqt::<Wire>(buf);
1455                m.namespace_prefix.encode_moqt::<Wire>(buf);
1456                encode_parameters(&m.parameters, buf)?;
1457            }
1458            ControlMessage::SubscribeTracks(m) => {
1459                m.namespace_prefix.validate_moqt()?;
1460                m.request_id.encode_moqt::<Wire>(buf);
1461                m.namespace_prefix.encode_moqt::<Wire>(buf);
1462                encode_parameters(&m.parameters, buf)?;
1463            }
1464            ControlMessage::TrackStatus(m) => {
1465                m.track_namespace.validate_moqt()?;
1466                check_full_track_name(&m.track_namespace, &m.track_name)?;
1467                m.request_id.encode_moqt::<Wire>(buf);
1468                m.track_namespace.encode_moqt::<Wire>(buf);
1469                VarInt::from_usize(m.track_name.len()).encode_moqt::<Wire>(buf);
1470                buf.put_slice(&m.track_name);
1471                encode_parameters(&m.parameters, buf)?;
1472            }
1473            ControlMessage::Fetch(m) => {
1474                m.request_id.encode_moqt::<Wire>(buf);
1475                VarInt::from_usize(m.fetch_type as usize).encode_moqt::<Wire>(buf);
1476                match &m.fetch_payload {
1477                    FetchPayload::Standalone {
1478                        track_namespace,
1479                        track_name,
1480                        start_group,
1481                        start_object,
1482                        end_group,
1483                        end_object,
1484                    } => {
1485                        track_namespace.validate_moqt()?;
1486                        check_full_track_name(track_namespace, track_name)?;
1487                        track_namespace.encode_moqt::<Wire>(buf);
1488                        VarInt::from_usize(track_name.len()).encode_moqt::<Wire>(buf);
1489                        buf.put_slice(track_name);
1490                        start_group.encode_moqt::<Wire>(buf);
1491                        start_object.encode_moqt::<Wire>(buf);
1492                        end_group.encode_moqt::<Wire>(buf);
1493                        end_object.encode_moqt::<Wire>(buf);
1494                    }
1495                    FetchPayload::Joining { joining_request_id, joining_start } => {
1496                        joining_request_id.encode_moqt::<Wire>(buf);
1497                        joining_start.encode_moqt::<Wire>(buf);
1498                    }
1499                }
1500                encode_parameters(&m.parameters, buf)?;
1501            }
1502            ControlMessage::FetchOk(m) => {
1503                buf.put_u8(m.end_of_track);
1504                m.end_group.encode_moqt::<Wire>(buf);
1505                m.end_object.encode_moqt::<Wire>(buf);
1506                encode_parameters(&m.parameters, buf)?;
1507                encode_track_properties(&m.track_properties, buf)?;
1508            }
1509            ControlMessage::PublishBlocked(m) => {
1510                m.namespace_suffix.validate_moqt()?;
1511                check_full_track_name(&m.namespace_suffix, &m.track_name)?;
1512                m.namespace_suffix.encode_moqt::<Wire>(buf);
1513                VarInt::from_usize(m.track_name.len()).encode_moqt::<Wire>(buf);
1514                buf.put_slice(&m.track_name);
1515            }
1516        }
1517        Ok(())
1518    }
1519
1520    fn decode_payload(msg_type: MessageType, buf: &mut impl Buf) -> Result<Self, CodecError> {
1521        match msg_type {
1522            MessageType::Setup => {
1523                let options = decode_setup_options(buf)?;
1524                Ok(ControlMessage::Setup(Setup { options }))
1525            }
1526            MessageType::GoAway => {
1527                let uri_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1528                // Draft-18 Section 10.4: "The maximum length of the New Session
1529                // URI is 8,192 bytes. If an endpoint receives a length
1530                // exceeding the maximum, it MUST close the session with a
1531                // PROTOCOL_VIOLATION." Checked here as well as on encode: a
1532                // client migrates to this URI, so an oversize one is handed
1533                // straight to connection setup, and the codec is the only layer
1534                // that was ever going to bound it.
1535                if uri_len > MAX_GOAWAY_URI_LENGTH {
1536                    return Err(CodecError::GoAwayUriTooLong);
1537                }
1538                let uri = read_bytes(buf, uri_len)?;
1539                let timeout = VarInt::decode_moqt::<Wire>(buf)?;
1540                let request_id = if buf.has_remaining() {
1541                    Some(VarInt::decode_moqt::<Wire>(buf)?)
1542                } else {
1543                    None
1544                };
1545                Ok(ControlMessage::GoAway(GoAway { new_session_uri: uri, timeout, request_id }))
1546            }
1547            MessageType::RequestOk => {
1548                let parameters = decode_parameters(buf)?;
1549                let track_properties = decode_track_properties(buf)?;
1550                Ok(ControlMessage::RequestOk(RequestOk { parameters, track_properties }))
1551            }
1552            MessageType::RequestError => {
1553                let error_code = VarInt::decode_moqt::<Wire>(buf)?;
1554                let retry_interval = VarInt::decode_moqt::<Wire>(buf)?;
1555                let reason_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1556                // Draft-18 Section 1.4.4: "The reason phrase length has a
1557                // maximum value of 1024 bytes. If an endpoint receives a length
1558                // exceeding the maximum, it MUST close the session with a
1559                // PROTOCOL_VIOLATION". A reason phrase is diagnostic text that
1560                // implementations log and surface, so an unbounded one is a
1561                // peer-controlled amplification into whatever consumes it.
1562                if reason_len > MAX_REASON_PHRASE_LENGTH {
1563                    return Err(CodecError::ReasonPhraseTooLong);
1564                }
1565                let reason_phrase = read_bytes(buf, reason_len)?;
1566                let redirect = if error_code.into_inner() == request_error_codes::REDIRECT {
1567                    let uri_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1568                    let connect_uri = read_bytes(buf, uri_len)?;
1569                    let track_namespace = TrackNamespace::decode_allow_empty_moqt::<Wire>(buf)?;
1570                    let name_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1571                    let track_name = read_bytes(buf, name_len)?;
1572                    check_full_track_name(&track_namespace, &track_name)?;
1573                    Some(Redirect { connect_uri, track_namespace, track_name })
1574                } else {
1575                    None
1576                };
1577                Ok(ControlMessage::RequestError(RequestError {
1578                    error_code,
1579                    retry_interval,
1580                    reason_phrase,
1581                    redirect,
1582                }))
1583            }
1584            MessageType::Subscribe => {
1585                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1586                let track_namespace = TrackNamespace::decode_moqt::<Wire>(buf)?;
1587                let tn_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1588                let track_name = read_bytes(buf, tn_len)?;
1589                check_full_track_name(&track_namespace, &track_name)?;
1590                let parameters = decode_parameters(buf)?;
1591                Ok(ControlMessage::Subscribe(Subscribe {
1592                    request_id,
1593                    track_namespace,
1594                    track_name,
1595                    parameters,
1596                }))
1597            }
1598            MessageType::SubscribeOk => {
1599                let track_alias = VarInt::decode_moqt::<Wire>(buf)?;
1600                let parameters = decode_parameters(buf)?;
1601                let track_properties = decode_track_properties(buf)?;
1602                Ok(ControlMessage::SubscribeOk(SubscribeOk {
1603                    track_alias,
1604                    parameters,
1605                    track_properties,
1606                }))
1607            }
1608            MessageType::RequestUpdate => {
1609                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1610                let parameters = decode_parameters(buf)?;
1611                Ok(ControlMessage::RequestUpdate(RequestUpdate { request_id, parameters }))
1612            }
1613            MessageType::Publish => {
1614                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1615                let track_namespace = TrackNamespace::decode_moqt::<Wire>(buf)?;
1616                let tn_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1617                let track_name = read_bytes(buf, tn_len)?;
1618                let track_alias = VarInt::decode_moqt::<Wire>(buf)?;
1619                check_full_track_name(&track_namespace, &track_name)?;
1620                let parameters = decode_parameters(buf)?;
1621                let track_properties = decode_track_properties(buf)?;
1622                Ok(ControlMessage::Publish(Publish {
1623                    request_id,
1624                    track_namespace,
1625                    track_name,
1626                    track_alias,
1627                    parameters,
1628                    track_properties,
1629                }))
1630            }
1631            MessageType::PublishDone => {
1632                let status_code = VarInt::decode_moqt::<Wire>(buf)?;
1633                let stream_count = VarInt::decode_moqt::<Wire>(buf)?;
1634                let reason_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1635                // Draft-18 Section 1.4.4, the same bound as REQUEST_ERROR above.
1636                if reason_len > MAX_REASON_PHRASE_LENGTH {
1637                    return Err(CodecError::ReasonPhraseTooLong);
1638                }
1639                let reason_phrase = read_bytes(buf, reason_len)?;
1640                Ok(ControlMessage::PublishDone(PublishDone {
1641                    status_code,
1642                    stream_count,
1643                    reason_phrase,
1644                }))
1645            }
1646            MessageType::PublishNamespace => {
1647                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1648                let track_namespace = TrackNamespace::decode_moqt::<Wire>(buf)?;
1649                let parameters = decode_parameters(buf)?;
1650                Ok(ControlMessage::PublishNamespace(PublishNamespace {
1651                    request_id,
1652                    track_namespace,
1653                    parameters,
1654                }))
1655            }
1656            MessageType::Namespace => {
1657                let namespace_suffix = TrackNamespace::decode_allow_empty_moqt::<Wire>(buf)?;
1658                Ok(ControlMessage::Namespace(Namespace { namespace_suffix }))
1659            }
1660            MessageType::NamespaceDone => {
1661                let namespace_suffix = TrackNamespace::decode_allow_empty_moqt::<Wire>(buf)?;
1662                Ok(ControlMessage::NamespaceDone(NamespaceDone { namespace_suffix }))
1663            }
1664            MessageType::SubscribeNamespace => {
1665                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1666                let namespace_prefix = TrackNamespace::decode_allow_empty_moqt::<Wire>(buf)?;
1667                let parameters = decode_parameters(buf)?;
1668                Ok(ControlMessage::SubscribeNamespace(SubscribeNamespace {
1669                    request_id,
1670                    namespace_prefix,
1671                    parameters,
1672                }))
1673            }
1674            MessageType::SubscribeTracks => {
1675                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1676                let namespace_prefix = TrackNamespace::decode_allow_empty_moqt::<Wire>(buf)?;
1677                let parameters = decode_parameters(buf)?;
1678                Ok(ControlMessage::SubscribeTracks(SubscribeTracks {
1679                    request_id,
1680                    namespace_prefix,
1681                    parameters,
1682                }))
1683            }
1684            MessageType::TrackStatus => {
1685                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1686                let track_namespace = TrackNamespace::decode_moqt::<Wire>(buf)?;
1687                let tn_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1688                let track_name = read_bytes(buf, tn_len)?;
1689                check_full_track_name(&track_namespace, &track_name)?;
1690                let parameters = decode_parameters(buf)?;
1691                Ok(ControlMessage::TrackStatus(TrackStatus {
1692                    request_id,
1693                    track_namespace,
1694                    track_name,
1695                    parameters,
1696                }))
1697            }
1698            MessageType::Fetch => {
1699                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1700                let fetch_type_val = VarInt::decode_moqt::<Wire>(buf)?.into_inner();
1701                let fetch_type = FetchType::from_u64(fetch_type_val)
1702                    .ok_or(CodecError::InvalidFetchType(fetch_type_val))?;
1703                let fetch_payload = match fetch_type {
1704                    FetchType::Standalone => {
1705                        let track_namespace = TrackNamespace::decode_moqt::<Wire>(buf)?;
1706                        let tn_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1707                        let track_name = read_bytes(buf, tn_len)?;
1708                        let start_group = VarInt::decode_moqt::<Wire>(buf)?;
1709                        let start_object = VarInt::decode_moqt::<Wire>(buf)?;
1710                        let end_group = VarInt::decode_moqt::<Wire>(buf)?;
1711                        let end_object = VarInt::decode_moqt::<Wire>(buf)?;
1712                        check_full_track_name(&track_namespace, &track_name)?;
1713                        FetchPayload::Standalone {
1714                            track_namespace,
1715                            track_name,
1716                            start_group,
1717                            start_object,
1718                            end_group,
1719                            end_object,
1720                        }
1721                    }
1722                    FetchType::RelativeJoining | FetchType::AbsoluteJoining => {
1723                        let joining_request_id = VarInt::decode_moqt::<Wire>(buf)?;
1724                        let joining_start = VarInt::decode_moqt::<Wire>(buf)?;
1725                        FetchPayload::Joining { joining_request_id, joining_start }
1726                    }
1727                };
1728                let parameters = decode_parameters(buf)?;
1729                Ok(ControlMessage::Fetch(Fetch {
1730                    request_id,
1731                    fetch_type,
1732                    fetch_payload,
1733                    parameters,
1734                }))
1735            }
1736            MessageType::FetchOk => {
1737                if buf.remaining() < 1 {
1738                    return Err(CodecError::UnexpectedEnd);
1739                }
1740                let end_of_track = buf.get_u8();
1741                let end_group = VarInt::decode_moqt::<Wire>(buf)?;
1742                let end_object = VarInt::decode_moqt::<Wire>(buf)?;
1743                let parameters = decode_parameters(buf)?;
1744                let track_properties = decode_track_properties(buf)?;
1745                Ok(ControlMessage::FetchOk(FetchOk {
1746                    end_of_track,
1747                    end_group,
1748                    end_object,
1749                    parameters,
1750                    track_properties,
1751                }))
1752            }
1753            MessageType::PublishBlocked => {
1754                let namespace_suffix = TrackNamespace::decode_allow_empty_moqt::<Wire>(buf)?;
1755                let tn_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1756                let track_name = read_bytes(buf, tn_len)?;
1757                check_full_track_name(&namespace_suffix, &track_name)?;
1758                Ok(ControlMessage::PublishBlocked(PublishBlocked { namespace_suffix, track_name }))
1759            }
1760        }
1761    }
1762
1763    pub fn message_type(&self) -> MessageType {
1764        match self {
1765            ControlMessage::Setup(_) => MessageType::Setup,
1766            ControlMessage::GoAway(_) => MessageType::GoAway,
1767            ControlMessage::RequestOk(_) => MessageType::RequestOk,
1768            ControlMessage::RequestError(_) => MessageType::RequestError,
1769            ControlMessage::Subscribe(_) => MessageType::Subscribe,
1770            ControlMessage::SubscribeOk(_) => MessageType::SubscribeOk,
1771            ControlMessage::RequestUpdate(_) => MessageType::RequestUpdate,
1772            ControlMessage::Publish(_) => MessageType::Publish,
1773            ControlMessage::PublishDone(_) => MessageType::PublishDone,
1774            ControlMessage::PublishNamespace(_) => MessageType::PublishNamespace,
1775            ControlMessage::Namespace(_) => MessageType::Namespace,
1776            ControlMessage::NamespaceDone(_) => MessageType::NamespaceDone,
1777            ControlMessage::SubscribeNamespace(_) => MessageType::SubscribeNamespace,
1778            ControlMessage::SubscribeTracks(_) => MessageType::SubscribeTracks,
1779            ControlMessage::TrackStatus(_) => MessageType::TrackStatus,
1780            ControlMessage::Fetch(_) => MessageType::Fetch,
1781            ControlMessage::FetchOk(_) => MessageType::FetchOk,
1782            ControlMessage::PublishBlocked(_) => MessageType::PublishBlocked,
1783        }
1784    }
1785}
1786
1787#[cfg(test)]
1788mod tests {
1789    use super::*;
1790
1791    /// Frame `body` as a draft-18 control message of `type_id`.
1792    fn frame(type_id: u64, body: &[u8]) -> Vec<u8> {
1793        let mut out = Vec::new();
1794        VarInt::from_u64_moqt(type_id).encode_moqt::<Wire>(&mut out);
1795        out.put_u16(body.len() as u16);
1796        out.put_slice(body);
1797        out
1798    }
1799
1800    fn param(key: u64, value: &[u8]) -> KeyValuePair {
1801        KeyValuePair { key: VarInt::from_u64_moqt(key), value: KvpValue::Bytes(value.to_vec()) }
1802    }
1803
1804    /// Draft-18 Section 10.2.11: "The LARGEST_OBJECT parameter (Parameter Type
1805    /// 0x9) is a Location." Section 10.2 defines Location as "Two consecutive
1806    /// varints (Group, Object)" — the value carries no length of its own.
1807    ///
1808    /// The frame below is built from the draft rather than from this encoder:
1809    /// REQUEST_OK, four body bytes, one parameter, type delta `0x09`, then the
1810    /// two varints `0x0a` and `0x03` for Location (10, 3). A length-prefixed
1811    /// spelling would need a fifth byte.
1812    ///
1813    /// With `0x09` back in the Length-prefixed arm, the decoder reads the
1814    /// group varint `0x0a` as a value length of 10 and runs off the end of a
1815    /// four-byte body. Both this test and
1816    /// [`a_location_does_not_eat_the_block_that_follows_it`] fail with:
1817    ///
1818    /// ```text
1819    /// spec-correct frame must decode: UnexpectedEnd
1820    /// ```
1821    #[test]
1822    fn largest_object_is_two_bare_varints() {
1823        let body = [0x01, 0x09, 0x0a, 0x03];
1824        let bytes = frame(0x07, &body);
1825
1826        let msg = ControlMessage::decode(&mut &bytes[..]).expect("spec-correct frame must decode");
1827        let ControlMessage::RequestOk(ok) = &msg else {
1828            panic!("expected REQUEST_OK, got {msg:?}")
1829        };
1830        assert_eq!(ok.parameters, vec![param(0x09, &[0x0a, 0x03])]);
1831        assert!(ok.track_properties.is_empty(), "the four body bytes are all parameter");
1832
1833        let mut out = Vec::new();
1834        msg.encode(&mut out).expect("re-encode");
1835        assert_eq!(out, bytes, "the value must go back out as the two bare varints it came in as");
1836    }
1837
1838    /// A Location value the encoder was handed but the decoder could not read
1839    /// back is refused on the way out, not written.
1840    ///
1841    /// LARGEST_OBJECT carries no length of its own — that is the whole point
1842    /// of the encoding — so `encode_parameters` writes its bytes verbatim. A
1843    /// value built in memory rather than decoded is under no obligation to be
1844    /// two varints, and without this check the codec answers `Ok(())` and puts
1845    /// a frame on the wire that `ControlMessage::decode` then refuses. One
1846    /// varint short and one varint long are the two ways to get it wrong.
1847    #[test]
1848    fn a_location_value_that_is_not_two_varints_is_refused_on_encode() {
1849        for (label, value) in
1850            [("one varint", vec![0x0a]), ("three varints", vec![0x0a, 0x03, 0x05])]
1851        {
1852            let msg = ControlMessage::RequestOk(RequestOk {
1853                parameters: vec![param(0x09, &value)],
1854                track_properties: Vec::new(),
1855            });
1856            let mut out = Vec::new();
1857            assert!(
1858                msg.encode(&mut out).is_err(),
1859                "LARGEST_OBJECT of {label} must not encode: the decoder cannot read it back"
1860            );
1861        }
1862
1863        // The well-formed value still goes out, so the check refuses the
1864        // malformed case and not the encoding itself.
1865        let msg = ControlMessage::RequestOk(RequestOk {
1866            parameters: vec![param(0x09, &[0x0a, 0x03])],
1867            track_properties: Vec::new(),
1868        });
1869        let mut out = Vec::new();
1870        msg.encode(&mut out).expect("a Location of exactly two varints must still encode");
1871        ControlMessage::decode(&mut &out[..]).expect("and must decode back");
1872    }
1873
1874    /// The same Location read through a message that carries other fields
1875    /// after it, so a stray length byte cannot hide in a trailing block.
1876    ///
1877    /// SUBSCRIBE_OK is track alias `0x05`, then the parameters, then the track
1878    /// properties. With LARGEST_OBJECT (10, 3) and one property
1879    /// (OBJECT_DELIVERY_TIMEOUT, type `0x02`, 5000ms as the four-byte varint
1880    /// `0x93 0x88`), the body is `05 01 09 0a 03 02 93 88`.
1881    #[test]
1882    fn a_location_does_not_eat_the_block_that_follows_it() {
1883        let body = [0x05, 0x01, 0x09, 0x0a, 0x03, 0x02, 0x93, 0x88];
1884        let bytes = frame(0x04, &body);
1885
1886        let msg = ControlMessage::decode(&mut &bytes[..]).expect("spec-correct frame must decode");
1887        let ControlMessage::SubscribeOk(ok) = &msg else {
1888            panic!("expected SUBSCRIBE_OK, got {msg:?}")
1889        };
1890        assert_eq!(ok.parameters, vec![param(0x09, &[0x0a, 0x03])]);
1891        assert_eq!(
1892            ok.track_properties,
1893            vec![KeyValuePair {
1894                key: VarInt::from_u64_moqt(0x02),
1895                value: KvpValue::Varint(VarInt::from_u64_moqt(5000)),
1896            }]
1897        );
1898
1899        let mut out = Vec::new();
1900        msg.encode(&mut out).expect("re-encode");
1901        assert_eq!(out, bytes);
1902    }
1903
1904    /// Draft-18 Section 10.2.14: the TRACK_NAMESPACE_PREFIX parameter
1905    /// (Parameter Type 0x34) "uses the Track Namespace encoding described in
1906    /// Section 2.4.1" — a varint field count followed by that many
1907    /// length-prefixed fields, and nothing in front of it. That encoding is not
1908    /// one of the four Section 10.2 lists, so it cannot be assumed to be
1909    /// Length-prefixed by default.
1910    ///
1911    /// The frame below is built from Section 2.4.1: REQUEST_UPDATE for request
1912    /// `7`, one parameter, type delta `0x34`, then the namespace ("live",
1913    /// "sports") as `02 04 "live" 06 "sports"`. Sixteen body bytes; a
1914    /// length-prefixed spelling would need a seventeenth for the outer length.
1915    ///
1916    /// This one does not fail loudly, which is the reason it exists. With
1917    /// `0x34` back in the Length-prefixed arm the field count `0x02` is read as
1918    /// an outer length of two bytes, so the parameter comes out holding the
1919    /// first field's length byte and the letter `l` of "live" — a namespace
1920    /// silently replaced by two bytes of its own framing:
1921    ///
1922    /// ```text
1923    /// assertion `left == right` failed
1924    ///   left: [KeyValuePair { key: VarInt(52), value: Bytes([4, 108]) }]
1925    ///  right: [KeyValuePair { key: VarInt(52), value: Bytes([2, 4, 108, 105, 118,
1926    ///          101, 6, 115, 112, 111, 114, 116, 115]) }]
1927    /// ```
1928    ///
1929    /// [`an_empty_track_namespace_prefix_is_one_zero_byte`] fails the same way,
1930    /// with `Bytes([])` where the one-byte namespace should be.
1931    #[test]
1932    fn track_namespace_prefix_is_a_bare_track_namespace() {
1933        let namespace: Vec<u8> = [&[0x02, 0x04][..], b"live", &[0x06][..], b"sports"].concat();
1934        assert_eq!(namespace.len(), 13);
1935
1936        let body: Vec<u8> = [&[0x07, 0x01, 0x34][..], &namespace].concat();
1937        assert_eq!(body.len(), 16);
1938        let bytes = frame(0x02, &body);
1939
1940        let msg = ControlMessage::decode(&mut &bytes[..]).expect("spec-correct frame must decode");
1941        let ControlMessage::RequestUpdate(update) = &msg else {
1942            panic!("expected REQUEST_UPDATE, got {msg:?}")
1943        };
1944        assert_eq!(update.request_id.into_inner(), 7);
1945        assert_eq!(update.parameters, vec![param(0x34, &namespace)]);
1946
1947        let mut out = Vec::new();
1948        msg.encode(&mut out).expect("re-encode");
1949        assert_eq!(out, bytes, "no outer length may appear in front of the Track Namespace");
1950    }
1951
1952    /// An empty prefix is a legal Track Namespace: Section 2.4.1 puts one at
1953    /// "between 0 and 32 Track Namespace Fields". On the wire that is the
1954    /// single byte `0x00`, and it must not be confused with a length-prefixed
1955    /// value of zero bytes.
1956    #[test]
1957    fn an_empty_track_namespace_prefix_is_one_zero_byte() {
1958        let body = [0x07, 0x01, 0x34, 0x00];
1959        let bytes = frame(0x02, &body);
1960
1961        let msg = ControlMessage::decode(&mut &bytes[..]).expect("empty prefix must decode");
1962        let ControlMessage::RequestUpdate(update) = &msg else {
1963            panic!("expected REQUEST_UPDATE, got {msg:?}")
1964        };
1965        assert_eq!(update.parameters, vec![param(0x34, &[0x00])]);
1966
1967        let mut out = Vec::new();
1968        msg.encode(&mut out).expect("re-encode");
1969        assert_eq!(out, bytes);
1970    }
1971}