mco_http/client/
request.rs1use 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
20pub struct Request<W> {
23 pub url: Url,
25
26 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 #[inline]
39 pub fn headers(&self) -> &Headers { &self.headers }
40
41 #[inline]
43 pub fn method(&self) -> Method { self.method.clone() }
44
45 #[inline]
47 pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
48 self.message.set_write_timeout(dur)
49 }
50
51 #[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 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 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 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 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 #[inline]
135 pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers }
136}
137
138
139
140impl Request<Streaming> {
141 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}