1 //! Client Responses
2 use std::io::{self, Read};
3 
4 use url::Url;
5 
6 use header;
7 use net::NetworkStream;
8 use http::{self, RawStatus, ResponseHead, HttpMessage};
9 use http::h1::Http11Message;
10 use status;
11 use version;
12 
13 /// A response for a client request to a remote server.
14 #[derive(Debug)]
15 pub struct Response {
16     /// The status from the server.
17     pub status: status::StatusCode,
18     /// The headers from the server.
19     pub headers: header::Headers,
20     /// The HTTP version of this response from the server.
21     pub version: version::HttpVersion,
22     /// The final URL of this response.
23     pub url: Url,
24     status_raw: RawStatus,
25     message: Box<HttpMessage>,
26 }
27 
28 impl Response {
29     /// Creates a new response from a server.
new(url: Url, stream: Box<NetworkStream + Send>) -> ::Result<Response>30     pub fn new(url: Url, stream: Box<NetworkStream + Send>) -> ::Result<Response> {
31         trace!("Response::new");
32         Response::with_message(url, Box::new(Http11Message::with_stream(stream)))
33     }
34 
35     /// Creates a new response received from the server on the given `HttpMessage`.
with_message(url: Url, mut message: Box<HttpMessage>) -> ::Result<Response>36     pub fn with_message(url: Url, mut message: Box<HttpMessage>) -> ::Result<Response> {
37         trace!("Response::with_message");
38         let ResponseHead { headers, raw_status, version } = match message.get_incoming() {
39             Ok(head) => head,
40             Err(e) => {
41                 let _ = message.close_connection();
42                 return Err(From::from(e));
43             }
44         };
45         let status = status::StatusCode::from_u16(raw_status.0);
46         debug!("version={:?}, status={:?}", version, status);
47         debug!("headers={:?}", headers);
48 
49         Ok(Response {
50             status: status,
51             version: version,
52             headers: headers,
53             url: url,
54             status_raw: raw_status,
55             message: message,
56         })
57     }
58 
59     /// Get the raw status code and reason.
60     #[inline]
status_raw(&self) -> &RawStatus61     pub fn status_raw(&self) -> &RawStatus {
62         &self.status_raw
63     }
64 
65     /// Gets a borrowed reference to the underlying `HttpMessage`.
66     #[inline]
get_ref(&self) -> &HttpMessage67     pub fn get_ref(&self) -> &HttpMessage {
68         &*self.message
69     }
70 }
71 
72 /// Read the response body.
73 impl Read for Response {
74     #[inline]
read(&mut self, buf: &mut [u8]) -> io::Result<usize>75     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
76         match self.message.read(buf) {
77             Err(e) => {
78                 let _ = self.message.close_connection();
79                 Err(e)
80             }
81             r => r
82         }
83     }
84 }
85 
86 impl Drop for Response {
drop(&mut self)87     fn drop(&mut self) {
88         // if not drained, theres old bits in the Reader. we can't reuse this,
89         // since those old bits would end up in new Responses
90         //
91         // otherwise, the response has been drained. we should check that the
92         // server has agreed to keep the connection open
93         let is_drained = !self.message.has_body();
94         trace!("Response.drop is_drained={}", is_drained);
95         if !(is_drained && http::should_keep_alive(self.version, &self.headers)) {
96             trace!("Response.drop closing connection");
97             if let Err(e) = self.message.close_connection() {
98                 info!("Response.drop error closing connection: {}", e);
99             }
100         }
101     }
102 }
103 
104 #[cfg(test)]
105 mod tests {
106     use std::io::{self, Read};
107 
108     use url::Url;
109 
110     use header::TransferEncoding;
111     use header::Encoding;
112     use http::HttpMessage;
113     use mock::MockStream;
114     use status;
115     use version;
116     use http::h1::Http11Message;
117 
118     use super::Response;
119 
read_to_string(mut r: Response) -> io::Result<String>120     fn read_to_string(mut r: Response) -> io::Result<String> {
121         let mut s = String::new();
122         try!(r.read_to_string(&mut s));
123         Ok(s)
124     }
125 
126 
127     #[test]
test_into_inner()128     fn test_into_inner() {
129         let message: Box<HttpMessage> = Box::new(
130             Http11Message::with_stream(Box::new(MockStream::new())));
131         let message = message.downcast::<Http11Message>().ok().unwrap();
132         let b = message.into_inner().downcast::<MockStream>().ok().unwrap();
133         assert_eq!(b, Box::new(MockStream::new()));
134     }
135 
136     #[test]
test_parse_chunked_response()137     fn test_parse_chunked_response() {
138         let stream = MockStream::with_input(b"\
139             HTTP/1.1 200 OK\r\n\
140             Transfer-Encoding: chunked\r\n\
141             \r\n\
142             1\r\n\
143             q\r\n\
144             2\r\n\
145             we\r\n\
146             2\r\n\
147             rt\r\n\
148             0\r\n\
149             \r\n"
150         );
151 
152         let url = Url::parse("http://hyper.rs").unwrap();
153         let res = Response::new(url, Box::new(stream)).unwrap();
154 
155         // The status line is correct?
156         assert_eq!(res.status, status::StatusCode::Ok);
157         assert_eq!(res.version, version::HttpVersion::Http11);
158         // The header is correct?
159         match res.headers.get::<TransferEncoding>() {
160             Some(encodings) => {
161                 assert_eq!(1, encodings.len());
162                 assert_eq!(Encoding::Chunked, encodings[0]);
163             },
164             None => panic!("Transfer-Encoding: chunked expected!"),
165         };
166         // The body is correct?
167         assert_eq!(read_to_string(res).unwrap(), "qwert".to_owned());
168     }
169 
170     /// Tests that when a chunk size is not a valid radix-16 number, an error
171     /// is returned.
172     #[test]
test_invalid_chunk_size_not_hex_digit()173     fn test_invalid_chunk_size_not_hex_digit() {
174         let stream = MockStream::with_input(b"\
175             HTTP/1.1 200 OK\r\n\
176             Transfer-Encoding: chunked\r\n\
177             \r\n\
178             X\r\n\
179             1\r\n\
180             0\r\n\
181             \r\n"
182         );
183 
184         let url = Url::parse("http://hyper.rs").unwrap();
185         let res = Response::new(url, Box::new(stream)).unwrap();
186 
187         assert!(read_to_string(res).is_err());
188     }
189 
190     /// Tests that when a chunk size contains an invalid extension, an error is
191     /// returned.
192     #[test]
test_invalid_chunk_size_extension()193     fn test_invalid_chunk_size_extension() {
194         let stream = MockStream::with_input(b"\
195             HTTP/1.1 200 OK\r\n\
196             Transfer-Encoding: chunked\r\n\
197             \r\n\
198             1 this is an invalid extension\r\n\
199             1\r\n\
200             0\r\n\
201             \r\n"
202         );
203 
204         let url = Url::parse("http://hyper.rs").unwrap();
205         let res = Response::new(url, Box::new(stream)).unwrap();
206 
207         assert!(read_to_string(res).is_err());
208     }
209 
210     /// Tests that when a valid extension that contains a digit is appended to
211     /// the chunk size, the chunk is correctly read.
212     #[test]
test_chunk_size_with_extension()213     fn test_chunk_size_with_extension() {
214         let stream = MockStream::with_input(b"\
215             HTTP/1.1 200 OK\r\n\
216             Transfer-Encoding: chunked\r\n\
217             \r\n\
218             1;this is an extension with a digit 1\r\n\
219             1\r\n\
220             0\r\n\
221             \r\n"
222         );
223 
224         let url = Url::parse("http://hyper.rs").unwrap();
225         let res = Response::new(url, Box::new(stream)).unwrap();
226 
227         assert_eq!(read_to_string(res).unwrap(), "1".to_owned());
228     }
229 
230     #[test]
test_parse_error_closes()231     fn test_parse_error_closes() {
232         let url = Url::parse("http://hyper.rs").unwrap();
233         let stream = MockStream::with_input(b"\
234             definitely not http
235         ");
236 
237         assert!(Response::new(url, Box::new(stream)).is_err());
238     }
239 }
240