websocket_sans_io/lib.rs
1#![cfg_attr(not(feature="explicitly_aligned_masking"),forbid(unsafe_code))]
2#![warn(missing_docs)]
3
4#![doc=include_str!("../README.md")]
5//!
6//! See [`WebsocketFrameEncoder`] and [`WebsocketFrameDecoder`] for continuation of the documentation.
7
8#![no_std]
9
10mod masking;
11
12/// Apply WebSocket masking to the giben block of data.
13///
14/// `phase` is a number from 0 to 3, meaning zeroeth byte a `payload_chunk` should be
15/// masked with `phase`'s byte of `mask`.
16///
17/// Crate features `unoptimised_maskin`, `explicitly_aligned_masking` and `masking_slice_size_{4,8,16,32}` affect implementation of this function.
18pub use masking::apply_mask;
19
20/// Type alias for payload length. u64 by default, u16 when `large_frames` crate feature is off.
21#[cfg(feature="large_frames")]
22pub type PayloadLength = u64;
23
24/// Type alias for payload length. u64 by default, u16 when `large_frames` crate feature is off.
25#[cfg(not(feature="large_frames"))]
26pub type PayloadLength = u16;
27
28mod frame_encoding;
29pub use frame_encoding::WebsocketFrameEncoder;
30mod frame_decoding;
31pub use frame_decoding::{FrameDecoderError,WebsocketFrameDecoder, WebsocketFrameEvent,WebsocketFrameDecoderAddDataResult};
32
33/// WebSocket frame type.
34///
35/// Also includes reserved opcode types as `#[doc(hidden)]` items.
36#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
37pub enum Opcode {
38 /// A continuation frame. Follows [`Opcode::Text`] or [`Opcode::Binary`] frames, extending content of it.
39 /// Final `Continuation` frames has [`FrameInfo::fin`] set to true.
40 ///
41 /// There may be control frames (e.g. [`Opcode::Ping`]) between initial data frame and continuation frames.
42 Continuation = 0,
43 /// First frame of a text WebSocket message.
44 Text = 1,
45 /// First frame of a binary WebSocket message.
46 Binary = 2,
47 #[doc(hidden)]
48 ReservedData3 = 3,
49 #[doc(hidden)]
50 ReservedData4 = 4,
51 #[doc(hidden)]
52 ReservedData5 = 5,
53 #[doc(hidden)]
54 ReservedData6 = 6,
55 #[doc(hidden)]
56 ReservedData7 = 7,
57 /// Last frame, indicating that WebSocket connection is now closed.
58 /// You should close the socket upon receipt of this message.
59 ConnectionClose = 8,
60 /// WebSocket ping message. You should copy the payload to outgoing
61 /// [`Opcode::Pong`] frame.
62 Ping = 9,
63 /// A reply to [`Opcode::Pong`] message.
64 Pong = 0xA,
65 #[doc(hidden)]
66 ReservedControlB = 0xB,
67 #[doc(hidden)]
68 ReservedControlC = 0xC,
69 #[doc(hidden)]
70 ReservedControlD = 0xD,
71 #[doc(hidden)]
72 ReservedControlE = 0xE,
73 #[doc(hidden)]
74 #[default]
75 ReservedControlF = 0xF,
76}
77
78impl Opcode {
79 /// Check if this opcode is of a data frame.
80 pub fn is_data(&self) -> bool {
81 match self {
82 Opcode::Continuation => true,
83 Opcode::Text => true,
84 Opcode::Binary => true,
85 Opcode::ReservedData3 => true,
86 Opcode::ReservedData4 => true,
87 Opcode::ReservedData5 => true,
88 Opcode::ReservedData6 => true,
89 Opcode::ReservedData7 => true,
90 _ => false,
91 }
92 }
93 /// Check if this opcode is of a control frame.
94 pub fn is_control(&self) -> bool {
95 ! self.is_data()
96 }
97}
98
99/// Information about WebSocket frame header.
100#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
101pub struct FrameInfo {
102 /// Type of this WebSocket frame
103 pub opcode: Opcode,
104 /// Length of this frame's payload. Not to be confused with WebSocket
105 /// **message** length, which is unknown, unless [`FrameInfo::fin` ] is set.
106 pub payload_length: PayloadLength,
107 /// Whether this frame uses (or should use, for encoder) masking
108 /// (and the mask used)
109 pub mask: Option<[u8; 4]>,
110 /// Whether this frame is a final frame and no [`Opcode::Continuation`] should follow
111 /// to extend the content of it.
112 pub fin: bool,
113 /// Should be `0` unless some extensions are used.
114 pub reserved: u8,
115}
116
117impl FrameInfo {
118 /// Indicates that this frame looks like a normal, well-formed
119 /// frame (not considering WebSocket extensions).
120 ///
121 /// Does not check for valitity of a frame within a sequence of frames,
122 /// e.g. for orphaned [`Opcode::Continuation`] frames or
123 /// for unfinished prior messages.
124 pub const fn is_reasonable(&self) -> bool {
125 if self.reserved != 0 { return false; }
126 match self.opcode {
127 Opcode::Continuation => true,
128 Opcode::Text => true,
129 Opcode::Binary => true,
130 Opcode::ConnectionClose => self.fin,
131 Opcode::Ping => self.fin,
132 Opcode::Pong => self.fin,
133 _ => false,
134 }
135 }
136}
137
138/// Maximum number of bytes in a WebSocket frame header. Less if `large_frames` crate feature is off.
139#[cfg(feature = "large_frames")]
140pub const MAX_HEADER_LENGTH: usize = 2 + 8 + 4;
141
142/// Maximum number of bytes in a WebSocket frame header. Less if `large_frames` crate feature is off.
143#[cfg(not(feature = "large_frames"))]
144pub const MAX_HEADER_LENGTH: usize = 2 + 2 + 4;
145
146#[cfg(test)]
147mod decoding_test;
148
149#[cfg(test)]
150mod frame_roundtrip_test;
151