1use 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
22const END_OF_HEADERS: u8 = 0;
26
27const ID_MASK: u8 = 0b0011_1111;
29
30const WIDTH_SHIFT: u32 = 6;
32
33#[derive(Clone, Copy)]
38pub(crate) struct Header {
39 pub(crate) id: u8,
42 pub(crate) name: &'static str,
44}
45
46pub(crate) trait HeaderValue
53where
54 Self: Copy,
55{
56 fn to_u32(self) -> u32;
58
59 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#[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 const HEADERS: &'static [Header];
123
124 fn empty() -> Self;
127
128 fn get(&self, id: u8) -> u32;
130
131 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 _ => return Err(Error::new(ErrorKind::UnknownHeader { id, name: None })),
179 }
180
181 Ok(())
182 }
183 }
184 };
185}
186
187envelope! {
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
213const VERSION_ID: u8 = 1;
215
216#[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#[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
234struct 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#[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 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#[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 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 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 out.push(b'\n');
320 }
321 }
322
323 Ok(())
324}
325
326#[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 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 _ => 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 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 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 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
473macro_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
482macro_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 #[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 #[inline]
532 pub fn supported() -> impl Iterator<Item = Format> {
533 Format::ALL.iter().copied().filter(|f| f.is_supported())
534 }
535
536 #[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 #[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 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#[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 #[inline]
627 pub(crate) const fn unsupported(format: Format) -> Self {
628 Self::new(ErrorKind::Unsupported(format))
629 }
630
631 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 #[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 #[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 #[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 #[test]
1198 fn text_envelope_is_order_independent() {
1199 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 #[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 0x01,
1235 VERSION as u8, 0x02,
1238 7, 0x43,
1241 0x2c,
1242 0x01, 0x04,
1245 5, 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 #[test]
1267 fn unknown_headers_are_rejected() {
1268 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 #[test]
1310 fn binary_envelope_rejects_malformed_input() {
1311 let mut at = 0;
1313 assert!(
1314 decode_envelope::<RequestHeader>(Mode::Binary, &[0x01, VERSION as u8], &mut at)
1315 .is_err()
1316 );
1317
1318 let mut at = 0;
1320 assert!(decode_envelope::<RequestHeader>(Mode::Binary, &[0x43, 1], &mut at).is_err());
1321
1322 let mut at = 0;
1324 assert!(decode_envelope::<RequestHeader>(Mode::Binary, &[0xc2, 1, 0x00], &mut at).is_err());
1325
1326 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 #[test]
1342 fn text_envelope_rejects_malformed_input() {
1343 let mut at = 0;
1345 assert!(decode_envelope::<RequestHeader>(Mode::Text, b"version: 2\n", &mut at).is_err());
1346
1347 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 let mut at = 0;
1356 assert!(decode_envelope::<RequestHeader>(Mode::Text, b"id 11\n\n", &mut at).is_err());
1357
1358 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 let mut at = 0;
1367 assert!(decode_envelope::<RequestHeader>(Mode::Text, &[0xff, 0xfe], &mut at).is_err());
1368 }
1369
1370 #[test]
1373 fn an_unsupported_version_is_refused() {
1374 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 #[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 #[test]
1415 fn the_version_is_reported_before_an_unknown_header() {
1416 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 #[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 #[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}