mco_http/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 crate::method::Method;
10use crate::header::Headers;
11use crate::header::Host;
12use crate::net::{NetworkStream, NetworkConnector, DefaultConnector, Fresh, Streaming};
13use crate::version;
14use crate::client::{Response, get_host_and_port};
15
16use crate::http::{HttpMessage, RequestHead};
17use crate::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<dyn 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<dyn HttpMessage>)
63            -> crate::Result<Request<Fresh>> {
64        let mut headers = Headers::new();
65        {
66            let (host, port) = 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)]
77    pub fn with_headers_and_message(method: Method, url: Url, headers: Headers,  message: Box<dyn 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.
90    pub fn new(method: Method, url: Url) -> crate::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.
96    pub fn with_connector<C, S>(method: Method, url: Url, connector: &C)
97        -> crate::Result<Request<Fresh>> where
98        C: NetworkConnector<Stream=S>,
99        S: Into<Box<dyn NetworkStream + Send>> {
100        let stream = {
101            let (host, port) = get_host_and_port(&url)?;
102            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.
110    pub fn start(mut self) -> crate::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]
135    pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers }
136}
137
138
139
140impl Request<Streaming> {
141    /// Completes writing the request, and returns a response to read from.
142    ///
143    /// Consumes the Request.
144    pub fn send(self) -> crate::Result<Response> {
145        Response::with_message(self.url, self.message)
146    }
147}
148
149impl Write for Request<Streaming> {
150    #[inline]
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]
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)]
174mod tests {
175    use std::io::Write;
176    use std::str::from_utf8;
177    use url::Url;
178    use crate::method::Method::{Get, Head, Post};
179    use crate::mock::{MockStream, MockConnector};
180    use crate::net::Fresh;
181    use crate::header::{ContentLength,TransferEncoding,Encoding};
182    use url::form_urlencoded;
183    use super::Request;
184    use crate::http::h1::Http11Message;
185
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
196    fn assert_no_body(s: &str) {
197        assert!(!s.contains("Content-Length:"));
198        assert!(!s.contains("Transfer-Encoding:"));
199    }
200
201    #[test]
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]
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]
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]
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]
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]
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]
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]
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]
296    fn test_write_error_closes() {
297        let url = Url::parse("http://mco_http.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}