tiny_http_sccache/
response.rs

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