Skip to main content

musli_web/
format.rs

1//! Runtime dispatch over the [`Format`]s which message bodies can be encoded
2//! with.
3//!
4//! Dispatch has to happen at runtime rather than through generics, since a
5//! server adapts to whichever format a client asks for. See the [wire format]
6//! for details.
7//!
8//! [wire format]: crate::api#wire-format
9
10use core::fmt::{self, Write};
11use core::str;
12
13use alloc::string::{String, ToString};
14use alloc::vec::Vec;
15
16use musli::reader::SliceReader;
17
18use crate::api::{
19    ChannelId, DecodeBody, EncodeBody, Format, Mode, RequestHeader, ResponseHeader, VERSION,
20};
21
22/// The tag byte which terminates a binary header block.
23///
24/// A header id is never zero, so this can never be confused for one.
25const END_OF_HEADERS: u8 = 0;
26
27/// The mask which recovers the header id out of a binary tag byte.
28const ID_MASK: u8 = 0b0011_1111;
29
30/// The shift which recovers the width class out of a binary tag byte.
31const WIDTH_SHIFT: u32 = 6;
32
33/// One header of a message envelope.
34///
35/// Each header is addressed by a small `id` in the binary envelope and by
36/// `name` in the text one, so the two spellings carry exactly the same model.
37#[derive(Clone, Copy)]
38pub(crate) struct Header {
39    /// The identifier used in the binary envelope, which is never zero and
40    /// never wider than [`ID_MASK`].
41    pub(crate) id: u8,
42    /// The name used in the text envelope.
43    pub(crate) name: &'static str,
44}
45
46/// A value which can be carried by a header.
47///
48/// Every header value is an unsigned integer, so `u32` is the common
49/// representation they are read and written through. This is what lets an
50/// unknown header be skipped: the tag says how wide the value is without
51/// saying anything about what it means.
52pub(crate) trait HeaderValue
53where
54    Self: Copy,
55{
56    /// Widen the value so it can be written.
57    fn to_u32(self) -> u32;
58
59    /// Narrow a value which was read, failing if it does not fit.
60    fn from_u32(value: u32) -> Option<Self>;
61}
62
63macro_rules! header_value {
64    ($($ty:ty),* $(,)?) => {
65        $(
66            impl HeaderValue for $ty {
67                #[inline]
68                fn to_u32(self) -> u32 {
69                    u32::from(self)
70                }
71
72                #[inline]
73                fn from_u32(value: u32) -> Option<Self> {
74                    Self::try_from(value).ok()
75                }
76            }
77        )*
78    };
79}
80
81header_value!(u8, u16);
82
83impl HeaderValue for u32 {
84    #[inline]
85    fn to_u32(self) -> u32 {
86        self
87    }
88
89    #[inline]
90    fn from_u32(value: u32) -> Option<Self> {
91        Some(value)
92    }
93}
94
95impl HeaderValue for ChannelId {
96    #[inline]
97    fn to_u32(self) -> u32 {
98        u32::from(self.raw())
99    }
100
101    #[inline]
102    fn from_u32(value: u32) -> Option<Self> {
103        Some(ChannelId::from_u16(u16::try_from(value).ok()?))
104    }
105}
106
107/// A message envelope, which is a block of headers in either spelling.
108///
109/// See the [wire format] for the shape this produces.
110///
111/// [wire format]: crate::api#wire-format
112// NB: Only the modules which speak the protocol have any use for envelopes.
113#[cfg_attr(
114    not(any(feature = "ws", feature = "client", feature = "web03")),
115    allow(dead_code)
116)]
117pub(crate) trait Envelope
118where
119    Self: Sized,
120{
121    /// Every header this envelope knows about, in the order they are written.
122    const HEADERS: &'static [Header];
123
124    /// The envelope with every header left at zero, which is what an absent
125    /// header reads back as.
126    fn empty() -> Self;
127
128    /// Read the value of one of [`Envelope::HEADERS`].
129    fn get(&self, id: u8) -> u32;
130
131    /// Apply a value to one of [`Envelope::HEADERS`].
132    ///
133    /// Fails if the value does not fit the header, which is how a peer which
134    /// writes a wider value than the header can hold is caught.
135    fn set(&mut self, id: u8, value: u32) -> Result<(), Error>;
136}
137
138macro_rules! envelope {
139    ($ty:ident { $($id:literal => $field:ident,)* }) => {
140        impl Envelope for $ty {
141            const HEADERS: &'static [Header] = &[
142                $(Header { id: $id, name: stringify!($field) },)*
143            ];
144
145            #[inline]
146            fn empty() -> Self {
147                Self {
148                    $($field: HeaderValue::from_u32(0).expect("Zero fits every header"),)*
149                }
150            }
151
152            #[inline]
153            fn get(&self, id: u8) -> u32 {
154                match id {
155                    $($id => self.$field.to_u32(),)*
156                    _ => 0,
157                }
158            }
159
160            #[inline]
161            fn set(&mut self, id: u8, value: u32) -> Result<(), Error> {
162                match id {
163                    $(
164                        $id => {
165                            let Some(value) = HeaderValue::from_u32(value) else {
166                                return Err(Error::new(ErrorKind::HeaderRange {
167                                    name: stringify!($field),
168                                    value,
169                                }));
170                            };
171
172                            self.$field = value;
173                        }
174                    )*
175                    // NB: Callers only ever pass an id which came out of
176                    // `HEADERS`, an unknown one is reported before it gets
177                    // here.
178                    _ => return Err(Error::new(ErrorKind::UnknownHeader { id, name: None })),
179                }
180
181                Ok(())
182            }
183        }
184    };
185}
186
187// NB: `version` is first in both, and its id is fixed forever. It is the one
188// header which has to keep meaning what it means across every version of the
189// protocol, since it is what says which version the rest is written against.
190envelope! {
191    RequestHeader {
192        1 => version,
193        2 => serial,
194        3 => id,
195        4 => format,
196        5 => channel,
197        6 => app,
198    }
199}
200
201envelope! {
202    ResponseHeader {
203        1 => version,
204        2 => serial,
205        3 => broadcast,
206        4 => error,
207        5 => format,
208        6 => channel,
209        7 => app,
210    }
211}
212
213/// The id of the `version` header, which is the same in every envelope.
214const VERSION_ID: u8 = 1;
215
216/// Look up a header by its binary id.
217#[inline]
218fn header_by_id<T>(id: u8) -> Option<&'static Header>
219where
220    T: Envelope,
221{
222    T::HEADERS.iter().find(|h| h.id == id)
223}
224
225/// Look up a header by its text name.
226#[inline]
227fn header_by_name<T>(name: &str) -> Option<&'static Header>
228where
229    T: Envelope,
230{
231    T::HEADERS.iter().find(|h| h.name == name)
232}
233
234/// Adapter which lets the text envelope be written straight into the outgoing
235/// buffer without going through an intermediate [`String`].
236///
237/// [`String`]: alloc::string::String
238struct Utf8Writer<'a> {
239    out: &'a mut Vec<u8>,
240}
241
242impl Write for Utf8Writer<'_> {
243    #[inline]
244    fn write_str(&mut self, s: &str) -> fmt::Result {
245        self.out.extend_from_slice(s.as_bytes());
246        Ok(())
247    }
248}
249
250/// Write one header of a binary envelope.
251///
252/// The tag byte carries both the header id and how wide the value is, so a
253/// reader can step over a header it does not know rather than losing its place.
254#[inline]
255fn write_binary_header(out: &mut Vec<u8>, id: u8, value: u32) {
256    debug_assert!(id != END_OF_HEADERS && id & ID_MASK == id);
257
258    // NB: The narrowest class which holds the value, so a small value costs no
259    // more than it has to.
260    if let Ok(value) = u8::try_from(value) {
261        out.push(id);
262        out.push(value);
263    } else if let Ok(value) = u16::try_from(value) {
264        out.push(id | (1 << WIDTH_SHIFT));
265        out.extend_from_slice(&value.to_le_bytes());
266    } else {
267        out.push(id | (2 << WIDTH_SHIFT));
268        out.extend_from_slice(&value.to_le_bytes());
269    }
270}
271
272/// Encode the fixed envelope of a message.
273///
274/// The envelope never depends on which format has been negotiated for bodies,
275/// which is what allows both peers to read it unconditionally. `mode` decides
276/// how it is spelled, see the [wire format].
277///
278/// [wire format]: crate::api#wire-format
279// NB: Only the modules which speak the protocol have any use for envelopes.
280#[cfg_attr(
281    not(any(feature = "ws", feature = "client", feature = "web03")),
282    allow(dead_code)
283)]
284#[inline]
285pub(crate) fn encode_envelope<T>(mode: Mode, out: &mut Vec<u8>, value: &T) -> Result<(), Error>
286where
287    T: Envelope,
288{
289    match mode {
290        Mode::Binary => {
291            for header in T::HEADERS {
292                let field = value.get(header.id);
293
294                // NB: An absent header reads back as zero, so writing one would
295                // say nothing.
296                if field == 0 {
297                    continue;
298                }
299
300                write_binary_header(out, header.id, field);
301            }
302
303            out.push(END_OF_HEADERS);
304        }
305        Mode::Text => {
306            let mut writer = Utf8Writer { out };
307
308            // NB: Every header is written, zero or not. This spelling is for
309            // people to read, so being explicit beats being terse.
310            for header in T::HEADERS {
311                let field = value.get(header.id);
312
313                if writeln!(writer, "{}: {field}", header.name).is_err() {
314                    return Err(Error::new(ErrorKind::TextWrite));
315                }
316            }
317
318            // NB: The empty line is what separates the envelope from the body.
319            out.push(b'\n');
320        }
321    }
322
323    Ok(())
324}
325
326/// Decode the fixed envelope of a message, advancing `at` past it.
327///
328/// A header which this build does not know about is stepped over and then
329/// reported, since a peer which speaks headers we do not is a peer we cannot
330/// safely act on.
331#[cfg_attr(
332    not(any(feature = "ws", feature = "client", feature = "web03")),
333    allow(dead_code)
334)]
335pub(crate) fn decode_envelope<T>(mode: Mode, buf: &[u8], at: &mut usize) -> Result<T, Error>
336where
337    T: Envelope,
338{
339    let Some(tail) = buf.get(*at..) else {
340        return Err(Error::new(ErrorKind::Overflow {
341            at: *at,
342            len: buf.len(),
343        }));
344    };
345
346    let mut header = T::empty();
347    // NB: Held rather than returned immediately so that the whole block is
348    // stepped over first, which is what makes the error precise rather than a
349    // desync somewhere further along.
350    let mut unknown = None;
351
352    let consumed = match mode {
353        Mode::Binary => {
354            let mut rest = tail;
355
356            loop {
357                let Some((&tag, next)) = rest.split_first() else {
358                    return Err(Error::new(ErrorKind::HeadersUnterminated));
359                };
360
361                rest = next;
362
363                if tag == END_OF_HEADERS {
364                    break;
365                }
366
367                let id = tag & ID_MASK;
368
369                let width = match tag >> WIDTH_SHIFT {
370                    0 => 1,
371                    1 => 2,
372                    2 => 4,
373                    // NB: Reserved for a future width, which cannot be stepped
374                    // over since nothing says how wide it is.
375                    _ => return Err(Error::new(ErrorKind::HeaderWidth { id })),
376                };
377
378                let Some((value, next)) = rest.split_at_checked(width) else {
379                    return Err(Error::new(ErrorKind::HeaderTruncated { id, width }));
380                };
381
382                rest = next;
383
384                let mut bytes = [0; 4];
385                bytes[..width].copy_from_slice(value);
386                let value = u32::from_le_bytes(bytes);
387
388                // NB: The tag said how wide this is, so an unknown header has
389                // already been stepped over by the time we get here.
390                if header_by_id::<T>(id).is_none() {
391                    if unknown.is_none() {
392                        unknown = Some(ErrorKind::UnknownHeader { id, name: None });
393                    }
394
395                    continue;
396                }
397
398                header.set(id, value)?;
399            }
400
401            tail.len() - rest.len()
402        }
403        Mode::Text => {
404            let Ok(text) = str::from_utf8(tail) else {
405                return Err(Error::new(ErrorKind::TextNotUtf8));
406            };
407
408            let mut rest = text;
409
410            loop {
411                let Some((line, next)) = rest.split_once('\n') else {
412                    return Err(Error::new(ErrorKind::HeadersUnterminated));
413                };
414
415                rest = next;
416
417                // NB: Tolerated so that an envelope which has been through
418                // something that insists on CRLF still reads.
419                let line = line.strip_suffix('\r').unwrap_or(line);
420
421                if line.is_empty() {
422                    break;
423                }
424
425                let Some((name, value)) = line.split_once(':') else {
426                    return Err(Error::new(ErrorKind::TextSeparator));
427                };
428
429                let name = name.trim();
430                let value = value.trim();
431
432                let Some(found) = header_by_name::<T>(name) else {
433                    if unknown.is_none() {
434                        unknown = Some(ErrorKind::UnknownHeader {
435                            id: 0,
436                            name: Some(name.to_string()),
437                        });
438                    }
439
440                    continue;
441                };
442
443                let Ok(value) = value.parse::<u32>() else {
444                    return Err(Error::new(ErrorKind::TextField { key: found.name }));
445                };
446
447                header.set(found.id, value)?;
448            }
449
450            tail.len() - rest.len()
451        }
452    };
453
454    // NB: Ahead of the unknown header, since the version is what explains it.
455    // A peer which states a version we do not have is refused outright: there
456    // is no way to know which parts of what it said still mean what they used
457    // to, so acting on the parts we recognize would be acting on a message we
458    // have not understood.
459    let version = header.get(VERSION_ID);
460
461    if version != VERSION {
462        return Err(Error::new(ErrorKind::UnsupportedVersion { version }));
463    }
464
465    if let Some(kind) = unknown {
466        return Err(Error::new(kind));
467    }
468
469    *at += consumed;
470    Ok(header)
471}
472
473/// Encode `value` with the given format, appending it to `out`.
474macro_rules! encode_with {
475    ($module:ident, $out:expr, $value:expr, $variant:ident) => {{
476        musli::$module::encode($out, $value)
477            .map(|_| ())
478            .map_err(Error::$variant)
479    }};
480}
481
482/// Decode a value with the given format from `buf` at `at`, advancing `at` past
483/// what was consumed.
484macro_rules! decode_with {
485    ($module:ident, $tail:expr, $at:expr, $len:expr, $variant:ident) => {{
486        let mut reader = SliceReader::new($tail);
487        let value = musli::$module::decode(&mut reader).map_err(Error::$variant)?;
488        *$at += $tail.len() - reader.remaining();
489        let _ = $len;
490        Ok(value)
491    }};
492}
493
494impl Format {
495    /// Test if this build of the crate has support for the format.
496    ///
497    /// Formats are gated behind features, so a peer might genuinely be unable
498    /// to speak a format which the other side asks for. This is what the
499    /// [negotiation protocol] uses to decide whether a request can be honored.
500    ///
501    /// [negotiation protocol]: crate::api#negotiating-the-format
502    ///
503    /// # Examples
504    ///
505    /// ```
506    /// use musli_web::api::Format;
507    ///
508    /// // The default format is always available.
509    /// assert!(Format::DEFAULT.is_supported());
510    /// ```
511    #[inline]
512    pub const fn is_supported(self) -> bool {
513        match self {
514            Format::Packed => cfg!(feature = "format-packed"),
515            Format::Storage => cfg!(feature = "format-storage"),
516            Format::Wire => cfg!(feature = "format-wire"),
517            Format::Descriptive => cfg!(feature = "format-descriptive"),
518            Format::Json => cfg!(feature = "format-json"),
519        }
520    }
521
522    /// Iterate over every format this build of the crate supports.
523    ///
524    /// # Examples
525    ///
526    /// ```
527    /// use musli_web::api::Format;
528    ///
529    /// assert!(Format::supported().any(|f| f == Format::DEFAULT));
530    /// ```
531    #[inline]
532    pub fn supported() -> impl Iterator<Item = Format> {
533        Format::ALL.iter().copied().filter(|f| f.is_supported())
534    }
535
536    /// Encode `value` with this format, appending it to `out`.
537    #[cfg_attr(
538        not(any(feature = "ws", feature = "client", feature = "web03")),
539        allow(dead_code)
540    )]
541    pub(crate) fn encode<T>(self, out: &mut Vec<u8>, value: &T) -> Result<(), Error>
542    where
543        T: ?Sized + EncodeBody,
544    {
545        match self {
546            #[cfg(feature = "format-packed")]
547            Format::Packed => encode_with!(packed, out, value, packed),
548            #[cfg(feature = "format-storage")]
549            Format::Storage => encode_with!(storage, out, value, storage),
550            #[cfg(feature = "format-wire")]
551            Format::Wire => encode_with!(wire, out, value, wire),
552            #[cfg(feature = "format-descriptive")]
553            Format::Descriptive => encode_with!(descriptive, out, value, descriptive),
554            #[cfg(feature = "format-json")]
555            Format::Json => musli::json::encode(out, value)
556                .map(|_| ())
557                .map_err(Error::json),
558            #[allow(unreachable_patterns)]
559            _ => Err(Error::unsupported(self)),
560        }
561    }
562
563    /// Decode a value with this format from `buf` starting at `at`, advancing
564    /// `at` past what was consumed.
565    ///
566    /// Advancing `at` is what allows several payloads to be decoded in sequence
567    /// out of a single message.
568    #[cfg_attr(
569        not(any(feature = "ws", feature = "client", feature = "web03")),
570        allow(dead_code)
571    )]
572    pub(crate) fn decode<'de, T>(self, buf: &'de [u8], at: &mut usize) -> Result<T, Error>
573    where
574        T: DecodeBody<'de>,
575    {
576        let Some(tail) = buf.get(*at..) else {
577            return Err(Error::new(ErrorKind::Overflow {
578                at: *at,
579                len: buf.len(),
580            }));
581        };
582
583        match self {
584            #[cfg(feature = "format-packed")]
585            Format::Packed => decode_with!(packed, tail, at, buf.len(), packed),
586            #[cfg(feature = "format-storage")]
587            Format::Storage => decode_with!(storage, tail, at, buf.len(), storage),
588            #[cfg(feature = "format-wire")]
589            Format::Wire => decode_with!(wire, tail, at, buf.len(), wire),
590            #[cfg(feature = "format-descriptive")]
591            Format::Descriptive => decode_with!(descriptive, tail, at, buf.len(), descriptive),
592            #[cfg(feature = "format-json")]
593            Format::Json => {
594                // NB: The borrow here is load-bearing. A `&mut &[u8]` selects
595                // the parser which keeps the referenced slice up to date as it
596                // parses, which is how the consumed length is recovered for a
597                // format that is not read through a `Reader`. Passing the slice
598                // by value selects a parser which does not write back, and the
599                // position would silently never advance.
600                let mut rest = tail;
601                let cursor = &mut rest;
602                let value = musli::json::decode(cursor).map_err(Error::json)?;
603                *at += tail.len() - rest.len();
604                Ok(value)
605            }
606            #[allow(unreachable_patterns)]
607            _ => Err(Error::unsupported(self)),
608        }
609    }
610}
611
612/// An error raised when encoding or decoding a message.
613#[derive(Debug)]
614pub struct Error {
615    kind: ErrorKind,
616}
617
618impl Error {
619    #[inline]
620    const fn new(kind: ErrorKind) -> Self {
621        Self { kind }
622    }
623
624    /// Construct an error indicating that the given format is not supported by
625    /// this build of the crate.
626    #[inline]
627    pub(crate) const fn unsupported(format: Format) -> Self {
628        Self::new(ErrorKind::Unsupported(format))
629    }
630
631    /// Test if the error is caused by a format which is not supported, and if
632    /// so return it.
633    #[inline]
634    pub fn unsupported_format(&self) -> Option<Format> {
635        match self.kind {
636            ErrorKind::Unsupported(format) => Some(format),
637            _ => None,
638        }
639    }
640
641    /// Test if the error is caused by a peer speaking a different version of
642    /// the protocol, and if so return the version it stated.
643    ///
644    /// A session between peers which do not agree on this is refused before
645    /// either of them has acted on anything the other said, see [the protocol
646    /// version].
647    ///
648    /// [the protocol version]: crate::api#the-protocol-version
649    #[inline]
650    pub fn unsupported_version(&self) -> Option<u32> {
651        match self.kind {
652            ErrorKind::UnsupportedVersion { version } => Some(version),
653            _ => None,
654        }
655    }
656
657    /// Test if the error is caused by a header this build does not know about.
658    ///
659    /// A peer which writes one is speaking a protocol this build has never
660    /// seen, which is never something to carry on through.
661    #[inline]
662    pub fn is_unknown_header(&self) -> bool {
663        matches!(self.kind, ErrorKind::UnknownHeader { .. })
664    }
665}
666
667macro_rules! error_kinds {
668    ($($(#[$meta:meta])* $variant:ident, $ctor:ident, $ty:path;)*) => {
669        #[derive(Debug)]
670        enum ErrorKind {
671            Unsupported(Format),
672            Overflow { at: usize, len: usize },
673            TextWrite,
674            TextNotUtf8,
675            TextSeparator,
676            TextField { key: &'static str },
677            HeadersUnterminated,
678            HeaderWidth { id: u8 },
679            HeaderTruncated { id: u8, width: usize },
680            HeaderRange { name: &'static str, value: u32 },
681            UnknownHeader { id: u8, name: Option<String> },
682            UnsupportedVersion { version: u32 },
683            $($(#[$meta])* $variant($ty),)*
684        }
685
686        impl Error {
687            $(
688                $(#[$meta])*
689                #[inline]
690                fn $ctor(error: $ty) -> Self {
691                    Self::new(ErrorKind::$variant(error))
692                }
693            )*
694        }
695
696        impl fmt::Display for Error {
697            #[inline]
698            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
699                match &self.kind {
700                    ErrorKind::Unsupported(format) => {
701                        write!(f, "Format `{format}` is not supported")
702                    }
703                    ErrorKind::Overflow { at, len } => {
704                        write!(f, "Offset {at} is out of bounds for a message of {len} bytes")
705                    }
706                    ErrorKind::TextWrite => {
707                        write!(f, "Failed to write text envelope")
708                    }
709                    ErrorKind::TextNotUtf8 => {
710                        write!(f, "Text envelope is not valid UTF-8")
711                    }
712                    ErrorKind::TextSeparator => {
713                        write!(f, "Text envelope has a line without a `:` separator")
714                    }
715                    ErrorKind::TextField { key } => {
716                        write!(f, "Text envelope header `{key}` is not a valid number")
717                    }
718                    ErrorKind::HeadersUnterminated => {
719                        write!(f, "Envelope is missing its end of headers marker")
720                    }
721                    ErrorKind::HeaderWidth { id } => {
722                        write!(f, "Header {id} uses a width this build cannot skip over")
723                    }
724                    ErrorKind::HeaderTruncated { id, width } => {
725                        write!(f, "Header {id} is missing its {width} byte value")
726                    }
727                    ErrorKind::HeaderRange { name, value } => {
728                        write!(f, "Header `{name}` cannot hold the value {value}")
729                    }
730                    ErrorKind::UnknownHeader { name: Some(name), .. } => {
731                        write!(f, "Unknown header `{name}`")
732                    }
733                    ErrorKind::UnknownHeader { id, name: None } => {
734                        write!(f, "Unknown header with id {id}")
735                    }
736                    ErrorKind::UnsupportedVersion { version } => {
737                        write!(
738                            f,
739                            "Peer speaks protocol version {version}, this build speaks {VERSION}"
740                        )
741                    }
742                    $($(#[$meta])* ErrorKind::$variant(..) => {
743                        write!(f, concat!("Error in the `", stringify!($ctor), "` format"))
744                    })*
745                }
746            }
747        }
748
749        impl core::error::Error for Error {
750            #[inline]
751            fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
752                match &self.kind {
753                    $($(#[$meta])* ErrorKind::$variant(error) => Some(error),)*
754                    _ => None,
755                }
756            }
757        }
758    };
759}
760
761error_kinds! {
762    #[cfg(feature = "format-packed")]
763    Packed, packed, musli::packed::Error;
764    #[cfg(feature = "format-storage")]
765    Storage, storage, musli::storage::Error;
766    #[cfg(feature = "format-wire")]
767    Wire, wire, musli::wire::Error;
768    #[cfg(feature = "format-descriptive")]
769    Descriptive, descriptive, musli::descriptive::Error;
770    #[cfg(feature = "format-json")]
771    Json, json, musli::json::Error;
772}
773
774#[cfg(test)]
775mod tests {
776    use super::*;
777
778    use alloc::vec::Vec;
779
780    use musli::{Decode, Encode};
781
782    use crate::api::{ChannelId, Format, Mode, RequestHeader, ResponseHeader, VERSION};
783
784    #[derive(Debug, PartialEq, Encode, Decode)]
785    struct Message<'de> {
786        message: &'de str,
787        tick: u32,
788    }
789
790    #[cfg(feature = "format-json")]
791    #[derive(Debug, PartialEq, Encode, Decode)]
792    #[musli(Text, tag = "type")]
793    enum JsonTarget {
794        #[musli(Text, name = "port")]
795        Port { id: u32 },
796    }
797
798    #[cfg(feature = "format-json")]
799    #[derive(Debug, PartialEq, Encode, Decode)]
800    #[musli(Text, tag = "type")]
801    enum JsonRealtime {
802        #[musli(Text, name = "rtkit")]
803        Rtkit,
804    }
805
806    /// Every supported format must round-trip a value.
807    #[test]
808    fn round_trip() {
809        for format in Format::supported() {
810            let mut buf = Vec::new();
811            let expected = Message {
812                message: "hello",
813                tick: 42,
814            };
815
816            format.encode(&mut buf, &expected).unwrap();
817
818            let mut at = 0;
819            let actual: Message<'_> = format.decode(&buf, &mut at).unwrap();
820
821            assert_eq!(actual, expected, "round trip failed for `{format}`");
822            assert_eq!(at, buf.len(), "`{format}` did not consume the whole body");
823        }
824    }
825
826    /// Several payloads must be decodable in sequence out of one buffer, which
827    /// is what `RawPacket::decode` relies on.
828    #[test]
829    fn sequential_payloads() {
830        for format in Format::supported() {
831            let mut buf = Vec::new();
832
833            let first = Message {
834                message: "first",
835                tick: 1,
836            };
837
838            let second = Message {
839                message: "second",
840                tick: 2,
841            };
842
843            format.encode(&mut buf, &first).unwrap();
844            let boundary = buf.len();
845            format.encode(&mut buf, &second).unwrap();
846
847            let mut at = 0;
848            let a: Message<'_> = format.decode(&buf, &mut at).unwrap();
849            assert_eq!(a, first, "first payload failed for `{format}`");
850            assert_eq!(at, boundary, "`{format}` misreported the first boundary");
851
852            let b: Message<'_> = format.decode(&buf, &mut at).unwrap();
853            assert_eq!(b, second, "second payload failed for `{format}`");
854            assert_eq!(at, buf.len(), "`{format}` did not consume both payloads");
855        }
856    }
857
858    /// A body must follow the fixed envelope in the same frame, for every
859    /// format, since that is the shape of every message on the wire.
860    #[test]
861    fn envelope_then_body() {
862        for format in Format::supported() {
863            let header = RequestHeader {
864                version: VERSION,
865                serial: 7,
866                id: 11,
867                format: format.to_u8(),
868                channel: ChannelId::from_u16(3),
869                app: 0,
870            };
871
872            let mut buf = Vec::new();
873            encode_envelope(Mode::DEFAULT, &mut buf, &header).unwrap();
874
875            let expected = Message {
876                message: "body",
877                tick: 9,
878            };
879
880            format.encode(&mut buf, &expected).unwrap();
881
882            let mut at = 0;
883            let decoded: RequestHeader = decode_envelope(Mode::DEFAULT, &buf, &mut at).unwrap();
884
885            assert_eq!(decoded.serial, 7);
886            assert_eq!(decoded.id, 11);
887            assert_eq!(decoded.format, format.to_u8());
888
889            let body: Message<'_> = format.decode(&buf, &mut at).unwrap();
890            assert_eq!(body, expected, "body failed for `{format}`");
891            assert_eq!(at, buf.len());
892        }
893    }
894
895    #[test]
896    #[cfg(feature = "format-json")]
897    fn json_tagged_payloads_are_sequential() {
898        let first = JsonTarget::Port { id: 1 };
899        let second = JsonRealtime::Rtkit;
900        let mut buf = Vec::new();
901
902        Format::Json.encode(&mut buf, &first).unwrap();
903        let boundary = buf.len();
904        Format::Json.encode(&mut buf, &second).unwrap();
905
906        let mut at = 0;
907        let decoded: JsonTarget = Format::Json.decode(&buf, &mut at).unwrap();
908        assert_eq!(decoded, first);
909        assert_eq!(at, boundary);
910
911        let decoded: JsonRealtime = Format::Json.decode(&buf, &mut at).unwrap();
912        assert_eq!(decoded, second);
913        assert_eq!(at, buf.len());
914    }
915
916    #[cfg(feature = "format-json")]
917    fn json_envelope_then_tagged_body(mode: Mode) {
918        let header = RequestHeader {
919            version: VERSION,
920            serial: 7,
921            id: 11,
922            format: Format::Json.to_u8(),
923            channel: ChannelId::from_u16(3),
924            app: 0,
925        };
926
927        let expected = JsonTarget::Port { id: 1 };
928        let mut buf = Vec::new();
929
930        encode_envelope(mode, &mut buf, &header).unwrap();
931        let boundary = buf.len();
932        Format::Json.encode(&mut buf, &expected).unwrap();
933
934        let mut at = 0;
935        let decoded: RequestHeader = decode_envelope(mode, &buf, &mut at).unwrap();
936        assert_eq!(decoded.serial, header.serial, "`{mode}` lost the serial");
937        assert_eq!(decoded.id, header.id, "`{mode}` lost the id");
938        assert_eq!(decoded.format, header.format, "`{mode}` lost the format");
939        assert_eq!(at, boundary, "`{mode}` misreported the envelope boundary");
940
941        let decoded: JsonTarget = Format::Json.decode(&buf, &mut at).unwrap();
942        assert_eq!(decoded, expected, "`{mode}` body failed");
943        assert_eq!(at, buf.len(), "`{mode}` did not consume the whole frame");
944    }
945
946    #[test]
947    #[cfg(feature = "format-json")]
948    fn json_binary_envelope_then_tagged_body() {
949        json_envelope_then_tagged_body(Mode::Binary);
950    }
951
952    #[test]
953    #[cfg(feature = "format-json")]
954    fn json_text_envelope_then_tagged_body() {
955        json_envelope_then_tagged_body(Mode::Text);
956    }
957
958    /// JSON must be keyed by field name, which is the point of offering it.
959    #[test]
960    #[cfg(feature = "format-json")]
961    fn json_is_human_readable() {
962        let mut buf = Vec::new();
963
964        Format::Json
965            .encode(
966                &mut buf,
967                &Message {
968                    message: "hello",
969                    tick: 42,
970                },
971            )
972            .unwrap();
973
974        assert_eq!(
975            core::str::from_utf8(&buf).unwrap(),
976            r#"{"message":"hello","tick":42}"#
977        );
978    }
979
980    /// The application version is an ordinary header in both envelopes, so it
981    /// round trips at every width and is left out of a binary one when unset.
982    #[test]
983    fn app_header_round_trips() {
984        for app in [0, 7, 300, 70_000, u32::MAX] {
985            for mode in Mode::ALL {
986                let request = RequestHeader {
987                    version: VERSION,
988                    serial: 1,
989                    id: 2,
990                    format: 0,
991                    channel: ChannelId::NONE,
992                    app,
993                };
994
995                let mut buf = Vec::new();
996                encode_envelope(*mode, &mut buf, &request).unwrap();
997                let mut at = 0;
998                let decoded: RequestHeader = decode_envelope(*mode, &buf, &mut at).unwrap();
999                assert_eq!(decoded.app, app);
1000                assert_eq!(at, buf.len());
1001
1002                let response = ResponseHeader {
1003                    version: VERSION,
1004                    serial: 0,
1005                    broadcast: 1,
1006                    error: 0,
1007                    format: 0,
1008                    channel: ChannelId::NONE,
1009                    app,
1010                };
1011
1012                let mut buf = Vec::new();
1013                encode_envelope(*mode, &mut buf, &response).unwrap();
1014                let mut at = 0;
1015                let decoded: ResponseHeader = decode_envelope(*mode, &buf, &mut at).unwrap();
1016                assert_eq!(decoded.app, app);
1017                assert_eq!(at, buf.len());
1018            }
1019        }
1020
1021        // NB: Request id 6 and response id 7, and absent when zero.
1022        let mut buf = Vec::new();
1023
1024        let request = RequestHeader {
1025            version: VERSION,
1026            serial: 0,
1027            id: 0,
1028            format: 0,
1029            channel: ChannelId::NONE,
1030            app: 5,
1031        };
1032
1033        encode_envelope(Mode::Binary, &mut buf, &request).unwrap();
1034        assert_eq!(buf, [0x01, VERSION as u8, 0x06, 5, 0x00]);
1035
1036        buf.clear();
1037
1038        let response = ResponseHeader {
1039            version: VERSION,
1040            serial: 0,
1041            broadcast: 0,
1042            error: 0,
1043            format: 0,
1044            channel: ChannelId::NONE,
1045            app: 0,
1046        };
1047
1048        encode_envelope(Mode::Binary, &mut buf, &response).unwrap();
1049        assert_eq!(buf, [0x01, VERSION as u8, 0x00]);
1050    }
1051
1052    /// The text envelope is what makes a frame readable by hand, so its exact
1053    /// shape is part of the protocol.
1054    #[test]
1055    fn text_envelope_is_http_like() {
1056        let mut buf = Vec::new();
1057
1058        let header = RequestHeader {
1059            version: VERSION,
1060            serial: 7,
1061            id: 11,
1062            format: Format::Json.to_u8(),
1063            channel: ChannelId::from_u16(3),
1064            app: 9,
1065        };
1066
1067        encode_envelope(Mode::Text, &mut buf, &header).unwrap();
1068
1069        assert_eq!(
1070            core::str::from_utf8(&buf).unwrap(),
1071            "version: 2\nserial: 7\nid: 11\nformat: 5\nchannel: 3\napp: 9\n\n"
1072        );
1073
1074        let mut buf = Vec::new();
1075
1076        let header = ResponseHeader {
1077            version: VERSION,
1078            serial: 0,
1079            broadcast: 13,
1080            error: 0,
1081            format: Format::Json.to_u8(),
1082            channel: ChannelId::NONE,
1083            app: 0,
1084        };
1085
1086        encode_envelope(Mode::Text, &mut buf, &header).unwrap();
1087
1088        assert_eq!(
1089            core::str::from_utf8(&buf).unwrap(),
1090            "version: 2\nserial: 0\nbroadcast: 13\nerror: 0\nformat: 5\nchannel: 0\napp: 0\n\n"
1091        );
1092    }
1093
1094    /// Both envelopes must survive a round trip in either mode, and report
1095    /// exactly how much of the buffer they consumed.
1096    #[test]
1097    fn text_envelope_round_trip() {
1098        for mode in Mode::ALL.iter().copied() {
1099            let mut buf = Vec::new();
1100
1101            let request = RequestHeader {
1102                version: VERSION,
1103                serial: 4294967295,
1104                id: 65535,
1105                format: Format::Json.to_u8(),
1106                channel: ChannelId::from_u16(65535),
1107                app: 0,
1108            };
1109
1110            encode_envelope(mode, &mut buf, &request).unwrap();
1111
1112            let mut at = 0;
1113            let decoded: RequestHeader = decode_envelope(mode, &buf, &mut at).unwrap();
1114
1115            assert_eq!(decoded.serial, request.serial, "`{mode}` lost the serial");
1116            assert_eq!(decoded.id, request.id, "`{mode}` lost the id");
1117            assert_eq!(decoded.format, request.format, "`{mode}` lost the format");
1118            assert_eq!(
1119                decoded.channel, request.channel,
1120                "`{mode}` lost the channel"
1121            );
1122            assert_eq!(at, buf.len(), "`{mode}` did not consume the whole envelope");
1123
1124            let mut buf = Vec::new();
1125
1126            let response = ResponseHeader {
1127                version: VERSION,
1128                serial: 9,
1129                broadcast: 13,
1130                error: 17,
1131                format: Format::Json.to_u8(),
1132                channel: ChannelId::from_u16(21),
1133                app: 0,
1134            };
1135
1136            encode_envelope(mode, &mut buf, &response).unwrap();
1137
1138            let mut at = 0;
1139            let decoded: ResponseHeader = decode_envelope(mode, &buf, &mut at).unwrap();
1140
1141            assert_eq!(decoded.serial, response.serial, "`{mode}` lost the serial");
1142            assert_eq!(
1143                decoded.broadcast, response.broadcast,
1144                "`{mode}` lost the broadcast"
1145            );
1146            assert_eq!(decoded.error, response.error, "`{mode}` lost the error");
1147            assert_eq!(decoded.format, response.format, "`{mode}` lost the format");
1148            assert_eq!(
1149                decoded.channel, response.channel,
1150                "`{mode}` lost the channel"
1151            );
1152            assert_eq!(at, buf.len(), "`{mode}` did not consume the whole envelope");
1153        }
1154    }
1155
1156    /// A text envelope has to be followed by a body in the same frame, which is
1157    /// what the empty line separates it from.
1158    #[test]
1159    #[cfg(feature = "format-json")]
1160    fn text_envelope_then_body() {
1161        let mut buf = Vec::new();
1162
1163        let header = RequestHeader {
1164            version: VERSION,
1165            serial: 1,
1166            id: 2,
1167            format: Format::Json.to_u8(),
1168            channel: ChannelId::NONE,
1169            app: 0,
1170        };
1171
1172        encode_envelope(Mode::Text, &mut buf, &header).unwrap();
1173
1174        let expected = Message {
1175            message: "hello",
1176            tick: 42,
1177        };
1178
1179        Format::Json.encode(&mut buf, &expected).unwrap();
1180
1181        assert_eq!(
1182            core::str::from_utf8(&buf).unwrap(),
1183            "version: 2\nserial: 1\nid: 2\nformat: 5\nchannel: 0\napp: 0\n\n{\"message\":\"hello\",\"tick\":42}"
1184        );
1185
1186        let mut at = 0;
1187        let decoded: RequestHeader = decode_envelope(Mode::Text, &buf, &mut at).unwrap();
1188        assert_eq!(decoded.id, 2);
1189
1190        let body: Message<'_> = Format::Json.decode(&buf, &mut at).unwrap();
1191        assert_eq!(body, expected);
1192        assert_eq!(at, buf.len());
1193    }
1194
1195    /// Headers may arrive in any order and an absent one reads as zero, which
1196    /// is what lets a header be dropped from a message that has no use for it.
1197    #[test]
1198    fn text_envelope_is_order_independent() {
1199        // Headers in a different order, one which is absent, and CRLF line
1200        // endings throughout.
1201        let buf = b"channel: 3\r\nid: 11\r\nversion: 2\r\n\r\n";
1202
1203        let mut at = 0;
1204        let header: RequestHeader = decode_envelope(Mode::Text, buf, &mut at).unwrap();
1205
1206        assert_eq!(header.id, 11);
1207        assert_eq!(header.channel, ChannelId::from_u16(3));
1208        assert_eq!(header.serial, 0, "an absent header reads as zero");
1209        assert_eq!(header.format, 0, "an absent header reads as zero");
1210        assert_eq!(at, buf.len());
1211    }
1212
1213    /// The binary envelope is a block of tagged headers so that an unknown one
1214    /// can be stepped over, and a header which is zero is simply not written.
1215    #[test]
1216    fn binary_envelope_is_tagged() {
1217        let mut buf = Vec::new();
1218
1219        let header = RequestHeader {
1220            version: VERSION,
1221            serial: 7,
1222            id: 300,
1223            format: Format::Json.to_u8(),
1224            channel: ChannelId::NONE,
1225            app: 0,
1226        };
1227
1228        encode_envelope(Mode::Binary, &mut buf, &header).unwrap();
1229
1230        assert_eq!(
1231            buf,
1232            [
1233                // `version` comes first and fits a byte.
1234                0x01,
1235                VERSION as u8, //
1236                // `serial` fits a byte, so it is written as one.
1237                0x02,
1238                7, //
1239                // `id` needs two, which the tag says.
1240                0x43,
1241                0x2c,
1242                0x01, //
1243                // `format` fits a byte.
1244                0x04,
1245                5, //
1246                // `channel` is zero, so it is not written at all.
1247                0x00,
1248            ]
1249        );
1250
1251        let mut at = 0;
1252        let decoded: RequestHeader = decode_envelope(Mode::Binary, &buf, &mut at).unwrap();
1253
1254        assert_eq!(decoded.serial, 7);
1255        assert_eq!(decoded.id, 300);
1256        assert_eq!(decoded.format, Format::Json.to_u8());
1257        assert_eq!(decoded.channel, ChannelId::NONE);
1258        assert_eq!(at, buf.len());
1259    }
1260
1261    /// A header this build does not know about has to be stepped over and then
1262    /// reported, in both spellings.
1263    ///
1264    /// Carrying on would mean acting on a message from a peer which speaks a
1265    /// protocol we have never seen, so it is refused outright.
1266    #[test]
1267    fn unknown_headers_are_rejected() {
1268        // A two byte header with id 63, which nothing is ever assigned.
1269        let buf = [
1270            0x01,
1271            VERSION as u8,
1272            0x02,
1273            7,
1274            0x7f,
1275            0xff,
1276            0xff,
1277            0x04,
1278            5,
1279            0x00,
1280        ];
1281
1282        let mut at = 0;
1283        let error = decode_envelope::<RequestHeader>(Mode::Binary, &buf, &mut at).unwrap_err();
1284
1285        assert!(error.is_unknown_header(), "Unexpected error: {error}");
1286        assert!(
1287            error.to_string().contains("63"),
1288            "Unexpected error: {error}"
1289        );
1290        assert_eq!(at, 0, "a refused envelope must not advance the cursor");
1291
1292        let buf = b"version: 2\nserial: 7\nfuture: whatever\nid: 11\n\n";
1293
1294        let mut at = 0;
1295        let error = decode_envelope::<RequestHeader>(Mode::Text, buf, &mut at).unwrap_err();
1296
1297        assert!(error.is_unknown_header(), "Unexpected error: {error}");
1298
1299        assert!(
1300            error.to_string().contains("future"),
1301            "The error has to name the header: {error}"
1302        );
1303
1304        assert_eq!(at, 0, "a refused envelope must not advance the cursor");
1305    }
1306
1307    /// A malformed binary envelope has to be reported rather than read as
1308    /// something else.
1309    #[test]
1310    fn binary_envelope_rejects_malformed_input() {
1311        // No end of headers marker.
1312        let mut at = 0;
1313        assert!(
1314            decode_envelope::<RequestHeader>(Mode::Binary, &[0x01, VERSION as u8], &mut at)
1315                .is_err()
1316        );
1317
1318        // A header whose value is cut short.
1319        let mut at = 0;
1320        assert!(decode_envelope::<RequestHeader>(Mode::Binary, &[0x43, 1], &mut at).is_err());
1321
1322        // A width class which is reserved, so the header cannot be skipped.
1323        let mut at = 0;
1324        assert!(decode_envelope::<RequestHeader>(Mode::Binary, &[0xc2, 1, 0x00], &mut at).is_err());
1325
1326        // A value which does not fit the header it is written to: `id` holds a
1327        // `u16`, and this writes 65536 into it.
1328        let mut at = 0;
1329        assert!(
1330            decode_envelope::<RequestHeader>(
1331                Mode::Binary,
1332                &[0x01, VERSION as u8, 0x83, 0, 0, 1, 0, 0x00],
1333                &mut at
1334            )
1335            .is_err()
1336        );
1337    }
1338
1339    /// A malformed text envelope has to be reported rather than silently read
1340    /// as something else.
1341    #[test]
1342    fn text_envelope_rejects_malformed_input() {
1343        // No empty line, so the envelope never ends.
1344        let mut at = 0;
1345        assert!(decode_envelope::<RequestHeader>(Mode::Text, b"version: 2\n", &mut at).is_err());
1346
1347        // A value which does not fit the header it is written to.
1348        let mut at = 0;
1349        assert!(
1350            decode_envelope::<RequestHeader>(Mode::Text, b"version: 2\nid: 65536\n\n", &mut at)
1351                .is_err()
1352        );
1353
1354        // A line which is not a field.
1355        let mut at = 0;
1356        assert!(decode_envelope::<RequestHeader>(Mode::Text, b"id 11\n\n", &mut at).is_err());
1357
1358        // A field whose value is not a number.
1359        let mut at = 0;
1360        assert!(
1361            decode_envelope::<RequestHeader>(Mode::Text, b"version: 2\nid: none\n\n", &mut at)
1362                .is_err()
1363        );
1364
1365        // Not text at all.
1366        let mut at = 0;
1367        assert!(decode_envelope::<RequestHeader>(Mode::Text, &[0xff, 0xfe], &mut at).is_err());
1368    }
1369
1370    /// A peer speaking a version this build does not have is refused, which is
1371    /// what keeps a session between mismatched peers from getting started.
1372    #[test]
1373    fn an_unsupported_version_is_refused() {
1374        // A well formed envelope which states a version from the future.
1375        let buf = [0x01, VERSION as u8 + 1, 0x02, 7, 0x00];
1376
1377        let mut at = 0;
1378        let error = decode_envelope::<RequestHeader>(Mode::Binary, &buf, &mut at).unwrap_err();
1379
1380        assert_eq!(error.unsupported_version(), Some(VERSION + 1));
1381        assert_eq!(at, 0, "a refused envelope must not advance the cursor");
1382
1383        let buf = alloc::format!("version: {}\nserial: 7\n\n", VERSION + 1);
1384
1385        let mut at = 0;
1386        let error =
1387            decode_envelope::<RequestHeader>(Mode::Text, buf.as_bytes(), &mut at).unwrap_err();
1388
1389        assert_eq!(error.unsupported_version(), Some(VERSION + 1));
1390        assert_eq!(at, 0, "a refused envelope must not advance the cursor");
1391    }
1392
1393    /// An envelope which states no version at all is refused too, since a peer
1394    /// which does not say what it speaks has not been understood either.
1395    #[test]
1396    fn an_unstated_version_is_refused() {
1397        for (mode, buf) in [
1398            (Mode::Binary, &[0x02, 7, 0x00][..]),
1399            (Mode::Text, &b"serial: 7\n\n"[..]),
1400        ] {
1401            let mut at = 0;
1402            let error = decode_envelope::<RequestHeader>(mode, buf, &mut at).unwrap_err();
1403
1404            assert_eq!(
1405                error.unsupported_version(),
1406                Some(0),
1407                "`{mode}` accepted an envelope which states no version"
1408            );
1409        }
1410    }
1411
1412    /// The version is what explains which headers may appear, so a version
1413    /// which does not match is reported ahead of a header we do not know.
1414    #[test]
1415    fn the_version_is_reported_before_an_unknown_header() {
1416        // Both wrong at once: a version from the future, and a header from it.
1417        let buf = [0x01, VERSION as u8 + 1, 0x7f, 0xff, 0xff, 0x00];
1418
1419        let mut at = 0;
1420        let error = decode_envelope::<RequestHeader>(Mode::Binary, &buf, &mut at).unwrap_err();
1421
1422        assert_eq!(error.unsupported_version(), Some(VERSION + 1));
1423
1424        assert!(
1425            !error.is_unknown_header(),
1426            "The version explains the header, so it is the more useful error"
1427        );
1428    }
1429
1430    /// Only a human readable format can be carried in a text frame, since the
1431    /// whole frame has to be valid UTF-8.
1432    #[test]
1433    fn text_mode_only_accepts_human_readable_formats() {
1434        for format in Format::ALL.iter().copied() {
1435            assert!(
1436                Mode::Binary.accepts(format),
1437                "`{format}` must fit a binary frame"
1438            );
1439
1440            assert_eq!(
1441                Mode::Text.accepts(format),
1442                format.is_human_readable(),
1443                "`{format}` is accepted by `text` exactly when it is human readable"
1444            );
1445        }
1446    }
1447
1448    /// A format the crate was not built with must fail cleanly rather than
1449    /// misbehaving.
1450    #[test]
1451    fn unsupported_is_reported() {
1452        for format in Format::ALL.iter().copied() {
1453            if format.is_supported() {
1454                continue;
1455            }
1456
1457            let mut buf = Vec::new();
1458            let error = format.encode(&mut buf, &1u32).unwrap_err();
1459            assert_eq!(error.unsupported_format(), Some(format));
1460        }
1461    }
1462}