mco_http/server/
response.rs

1//! Server Responses
2//!
3//! These are responses sent by a `mco_http::Server` to clients, after
4//! receiving a request.
5use std::any::{Any, TypeId};
6use std::marker::PhantomData;
7use std::mem;
8use std::io::{self, Write};
9use std::ptr;
10use std::thread;
11
12use time::now_utc;
13
14use crate::header;
15use crate::http::h1::{LINE_ENDING, HttpWriter};
16use crate::http::h1::HttpWriter::{ThroughWriter, ChunkedWriter, SizedWriter, EmptyWriter};
17use crate::status;
18use crate::net::{Fresh, Streaming};
19use crate::version;
20
21
22/// The outgoing half for a Tcp connection, created by a `Server` and given to a `Handler`.
23///
24/// The default `StatusCode` for a `Response` is `200 OK`.
25///
26/// There is a `Drop` implementation for `Response` that will automatically
27/// write the head and flush the body, if the handler has not already done so,
28/// so that the server doesn't accidentally leave dangling requests.
29#[derive(Debug)]
30pub struct Response<'a, W: Any = Fresh> {
31    /// The HTTP version of this response.
32    pub version: version::HttpVersion,
33    // Stream the Response is writing to, not accessible through UnwrittenResponse
34    pub body: HttpWriter<&'a mut (dyn Write + 'a)>,
35    // The status code for the request.
36    pub status: status::StatusCode,
37    // The outgoing headers on this response.
38    pub headers: &'a mut header::Headers,
39
40    _writing: PhantomData<W>
41}
42
43impl<'a, W: Any> Response<'a, W> {
44    /// The status of this response.
45    #[inline]
46    pub fn status(&self) -> status::StatusCode { self.status }
47
48    /// The headers of this response.
49    #[inline]
50    pub fn headers(&self) -> &header::Headers { &*self.headers }
51
52    /// Construct a Response from its constituent parts.
53    #[inline]
54    pub fn construct(version: version::HttpVersion,
55                     body: HttpWriter<&'a mut (dyn Write + 'a)>,
56                     status: status::StatusCode,
57                     headers: &'a mut header::Headers) -> Response<'a, Fresh> {
58        Response {
59            status: status,
60            version: version,
61            body: body,
62            headers: headers,
63            _writing: PhantomData,
64        }
65    }
66
67    /// Deconstruct this Response into its constituent parts.
68    #[inline]
69    pub fn deconstruct(self) -> (version::HttpVersion, HttpWriter<&'a mut (dyn Write + 'a)>,
70                                 status::StatusCode, &'a mut header::Headers) {
71        unsafe {
72            let parts = (
73                self.version,
74                ptr::read(&self.body),
75                self.status,
76                ptr::read(&self.headers)
77            );
78            mem::forget(self);
79            parts
80        }
81    }
82
83    fn write_head(&mut self) -> io::Result<Body> {
84        debug!("writing head: {:?} {:?}", self.version, self.status);
85        write!(&mut self.body, "{} {}\r\n", self.version, self.status)?;
86
87        if !self.headers.has::<header::Date>() {
88            self.headers.set(header::Date(header::HttpDate(now_utc())));
89        }
90
91        let body_type = match self.status {
92            status::StatusCode::NoContent | status::StatusCode::NotModified => Body::Empty,
93            c if c.class() == status::StatusClass::Informational => Body::Empty,
94            _ => if let Some(cl) = self.headers.get::<header::ContentLength>() {
95                Body::Sized(**cl)
96            } else {
97                Body::Chunked
98            }
99        };
100
101        // can't do in match above, thanks borrowck
102        if body_type == Body::Chunked {
103            let encodings = match self.headers.get_mut::<header::TransferEncoding>() {
104                Some(&mut header::TransferEncoding(ref mut encodings)) => {
105                    //TODO: check if chunked is already in encodings. use HashSet?
106                    encodings.push(header::Encoding::Chunked);
107                    false
108                },
109                None => true
110            };
111
112            if encodings {
113                self.headers.set::<header::TransferEncoding>(
114                    header::TransferEncoding(vec![header::Encoding::Chunked]))
115            }
116        }
117
118
119        debug!("headers [\n{:?}]", self.headers);
120        write!(&mut self.body, "{}", self.headers)?;
121        write!(&mut self.body, "{}", LINE_ENDING)?;
122
123        Ok(body_type)
124    }
125}
126
127impl<'a> Response<'a, Fresh> {
128    /// Creates a new Response that can be used to write to a network stream.
129    #[inline]
130    pub fn new(stream: &'a mut (dyn Write + 'a), headers: &'a mut header::Headers) ->
131            Response<'a, Fresh> {
132        Response {
133            status: status::StatusCode::Ok,
134            version: version::HttpVersion::Http11,
135            headers: headers,
136            body: ThroughWriter(stream),
137            _writing: PhantomData,
138        }
139    }
140
141    /// Writes the body and ends the response.
142    ///
143    /// This is a shortcut method for when you have a response with a fixed
144    /// size, and would only need a single `write` call normally.
145    ///
146    /// # Example
147    ///
148    /// ```
149    /// # use mco_http::server::Response;
150    /// fn handler(res: Response) {
151    ///     res.send(b"Hello World!").unwrap();
152    /// }
153    /// ```
154    ///
155    /// The above is the same, but shorter, than the longer:
156    ///
157    /// ```
158    /// # use mco_http::server::Response;
159    /// use std::io::Write;
160    /// use mco_http::header::ContentLength;
161    /// fn handler(mut res: Response) {
162    ///     let body = b"Hello World!";
163    ///     res.headers_mut().set(ContentLength(body.len() as u64));
164    ///     let mut res = res.start().unwrap();
165    ///     res.write_all(body).unwrap();
166    /// }
167    /// ```
168    #[inline]
169    pub fn send(self, body: &[u8]) -> io::Result<()> {
170        self.headers.set(header::ContentLength(body.len() as u64));
171        let mut stream = self.start()?;
172        stream.write_all(body)?;
173        stream.end()
174    }
175
176    /// Consume this Response<Fresh>, writing the Headers and Status and
177    /// creating a Response<Streaming>
178    pub fn start(mut self) -> io::Result<Response<'a, Streaming>> {
179        let body_type = self.write_head()?;
180        let (version, body, status, headers) = self.deconstruct();
181        let stream = match body_type {
182            Body::Chunked => ChunkedWriter(body.into_inner()),
183            Body::Sized(len) => SizedWriter(body.into_inner(), len),
184            Body::Empty => EmptyWriter(body.into_inner()),
185        };
186
187        // "copy" to change the phantom type
188        Ok(Response {
189            version: version,
190            body: stream,
191            status: status,
192            headers: headers,
193            _writing: PhantomData,
194        })
195    }
196    /// Get a mutable reference to the status.
197    #[inline]
198    pub fn status_mut(&mut self) -> &mut status::StatusCode { &mut self.status }
199
200    /// Get a mutable reference to the Headers.
201    #[inline]
202    pub fn headers_mut(&mut self) -> &mut header::Headers { self.headers }
203}
204
205
206impl<'a> Response<'a, Streaming> {
207    /// Flushes all writing of a response to the client.
208    #[inline]
209    pub fn end(self) -> io::Result<()> {
210        trace!("ending");
211        let (_, body, _, _) = self.deconstruct();
212        body.end()?;
213        Ok(())
214    }
215}
216
217impl<'a> Write for Response<'a, Streaming> {
218    #[inline]
219    fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
220        debug!("write {:?} bytes", msg.len());
221        self.body.write(msg)
222    }
223
224    #[inline]
225    fn flush(&mut self) -> io::Result<()> {
226        self.body.flush()
227    }
228}
229
230#[derive(PartialEq)]
231enum Body {
232    Chunked,
233    Sized(u64),
234    Empty,
235}
236
237impl<'a, T: Any> Drop for Response<'a, T> {
238    fn drop(&mut self) {
239        if TypeId::of::<T>() == TypeId::of::<Fresh>() {
240            if thread::panicking() {
241                self.status = status::StatusCode::InternalServerError;
242            }
243
244            let mut body = match self.write_head() {
245                Ok(Body::Chunked) => ChunkedWriter(self.body.get_mut()),
246                Ok(Body::Sized(len)) => SizedWriter(self.body.get_mut(), len),
247                Ok(Body::Empty) => EmptyWriter(self.body.get_mut()),
248                Err(e) => {
249                    debug!("error dropping request: {:?}", e);
250                    return;
251                }
252            };
253            end(&mut body);
254        } else {
255            end(&mut self.body);
256        };
257
258
259        #[inline]
260        fn end<W: Write>(w: &mut W) {
261            match w.write(&[]) {
262                Ok(_) => match w.flush() {
263                    Ok(_) => debug!("drop successful"),
264                    Err(e) => debug!("error dropping request: {:?}", e)
265                },
266                Err(e) => debug!("error dropping request: {:?}", e)
267            }
268        }
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use crate::header::Headers;
275    use crate::mock::MockStream;
276    use crate::runtime;
277    use super::Response;
278
279    macro_rules! lines {
280        ($s:ident = $($line:pat),+) => ({
281            let s = String::from_utf8($s.write).unwrap();
282            let mut lines = s.split_terminator("\r\n");
283
284            $(
285                match lines.next() {
286                    Some($line) => (),
287                    other => panic!("line mismatch: {:?} != {:?}", other, stringify!($line))
288                }
289            )+
290
291            assert_eq!(lines.next(), None);
292        })
293    }
294
295    #[test]
296    fn test_fresh_start() {
297        let mut headers = Headers::new();
298        let mut stream = MockStream::new();
299        {
300            let res = Response::new(&mut stream, &mut headers);
301            res.start().unwrap().deconstruct();
302        }
303
304        lines! { stream =
305            "HTTP/1.1 200 OK",
306            _date,
307            _transfer_encoding,
308            ""
309        }
310    }
311
312    #[test]
313    fn test_streaming_end() {
314        let mut headers = Headers::new();
315        let mut stream = MockStream::new();
316        {
317            let res = Response::new(&mut stream, &mut headers);
318            res.start().unwrap().end().unwrap();
319        }
320
321        lines! { stream =
322            "HTTP/1.1 200 OK",
323            _date,
324            _transfer_encoding,
325            "",
326            "0",
327            "" // empty zero body
328        }
329    }
330
331    #[test]
332    fn test_fresh_drop() {
333        use crate::status::StatusCode;
334        let mut headers = Headers::new();
335        let mut stream = MockStream::new();
336        {
337            let mut res = Response::new(&mut stream, &mut headers);
338            *res.status_mut() = StatusCode::NotFound;
339        }
340
341        lines! { stream =
342            "HTTP/1.1 404 Not Found",
343            _date,
344            _transfer_encoding,
345            "",
346            "0",
347            "" // empty zero body
348        }
349    }
350
351    // x86 windows msvc does not support unwinding
352    // See https://github.com/rust-lang/rust/issues/25869
353    #[cfg(not(all(windows, target_arch="x86", target_env="msvc")))]
354    #[test]
355    fn test_fresh_drop_panicing() {
356        use std::sync::{Arc, Mutex};
357
358        use crate::status::StatusCode;
359
360        let stream = crate::mock::MockStream::new();
361        let stream = Arc::new(Mutex::new(stream));
362        let inner_stream = stream.clone();
363        let join_handle = runtime::spawn(move || {
364            let mut headers = Headers::new();
365            let mut stream = inner_stream.lock().unwrap();
366            let mut res = Response::new(&mut *stream, &mut headers);
367            *res.status_mut() = StatusCode::NotFound;
368
369            panic!("inside")
370        });
371
372        assert!(join_handle.join().is_err());
373
374        let stream = match stream.lock() {
375            Err(poisoned) => poisoned.into_inner().clone(),
376            Ok(_) => unreachable!()
377        };
378
379        lines! { stream =
380            "HTTP/1.1 500 Internal Server Error",
381            _date,
382            _transfer_encoding,
383            "",
384            "0",
385            "" // empty zero body
386        }
387    }
388
389
390    #[test]
391    fn test_streaming_drop() {
392        use std::io::Write;
393        use crate::status::StatusCode;
394        let mut headers = Headers::new();
395        let mut stream = MockStream::new();
396        {
397            let mut res = Response::new(&mut stream, &mut headers);
398            *res.status_mut() = StatusCode::NotFound;
399            let mut stream = res.start().unwrap();
400            stream.write_all(b"foo").unwrap();
401        }
402
403        lines! { stream =
404            "HTTP/1.1 404 Not Found",
405            _date,
406            _transfer_encoding,
407            "",
408            "3",
409            "foo",
410            "0",
411            "" // empty zero body
412        }
413    }
414
415    #[test]
416    fn test_no_content() {
417        use crate::status::StatusCode;
418        let mut headers = Headers::new();
419        let mut stream = MockStream::new();
420        {
421            let mut res = Response::new(&mut stream, &mut headers);
422            *res.status_mut() = StatusCode::NoContent;
423            res.start().unwrap();
424        }
425
426        lines! { stream =
427            "HTTP/1.1 204 No Content",
428            _date,
429            ""
430        }
431    }
432}