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