blitz_ws/protocol/frame/
codec.rs

1//! Codes defined in RFC 6455
2
3use std::fmt::Display;
4
5/// WebSocket message opcode as in RFC 6455.
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7pub enum OpCode {
8    /// Data (text or binary).
9    Data(Data),
10    /// Control (close, ping, pong).
11    Control(Control),
12}
13
14/// Data opcodes as in RFC 6455
15#[repr(u8)]
16#[derive(Debug, PartialEq, Eq, Clone, Copy)]
17pub enum Data {
18    /// A continuation frame
19    Continuation = 0x0,
20    /// A text frame
21    Text = 0x1,
22    /// A binary frame
23    Binary = 0x2,
24    /// 0xb-f are reserved for further control frames
25    Reserved(u8),
26}
27
28/// Control opcodes as in RFC 6455
29#[repr(u8)]
30#[derive(Debug, PartialEq, Eq, Clone, Copy)]
31pub enum Control {
32    /// A close frame
33    Close = 0x8,
34    /// A ping frame
35    Ping = 0x9,
36    /// A pong frame
37    Pong = 0xA,
38    /// 0xb-f are reserved for further control frames
39    Reserved(u8),
40}
41
42impl Display for Data {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        match *self {
45            Self::Continuation => write!(f, "CONTINUE"),
46            Self::Text => write!(f, "TEXT"),
47            Self::Binary => write!(f, "BINARY"),
48            Self::Reserved(other) => write!(f, "RESERVED_DATA_{other}'"),
49        }
50    }
51}
52
53impl Display for Control {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        match *self {
56            Self::Close => write!(f, "CLOSE"),
57            Self::Ping => write!(f, "PING"),
58            Self::Pong => write!(f, "PONG"),
59            Self::Reserved(other) => write!(f, "RESERVED_CONTROL_{other}"),
60        }
61    }
62}
63
64impl Display for OpCode {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        match *self {
67            Self::Data(d) => d.fmt(f),
68            Self::Control(c) => c.fmt(f),
69        }
70    }
71}
72
73impl From<OpCode> for u8 {
74    fn from(value: OpCode) -> Self {
75        match value {
76            self::OpCode::Data(self::Data::Continuation) => 0x0,
77            self::OpCode::Data(self::Data::Text) => 0x1,
78            self::OpCode::Data(self::Data::Binary) => 0x2,
79            self::OpCode::Data(self::Data::Reserved(i)) => i,
80
81            self::OpCode::Control(self::Control::Close) => 0x8,
82            self::OpCode::Control(self::Control::Ping) => 0x9,
83            self::OpCode::Control(self::Control::Pong) => 0xA,
84            self::OpCode::Control(self::Control::Reserved(i)) => i,
85        }
86    }
87}
88
89impl From<u8> for OpCode {
90    fn from(value: u8) -> Self {
91        match value {
92            0x0 => Self::Data(Data::Continuation),
93            0x1 => Self::Data(Data::Text),
94            0x2 => Self::Data(Data::Binary),
95            i @ 0x3..=0x7 => Self::Data(Data::Reserved(i)),
96            0x8 => Self::Control(Control::Close),
97            0x9 => Self::Control(Control::Ping),
98            0xA => Self::Control(Control::Pong),
99            i @ 0xB..=0xF => Self::Control(Control::Reserved(i)),
100            _ => panic!("Bug: OpCode out of range"),
101        }
102    }
103}
104
105/// Status code used to indicate why an endpoint is closing the WebSocket connection.
106#[repr(u16)]
107#[non_exhaustive]
108#[derive(Debug, PartialEq, Eq, Clone, Copy)]
109pub enum CloseCode {
110    /// Indicates a normal closure, meaning that the purpose for
111    /// which the connection was established has been fulfilled.
112    Normal = 0x3e8,
113
114    /// Indicates that an endpoint is "going away", such as a server
115    /// going down or a browser having navigated away from a page.
116    Away = 0x3e9,
117
118    /// Indicates that an endpoint is terminating the connection due
119    /// to a protocol error.
120    Protocol = 0x3EA,
121
122    /// Indicates that an endpoint is terminating the connection
123    /// because it has received a type of data it cannot accept (e.g., an
124    /// endpoint that understands only text data MAY send this if it
125    /// receives a binary message).
126    Unsupported = 0x3EB,
127
128    /// Indicates that no status code was included in a closing frame. This
129    /// close code makes it possible to use a single method, `on_close` to
130    /// handle even cases where no close code was provided.
131    Status = 0x3ED,
132
133    /// Indicates an abnormal closure. If the abnormal closure was due to an
134    /// error, this close code will not be used. Instead, the `on_error` method
135    /// of the handler will be called with the error. However, if the connection
136    /// is simply dropped, without an error, this close code will be sent to the
137    /// handler.
138    Abnormal = 0x3EE,
139
140    /// Indicates that an endpoint is terminating the connection
141    /// because it has received data within a message that was not
142    /// consistent with the type of the message (e.g., non-UTF-8 \[RFC3629\]
143    /// data within a text message).
144    Invalid = 0x3EF,
145
146    /// Indicates that an endpoint is terminating the connection
147    /// because it has received a message that violates its policy.  This
148    /// is a generic status code that can be returned when there is no
149    /// other more suitable status code (e.g., Unsupported or Size) or if there
150    /// is a need to hide specific details about the policy.
151    Policy = 0x3F0,
152
153    /// Indicates that an endpoint is terminating the connection
154    /// because it has received a message that is too big for it to
155    /// process.
156    Size = 0x3F1,
157
158    /// Indicates that an endpoint (client) is terminating the
159    /// connection because it has expected the server to negotiate one or
160    /// more extension, but the server didn't return them in the response
161    /// message of the WebSocket handshake.  The list of extensions that
162    /// are needed should be given as the reason for closing.
163    /// Note that this status code is not used by the server, because it
164    /// can fail the WebSocket handshake instead.
165    Extension = 0x3F2,
166
167    /// Indicates that a server is terminating the connection because
168    /// it encountered an unexpected condition that prevented it from
169    /// fulfilling the request.
170    Error = 0x3F3,
171
172    /// Indicates that the server is restarting. A client may choose to reconnect,
173    /// and if it does, it should use a randomized delay of 5-30 seconds between attempts.
174    Restart = 0x3F4,
175
176    /// Indicates that the server is overloaded and the client should either connect
177    /// to a different IP (when multiple targets exist), or reconnect to the same IP
178    /// when a user has performed an action.
179    Again = 0x3F5,
180
181    #[doc(hidden)]
182    Tls = 0x3F7,
183
184    #[doc(hidden)]
185    Reserved(u16),
186
187    #[doc(hidden)]
188    Iana(u16),
189
190    #[doc(hidden)]
191    Library(u16),
192
193    #[doc(hidden)]
194    Bad(u16),
195}
196
197impl CloseCode {
198    /// Check if this CloseCode is allowed.
199    pub fn allowed(self) -> bool {
200        !matches!(
201            self,
202            Self::Bad(_) | Self::Reserved(_) | Self::Status | Self::Abnormal | Self::Tls
203        )
204    }
205}
206
207impl Display for CloseCode {
208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209        let code: u16 = self.into();
210        write!(f, "{code}")
211    }
212}
213
214impl From<CloseCode> for u16 {
215    fn from(value: CloseCode) -> u16 {
216        match value {
217            self::CloseCode::Normal => 0x3e8,
218            self::CloseCode::Away => 0x3e9,
219            self::CloseCode::Protocol => 0x3EA,
220            self::CloseCode::Unsupported => 0x3EB,
221            self::CloseCode::Status => 0x3ED,
222            self::CloseCode::Abnormal => 0x3EE,
223            self::CloseCode::Invalid => 0x3EF,
224            self::CloseCode::Policy => 0x3F0,
225            self::CloseCode::Size => 0x3F1,
226            self::CloseCode::Extension => 0x3F2,
227            self::CloseCode::Error => 0x3F3,
228            self::CloseCode::Restart => 0x3F4,
229            self::CloseCode::Again => 0x3F5,
230            self::CloseCode::Tls => 0x3F7,
231            self::CloseCode::Bad(other) => other,
232            self::CloseCode::Reserved(other) => other,
233            self::CloseCode::Iana(other) => other,
234            self::CloseCode::Library(other) => other,
235        }
236    }
237}
238
239impl<'t> From<&'t CloseCode> for u16 {
240    fn from(value: &'t CloseCode) -> Self {
241        (*value).into()
242    }
243}
244
245impl From<u16> for CloseCode {
246    fn from(value: u16) -> Self {
247        match value {
248            0x3e8 => Self::Normal,
249            0x3e9 => Self::Away,
250            0x3EA => Self::Protocol,
251            0x3EB => Self::Unsupported,
252            0x3ED => Self::Status,
253            0x3EE => Self::Abnormal,
254            0x3EF => Self::Invalid,
255            0x3F0 => Self::Policy,
256            0x3F1 => Self::Size,
257            0x3F2 => Self::Extension,
258            0x3F3 => Self::Error,
259            0x3F4 => Self::Restart,
260            0x3F5 => Self::Again,
261            0x3F7 => Self::Tls,
262            0x1..=0x3E7 => Self::Bad(value),
263            0x3F8..=0xBB7 => Self::Reserved(value),
264            0xBB8..=0xF9F => Self::Iana(value),
265            0xFA0..=0x1387 => Self::Library(value),
266            _ => Self::Bad(value),
267        }
268    }
269}