1use std::fmt;
2
3use base64::prelude::*;
4
5#[derive(Debug, Eq, PartialEq, Clone, Copy)]
9pub enum OpCode {
10 Continue,
12
13 Text,
15
16 Binary,
18
19 Close,
21
22 Ping,
24
25 Pong,
27
28 Bad,
30}
31
32impl fmt::Display for OpCode {
33 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34 use OpCode::*;
35
36 f.write_str(match self {
37 Continue => "CONTINUE",
38 Text => "TEXT",
39 Binary => "BINARY",
40 Close => "CLOSE",
41 Ping => "PING",
42 Pong => "PONG",
43 Bad => "BAD",
44 })
45 }
46}
47
48impl From<OpCode> for u8 {
49 fn from(op: OpCode) -> u8 {
50 use self::OpCode::*;
51
52 match op {
53 Continue => 0,
54 Text => 1,
55 Binary => 2,
56 Close => 8,
57 Ping => 9,
58 Pong => 10,
59 Bad => {
60 tracing::error!("Attempted to convert invalid opcode to u8. This is a bug.");
61 8 }
63 }
64 }
65}
66
67impl From<u8> for OpCode {
68 fn from(byte: u8) -> OpCode {
69 use self::OpCode::*;
70
71 match byte {
72 0 => Continue,
73 1 => Text,
74 2 => Binary,
75 8 => Close,
76 9 => Ping,
77 10 => Pong,
78 _ => Bad,
79 }
80 }
81}
82
83#[derive(Debug, Eq, PartialEq, Clone, Copy)]
85pub enum CloseCode {
86 Normal,
89
90 Away,
93
94 Protocol,
96
97 Unsupported,
101
102 Abnormal,
107
108 Invalid,
112
113 Policy,
118
119 Size,
122
123 Extension,
129
130 Error,
133
134 Restart,
137
138 Again,
142
143 #[doc(hidden)]
144 Tls,
145
146 #[doc(hidden)]
147 Other(u16),
148}
149
150impl From<CloseCode> for u16 {
151 fn from(code: CloseCode) -> u16 {
152 use self::CloseCode::*;
153
154 match code {
155 Normal => 1000,
156 Away => 1001,
157 Protocol => 1002,
158 Unsupported => 1003,
159 Abnormal => 1006,
160 Invalid => 1007,
161 Policy => 1008,
162 Size => 1009,
163 Extension => 1010,
164 Error => 1011,
165 Restart => 1012,
166 Again => 1013,
167 Tls => 1015,
168 Other(code) => code,
169 }
170 }
171}
172
173impl From<u16> for CloseCode {
174 fn from(code: u16) -> CloseCode {
175 use self::CloseCode::*;
176
177 match code {
178 1000 => Normal,
179 1001 => Away,
180 1002 => Protocol,
181 1003 => Unsupported,
182 1006 => Abnormal,
183 1007 => Invalid,
184 1008 => Policy,
185 1009 => Size,
186 1010 => Extension,
187 1011 => Error,
188 1012 => Restart,
189 1013 => Again,
190 1015 => Tls,
191 _ => Other(code),
192 }
193 }
194}
195
196#[derive(Debug, Eq, PartialEq, Clone)]
197pub struct CloseReason {
199 pub code: CloseCode,
201
202 pub description: Option<String>,
204}
205
206impl From<CloseCode> for CloseReason {
207 fn from(code: CloseCode) -> Self {
208 CloseReason {
209 code,
210 description: None,
211 }
212 }
213}
214
215impl<T: Into<String>> From<(CloseCode, T)> for CloseReason {
216 fn from(info: (CloseCode, T)) -> Self {
217 CloseReason {
218 code: info.0,
219 description: Some(info.1.into()),
220 }
221 }
222}
223
224static WS_GUID: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
227
228const WS_ACCEPT_LEN: usize = 28;
230
231pub fn hash_key(key: &[u8]) -> [u8; WS_ACCEPT_LEN] {
235 let hash = {
236 use sha1::Digest as _;
237
238 let mut hasher = sha1::Sha1::new();
239
240 hasher.update(key);
241 hasher.update(WS_GUID);
242
243 hasher.finalize()
244 };
245
246 let mut hash_b64 = [0; WS_ACCEPT_LEN];
247 let n = BASE64_STANDARD.encode_slice(hash, &mut hash_b64).unwrap();
248 assert_eq!(n, WS_ACCEPT_LEN);
249
250 hash_b64
251}
252
253#[cfg(test)]
254mod test {
255 #![allow(unused_imports, unused_variables, dead_code)]
256 use super::*;
257
258 macro_rules! opcode_into {
259 ($from:expr => $opcode:pat) => {
260 match OpCode::from($from) {
261 e @ $opcode => {}
262 e => unreachable!("{:?}", e),
263 }
264 };
265 }
266
267 macro_rules! opcode_from {
268 ($from:expr => $opcode:pat) => {
269 let res: u8 = $from.into();
270 match res {
271 e @ $opcode => {}
272 e => unreachable!("{:?}", e),
273 }
274 };
275 }
276
277 #[test]
278 fn test_to_opcode() {
279 opcode_into!(0 => OpCode::Continue);
280 opcode_into!(1 => OpCode::Text);
281 opcode_into!(2 => OpCode::Binary);
282 opcode_into!(8 => OpCode::Close);
283 opcode_into!(9 => OpCode::Ping);
284 opcode_into!(10 => OpCode::Pong);
285 opcode_into!(99 => OpCode::Bad);
286 }
287
288 #[test]
289 fn test_from_opcode() {
290 opcode_from!(OpCode::Continue => 0);
291 opcode_from!(OpCode::Text => 1);
292 opcode_from!(OpCode::Binary => 2);
293 opcode_from!(OpCode::Close => 8);
294 opcode_from!(OpCode::Ping => 9);
295 opcode_from!(OpCode::Pong => 10);
296 }
297
298 #[test]
299 #[should_panic]
300 fn test_from_opcode_debug() {
301 opcode_from!(OpCode::Bad => 99);
302 }
303
304 #[test]
305 fn test_from_opcode_display() {
306 assert_eq!(format!("{}", OpCode::Continue), "CONTINUE");
307 assert_eq!(format!("{}", OpCode::Text), "TEXT");
308 assert_eq!(format!("{}", OpCode::Binary), "BINARY");
309 assert_eq!(format!("{}", OpCode::Close), "CLOSE");
310 assert_eq!(format!("{}", OpCode::Ping), "PING");
311 assert_eq!(format!("{}", OpCode::Pong), "PONG");
312 assert_eq!(format!("{}", OpCode::Bad), "BAD");
313 }
314
315 #[test]
316 fn test_hash_key() {
317 let hash = hash_key(b"hello actix-web");
318 assert_eq!(&hash, b"cR1dlyUUJKp0s/Bel25u5TgvC3E=");
319 }
320
321 #[test]
322 fn close_code_from_u16() {
323 assert_eq!(CloseCode::from(1000u16), CloseCode::Normal);
324 assert_eq!(CloseCode::from(1001u16), CloseCode::Away);
325 assert_eq!(CloseCode::from(1002u16), CloseCode::Protocol);
326 assert_eq!(CloseCode::from(1003u16), CloseCode::Unsupported);
327 assert_eq!(CloseCode::from(1006u16), CloseCode::Abnormal);
328 assert_eq!(CloseCode::from(1007u16), CloseCode::Invalid);
329 assert_eq!(CloseCode::from(1008u16), CloseCode::Policy);
330 assert_eq!(CloseCode::from(1009u16), CloseCode::Size);
331 assert_eq!(CloseCode::from(1010u16), CloseCode::Extension);
332 assert_eq!(CloseCode::from(1011u16), CloseCode::Error);
333 assert_eq!(CloseCode::from(1012u16), CloseCode::Restart);
334 assert_eq!(CloseCode::from(1013u16), CloseCode::Again);
335 assert_eq!(CloseCode::from(1015u16), CloseCode::Tls);
336 assert_eq!(CloseCode::from(2000u16), CloseCode::Other(2000));
337 }
338
339 #[test]
340 fn close_code_into_u16() {
341 assert_eq!(1000u16, Into::<u16>::into(CloseCode::Normal));
342 assert_eq!(1001u16, Into::<u16>::into(CloseCode::Away));
343 assert_eq!(1002u16, Into::<u16>::into(CloseCode::Protocol));
344 assert_eq!(1003u16, Into::<u16>::into(CloseCode::Unsupported));
345 assert_eq!(1006u16, Into::<u16>::into(CloseCode::Abnormal));
346 assert_eq!(1007u16, Into::<u16>::into(CloseCode::Invalid));
347 assert_eq!(1008u16, Into::<u16>::into(CloseCode::Policy));
348 assert_eq!(1009u16, Into::<u16>::into(CloseCode::Size));
349 assert_eq!(1010u16, Into::<u16>::into(CloseCode::Extension));
350 assert_eq!(1011u16, Into::<u16>::into(CloseCode::Error));
351 assert_eq!(1012u16, Into::<u16>::into(CloseCode::Restart));
352 assert_eq!(1013u16, Into::<u16>::into(CloseCode::Again));
353 assert_eq!(1015u16, Into::<u16>::into(CloseCode::Tls));
354 assert_eq!(2000u16, Into::<u16>::into(CloseCode::Other(2000)));
355 }
356}