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