websocket_sans_io/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#![cfg_attr(not(feature="explicitly_aligned_masking"),forbid(unsafe_code))]
#![warn(missing_docs)]

#![doc=include_str!("../README.md")]
//! 
//! See [`WebsocketFrameEncoder`] and [`WebsocketFrameDecoder`] for continuation of the documentation.

#![no_std]

mod masking;

/// Apply WebSocket masking to the giben block of data.
/// 
/// `phase` is a number from 0 to 3, meaning zeroeth byte a `payload_chunk` should be 
/// masked with `phase`'s byte of `mask`.
/// 
/// Crate features `unoptimised_maskin`, `explicitly_aligned_masking` and `masking_slice_size_{4,8,16,32}` affect implementation of this function.
pub use masking::apply_mask;

/// Type alias for payload length. u64 by default, u16 when `large_frames` crate feature is off.
#[cfg(feature="large_frames")]
pub type PayloadLength = u64;

/// Type alias for payload length. u64 by default, u16 when `large_frames` crate feature is off.
#[cfg(not(feature="large_frames"))]
pub type PayloadLength = u16;

mod frame_encoding;
pub use frame_encoding::WebsocketFrameEncoder;
mod frame_decoding;
pub use frame_decoding::{FrameDecoderError,WebsocketFrameDecoder, WebsocketFrameEvent,WebsocketFrameDecoderAddDataResult};

/// WebSocket frame type.
/// 
/// Also includes reserved opcode types as `#[doc(hidden)]` items.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub enum Opcode {
    /// A continuation frame. Follows [`Opcode::Text`] or [`Opcode::Binary`] frames, extending content of it.
    /// Final `Continuation` frames has [`FrameInfo::fin`] set to true.
    /// 
    /// There may be control frames (e.g. [`Opcode::Ping`]) between initial data frame and continuation frames.
    Continuation = 0,
    /// First frame of a text WebSocket message.
    Text = 1,
    /// First frame of a binary WebSocket message.
    Binary = 2,
    #[doc(hidden)]
    ReservedData3 = 3,
    #[doc(hidden)]
    ReservedData4 = 4,
    #[doc(hidden)]
    ReservedData5 = 5,
    #[doc(hidden)]
    ReservedData6 = 6,
    #[doc(hidden)]
    ReservedData7 = 7,
    /// Last frame, indicating that WebSocket connection is now closed.
    /// You should close the socket upon receipt of this message.
    ConnectionClose = 8,
    /// WebSocket ping message. You should copy the payload to outgoing
    /// [`Opcode::Pong`] frame.
    Ping = 9,
    /// A reply to [`Opcode::Pong`] message.
    Pong = 0xA,
    #[doc(hidden)]
    ReservedControlB = 0xB,
    #[doc(hidden)]
    ReservedControlC = 0xC,
    #[doc(hidden)]
    ReservedControlD = 0xD,
    #[doc(hidden)]
    ReservedControlE = 0xE,
    #[doc(hidden)]
    #[default]
    ReservedControlF = 0xF,
}

impl Opcode {
    /// Check if this opcode is of a data frame.
    pub fn is_data(&self) -> bool {
        match self {
            Opcode::Continuation => true,
            Opcode::Text  => true,
            Opcode::Binary  => true,
            Opcode::ReservedData3 => true,
            Opcode::ReservedData4 => true,
            Opcode::ReservedData5 => true,
            Opcode::ReservedData6 => true,
            Opcode::ReservedData7 => true,
           _ => false,
        }
    }
    /// Check if this opcode is of a control frame.
    pub fn is_control(&self) -> bool {
        ! self.is_data()
    }
}

/// Information about WebSocket frame header.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct FrameInfo {
    /// Type of this WebSocket frame
    pub opcode: Opcode,
    /// Length of this frame's payload. Not to be confused with WebSocket 
    /// **message** length, which is unknown, unless [`FrameInfo::fin` ] is set.
    pub payload_length: PayloadLength,
    /// Whether this frame uses (or should use, for encoder) masking
    /// (and the mask used)
    pub mask: Option<[u8; 4]>,
    /// Whether this frame is a final frame and no [`Opcode::Continuation`] should follow
    /// to extend the content of it.
    pub fin: bool,
    /// Should be `0` unless some extensions are used.
    pub reserved: u8,
}

impl FrameInfo {
    /// Indicates that this frame looks like a normal, well-formed
    /// frame (not considering WebSocket extensions).
    /// 
    /// Does not check for valitity of a frame within a sequence of frames,
    /// e.g. for orphaned [`Opcode::Continuation`] frames or
    /// for unfinished prior messages.
    pub const fn is_reasonable(&self) -> bool {
        if self.reserved != 0 { return false; }
        match self.opcode {
            Opcode::Continuation => true,
            Opcode::Text => true,
            Opcode::Binary => true,
            Opcode::ConnectionClose => self.fin,
            Opcode::Ping => self.fin,
            Opcode::Pong => self.fin,
            _ => false,
        }
    }
}

/// Maximum number of bytes in a WebSocket frame header. Less if `large_frames` crate feature is off.
#[cfg(feature = "large_frames")]
pub const MAX_HEADER_LENGTH: usize = 2 + 8 + 4;

/// Maximum number of bytes in a WebSocket frame header. Less if `large_frames` crate feature is off.
#[cfg(not(feature = "large_frames"))]
pub const MAX_HEADER_LENGTH: usize = 2 + 2 + 4;

#[cfg(test)]
mod decoding_test;

#[cfg(test)]
mod frame_roundtrip_test;