Skip to main content

moqtap_codec/draft17/
message.rs

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