1use std::borrow::Cow;
65use std::default::Default;
66use std::io::{self, copy, Read};
67use std::fmt;
68
69use std::time::Duration;
70
71use url::Url;
72use url::ParseError as UrlError;
73
74use crate::header::{Headers, Header, HeaderFormat};
75use crate::header::{ContentLength, Host, Location};
76use crate::method::Method;
77use crate::net::{NetworkConnector, NetworkStream, SslClient};
78use crate::Error;
79
80use self::proxy::{Proxy, tunnel};
81use self::scheme::Scheme;
82pub use self::pool::Pool;
83pub use self::request::Request;
84pub use self::response::Response;
85
86mod proxy;
87pub mod pool;
88pub mod request;
89pub mod response;
90
91use crate::http::Protocol;
92use crate::http::h1::Http11Protocol;
93
94
95pub struct Client {
99 protocol: Box<dyn Protocol + Send + Sync>,
100 redirect_policy: RedirectPolicy,
101 read_timeout: Option<Duration>,
102 write_timeout: Option<Duration>,
103 proxy: Option<(Scheme, Cow<'static, str>, u16)>
104}
105
106impl fmt::Debug for Client {
107 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
108 fmt.debug_struct("Client")
109 .field("redirect_policy", &self.redirect_policy)
110 .field("read_timeout", &self.read_timeout)
111 .field("write_timeout", &self.write_timeout)
112 .field("proxy", &self.proxy)
113 .finish()
114 }
115}
116
117impl Client {
118
119 pub fn new() -> Client {
121 Client::with_pool_config(Default::default())
122 }
123
124 pub fn with_pool_config(config: pool::Config) -> Client {
126 Client::with_connector(Pool::new(config))
127 }
128
129 pub fn with_http_proxy<H>(host: H, port: u16) -> Client
131 where H: Into<Cow<'static, str>> {
132 let host = host.into();
133 let proxy = tunnel((Scheme::Http, host.clone(), port));
134 let mut client = Client::with_connector(Pool::with_connector(Default::default(), proxy));
135 client.proxy = Some((Scheme::Http, host, port));
136 client
137 }
138
139 pub fn with_proxy_config<C, S>(proxy_config: ProxyConfig<C, S>) -> Client
141 where C: NetworkConnector + Send + Sync + 'static,
142 C::Stream: NetworkStream + Send + Clone,
143 S: SslClient<C::Stream> + Send + Sync + 'static {
144
145 let scheme = proxy_config.scheme;
146 let host = proxy_config.host;
147 let port = proxy_config.port;
148 let proxy = Proxy {
149 proxy: (scheme.clone(), host.clone(), port),
150 connector: proxy_config.connector,
151 ssl: proxy_config.ssl,
152 };
153
154 let mut client = match proxy_config.pool_config {
155 Some(pool_config) => Client::with_connector(Pool::with_connector(pool_config, proxy)),
156 None => Client::with_connector(proxy),
157 };
158 client.proxy = Some((scheme, host, port));
159 client
160 }
161
162 pub fn with_connector<C, S>(connector: C) -> Client
164 where C: NetworkConnector<Stream=S> + Send + Sync + 'static, S: NetworkStream + Send {
165 Client::with_protocol(Http11Protocol::with_connector(connector))
166 }
167
168 pub fn with_protocol<P: Protocol + Send + Sync + 'static>(protocol: P) -> Client {
170 Client {
171 protocol: Box::new(protocol),
172 redirect_policy: Default::default(),
173 read_timeout: None,
174 write_timeout: None,
175 proxy: None,
176 }
177 }
178
179 pub fn set_redirect_policy(&mut self, policy: RedirectPolicy) {
181 self.redirect_policy = policy;
182 }
183
184 pub fn set_read_timeout(&mut self, dur: Option<Duration>) {
186 self.read_timeout = dur;
187 }
188
189 pub fn set_write_timeout(&mut self, dur: Option<Duration>) {
191 self.write_timeout = dur;
192 }
193
194 pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
196 self.request(Method::Get, url)
197 }
198
199 pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
201 self.request(Method::Head, url)
202 }
203
204 pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
206 self.request(Method::Patch, url)
207 }
208
209 pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
211 self.request(Method::Post, url)
212 }
213
214 pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
216 self.request(Method::Put, url)
217 }
218
219 pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
221 self.request(Method::Delete, url)
222 }
223
224
225 pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
227 RequestBuilder {
228 client: self,
229 method: method,
230 url: url.into_url(),
231 body: None,
232 headers: None,
233 }
234 }
235}
236
237impl Default for Client {
238 fn default() -> Client { Client::new() }
239}
240
241pub struct RequestBuilder<'a> {
246 client: &'a Client,
247 url: Result<Url, UrlError>,
257 headers: Option<Headers>,
258 method: Method,
259 body: Option<Body<'a>>,
260}
261
262impl<'a> RequestBuilder<'a> {
263
264 pub fn body<B: Into<Body<'a>>>(mut self, body: B) -> RequestBuilder<'a> {
266 self.body = Some(body.into());
267 self
268 }
269
270 pub fn headers(mut self, headers: Headers) -> RequestBuilder<'a> {
272 self.headers = Some(headers);
273 self
274 }
275
276 pub fn header<H: Header + HeaderFormat>(mut self, header: H) -> RequestBuilder<'a> {
278 {
279 let headers = match self.headers {
280 Some(ref mut h) => h,
281 None => {
282 self.headers = Some(Headers::new());
283 self.headers.as_mut().unwrap()
284 }
285 };
286
287 headers.set(header);
288 }
289 self
290 }
291
292 pub fn send(self) -> crate::Result<Response> {
294 let RequestBuilder { client, method, url, headers, body } = self;
295 let mut url = r#try!(url);
296 trace!("send method={:?}, url={:?}, client={:?}", method, url, client);
297
298 let can_have_body = match method {
299 Method::Get | Method::Head => false,
300 _ => true
301 };
302
303 let mut body = if can_have_body {
304 body
305 } else {
306 None
307 };
308
309 loop {
310 let mut req = {
311 let (host, port) = r#try!(get_host_and_port(&url));
312 let mut message = r#try!(client.protocol.new_message(&host, port, url.scheme()));
313 if url.scheme() == "http" && client.proxy.is_some() {
314 message.set_proxied(true);
315 }
316
317 let mut h = Headers::with_capacity({match headers.as_ref() {
318 None => {1}
319 Some(n) => {n.len()}
320 }});
321 h.set(Host {
322 hostname: host.to_owned(),
323 port: Some(port),
324 });
325 if let Some(ref headers) = headers {
326 h.extend(headers.iter());
327 }
328 let headers = h;
329 Request::with_headers_and_message(method.clone(), url.clone(), headers, message)
330 };
331
332 r#try!(req.set_write_timeout(client.write_timeout));
333 r#try!(req.set_read_timeout(client.read_timeout));
334
335 match (can_have_body, body.as_ref()) {
336 (true, Some(body)) => match body.size() {
337 Some(size) => req.headers_mut().set(ContentLength(size)),
338 None => (), },
340 (true, None) => req.headers_mut().set(ContentLength(0)),
341 _ => () }
343 let mut streaming = r#try!(req.start());
344 if let Some(mut rdr) = body.take() {
345 r#try!(copy(&mut rdr, &mut streaming));
346 }
347 let res = r#try!(streaming.send());
348 if !res.status.is_redirection() {
349 return Ok(res)
350 }
351 debug!("redirect code {:?} for {}", res.status, url);
352
353 let loc = {
354 let loc = match res.headers.get::<Location>() {
356 Some(&Location(ref loc)) => {
357 Some(url.join(loc))
358 }
359 None => {
360 debug!("no Location header");
361 None
363 }
364 };
365 match loc {
366 Some(r) => r,
367 None => return Ok(res)
368 }
369 };
370 url = match loc {
371 Ok(u) => u,
372 Err(e) => {
373 debug!("Location header had invalid URI: {:?}", e);
374 return Ok(res);
375 }
376 };
377 match client.redirect_policy {
378 RedirectPolicy::FollowAll => (), RedirectPolicy::FollowIf(cond) if cond(&url) => (), _ => return Ok(res),
382 }
383 }
384 }
385}
386
387pub enum Body<'a> {
389 ChunkedBody(&'a mut (dyn Read + 'a)),
391 SizedBody(&'a mut (dyn Read + 'a), u64),
393 BufBody(&'a [u8] , usize),
395}
396
397impl<'a> Body<'a> {
398 fn size(&self) -> Option<u64> {
399 match *self {
400 Body::SizedBody(_, len) => Some(len),
401 Body::BufBody(_, len) => Some(len as u64),
402 _ => None
403 }
404 }
405}
406
407impl<'a> Read for Body<'a> {
408 #[inline]
409 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
410 match *self {
411 Body::ChunkedBody(ref mut r) => r.read(buf),
412 Body::SizedBody(ref mut r, _) => r.read(buf),
413 Body::BufBody(ref mut r, _) => Read::read(r, buf),
414 }
415 }
416}
417
418impl<'a> Into<Body<'a>> for &'a [u8] {
419 #[inline]
420 fn into(self) -> Body<'a> {
421 Body::BufBody(self, self.len())
422 }
423}
424
425impl<'a> Into<Body<'a>> for &'a str {
426 #[inline]
427 fn into(self) -> Body<'a> {
428 self.as_bytes().into()
429 }
430}
431
432impl<'a> Into<Body<'a>> for &'a String {
433 #[inline]
434 fn into(self) -> Body<'a> {
435 self.as_bytes().into()
436 }
437}
438
439impl<'a, R: Read> From<&'a mut R> for Body<'a> {
440 #[inline]
441 fn from(r: &'a mut R) -> Body<'a> {
442 Body::ChunkedBody(r)
443 }
444}
445
446pub trait IntoUrl {
448 fn into_url(self) -> Result<Url, UrlError>;
450}
451
452impl IntoUrl for Url {
453 fn into_url(self) -> Result<Url, UrlError> {
454 Ok(self)
455 }
456}
457
458impl<'a> IntoUrl for &'a str {
459 fn into_url(self) -> Result<Url, UrlError> {
460 Url::parse(self)
461 }
462}
463
464impl<'a> IntoUrl for &'a String {
465 fn into_url(self) -> Result<Url, UrlError> {
466 Url::parse(self)
467 }
468}
469
470pub struct ProxyConfig<C, S>
472where C: NetworkConnector + Send + Sync + 'static,
473 C::Stream: NetworkStream + Clone + Send,
474 S: SslClient<C::Stream> + Send + Sync + 'static {
475 scheme: Scheme,
476 host: Cow<'static, str>,
477 port: u16,
478 pool_config: Option<pool::Config>,
479 connector: C,
480 ssl: S,
481}
482
483impl<C, S> ProxyConfig<C, S>
484where C: NetworkConnector + Send + Sync + 'static,
485 C::Stream: NetworkStream + Clone + Send,
486 S: SslClient<C::Stream> + Send + Sync + 'static {
487
488 #[inline]
490 pub fn new<H: Into<Cow<'static, str>>>(scheme: &str, host: H, port: u16, connector: C, ssl: S) -> ProxyConfig<C, S> {
491 ProxyConfig {
492 scheme: scheme.into(),
493 host: host.into(),
494 port: port,
495 pool_config: Some(pool::Config::default()),
496 connector: connector,
497 ssl: ssl,
498 }
499 }
500
501 pub fn set_pool_config(&mut self, pool_config: Option<pool::Config>) {
507 self.pool_config = pool_config;
508 }
509}
510
511#[derive(Copy)]
513pub enum RedirectPolicy {
514 FollowNone,
516 FollowAll,
518 FollowIf(fn(&Url) -> bool),
520}
521
522impl fmt::Debug for RedirectPolicy {
523 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
524 match *self {
525 RedirectPolicy::FollowNone => fmt.write_str("FollowNone"),
526 RedirectPolicy::FollowAll => fmt.write_str("FollowAll"),
527 RedirectPolicy::FollowIf(_) => fmt.write_str("FollowIf"),
528 }
529 }
530}
531
532impl Clone for RedirectPolicy {
534 fn clone(&self) -> RedirectPolicy {
535 *self
536 }
537}
538
539impl Default for RedirectPolicy {
540 fn default() -> RedirectPolicy {
541 RedirectPolicy::FollowAll
542 }
543}
544
545
546fn get_host_and_port(url: &Url) -> crate::Result<(&str, u16)> {
547 let host = match url.host_str() {
548 Some(host) => host,
549 None => return Err(Error::Uri(UrlError::EmptyHost)),
550 };
551 trace!("host={:?}", host);
552 let port = match url.port_or_known_default() {
553 Some(port) => port,
554 None => return Err(Error::Uri(UrlError::InvalidPort)),
555 };
556 trace!("port={:?}", port);
557 Ok((host, port))
558}
559
560mod scheme {
561
562 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
563 pub enum Scheme {
564 Http,
565 Https,
566 Other(String),
567 }
568
569 impl<'a> From<&'a str> for Scheme {
570 fn from(s: &'a str) -> Scheme {
571 match s {
572 "http" => Scheme::Http,
573 "https" => Scheme::Https,
574 s => Scheme::Other(String::from(s)),
575 }
576 }
577 }
578
579 impl AsRef<str> for Scheme {
580 fn as_ref(&self) -> &str {
581 match *self {
582 Scheme::Http => "http",
583 Scheme::Https => "https",
584 Scheme::Other(ref s) => s,
585 }
586 }
587 }
588
589}
590
591#[cfg(test)]
592mod tests {
593 use std::io::Read;
594 use crate::header::Server;
595 use crate::http::h1::Http11Message;
596 use crate::mock::{MockStream, MockSsl};
597 use super::{Client, RedirectPolicy};
598 use super::scheme::Scheme;
599 use super::proxy::Proxy;
600 use super::pool::Pool;
601 use url::Url;
602
603 mock_connector!(MockRedirectPolicy {
604 "http://127.0.0.1" => "HTTP/1.1 301 Redirect\r\n\
605 Location: http://127.0.0.2\r\n\
606 Server: mock1\r\n\
607 \r\n\
608 "
609 "http://127.0.0.2" => "HTTP/1.1 302 Found\r\n\
610 Location: https://127.0.0.3\r\n\
611 Server: mock2\r\n\
612 \r\n\
613 "
614 "https://127.0.0.3" => "HTTP/1.1 200 OK\r\n\
615 Server: mock3\r\n\
616 \r\n\
617 "
618 });
619
620
621 #[test]
622 fn test_proxy() {
623 use super::pool::PooledStream;
624 type MessageStream = PooledStream<super::proxy::Proxied<MockStream, MockStream>>;
625 mock_connector!(ProxyConnector {
626 b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
627 });
628 let tunnel = Proxy {
629 connector: ProxyConnector,
630 proxy: (Scheme::Http, "example.proxy".into(), 8008),
631 ssl: MockSsl,
632 };
633 let mut client = Client::with_connector(Pool::with_connector(Default::default(), tunnel));
634 client.proxy = Some((Scheme::Http, "example.proxy".into(), 8008));
635 let mut dump = vec![];
636 client.get("http://127.0.0.1/foo/bar").send().unwrap().read_to_end(&mut dump).unwrap();
637
638 let box_message = client.protocol.new_message("127.0.0.1", 80, "http").unwrap();
639 let message = box_message.downcast::<Http11Message>().unwrap();
640 let stream = message.into_inner().downcast::<MessageStream>().unwrap().into_inner().into_normal().unwrap();
641
642 let s = ::std::str::from_utf8(&stream.write).unwrap();
643 let request_line = "GET http://127.0.0.1/foo/bar HTTP/1.1\r\n";
644 assert!(s.starts_with(request_line), "{:?} doesn't start with {:?}", s, request_line);
645 assert!(s.contains("Host: 127.0.0.1\r\n"));
646 }
647
648 #[test]
649 fn test_proxy_tunnel() {
650 use super::pool::PooledStream;
651 type MessageStream = PooledStream<super::proxy::Proxied<MockStream, MockStream>>;
652
653 mock_connector!(ProxyConnector {
654 b"HTTP/1.1 200 OK\r\n\r\n",
655 b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
656 });
657 let tunnel = Proxy {
658 connector: ProxyConnector,
659 proxy: (Scheme::Http, "example.proxy".into(), 8008),
660 ssl: MockSsl,
661 };
662 let mut client = Client::with_connector(Pool::with_connector(Default::default(), tunnel));
663 client.proxy = Some((Scheme::Http, "example.proxy".into(), 8008));
664 let mut dump = vec![];
665 client.get("https://127.0.0.1/foo/bar").send().unwrap().read_to_end(&mut dump).unwrap();
666
667 let box_message = client.protocol.new_message("127.0.0.1", 443, "https").unwrap();
668 let message = box_message.downcast::<Http11Message>().unwrap();
669 let stream = message.into_inner().downcast::<MessageStream>().unwrap().into_inner().into_tunneled().unwrap();
670
671 let s = ::std::str::from_utf8(&stream.write).unwrap();
672 let connect_line = "CONNECT 127.0.0.1:443 HTTP/1.1\r\nHost: 127.0.0.1:443\r\n\r\n";
673 assert_eq!(&s[..connect_line.len()], connect_line);
674
675 let s = &s[connect_line.len()..];
676 let request_line = "GET /foo/bar HTTP/1.1\r\n";
677 assert_eq!(&s[..request_line.len()], request_line);
678 assert!(s.contains("Host: 127.0.0.1\r\n"));
679 }
680
681 #[test]
682 fn test_redirect_followall() {
683 let mut client = Client::with_connector(MockRedirectPolicy);
684 client.set_redirect_policy(RedirectPolicy::FollowAll);
685
686 let res = client.get("http://127.0.0.1").send().unwrap();
687 assert_eq!(res.headers.get(), Some(&Server("mock3".to_owned())));
688 }
689
690 #[test]
691 fn test_redirect_dontfollow() {
692 let mut client = Client::with_connector(MockRedirectPolicy);
693 client.set_redirect_policy(RedirectPolicy::FollowNone);
694 let res = client.get("http://127.0.0.1").send().unwrap();
695 assert_eq!(res.headers.get(), Some(&Server("mock1".to_owned())));
696 }
697
698 #[test]
699 fn test_redirect_followif() {
700 fn follow_if(url: &Url) -> bool {
701 !url.as_str().contains("127.0.0.3")
702 }
703 let mut client = Client::with_connector(MockRedirectPolicy);
704 client.set_redirect_policy(RedirectPolicy::FollowIf(follow_if));
705 let res = client.get("http://127.0.0.1").send().unwrap();
706 assert_eq!(res.headers.get(), Some(&Server("mock2".to_owned())));
707 }
708
709 mock_connector!(Issue640Connector {
710 b"HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\n",
711 b"GET",
712 b"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n",
713 b"HEAD",
714 b"HTTP/1.1 200 OK\r\nContent-Length: 4\r\n\r\n",
715 b"POST"
716 });
717
718 #[test]
720 fn test_head_response_body_keep_alive() {
721 let client = Client::with_connector(Pool::with_connector(Default::default(), Issue640Connector));
722
723 let mut s = String::new();
724 client.get("http://127.0.0.1").send().unwrap().read_to_string(&mut s).unwrap();
725 assert_eq!(s, "GET");
726
727 let mut s = String::new();
728 client.head("http://127.0.0.1").send().unwrap().read_to_string(&mut s).unwrap();
729 assert_eq!(s, "");
730
731 let mut s = String::new();
732 client.post("http://127.0.0.1").send().unwrap().read_to_string(&mut s).unwrap();
733 assert_eq!(s, "POST");
734 }
735
736 #[test]
737 fn test_request_body_error_is_returned() {
738 mock_connector!(Connector {
739 b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n",
740 b"HELLO"
741 });
742
743 struct BadBody;
744
745 impl ::std::io::Read for BadBody {
746 fn read(&mut self, _buf: &mut [u8]) -> ::std::io::Result<usize> {
747 Err(std::io::Error::new(std::io::ErrorKind::Other, "BadBody read"))
748 }
749 }
750
751 let client = Client::with_connector(Connector);
752 let err = client.post("http://127.0.0.1").body(&mut BadBody).send().unwrap_err();
753 assert_eq!(err.to_string(), "BadBody read");
754 }
755}