Skip to main content

tungstenite/handshake/
server.rs

1//! Server handshake machine.
2
3use std::{
4    io::{self, Read, Write},
5    marker::PhantomData,
6    result::Result as StdResult,
7};
8
9use headers::{Header, HeaderMapExt};
10use http::{
11    response::Builder, HeaderMap, Request as HttpRequest, 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    extensions::{headers::SecWebsocketExtensions, Extensions},
25    handshake::version_as_str,
26    protocol::{Role, WebSocket, WebSocketConfig},
27};
28
29/// Server request type.
30pub type Request = HttpRequest<()>;
31
32/// Server response type.
33pub type Response = HttpResponse<()>;
34
35/// Server error response type.
36pub type ErrorResponse = HttpResponse<Option<String>>;
37
38fn create_parts<T>(request: &HttpRequest<T>) -> Result<Builder> {
39    if request.method() != http::Method::GET {
40        return Err(Error::Protocol(ProtocolError::WrongHttpMethod));
41    }
42
43    if request.version() < http::Version::HTTP_11 {
44        return Err(Error::Protocol(ProtocolError::WrongHttpVersion));
45    }
46
47    if !request
48        .headers()
49        .get("Connection")
50        .and_then(|h| h.to_str().ok())
51        .map(|h| h.split([' ', ',']).any(|p| p.eq_ignore_ascii_case("Upgrade")))
52        .unwrap_or(false)
53    {
54        return Err(Error::Protocol(ProtocolError::MissingConnectionUpgradeHeader));
55    }
56
57    if !request
58        .headers()
59        .get("Upgrade")
60        .and_then(|h| h.to_str().ok())
61        .map(|h| h.eq_ignore_ascii_case("websocket"))
62        .unwrap_or(false)
63    {
64        return Err(Error::Protocol(ProtocolError::MissingUpgradeWebSocketHeader));
65    }
66
67    if !request.headers().get("Sec-WebSocket-Version").map(|h| h == "13").unwrap_or(false) {
68        return Err(Error::Protocol(ProtocolError::MissingSecWebSocketVersionHeader));
69    }
70
71    let key = request
72        .headers()
73        .get("Sec-WebSocket-Key")
74        .ok_or(Error::Protocol(ProtocolError::MissingSecWebSocketKey))?;
75
76    let builder = Response::builder()
77        .status(StatusCode::SWITCHING_PROTOCOLS)
78        .version(request.version())
79        .header("Connection", "Upgrade")
80        .header("Upgrade", "websocket")
81        .header("Sec-WebSocket-Accept", derive_accept_key(key.as_bytes()));
82
83    Ok(builder)
84}
85
86/// Create a response for the request.
87pub fn create_response(request: &Request) -> Result<Response> {
88    Ok(create_parts(request)?.body(())?)
89}
90
91/// Create a response for the request with a custom body.
92pub fn create_response_with_body<T1, T2>(
93    request: &HttpRequest<T1>,
94    generate_body: impl FnOnce() -> T2,
95) -> Result<HttpResponse<T2>> {
96    Ok(create_parts(request)?.body(generate_body())?)
97}
98
99/// Write `response` to the stream `w`.
100pub fn write_response<T>(mut w: impl io::Write, response: &HttpResponse<T>) -> Result<()> {
101    writeln!(
102        w,
103        "{version} {status}\r",
104        version = version_as_str(response.version())?,
105        status = response.status()
106    )?;
107
108    for (k, v) in response.headers() {
109        writeln!(w, "{}: {}\r", k, v.to_str()?)?;
110    }
111
112    writeln!(w, "\r")?;
113
114    Ok(())
115}
116
117impl TryParse for Request {
118    fn try_parse(buf: &[u8]) -> Result<Option<(usize, Self)>> {
119        let mut hbuffer = [httparse::EMPTY_HEADER; MAX_HEADERS];
120        let mut req = httparse::Request::new(&mut hbuffer);
121        Ok(match req.parse(buf)? {
122            Status::Partial => None,
123            Status::Complete(size) => Some((size, Request::from_httparse(req)?)),
124        })
125    }
126}
127
128impl<'h, 'b: 'h> FromHttparse<httparse::Request<'h, 'b>> for Request {
129    fn from_httparse(raw: httparse::Request<'h, 'b>) -> Result<Self> {
130        if raw.method.expect("Bug: no method in header") != "GET" {
131            return Err(Error::Protocol(ProtocolError::WrongHttpMethod));
132        }
133
134        if raw.version.expect("Bug: no HTTP version") < /*1.*/1 {
135            return Err(Error::Protocol(ProtocolError::WrongHttpVersion));
136        }
137
138        let headers = HeaderMap::from_httparse(raw.headers)?;
139
140        let mut request = Request::new(());
141        *request.method_mut() = http::Method::GET;
142        *request.headers_mut() = headers;
143        *request.uri_mut() = raw.path.expect("Bug: no path in header").parse()?;
144        // TODO: httparse only supports HTTP 0.9/1.0/1.1 but not HTTP 2.0
145        // so the only valid value we could get in the response would be 1.1.
146        *request.version_mut() = http::Version::HTTP_11;
147
148        Ok(request)
149    }
150}
151
152/// The callback trait.
153///
154/// The callback is called when the server receives an incoming WebSocket
155/// handshake request from the client. Specifying a callback allows you to analyze incoming headers
156/// and add additional headers to the response that server sends to the client and/or reject the
157/// connection based on the incoming headers.
158pub trait Callback: Sized {
159    /// Called whenever the server read the request from the client and is ready to reply to it.
160    /// May return additional reply headers.
161    /// Returning an error resulting in rejecting the incoming connection.
162    fn on_request(
163        self,
164        request: &Request,
165        response: Response,
166    ) -> StdResult<Response, ErrorResponse>;
167}
168
169impl<F> Callback for F
170where
171    F: FnOnce(&Request, Response) -> StdResult<Response, ErrorResponse>,
172{
173    fn on_request(
174        self,
175        request: &Request,
176        response: Response,
177    ) -> StdResult<Response, ErrorResponse> {
178        self(request, response)
179    }
180}
181
182/// Stub for callback that does nothing.
183#[derive(Clone, Copy, Debug)]
184pub struct NoCallback;
185
186impl Callback for NoCallback {
187    fn on_request(
188        self,
189        _request: &Request,
190        response: Response,
191    ) -> StdResult<Response, ErrorResponse> {
192        Ok(response)
193    }
194}
195
196/// Server handshake role.
197#[allow(missing_copy_implementations)]
198#[derive(Debug)]
199pub struct ServerHandshake<S, C> {
200    /// Callback which is called whenever the server read the request from the client and is ready
201    /// to reply to it. The callback returns an optional headers which will be added to the reply
202    /// which the server sends to the user.
203    callback: Option<C>,
204    /// WebSocket configuration.
205    config: Option<WebSocketConfig>,
206    /// Error code/flag. If set, an error will be returned after sending response to the client.
207    error_response: Option<ErrorResponse>,
208    // Negotiated extension context for server.
209    extensions: Extensions,
210    /// Internal stream type.
211    _marker: PhantomData<S>,
212}
213
214impl<S: Read + Write, C: Callback> ServerHandshake<S, C> {
215    /// Start server handshake. `callback` specifies a custom callback which the user can pass to
216    /// the handshake, this callback will be called when the a websocket client connects to the
217    /// server, you can specify the callback if you want to add additional header to the client
218    /// upon join based on the incoming headers.
219    pub fn start(stream: S, callback: C, config: Option<WebSocketConfig>) -> MidHandshake<Self> {
220        trace!("Server handshake initiated.");
221        MidHandshake {
222            machine: HandshakeMachine::start_read(stream),
223            role: ServerHandshake {
224                callback: Some(callback),
225                config,
226                error_response: None,
227                extensions: Extensions::default(),
228                _marker: PhantomData,
229            },
230        }
231    }
232}
233
234impl<S: Read + Write, C: Callback> HandshakeRole for ServerHandshake<S, C> {
235    type IncomingData = Request;
236    type InternalStream = S;
237    type FinalResult = WebSocket<S>;
238
239    fn stage_finished(
240        &mut self,
241        finish: StageResult<Self::IncomingData, Self::InternalStream>,
242    ) -> Result<ProcessingResult<Self::InternalStream, Self::FinalResult>> {
243        Ok(match finish {
244            StageResult::DoneReading { stream, result, tail } => {
245                if !tail.is_empty() {
246                    return Err(Error::Protocol(ProtocolError::JunkAfterRequest));
247                }
248
249                let mut response = create_response(&result)?;
250                if let Some(extensions) =
251                    result.headers().typed_try_get::<SecWebsocketExtensions>().map_err(|_| {
252                        ProtocolError::InvalidHeader(SecWebsocketExtensions::name().clone().into())
253                    })?
254                {
255                    let extensions_config = self
256                        .config
257                        .ok_or_else(|| {
258                            ProtocolError::InvalidHeader(
259                                SecWebsocketExtensions::name().clone().into(),
260                            )
261                        })?
262                        .extensions;
263                    let (extensions, agreed) = extensions_config
264                        .accept_offers(&extensions)
265                        .map_err(ProtocolError::from)?;
266
267                    if let Some(agreed) = agreed {
268                        response.headers_mut().typed_insert(agreed)
269                    };
270                    self.extensions = extensions;
271                }
272
273                let callback_result = if let Some(callback) = self.callback.take() {
274                    callback.on_request(&result, response)
275                } else {
276                    Ok(response)
277                };
278
279                match callback_result {
280                    Ok(response) => {
281                        let mut output = vec![];
282                        write_response(&mut output, &response)?;
283                        ProcessingResult::Continue(HandshakeMachine::start_write(stream, output))
284                    }
285
286                    Err(resp) => {
287                        if resp.status().is_success() {
288                            return Err(Error::Protocol(ProtocolError::CustomResponseSuccessful));
289                        }
290
291                        self.error_response = Some(resp);
292                        let resp = self.error_response.as_ref().unwrap();
293
294                        let mut output = vec![];
295                        write_response(&mut output, resp)?;
296
297                        if let Some(body) = resp.body() {
298                            output.extend_from_slice(body.as_bytes());
299                        }
300
301                        ProcessingResult::Continue(HandshakeMachine::start_write(stream, output))
302                    }
303                }
304            }
305
306            StageResult::DoneWriting(stream) => {
307                if let Some(err) = self.error_response.take() {
308                    debug!("Server handshake failed.");
309
310                    let (parts, body) = err.into_parts();
311                    let body = body.map(|b| b.as_bytes().to_vec());
312                    return Err(Error::Http(http::Response::from_parts(parts, body).into()));
313                } else {
314                    debug!("Server handshake done.");
315                    let websocket = WebSocket::from_raw_socket_with_extensions(
316                        stream,
317                        Role::Server,
318                        self.config,
319                        std::mem::take(&mut self.extensions),
320                    );
321                    ProcessingResult::Done(websocket)
322                }
323            }
324        })
325    }
326}
327
328#[cfg(test)]
329mod tests {
330    use super::{super::machine::TryParse, create_response, Request};
331
332    #[test]
333    fn request_parsing() {
334        const DATA: &[u8] = b"GET /script.ws HTTP/1.1\r\nHost: foo.com\r\n\r\n";
335        let (_, req) = Request::try_parse(DATA).unwrap().unwrap();
336        assert_eq!(req.uri().path(), "/script.ws");
337        assert_eq!(req.headers().get("Host").unwrap(), &b"foo.com"[..]);
338    }
339
340    #[test]
341    fn request_replying() {
342        const DATA: &[u8] = b"\
343            GET /script.ws HTTP/1.1\r\n\
344            Host: foo.com\r\n\
345            Connection: upgrade\r\n\
346            Upgrade: websocket\r\n\
347            Sec-WebSocket-Version: 13\r\n\
348            Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\
349            \r\n";
350        let (_, req) = Request::try_parse(DATA).unwrap().unwrap();
351        let response = create_response(&req).unwrap();
352
353        assert_eq!(
354            response.headers().get("Sec-WebSocket-Accept").unwrap(),
355            b"s3pPLMBiTxaQ9kYGzzhZRbK+xOo=".as_ref()
356        );
357    }
358}