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