1use std::io;
7
8use derive_more::{Display, Error, From};
9use http::{header, Method, StatusCode};
10
11use crate::{body::BoxBody, header::HeaderValue, RequestHead, Response, ResponseBuilder};
12
13mod codec;
14mod dispatcher;
15mod frame;
16mod mask;
17mod proto;
18
19pub use self::{
20 codec::{Codec, Frame, Item, Message},
21 dispatcher::Dispatcher,
22 frame::Parser,
23 proto::{hash_key, CloseCode, CloseReason, OpCode},
24};
25
26#[derive(Debug, Display, Error, From)]
28pub enum ProtocolError {
29 #[display("Received an unmasked frame from client")]
31 UnmaskedFrame,
32
33 #[display("Received a masked frame from server")]
35 MaskedFrame,
36
37 #[display("Invalid opcode ({})", _0)]
39 InvalidOpcode(#[error(not(source))] u8),
40
41 #[display("Invalid control frame length ({})", _0)]
48 InvalidLength(#[error(not(source))] usize),
49
50 #[display("Bad opcode")]
62 BadOpCode,
63
64 #[display("Payload reached size limit")]
66 Overflow,
67
68 #[display("Continuation has not started")]
70 ContinuationNotStarted,
71
72 #[display("Received new continuation but it has already started")]
74 ContinuationStarted,
75
76 #[display("Unknown continuation fragment: {}", _0)]
78 ContinuationFragment(#[error(not(source))] OpCode),
79
80 #[display("I/O error: {}", _0)]
82 Io(io::Error),
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, Error)]
87pub enum HandshakeError {
88 #[display("method not allowed")]
90 GetMethodRequired,
91
92 #[display("WebSocket upgrade is expected")]
94 NoWebsocketUpgrade,
95
96 #[display("connection upgrade is expected")]
98 NoConnectionUpgrade,
99
100 #[display("WebSocket version header is required")]
102 NoVersionHeader,
103
104 #[display("unsupported WebSocket version")]
106 UnsupportedVersion,
107
108 #[display("unknown WebSocket key")]
110 BadWebsocketKey,
111}
112
113impl From<HandshakeError> for Response<BoxBody> {
114 fn from(err: HandshakeError) -> Self {
115 match err {
116 HandshakeError::GetMethodRequired => {
117 let mut res = Response::new(StatusCode::METHOD_NOT_ALLOWED);
118 #[allow(clippy::declare_interior_mutable_const)]
119 const HV_GET: HeaderValue = HeaderValue::from_static("GET");
120 res.headers_mut().insert(header::ALLOW, HV_GET);
121 res
122 }
123
124 HandshakeError::NoWebsocketUpgrade => {
125 let mut res = Response::bad_request();
126 res.head_mut().reason = Some("No WebSocket Upgrade header found");
127 res
128 }
129
130 HandshakeError::NoConnectionUpgrade => {
131 let mut res = Response::bad_request();
132 res.head_mut().reason = Some("No Connection upgrade");
133 res
134 }
135
136 HandshakeError::NoVersionHeader => {
137 let mut res = Response::bad_request();
138 res.head_mut().reason = Some("WebSocket version header is required");
139 res
140 }
141
142 HandshakeError::UnsupportedVersion => {
143 let mut res = Response::bad_request();
144 res.head_mut().reason = Some("Unsupported WebSocket version");
145 res
146 }
147
148 HandshakeError::BadWebsocketKey => {
149 let mut res = Response::bad_request();
150 res.head_mut().reason = Some("Handshake error");
151 res
152 }
153 }
154 }
155}
156
157impl From<&HandshakeError> for Response<BoxBody> {
158 fn from(err: &HandshakeError) -> Self {
159 (*err).into()
160 }
161}
162
163pub fn handshake(req: &RequestHead) -> Result<ResponseBuilder, HandshakeError> {
165 verify_handshake(req)?;
166 Ok(handshake_response(req))
167}
168
169pub fn verify_handshake(req: &RequestHead) -> Result<(), HandshakeError> {
171 if req.method != Method::GET {
173 return Err(HandshakeError::GetMethodRequired);
174 }
175
176 let has_hdr = if let Some(hdr) = req.headers().get(header::UPGRADE) {
178 if let Ok(s) = hdr.to_str() {
179 s.to_ascii_lowercase().contains("websocket")
180 } else {
181 false
182 }
183 } else {
184 false
185 };
186 if !has_hdr {
187 return Err(HandshakeError::NoWebsocketUpgrade);
188 }
189
190 if !req.upgrade() {
192 return Err(HandshakeError::NoConnectionUpgrade);
193 }
194
195 if !req.headers().contains_key(header::SEC_WEBSOCKET_VERSION) {
197 return Err(HandshakeError::NoVersionHeader);
198 }
199 let supported_ver = {
200 if let Some(hdr) = req.headers().get(header::SEC_WEBSOCKET_VERSION) {
201 hdr == "13" || hdr == "8" || hdr == "7"
202 } else {
203 false
204 }
205 };
206 if !supported_ver {
207 return Err(HandshakeError::UnsupportedVersion);
208 }
209
210 if !req.headers().contains_key(header::SEC_WEBSOCKET_KEY) {
212 return Err(HandshakeError::BadWebsocketKey);
213 }
214 Ok(())
215}
216
217pub fn handshake_response(req: &RequestHead) -> ResponseBuilder {
221 let key = {
222 let key = req.headers().get(header::SEC_WEBSOCKET_KEY).unwrap();
223 proto::hash_key(key.as_ref())
224 };
225
226 Response::build(StatusCode::SWITCHING_PROTOCOLS)
227 .upgrade("websocket")
228 .insert_header((
229 header::SEC_WEBSOCKET_ACCEPT,
230 HeaderValue::from_bytes(&key).unwrap(),
232 ))
233 .take()
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use crate::{header, test::TestRequest};
240
241 #[test]
242 fn test_handshake() {
243 let req = TestRequest::default().method(Method::POST).finish();
244 assert_eq!(
245 HandshakeError::GetMethodRequired,
246 verify_handshake(req.head()).unwrap_err(),
247 );
248
249 let req = TestRequest::default().finish();
250 assert_eq!(
251 HandshakeError::NoWebsocketUpgrade,
252 verify_handshake(req.head()).unwrap_err(),
253 );
254
255 let req = TestRequest::default()
256 .insert_header((header::UPGRADE, header::HeaderValue::from_static("test")))
257 .finish();
258 assert_eq!(
259 HandshakeError::NoWebsocketUpgrade,
260 verify_handshake(req.head()).unwrap_err(),
261 );
262
263 let req = TestRequest::default()
264 .insert_header((
265 header::UPGRADE,
266 header::HeaderValue::from_static("websocket"),
267 ))
268 .finish();
269 assert_eq!(
270 HandshakeError::NoConnectionUpgrade,
271 verify_handshake(req.head()).unwrap_err(),
272 );
273
274 let req = TestRequest::default()
275 .insert_header((
276 header::UPGRADE,
277 header::HeaderValue::from_static("websocket"),
278 ))
279 .insert_header((
280 header::CONNECTION,
281 header::HeaderValue::from_static("upgrade"),
282 ))
283 .finish();
284 assert_eq!(
285 HandshakeError::NoVersionHeader,
286 verify_handshake(req.head()).unwrap_err(),
287 );
288
289 let req = TestRequest::default()
290 .insert_header((
291 header::UPGRADE,
292 header::HeaderValue::from_static("websocket"),
293 ))
294 .insert_header((
295 header::CONNECTION,
296 header::HeaderValue::from_static("upgrade"),
297 ))
298 .insert_header((
299 header::SEC_WEBSOCKET_VERSION,
300 header::HeaderValue::from_static("5"),
301 ))
302 .finish();
303 assert_eq!(
304 HandshakeError::UnsupportedVersion,
305 verify_handshake(req.head()).unwrap_err(),
306 );
307
308 let req = TestRequest::default()
309 .insert_header((
310 header::UPGRADE,
311 header::HeaderValue::from_static("websocket"),
312 ))
313 .insert_header((
314 header::CONNECTION,
315 header::HeaderValue::from_static("upgrade"),
316 ))
317 .insert_header((
318 header::SEC_WEBSOCKET_VERSION,
319 header::HeaderValue::from_static("13"),
320 ))
321 .finish();
322 assert_eq!(
323 HandshakeError::BadWebsocketKey,
324 verify_handshake(req.head()).unwrap_err(),
325 );
326
327 let req = TestRequest::default()
328 .insert_header((
329 header::UPGRADE,
330 header::HeaderValue::from_static("websocket"),
331 ))
332 .insert_header((
333 header::CONNECTION,
334 header::HeaderValue::from_static("upgrade"),
335 ))
336 .insert_header((
337 header::SEC_WEBSOCKET_VERSION,
338 header::HeaderValue::from_static("13"),
339 ))
340 .insert_header((
341 header::SEC_WEBSOCKET_KEY,
342 header::HeaderValue::from_static("13"),
343 ))
344 .finish();
345 assert_eq!(
346 StatusCode::SWITCHING_PROTOCOLS,
347 handshake_response(req.head()).finish().status()
348 );
349 }
350
351 #[test]
352 fn test_ws_error_http_response() {
353 let resp: Response<BoxBody> = HandshakeError::GetMethodRequired.into();
354 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
355 let resp: Response<BoxBody> = HandshakeError::NoWebsocketUpgrade.into();
356 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
357 let resp: Response<BoxBody> = HandshakeError::NoConnectionUpgrade.into();
358 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
359 let resp: Response<BoxBody> = HandshakeError::NoVersionHeader.into();
360 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
361 let resp: Response<BoxBody> = HandshakeError::UnsupportedVersion.into();
362 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
363 let resp: Response<BoxBody> = HandshakeError::BadWebsocketKey.into();
364 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
365 }
366}