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