1 //! Client Requests
2 use std::marker::PhantomData;
3 use std::io::{self, Write};
4 
5 use std::time::Duration;
6 
7 use url::Url;
8 
9 use method::Method;
10 use header::Headers;
11 use header::Host;
12 use net::{NetworkStream, NetworkConnector, DefaultConnector, Fresh, Streaming};
13 use version;
14 use client::{Response, get_host_and_port};
15 
16 use http::{HttpMessage, RequestHead};
17 use http::h1::Http11Message;
18 
19 
20 /// A client request to a remote server.
21 /// The W type tracks the state of the request, Fresh vs Streaming.
22 pub struct Request<W> {
23     /// The target URI for this request.
24     pub url: Url,
25 
26     /// The HTTP version of this request.
27     pub version: version::HttpVersion,
28 
29     message: Box<HttpMessage>,
30     headers: Headers,
31     method: Method,
32 
33     _marker: PhantomData<W>,
34 }
35 
36 impl<W> Request<W> {
37     /// Read the Request headers.
38     #[inline]
headers(&self) -> &Headers39     pub fn headers(&self) -> &Headers { &self.headers }
40 
41     /// Read the Request method.
42     #[inline]
method(&self) -> Method43     pub fn method(&self) -> Method { self.method.clone() }
44 
45     /// Set the write timeout.
46     #[inline]
set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()>47     pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
48         self.message.set_write_timeout(dur)
49     }
50 
51     /// Set the read timeout.
52     #[inline]
set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()>53     pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
54         self.message.set_read_timeout(dur)
55     }
56 }
57 
58 impl Request<Fresh> {
59     /// Create a new `Request<Fresh>` that will use the given `HttpMessage` for its communication
60     /// with the server. This implies that the given `HttpMessage` instance has already been
61     /// properly initialized by the caller (e.g. a TCP connection's already established).
with_message(method: Method, url: Url, message: Box<HttpMessage>) -> ::Result<Request<Fresh>>62     pub fn with_message(method: Method, url: Url, message: Box<HttpMessage>)
63             -> ::Result<Request<Fresh>> {
64         let mut headers = Headers::new();
65         {
66             let (host, port) = try!(get_host_and_port(&url));
67             headers.set(Host {
68                 hostname: host.to_owned(),
69                 port: Some(port),
70             });
71         }
72 
73         Ok(Request::with_headers_and_message(method, url, headers, message))
74     }
75 
76     #[doc(hidden)]
with_headers_and_message(method: Method, url: Url, headers: Headers, message: Box<HttpMessage>) -> Request<Fresh>77     pub fn with_headers_and_message(method: Method, url: Url, headers: Headers,  message: Box<HttpMessage>)
78                 -> Request<Fresh> {
79         Request {
80             method: method,
81             headers: headers,
82             url: url,
83             version: version::HttpVersion::Http11,
84             message: message,
85             _marker: PhantomData,
86         }
87     }
88 
89     /// Create a new client request.
new(method: Method, url: Url) -> ::Result<Request<Fresh>>90     pub fn new(method: Method, url: Url) -> ::Result<Request<Fresh>> {
91         let conn = DefaultConnector::default();
92         Request::with_connector(method, url, &conn)
93     }
94 
95     /// Create a new client request with a specific underlying NetworkStream.
with_connector<C, S>(method: Method, url: Url, connector: &C) -> ::Result<Request<Fresh>> where C: NetworkConnector<Stream=S>, S: Into<Box<NetworkStream + Send>>96     pub fn with_connector<C, S>(method: Method, url: Url, connector: &C)
97         -> ::Result<Request<Fresh>> where
98         C: NetworkConnector<Stream=S>,
99         S: Into<Box<NetworkStream + Send>> {
100         let stream = {
101             let (host, port) = try!(get_host_and_port(&url));
102             try!(connector.connect(host, port, url.scheme())).into()
103         };
104 
105         Request::with_message(method, url, Box::new(Http11Message::with_stream(stream)))
106     }
107 
108     /// Consume a Fresh Request, writing the headers and method,
109     /// returning a Streaming Request.
start(mut self) -> ::Result<Request<Streaming>>110     pub fn start(mut self) -> ::Result<Request<Streaming>> {
111         let head = match self.message.set_outgoing(RequestHead {
112             headers: self.headers,
113             method: self.method,
114             url: self.url,
115         }) {
116             Ok(head) => head,
117             Err(e) => {
118                 let _ = self.message.close_connection();
119                 return Err(From::from(e));
120             }
121         };
122 
123         Ok(Request {
124             method: head.method,
125             headers: head.headers,
126             url: head.url,
127             version: self.version,
128             message: self.message,
129             _marker: PhantomData,
130         })
131     }
132 
133     /// Get a mutable reference to the Request headers.
134     #[inline]
headers_mut(&mut self) -> &mut Headers135     pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers }
136 }
137 
138 
139 
140 impl Request<Streaming> {
141     /// Completes writing the request, and returns a response to read from.
142     ///
143     /// Consumes the Request.
send(self) -> ::Result<Response>144     pub fn send(self) -> ::Result<Response> {
145         Response::with_message(self.url, self.message)
146     }
147 }
148 
149 impl Write for Request<Streaming> {
150     #[inline]
write(&mut self, msg: &[u8]) -> io::Result<usize>151     fn write(&mut self, msg: &[u8]) -> io::Result<usize> {
152         match self.message.write(msg) {
153             Ok(n) => Ok(n),
154             Err(e) => {
155                 let _ = self.message.close_connection();
156                 Err(e)
157             }
158         }
159     }
160 
161     #[inline]
flush(&mut self) -> io::Result<()>162     fn flush(&mut self) -> io::Result<()> {
163         match self.message.flush() {
164             Ok(r) => Ok(r),
165             Err(e) => {
166                 let _ = self.message.close_connection();
167                 Err(e)
168             }
169         }
170     }
171 }
172 
173 #[cfg(test)]
174 mod tests {
175     use std::io::Write;
176     use std::str::from_utf8;
177     use url::Url;
178     use method::Method::{Get, Head, Post};
179     use mock::{MockStream, MockConnector};
180     use net::Fresh;
181     use header::{ContentLength,TransferEncoding,Encoding};
182     use url::form_urlencoded;
183     use super::Request;
184     use http::h1::Http11Message;
185 
run_request(req: Request<Fresh>) -> Vec<u8>186     fn run_request(req: Request<Fresh>) -> Vec<u8> {
187         let req = req.start().unwrap();
188         let message = req.message;
189         let mut message = message.downcast::<Http11Message>().ok().unwrap();
190         message.flush_outgoing().unwrap();
191         let stream = *message
192             .into_inner().downcast::<MockStream>().ok().unwrap();
193         stream.write
194     }
195 
assert_no_body(s: &str)196     fn assert_no_body(s: &str) {
197         assert!(!s.contains("Content-Length:"));
198         assert!(!s.contains("Transfer-Encoding:"));
199     }
200 
201     #[test]
test_get_empty_body()202     fn test_get_empty_body() {
203         let req = Request::with_connector(
204             Get, Url::parse("http://example.dom").unwrap(), &mut MockConnector
205         ).unwrap();
206         let bytes = run_request(req);
207         let s = from_utf8(&bytes[..]).unwrap();
208         assert_no_body(s);
209     }
210 
211     #[test]
test_head_empty_body()212     fn test_head_empty_body() {
213         let req = Request::with_connector(
214             Head, Url::parse("http://example.dom").unwrap(), &mut MockConnector
215         ).unwrap();
216         let bytes = run_request(req);
217         let s = from_utf8(&bytes[..]).unwrap();
218         assert_no_body(s);
219     }
220 
221     #[test]
test_url_query()222     fn test_url_query() {
223         let url = Url::parse("http://example.dom?q=value").unwrap();
224         let req = Request::with_connector(
225             Get, url, &mut MockConnector
226         ).unwrap();
227         let bytes = run_request(req);
228         let s = from_utf8(&bytes[..]).unwrap();
229         assert!(s.contains("?q=value"));
230     }
231 
232     #[test]
test_post_content_length()233     fn test_post_content_length() {
234         let url = Url::parse("http://example.dom").unwrap();
235         let mut req = Request::with_connector(
236             Post, url, &mut MockConnector
237         ).unwrap();
238         let mut body = String::new();
239         form_urlencoded::Serializer::new(&mut body).append_pair("q", "value");
240         req.headers_mut().set(ContentLength(body.len() as u64));
241         let bytes = run_request(req);
242         let s = from_utf8(&bytes[..]).unwrap();
243         assert!(s.contains("Content-Length:"));
244     }
245 
246     #[test]
test_post_chunked()247     fn test_post_chunked() {
248         let url = Url::parse("http://example.dom").unwrap();
249         let req = Request::with_connector(
250             Post, url, &mut MockConnector
251         ).unwrap();
252         let bytes = run_request(req);
253         let s = from_utf8(&bytes[..]).unwrap();
254         assert!(!s.contains("Content-Length:"));
255     }
256 
257     #[test]
test_host_header()258     fn test_host_header() {
259         let url = Url::parse("http://example.dom").unwrap();
260         let req = Request::with_connector(
261             Get, url, &mut MockConnector
262         ).unwrap();
263         let bytes = run_request(req);
264         let s = from_utf8(&bytes[..]).unwrap();
265         assert!(s.contains("Host: example.dom"));
266     }
267 
268     #[test]
test_proxy()269     fn test_proxy() {
270         let url = Url::parse("http://example.dom").unwrap();
271         let mut req = Request::with_connector(
272             Get, url, &mut MockConnector
273         ).unwrap();
274         req.message.set_proxied(true);
275         let bytes = run_request(req);
276         let s = from_utf8(&bytes[..]).unwrap();
277         let request_line = "GET http://example.dom/ HTTP/1.1";
278         assert_eq!(&s[..request_line.len()], request_line);
279         assert!(s.contains("Host: example.dom"));
280     }
281 
282     #[test]
test_post_chunked_with_encoding()283     fn test_post_chunked_with_encoding() {
284         let url = Url::parse("http://example.dom").unwrap();
285         let mut req = Request::with_connector(
286             Post, url, &mut MockConnector
287         ).unwrap();
288         req.headers_mut().set(TransferEncoding(vec![Encoding::Chunked]));
289         let bytes = run_request(req);
290         let s = from_utf8(&bytes[..]).unwrap();
291         assert!(!s.contains("Content-Length:"));
292         assert!(s.contains("Transfer-Encoding:"));
293     }
294 
295     #[test]
test_write_error_closes()296     fn test_write_error_closes() {
297         let url = Url::parse("http://hyper.rs").unwrap();
298         let req = Request::with_connector(
299             Get, url, &mut MockConnector
300         ).unwrap();
301         let mut req = req.start().unwrap();
302 
303         req.message.downcast_mut::<Http11Message>().unwrap()
304             .get_mut().downcast_mut::<MockStream>().unwrap()
305             .error_on_write = true;
306 
307         req.write(b"foo").unwrap();
308         assert!(req.flush().is_err());
309 
310         assert!(req.message.downcast_ref::<Http11Message>().unwrap()
311             .get_ref().downcast_ref::<MockStream>().unwrap()
312             .is_closed);
313     }
314 }
315