cogo_http/server/
response.rs

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