tiny_http/
request.rs

1use std::io::Error as IoError;
2use std::io::{self, Cursor, ErrorKind, Read, Write};
3
4use std::fmt;
5use std::net::SocketAddr;
6use std::str::FromStr;
7
8use std::sync::mpsc::Sender;
9
10use crate::util::{EqualReader, FusedReader};
11use crate::{HTTPVersion, Header, Method, Response, StatusCode};
12use chunked_transfer::Decoder;
13
14/// Represents an HTTP request made by a client.
15///
16/// A `Request` object is what is produced by the server, and is your what
17/// your code must analyse and answer.
18///
19/// This object implements the `Send` trait, therefore you can dispatch your requests to
20/// worker threads.
21///
22/// # Pipelining
23///
24/// If a client sends multiple requests in a row (without waiting for the response), then you will
25/// get multiple `Request` objects simultaneously. This is called *requests pipelining*.
26/// Tiny-http automatically reorders the responses so that you don't need to worry about the order
27/// in which you call `respond` or `into_writer`.
28///
29/// This mechanic is disabled if:
30///
31///  - The body of a request is large enough (handling requires pipelining requires storing the
32///    body of the request in a buffer ; if the body is too big, tiny-http will avoid doing that)
33///  - A request sends a `Expect: 100-continue` header (which means that the client waits to
34///    know whether its body will be processed before sending it)
35///  - A request sends a `Connection: close` header or `Connection: upgrade` header (used for
36///    websockets), which indicates that this is the last request that will be received on this
37///    connection
38///
39/// # Automatic cleanup
40///
41/// If a `Request` object is destroyed without `into_writer` or `respond` being called,
42/// an empty response with a 500 status code (internal server error) will automatically be
43/// sent back to the client.
44/// This means that if your code fails during the handling of a request, this "internal server
45/// error" response will automatically be sent during the stack unwinding.
46///
47/// # Testing
48///
49/// If you want to build fake requests to test your server, use [`TestRequest`](crate::test::TestRequest).
50pub struct Request {
51    // where to read the body from
52    data_reader: Option<Box<dyn Read + Send + 'static>>,
53
54    // if this writer is empty, then the request has been answered
55    response_writer: Option<Box<dyn Write + Send + 'static>>,
56
57    remote_addr: Option<SocketAddr>,
58
59    // true if HTTPS, false if HTTP
60    secure: bool,
61
62    method: Method,
63
64    path: String,
65
66    http_version: HTTPVersion,
67
68    headers: Vec<Header>,
69
70    body_length: Option<usize>,
71
72    // true if a `100 Continue` response must be sent when `as_reader()` is called
73    must_send_continue: bool,
74
75    // If Some, a message must be sent after responding
76    notify_when_responded: Option<Sender<()>>,
77}
78
79struct NotifyOnDrop<R> {
80    sender: Sender<()>,
81    inner: R,
82}
83
84impl<R: Read> Read for NotifyOnDrop<R> {
85    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
86        self.inner.read(buf)
87    }
88}
89impl<R: Write> Write for NotifyOnDrop<R> {
90    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
91        self.inner.write(buf)
92    }
93    fn flush(&mut self) -> io::Result<()> {
94        self.inner.flush()
95    }
96}
97impl<R> Drop for NotifyOnDrop<R> {
98    fn drop(&mut self) {
99        self.sender.send(()).unwrap();
100    }
101}
102
103/// Error that can happen when building a `Request` object.
104#[derive(Debug)]
105pub enum RequestCreationError {
106    /// The client sent an `Expect` header that was not recognized by tiny-http.
107    ExpectationFailed,
108
109    /// Error while reading data from the socket during the creation of the `Request`.
110    CreationIoError(IoError),
111}
112
113impl From<IoError> for RequestCreationError {
114    fn from(err: IoError) -> RequestCreationError {
115        RequestCreationError::CreationIoError(err)
116    }
117}
118
119/// Builds a new request.
120///
121/// After the request line and headers have been read from the socket, a new `Request` object
122/// is built.
123///
124/// You must pass a `Read` that will allow the `Request` object to read from the incoming data.
125/// It is the responsibility of the `Request` to read only the data of the request and not further.
126///
127/// The `Write` object will be used by the `Request` to write the response.
128#[allow(clippy::too_many_arguments)]
129pub fn new_request<R, W>(
130    secure: bool,
131    method: Method,
132    path: String,
133    version: HTTPVersion,
134    headers: Vec<Header>,
135    remote_addr: Option<SocketAddr>,
136    mut source_data: R,
137    writer: W,
138) -> Result<Request, RequestCreationError>
139where
140    R: Read + Send + 'static,
141    W: Write + Send + 'static,
142{
143    // finding the transfer-encoding header
144    let transfer_encoding = headers
145        .iter()
146        .find(|h: &&Header| h.field.equiv("Transfer-Encoding"))
147        .map(|h| h.value.clone());
148
149    // finding the content-length header
150    let content_length = if transfer_encoding.is_some() {
151        // if transfer-encoding is specified, the Content-Length
152        // header must be ignored (RFC2616 #4.4)
153        None
154    } else {
155        headers
156            .iter()
157            .find(|h: &&Header| h.field.equiv("Content-Length"))
158            .and_then(|h| FromStr::from_str(h.value.as_str()).ok())
159    };
160
161    // true if the client sent a `Expect: 100-continue` header
162    let expects_continue = {
163        match headers
164            .iter()
165            .find(|h: &&Header| h.field.equiv("Expect"))
166            .map(|h| h.value.as_str())
167        {
168            None => false,
169            Some(v) if v.eq_ignore_ascii_case("100-continue") => true,
170            _ => return Err(RequestCreationError::ExpectationFailed),
171        }
172    };
173
174    // true if the client sent a `Connection: upgrade` header
175    let connection_upgrade = {
176        match headers
177            .iter()
178            .find(|h: &&Header| h.field.equiv("Connection"))
179            .map(|h| h.value.as_str())
180        {
181            Some(v) if v.to_ascii_lowercase().contains("upgrade") => true,
182            _ => false,
183        }
184    };
185
186    // we wrap `source_data` around a reading whose nature depends on the transfer-encoding and
187    // content-length headers
188    let reader = if connection_upgrade {
189        // if we have a `Connection: upgrade`, always keeping the whole reader
190        Box::new(source_data) as Box<dyn Read + Send + 'static>
191    } else if let Some(content_length) = content_length {
192        if content_length == 0 {
193            Box::new(io::empty()) as Box<dyn Read + Send + 'static>
194        } else if content_length <= 1024 && !expects_continue {
195            // if the content-length is small enough, we just read everything into a buffer
196
197            let mut buffer = vec![0; content_length];
198            let mut offset = 0;
199
200            while offset != content_length {
201                let read = source_data.read(&mut buffer[offset..])?;
202                if read == 0 {
203                    // the socket returned EOF, but we were before the expected content-length
204                    // aborting
205                    let info = "Connection has been closed before we received enough data";
206                    let err = IoError::new(ErrorKind::ConnectionAborted, info);
207                    return Err(RequestCreationError::CreationIoError(err));
208                }
209
210                offset += read;
211            }
212
213            Box::new(Cursor::new(buffer)) as Box<dyn Read + Send + 'static>
214        } else {
215            let (data_reader, _) = EqualReader::new(source_data, content_length); // TODO:
216            Box::new(FusedReader::new(data_reader)) as Box<dyn Read + Send + 'static>
217        }
218    } else if transfer_encoding.is_some() {
219        // if a transfer-encoding was specified, then "chunked" is ALWAYS applied
220        // over the message (RFC2616 #3.6)
221        Box::new(FusedReader::new(Decoder::new(source_data))) as Box<dyn Read + Send + 'static>
222    } else {
223        // if we have neither a Content-Length nor a Transfer-Encoding,
224        // assuming that we have no data
225        // TODO: could also be multipart/byteranges
226        Box::new(io::empty()) as Box<dyn Read + Send + 'static>
227    };
228
229    Ok(Request {
230        data_reader: Some(reader),
231        response_writer: Some(Box::new(writer) as Box<dyn Write + Send + 'static>),
232        remote_addr,
233        secure,
234        method,
235        path,
236        http_version: version,
237        headers,
238        body_length: content_length,
239        must_send_continue: expects_continue,
240        notify_when_responded: None,
241    })
242}
243
244impl Request {
245    /// Returns true if the request was made through HTTPS.
246    #[inline]
247    pub fn secure(&self) -> bool {
248        self.secure
249    }
250
251    /// Returns the method requested by the client (eg. `GET`, `POST`, etc.).
252    #[inline]
253    pub fn method(&self) -> &Method {
254        &self.method
255    }
256
257    /// Returns the resource requested by the client.
258    #[inline]
259    pub fn url(&self) -> &str {
260        &self.path
261    }
262
263    /// Returns a list of all headers sent by the client.
264    #[inline]
265    pub fn headers(&self) -> &[Header] {
266        &self.headers
267    }
268
269    /// Returns the HTTP version of the request.
270    #[inline]
271    pub fn http_version(&self) -> &HTTPVersion {
272        &self.http_version
273    }
274
275    /// Returns the length of the body in bytes.
276    ///
277    /// Returns `None` if the length is unknown.
278    #[inline]
279    pub fn body_length(&self) -> Option<usize> {
280        self.body_length
281    }
282
283    /// Returns the address of the client that sent this request.
284    ///
285    /// The address is always `Some` for TCP listeners, but always `None` for UNIX listeners
286    /// (as the remote address of a UNIX client is almost always unnamed).
287    ///
288    /// Note that this is gathered from the socket. If you receive the request from a proxy,
289    /// this function will return the address of the proxy and not the address of the actual
290    /// user.
291    #[inline]
292    pub fn remote_addr(&self) -> Option<&SocketAddr> {
293        self.remote_addr.as_ref()
294    }
295
296    /// Sends a response with a `Connection: upgrade` header, then turns the `Request` into a `Stream`.
297    ///
298    /// The main purpose of this function is to support websockets.
299    /// If you detect that the request wants to use some kind of protocol upgrade, you can
300    ///  call this function to obtain full control of the socket stream.
301    ///
302    /// If you call this on a non-websocket request, tiny-http will wait until this `Stream` object
303    ///  is destroyed before continuing to read or write on the socket. Therefore you should always
304    ///  destroy it as soon as possible.
305    pub fn upgrade<R: Read>(
306        mut self,
307        protocol: &str,
308        response: Response<R>,
309    ) -> Box<dyn ReadWrite + Send> {
310        use crate::util::CustomStream;
311
312        response
313            .raw_print(
314                self.response_writer.as_mut().unwrap().by_ref(),
315                self.http_version.clone(),
316                &self.headers,
317                false,
318                Some(protocol),
319            )
320            .ok(); // TODO: unused result
321
322        self.response_writer.as_mut().unwrap().flush().ok(); // TODO: unused result
323
324        let stream = CustomStream::new(self.extract_reader_impl(), self.extract_writer_impl());
325        if let Some(sender) = self.notify_when_responded.take() {
326            let stream = NotifyOnDrop {
327                sender,
328                inner: stream,
329            };
330            Box::new(stream) as Box<dyn ReadWrite + Send>
331        } else {
332            Box::new(stream) as Box<dyn ReadWrite + Send>
333        }
334    }
335
336    /// Allows to read the body of the request.
337    ///
338    /// # Example
339    ///
340    /// ```no_run
341    /// # extern crate rustc_serialize;
342    /// # extern crate tiny_http;
343    /// # use rustc_serialize::json::Json;
344    /// # use std::io::Read;
345    /// # fn get_content_type(_: &tiny_http::Request) -> &'static str { "" }
346    /// # fn main() {
347    /// # let server = tiny_http::Server::http("0.0.0.0:0").unwrap();
348    /// let mut request = server.recv().unwrap();
349    ///
350    /// if get_content_type(&request) == "application/json" {
351    ///     let mut content = String::new();
352    ///     request.as_reader().read_to_string(&mut content).unwrap();
353    ///     let json: Json = content.parse().unwrap();
354    /// }
355    /// # }
356    /// ```
357    ///
358    /// If the client sent a `Expect: 100-continue` header with the request, calling this
359    ///  function will send back a `100 Continue` response.
360    #[inline]
361    pub fn as_reader(&mut self) -> &mut dyn Read {
362        if self.must_send_continue {
363            let msg = Response::new_empty(StatusCode(100));
364            msg.raw_print(
365                self.response_writer.as_mut().unwrap().by_ref(),
366                self.http_version.clone(),
367                &self.headers,
368                true,
369                None,
370            )
371            .ok();
372            self.response_writer.as_mut().unwrap().flush().ok();
373            self.must_send_continue = false;
374        }
375
376        self.data_reader.as_mut().unwrap()
377    }
378
379    /// Turns the `Request` into a writer.
380    ///
381    /// The writer has a raw access to the stream to the user.
382    /// This function is useful for things like CGI.
383    ///
384    /// Note that the destruction of the `Writer` object may trigger
385    /// some events. For exemple if a client has sent multiple requests and the requests
386    /// have been processed in parallel, the destruction of a writer will trigger
387    /// the writing of the next response.
388    /// Therefore you should always destroy the `Writer` as soon as possible.
389    #[inline]
390    pub fn into_writer(mut self) -> Box<dyn Write + Send + 'static> {
391        let writer = self.extract_writer_impl();
392        if let Some(sender) = self.notify_when_responded.take() {
393            let writer = NotifyOnDrop {
394                sender,
395                inner: writer,
396            };
397            Box::new(writer) as Box<dyn Write + Send + 'static>
398        } else {
399            writer
400        }
401    }
402
403    /// Extract the response `Writer` object from the Request, dropping this `Writer` has the same side effects
404    /// as the object returned by `into_writer` above.
405    ///
406    /// This may only be called once on a single request.
407    fn extract_writer_impl(&mut self) -> Box<dyn Write + Send + 'static> {
408        use std::mem;
409
410        assert!(self.response_writer.is_some());
411
412        let mut writer = None;
413        mem::swap(&mut self.response_writer, &mut writer);
414        writer.unwrap()
415    }
416
417    /// Extract the body `Reader` object from the Request.
418    ///
419    /// This may only be called once on a single request.
420    fn extract_reader_impl(&mut self) -> Box<dyn Read + Send + 'static> {
421        use std::mem;
422
423        assert!(self.data_reader.is_some());
424
425        let mut reader = None;
426        mem::swap(&mut self.data_reader, &mut reader);
427        reader.unwrap()
428    }
429
430    /// Sends a response to this request.
431    #[inline]
432    pub fn respond<R>(mut self, response: Response<R>) -> Result<(), IoError>
433    where
434        R: Read,
435    {
436        let res = self.respond_impl(response);
437        if let Some(sender) = self.notify_when_responded.take() {
438            sender.send(()).unwrap();
439        }
440        res
441    }
442
443    fn respond_impl<R>(&mut self, response: Response<R>) -> Result<(), IoError>
444    where
445        R: Read,
446    {
447        let mut writer = self.extract_writer_impl();
448
449        let do_not_send_body = self.method == Method::Head;
450
451        Self::ignore_client_closing_errors(response.raw_print(
452            writer.by_ref(),
453            self.http_version.clone(),
454            &self.headers,
455            do_not_send_body,
456            None,
457        ))?;
458
459        Self::ignore_client_closing_errors(writer.flush())
460    }
461
462    fn ignore_client_closing_errors(result: io::Result<()>) -> io::Result<()> {
463        result.or_else(|err| match err.kind() {
464            ErrorKind::BrokenPipe => Ok(()),
465            ErrorKind::ConnectionAborted => Ok(()),
466            ErrorKind::ConnectionRefused => Ok(()),
467            ErrorKind::ConnectionReset => Ok(()),
468            _ => Err(err),
469        })
470    }
471
472    pub(crate) fn with_notify_sender(mut self, sender: Sender<()>) -> Self {
473        self.notify_when_responded = Some(sender);
474        self
475    }
476}
477
478impl fmt::Debug for Request {
479    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
480        write!(
481            formatter,
482            "Request({} {} from {:?})",
483            self.method, self.path, self.remote_addr
484        )
485    }
486}
487
488impl Drop for Request {
489    fn drop(&mut self) {
490        if self.response_writer.is_some() {
491            let response = Response::empty(500);
492            let _ = self.respond_impl(response); // ignoring any potential error
493            if let Some(sender) = self.notify_when_responded.take() {
494                sender.send(()).unwrap();
495            }
496        }
497    }
498}
499
500/// Dummy trait that regroups the `Read` and `Write` traits.
501///
502/// Automatically implemented on all types that implement both `Read` and `Write`.
503pub trait ReadWrite: Read + Write {}
504impl<T> ReadWrite for T where T: Read + Write {}
505
506#[cfg(test)]
507mod tests {
508    use super::Request;
509
510    #[test]
511    fn must_be_send() {
512        #![allow(dead_code)]
513        fn f<T: Send>(_: &T) {}
514        fn bar(rq: &Request) {
515            f(rq);
516        }
517    }
518}