hyper_sync/client/
request.rs

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