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                match decode_kvp_delta(&mut inner) {
560                    Ok(nested) => check_track_property_values(&nested)?,
561                    // Not a Key-Value-Pair run. See the note above: reading the
562                    // block is a permission, so one that cannot be read is
563                    // carried rather than refused.
564                    Err(_) => return Ok(()),
565                }
566            }
567            KvpValue::Bytes(_) => {}
568        }
569    }
570    Ok(())
571}
572
573/// Decode the Track Properties that fill the tail of a control message.
574///
575/// [`decode_kvp_delta`] with the Property registry's value rules applied. The
576/// two are separate because that function also reads Setup Options, which are a
577/// third namespace numbering its entries independently of this one.
578fn decode_track_properties(buf: &mut impl Buf) -> Result<Vec<KeyValuePair>, CodecError> {
579    let properties = decode_kvp_delta(buf)?;
580    check_track_property_values(&properties)?;
581    Ok(properties)
582}
583
584/// Encode a control message's Track Properties.
585///
586/// Held to the same value ranges as the decoder. A value this codec refuses to
587/// read is one it must not write: the peer that receives it is required to close
588/// the session, so the sender's first sign of trouble would be the session
589/// going.
590fn encode_track_properties(
591    properties: &[KeyValuePair],
592    buf: &mut impl BufMut,
593) -> Result<(), CodecError> {
594    check_track_property_values(properties)?;
595    encode_kvp_delta(properties, buf)
596}
597
598/// The Setup Option types this draft defines.
599///
600/// Section 9.4.1 assigns PATH, AUTHORIZATION TOKEN, MAX_AUTH_TOKEN_CACHE_SIZE, AUTHORITY and
601/// MOQT_IMPLEMENTATION.
602///
603/// The list exists for one rule and one direction. Section 9.4: "Receivers
604/// MUST allow duplicates of unknown Setup Options." A receiver may therefore
605/// refuse a repeat only of a type it can name, and an option outside this list
606/// is one an extension defined and this codec has no business closing a session
607/// over. Nothing else reads it - unknown options are still decoded and carried,
608/// as "Receivers MUST ignore unrecognized Setup Options" requires.
609const KNOWN_SETUP_OPTIONS: &[u64] = &[0x01, 0x03, 0x04, 0x05, 0x07];
610
611/// The one Setup Option whose definition allows more than one instance.
612///
613/// Section 9.4.1.4: "The AUTHORIZATION TOKEN Setup Option (Option Type 0x03)
614/// is functionally equivalent to the AUTHORIZATION TOKEN message parameter...
615/// The endpoint can specify one or more tokens in SETUP that the peer can use to
616/// authorize MOQT session establishment." That is the "unless the option
617/// definition explicitly allows multiple instances" carve-out, and it is the
618/// only one on this draft.
619const REPEATABLE_SETUP_OPTION: u64 = 0x03;
620
621/// Decode the Setup Options of a SETUP message.
622///
623/// Section 9.4: "Senders MUST NOT repeat the same Option Type in a message
624/// unless the option definition explicitly allows multiple instances. Receivers
625/// MUST allow duplicates of unknown Setup Options."
626///
627/// The second sentence is why this is not the mirror of
628/// [`encode_setup_options`]: a repeat of a type this draft names is refused, and
629/// a repeat of any other type is carried. Types ascend and are delta-encoded, so
630/// a repeat is always a zero delta against the option before it.
631fn decode_setup_options(buf: &mut impl Buf) -> Result<Vec<KeyValuePair>, CodecError> {
632    let options = decode_kvp_delta(buf)?;
633    for (i, option) in options.iter().enumerate() {
634        let key = option.key.into_inner();
635        if key == REPEATABLE_SETUP_OPTION || !KNOWN_SETUP_OPTIONS.contains(&key) {
636            continue;
637        }
638        if options[..i].iter().any(|earlier| earlier.key == option.key) {
639            return Err(CodecError::DuplicateParameter(key));
640        }
641    }
642    check_authorization_tokens(&options)?;
643    Ok(options)
644}
645
646/// Encode the Setup Options of a SETUP message.
647///
648/// The sender's half of the same sentence, and it is the wider half: "Senders
649/// MUST NOT repeat the same Option Type in a message" names no exception for
650/// types the sender does not recognise, so every repeat is refused here except
651/// the one the draft allows. A caller holding an option this codec has never
652/// heard of still may not send it twice.
653///
654/// The token is in this namespace as well, and is held to its structure here for
655/// the reason [`encode_parameters`] gives.
656fn encode_setup_options(options: &[KeyValuePair], buf: &mut impl BufMut) -> Result<(), CodecError> {
657    check_authorization_tokens(options)?;
658    for (i, option) in options.iter().enumerate() {
659        if option.key.into_inner() == REPEATABLE_SETUP_OPTION {
660            continue;
661        }
662        if options[..i].iter().any(|earlier| earlier.key == option.key) {
663            return Err(CodecError::DuplicateParameter(option.key.into_inner()));
664        }
665    }
666    encode_kvp_delta(options, buf)
667}
668
669// ============================================================
670// Message Types
671// ============================================================
672
673#[derive(Debug, Clone, Copy, PartialEq, Eq)]
674#[repr(u64)]
675pub enum MessageType {
676    RequestUpdate = 0x02,
677    Subscribe = 0x03,
678    SubscribeOk = 0x04,
679    RequestError = 0x05,
680    PublishNamespace = 0x06,
681    RequestOk = 0x07,
682    Namespace = 0x08,
683    PublishDone = 0x0B,
684    TrackStatus = 0x0D,
685    NamespaceDone = 0x0E,
686    PublishBlocked = 0x0F,
687    GoAway = 0x10,
688    SubscribeNamespace = 0x11,
689    Fetch = 0x16,
690    FetchOk = 0x18,
691    Publish = 0x1D,
692    PublishOk = 0x1E,
693    Setup = 0x2F00,
694}
695
696impl MessageType {
697    pub fn from_id(id: u64) -> Option<Self> {
698        match id {
699            0x02 => Some(MessageType::RequestUpdate),
700            0x03 => Some(MessageType::Subscribe),
701            0x04 => Some(MessageType::SubscribeOk),
702            0x05 => Some(MessageType::RequestError),
703            0x06 => Some(MessageType::PublishNamespace),
704            0x07 => Some(MessageType::RequestOk),
705            0x08 => Some(MessageType::Namespace),
706            0x0B => Some(MessageType::PublishDone),
707            0x0D => Some(MessageType::TrackStatus),
708            0x0E => Some(MessageType::NamespaceDone),
709            0x0F => Some(MessageType::PublishBlocked),
710            0x10 => Some(MessageType::GoAway),
711            0x11 => Some(MessageType::SubscribeNamespace),
712            0x16 => Some(MessageType::Fetch),
713            0x18 => Some(MessageType::FetchOk),
714            0x1D => Some(MessageType::Publish),
715            0x1E => Some(MessageType::PublishOk),
716            0x2F00 => Some(MessageType::Setup),
717            _ => None,
718        }
719    }
720
721    pub fn id(&self) -> u64 {
722        *self as u64
723    }
724
725    /// This type's name in the shared vector corpus: the `message_type` its
726    /// draft's `codec/messages/*.json` files carry, in `snake_case`.
727    pub fn name(&self) -> &'static str {
728        match self {
729            MessageType::RequestUpdate => "request_update",
730            MessageType::Subscribe => "subscribe",
731            MessageType::SubscribeOk => "subscribe_ok",
732            MessageType::RequestError => "request_error",
733            MessageType::PublishNamespace => "publish_namespace",
734            MessageType::RequestOk => "request_ok",
735            MessageType::Namespace => "namespace",
736            MessageType::PublishDone => "publish_done",
737            MessageType::TrackStatus => "track_status",
738            MessageType::NamespaceDone => "namespace_done",
739            MessageType::PublishBlocked => "publish_blocked",
740            MessageType::GoAway => "goaway",
741            MessageType::SubscribeNamespace => "subscribe_namespace",
742            MessageType::Fetch => "fetch",
743            MessageType::FetchOk => "fetch_ok",
744            MessageType::Publish => "publish",
745            MessageType::PublishOk => "publish_ok",
746            MessageType::Setup => "setup",
747        }
748    }
749}
750
751// ============================================================
752// Session Lifecycle Messages
753// ============================================================
754
755/// Unified SETUP (0x2F00). Replaces ClientSetup/ServerSetup.
756#[derive(Debug, Clone, PartialEq, Eq)]
757pub struct Setup {
758    pub options: Vec<KeyValuePair>,
759}
760
761#[derive(Debug, Clone, PartialEq, Eq)]
762pub struct GoAway {
763    pub new_session_uri: Vec<u8>,
764    pub timeout: VarInt,
765}
766
767// ============================================================
768// Consolidated Response Messages
769// ============================================================
770
771/// REQUEST_OK (0x07). No request_id in draft-17.
772#[derive(Debug, Clone, PartialEq, Eq)]
773pub struct RequestOk {
774    pub parameters: Vec<KeyValuePair>,
775}
776
777/// REQUEST_ERROR (0x05). No request_id in draft-17.
778#[derive(Debug, Clone, PartialEq, Eq)]
779pub struct RequestError {
780    pub error_code: VarInt,
781    pub retry_interval: VarInt,
782    pub reason_phrase: Vec<u8>,
783}
784
785// ============================================================
786// Subscribe Messages
787// ============================================================
788
789#[derive(Debug, Clone, PartialEq, Eq)]
790pub struct Subscribe {
791    pub request_id: VarInt,
792    pub required_request_id_delta: VarInt,
793    pub track_namespace: TrackNamespace,
794    pub track_name: Vec<u8>,
795    pub parameters: Vec<KeyValuePair>,
796}
797
798/// SUBSCRIBE_OK (0x04). No request_id in draft-17. Gains track_properties.
799#[derive(Debug, Clone, PartialEq, Eq)]
800pub struct SubscribeOk {
801    pub track_alias: VarInt,
802    pub parameters: Vec<KeyValuePair>,
803    pub track_properties: Vec<KeyValuePair>,
804}
805
806#[derive(Debug, Clone, PartialEq, Eq)]
807pub struct RequestUpdate {
808    pub request_id: VarInt,
809    pub required_request_id_delta: VarInt,
810    pub parameters: Vec<KeyValuePair>,
811}
812
813// ============================================================
814// Publish Messages
815// ============================================================
816
817#[derive(Debug, Clone, PartialEq, Eq)]
818pub struct Publish {
819    pub request_id: VarInt,
820    pub required_request_id_delta: VarInt,
821    pub track_namespace: TrackNamespace,
822    pub track_name: Vec<u8>,
823    pub track_alias: VarInt,
824    pub parameters: Vec<KeyValuePair>,
825    pub track_properties: Vec<KeyValuePair>,
826}
827
828/// PUBLISH_OK (0x1E). No request_id in draft-17.
829#[derive(Debug, Clone, PartialEq, Eq)]
830pub struct PublishOk {
831    pub parameters: Vec<KeyValuePair>,
832}
833
834/// PUBLISH_DONE (0x0B). No request_id in draft-17.
835#[derive(Debug, Clone, PartialEq, Eq)]
836pub struct PublishDone {
837    pub status_code: VarInt,
838    pub stream_count: VarInt,
839    pub reason_phrase: Vec<u8>,
840}
841
842// ============================================================
843// Publish Namespace Messages
844// ============================================================
845
846#[derive(Debug, Clone, PartialEq, Eq)]
847pub struct PublishNamespace {
848    pub request_id: VarInt,
849    pub required_request_id_delta: VarInt,
850    pub track_namespace: TrackNamespace,
851    pub parameters: Vec<KeyValuePair>,
852}
853
854// ============================================================
855// Namespace Messages
856// ============================================================
857
858#[derive(Debug, Clone, PartialEq, Eq)]
859pub struct Namespace {
860    pub namespace_suffix: TrackNamespace,
861}
862
863#[derive(Debug, Clone, PartialEq, Eq)]
864pub struct NamespaceDone {
865    pub namespace_suffix: TrackNamespace,
866}
867
868// ============================================================
869// Subscribe Namespace Messages
870// ============================================================
871
872#[derive(Debug, Clone, PartialEq, Eq)]
873pub struct SubscribeNamespace {
874    pub request_id: VarInt,
875    pub required_request_id_delta: VarInt,
876    pub namespace_prefix: TrackNamespace,
877    pub subscribe_options: VarInt,
878    pub parameters: Vec<KeyValuePair>,
879}
880
881// ============================================================
882// Track Status Messages
883// ============================================================
884
885#[derive(Debug, Clone, PartialEq, Eq)]
886pub struct TrackStatus {
887    pub request_id: VarInt,
888    pub required_request_id_delta: VarInt,
889    pub track_namespace: TrackNamespace,
890    pub track_name: Vec<u8>,
891    pub parameters: Vec<KeyValuePair>,
892}
893
894// ============================================================
895// Fetch Messages
896// ============================================================
897
898#[derive(Debug, Clone, Copy, PartialEq, Eq)]
899#[repr(u64)]
900pub enum FetchType {
901    Standalone = 1,
902    RelativeJoining = 2,
903    AbsoluteJoining = 3,
904}
905
906impl FetchType {
907    pub fn from_u64(v: u64) -> Option<Self> {
908        match v {
909            1 => Some(FetchType::Standalone),
910            2 => Some(FetchType::RelativeJoining),
911            3 => Some(FetchType::AbsoluteJoining),
912            _ => None,
913        }
914    }
915}
916
917#[derive(Debug, Clone, PartialEq, Eq)]
918pub struct Fetch {
919    pub request_id: VarInt,
920    pub required_request_id_delta: VarInt,
921    pub fetch_type: FetchType,
922    pub fetch_payload: FetchPayload,
923    pub parameters: Vec<KeyValuePair>,
924}
925
926#[derive(Debug, Clone, PartialEq, Eq)]
927pub enum FetchPayload {
928    Standalone {
929        track_namespace: TrackNamespace,
930        track_name: Vec<u8>,
931        start_group: VarInt,
932        start_object: VarInt,
933        end_group: VarInt,
934        end_object: VarInt,
935    },
936    Joining {
937        joining_request_id: VarInt,
938        joining_start: VarInt,
939    },
940}
941
942/// FETCH_OK (0x18). No request_id in draft-17. end_of_track is uint8.
943#[derive(Debug, Clone, PartialEq, Eq)]
944pub struct FetchOk {
945    pub end_of_track: u8,
946    pub end_group: VarInt,
947    pub end_object: VarInt,
948    pub parameters: Vec<KeyValuePair>,
949    pub track_properties: Vec<KeyValuePair>,
950}
951
952// ============================================================
953// Publish Blocked (new in draft-17)
954// ============================================================
955
956#[derive(Debug, Clone, PartialEq, Eq)]
957pub struct PublishBlocked {
958    pub namespace_suffix: TrackNamespace,
959    pub track_name: Vec<u8>,
960}
961
962// ============================================================
963// Unified Message Enum
964// ============================================================
965
966#[derive(Debug, Clone, PartialEq, Eq)]
967pub enum ControlMessage {
968    Setup(Setup),
969    GoAway(GoAway),
970    RequestOk(RequestOk),
971    RequestError(RequestError),
972    Subscribe(Subscribe),
973    SubscribeOk(SubscribeOk),
974    RequestUpdate(RequestUpdate),
975    Publish(Publish),
976    PublishOk(PublishOk),
977    PublishDone(PublishDone),
978    PublishNamespace(PublishNamespace),
979    Namespace(Namespace),
980    NamespaceDone(NamespaceDone),
981    SubscribeNamespace(SubscribeNamespace),
982    TrackStatus(TrackStatus),
983    Fetch(Fetch),
984    FetchOk(FetchOk),
985    PublishBlocked(PublishBlocked),
986}
987
988/// Refuse a FETCH whose range ends before it starts.
989///
990/// Section 9.14.3: "Fetch specifies an inclusive range of Objects starting at
991/// Start Location and ending at End Location. End Location MUST specify the
992/// same or a larger Location than Start Location for Standalone and Absolute Joining Fetches." A Joining Fetch names
993/// no explicit range - it is computed from the subscription it joins - so only
994/// a standalone range is checked here.
995///
996/// SUBSCRIBE is not checked here, and needs no check: this draft's
997/// AbsoluteRange filter carries an End Group Delta measured from the start
998/// location rather than an absolute End Group, so an end before the start
999/// has no encoding.
1000///
1001/// Applied on both sides. A range that ends before it starts selects nothing,
1002/// and the peer's only recourse is an error response or a session close, so
1003/// writing one is not a way to ask for anything.
1004fn check_ranges(message: &ControlMessage) -> Result<(), CodecError> {
1005    match message {
1006        ControlMessage::Fetch(m) => match &m.fetch_payload {
1007            FetchPayload::Standalone {
1008                start_group, start_object, end_group, end_object, ..
1009            } => check_location_range(
1010                start_group.into_inner(),
1011                start_object.into_inner(),
1012                end_group.into_inner(),
1013                end_object.into_inner(),
1014            ),
1015            FetchPayload::Joining { .. } => Ok(()),
1016        },
1017        _ => Ok(()),
1018    }
1019}
1020
1021/// Refuse a message whose discriminator disagrees with the fields beside it.
1022///
1023/// One draft-17 message carries a field that says which of the following fields
1024/// are on the wire: FETCH's Fetch Type. This codec holds the alternatives in an
1025/// enum of its own, [`FetchPayload`], so a value can say one thing in its
1026/// discriminator and another in its body, and the two sides of the codec
1027/// resolve that differently — the encoder writes whatever the body holds, and
1028/// the decoder reads whatever the discriminator announces.
1029///
1030/// The result is a message that does not survive its own round trip. A FETCH
1031/// whose type says Standalone and whose body is a joining pair encodes to a
1032/// joining request id and a joining start where a Track Namespace and a Track
1033/// Name belong, and comes back as a Standalone fetch of a track named after two
1034/// integers — or, more often, as an error, which at least is honest. Refusing
1035/// at the encoder keeps the two readings from ever diverging on the wire.
1036///
1037/// The two joining types share one body shape, so the check is between
1038/// Standalone and everything else rather than one arm per type.
1039fn check_discriminators(message: &ControlMessage) -> Result<(), CodecError> {
1040    if let ControlMessage::Fetch(m) = message {
1041        let body_is_standalone = matches!(m.fetch_payload, FetchPayload::Standalone { .. });
1042        if body_is_standalone != (m.fetch_type == FetchType::Standalone) {
1043            return Err(CodecError::InvalidField);
1044        }
1045    }
1046    Ok(())
1047}
1048
1049/// Whether draft-17 lets Message Parameter `key` appear in `message`.
1050///
1051/// Section 9.3.1: "Each Message Parameter definition indicates the message
1052/// types in which it can appear. If it appears in some other type of message,
1053/// the receiving endpoint MUST close the connection with a PROTOCOL_VIOLATION."
1054/// One arm per entry in the Message Parameters registry (Section 14.3),
1055/// carrying the message types that entry's own subsection names.
1056///
1057/// Where a name is qualified, the qualifier describes one of the destinations
1058/// rather than adding another. LARGEST_OBJECT "MAY appear in SUBSCRIBE_OK,
1059/// PUBLISH or in REQUEST_OK (in response to REQUEST_UPDATE or TRACK_STATUS)"
1060/// names three message types, and drafts 18 and 19 write that same rule as
1061/// SUBSCRIBE_OK, PUBLISH, REQUEST_UPDATE_OK and TRACK_STATUS_OK once those
1062/// responses have names of their own.
1063///
1064/// FETCH_OK has no arm in the table below, and that is the draft's doing rather
1065/// than an omission here: Section 9.15 gives it a Parameters field and no
1066/// parameter definition names it, so every type this draft defines is "some
1067/// other type of message" there.
1068///
1069/// The table decides scope only. A type this draft does not define has no scope
1070/// to be outside of and is answered by [`CodecError::UnknownMessageParameter`],
1071/// which is why the final arm carries rather than refuses.
1072fn parameter_in_scope(key: u64, message: MessageType) -> bool {
1073    use MessageType as M;
1074    match key {
1075        // Section 9.3.3 DELIVERY TIMEOUT: "It MAY appear in a PUBLISH_OK,
1076        // SUBSCRIBE, or REQUEST_UPDATE message."
1077        0x02 => matches!(message, M::PublishOk | M::Subscribe | M::RequestUpdate),
1078        // Section 9.3.2 AUTHORIZATION TOKEN: "It MAY appear in a PUBLISH,
1079        // SUBSCRIBE, REQUEST_UPDATE, SUBSCRIBE_NAMESPACE, PUBLISH_NAMESPACE,
1080        // TRACK_STATUS or FETCH message."
1081        0x03 => matches!(
1082            message,
1083            M::Publish
1084                | M::Subscribe
1085                | M::RequestUpdate
1086                | M::SubscribeNamespace
1087                | M::PublishNamespace
1088                | M::TrackStatus
1089                | M::Fetch
1090        ),
1091        // Section 9.3.4 RENDEZVOUS TIMEOUT: it "MAY appear in a SUBSCRIBE
1092        // message".
1093        0x04 => matches!(message, M::Subscribe),
1094        // Section 9.3.8 EXPIRES: "It MAY appear in SUBSCRIBE_OK, PUBLISH,
1095        // PUBLISH_OK, or REQUEST_OK."
1096        0x08 => matches!(message, M::SubscribeOk | M::Publish | M::PublishOk | M::RequestOk),
1097        // Section 9.3.9 LARGEST OBJECT: "It MAY appear in SUBSCRIBE_OK, PUBLISH
1098        // or in REQUEST_OK (in response to REQUEST_UPDATE or TRACK_STATUS)."
1099        0x09 => matches!(message, M::SubscribeOk | M::Publish | M::RequestOk),
1100        // Section 9.3.10 FORWARD: "It MAY appear in SUBSCRIBE, REQUEST_UPDATE
1101        // (for a subscription), PUBLISH, PUBLISH_OK and SUBSCRIBE_NAMESPACE."
1102        0x10 => matches!(
1103            message,
1104            M::Subscribe | M::RequestUpdate | M::Publish | M::PublishOk | M::SubscribeNamespace
1105        ),
1106        // Section 9.3.5 SUBSCRIBER PRIORITY: "It MAY appear in a SUBSCRIBE,
1107        // FETCH, REQUEST_UPDATE (for a subscription or FETCH), or PUBLISH_OK
1108        // message."
1109        0x20 => matches!(message, M::Subscribe | M::Fetch | M::RequestUpdate | M::PublishOk),
1110        // Section 9.3.7 SUBSCRIPTION FILTER: "It MAY appear in a SUBSCRIBE,
1111        // PUBLISH_OK or REQUEST_UPDATE (for a subscription) message."
1112        0x21 => matches!(message, M::Subscribe | M::PublishOk | M::RequestUpdate),
1113        // Section 9.3.6 GROUP ORDER: "It MAY appear in a SUBSCRIBE, PUBLISH_OK,
1114        // or FETCH."
1115        0x22 => matches!(message, M::Subscribe | M::PublishOk | M::Fetch),
1116        // Section 9.3.11 NEW GROUP REQUEST: "It MAY appear in PUBLISH_OK,
1117        // SUBSCRIBE or REQUEST_UPDATE for a subscription."
1118        0x32 => matches!(message, M::PublishOk | M::Subscribe | M::RequestUpdate),
1119        _ => true,
1120    }
1121}
1122
1123/// Refuse a message carrying a Message Parameter its own definition does not
1124/// place there.
1125///
1126/// Section 9.3.1 answers this with a close, which the drafts below do not.
1127/// Draft-16 Section 9.2.2, and drafts 07 through 15 under the older name
1128/// Version Specific Parameters, end the same sentence "it MUST be ignored" —
1129/// so this check belongs to drafts 17, 18 and 19 and to no draft before them.
1130///
1131/// Applied on both sides. A parameter outside its scope is one the peer must
1132/// close the session over, so writing one is a way to end a session rather than
1133/// a way to ask for anything.
1134fn check_parameter_scope(message: &ControlMessage) -> Result<(), CodecError> {
1135    let parameters = match message {
1136        ControlMessage::RequestOk(m) => &m.parameters,
1137        ControlMessage::Subscribe(m) => &m.parameters,
1138        ControlMessage::SubscribeOk(m) => &m.parameters,
1139        ControlMessage::RequestUpdate(m) => &m.parameters,
1140        ControlMessage::Publish(m) => &m.parameters,
1141        ControlMessage::PublishOk(m) => &m.parameters,
1142        ControlMessage::PublishNamespace(m) => &m.parameters,
1143        ControlMessage::SubscribeNamespace(m) => &m.parameters,
1144        ControlMessage::TrackStatus(m) => &m.parameters,
1145        ControlMessage::Fetch(m) => &m.parameters,
1146        ControlMessage::FetchOk(m) => &m.parameters,
1147        // No Message Parameters field. SETUP is named here rather than left to
1148        // a wildcard because the draft says why it can never have one: Section
1149        // 9.3.1 notes that "since Setup Options use a separate namespace, it is
1150        // impossible for Message Parameters to appear in Setup messages", and
1151        // this codec keeps the two namespaces in separate fields.
1152        ControlMessage::Setup(_)
1153        | ControlMessage::GoAway(_)
1154        | ControlMessage::RequestError(_)
1155        | ControlMessage::PublishDone(_)
1156        | ControlMessage::Namespace(_)
1157        | ControlMessage::NamespaceDone(_)
1158        | ControlMessage::PublishBlocked(_) => return Ok(()),
1159    };
1160
1161    let message_type = message.message_type();
1162    for parameter in parameters {
1163        let key = parameter.key.into_inner();
1164        if !parameter_in_scope(key, message_type) {
1165            return Err(CodecError::ParameterOutOfScope { key, message_type: message_type.id() });
1166        }
1167    }
1168    Ok(())
1169}
1170
1171impl ControlMessage {
1172    pub fn encode(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
1173        check_discriminators(self)?;
1174        check_ranges(self)?;
1175        check_parameter_scope(self)?;
1176        let mut payload = Vec::with_capacity(256);
1177        self.encode_payload(&mut payload)?;
1178
1179        if payload.len() > MAX_MESSAGE_LENGTH {
1180            return Err(CodecError::MessageTooLong(payload.len()));
1181        }
1182
1183        let msg_type = self.message_type();
1184        VarInt::from_usize(msg_type.id() as usize).encode_moqt::<Wire>(buf);
1185        // Draft-17: 16-bit length (big-endian)
1186        buf.put_u16(payload.len() as u16);
1187        buf.put_slice(&payload);
1188        Ok(())
1189    }
1190
1191    pub fn decode(buf: &mut impl Buf) -> Result<Self, CodecError> {
1192        let type_id = VarInt::decode_moqt::<Wire>(buf)?.into_inner();
1193        let msg_type =
1194            MessageType::from_id(type_id).ok_or(CodecError::UnknownMessageType(type_id))?;
1195        // Draft-17: 16-bit length (big-endian)
1196        if buf.remaining() < 2 {
1197            return Err(CodecError::UnexpectedEnd);
1198        }
1199        let payload_len = buf.get_u16() as usize;
1200        if buf.remaining() < payload_len {
1201            return Err(CodecError::UnexpectedEnd);
1202        }
1203        let payload_bytes = buf.copy_to_bytes(payload_len);
1204        let mut payload = &payload_bytes[..];
1205        let msg = match Self::decode_payload(msg_type, &mut payload) {
1206            Ok(msg) => msg,
1207            // The fields wanted more bytes than the Length allowed. This buffer
1208            // is already bounded by that Length, so running out inside it cannot
1209            // mean the message is still arriving - which is what the same error
1210            // means everywhere else, and why a reader loops on it rather than
1211            // closing. Here there is nothing left to arrive.
1212            Err(
1213                CodecError::UnexpectedEnd
1214                | CodecError::Kvp(crate::kvp::KvpError::UnexpectedEnd)
1215                | CodecError::Kvp(crate::kvp::KvpError::VarInt(
1216                    crate::varint::VarIntError::UnexpectedEnd,
1217                ))
1218                | CodecError::VarInt(crate::varint::VarIntError::UnexpectedEnd),
1219            ) => {
1220                return Err(CodecError::ControlMessageLengthMismatch {
1221                    declared: payload_len,
1222                    detail: "its fields ran past the end",
1223                });
1224            }
1225            Err(e) => return Err(e),
1226        };
1227        check_ranges(&msg)?;
1228        check_parameter_scope(&msg)?;
1229        // The declared length is part of the message, not a hint. Bytes left over
1230        // after the fields have been read mean the sender and this reader disagree
1231        // about the shape of the message, and guessing which of the two is right
1232        // is how a trailing field gets silently dropped.
1233        if payload.has_remaining() {
1234            return Err(CodecError::ControlMessageLengthMismatch {
1235                declared: payload_len,
1236                detail: "its fields left bytes unread",
1237            });
1238        }
1239        Ok(msg)
1240    }
1241
1242    fn encode_payload(&self, buf: &mut impl BufMut) -> Result<(), CodecError> {
1243        match self {
1244            ControlMessage::Setup(m) => {
1245                encode_setup_options(&m.options, buf)?;
1246            }
1247            ControlMessage::GoAway(m) => {
1248                if m.new_session_uri.len() > MAX_GOAWAY_URI_LENGTH {
1249                    return Err(CodecError::GoAwayUriTooLong);
1250                }
1251                VarInt::from_usize(m.new_session_uri.len()).encode_moqt::<Wire>(buf);
1252                buf.put_slice(&m.new_session_uri);
1253                m.timeout.encode_moqt::<Wire>(buf);
1254            }
1255            ControlMessage::RequestOk(m) => {
1256                encode_parameters(&m.parameters, buf)?;
1257            }
1258            ControlMessage::RequestError(m) => {
1259                if m.reason_phrase.len() > MAX_REASON_PHRASE_LENGTH {
1260                    return Err(CodecError::ReasonPhraseTooLong);
1261                }
1262                m.error_code.encode_moqt::<Wire>(buf);
1263                m.retry_interval.encode_moqt::<Wire>(buf);
1264                VarInt::from_usize(m.reason_phrase.len()).encode_moqt::<Wire>(buf);
1265                buf.put_slice(&m.reason_phrase);
1266            }
1267            ControlMessage::Subscribe(m) => {
1268                check_required_request_id_delta(m.request_id, m.required_request_id_delta)?;
1269                m.track_namespace.validate_moqt()?;
1270                check_full_track_name(&m.track_namespace, &m.track_name)?;
1271                m.request_id.encode_moqt::<Wire>(buf);
1272                m.required_request_id_delta.encode_moqt::<Wire>(buf);
1273                m.track_namespace.encode_moqt::<Wire>(buf);
1274                VarInt::from_usize(m.track_name.len()).encode_moqt::<Wire>(buf);
1275                buf.put_slice(&m.track_name);
1276                encode_parameters(&m.parameters, buf)?;
1277            }
1278            ControlMessage::SubscribeOk(m) => {
1279                m.track_alias.encode_moqt::<Wire>(buf);
1280                encode_parameters(&m.parameters, buf)?;
1281                encode_track_properties(&m.track_properties, buf)?;
1282            }
1283            ControlMessage::RequestUpdate(m) => {
1284                check_required_request_id_delta(m.request_id, m.required_request_id_delta)?;
1285                m.request_id.encode_moqt::<Wire>(buf);
1286                m.required_request_id_delta.encode_moqt::<Wire>(buf);
1287                encode_parameters(&m.parameters, buf)?;
1288            }
1289            ControlMessage::Publish(m) => {
1290                check_required_request_id_delta(m.request_id, m.required_request_id_delta)?;
1291                m.track_namespace.validate_moqt()?;
1292                check_full_track_name(&m.track_namespace, &m.track_name)?;
1293                m.request_id.encode_moqt::<Wire>(buf);
1294                m.required_request_id_delta.encode_moqt::<Wire>(buf);
1295                m.track_namespace.encode_moqt::<Wire>(buf);
1296                VarInt::from_usize(m.track_name.len()).encode_moqt::<Wire>(buf);
1297                buf.put_slice(&m.track_name);
1298                m.track_alias.encode_moqt::<Wire>(buf);
1299                encode_parameters(&m.parameters, buf)?;
1300                encode_track_properties(&m.track_properties, buf)?;
1301            }
1302            ControlMessage::PublishOk(m) => {
1303                encode_parameters(&m.parameters, buf)?;
1304            }
1305            ControlMessage::PublishDone(m) => {
1306                if m.reason_phrase.len() > MAX_REASON_PHRASE_LENGTH {
1307                    return Err(CodecError::ReasonPhraseTooLong);
1308                }
1309                m.status_code.encode_moqt::<Wire>(buf);
1310                m.stream_count.encode_moqt::<Wire>(buf);
1311                VarInt::from_usize(m.reason_phrase.len()).encode_moqt::<Wire>(buf);
1312                buf.put_slice(&m.reason_phrase);
1313            }
1314            ControlMessage::PublishNamespace(m) => {
1315                check_required_request_id_delta(m.request_id, m.required_request_id_delta)?;
1316                m.track_namespace.validate_moqt()?;
1317                m.request_id.encode_moqt::<Wire>(buf);
1318                m.required_request_id_delta.encode_moqt::<Wire>(buf);
1319                m.track_namespace.encode_moqt::<Wire>(buf);
1320                encode_parameters(&m.parameters, buf)?;
1321            }
1322            ControlMessage::Namespace(m) => {
1323                m.namespace_suffix.validate_moqt()?;
1324                m.namespace_suffix.encode_moqt::<Wire>(buf);
1325            }
1326            ControlMessage::NamespaceDone(m) => {
1327                m.namespace_suffix.validate_moqt()?;
1328                m.namespace_suffix.encode_moqt::<Wire>(buf);
1329            }
1330            ControlMessage::SubscribeNamespace(m) => {
1331                check_required_request_id_delta(m.request_id, m.required_request_id_delta)?;
1332                m.namespace_prefix.validate_moqt()?;
1333                m.request_id.encode_moqt::<Wire>(buf);
1334                m.required_request_id_delta.encode_moqt::<Wire>(buf);
1335                m.namespace_prefix.encode_moqt::<Wire>(buf);
1336                m.subscribe_options.encode_moqt::<Wire>(buf);
1337                encode_parameters(&m.parameters, buf)?;
1338            }
1339            ControlMessage::TrackStatus(m) => {
1340                check_required_request_id_delta(m.request_id, m.required_request_id_delta)?;
1341                m.track_namespace.validate_moqt()?;
1342                check_full_track_name(&m.track_namespace, &m.track_name)?;
1343                m.request_id.encode_moqt::<Wire>(buf);
1344                m.required_request_id_delta.encode_moqt::<Wire>(buf);
1345                m.track_namespace.encode_moqt::<Wire>(buf);
1346                VarInt::from_usize(m.track_name.len()).encode_moqt::<Wire>(buf);
1347                buf.put_slice(&m.track_name);
1348                encode_parameters(&m.parameters, buf)?;
1349            }
1350            ControlMessage::Fetch(m) => {
1351                check_required_request_id_delta(m.request_id, m.required_request_id_delta)?;
1352                m.request_id.encode_moqt::<Wire>(buf);
1353                m.required_request_id_delta.encode_moqt::<Wire>(buf);
1354                VarInt::from_usize(m.fetch_type as usize).encode_moqt::<Wire>(buf);
1355                match &m.fetch_payload {
1356                    FetchPayload::Standalone {
1357                        track_namespace,
1358                        track_name,
1359                        start_group,
1360                        start_object,
1361                        end_group,
1362                        end_object,
1363                    } => {
1364                        track_namespace.validate_moqt()?;
1365                        check_full_track_name(track_namespace, track_name)?;
1366                        track_namespace.encode_moqt::<Wire>(buf);
1367                        VarInt::from_usize(track_name.len()).encode_moqt::<Wire>(buf);
1368                        buf.put_slice(track_name);
1369                        start_group.encode_moqt::<Wire>(buf);
1370                        start_object.encode_moqt::<Wire>(buf);
1371                        end_group.encode_moqt::<Wire>(buf);
1372                        end_object.encode_moqt::<Wire>(buf);
1373                    }
1374                    FetchPayload::Joining { joining_request_id, joining_start } => {
1375                        joining_request_id.encode_moqt::<Wire>(buf);
1376                        joining_start.encode_moqt::<Wire>(buf);
1377                    }
1378                }
1379                encode_parameters(&m.parameters, buf)?;
1380            }
1381            ControlMessage::FetchOk(m) => {
1382                buf.put_u8(m.end_of_track);
1383                m.end_group.encode_moqt::<Wire>(buf);
1384                m.end_object.encode_moqt::<Wire>(buf);
1385                encode_parameters(&m.parameters, buf)?;
1386                encode_track_properties(&m.track_properties, buf)?;
1387            }
1388            ControlMessage::PublishBlocked(m) => {
1389                m.namespace_suffix.validate_moqt()?;
1390                check_full_track_name(&m.namespace_suffix, &m.track_name)?;
1391                m.namespace_suffix.encode_moqt::<Wire>(buf);
1392                VarInt::from_usize(m.track_name.len()).encode_moqt::<Wire>(buf);
1393                buf.put_slice(&m.track_name);
1394            }
1395        }
1396        Ok(())
1397    }
1398
1399    fn decode_payload(msg_type: MessageType, buf: &mut impl Buf) -> Result<Self, CodecError> {
1400        match msg_type {
1401            MessageType::Setup => {
1402                let options = decode_setup_options(buf)?;
1403                Ok(ControlMessage::Setup(Setup { options }))
1404            }
1405            MessageType::GoAway => {
1406                let uri_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1407                // Draft-17 Section 9.5: "The maximum length of the New Session
1408                // URI is 8,192 bytes. If an endpoint receives a length
1409                // exceeding the maximum, it MUST close the session with a
1410                // PROTOCOL_VIOLATION." Checked here as well as on encode: a
1411                // client migrates to this URI, so an oversize one is handed
1412                // straight to connection setup, and the codec is the only layer
1413                // that was ever going to bound it.
1414                if uri_len > MAX_GOAWAY_URI_LENGTH {
1415                    return Err(CodecError::GoAwayUriTooLong);
1416                }
1417                let uri = read_bytes(buf, uri_len)?;
1418                let timeout = VarInt::decode_moqt::<Wire>(buf)?;
1419                Ok(ControlMessage::GoAway(GoAway { new_session_uri: uri, timeout }))
1420            }
1421            MessageType::RequestOk => {
1422                let parameters = decode_parameters(buf)?;
1423                Ok(ControlMessage::RequestOk(RequestOk { parameters }))
1424            }
1425            MessageType::RequestError => {
1426                let error_code = VarInt::decode_moqt::<Wire>(buf)?;
1427                let retry_interval = VarInt::decode_moqt::<Wire>(buf)?;
1428                let reason_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1429                // Draft-17 Section 1.4.4: "The reason phrase length has a
1430                // maximum value of 1024 bytes. If an endpoint receives a length
1431                // exceeding the maximum, it MUST close the session with a
1432                // PROTOCOL_VIOLATION". A reason phrase is diagnostic text that
1433                // implementations log and surface, so an unbounded one is a
1434                // peer-controlled amplification into whatever consumes it.
1435                if reason_len > MAX_REASON_PHRASE_LENGTH {
1436                    return Err(CodecError::ReasonPhraseTooLong);
1437                }
1438                let reason_phrase = read_bytes(buf, reason_len)?;
1439                Ok(ControlMessage::RequestError(RequestError {
1440                    error_code,
1441                    retry_interval,
1442                    reason_phrase,
1443                }))
1444            }
1445            MessageType::Subscribe => {
1446                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1447                let required_request_id_delta = VarInt::decode_moqt::<Wire>(buf)?;
1448                let track_namespace = TrackNamespace::decode_moqt::<Wire>(buf)?;
1449                let tn_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1450                let track_name = read_bytes(buf, tn_len)?;
1451                check_required_request_id_delta(request_id, required_request_id_delta)?;
1452                check_full_track_name(&track_namespace, &track_name)?;
1453                let parameters = decode_parameters(buf)?;
1454                Ok(ControlMessage::Subscribe(Subscribe {
1455                    request_id,
1456                    required_request_id_delta,
1457                    track_namespace,
1458                    track_name,
1459                    parameters,
1460                }))
1461            }
1462            MessageType::SubscribeOk => {
1463                let track_alias = VarInt::decode_moqt::<Wire>(buf)?;
1464                let parameters = decode_parameters(buf)?;
1465                let track_properties = decode_track_properties(buf)?;
1466                Ok(ControlMessage::SubscribeOk(SubscribeOk {
1467                    track_alias,
1468                    parameters,
1469                    track_properties,
1470                }))
1471            }
1472            MessageType::RequestUpdate => {
1473                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1474                let required_request_id_delta = VarInt::decode_moqt::<Wire>(buf)?;
1475                check_required_request_id_delta(request_id, required_request_id_delta)?;
1476                let parameters = decode_parameters(buf)?;
1477                Ok(ControlMessage::RequestUpdate(RequestUpdate {
1478                    request_id,
1479                    required_request_id_delta,
1480                    parameters,
1481                }))
1482            }
1483            MessageType::Publish => {
1484                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1485                let required_request_id_delta = VarInt::decode_moqt::<Wire>(buf)?;
1486                let track_namespace = TrackNamespace::decode_moqt::<Wire>(buf)?;
1487                let tn_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1488                let track_name = read_bytes(buf, tn_len)?;
1489                let track_alias = VarInt::decode_moqt::<Wire>(buf)?;
1490                check_required_request_id_delta(request_id, required_request_id_delta)?;
1491                check_full_track_name(&track_namespace, &track_name)?;
1492                let parameters = decode_parameters(buf)?;
1493                let track_properties = decode_track_properties(buf)?;
1494                Ok(ControlMessage::Publish(Publish {
1495                    request_id,
1496                    required_request_id_delta,
1497                    track_namespace,
1498                    track_name,
1499                    track_alias,
1500                    parameters,
1501                    track_properties,
1502                }))
1503            }
1504            MessageType::PublishOk => {
1505                let parameters = decode_parameters(buf)?;
1506                Ok(ControlMessage::PublishOk(PublishOk { parameters }))
1507            }
1508            MessageType::PublishDone => {
1509                let status_code = VarInt::decode_moqt::<Wire>(buf)?;
1510                let stream_count = VarInt::decode_moqt::<Wire>(buf)?;
1511                let reason_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1512                // Draft-17 Section 1.4.4, the same bound as REQUEST_ERROR above.
1513                if reason_len > MAX_REASON_PHRASE_LENGTH {
1514                    return Err(CodecError::ReasonPhraseTooLong);
1515                }
1516                let reason_phrase = read_bytes(buf, reason_len)?;
1517                Ok(ControlMessage::PublishDone(PublishDone {
1518                    status_code,
1519                    stream_count,
1520                    reason_phrase,
1521                }))
1522            }
1523            MessageType::PublishNamespace => {
1524                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1525                let required_request_id_delta = VarInt::decode_moqt::<Wire>(buf)?;
1526                let track_namespace = TrackNamespace::decode_moqt::<Wire>(buf)?;
1527                check_required_request_id_delta(request_id, required_request_id_delta)?;
1528                let parameters = decode_parameters(buf)?;
1529                Ok(ControlMessage::PublishNamespace(PublishNamespace {
1530                    request_id,
1531                    required_request_id_delta,
1532                    track_namespace,
1533                    parameters,
1534                }))
1535            }
1536            MessageType::Namespace => {
1537                let namespace_suffix = TrackNamespace::decode_allow_empty_moqt::<Wire>(buf)?;
1538                Ok(ControlMessage::Namespace(Namespace { namespace_suffix }))
1539            }
1540            MessageType::NamespaceDone => {
1541                let namespace_suffix = TrackNamespace::decode_allow_empty_moqt::<Wire>(buf)?;
1542                Ok(ControlMessage::NamespaceDone(NamespaceDone { namespace_suffix }))
1543            }
1544            MessageType::SubscribeNamespace => {
1545                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1546                let required_request_id_delta = VarInt::decode_moqt::<Wire>(buf)?;
1547                let namespace_prefix = TrackNamespace::decode_allow_empty_moqt::<Wire>(buf)?;
1548                let subscribe_options = VarInt::decode_moqt::<Wire>(buf)?;
1549                check_required_request_id_delta(request_id, required_request_id_delta)?;
1550                let parameters = decode_parameters(buf)?;
1551                Ok(ControlMessage::SubscribeNamespace(SubscribeNamespace {
1552                    request_id,
1553                    required_request_id_delta,
1554                    namespace_prefix,
1555                    subscribe_options,
1556                    parameters,
1557                }))
1558            }
1559            MessageType::TrackStatus => {
1560                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1561                let required_request_id_delta = VarInt::decode_moqt::<Wire>(buf)?;
1562                let track_namespace = TrackNamespace::decode_moqt::<Wire>(buf)?;
1563                let tn_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1564                let track_name = read_bytes(buf, tn_len)?;
1565                check_required_request_id_delta(request_id, required_request_id_delta)?;
1566                check_full_track_name(&track_namespace, &track_name)?;
1567                let parameters = decode_parameters(buf)?;
1568                Ok(ControlMessage::TrackStatus(TrackStatus {
1569                    request_id,
1570                    required_request_id_delta,
1571                    track_namespace,
1572                    track_name,
1573                    parameters,
1574                }))
1575            }
1576            MessageType::Fetch => {
1577                let request_id = VarInt::decode_moqt::<Wire>(buf)?;
1578                let required_request_id_delta = VarInt::decode_moqt::<Wire>(buf)?;
1579                let fetch_type_val = VarInt::decode_moqt::<Wire>(buf)?.into_inner();
1580                let fetch_type = FetchType::from_u64(fetch_type_val)
1581                    .ok_or(CodecError::InvalidFetchType(fetch_type_val))?;
1582                let fetch_payload = match fetch_type {
1583                    FetchType::Standalone => {
1584                        let track_namespace = TrackNamespace::decode_moqt::<Wire>(buf)?;
1585                        let tn_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1586                        let track_name = read_bytes(buf, tn_len)?;
1587                        let start_group = VarInt::decode_moqt::<Wire>(buf)?;
1588                        let start_object = VarInt::decode_moqt::<Wire>(buf)?;
1589                        let end_group = VarInt::decode_moqt::<Wire>(buf)?;
1590                        let end_object = VarInt::decode_moqt::<Wire>(buf)?;
1591                        check_full_track_name(&track_namespace, &track_name)?;
1592                        FetchPayload::Standalone {
1593                            track_namespace,
1594                            track_name,
1595                            start_group,
1596                            start_object,
1597                            end_group,
1598                            end_object,
1599                        }
1600                    }
1601                    FetchType::RelativeJoining | FetchType::AbsoluteJoining => {
1602                        let joining_request_id = VarInt::decode_moqt::<Wire>(buf)?;
1603                        let joining_start = VarInt::decode_moqt::<Wire>(buf)?;
1604                        FetchPayload::Joining { joining_request_id, joining_start }
1605                    }
1606                };
1607                check_required_request_id_delta(request_id, required_request_id_delta)?;
1608                let parameters = decode_parameters(buf)?;
1609                Ok(ControlMessage::Fetch(Fetch {
1610                    request_id,
1611                    required_request_id_delta,
1612                    fetch_type,
1613                    fetch_payload,
1614                    parameters,
1615                }))
1616            }
1617            MessageType::FetchOk => {
1618                if buf.remaining() < 1 {
1619                    return Err(CodecError::UnexpectedEnd);
1620                }
1621                let end_of_track = buf.get_u8();
1622                let end_group = VarInt::decode_moqt::<Wire>(buf)?;
1623                let end_object = VarInt::decode_moqt::<Wire>(buf)?;
1624                let parameters = decode_parameters(buf)?;
1625                let track_properties = decode_track_properties(buf)?;
1626                Ok(ControlMessage::FetchOk(FetchOk {
1627                    end_of_track,
1628                    end_group,
1629                    end_object,
1630                    parameters,
1631                    track_properties,
1632                }))
1633            }
1634            MessageType::PublishBlocked => {
1635                let namespace_suffix = TrackNamespace::decode_allow_empty_moqt::<Wire>(buf)?;
1636                let tn_len = VarInt::decode_moqt::<Wire>(buf)?.into_inner() as usize;
1637                let track_name = read_bytes(buf, tn_len)?;
1638                check_full_track_name(&namespace_suffix, &track_name)?;
1639                Ok(ControlMessage::PublishBlocked(PublishBlocked { namespace_suffix, track_name }))
1640            }
1641        }
1642    }
1643
1644    pub fn message_type(&self) -> MessageType {
1645        match self {
1646            ControlMessage::Setup(_) => MessageType::Setup,
1647            ControlMessage::GoAway(_) => MessageType::GoAway,
1648            ControlMessage::RequestOk(_) => MessageType::RequestOk,
1649            ControlMessage::RequestError(_) => MessageType::RequestError,
1650            ControlMessage::Subscribe(_) => MessageType::Subscribe,
1651            ControlMessage::SubscribeOk(_) => MessageType::SubscribeOk,
1652            ControlMessage::RequestUpdate(_) => MessageType::RequestUpdate,
1653            ControlMessage::Publish(_) => MessageType::Publish,
1654            ControlMessage::PublishOk(_) => MessageType::PublishOk,
1655            ControlMessage::PublishDone(_) => MessageType::PublishDone,
1656            ControlMessage::PublishNamespace(_) => MessageType::PublishNamespace,
1657            ControlMessage::Namespace(_) => MessageType::Namespace,
1658            ControlMessage::NamespaceDone(_) => MessageType::NamespaceDone,
1659            ControlMessage::SubscribeNamespace(_) => MessageType::SubscribeNamespace,
1660            ControlMessage::TrackStatus(_) => MessageType::TrackStatus,
1661            ControlMessage::Fetch(_) => MessageType::Fetch,
1662            ControlMessage::FetchOk(_) => MessageType::FetchOk,
1663            ControlMessage::PublishBlocked(_) => MessageType::PublishBlocked,
1664        }
1665    }
1666}