1use std::io;
7
8use base64::prelude::*;
9use derive_more::{Display, Error, From};
10use http::{header, Method, StatusCode};
11
12use crate::{body::BoxBody, header::HeaderValue, RequestHead, Response, ResponseBuilder};
13
14mod codec;
15mod dispatcher;
16mod frame;
17mod mask;
18mod proto;
19
20pub use self::{
21 codec::{Codec, Frame, Item, Message},
22 dispatcher::Dispatcher,
23 frame::Parser,
24 proto::{hash_key, CloseCode, CloseReason, OpCode},
25};
26
27#[derive(Debug, Display, Error, From)]
29pub enum ProtocolError {
30 #[display("Received an unmasked frame from client")]
32 UnmaskedFrame,
33
34 #[display("Received a masked frame from server")]
36 MaskedFrame,
37
38 #[display("Invalid opcode ({})", _0)]
40 InvalidOpcode(#[error(not(source))] u8),
41
42 #[display("Invalid control frame length ({})", _0)]
49 InvalidLength(#[error(not(source))] usize),
50
51 #[display("Bad opcode")]
63 BadOpCode,
64
65 #[display("Payload length is invalid or exceeds the size limit")]
70 Overflow,
71
72 #[display("Continuation has not started")]
74 ContinuationNotStarted,
75
76 #[display("Received new continuation but it has already started")]
78 ContinuationStarted,
79
80 #[display("Unknown continuation fragment: {}", _0)]
82 ContinuationFragment(#[error(not(source))] OpCode),
83
84 #[display("I/O error: {}", _0)]
86 Io(io::Error),
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, Error)]
91pub enum HandshakeError {
92 #[display("method not allowed")]
94 GetMethodRequired,
95
96 #[display("WebSocket upgrade is expected")]
98 NoWebsocketUpgrade,
99
100 #[display("connection upgrade is expected")]
102 NoConnectionUpgrade,
103
104 #[display("WebSocket version header is required")]
106 NoVersionHeader,
107
108 #[display("unsupported WebSocket version")]
110 UnsupportedVersion,
111
112 #[display("unknown WebSocket key")]
114 BadWebsocketKey,
115}
116
117impl From<HandshakeError> for Response<BoxBody> {
118 fn from(err: HandshakeError) -> Self {
119 match err {
120 HandshakeError::GetMethodRequired => {
121 let mut res = Response::new(StatusCode::METHOD_NOT_ALLOWED);
122 #[allow(clippy::declare_interior_mutable_const)]
123 const HV_GET: HeaderValue = HeaderValue::from_static("GET");
124 res.headers_mut().insert(header::ALLOW, HV_GET);
125 res
126 }
127
128 HandshakeError::NoWebsocketUpgrade => {
129 let mut res = Response::bad_request();
130 res.head_mut().reason = Some("No WebSocket Upgrade header found");
131 res
132 }
133
134 HandshakeError::NoConnectionUpgrade => {
135 let mut res = Response::bad_request();
136 res.head_mut().reason = Some("No Connection upgrade");
137 res
138 }
139
140 HandshakeError::NoVersionHeader => {
141 let mut res = Response::bad_request();
142 res.head_mut().reason = Some("WebSocket version header is required");
143 res
144 }
145
146 HandshakeError::UnsupportedVersion => {
147 #[allow(clippy::declare_interior_mutable_const)]
148 const HV_13: HeaderValue = HeaderValue::from_static("13");
149 #[allow(clippy::declare_interior_mutable_const)]
150 const HV_WEBSOCKET: HeaderValue = HeaderValue::from_static("websocket");
151 let mut res = Response::new(StatusCode::UPGRADE_REQUIRED);
152 res.headers_mut().insert(header::UPGRADE, HV_WEBSOCKET);
153 res.headers_mut()
154 .insert(header::SEC_WEBSOCKET_VERSION, HV_13);
155 res.head_mut().reason = Some("Unsupported WebSocket version");
156 res
157 }
158
159 HandshakeError::BadWebsocketKey => {
160 let mut res = Response::bad_request();
161 res.head_mut().reason = Some("Handshake error");
162 res
163 }
164 }
165 }
166}
167
168impl From<&HandshakeError> for Response<BoxBody> {
169 fn from(err: &HandshakeError) -> Self {
170 (*err).into()
171 }
172}
173
174pub fn handshake(req: &RequestHead) -> Result<ResponseBuilder, HandshakeError> {
176 verify_handshake(req)?;
177 Ok(handshake_response(req))
178}
179
180pub fn verify_handshake(req: &RequestHead) -> Result<(), HandshakeError> {
182 if req.method != Method::GET {
184 return Err(HandshakeError::GetMethodRequired);
185 }
186
187 let has_hdr = if let Some(hdr) = req.headers().get(header::UPGRADE) {
189 if let Ok(s) = hdr.to_str() {
190 s.to_ascii_lowercase().contains("websocket")
191 } else {
192 false
193 }
194 } else {
195 false
196 };
197 if !has_hdr {
198 return Err(HandshakeError::NoWebsocketUpgrade);
199 }
200
201 if !req.upgrade() {
203 return Err(HandshakeError::NoConnectionUpgrade);
204 }
205
206 if !req.headers().contains_key(header::SEC_WEBSOCKET_VERSION) {
208 return Err(HandshakeError::NoVersionHeader);
209 }
210 let supported_ver = {
211 if let Some(hdr) = req.headers().get(header::SEC_WEBSOCKET_VERSION) {
212 hdr == "13"
213 } else {
214 false
215 }
216 };
217 if !supported_ver {
218 return Err(HandshakeError::UnsupportedVersion);
219 }
220
221 let key = match req.headers().get(header::SEC_WEBSOCKET_KEY) {
223 Some(key) => key.as_bytes(),
224 None => return Err(HandshakeError::BadWebsocketKey),
225 };
226
227 let mut decoded = [0u8; 16];
228 if key.len() != 24 || BASE64_STANDARD.decode_slice(key, &mut decoded) != Ok(16) {
229 return Err(HandshakeError::BadWebsocketKey);
230 }
231 Ok(())
232}
233
234pub fn handshake_response(req: &RequestHead) -> ResponseBuilder {
238 let key = {
239 let key = req.headers().get(header::SEC_WEBSOCKET_KEY).unwrap();
240 proto::hash_key(key.as_ref())
241 };
242
243 Response::build(StatusCode::SWITCHING_PROTOCOLS)
244 .upgrade("websocket")
245 .insert_header((
246 header::SEC_WEBSOCKET_ACCEPT,
247 HeaderValue::from_bytes(&key).unwrap(),
249 ))
250 .take()
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256 use crate::{header, test::TestRequest};
257
258 #[test]
259 fn test_handshake() {
260 let req = TestRequest::default().method(Method::POST).finish();
261 assert_eq!(
262 HandshakeError::GetMethodRequired,
263 verify_handshake(req.head()).unwrap_err(),
264 );
265
266 let req = TestRequest::default().finish();
267 assert_eq!(
268 HandshakeError::NoWebsocketUpgrade,
269 verify_handshake(req.head()).unwrap_err(),
270 );
271
272 let req = TestRequest::default()
273 .insert_header((header::UPGRADE, header::HeaderValue::from_static("test")))
274 .finish();
275 assert_eq!(
276 HandshakeError::NoWebsocketUpgrade,
277 verify_handshake(req.head()).unwrap_err(),
278 );
279
280 let req = TestRequest::default()
281 .insert_header((
282 header::UPGRADE,
283 header::HeaderValue::from_static("websocket"),
284 ))
285 .finish();
286 assert_eq!(
287 HandshakeError::NoConnectionUpgrade,
288 verify_handshake(req.head()).unwrap_err(),
289 );
290
291 let req = TestRequest::default()
292 .insert_header((
293 header::UPGRADE,
294 header::HeaderValue::from_static("websocket"),
295 ))
296 .insert_header((
297 header::CONNECTION,
298 header::HeaderValue::from_static("upgrade"),
299 ))
300 .finish();
301 assert_eq!(
302 HandshakeError::NoVersionHeader,
303 verify_handshake(req.head()).unwrap_err(),
304 );
305
306 let req = TestRequest::default()
307 .insert_header((
308 header::UPGRADE,
309 header::HeaderValue::from_static("websocket"),
310 ))
311 .insert_header((
312 header::CONNECTION,
313 header::HeaderValue::from_static("upgrade"),
314 ))
315 .insert_header((
316 header::SEC_WEBSOCKET_VERSION,
317 header::HeaderValue::from_static("5"),
318 ))
319 .finish();
320 assert_eq!(
321 HandshakeError::UnsupportedVersion,
322 verify_handshake(req.head()).unwrap_err(),
323 );
324
325 for &ver in &["7", "8"] {
326 let req = TestRequest::default()
327 .insert_header((
328 header::UPGRADE,
329 header::HeaderValue::from_static("websocket"),
330 ))
331 .insert_header((
332 header::CONNECTION,
333 header::HeaderValue::from_static("upgrade"),
334 ))
335 .insert_header((
336 header::SEC_WEBSOCKET_VERSION,
337 header::HeaderValue::from_static(ver),
338 ))
339 .finish();
340 assert_eq!(
341 HandshakeError::UnsupportedVersion,
342 verify_handshake(req.head()).unwrap_err(),
343 );
344 }
345
346 let req = TestRequest::default()
347 .insert_header((
348 header::UPGRADE,
349 header::HeaderValue::from_static("websocket"),
350 ))
351 .insert_header((
352 header::CONNECTION,
353 header::HeaderValue::from_static("upgrade"),
354 ))
355 .insert_header((
356 header::SEC_WEBSOCKET_VERSION,
357 header::HeaderValue::from_static("13"),
358 ))
359 .finish();
360 assert_eq!(
361 HandshakeError::BadWebsocketKey,
362 verify_handshake(req.head()).unwrap_err(),
363 );
364
365 let req = TestRequest::default()
366 .insert_header((
367 header::UPGRADE,
368 header::HeaderValue::from_static("websocket"),
369 ))
370 .insert_header((
371 header::CONNECTION,
372 header::HeaderValue::from_static("keep-alive"),
373 ))
374 .append_header((header::CONNECTION, "Upgrade"))
375 .insert_header((
376 header::SEC_WEBSOCKET_VERSION,
377 header::HeaderValue::from_static("13"),
378 ))
379 .insert_header((
380 header::SEC_WEBSOCKET_KEY,
381 header::HeaderValue::from_static("dGhlIHNhbXBsZSBub25jZQ=="),
382 ))
383 .finish();
384 assert_eq!(
385 StatusCode::SWITCHING_PROTOCOLS,
386 handshake(req.head()).unwrap().finish().status()
387 );
388
389 let req = TestRequest::default()
391 .insert_header((
392 header::UPGRADE,
393 header::HeaderValue::from_static("websocket"),
394 ))
395 .insert_header((
396 header::CONNECTION,
397 header::HeaderValue::from_static("upgrade"),
398 ))
399 .insert_header((
400 header::SEC_WEBSOCKET_VERSION,
401 header::HeaderValue::from_static("13"),
402 ))
403 .insert_header((
404 header::SEC_WEBSOCKET_KEY,
405 header::HeaderValue::from_static("13"),
406 ))
407 .finish();
408 assert_eq!(
409 HandshakeError::BadWebsocketKey,
410 verify_handshake(req.head()).unwrap_err(),
411 );
412 }
413
414 #[test]
415 fn test_ws_error_http_response() {
416 let resp: Response<BoxBody> = HandshakeError::GetMethodRequired.into();
417 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
418 let resp: Response<BoxBody> = HandshakeError::NoWebsocketUpgrade.into();
419 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
420 let resp: Response<BoxBody> = HandshakeError::NoConnectionUpgrade.into();
421 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
422 let resp: Response<BoxBody> = HandshakeError::NoVersionHeader.into();
423 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
424 let resp: Response<BoxBody> = HandshakeError::UnsupportedVersion.into();
425 assert_eq!(resp.status(), StatusCode::UPGRADE_REQUIRED);
426 assert!(!resp.upgrade());
427 assert_eq!(resp.headers().get(header::UPGRADE).unwrap(), "websocket");
428 assert_eq!(
429 resp.headers().get(header::SEC_WEBSOCKET_VERSION).unwrap(),
430 "13"
431 );
432 let resp: Response<BoxBody> = HandshakeError::BadWebsocketKey.into();
433 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
434 }
435}