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