1use std::fmt;
2
3use base64::{Engine, engine::general_purpose::STANDARD as base64};
4
5#[derive(Debug, Eq, PartialEq, Clone, Copy)]
7pub enum OpCode {
8 Continue,
10 Text,
12 Binary,
14 Close,
16 Ping,
18 Pong,
20 Bad,
22}
23
24impl fmt::Display for OpCode {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match *self {
27 OpCode::Continue => write!(f, "CONTINUE"),
28 OpCode::Text => write!(f, "TEXT"),
29 OpCode::Binary => write!(f, "BINARY"),
30 OpCode::Close => write!(f, "CLOSE"),
31 OpCode::Ping => write!(f, "PING"),
32 OpCode::Pong => write!(f, "PONG"),
33 OpCode::Bad => write!(f, "BAD"),
34 }
35 }
36}
37
38impl From<OpCode> for u8 {
39 fn from(code: OpCode) -> u8 {
40 match code {
41 OpCode::Continue => 0,
42 OpCode::Text => 1,
43 OpCode::Binary => 2,
44 OpCode::Close => 8,
45 OpCode::Ping => 9,
46 OpCode::Pong => 10,
47 OpCode::Bad => {
48 log::error!("Attempted to convert invalid opcode to u8. This is a bug.");
49 8 }
51 }
52 }
53}
54
55impl From<u8> for OpCode {
56 fn from(byte: u8) -> OpCode {
57 match byte {
58 0 => OpCode::Continue,
59 1 => OpCode::Text,
60 2 => OpCode::Binary,
61 8 => OpCode::Close,
62 9 => OpCode::Ping,
63 10 => OpCode::Pong,
64 _ => OpCode::Bad,
65 }
66 }
67}
68
69#[derive(Debug, Eq, PartialEq, Clone, Copy)]
72pub enum CloseCode {
73 Normal,
76 Away,
79 Protocol,
82 Unsupported,
87 Abnormal,
93 Invalid,
98 Policy,
104 Size,
108 Extension,
116 Error,
120 Restart,
124 Again,
128 #[doc(hidden)]
129 Tls,
130 #[doc(hidden)]
131 Other(u16),
132}
133
134impl From<CloseCode> for u16 {
135 fn from(code: CloseCode) -> u16 {
136 match code {
137 CloseCode::Normal => 1000,
138 CloseCode::Away => 1001,
139 CloseCode::Protocol => 1002,
140 CloseCode::Unsupported => 1003,
141 CloseCode::Abnormal => 1006,
142 CloseCode::Invalid => 1007,
143 CloseCode::Policy => 1008,
144 CloseCode::Size => 1009,
145 CloseCode::Extension => 1010,
146 CloseCode::Error => 1011,
147 CloseCode::Restart => 1012,
148 CloseCode::Again => 1013,
149 CloseCode::Tls => 1015,
150 CloseCode::Other(code) => code,
151 }
152 }
153}
154
155impl From<u16> for CloseCode {
156 fn from(code: u16) -> CloseCode {
157 match code {
158 1000 => CloseCode::Normal,
159 1001 => CloseCode::Away,
160 1002 => CloseCode::Protocol,
161 1003 => CloseCode::Unsupported,
162 1006 => CloseCode::Abnormal,
163 1007 => CloseCode::Invalid,
164 1008 => CloseCode::Policy,
165 1009 => CloseCode::Size,
166 1010 => CloseCode::Extension,
167 1011 => CloseCode::Error,
168 1012 => CloseCode::Restart,
169 1013 => CloseCode::Again,
170 1015 => CloseCode::Tls,
171 _ => CloseCode::Other(code),
172 }
173 }
174}
175
176#[derive(Debug, Eq, PartialEq, Clone)]
177pub struct CloseReason {
179 pub code: CloseCode,
181 pub description: Option<String>,
183}
184
185impl From<CloseCode> for CloseReason {
186 fn from(code: CloseCode) -> Self {
187 CloseReason {
188 code,
189 description: None,
190 }
191 }
192}
193
194impl<T: Into<String>> From<(CloseCode, T)> for CloseReason {
195 fn from(info: (CloseCode, T)) -> Self {
196 CloseReason {
197 code: info.0,
198 description: Some(info.1.into()),
199 }
200 }
201}
202
203static WS_GUID: &str = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
204
205pub fn hash_key(key: &[u8]) -> String {
207 use sha1::Digest;
208 let mut hasher = sha1::Sha1::new();
209
210 hasher.update(key);
211 hasher.update(WS_GUID.as_bytes());
212
213 base64.encode(&hasher.finalize()[..])
214}
215
216#[cfg(test)]
217#[allow(unused_imports, unused_variables, dead_code)]
218mod test {
219 use super::*;
220
221 macro_rules! opcode_into {
222 ($from:expr => $opcode:pat) => {
223 match OpCode::from($from) {
224 e @ $opcode => (),
225 e => unreachable!("{:?}", e),
226 }
227 };
228 }
229
230 macro_rules! opcode_from {
231 ($from:expr => $opcode:pat) => {
232 let res: u8 = $from.into();
233 match res {
234 e @ $opcode => (),
235 e => unreachable!("{:?}", e),
236 }
237 };
238 }
239
240 #[test]
241 fn test_to_opcode() {
242 opcode_into!(0 => OpCode::Continue);
243 opcode_into!(1 => OpCode::Text);
244 opcode_into!(2 => OpCode::Binary);
245 opcode_into!(8 => OpCode::Close);
246 opcode_into!(9 => OpCode::Ping);
247 opcode_into!(10 => OpCode::Pong);
248 opcode_into!(99 => OpCode::Bad);
249 }
250
251 #[test]
252 fn test_from_opcode() {
253 opcode_from!(OpCode::Continue => 0);
254 opcode_from!(OpCode::Text => 1);
255 opcode_from!(OpCode::Binary => 2);
256 opcode_from!(OpCode::Close => 8);
257 opcode_from!(OpCode::Ping => 9);
258 opcode_from!(OpCode::Pong => 10);
259 }
260
261 #[test]
262 #[should_panic]
263 #[allow(clippy::should_panic_without_expect)]
264 fn test_from_opcode_debug() {
265 opcode_from!(OpCode::Bad => 99);
266 }
267
268 #[test]
269 fn test_from_opcode_display() {
270 assert_eq!(format!("{}", OpCode::Continue), "CONTINUE");
271 assert_eq!(format!("{}", OpCode::Text), "TEXT");
272 assert_eq!(format!("{}", OpCode::Binary), "BINARY");
273 assert_eq!(format!("{}", OpCode::Close), "CLOSE");
274 assert_eq!(format!("{}", OpCode::Ping), "PING");
275 assert_eq!(format!("{}", OpCode::Pong), "PONG");
276 assert_eq!(format!("{}", OpCode::Bad), "BAD");
277 }
278
279 #[test]
280 fn test_hash_key() {
281 let hash = hash_key(b"hello actix-web");
282 assert_eq!(&hash, "cR1dlyUUJKp0s/Bel25u5TgvC3E=");
283 }
284
285 #[test]
286 fn closecode_from_u16() {
287 assert_eq!(CloseCode::from(1000u16), CloseCode::Normal);
288 assert_eq!(CloseCode::from(1001u16), CloseCode::Away);
289 assert_eq!(CloseCode::from(1002u16), CloseCode::Protocol);
290 assert_eq!(CloseCode::from(1003u16), CloseCode::Unsupported);
291 assert_eq!(CloseCode::from(1006u16), CloseCode::Abnormal);
292 assert_eq!(CloseCode::from(1007u16), CloseCode::Invalid);
293 assert_eq!(CloseCode::from(1008u16), CloseCode::Policy);
294 assert_eq!(CloseCode::from(1009u16), CloseCode::Size);
295 assert_eq!(CloseCode::from(1010u16), CloseCode::Extension);
296 assert_eq!(CloseCode::from(1011u16), CloseCode::Error);
297 assert_eq!(CloseCode::from(1012u16), CloseCode::Restart);
298 assert_eq!(CloseCode::from(1013u16), CloseCode::Again);
299 assert_eq!(CloseCode::from(1015u16), CloseCode::Tls);
300 assert_eq!(CloseCode::from(2000u16), CloseCode::Other(2000));
301 }
302
303 #[test]
304 fn closecode_into_u16() {
305 assert_eq!(1000u16, Into::<u16>::into(CloseCode::Normal));
306 assert_eq!(1001u16, Into::<u16>::into(CloseCode::Away));
307 assert_eq!(1002u16, Into::<u16>::into(CloseCode::Protocol));
308 assert_eq!(1003u16, Into::<u16>::into(CloseCode::Unsupported));
309 assert_eq!(1006u16, Into::<u16>::into(CloseCode::Abnormal));
310 assert_eq!(1007u16, Into::<u16>::into(CloseCode::Invalid));
311 assert_eq!(1008u16, Into::<u16>::into(CloseCode::Policy));
312 assert_eq!(1009u16, Into::<u16>::into(CloseCode::Size));
313 assert_eq!(1010u16, Into::<u16>::into(CloseCode::Extension));
314 assert_eq!(1011u16, Into::<u16>::into(CloseCode::Error));
315 assert_eq!(1012u16, Into::<u16>::into(CloseCode::Restart));
316 assert_eq!(1013u16, Into::<u16>::into(CloseCode::Again));
317 assert_eq!(1015u16, Into::<u16>::into(CloseCode::Tls));
318 assert_eq!(2000u16, Into::<u16>::into(CloseCode::Other(2000)));
319 }
320}