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