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