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")]
52 BadOpCode,
53
54 #[display("payload reached size limit")]
56 Overflow,
57
58 #[display("continuation has not started")]
60 ContinuationNotStarted,
61
62 #[display("received new continuation but it has already started")]
64 ContinuationStarted,
65
66 #[display("unknown continuation fragment: {}", _0)]
68 ContinuationFragment(#[error(not(source))] OpCode),
69
70 #[display("I/O error: {}", _0)]
72 Io(io::Error),
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, Error)]
77pub enum HandshakeError {
78 #[display("method not allowed")]
80 GetMethodRequired,
81
82 #[display("WebSocket upgrade is expected")]
84 NoWebsocketUpgrade,
85
86 #[display("connection upgrade is expected")]
88 NoConnectionUpgrade,
89
90 #[display("WebSocket version header is required")]
92 NoVersionHeader,
93
94 #[display("unsupported WebSocket version")]
96 UnsupportedVersion,
97
98 #[display("unknown WebSocket key")]
100 BadWebsocketKey,
101}
102
103impl From<HandshakeError> for Response<BoxBody> {
104 fn from(err: HandshakeError) -> Self {
105 match err {
106 HandshakeError::GetMethodRequired => {
107 let mut res = Response::new(StatusCode::METHOD_NOT_ALLOWED);
108 #[allow(clippy::declare_interior_mutable_const)]
109 const HV_GET: HeaderValue = HeaderValue::from_static("GET");
110 res.headers_mut().insert(header::ALLOW, HV_GET);
111 res
112 }
113
114 HandshakeError::NoWebsocketUpgrade => {
115 let mut res = Response::bad_request();
116 res.head_mut().reason = Some("No WebSocket Upgrade header found");
117 res
118 }
119
120 HandshakeError::NoConnectionUpgrade => {
121 let mut res = Response::bad_request();
122 res.head_mut().reason = Some("No Connection upgrade");
123 res
124 }
125
126 HandshakeError::NoVersionHeader => {
127 let mut res = Response::bad_request();
128 res.head_mut().reason = Some("WebSocket version header is required");
129 res
130 }
131
132 HandshakeError::UnsupportedVersion => {
133 let mut res = Response::bad_request();
134 res.head_mut().reason = Some("Unsupported WebSocket version");
135 res
136 }
137
138 HandshakeError::BadWebsocketKey => {
139 let mut res = Response::bad_request();
140 res.head_mut().reason = Some("Handshake error");
141 res
142 }
143 }
144 }
145}
146
147impl From<&HandshakeError> for Response<BoxBody> {
148 fn from(err: &HandshakeError) -> Self {
149 (*err).into()
150 }
151}
152
153pub fn handshake(req: &RequestHead) -> Result<ResponseBuilder, HandshakeError> {
155 verify_handshake(req)?;
156 Ok(handshake_response(req))
157}
158
159pub fn verify_handshake(req: &RequestHead) -> Result<(), HandshakeError> {
161 if req.method != Method::GET {
163 return Err(HandshakeError::GetMethodRequired);
164 }
165
166 let has_hdr = if let Some(hdr) = req.headers().get(header::UPGRADE) {
168 if let Ok(s) = hdr.to_str() {
169 s.to_ascii_lowercase().contains("websocket")
170 } else {
171 false
172 }
173 } else {
174 false
175 };
176 if !has_hdr {
177 return Err(HandshakeError::NoWebsocketUpgrade);
178 }
179
180 if !req.upgrade() {
182 return Err(HandshakeError::NoConnectionUpgrade);
183 }
184
185 if !req.headers().contains_key(header::SEC_WEBSOCKET_VERSION) {
187 return Err(HandshakeError::NoVersionHeader);
188 }
189 let supported_ver = {
190 if let Some(hdr) = req.headers().get(header::SEC_WEBSOCKET_VERSION) {
191 hdr == "13" || hdr == "8" || hdr == "7"
192 } else {
193 false
194 }
195 };
196 if !supported_ver {
197 return Err(HandshakeError::UnsupportedVersion);
198 }
199
200 if !req.headers().contains_key(header::SEC_WEBSOCKET_KEY) {
202 return Err(HandshakeError::BadWebsocketKey);
203 }
204 Ok(())
205}
206
207pub fn handshake_response(req: &RequestHead) -> ResponseBuilder {
211 let key = {
212 let key = req.headers().get(header::SEC_WEBSOCKET_KEY).unwrap();
213 proto::hash_key(key.as_ref())
214 };
215
216 Response::build(StatusCode::SWITCHING_PROTOCOLS)
217 .upgrade("websocket")
218 .insert_header((
219 header::SEC_WEBSOCKET_ACCEPT,
220 HeaderValue::from_bytes(&key).unwrap(),
222 ))
223 .take()
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229 use crate::{header, test::TestRequest};
230
231 #[test]
232 fn test_handshake() {
233 let req = TestRequest::default().method(Method::POST).finish();
234 assert_eq!(
235 HandshakeError::GetMethodRequired,
236 verify_handshake(req.head()).unwrap_err(),
237 );
238
239 let req = TestRequest::default().finish();
240 assert_eq!(
241 HandshakeError::NoWebsocketUpgrade,
242 verify_handshake(req.head()).unwrap_err(),
243 );
244
245 let req = TestRequest::default()
246 .insert_header((header::UPGRADE, header::HeaderValue::from_static("test")))
247 .finish();
248 assert_eq!(
249 HandshakeError::NoWebsocketUpgrade,
250 verify_handshake(req.head()).unwrap_err(),
251 );
252
253 let req = TestRequest::default()
254 .insert_header((
255 header::UPGRADE,
256 header::HeaderValue::from_static("websocket"),
257 ))
258 .finish();
259 assert_eq!(
260 HandshakeError::NoConnectionUpgrade,
261 verify_handshake(req.head()).unwrap_err(),
262 );
263
264 let req = TestRequest::default()
265 .insert_header((
266 header::UPGRADE,
267 header::HeaderValue::from_static("websocket"),
268 ))
269 .insert_header((
270 header::CONNECTION,
271 header::HeaderValue::from_static("upgrade"),
272 ))
273 .finish();
274 assert_eq!(
275 HandshakeError::NoVersionHeader,
276 verify_handshake(req.head()).unwrap_err(),
277 );
278
279 let req = TestRequest::default()
280 .insert_header((
281 header::UPGRADE,
282 header::HeaderValue::from_static("websocket"),
283 ))
284 .insert_header((
285 header::CONNECTION,
286 header::HeaderValue::from_static("upgrade"),
287 ))
288 .insert_header((
289 header::SEC_WEBSOCKET_VERSION,
290 header::HeaderValue::from_static("5"),
291 ))
292 .finish();
293 assert_eq!(
294 HandshakeError::UnsupportedVersion,
295 verify_handshake(req.head()).unwrap_err(),
296 );
297
298 let req = TestRequest::default()
299 .insert_header((
300 header::UPGRADE,
301 header::HeaderValue::from_static("websocket"),
302 ))
303 .insert_header((
304 header::CONNECTION,
305 header::HeaderValue::from_static("upgrade"),
306 ))
307 .insert_header((
308 header::SEC_WEBSOCKET_VERSION,
309 header::HeaderValue::from_static("13"),
310 ))
311 .finish();
312 assert_eq!(
313 HandshakeError::BadWebsocketKey,
314 verify_handshake(req.head()).unwrap_err(),
315 );
316
317 let req = TestRequest::default()
318 .insert_header((
319 header::UPGRADE,
320 header::HeaderValue::from_static("websocket"),
321 ))
322 .insert_header((
323 header::CONNECTION,
324 header::HeaderValue::from_static("upgrade"),
325 ))
326 .insert_header((
327 header::SEC_WEBSOCKET_VERSION,
328 header::HeaderValue::from_static("13"),
329 ))
330 .insert_header((
331 header::SEC_WEBSOCKET_KEY,
332 header::HeaderValue::from_static("13"),
333 ))
334 .finish();
335 assert_eq!(
336 StatusCode::SWITCHING_PROTOCOLS,
337 handshake_response(req.head()).finish().status()
338 );
339 }
340
341 #[test]
342 fn test_ws_error_http_response() {
343 let resp: Response<BoxBody> = HandshakeError::GetMethodRequired.into();
344 assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
345 let resp: Response<BoxBody> = HandshakeError::NoWebsocketUpgrade.into();
346 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
347 let resp: Response<BoxBody> = HandshakeError::NoConnectionUpgrade.into();
348 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
349 let resp: Response<BoxBody> = HandshakeError::NoVersionHeader.into();
350 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
351 let resp: Response<BoxBody> = HandshakeError::UnsupportedVersion.into();
352 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
353 let resp: Response<BoxBody> = HandshakeError::BadWebsocketKey.into();
354 assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
355 }
356}