1use std::{
4 io::{self, Read, Write},
5 marker::PhantomData,
6 result::Result as StdResult,
7};
8
9use http::{
10 header::HeaderValue, response::Builder, HeaderMap, Request as HttpRequest,
11 Response as HttpResponse, StatusCode,
12};
13use httparse::Status;
14use log::*;
15
16use super::{
17 derive_accept_key,
18 headers::{FromHttparse, MAX_HEADERS},
19 machine::{HandshakeMachine, StageResult, TryParse},
20 HandshakeRole, MidHandshake, ProcessingResult,
21};
22use crate::{
23 error::{Error, ProtocolError, Result},
24 handshake::version_as_str,
25 protocol::{Role, WebSocket, WebSocketConfig},
26};
27
28pub type Request = HttpRequest<()>;
30
31pub type Response = HttpResponse<()>;
33
34pub type ErrorResponse = HttpResponse<Option<String>>;
36
37fn create_parts<T>(request: &HttpRequest<T>) -> Result<Builder> {
38 if request.method() != http::Method::GET {
39 return Err(Error::Protocol(ProtocolError::WrongHttpMethod));
40 }
41
42 if request.version() < http::Version::HTTP_11 {
43 return Err(Error::Protocol(ProtocolError::WrongHttpVersion));
44 }
45
46 if !request
47 .headers()
48 .get("Connection")
49 .and_then(|h| h.to_str().ok())
50 .map(|h| h.split([' ', ',']).any(|p| p.eq_ignore_ascii_case("Upgrade")))
51 .unwrap_or(false)
52 {
53 return Err(Error::Protocol(ProtocolError::MissingConnectionUpgradeHeader));
54 }
55
56 if !request
57 .headers()
58 .get("Upgrade")
59 .and_then(|h| h.to_str().ok())
60 .map(|h| h.eq_ignore_ascii_case("websocket"))
61 .unwrap_or(false)
62 {
63 return Err(Error::Protocol(ProtocolError::MissingUpgradeWebSocketHeader));
64 }
65
66 if !request.headers().get("Sec-WebSocket-Version").map(|h| h == "13").unwrap_or(false) {
67 return Err(Error::Protocol(ProtocolError::MissingSecWebSocketVersionHeader));
68 }
69
70 let key = request
71 .headers()
72 .get("Sec-WebSocket-Key")
73 .ok_or(Error::Protocol(ProtocolError::MissingSecWebSocketKey))?;
74
75 if !is_valid_sec_websocket_key(key) {
76 return Err(Error::Protocol(ProtocolError::InvalidSecWebSocketKey));
77 }
78
79 let builder = Response::builder()
80 .status(StatusCode::SWITCHING_PROTOCOLS)
81 .version(request.version())
82 .header("Connection", "Upgrade")
83 .header("Upgrade", "websocket")
84 .header("Sec-WebSocket-Accept", derive_accept_key(key.as_bytes()));
85
86 Ok(builder)
87}
88
89fn is_valid_sec_websocket_key(key: &HeaderValue) -> bool {
90 if key.len() != 24 {
91 return false;
92 }
93
94 let Ok(decoded) = data_encoding::BASE64.decode(key.as_bytes()) else {
95 return false;
96 };
97
98 decoded.len() == 16
99}
100
101pub fn create_response(request: &Request) -> Result<Response> {
103 Ok(create_parts(request)?.body(())?)
104}
105
106pub fn create_response_with_body<T1, T2>(
108 request: &HttpRequest<T1>,
109 generate_body: impl FnOnce() -> T2,
110) -> Result<HttpResponse<T2>> {
111 Ok(create_parts(request)?.body(generate_body())?)
112}
113
114pub fn write_response<T>(mut w: impl io::Write, response: &HttpResponse<T>) -> Result<()> {
116 writeln!(
117 w,
118 "{version} {status}\r",
119 version = version_as_str(response.version())?,
120 status = response.status()
121 )?;
122
123 for (k, v) in response.headers() {
124 writeln!(w, "{}: {}\r", k, v.to_str()?)?;
125 }
126
127 writeln!(w, "\r")?;
128
129 Ok(())
130}
131
132impl TryParse for Request {
133 fn try_parse(buf: &[u8]) -> Result<Option<(usize, Self)>> {
134 let mut hbuffer = [httparse::EMPTY_HEADER; MAX_HEADERS];
135 let mut req = httparse::Request::new(&mut hbuffer);
136 Ok(match req.parse(buf)? {
137 Status::Partial => None,
138 Status::Complete(size) => Some((size, Request::from_httparse(req)?)),
139 })
140 }
141}
142
143impl<'h, 'b: 'h> FromHttparse<httparse::Request<'h, 'b>> for Request {
144 fn from_httparse(raw: httparse::Request<'h, 'b>) -> Result<Self> {
145 if raw.method.expect("Bug: no method in header") != "GET" {
146 return Err(Error::Protocol(ProtocolError::WrongHttpMethod));
147 }
148
149 if raw.version.expect("Bug: no HTTP version") < 1 {
150 return Err(Error::Protocol(ProtocolError::WrongHttpVersion));
151 }
152
153 let headers = HeaderMap::from_httparse(raw.headers)?;
154
155 let mut request = Request::new(());
156 *request.method_mut() = http::Method::GET;
157 *request.headers_mut() = headers;
158 *request.uri_mut() = raw.path.expect("Bug: no path in header").parse()?;
159 *request.version_mut() = http::Version::HTTP_11;
162
163 Ok(request)
164 }
165}
166
167pub trait Callback: Sized {
174 fn on_request(
178 self,
179 request: &Request,
180 response: Response,
181 ) -> StdResult<Response, ErrorResponse>;
182}
183
184impl<F> Callback for F
185where
186 F: FnOnce(&Request, Response) -> StdResult<Response, ErrorResponse>,
187{
188 fn on_request(
189 self,
190 request: &Request,
191 response: Response,
192 ) -> StdResult<Response, ErrorResponse> {
193 self(request, response)
194 }
195}
196
197#[derive(Clone, Copy, Debug)]
199pub struct NoCallback;
200
201impl Callback for NoCallback {
202 fn on_request(
203 self,
204 _request: &Request,
205 response: Response,
206 ) -> StdResult<Response, ErrorResponse> {
207 Ok(response)
208 }
209}
210
211#[allow(missing_copy_implementations)]
213#[derive(Debug)]
214pub struct ServerHandshake<S, C> {
215 callback: Option<C>,
219 config: Option<WebSocketConfig>,
221 error_response: Option<ErrorResponse>,
223 _marker: PhantomData<S>,
225}
226
227impl<S: Read + Write, C: Callback> ServerHandshake<S, C> {
228 pub fn start(stream: S, callback: C, config: Option<WebSocketConfig>) -> MidHandshake<Self> {
233 trace!("Server handshake initiated.");
234 MidHandshake {
235 machine: HandshakeMachine::start_read(stream),
236 role: ServerHandshake {
237 callback: Some(callback),
238 config,
239 error_response: None,
240 _marker: PhantomData,
241 },
242 }
243 }
244}
245
246impl<S: Read + Write, C: Callback> HandshakeRole for ServerHandshake<S, C> {
247 type IncomingData = Request;
248 type InternalStream = S;
249 type FinalResult = WebSocket<S>;
250
251 fn stage_finished(
252 &mut self,
253 finish: StageResult<Self::IncomingData, Self::InternalStream>,
254 ) -> Result<ProcessingResult<Self::InternalStream, Self::FinalResult>> {
255 Ok(match finish {
256 StageResult::DoneReading { stream, result, tail } => {
257 if !tail.is_empty() {
258 return Err(Error::Protocol(ProtocolError::JunkAfterRequest));
259 }
260
261 let response = create_response(&result)?;
262 let callback_result = if let Some(callback) = self.callback.take() {
263 callback.on_request(&result, response)
264 } else {
265 Ok(response)
266 };
267
268 match callback_result {
269 Ok(response) => {
270 let mut output = vec![];
271 write_response(&mut output, &response)?;
272 ProcessingResult::Continue(HandshakeMachine::start_write(stream, output))
273 }
274
275 Err(resp) => {
276 if resp.status().is_success() {
277 return Err(Error::Protocol(ProtocolError::CustomResponseSuccessful));
278 }
279
280 self.error_response = Some(resp);
281 let resp = self.error_response.as_ref().unwrap();
282
283 let mut output = vec![];
284 write_response(&mut output, resp)?;
285
286 if let Some(body) = resp.body() {
287 output.extend_from_slice(body.as_bytes());
288 }
289
290 ProcessingResult::Continue(HandshakeMachine::start_write(stream, output))
291 }
292 }
293 }
294
295 StageResult::DoneWriting(stream) => {
296 if let Some(err) = self.error_response.take() {
297 debug!("Server handshake failed.");
298
299 let (parts, body) = err.into_parts();
300 let body = body.map(|b| b.as_bytes().to_vec());
301 return Err(Error::Http(http::Response::from_parts(parts, body).into()));
302 } else {
303 debug!("Server handshake done.");
304 let websocket = WebSocket::from_raw_socket(stream, Role::Server, self.config);
305 ProcessingResult::Done(websocket)
306 }
307 }
308 })
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use super::{super::machine::TryParse, create_response, Request};
315 use crate::error::{Error, ProtocolError};
316
317 fn request_with_key(key: &str) -> Request {
318 let data = format!(
319 "\
320 GET /script.ws HTTP/1.1\r\n\
321 Host: foo.com\r\n\
322 Connection: upgrade\r\n\
323 Upgrade: websocket\r\n\
324 Sec-WebSocket-Version: 13\r\n\
325 Sec-WebSocket-Key: {key}\r\n\
326 \r\n"
327 );
328
329 let (_, req) = Request::try_parse(data.as_bytes()).unwrap().unwrap();
330 req
331 }
332
333 fn assert_invalid_sec_websocket_key(key: &str) {
334 let req = request_with_key(key);
335 let err = create_response(&req).unwrap_err();
336 assert!(matches!(err, Error::Protocol(ProtocolError::InvalidSecWebSocketKey)));
337 }
338
339 #[test]
340 fn request_parsing() {
341 const DATA: &[u8] = b"GET /script.ws HTTP/1.1\r\nHost: foo.com\r\n\r\n";
342 let (_, req) = Request::try_parse(DATA).unwrap().unwrap();
343 assert_eq!(req.uri().path(), "/script.ws");
344 assert_eq!(req.headers().get("Host").unwrap(), &b"foo.com"[..]);
345 }
346
347 #[test]
348 fn request_replying() {
349 const DATA: &[u8] = b"\
350 GET /script.ws HTTP/1.1\r\n\
351 Host: foo.com\r\n\
352 Connection: upgrade\r\n\
353 Upgrade: websocket\r\n\
354 Sec-WebSocket-Version: 13\r\n\
355 Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\
356 \r\n";
357 let (_, req) = Request::try_parse(DATA).unwrap().unwrap();
358 let response = create_response(&req).unwrap();
359
360 assert_eq!(
361 response.headers().get("Sec-WebSocket-Accept").unwrap(),
362 b"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=".as_ref()
363 );
364 }
365
366 #[test]
367 fn test_invalid_websocket_key_empty() {
368 assert_invalid_sec_websocket_key("");
369 }
370
371 #[test]
372 fn test_invalid_websocket_key_too_long() {
373 assert_invalid_sec_websocket_key("dGhlIHNhbXBsZSBub25jZQ==AAAAAAAAAA");
374 }
375
376 #[test]
377 fn test_invalid_websocket_key_base64_symbol() {
378 assert_invalid_sec_websocket_key("dGhlIHNhbXBsZSBub25jZQ!!");
379 }
380
381 #[test]
382 fn test_invalid_websocket_key_decoded_length() {
383 assert_invalid_sec_websocket_key("AAAAAAAAAAAAAAAAAAAAAAAA");
384 }
385
386 #[test]
387 fn test_valid_websocket_key() {
388 let req = request_with_key("dGhlIHNhbXBsZSBub25jZQ==");
389 assert!(create_response(&req).is_ok());
390 }
391}