tiny_http/
response.rs

1use crate::common::{HTTPVersion, Header, StatusCode};
2use httpdate::HttpDate;
3use std::cmp::Ordering;
4use std::sync::mpsc::Receiver;
5
6use std::io::Result as IoResult;
7use std::io::{self, Cursor, Read, Write};
8
9use std::fs::File;
10
11use std::str::FromStr;
12use std::time::SystemTime;
13
14/// Object representing an HTTP response whose purpose is to be given to a `Request`.
15///
16/// Some headers cannot be changed. Trying to define the value
17/// of one of these will have no effect:
18///
19///  - `Connection`
20///  - `Trailer`
21///  - `Transfer-Encoding`
22///  - `Upgrade`
23///
24/// Some headers have special behaviors:
25///
26///  - `Content-Encoding`: If you define this header, the library
27///     will assume that the data from the `Read` object has the specified encoding
28///     and will just pass-through.
29///
30///  - `Content-Length`: The length of the data should be set manually
31///     using the `Reponse` object's API. Attempting to set the value of this
32///     header will be equivalent to modifying the size of the data but the header
33///     itself may not be present in the final result.
34///
35///  - `Content-Type`: You may only set this header to one value at a time. If you
36///     try to set it more than once, the existing value will be overwritten. This
37///     behavior differs from the default for most headers, which is to allow them to
38///     be set multiple times in the same response.
39///
40pub struct Response<R> {
41    reader: R,
42    status_code: StatusCode,
43    headers: Vec<Header>,
44    data_length: Option<usize>,
45    chunked_threshold: Option<usize>,
46}
47
48/// A `Response` without a template parameter.
49pub type ResponseBox = Response<Box<dyn Read + Send>>;
50
51/// Transfer encoding to use when sending the message.
52/// Note that only *supported* encoding are listed here.
53#[derive(Copy, Clone)]
54enum TransferEncoding {
55    Identity,
56    Chunked,
57}
58
59impl FromStr for TransferEncoding {
60    type Err = ();
61
62    fn from_str(input: &str) -> Result<TransferEncoding, ()> {
63        if input.eq_ignore_ascii_case("identity") {
64            Ok(TransferEncoding::Identity)
65        } else if input.eq_ignore_ascii_case("chunked") {
66            Ok(TransferEncoding::Chunked)
67        } else {
68            Err(())
69        }
70    }
71}
72
73/// Builds a Date: header with the current date.
74fn build_date_header() -> Header {
75    let d = HttpDate::from(SystemTime::now());
76    Header::from_bytes(&b"Date"[..], &d.to_string().into_bytes()[..]).unwrap()
77}
78
79fn write_message_header<W>(
80    mut writer: W,
81    http_version: &HTTPVersion,
82    status_code: &StatusCode,
83    headers: &[Header],
84) -> IoResult<()>
85where
86    W: Write,
87{
88    // writing status line
89    write!(
90        &mut writer,
91        "HTTP/{}.{} {} {}\r\n",
92        http_version.0,
93        http_version.1,
94        status_code.0,
95        status_code.default_reason_phrase()
96    )?;
97
98    // writing headers
99    for header in headers.iter() {
100        writer.write_all(header.field.as_str().as_ref())?;
101        write!(&mut writer, ": ")?;
102        writer.write_all(header.value.as_str().as_ref())?;
103        write!(&mut writer, "\r\n")?;
104    }
105
106    // separator between header and data
107    write!(&mut writer, "\r\n")?;
108
109    Ok(())
110}
111
112fn choose_transfer_encoding(
113    status_code: StatusCode,
114    request_headers: &[Header],
115    http_version: &HTTPVersion,
116    entity_length: &Option<usize>,
117    has_additional_headers: bool,
118    chunked_threshold: usize,
119) -> TransferEncoding {
120    use crate::util;
121
122    // HTTP 1.0 doesn't support other encoding
123    if *http_version <= (1, 0) {
124        return TransferEncoding::Identity;
125    }
126
127    // Per section 3.3.1 of RFC7230:
128    // A server MUST NOT send a Transfer-Encoding header field in any response with a status code
129    // of 1xx (Informational) or 204 (No Content).
130    if status_code.0 < 200 || status_code.0 == 204 {
131        return TransferEncoding::Identity;
132    }
133
134    // parsing the request's TE header
135    let user_request = request_headers
136        .iter()
137        // finding TE
138        .find(|h| h.field.equiv("TE"))
139        // getting its value
140        .map(|h| h.value.clone())
141        // getting the corresponding TransferEncoding
142        .and_then(|value| {
143            // getting list of requested elements
144            let mut parse = util::parse_header_value(value.as_str()); // TODO: remove conversion
145
146            // sorting elements by most priority
147            parse.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));
148
149            // trying to parse each requested encoding
150            for value in parse.iter() {
151                // q=0 are ignored
152                if value.1 <= 0.0 {
153                    continue;
154                }
155
156                if let Ok(te) = TransferEncoding::from_str(value.0) {
157                    return Some(te);
158                }
159            }
160
161            // encoding not found
162            None
163        });
164
165    if let Some(user_request) = user_request {
166        return user_request;
167    }
168
169    // if we have additional headers, using chunked
170    if has_additional_headers {
171        return TransferEncoding::Chunked;
172    }
173
174    // if we don't have a Content-Length, or if the Content-Length is too big, using chunks writer
175    if entity_length
176        .as_ref()
177        .map_or(true, |val| *val >= chunked_threshold)
178    {
179        return TransferEncoding::Chunked;
180    }
181
182    // Identity by default
183    TransferEncoding::Identity
184}
185
186impl<R> Response<R>
187where
188    R: Read,
189{
190    /// Creates a new Response object.
191    ///
192    /// The `additional_headers` argument is a receiver that
193    ///  may provide headers even after the response has been sent.
194    ///
195    /// All the other arguments are straight-forward.
196    pub fn new(
197        status_code: StatusCode,
198        headers: Vec<Header>,
199        data: R,
200        data_length: Option<usize>,
201        additional_headers: Option<Receiver<Header>>,
202    ) -> Response<R> {
203        let mut response = Response {
204            reader: data,
205            status_code,
206            headers: Vec::with_capacity(16),
207            data_length,
208            chunked_threshold: None,
209        };
210
211        for h in headers {
212            response.add_header(h)
213        }
214
215        // dummy implementation
216        if let Some(additional_headers) = additional_headers {
217            for h in additional_headers.iter() {
218                response.add_header(h)
219            }
220        }
221
222        response
223    }
224
225    /// Set a threshold for `Content-Length` where we chose chunked
226    /// transfer. Notice that chunked transfer might happen regardless of
227    /// this threshold, for instance when the request headers indicate
228    /// it is wanted or when there is no `Content-Length`.
229    pub fn with_chunked_threshold(mut self, length: usize) -> Response<R> {
230        self.chunked_threshold = Some(length);
231        self
232    }
233
234    /// Convert the response into the underlying `Read` type.
235    ///
236    /// This is mainly useful for testing as it must consume the `Response`.
237    pub fn into_reader(self) -> R {
238        self.reader
239    }
240
241    /// The current `Content-Length` threshold for switching over to
242    /// chunked transfer. The default is 32768 bytes. Notice that
243    /// chunked transfer is mutually exclusive with sending a
244    /// `Content-Length` header as per the HTTP spec.
245    pub fn chunked_threshold(&self) -> usize {
246        self.chunked_threshold.unwrap_or(32768)
247    }
248
249    /// Adds a header to the list.
250    /// Does all the checks.
251    pub fn add_header<H>(&mut self, header: H)
252    where
253        H: Into<Header>,
254    {
255        let header = header.into();
256
257        // ignoring forbidden headers
258        if header.field.equiv("Connection")
259            || header.field.equiv("Trailer")
260            || header.field.equiv("Transfer-Encoding")
261            || header.field.equiv("Upgrade")
262        {
263            return;
264        }
265
266        // if the header is Content-Length, setting the data length
267        if header.field.equiv("Content-Length") {
268            if let Ok(val) = usize::from_str(header.value.as_str()) {
269                self.data_length = Some(val)
270            }
271
272            return;
273        // if the header is Content-Type and it's already set, overwrite it
274        } else if header.field.equiv("Content-Type") {
275            if let Some(content_type_header) = self
276                .headers
277                .iter_mut()
278                .find(|h| h.field.equiv("Content-Type"))
279            {
280                content_type_header.value = header.value;
281                return;
282            }
283        }
284
285        self.headers.push(header);
286    }
287
288    /// Returns the same request, but with an additional header.
289    ///
290    /// Some headers cannot be modified and some other have a
291    ///  special behavior. See the documentation above.
292    #[inline]
293    pub fn with_header<H>(mut self, header: H) -> Response<R>
294    where
295        H: Into<Header>,
296    {
297        self.add_header(header.into());
298        self
299    }
300
301    /// Returns the same request, but with a different status code.
302    #[inline]
303    pub fn with_status_code<S>(mut self, code: S) -> Response<R>
304    where
305        S: Into<StatusCode>,
306    {
307        self.status_code = code.into();
308        self
309    }
310
311    /// Returns the same request, but with different data.
312    pub fn with_data<S>(self, reader: S, data_length: Option<usize>) -> Response<S>
313    where
314        S: Read,
315    {
316        Response {
317            reader,
318            headers: self.headers,
319            status_code: self.status_code,
320            data_length,
321            chunked_threshold: self.chunked_threshold,
322        }
323    }
324
325    /// Prints the HTTP response to a writer.
326    ///
327    /// This function is the one used to send the response to the client's socket.
328    /// Therefore you shouldn't expect anything pretty-printed or even readable.
329    ///
330    /// The HTTP version and headers passed as arguments are used to
331    ///  decide which features (most notably, encoding) to use.
332    ///
333    /// Note: does not flush the writer.
334    pub fn raw_print<W: Write>(
335        mut self,
336        mut writer: W,
337        http_version: HTTPVersion,
338        request_headers: &[Header],
339        do_not_send_body: bool,
340        upgrade: Option<&str>,
341    ) -> IoResult<()> {
342        let mut transfer_encoding = Some(choose_transfer_encoding(
343            self.status_code,
344            request_headers,
345            &http_version,
346            &self.data_length,
347            false, /* TODO */
348            self.chunked_threshold(),
349        ));
350
351        // add `Date` if not in the headers
352        if !self.headers.iter().any(|h| h.field.equiv("Date")) {
353            self.headers.insert(0, build_date_header());
354        }
355
356        // add `Server` if not in the headers
357        if !self.headers.iter().any(|h| h.field.equiv("Server")) {
358            self.headers.insert(
359                0,
360                Header::from_bytes(&b"Server"[..], &b"tiny-http (Rust)"[..]).unwrap(),
361            );
362        }
363
364        // handling upgrade
365        if let Some(upgrade) = upgrade {
366            self.headers.insert(
367                0,
368                Header::from_bytes(&b"Upgrade"[..], upgrade.as_bytes()).unwrap(),
369            );
370            self.headers.insert(
371                0,
372                Header::from_bytes(&b"Connection"[..], &b"upgrade"[..]).unwrap(),
373            );
374            transfer_encoding = None;
375        }
376
377        // if the transfer encoding is identity, the content length must be known ; therefore if
378        // we don't know it, we buffer the entire response first here
379        // while this is an expensive operation, it is only ever needed for clients using HTTP 1.0
380        let (mut reader, data_length): (Box<dyn Read>, _) =
381            match (self.data_length, transfer_encoding) {
382                (Some(l), _) => (Box::new(self.reader), Some(l)),
383                (None, Some(TransferEncoding::Identity)) => {
384                    let mut buf = Vec::new();
385                    self.reader.read_to_end(&mut buf)?;
386                    let l = buf.len();
387                    (Box::new(Cursor::new(buf)), Some(l))
388                }
389                _ => (Box::new(self.reader), None),
390            };
391
392        // checking whether to ignore the body of the response
393        let do_not_send_body = do_not_send_body
394            || match self.status_code.0 {
395                // status code 1xx, 204 and 304 MUST not include a body
396                100..=199 | 204 | 304 => true,
397                _ => false,
398            };
399
400        // preparing headers for transfer
401        match transfer_encoding {
402            Some(TransferEncoding::Chunked) => self
403                .headers
404                .push(Header::from_bytes(&b"Transfer-Encoding"[..], &b"chunked"[..]).unwrap()),
405
406            Some(TransferEncoding::Identity) => {
407                assert!(data_length.is_some());
408                let data_length = data_length.unwrap();
409
410                self.headers.push(
411                    Header::from_bytes(
412                        &b"Content-Length"[..],
413                        format!("{}", data_length).as_bytes(),
414                    )
415                    .unwrap(),
416                )
417            }
418
419            _ => (),
420        };
421
422        // sending headers
423        write_message_header(
424            writer.by_ref(),
425            &http_version,
426            &self.status_code,
427            &self.headers,
428        )?;
429
430        // sending the body
431        if !do_not_send_body {
432            match transfer_encoding {
433                Some(TransferEncoding::Chunked) => {
434                    use chunked_transfer::Encoder;
435
436                    let mut writer = Encoder::new(writer);
437                    io::copy(&mut reader, &mut writer)?;
438                }
439
440                Some(TransferEncoding::Identity) => {
441                    assert!(data_length.is_some());
442                    let data_length = data_length.unwrap();
443
444                    if data_length >= 1 {
445                        io::copy(&mut reader, &mut writer)?;
446                    }
447                }
448
449                _ => (),
450            }
451        }
452
453        Ok(())
454    }
455
456    /// Retrieves the current value of the `Response` status code
457    pub fn status_code(&self) -> StatusCode {
458        self.status_code
459    }
460
461    /// Retrieves the current value of the `Response` data length
462    pub fn data_length(&self) -> Option<usize> {
463        self.data_length
464    }
465
466    /// Retrieves the current list of `Response` headers
467    pub fn headers(&self) -> &[Header] {
468        &self.headers
469    }
470}
471
472impl<R> Response<R>
473where
474    R: Read + Send + 'static,
475{
476    /// Turns this response into a `Response<Box<Read + Send>>`.
477    pub fn boxed(self) -> ResponseBox {
478        Response {
479            reader: Box::new(self.reader) as Box<dyn Read + Send>,
480            status_code: self.status_code,
481            headers: self.headers,
482            data_length: self.data_length,
483            chunked_threshold: self.chunked_threshold,
484        }
485    }
486}
487
488impl Response<File> {
489    /// Builds a new `Response` from a `File`.
490    ///
491    /// The `Content-Type` will **not** be automatically detected,
492    ///  you must set it yourself.
493    pub fn from_file(file: File) -> Response<File> {
494        let file_size = file.metadata().ok().map(|v| v.len() as usize);
495
496        Response::new(
497            StatusCode(200),
498            Vec::with_capacity(0),
499            file,
500            file_size,
501            None,
502        )
503    }
504}
505
506impl Response<Cursor<Vec<u8>>> {
507    pub fn from_data<D>(data: D) -> Response<Cursor<Vec<u8>>>
508    where
509        D: Into<Vec<u8>>,
510    {
511        let data = data.into();
512        let data_len = data.len();
513
514        Response::new(
515            StatusCode(200),
516            Vec::with_capacity(0),
517            Cursor::new(data),
518            Some(data_len),
519            None,
520        )
521    }
522
523    pub fn from_string<S>(data: S) -> Response<Cursor<Vec<u8>>>
524    where
525        S: Into<String>,
526    {
527        let data = data.into();
528        let data_len = data.len();
529
530        Response::new(
531            StatusCode(200),
532            vec![
533                Header::from_bytes(&b"Content-Type"[..], &b"text/plain; charset=UTF-8"[..])
534                    .unwrap(),
535            ],
536            Cursor::new(data.into_bytes()),
537            Some(data_len),
538            None,
539        )
540    }
541}
542
543impl Response<io::Empty> {
544    /// Builds an empty `Response` with the given status code.
545    pub fn empty<S>(status_code: S) -> Response<io::Empty>
546    where
547        S: Into<StatusCode>,
548    {
549        Response::new(
550            status_code.into(),
551            Vec::with_capacity(0),
552            io::empty(),
553            Some(0),
554            None,
555        )
556    }
557
558    /// DEPRECATED. Use `empty` instead.
559    pub fn new_empty(status_code: StatusCode) -> Response<io::Empty> {
560        Response::empty(status_code)
561    }
562}
563
564impl Clone for Response<io::Empty> {
565    fn clone(&self) -> Response<io::Empty> {
566        Response {
567            reader: io::empty(),
568            status_code: self.status_code,
569            headers: self.headers.clone(),
570            data_length: self.data_length,
571            chunked_threshold: self.chunked_threshold,
572        }
573    }
574}