Coverage Report

Created: 2026-02-26 06:02

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