hyper_sync/client/
request.rs1use 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
20pub struct Request<W> {
23 pub url: Url,
25
26 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 #[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<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 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 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 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 #[inline]
132 pub fn headers_mut(&mut self) -> &mut Headers { &mut self.headers }
133}
134
135
136
137impl Request<Streaming> {
138 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}