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