committed by
					
						 Sean McArthur
						Sean McArthur
					
				
			
			
				
	
			
			
			
						parent
						
							81e0f1ff2a
						
					
				
				
					commit
					cf8944a0f0
				
			| @@ -1 +0,0 @@ | |||||||
| disable_all_formatting = true |  | ||||||
| @@ -55,7 +55,9 @@ dist: trusty | |||||||
| env: | env: | ||||||
|   global: |   global: | ||||||
|     - REQWEST_TEST_BODY_FULL=1 |     - REQWEST_TEST_BODY_FULL=1 | ||||||
|  | before_script: | ||||||
|  |   - rustup component add rustfmt | ||||||
| script: | script: | ||||||
|  |   - cargo fmt -- --check | ||||||
|   - cargo build $FEATURES |   - cargo build $FEATURES | ||||||
|   - cargo test -v $FEATURES -- --test-threads=1 |   - cargo test -v $FEATURES -- --test-threads=1 | ||||||
|   | |||||||
| @@ -1,16 +1,11 @@ | |||||||
| #![deny(warnings)] | #![deny(warnings)] | ||||||
|  |  | ||||||
| extern crate futures; |  | ||||||
| extern crate reqwest; |  | ||||||
| extern crate tokio; |  | ||||||
|  |  | ||||||
| use std::mem; |  | ||||||
| use std::io::{self, Cursor}; |  | ||||||
| use futures::{Future, Stream}; | use futures::{Future, Stream}; | ||||||
| use reqwest::r#async::{Client, Decoder}; | use reqwest::r#async::{Client, Decoder}; | ||||||
|  | use std::io::{self, Cursor}; | ||||||
|  | use std::mem; | ||||||
|  |  | ||||||
|  | fn fetch() -> impl Future<Item = (), Error = ()> { | ||||||
| fn fetch() -> impl Future<Item=(), Error=()> { |  | ||||||
|     Client::new() |     Client::new() | ||||||
|         .get("https://hyper.rs") |         .get("https://hyper.rs") | ||||||
|         .send() |         .send() | ||||||
| @@ -23,8 +18,7 @@ fn fetch() -> impl Future<Item=(), Error=()> { | |||||||
|         .map_err(|err| println!("request error: {}", err)) |         .map_err(|err| println!("request error: {}", err)) | ||||||
|         .map(|body| { |         .map(|body| { | ||||||
|             let mut body = Cursor::new(body); |             let mut body = Cursor::new(body); | ||||||
|             let _ = io::copy(&mut body, &mut io::stdout()) |             let _ = io::copy(&mut body, &mut io::stdout()).map_err(|err| { | ||||||
|                 .map_err(|err| { |  | ||||||
|                 println!("stdout error: {}", err); |                 println!("stdout error: {}", err); | ||||||
|             }); |             }); | ||||||
|         }) |         }) | ||||||
|   | |||||||
| @@ -1,11 +1,5 @@ | |||||||
| #![deny(warnings)] | #![deny(warnings)] | ||||||
|  |  | ||||||
| extern crate futures; |  | ||||||
| extern crate reqwest; |  | ||||||
| extern crate tokio; |  | ||||||
| extern crate serde; |  | ||||||
| extern crate serde_json; |  | ||||||
|  |  | ||||||
| use futures::Future; | use futures::Future; | ||||||
| use reqwest::r#async::{Client, Response}; | use reqwest::r#async::{Client, Response}; | ||||||
| use serde::Deserialize; | use serde::Deserialize; | ||||||
| @@ -21,27 +15,18 @@ struct SlideshowContainer { | |||||||
|     slideshow: Slideshow, |     slideshow: Slideshow, | ||||||
| } | } | ||||||
|  |  | ||||||
| fn fetch() -> impl Future<Item=(), Error=()> { | fn fetch() -> impl Future<Item = (), Error = ()> { | ||||||
|     let client = Client::new(); |     let client = Client::new(); | ||||||
|  |  | ||||||
|     let json = |mut res : Response | { |     let json = |mut res: Response| res.json::<SlideshowContainer>(); | ||||||
|         res.json::<SlideshowContainer>() |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     let request1 = |     let request1 = client.get("https://httpbin.org/json").send().and_then(json); | ||||||
|         client |  | ||||||
|             .get("https://httpbin.org/json") |  | ||||||
|             .send() |  | ||||||
|             .and_then(json); |  | ||||||
|  |  | ||||||
|     let request2 = |     let request2 = client.get("https://httpbin.org/json").send().and_then(json); | ||||||
|         client |  | ||||||
|             .get("https://httpbin.org/json") |  | ||||||
|             .send() |  | ||||||
|             .and_then(json); |  | ||||||
|  |  | ||||||
|     request1.join(request2) |     request1 | ||||||
|         .map(|(res1, res2)|{ |         .join(request2) | ||||||
|  |         .map(|(res1, res2)| { | ||||||
|             println!("{:?}", res1); |             println!("{:?}", res1); | ||||||
|             println!("{:?}", res2); |             println!("{:?}", res2); | ||||||
|         }) |         }) | ||||||
|   | |||||||
| @@ -1,18 +1,11 @@ | |||||||
| #![deny(warnings)] | #![deny(warnings)] | ||||||
|  |  | ||||||
| #[macro_use] |  | ||||||
| extern crate futures; |  | ||||||
| extern crate bytes; |  | ||||||
| extern crate reqwest; |  | ||||||
| extern crate tokio; |  | ||||||
| extern crate tokio_threadpool; |  | ||||||
|  |  | ||||||
| use std::io::{self, Cursor}; | use std::io::{self, Cursor}; | ||||||
| use std::mem; | use std::mem; | ||||||
| use std::path::Path; | use std::path::Path; | ||||||
|  |  | ||||||
| use bytes::Bytes; | use bytes::Bytes; | ||||||
| use futures::{Async, Future, Poll, Stream}; | use futures::{try_ready, Async, Future, Poll, Stream}; | ||||||
| use reqwest::r#async::{Client, Decoder}; | use reqwest::r#async::{Client, Decoder}; | ||||||
| use tokio::fs::File; | use tokio::fs::File; | ||||||
| use tokio::io::AsyncRead; | use tokio::io::AsyncRead; | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| extern crate reqwest; |  | ||||||
|  |  | ||||||
| fn main() { | fn main() { | ||||||
|     reqwest::Client::new() |     reqwest::Client::new() | ||||||
|         .post("http://www.baidu.com") |         .post("http://www.baidu.com") | ||||||
|   | |||||||
| @@ -3,19 +3,16 @@ | |||||||
| //! This is useful for some ad-hoc experiments and situations when you don't | //! This is useful for some ad-hoc experiments and situations when you don't | ||||||
| //! really care about the structure of the JSON and just need to display it or | //! really care about the structure of the JSON and just need to display it or | ||||||
| //! process it at runtime. | //! process it at runtime. | ||||||
| extern crate reqwest; | use serde_json::json; | ||||||
| #[macro_use] extern crate serde_json; |  | ||||||
|  |  | ||||||
| fn main() -> Result<(), reqwest::Error> { | fn main() -> Result<(), reqwest::Error> { | ||||||
|     let echo_json: serde_json::Value = reqwest::Client::new() |     let echo_json: serde_json::Value = reqwest::Client::new() | ||||||
|         .post("https://jsonplaceholder.typicode.com/posts") |         .post("https://jsonplaceholder.typicode.com/posts") | ||||||
|         .json( |         .json(&json!({ | ||||||
|             &json!({ |  | ||||||
|             "title": "Reqwest.rs", |             "title": "Reqwest.rs", | ||||||
|             "body": "https://docs.rs/reqwest", |             "body": "https://docs.rs/reqwest", | ||||||
|             "userId": 1 |             "userId": 1 | ||||||
|             }) |         })) | ||||||
|         ) |  | ||||||
|         .send()? |         .send()? | ||||||
|         .json()?; |         .json()?; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,9 +3,6 @@ | |||||||
| //! In contrast to the arbitrary JSON example, this brings up the full power of | //! In contrast to the arbitrary JSON example, this brings up the full power of | ||||||
| //! Rust compile-time type system guaranties though it requires a little bit | //! Rust compile-time type system guaranties though it requires a little bit | ||||||
| //! more code. | //! more code. | ||||||
| extern crate reqwest; |  | ||||||
| extern crate serde; |  | ||||||
| extern crate serde_json; |  | ||||||
|  |  | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
|  |  | ||||||
| @@ -23,7 +20,7 @@ fn main() -> Result<(), reqwest::Error> { | |||||||
|         id: None, |         id: None, | ||||||
|         title: "Reqwest.rs".into(), |         title: "Reqwest.rs".into(), | ||||||
|         body: "https://docs.rs/reqwest".into(), |         body: "https://docs.rs/reqwest".into(), | ||||||
|         user_id: 1 |         user_id: 1, | ||||||
|     }; |     }; | ||||||
|     let new_post: Post = reqwest::Client::new() |     let new_post: Post = reqwest::Client::new() | ||||||
|         .post("https://jsonplaceholder.typicode.com/posts") |         .post("https://jsonplaceholder.typicode.com/posts") | ||||||
|   | |||||||
| @@ -2,9 +2,6 @@ | |||||||
|  |  | ||||||
| //! `cargo run --example simple` | //! `cargo run --example simple` | ||||||
|  |  | ||||||
| extern crate reqwest; |  | ||||||
| extern crate env_logger; |  | ||||||
|  |  | ||||||
| fn main() -> Result<(), Box<dyn std::error::Error>> { | fn main() -> Result<(), Box<dyn std::error::Error>> { | ||||||
|     env_logger::init(); |     env_logger::init(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| pub use self::body::{Body, Chunk}; | pub use self::body::{Body, Chunk}; | ||||||
| pub use self::decoder::{Decoder, ReadableChunks}; |  | ||||||
| pub use self::client::{Client, ClientBuilder}; | pub use self::client::{Client, ClientBuilder}; | ||||||
|  | pub use self::decoder::{Decoder, ReadableChunks}; | ||||||
| pub use self::request::{Request, RequestBuilder}; | pub use self::request::{Request, RequestBuilder}; | ||||||
| pub use self::response::{Response, ResponseBuilderExt}; | pub use self::response::{Response, ResponseBuilderExt}; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
|  |  | ||||||
| use futures::{Future, Stream, Poll, Async, try_ready}; |  | ||||||
| use bytes::{Buf, Bytes}; | use bytes::{Buf, Bytes}; | ||||||
|  | use futures::{try_ready, Async, Future, Poll, Stream}; | ||||||
| use hyper::body::Payload; | use hyper::body::Payload; | ||||||
| use tokio::timer::Delay; | use tokio::timer::Delay; | ||||||
|  |  | ||||||
| @@ -15,7 +15,7 @@ enum Inner { | |||||||
|     Hyper { |     Hyper { | ||||||
|         body: hyper::Body, |         body: hyper::Body, | ||||||
|         timeout: Option<Delay>, |         timeout: Option<Delay>, | ||||||
|     } |     }, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Body { | impl Body { | ||||||
| @@ -29,10 +29,7 @@ impl Body { | |||||||
|     #[inline] |     #[inline] | ||||||
|     pub(crate) fn response(body: hyper::Body, timeout: Option<Delay>) -> Body { |     pub(crate) fn response(body: hyper::Body, timeout: Option<Delay>) -> Body { | ||||||
|         Body { |         Body { | ||||||
|             inner: Inner::Hyper { |             inner: Inner::Hyper { body, timeout }, | ||||||
|                 body, |  | ||||||
|                 timeout, |  | ||||||
|             }, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -65,7 +62,7 @@ impl Body { | |||||||
|             Inner::Hyper { body, timeout } => { |             Inner::Hyper { body, timeout } => { | ||||||
|                 debug_assert!(timeout.is_none()); |                 debug_assert!(timeout.is_none()); | ||||||
|                 (None, body) |                 (None, body) | ||||||
|             }, |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -77,14 +74,17 @@ impl Stream for Body { | |||||||
|     #[inline] |     #[inline] | ||||||
|     fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { |     fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { | ||||||
|         let opt = match self.inner { |         let opt = match self.inner { | ||||||
|             Inner::Hyper { ref mut body, ref mut timeout } => { |             Inner::Hyper { | ||||||
|  |                 ref mut body, | ||||||
|  |                 ref mut timeout, | ||||||
|  |             } => { | ||||||
|                 if let Some(ref mut timeout) = timeout { |                 if let Some(ref mut timeout) = timeout { | ||||||
|                     if let Async::Ready(()) = try_!(timeout.poll()) { |                     if let Async::Ready(()) = try_!(timeout.poll()) { | ||||||
|                         return Err(crate::error::timedout(None)); |                         return Err(crate::error::timedout(None)); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 try_ready!(body.poll_data().map_err(crate::error::from)) |                 try_ready!(body.poll_data().map_err(crate::error::from)) | ||||||
|             }, |             } | ||||||
|             Inner::Reusable(ref mut bytes) => { |             Inner::Reusable(ref mut bytes) => { | ||||||
|                 return if bytes.is_empty() { |                 return if bytes.is_empty() { | ||||||
|                     Ok(Async::Ready(None)) |                     Ok(Async::Ready(None)) | ||||||
| @@ -93,12 +93,10 @@ impl Stream for Body { | |||||||
|                     *bytes = Bytes::new(); |                     *bytes = Bytes::new(); | ||||||
|                     Ok(Async::Ready(Some(chunk))) |                     Ok(Async::Ready(Some(chunk))) | ||||||
|                 }; |                 }; | ||||||
|             }, |             } | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         Ok(Async::Ready(opt.map(|chunk| Chunk { |         Ok(Async::Ready(opt.map(|chunk| Chunk { inner: chunk }))) | ||||||
|             inner: chunk, |  | ||||||
|         }))) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -161,7 +159,7 @@ impl Chunk { | |||||||
|     #[inline] |     #[inline] | ||||||
|     pub(crate) fn from_chunk(chunk: Bytes) -> Chunk { |     pub(crate) fn from_chunk(chunk: Bytes) -> Chunk { | ||||||
|         Chunk { |         Chunk { | ||||||
|             inner: hyper::Chunk::from(chunk) |             inner: hyper::Chunk::from(chunk), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -197,7 +195,9 @@ impl std::ops::Deref for Chunk { | |||||||
|  |  | ||||||
| impl Extend<u8> for Chunk { | impl Extend<u8> for Chunk { | ||||||
|     fn extend<T>(&mut self, iter: T) |     fn extend<T>(&mut self, iter: T) | ||||||
|     where T: IntoIterator<Item=u8> { |     where | ||||||
|  |         T: IntoIterator<Item = u8>, | ||||||
|  |     { | ||||||
|         self.inner.extend(iter) |         self.inner.extend(iter) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -219,7 +219,9 @@ impl From<Vec<u8>> for Chunk { | |||||||
|  |  | ||||||
| impl From<&'static [u8]> for Chunk { | impl From<&'static [u8]> for Chunk { | ||||||
|     fn from(slice: &'static [u8]) -> Chunk { |     fn from(slice: &'static [u8]) -> Chunk { | ||||||
|         Chunk { inner: slice.into() } |         Chunk { | ||||||
|  |             inner: slice.into(), | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -231,13 +233,17 @@ impl From<String> for Chunk { | |||||||
|  |  | ||||||
| impl From<&'static str> for Chunk { | impl From<&'static str> for Chunk { | ||||||
|     fn from(slice: &'static str) -> Chunk { |     fn from(slice: &'static str) -> Chunk { | ||||||
|         Chunk { inner: slice.into() } |         Chunk { | ||||||
|  |             inner: slice.into(), | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl From<Bytes> for Chunk { | impl From<Bytes> for Chunk { | ||||||
|     fn from(bytes: Bytes) -> Chunk { |     fn from(bytes: Bytes) -> Chunk { | ||||||
|         Chunk { inner: bytes.into() } |         Chunk { | ||||||
|  |             inner: bytes.into(), | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -249,8 +255,7 @@ impl From<Chunk> for hyper::Chunk { | |||||||
|  |  | ||||||
| impl fmt::Debug for Body { | impl fmt::Debug for Body { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         f.debug_struct("Body") |         f.debug_struct("Body").finish() | ||||||
|             .finish() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,26 +1,14 @@ | |||||||
| use std::{fmt, str}; | use std::net::IpAddr; | ||||||
| use std::sync::{Arc, RwLock}; | use std::sync::{Arc, RwLock}; | ||||||
| use std::time::Duration; | use std::time::Duration; | ||||||
| use std::net::IpAddr; | use std::{fmt, str}; | ||||||
|  |  | ||||||
|  | use crate::header::{ | ||||||
|  |     Entry, HeaderMap, HeaderValue, ACCEPT, ACCEPT_ENCODING, CONTENT_ENCODING, CONTENT_LENGTH, | ||||||
|  |     CONTENT_TYPE, LOCATION, PROXY_AUTHORIZATION, RANGE, REFERER, TRANSFER_ENCODING, USER_AGENT, | ||||||
|  | }; | ||||||
| use bytes::Bytes; | use bytes::Bytes; | ||||||
| use futures::{Async, Future, Poll}; | use futures::{Async, Future, Poll}; | ||||||
| use crate::header::{ |  | ||||||
|     Entry, |  | ||||||
|     HeaderMap, |  | ||||||
|     HeaderValue, |  | ||||||
|     ACCEPT, |  | ||||||
|     ACCEPT_ENCODING, |  | ||||||
|     CONTENT_LENGTH, |  | ||||||
|     CONTENT_ENCODING, |  | ||||||
|     CONTENT_TYPE, |  | ||||||
|     LOCATION, |  | ||||||
|     PROXY_AUTHORIZATION, |  | ||||||
|     RANGE, |  | ||||||
|     REFERER, |  | ||||||
|     TRANSFER_ENCODING, |  | ||||||
|     USER_AGENT, |  | ||||||
| }; |  | ||||||
| use http::Uri; | use http::Uri; | ||||||
| use hyper::client::ResponseFuture; | use hyper::client::ResponseFuture; | ||||||
| use mime; | use mime; | ||||||
| @@ -28,24 +16,22 @@ use mime; | |||||||
| use native_tls::TlsConnector; | use native_tls::TlsConnector; | ||||||
| use tokio::{clock, timer::Delay}; | use tokio::{clock, timer::Delay}; | ||||||
|  |  | ||||||
| use log::{debug}; | use log::debug; | ||||||
|  |  | ||||||
|  |  | ||||||
| use super::request::{Request, RequestBuilder}; | use super::request::{Request, RequestBuilder}; | ||||||
| use super::response::Response; | use super::response::Response; | ||||||
| use crate::connect::Connector; | use crate::connect::Connector; | ||||||
| use crate::into_url::{expect_uri, try_uri}; |  | ||||||
| use crate::cookie; | use crate::cookie; | ||||||
| use crate::redirect::{self, RedirectPolicy, remove_sensitive_headers}; | use crate::into_url::{expect_uri, try_uri}; | ||||||
| use crate::{IntoUrl, Method, Proxy, StatusCode, Url}; |  | ||||||
| use crate::proxy::get_proxies; | use crate::proxy::get_proxies; | ||||||
| #[cfg(feature = "tls")] | use crate::redirect::{self, remove_sensitive_headers, RedirectPolicy}; | ||||||
| use crate::{Certificate, Identity}; |  | ||||||
| #[cfg(feature = "tls")] | #[cfg(feature = "tls")] | ||||||
| use crate::tls::TlsBackend; | use crate::tls::TlsBackend; | ||||||
|  | #[cfg(feature = "tls")] | ||||||
|  | use crate::{Certificate, Identity}; | ||||||
|  | use crate::{IntoUrl, Method, Proxy, StatusCode, Url}; | ||||||
|  |  | ||||||
| static DEFAULT_USER_AGENT: &str = | static DEFAULT_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); | ||||||
|     concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")); |  | ||||||
|  |  | ||||||
| /// An asynchronous `Client` to make Requests with. | /// An asynchronous `Client` to make Requests with. | ||||||
| /// | /// | ||||||
| @@ -98,7 +84,10 @@ impl ClientBuilder { | |||||||
|     pub fn new() -> ClientBuilder { |     pub fn new() -> ClientBuilder { | ||||||
|         let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2); |         let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2); | ||||||
|         headers.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT)); |         headers.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT)); | ||||||
|         headers.insert(ACCEPT, HeaderValue::from_str(mime::STAR_STAR.as_ref()).expect("unable to parse mime")); |         headers.insert( | ||||||
|  |             ACCEPT, | ||||||
|  |             HeaderValue::from_str(mime::STAR_STAR.as_ref()).expect("unable to parse mime"), | ||||||
|  |         ); | ||||||
|  |  | ||||||
|         ClientBuilder { |         ClientBuilder { | ||||||
|             config: Config { |             config: Config { | ||||||
| @@ -156,8 +145,13 @@ impl ClientBuilder { | |||||||
|                         id.add_to_native_tls(&mut tls)?; |                         id.add_to_native_tls(&mut tls)?; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     Connector::new_default_tls(tls, proxies.clone(), config.local_address, config.nodelay)? |                     Connector::new_default_tls( | ||||||
|                 }, |                         tls, | ||||||
|  |                         proxies.clone(), | ||||||
|  |                         config.local_address, | ||||||
|  |                         config.nodelay, | ||||||
|  |                     )? | ||||||
|  |                 } | ||||||
|                 #[cfg(feature = "rustls-tls")] |                 #[cfg(feature = "rustls-tls")] | ||||||
|                 TlsBackend::Rustls => { |                 TlsBackend::Rustls => { | ||||||
|                     use crate::tls::NoVerifier; |                     use crate::tls::NoVerifier; | ||||||
| @@ -166,15 +160,14 @@ impl ClientBuilder { | |||||||
|                     if config.http2_only { |                     if config.http2_only { | ||||||
|                         tls.set_protocols(&["h2".into()]); |                         tls.set_protocols(&["h2".into()]); | ||||||
|                     } else { |                     } else { | ||||||
|                         tls.set_protocols(&[ |                         tls.set_protocols(&["h2".into(), "http/1.1".into()]); | ||||||
|                             "h2".into(), |  | ||||||
|                             "http/1.1".into(), |  | ||||||
|                         ]); |  | ||||||
|                     } |                     } | ||||||
|                     tls.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); |                     tls.root_store | ||||||
|  |                         .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); | ||||||
|  |  | ||||||
|                     if !config.certs_verification { |                     if !config.certs_verification { | ||||||
|                         tls.dangerous().set_certificate_verifier(Arc::new(NoVerifier)); |                         tls.dangerous() | ||||||
|  |                             .set_certificate_verifier(Arc::new(NoVerifier)); | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     for cert in config.root_certs { |                     for cert in config.root_certs { | ||||||
| @@ -185,7 +178,12 @@ impl ClientBuilder { | |||||||
|                         id.add_to_rustls(&mut tls)?; |                         id.add_to_rustls(&mut tls)?; | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     Connector::new_rustls_tls(tls, proxies.clone(), config.local_address, config.nodelay)? |                     Connector::new_rustls_tls( | ||||||
|  |                         tls, | ||||||
|  |                         proxies.clone(), | ||||||
|  |                         config.local_address, | ||||||
|  |                         config.nodelay, | ||||||
|  |                     )? | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -208,9 +206,7 @@ impl ClientBuilder { | |||||||
|  |  | ||||||
|         let hyper_client = builder.build(connector); |         let hyper_client = builder.build(connector); | ||||||
|  |  | ||||||
|         let proxies_maybe_http_auth = proxies |         let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth()); | ||||||
|             .iter() |  | ||||||
|             .any(|p| p.maybe_has_http_auth()); |  | ||||||
|  |  | ||||||
|         let cookie_store = config.cookie_store.map(RwLock::new); |         let cookie_store = config.cookie_store.map(RwLock::new); | ||||||
|  |  | ||||||
| @@ -277,7 +273,10 @@ impl ClientBuilder { | |||||||
|     /// site will be trusted for use from any other. This introduces a |     /// site will be trusted for use from any other. This introduces a | ||||||
|     /// significant vulnerability to man-in-the-middle attacks. |     /// significant vulnerability to man-in-the-middle attacks. | ||||||
|     #[cfg(feature = "default-tls")] |     #[cfg(feature = "default-tls")] | ||||||
|     pub fn danger_accept_invalid_hostnames(mut self, accept_invalid_hostname: bool) -> ClientBuilder { |     pub fn danger_accept_invalid_hostnames( | ||||||
|  |         mut self, | ||||||
|  |         accept_invalid_hostname: bool, | ||||||
|  |     ) -> ClientBuilder { | ||||||
|         self.config.hostname_verification = !accept_invalid_hostname; |         self.config.hostname_verification = !accept_invalid_hostname; | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
| @@ -299,7 +298,6 @@ impl ClientBuilder { | |||||||
|         self |         self | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /// Sets the default headers for every request. |     /// Sets the default headers for every request. | ||||||
|     pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder { |     pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder { | ||||||
|         for (key, value) in headers.iter() { |         for (key, value) in headers.iter() { | ||||||
| @@ -349,7 +347,6 @@ impl ClientBuilder { | |||||||
|         self |         self | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /// Set a `RedirectPolicy` for this client. |     /// Set a `RedirectPolicy` for this client. | ||||||
|     /// |     /// | ||||||
|     /// Default will follow redirects up to a maximum of 10. |     /// Default will follow redirects up to a maximum of 10. | ||||||
| @@ -454,9 +451,7 @@ impl Client { | |||||||
|     /// Use `Client::builder()` if you wish to handle the failure as an `Error` |     /// Use `Client::builder()` if you wish to handle the failure as an `Error` | ||||||
|     /// instead of panicking. |     /// instead of panicking. | ||||||
|     pub fn new() -> Client { |     pub fn new() -> Client { | ||||||
|         ClientBuilder::new() |         ClientBuilder::new().build().expect("Client::new()") | ||||||
|             .build() |  | ||||||
|             .expect("Client::new()") |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Creates a `ClientBuilder` to configure a `Client`. |     /// Creates a `ClientBuilder` to configure a `Client`. | ||||||
| @@ -529,9 +524,7 @@ impl Client { | |||||||
|     /// |     /// | ||||||
|     /// This method fails whenever supplied `Url` cannot be parsed. |     /// This method fails whenever supplied `Url` cannot be parsed. | ||||||
|     pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder { |     pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder { | ||||||
|         let req = url |         let req = url.into_url().map(move |url| Request::new(method, url)); | ||||||
|             .into_url() |  | ||||||
|             .map(move |url| Request::new(method, url)); |  | ||||||
|         RequestBuilder::new(self.clone(), req) |         RequestBuilder::new(self.clone(), req) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -551,14 +544,8 @@ impl Client { | |||||||
|         self.execute_request(request) |         self.execute_request(request) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     pub(super) fn execute_request(&self, req: Request) -> Pending { |     pub(super) fn execute_request(&self, req: Request) -> Pending { | ||||||
|         let ( |         let (method, url, mut headers, body) = req.pieces(); | ||||||
|             method, |  | ||||||
|             url, |  | ||||||
|             mut headers, |  | ||||||
|             body |  | ||||||
|         ) = req.pieces(); |  | ||||||
|  |  | ||||||
|         // insert default headers in the request headers |         // insert default headers in the request headers | ||||||
|         // without overwriting already appended headers. |         // without overwriting already appended headers. | ||||||
| @@ -576,9 +563,8 @@ impl Client { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if self.inner.gzip && |         if self.inner.gzip && !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE) | ||||||
|             !headers.contains_key(ACCEPT_ENCODING) && |         { | ||||||
|             !headers.contains_key(RANGE) { |  | ||||||
|             headers.insert(ACCEPT_ENCODING, HeaderValue::from_static("gzip")); |             headers.insert(ACCEPT_ENCODING, HeaderValue::from_static("gzip")); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -588,10 +574,8 @@ impl Client { | |||||||
|             Some(body) => { |             Some(body) => { | ||||||
|                 let (reusable, body) = body.into_hyper(); |                 let (reusable, body) = body.into_hyper(); | ||||||
|                 (Some(reusable), body) |                 (Some(reusable), body) | ||||||
|             }, |  | ||||||
|             None => { |  | ||||||
|                 (None, hyper::Body::empty()) |  | ||||||
|             } |             } | ||||||
|  |             None => (None, hyper::Body::empty()), | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         self.proxy_auth(&uri, &mut headers); |         self.proxy_auth(&uri, &mut headers); | ||||||
| @@ -606,9 +590,10 @@ impl Client { | |||||||
|  |  | ||||||
|         let in_flight = self.inner.hyper.request(req); |         let in_flight = self.inner.hyper.request(req); | ||||||
|  |  | ||||||
|         let timeout = self.inner.request_timeout.map(|dur| { |         let timeout = self | ||||||
|             Delay::new(clock::now() + dur) |             .inner | ||||||
|         }); |             .request_timeout | ||||||
|  |             .map(|dur| Delay::new(clock::now() + dur)); | ||||||
|  |  | ||||||
|         Pending { |         Pending { | ||||||
|             inner: PendingInner::Request(PendingRequest { |             inner: PendingInner::Request(PendingRequest { | ||||||
| @@ -643,14 +628,10 @@ impl Client { | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|         for proxy in self.inner.proxies.iter() { |         for proxy in self.inner.proxies.iter() { | ||||||
|             if proxy.is_match(dst) { |             if proxy.is_match(dst) { | ||||||
|                 if let Some(header) = proxy.http_basic_auth(dst) { |                 if let Some(header) = proxy.http_basic_auth(dst) { | ||||||
|                     headers.insert( |                     headers.insert(PROXY_AUTHORIZATION, header); | ||||||
|                         PROXY_AUTHORIZATION, |  | ||||||
|                         header, |  | ||||||
|                     ); |  | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 break; |                 break; | ||||||
| @@ -671,8 +652,7 @@ impl fmt::Debug for Client { | |||||||
|  |  | ||||||
| impl fmt::Debug for ClientBuilder { | impl fmt::Debug for ClientBuilder { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         f.debug_struct("ClientBuilder") |         f.debug_struct("ClientBuilder").finish() | ||||||
|             .finish() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -726,7 +706,9 @@ impl Future for Pending { | |||||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { |     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||||
|         match self.inner { |         match self.inner { | ||||||
|             PendingInner::Request(ref mut req) => req.poll(), |             PendingInner::Request(ref mut req) => req.poll(), | ||||||
|             PendingInner::Error(ref mut err) => Err(err.take().expect("Pending error polled more than once")), |             PendingInner::Error(ref mut err) => { | ||||||
|  |                 Err(err.take().expect("Pending error polled more than once")) | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -755,33 +737,35 @@ impl Future for PendingRequest { | |||||||
|                 store.0.store_response_cookies(cookies, &self.url); |                 store.0.store_response_cookies(cookies, &self.url); | ||||||
|             } |             } | ||||||
|             let should_redirect = match res.status() { |             let should_redirect = match res.status() { | ||||||
|                 StatusCode::MOVED_PERMANENTLY | |                 StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => { | ||||||
|                 StatusCode::FOUND | |  | ||||||
|                 StatusCode::SEE_OTHER => { |  | ||||||
|                     self.body = None; |                     self.body = None; | ||||||
|                     for header in &[TRANSFER_ENCODING, CONTENT_ENCODING, CONTENT_TYPE, CONTENT_LENGTH] { |                     for header in &[ | ||||||
|  |                         TRANSFER_ENCODING, | ||||||
|  |                         CONTENT_ENCODING, | ||||||
|  |                         CONTENT_TYPE, | ||||||
|  |                         CONTENT_LENGTH, | ||||||
|  |                     ] { | ||||||
|                         self.headers.remove(header); |                         self.headers.remove(header); | ||||||
|                     } |                     } | ||||||
|  |  | ||||||
|                     match self.method { |                     match self.method { | ||||||
|                         Method::GET | Method::HEAD => {}, |                         Method::GET | Method::HEAD => {} | ||||||
|                         _ => { |                         _ => { | ||||||
|                             self.method = Method::GET; |                             self.method = Method::GET; | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     true |                     true | ||||||
|                 }, |                 } | ||||||
|                 StatusCode::TEMPORARY_REDIRECT | |                 StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => { | ||||||
|                 StatusCode::PERMANENT_REDIRECT => match self.body { |                     match self.body { | ||||||
|                         Some(Some(_)) | None => true, |                         Some(Some(_)) | None => true, | ||||||
|                         Some(None) => false, |                         Some(None) => false, | ||||||
|                 }, |                     } | ||||||
|  |                 } | ||||||
|                 _ => false, |                 _ => false, | ||||||
|             }; |             }; | ||||||
|             if should_redirect { |             if should_redirect { | ||||||
|                 let loc = res.headers() |                 let loc = res.headers().get(LOCATION).and_then(|val| { | ||||||
|                     .get(LOCATION) |  | ||||||
|                     .and_then(|val| { |  | ||||||
|                     let loc = (|| -> Option<Url> { |                     let loc = (|| -> Option<Url> { | ||||||
|                         // Some sites may send a utf-8 Location header, |                         // Some sites may send a utf-8 Location header, | ||||||
|                         // even though we're supposed to treat those bytes |                         // even though we're supposed to treat those bytes | ||||||
| @@ -812,11 +796,10 @@ impl Future for PendingRequest { | |||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                     self.urls.push(self.url.clone()); |                     self.urls.push(self.url.clone()); | ||||||
|                     let action = self.client.redirect_policy.check( |                     let action = self | ||||||
|                         res.status(), |                         .client | ||||||
|                         &loc, |                         .redirect_policy | ||||||
|                         &self.urls, |                         .check(res.status(), &loc, &self.urls); | ||||||
|                     ); |  | ||||||
|  |  | ||||||
|                     match action { |                     match action { | ||||||
|                         redirect::Action::Follow => { |                         redirect::Action::Follow => { | ||||||
| @@ -844,13 +827,13 @@ impl Future for PendingRequest { | |||||||
|                             *req.headers_mut() = self.headers.clone(); |                             *req.headers_mut() = self.headers.clone(); | ||||||
|                             self.in_flight = self.client.hyper.request(req); |                             self.in_flight = self.client.hyper.request(req); | ||||||
|                             continue; |                             continue; | ||||||
|                         }, |                         } | ||||||
|                         redirect::Action::Stop => { |                         redirect::Action::Stop => { | ||||||
|                             debug!("redirect_policy disallowed redirection to '{}'", loc); |                             debug!("redirect_policy disallowed redirection to '{}'", loc); | ||||||
|                         }, |                         } | ||||||
|                         redirect::Action::LoopDetected => { |                         redirect::Action::LoopDetected => { | ||||||
|                             return Err(crate::error::loop_detected(self.url.clone())); |                             return Err(crate::error::loop_detected(self.url.clone())); | ||||||
|                         }, |                         } | ||||||
|                         redirect::Action::TooManyRedirects => { |                         redirect::Action::TooManyRedirects => { | ||||||
|                             return Err(crate::error::too_many_redirects(self.url.clone())); |                             return Err(crate::error::too_many_redirects(self.url.clone())); | ||||||
|                         } |                         } | ||||||
| @@ -866,17 +849,12 @@ impl Future for PendingRequest { | |||||||
| impl fmt::Debug for Pending { | impl fmt::Debug for Pending { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         match self.inner { |         match self.inner { | ||||||
|             PendingInner::Request(ref req) => { |             PendingInner::Request(ref req) => f | ||||||
|                 f.debug_struct("Pending") |                 .debug_struct("Pending") | ||||||
|                 .field("method", &req.method) |                 .field("method", &req.method) | ||||||
|                 .field("url", &req.url) |                 .field("url", &req.url) | ||||||
|                     .finish() |                 .finish(), | ||||||
|             }, |             PendingInner::Error(ref err) => f.debug_struct("Pending").field("error", err).finish(), | ||||||
|             PendingInner::Error(ref err) => { |  | ||||||
|                 f.debug_struct("Pending") |  | ||||||
|                     .field("error", err) |  | ||||||
|                     .finish() |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -903,7 +881,7 @@ fn add_cookie_header(headers: &mut HeaderMap, cookie_store: &cookie::CookieStore | |||||||
|     if !header.is_empty() { |     if !header.is_empty() { | ||||||
|         headers.insert( |         headers.insert( | ||||||
|             crate::header::COOKIE, |             crate::header::COOKIE, | ||||||
|             HeaderValue::from_bytes(header.as_bytes()).unwrap() |             HeaderValue::from_bytes(header.as_bytes()).unwrap(), | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -20,18 +20,18 @@ The following types directly support the gzip compression case: | |||||||
| - `Pending` is a non-blocking constructor for a `Decoder` in case the body needs to be checked for EOF | - `Pending` is a non-blocking constructor for a `Decoder` in case the body needs to be checked for EOF | ||||||
| */ | */ | ||||||
|  |  | ||||||
| use std::fmt; |  | ||||||
| use std::mem; |  | ||||||
| use std::cmp; | use std::cmp; | ||||||
|  | use std::fmt; | ||||||
| use std::io::{self, Read}; | use std::io::{self, Read}; | ||||||
|  | use std::mem; | ||||||
|  |  | ||||||
| use bytes::{Buf, BufMut, BytesMut}; | use bytes::{Buf, BufMut, BytesMut}; | ||||||
| use flate2::read::GzDecoder; | use flate2::read::GzDecoder; | ||||||
| use futures::{Async, Future, Poll, Stream}; | use futures::{Async, Future, Poll, Stream}; | ||||||
| use hyper::{HeaderMap}; |  | ||||||
| use hyper::header::{CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING}; | use hyper::header::{CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING}; | ||||||
|  | use hyper::HeaderMap; | ||||||
|  |  | ||||||
| use log::{warn}; | use log::warn; | ||||||
|  |  | ||||||
| use super::{Body, Chunk}; | use super::{Body, Chunk}; | ||||||
| use crate::error; | use crate::error; | ||||||
| @@ -42,7 +42,7 @@ const INIT_BUFFER_SIZE: usize = 8192; | |||||||
| /// | /// | ||||||
| /// The inner decoder may be constructed asynchronously. | /// The inner decoder may be constructed asynchronously. | ||||||
| pub struct Decoder { | pub struct Decoder { | ||||||
|     inner: Inner |     inner: Inner, | ||||||
| } | } | ||||||
|  |  | ||||||
| enum Inner { | enum Inner { | ||||||
| @@ -51,7 +51,7 @@ enum Inner { | |||||||
|     /// A `Gzip` decoder will uncompress the gzipped response content before returning it. |     /// A `Gzip` decoder will uncompress the gzipped response content before returning it. | ||||||
|     Gzip(Gzip), |     Gzip(Gzip), | ||||||
|     /// A decoder that doesn't have a value yet. |     /// A decoder that doesn't have a value yet. | ||||||
|     Pending(Pending) |     Pending(Pending), | ||||||
| } | } | ||||||
|  |  | ||||||
| /// A future attempt to poll the response body for EOF so we know whether to use gzip or not. | /// A future attempt to poll the response body for EOF so we know whether to use gzip or not. | ||||||
| @@ -68,8 +68,7 @@ struct Gzip { | |||||||
|  |  | ||||||
| impl fmt::Debug for Decoder { | impl fmt::Debug for Decoder { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         f.debug_struct("Decoder") |         f.debug_struct("Decoder").finish() | ||||||
|             .finish() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -80,7 +79,7 @@ impl Decoder { | |||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn empty() -> Decoder { |     pub fn empty() -> Decoder { | ||||||
|         Decoder { |         Decoder { | ||||||
|             inner: Inner::PlainText(Body::empty()) |             inner: Inner::PlainText(Body::empty()), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -90,7 +89,7 @@ impl Decoder { | |||||||
|     #[inline] |     #[inline] | ||||||
|     fn plain_text(body: Body) -> Decoder { |     fn plain_text(body: Body) -> Decoder { | ||||||
|         Decoder { |         Decoder { | ||||||
|             inner: Inner::PlainText(body) |             inner: Inner::PlainText(body), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -100,7 +99,9 @@ impl Decoder { | |||||||
|     #[inline] |     #[inline] | ||||||
|     fn gzip(body: Body) -> Decoder { |     fn gzip(body: Body) -> Decoder { | ||||||
|         Decoder { |         Decoder { | ||||||
|             inner: Inner::Pending(Pending { body: ReadableChunks::new(body) }) |             inner: Inner::Pending(Pending { | ||||||
|  |                 body: ReadableChunks::new(body), | ||||||
|  |             }), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -120,8 +121,8 @@ impl Decoder { | |||||||
|                 .get_all(CONTENT_ENCODING) |                 .get_all(CONTENT_ENCODING) | ||||||
|                 .iter() |                 .iter() | ||||||
|                 .any(|enc| enc == "gzip"); |                 .any(|enc| enc == "gzip"); | ||||||
|             content_encoding_gzip || |             content_encoding_gzip | ||||||
|             headers |                 || headers | ||||||
|                     .get_all(TRANSFER_ENCODING) |                     .get_all(TRANSFER_ENCODING) | ||||||
|                     .iter() |                     .iter() | ||||||
|                     .any(|enc| enc == "gzip") |                     .any(|enc| enc == "gzip") | ||||||
| @@ -153,15 +154,13 @@ impl Stream for Decoder { | |||||||
|     fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { |     fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { | ||||||
|         // Do a read or poll for a pendidng decoder value. |         // Do a read or poll for a pendidng decoder value. | ||||||
|         let new_value = match self.inner { |         let new_value = match self.inner { | ||||||
|             Inner::Pending(ref mut future) => { |             Inner::Pending(ref mut future) => match future.poll() { | ||||||
|                 match future.poll() { |  | ||||||
|                 Ok(Async::Ready(inner)) => inner, |                 Ok(Async::Ready(inner)) => inner, | ||||||
|                 Ok(Async::NotReady) => return Ok(Async::NotReady), |                 Ok(Async::NotReady) => return Ok(Async::NotReady), | ||||||
|                     Err(e) => return Err(e) |                 Err(e) => return Err(e), | ||||||
|                 } |  | ||||||
|             }, |             }, | ||||||
|             Inner::PlainText(ref mut body) => return body.poll(), |             Inner::PlainText(ref mut body) => return body.poll(), | ||||||
|             Inner::Gzip(ref mut decoder) => return decoder.poll() |             Inner::Gzip(ref mut decoder) => return decoder.poll(), | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         self.inner = new_value; |         self.inner = new_value; | ||||||
| @@ -177,13 +176,13 @@ impl Future for Pending { | |||||||
|         let body_state = match self.body.poll_stream() { |         let body_state = match self.body.poll_stream() { | ||||||
|             Ok(Async::Ready(state)) => state, |             Ok(Async::Ready(state)) => state, | ||||||
|             Ok(Async::NotReady) => return Ok(Async::NotReady), |             Ok(Async::NotReady) => return Ok(Async::NotReady), | ||||||
|             Err(e) => return Err(e) |             Err(e) => return Err(e), | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let body = mem::replace(&mut self.body, ReadableChunks::new(Body::empty())); |         let body = mem::replace(&mut self.body, ReadableChunks::new(Body::empty())); | ||||||
|         match body_state { |         match body_state { | ||||||
|             StreamState::Eof => Ok(Async::Ready(Inner::PlainText(Body::empty()))), |             StreamState::Eof => Ok(Async::Ready(Inner::PlainText(Body::empty()))), | ||||||
|             StreamState::HasMore => Ok(Async::Ready(Inner::Gzip(Gzip::new(body)))) |             StreamState::HasMore => Ok(Async::Ready(Inner::Gzip(Gzip::new(body)))), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -258,7 +257,7 @@ enum StreamState { | |||||||
|     /// More bytes can be read from the stream. |     /// More bytes can be read from the stream. | ||||||
|     HasMore, |     HasMore, | ||||||
|     /// No more bytes can be read from the stream. |     /// No more bytes can be read from the stream. | ||||||
|     Eof |     Eof, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<S> ReadableChunks<S> { | impl<S> ReadableChunks<S> { | ||||||
| @@ -273,8 +272,7 @@ impl<S> ReadableChunks<S> { | |||||||
|  |  | ||||||
| impl<S> fmt::Debug for ReadableChunks<S> { | impl<S> fmt::Debug for ReadableChunks<S> { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         f.debug_struct("ReadableChunks") |         f.debug_struct("ReadableChunks").finish() | ||||||
|             .finish() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -296,20 +294,12 @@ where | |||||||
|                     } else { |                     } else { | ||||||
|                         return Ok(len); |                         return Ok(len); | ||||||
|                     } |                     } | ||||||
|                 }, |                 } | ||||||
|                 ReadState::NotReady => { |                 ReadState::NotReady => match self.poll_stream() { | ||||||
|                     match self.poll_stream() { |  | ||||||
|                     Ok(Async::Ready(StreamState::HasMore)) => continue, |                     Ok(Async::Ready(StreamState::HasMore)) => continue, | ||||||
|                         Ok(Async::Ready(StreamState::Eof)) => { |                     Ok(Async::Ready(StreamState::Eof)) => return Ok(0), | ||||||
|                             return Ok(0) |                     Ok(Async::NotReady) => return Err(io::ErrorKind::WouldBlock.into()), | ||||||
|                         }, |                     Err(e) => return Err(error::into_io(e)), | ||||||
|                         Ok(Async::NotReady) => { |  | ||||||
|                             return Err(io::ErrorKind::WouldBlock.into()) |  | ||||||
|                         }, |  | ||||||
|                         Err(e) => { |  | ||||||
|                             return Err(error::into_io(e)) |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 }, |                 }, | ||||||
|                 ReadState::Eof => return Ok(0), |                 ReadState::Eof => return Ok(0), | ||||||
|             } |             } | ||||||
| @@ -320,7 +310,8 @@ where | |||||||
| } | } | ||||||
|  |  | ||||||
| impl<S> ReadableChunks<S> | impl<S> ReadableChunks<S> | ||||||
|     where S: Stream<Item = Chunk, Error = error::Error> | where | ||||||
|  |     S: Stream<Item = Chunk, Error = error::Error>, | ||||||
| { | { | ||||||
|     /// Poll the readiness of the inner reader. |     /// Poll the readiness of the inner reader. | ||||||
|     /// |     /// | ||||||
| @@ -332,16 +323,14 @@ impl<S> ReadableChunks<S> | |||||||
|                 self.state = ReadState::Ready(chunk); |                 self.state = ReadState::Ready(chunk); | ||||||
|  |  | ||||||
|                 Ok(Async::Ready(StreamState::HasMore)) |                 Ok(Async::Ready(StreamState::HasMore)) | ||||||
|             }, |             } | ||||||
|             Ok(Async::Ready(None)) => { |             Ok(Async::Ready(None)) => { | ||||||
|                 self.state = ReadState::Eof; |                 self.state = ReadState::Eof; | ||||||
|  |  | ||||||
|                 Ok(Async::Ready(StreamState::Eof)) |                 Ok(Async::Ready(StreamState::Eof)) | ||||||
|             }, |             } | ||||||
|             Ok(Async::NotReady) => { |             Ok(Async::NotReady) => Ok(Async::NotReady), | ||||||
|                 Ok(Async::NotReady) |             Err(e) => Err(e), | ||||||
|             }, |  | ||||||
|             Err(e) => Err(e) |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,10 +2,10 @@ | |||||||
| use std::borrow::Cow; | use std::borrow::Cow; | ||||||
| use std::fmt; | use std::fmt; | ||||||
|  |  | ||||||
|  | use http::HeaderMap; | ||||||
| use mime_guess::Mime; | use mime_guess::Mime; | ||||||
| use url::percent_encoding::{self, EncodeSet, PATH_SEGMENT_ENCODE_SET}; | use url::percent_encoding::{self, EncodeSet, PATH_SEGMENT_ENCODE_SET}; | ||||||
| use uuid::Uuid; | use uuid::Uuid; | ||||||
| use http::HeaderMap; |  | ||||||
|  |  | ||||||
| use futures::Stream; | use futures::Stream; | ||||||
|  |  | ||||||
| @@ -98,7 +98,7 @@ impl Form { | |||||||
|  |  | ||||||
|     /// Consume this instance and transform into an instance of hyper::Body for use in a request. |     /// Consume this instance and transform into an instance of hyper::Body for use in a request. | ||||||
|     pub(crate) fn stream(mut self) -> hyper::Body { |     pub(crate) fn stream(mut self) -> hyper::Body { | ||||||
|         if self.inner.fields.is_empty(){ |         if self.inner.fields.is_empty() { | ||||||
|             return hyper::Body::empty(); |             return hyper::Body::empty(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -126,12 +126,20 @@ impl Form { | |||||||
|         let boundary = hyper::Body::from(format!("--{}\r\n", self.boundary())); |         let boundary = hyper::Body::from(format!("--{}\r\n", self.boundary())); | ||||||
|         // append headers |         // append headers | ||||||
|         let header = hyper::Body::from({ |         let header = hyper::Body::from({ | ||||||
|             let mut h = self.inner.percent_encoding.encode_headers(&name.into(), &part.meta); |             let mut h = self | ||||||
|  |                 .inner | ||||||
|  |                 .percent_encoding | ||||||
|  |                 .encode_headers(&name.into(), &part.meta); | ||||||
|             h.extend_from_slice(b"\r\n\r\n"); |             h.extend_from_slice(b"\r\n\r\n"); | ||||||
|             h |             h | ||||||
|         }); |         }); | ||||||
|         // then append form data followed by terminating CRLF |         // then append form data followed by terminating CRLF | ||||||
|         hyper::Body::wrap_stream(boundary.chain(header).chain(hyper::Body::wrap_stream(part.value)).chain(hyper::Body::from("\r\n".to_owned()))) |         hyper::Body::wrap_stream( | ||||||
|  |             boundary | ||||||
|  |                 .chain(header) | ||||||
|  |                 .chain(hyper::Body::wrap_stream(part.value)) | ||||||
|  |                 .chain(hyper::Body::from("\r\n".to_owned())), | ||||||
|  |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(crate) fn compute_length(&mut self) -> Option<u64> { |     pub(crate) fn compute_length(&mut self) -> Option<u64> { | ||||||
| @@ -188,7 +196,9 @@ impl Part { | |||||||
|         T::Item: Into<Chunk>, |         T::Item: Into<Chunk>, | ||||||
|         T::Error: std::error::Error + Send + Sync, |         T::Error: std::error::Error + Send + Sync, | ||||||
|     { |     { | ||||||
|         Part::new(Body::wrap(hyper::Body::wrap_stream(value.map(|chunk| chunk.into())))) |         Part::new(Body::wrap(hyper::Body::wrap_stream( | ||||||
|  |             value.map(|chunk| chunk.into()), | ||||||
|  |         ))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn new(value: Body) -> Part { |     fn new(value: Body) -> Part { | ||||||
| @@ -306,7 +316,13 @@ impl<P: PartProps> FormParts<P> { | |||||||
|                     // in Reader. Not the cleanest solution because if that format string is |                     // in Reader. Not the cleanest solution because if that format string is | ||||||
|                     // ever changed then this formula needs to be changed too which is not an |                     // ever changed then this formula needs to be changed too which is not an | ||||||
|                     // obvious dependency in the code. |                     // obvious dependency in the code. | ||||||
|                     length += 2 + self.boundary().len() as u64 + 2 + header_length as u64 + 4 + value_length + 2 |                     length += 2 | ||||||
|  |                         + self.boundary().len() as u64 | ||||||
|  |                         + 2 | ||||||
|  |                         + header_length as u64 | ||||||
|  |                         + 4 | ||||||
|  |                         + value_length | ||||||
|  |                         + 2 | ||||||
|                 } |                 } | ||||||
|                 _ => return None, |                 _ => return None, | ||||||
|             } |             } | ||||||
| @@ -340,7 +356,7 @@ impl PartMetadata { | |||||||
|         PartMetadata { |         PartMetadata { | ||||||
|             mime: None, |             mime: None, | ||||||
|             file_name: None, |             file_name: None, | ||||||
|             headers: HeaderMap::default() |             headers: HeaderMap::default(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -358,11 +374,10 @@ impl PartMetadata { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| impl PartMetadata { | impl PartMetadata { | ||||||
|     pub(crate) fn fmt_fields<'f, 'fa, 'fb>( |     pub(crate) fn fmt_fields<'f, 'fa, 'fb>( | ||||||
|         &self, |         &self, | ||||||
|         debug_struct: &'f mut fmt::DebugStruct<'fa, 'fb> |         debug_struct: &'f mut fmt::DebugStruct<'fa, 'fb>, | ||||||
|     ) -> &'f mut fmt::DebugStruct<'fa, 'fb> { |     ) -> &'f mut fmt::DebugStruct<'fa, 'fb> { | ||||||
|         debug_struct |         debug_struct | ||||||
|             .field("mime", &self.mime) |             .field("mime", &self.mime) | ||||||
| @@ -390,7 +405,9 @@ impl EncodeSet for AttrCharEncodeSet { | |||||||
|             '|' => false, |             '|' => false, | ||||||
|             '~' => false, |             '~' => false, | ||||||
|             _ => { |             _ => { | ||||||
|                   let is_alpha_numeric = ch >= 0x41 && ch <= 0x5a || ch >= 0x61 && ch <= 0x7a || ch >= 0x30 && ch <= 0x39; |                 let is_alpha_numeric = ch >= 0x41 && ch <= 0x5a | ||||||
|  |                     || ch >= 0x61 && ch <= 0x7a | ||||||
|  |                     || ch >= 0x30 && ch <= 0x39; | ||||||
|                 !is_alpha_numeric |                 !is_alpha_numeric | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -417,7 +434,10 @@ impl PercentEncoding { | |||||||
|                 None => "".to_string(), |                 None => "".to_string(), | ||||||
|             }, |             }, | ||||||
|         ); |         ); | ||||||
|         field.headers.iter().fold(s.into_bytes(), |mut header, (k,v)| { |         field | ||||||
|  |             .headers | ||||||
|  |             .iter() | ||||||
|  |             .fold(s.into_bytes(), |mut header, (k, v)| { | ||||||
|                 header.extend_from_slice(b"\r\n"); |                 header.extend_from_slice(b"\r\n"); | ||||||
|                 header.extend_from_slice(k.as_str().as_bytes()); |                 header.extend_from_slice(k.as_str().as_bytes()); | ||||||
|                 header.extend_from_slice(b": "); |                 header.extend_from_slice(b": "); | ||||||
| @@ -429,7 +449,8 @@ impl PercentEncoding { | |||||||
|     // According to RFC7578 Section 4.2, `filename*=` syntax is invalid. |     // According to RFC7578 Section 4.2, `filename*=` syntax is invalid. | ||||||
|     // See https://github.com/seanmonstar/reqwest/issues/419. |     // See https://github.com/seanmonstar/reqwest/issues/419. | ||||||
|     fn format_filename(&self, filename: &str) -> String { |     fn format_filename(&self, filename: &str) -> String { | ||||||
|         let legal_filename = filename.replace("\\", "\\\\") |         let legal_filename = filename | ||||||
|  |             .replace("\\", "\\\\") | ||||||
|             .replace("\"", "\\\"") |             .replace("\"", "\\\"") | ||||||
|             .replace("\r", "\\\r") |             .replace("\r", "\\\r") | ||||||
|             .replace("\n", "\\\n"); |             .replace("\n", "\\\n"); | ||||||
| @@ -439,14 +460,12 @@ impl PercentEncoding { | |||||||
|     fn format_parameter(&self, name: &str, value: &str) -> String { |     fn format_parameter(&self, name: &str, value: &str) -> String { | ||||||
|         let legal_value = match *self { |         let legal_value = match *self { | ||||||
|             PercentEncoding::PathSegment => { |             PercentEncoding::PathSegment => { | ||||||
|                 percent_encoding::utf8_percent_encode(value, PATH_SEGMENT_ENCODE_SET) |                 percent_encoding::utf8_percent_encode(value, PATH_SEGMENT_ENCODE_SET).to_string() | ||||||
|                     .to_string() |             } | ||||||
|             }, |  | ||||||
|             PercentEncoding::AttrChar => { |             PercentEncoding::AttrChar => { | ||||||
|                 percent_encoding::utf8_percent_encode(value, AttrCharEncodeSet) |                 percent_encoding::utf8_percent_encode(value, AttrCharEncodeSet).to_string() | ||||||
|                     .to_string() |             } | ||||||
|             }, |             PercentEncoding::NoOp => value.to_string(), | ||||||
|             PercentEncoding::NoOp => { value.to_string() }, |  | ||||||
|         }; |         }; | ||||||
|         if value.len() == legal_value.len() { |         if value.len() == legal_value.len() { | ||||||
|             // nothing has been percent encoded |             // nothing has been percent encoded | ||||||
| @@ -477,19 +496,24 @@ mod tests { | |||||||
|     #[test] |     #[test] | ||||||
|     fn stream_to_end() { |     fn stream_to_end() { | ||||||
|         let mut form = Form::new() |         let mut form = Form::new() | ||||||
|             .part("reader1", Part::stream(futures::stream::once::<_, hyper::Error>(Ok(Chunk::from("part1".to_owned()))))) |  | ||||||
|             .part("key1", Part::text("value1")) |  | ||||||
|             .part( |             .part( | ||||||
|                 "key2", |                 "reader1", | ||||||
|                 Part::text("value2").mime(mime::IMAGE_BMP), |                 Part::stream(futures::stream::once::<_, hyper::Error>(Ok(Chunk::from( | ||||||
|  |                     "part1".to_owned(), | ||||||
|  |                 )))), | ||||||
|             ) |             ) | ||||||
|             .part("reader2", Part::stream(futures::stream::once::<_, hyper::Error>(Ok(Chunk::from("part2".to_owned()))))) |             .part("key1", Part::text("value1")) | ||||||
|  |             .part("key2", Part::text("value2").mime(mime::IMAGE_BMP)) | ||||||
|             .part( |             .part( | ||||||
|                 "key3", |                 "reader2", | ||||||
|                 Part::text("value3").file_name("filename"), |                 Part::stream(futures::stream::once::<_, hyper::Error>(Ok(Chunk::from( | ||||||
|             ); |                     "part2".to_owned(), | ||||||
|  |                 )))), | ||||||
|  |             ) | ||||||
|  |             .part("key3", Part::text("value3").file_name("filename")); | ||||||
|         form.inner.boundary = "boundary".to_string(); |         form.inner.boundary = "boundary".to_string(); | ||||||
|         let expected = "--boundary\r\n\ |         let expected = | ||||||
|  |             "--boundary\r\n\ | ||||||
|              Content-Disposition: form-data; name=\"reader1\"\r\n\r\n\ |              Content-Disposition: form-data; name=\"reader1\"\r\n\r\n\ | ||||||
|              part1\r\n\ |              part1\r\n\ | ||||||
|              --boundary\r\n\ |              --boundary\r\n\ | ||||||
| @@ -508,7 +532,9 @@ mod tests { | |||||||
|         let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |         let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); | ||||||
|         let body_ft = form.stream(); |         let body_ft = form.stream(); | ||||||
|  |  | ||||||
|         let out = rt.block_on(body_ft.map(|c| c.into_bytes()).concat2()).unwrap(); |         let out = rt | ||||||
|  |             .block_on(body_ft.map(|c| c.into_bytes()).concat2()) | ||||||
|  |             .unwrap(); | ||||||
|         // These prints are for debug purposes in case the test fails |         // These prints are for debug purposes in case the test fails | ||||||
|         println!( |         println!( | ||||||
|             "START REAL\n{}\nEND REAL", |             "START REAL\n{}\nEND REAL", | ||||||
| @@ -534,7 +560,9 @@ mod tests { | |||||||
|         let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |         let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); | ||||||
|         let body_ft = form.stream(); |         let body_ft = form.stream(); | ||||||
|  |  | ||||||
|         let out = rt.block_on(body_ft.map(|c| c.into_bytes()).concat2()).unwrap(); |         let out = rt | ||||||
|  |             .block_on(body_ft.map(|c| c.into_bytes()).concat2()) | ||||||
|  |             .unwrap(); | ||||||
|         // These prints are for debug purposes in case the test fails |         // These prints are for debug purposes in case the test fails | ||||||
|         println!( |         println!( | ||||||
|             "START REAL\n{}\nEND REAL", |             "START REAL\n{}\nEND REAL", | ||||||
|   | |||||||
| @@ -1,18 +1,18 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
|  |  | ||||||
| use base64::{encode}; | use base64::encode; | ||||||
| use futures::Future; | use futures::Future; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| use serde_json; | use serde_json; | ||||||
| use serde_urlencoded; | use serde_urlencoded; | ||||||
|  |  | ||||||
| use super::body::{Body}; | use super::body::Body; | ||||||
| use super::client::{Client, Pending}; | use super::client::{Client, Pending}; | ||||||
| use super::multipart; | use super::multipart; | ||||||
| use super::response::Response; | use super::response::Response; | ||||||
| use crate::header::{CONTENT_LENGTH, CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue}; | use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_LENGTH, CONTENT_TYPE}; | ||||||
| use http::HttpTryFrom; |  | ||||||
| use crate::{Method, Url}; | use crate::{Method, Url}; | ||||||
|  | use http::HttpTryFrom; | ||||||
|  |  | ||||||
| /// A request which can be executed with `Client::execute()`. | /// A request which can be executed with `Client::execute()`. | ||||||
| pub struct Request { | pub struct Request { | ||||||
| @@ -95,10 +95,7 @@ impl Request { | |||||||
|  |  | ||||||
| impl RequestBuilder { | impl RequestBuilder { | ||||||
|     pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder { |     pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder { | ||||||
|         RequestBuilder { |         RequestBuilder { client, request } | ||||||
|             client, |  | ||||||
|             request, |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Add a `Header` to this Request. |     /// Add a `Header` to this Request. | ||||||
| @@ -110,11 +107,11 @@ impl RequestBuilder { | |||||||
|         let mut error = None; |         let mut error = None; | ||||||
|         if let Ok(ref mut req) = self.request { |         if let Ok(ref mut req) = self.request { | ||||||
|             match <HeaderName as HttpTryFrom<K>>::try_from(key) { |             match <HeaderName as HttpTryFrom<K>>::try_from(key) { | ||||||
|                 Ok(key) => { |                 Ok(key) => match <HeaderValue as HttpTryFrom<V>>::try_from(value) { | ||||||
|                     match <HeaderValue as HttpTryFrom<V>>::try_from(value) { |                     Ok(value) => { | ||||||
|                         Ok(value) => { req.headers_mut().append(key, value); } |                         req.headers_mut().append(key, value); | ||||||
|                         Err(e) => error = Some(crate::error::from(e.into())), |  | ||||||
|                     } |                     } | ||||||
|  |                     Err(e) => error = Some(crate::error::from(e.into())), | ||||||
|                 }, |                 }, | ||||||
|                 Err(e) => error = Some(crate::error::from(e.into())), |                 Err(e) => error = Some(crate::error::from(e.into())), | ||||||
|             }; |             }; | ||||||
| @@ -168,7 +165,7 @@ impl RequestBuilder { | |||||||
|     { |     { | ||||||
|         let auth = match password { |         let auth = match password { | ||||||
|             Some(password) => format!("{}:{}", username, password), |             Some(password) => format!("{}:{}", username, password), | ||||||
|             None => format!("{}:", username) |             None => format!("{}:", username), | ||||||
|         }; |         }; | ||||||
|         let header_value = format!("Basic {}", encode(&auth)); |         let header_value = format!("Basic {}", encode(&auth)); | ||||||
|         self.header(crate::header::AUTHORIZATION, &*header_value) |         self.header(crate::header::AUTHORIZATION, &*header_value) | ||||||
| @@ -221,10 +218,7 @@ impl RequestBuilder { | |||||||
|     pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder { |     pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder { | ||||||
|         let mut builder = self.header( |         let mut builder = self.header( | ||||||
|             CONTENT_TYPE, |             CONTENT_TYPE, | ||||||
|             format!( |             format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(), | ||||||
|                 "multipart/form-data; boundary={}", |  | ||||||
|                 multipart.boundary() |  | ||||||
|             ).as_str() |  | ||||||
|         ); |         ); | ||||||
|  |  | ||||||
|         builder = match multipart.compute_length() { |         builder = match multipart.compute_length() { | ||||||
| @@ -286,10 +280,10 @@ impl RequestBuilder { | |||||||
|                 Ok(body) => { |                 Ok(body) => { | ||||||
|                     req.headers_mut().insert( |                     req.headers_mut().insert( | ||||||
|                         CONTENT_TYPE, |                         CONTENT_TYPE, | ||||||
|                         HeaderValue::from_static("application/x-www-form-urlencoded") |                         HeaderValue::from_static("application/x-www-form-urlencoded"), | ||||||
|                     ); |                     ); | ||||||
|                     *req.body_mut() = Some(body.into()); |                     *req.body_mut() = Some(body.into()); | ||||||
|                 }, |                 } | ||||||
|                 Err(err) => error = Some(crate::error::from(err)), |                 Err(err) => error = Some(crate::error::from(err)), | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -310,12 +304,10 @@ impl RequestBuilder { | |||||||
|         if let Ok(ref mut req) = self.request { |         if let Ok(ref mut req) = self.request { | ||||||
|             match serde_json::to_vec(json) { |             match serde_json::to_vec(json) { | ||||||
|                 Ok(body) => { |                 Ok(body) => { | ||||||
|                     req.headers_mut().insert( |                     req.headers_mut() | ||||||
|                         CONTENT_TYPE, |                         .insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); | ||||||
|                         HeaderValue::from_static("application/json") |  | ||||||
|                     ); |  | ||||||
|                     *req.body_mut() = Some(body.into()); |                     *req.body_mut() = Some(body.into()); | ||||||
|                 }, |                 } | ||||||
|                 Err(err) => error = Some(crate::error::from(err)), |                 Err(err) => error = Some(crate::error::from(err)), | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -368,8 +360,7 @@ impl RequestBuilder { | |||||||
|  |  | ||||||
| impl fmt::Debug for Request { | impl fmt::Debug for Request { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         fmt_request_fields(&mut f.debug_struct("Request"), self) |         fmt_request_fields(&mut f.debug_struct("Request"), self).finish() | ||||||
|             .finish() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -377,27 +368,22 @@ impl fmt::Debug for RequestBuilder { | |||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         let mut builder = f.debug_struct("RequestBuilder"); |         let mut builder = f.debug_struct("RequestBuilder"); | ||||||
|         match self.request { |         match self.request { | ||||||
|             Ok(ref req) => { |             Ok(ref req) => fmt_request_fields(&mut builder, req).finish(), | ||||||
|                 fmt_request_fields(&mut builder, req) |             Err(ref err) => builder.field("error", err).finish(), | ||||||
|                     .finish() |  | ||||||
|             }, |  | ||||||
|             Err(ref err) => { |  | ||||||
|                 builder |  | ||||||
|                     .field("error", err) |  | ||||||
|                     .finish() |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn fmt_request_fields<'a, 'b>(f: &'a mut fmt::DebugStruct<'a, 'b>, req: &Request) -> &'a mut fmt::DebugStruct<'a, 'b> { | fn fmt_request_fields<'a, 'b>( | ||||||
|  |     f: &'a mut fmt::DebugStruct<'a, 'b>, | ||||||
|  |     req: &Request, | ||||||
|  | ) -> &'a mut fmt::DebugStruct<'a, 'b> { | ||||||
|     f.field("method", &req.method) |     f.field("method", &req.method) | ||||||
|         .field("url", &req.url) |         .field("url", &req.url) | ||||||
|         .field("headers", &req.headers) |         .field("headers", &req.headers) | ||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) fn replace_headers(dst: &mut HeaderMap, src: HeaderMap) { | pub(crate) fn replace_headers(dst: &mut HeaderMap, src: HeaderMap) { | ||||||
|  |  | ||||||
|     // IntoIter of HeaderMap yields (Option<HeaderName>, HeaderValue). |     // IntoIter of HeaderMap yields (Option<HeaderName>, HeaderValue). | ||||||
|     // The first time a name is yielded, it will be Some(name), and if |     // The first time a name is yielded, it will be Some(name), and if | ||||||
|     // there are more values with the same name, the next yield will be |     // there are more values with the same name, the next yield will be | ||||||
| @@ -413,11 +399,11 @@ pub(crate) fn replace_headers(dst: &mut HeaderMap, src: HeaderMap) { | |||||||
|             Some(key) => { |             Some(key) => { | ||||||
|                 dst.insert(key.clone(), value); |                 dst.insert(key.clone(), value); | ||||||
|                 prev_name = Some(key); |                 prev_name = Some(key); | ||||||
|             }, |             } | ||||||
|             None => match prev_name { |             None => match prev_name { | ||||||
|                 Some(ref key) => { |                 Some(ref key) => { | ||||||
|                     dst.append(key.clone(), value); |                     dst.append(key.clone(), value); | ||||||
|                 }, |                 } | ||||||
|                 None => unreachable!("HeaderMap::into_iter yielded None first"), |                 None => unreachable!("HeaderMap::into_iter yielded None first"), | ||||||
|             }, |             }, | ||||||
|         } |         } | ||||||
| @@ -427,8 +413,8 @@ pub(crate) fn replace_headers(dst: &mut HeaderMap, src: HeaderMap) { | |||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use super::Client; |     use super::Client; | ||||||
|     use std::collections::BTreeMap; |  | ||||||
|     use serde::Serialize; |     use serde::Serialize; | ||||||
|  |     use std::collections::BTreeMap; | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn add_query_append() { |     fn add_query_append() { | ||||||
| @@ -467,7 +453,10 @@ mod tests { | |||||||
|         let some_url = "https://google.com/"; |         let some_url = "https://google.com/"; | ||||||
|         let r = client.get(some_url); |         let r = client.get(some_url); | ||||||
|  |  | ||||||
|         let params = Params { foo: "bar".into(), qux: 3 }; |         let params = Params { | ||||||
|  |             foo: "bar".into(), | ||||||
|  |             qux: 3, | ||||||
|  |         }; | ||||||
|  |  | ||||||
|         let r = r.query(¶ms); |         let r = r.query(¶ms); | ||||||
|  |  | ||||||
| @@ -510,11 +499,7 @@ mod tests { | |||||||
|  |  | ||||||
|         assert_eq!(req.headers()["im-a"], "keeper"); |         assert_eq!(req.headers()["im-a"], "keeper"); | ||||||
|  |  | ||||||
|         let foo = req |         let foo = req.headers().get_all("foo").iter().collect::<Vec<_>>(); | ||||||
|             .headers() |  | ||||||
|             .get_all("foo") |  | ||||||
|             .iter() |  | ||||||
|             .collect::<Vec<_>>(); |  | ||||||
|         assert_eq!(foo.len(), 2); |         assert_eq!(foo.len(), 2); | ||||||
|         assert_eq!(foo[0], "bar"); |         assert_eq!(foo[0], "bar"); | ||||||
|         assert_eq!(foo[1], "baz"); |         assert_eq!(foo[1], "baz"); | ||||||
|   | |||||||
| @@ -1,28 +1,26 @@ | |||||||
| use std::fmt; |  | ||||||
| use std::mem; |  | ||||||
| use std::marker::PhantomData; |  | ||||||
| use std::net::SocketAddr; |  | ||||||
| use std::borrow::Cow; | use std::borrow::Cow; | ||||||
|  | use std::fmt; | ||||||
|  | use std::marker::PhantomData; | ||||||
|  | use std::mem; | ||||||
|  | use std::net::SocketAddr; | ||||||
|  |  | ||||||
| use encoding_rs::{Encoding, UTF_8}; | use encoding_rs::{Encoding, UTF_8}; | ||||||
| use futures::{Async, Future, Poll, Stream, try_ready}; |  | ||||||
| use futures::stream::Concat2; | use futures::stream::Concat2; | ||||||
|  | use futures::{try_ready, Async, Future, Poll, Stream}; | ||||||
| use http; | use http; | ||||||
| use hyper::{HeaderMap, StatusCode, Version}; |  | ||||||
| use hyper::client::connect::HttpInfo; | use hyper::client::connect::HttpInfo; | ||||||
| use hyper::header::{CONTENT_LENGTH}; | use hyper::header::CONTENT_LENGTH; | ||||||
|  | use hyper::{HeaderMap, StatusCode, Version}; | ||||||
|  | use log::debug; | ||||||
| use mime::Mime; | use mime::Mime; | ||||||
| use tokio::timer::Delay; |  | ||||||
| use serde::de::DeserializeOwned; | use serde::de::DeserializeOwned; | ||||||
| use serde_json; | use serde_json; | ||||||
|  | use tokio::timer::Delay; | ||||||
| use url::Url; | use url::Url; | ||||||
| use log::{debug}; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| use crate::cookie; |  | ||||||
| use super::Decoder; |  | ||||||
| use super::body::Body; | use super::body::Body; | ||||||
|  | use super::Decoder; | ||||||
|  | use crate::cookie; | ||||||
|  |  | ||||||
| /// A Response to a submitted `Request`. | /// A Response to a submitted `Request`. | ||||||
| pub struct Response { | pub struct Response { | ||||||
| @@ -37,7 +35,12 @@ pub struct Response { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl Response { | impl Response { | ||||||
|     pub(super) fn new(res: hyper::Response<hyper::Body>, url: Url, gzip: bool, timeout: Option<Delay>) -> Response { |     pub(super) fn new( | ||||||
|  |         res: hyper::Response<hyper::Body>, | ||||||
|  |         url: Url, | ||||||
|  |         gzip: bool, | ||||||
|  |         timeout: Option<Delay>, | ||||||
|  |     ) -> Response { | ||||||
|         let (parts, body) = res.into_parts(); |         let (parts, body) = res.into_parts(); | ||||||
|         let status = parts.status; |         let status = parts.status; | ||||||
|         let version = parts.version; |         let version = parts.version; | ||||||
| @@ -57,7 +60,6 @@ impl Response { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /// Get the `StatusCode` of this `Response`. |     /// Get the `StatusCode` of this `Response`. | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn status(&self) -> StatusCode { |     pub fn status(&self) -> StatusCode { | ||||||
| @@ -80,8 +82,7 @@ impl Response { | |||||||
|     /// |     /// | ||||||
|     /// Note that invalid 'Set-Cookie' headers will be ignored. |     /// Note that invalid 'Set-Cookie' headers will be ignored. | ||||||
|     pub fn cookies<'a>(&'a self) -> impl Iterator<Item = cookie::Cookie<'a>> + 'a { |     pub fn cookies<'a>(&'a self) -> impl Iterator<Item = cookie::Cookie<'a>> + 'a { | ||||||
|         cookie::extract_response_cookies(&self.headers) |         cookie::extract_response_cookies(&self.headers).filter_map(Result::ok) | ||||||
|             .filter_map(Result::ok) |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Get the final `Url` of this `Response`. |     /// Get the final `Url` of this `Response`. | ||||||
| @@ -92,8 +93,7 @@ impl Response { | |||||||
|  |  | ||||||
|     /// Get the remote address used to get this `Response`. |     /// Get the remote address used to get this `Response`. | ||||||
|     pub fn remote_addr(&self) -> Option<SocketAddr> { |     pub fn remote_addr(&self) -> Option<SocketAddr> { | ||||||
|         self |         self.extensions | ||||||
|             .extensions |  | ||||||
|             .get::<HttpInfo>() |             .get::<HttpInfo>() | ||||||
|             .map(|info| info.remote_addr()) |             .map(|info| info.remote_addr()) | ||||||
|     } |     } | ||||||
| @@ -106,8 +106,7 @@ impl Response { | |||||||
|     /// - The response is gzipped and automatically decoded (thus changing |     /// - The response is gzipped and automatically decoded (thus changing | ||||||
|     ///   the actual decoded length). |     ///   the actual decoded length). | ||||||
|     pub fn content_length(&self) -> Option<u64> { |     pub fn content_length(&self) -> Option<u64> { | ||||||
|         self |         self.headers() | ||||||
|             .headers() |  | ||||||
|             .get(CONTENT_LENGTH) |             .get(CONTENT_LENGTH) | ||||||
|             .and_then(|ct_len| ct_len.to_str().ok()) |             .and_then(|ct_len| ct_len.to_str().ok()) | ||||||
|             .and_then(|ct_len| ct_len.parse().ok()) |             .and_then(|ct_len| ct_len.parse().ok()) | ||||||
| @@ -145,27 +144,24 @@ impl Response { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Get the response text given a specific encoding |     /// Get the response text given a specific encoding | ||||||
|     pub fn text_with_charset(&mut self, default_encoding: &str) -> impl Future<Item = String, Error = crate::Error> { |     pub fn text_with_charset( | ||||||
|  |         &mut self, | ||||||
|  |         default_encoding: &str, | ||||||
|  |     ) -> impl Future<Item = String, Error = crate::Error> { | ||||||
|         let body = mem::replace(&mut self.body, Decoder::empty()); |         let body = mem::replace(&mut self.body, Decoder::empty()); | ||||||
|         let content_type = self.headers.get(crate::header::CONTENT_TYPE) |         let content_type = self | ||||||
|             .and_then(|value| { |             .headers | ||||||
|                 value.to_str().ok() |             .get(crate::header::CONTENT_TYPE) | ||||||
|             }) |             .and_then(|value| value.to_str().ok()) | ||||||
|             .and_then(|value| { |             .and_then(|value| value.parse::<Mime>().ok()); | ||||||
|                 value.parse::<Mime>().ok() |  | ||||||
|             }); |  | ||||||
|         let encoding_name = content_type |         let encoding_name = content_type | ||||||
|             .as_ref() |             .as_ref() | ||||||
|             .and_then(|mime| { |             .and_then(|mime| mime.get_param("charset").map(|charset| charset.as_str())) | ||||||
|                 mime |  | ||||||
|                     .get_param("charset") |  | ||||||
|                     .map(|charset| charset.as_str()) |  | ||||||
|             }) |  | ||||||
|             .unwrap_or(default_encoding); |             .unwrap_or(default_encoding); | ||||||
|         let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8); |         let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8); | ||||||
|         Text { |         Text { | ||||||
|             concat: body.concat2(), |             concat: body.concat2(), | ||||||
|             encoding |             encoding, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -256,7 +252,8 @@ impl<T: Into<Body>> From<http::Response<T>> for Response { | |||||||
|         let (mut parts, body) = r.into_parts(); |         let (mut parts, body) = r.into_parts(); | ||||||
|         let body = body.into(); |         let body = body.into(); | ||||||
|         let body = Decoder::detect(&mut parts.headers, body, false); |         let body = Decoder::detect(&mut parts.headers, body, false); | ||||||
|         let url = parts.extensions |         let url = parts | ||||||
|  |             .extensions | ||||||
|             .remove::<ResponseUrl>() |             .remove::<ResponseUrl>() | ||||||
|             .unwrap_or_else(|| ResponseUrl(Url::parse("http://no.url.provided.local").unwrap())); |             .unwrap_or_else(|| ResponseUrl(Url::parse("http://no.url.provided.local").unwrap())); | ||||||
|         let url = url.0; |         let url = url.0; | ||||||
| @@ -289,8 +286,7 @@ impl<T: DeserializeOwned> Future for Json<T> { | |||||||
|  |  | ||||||
| impl<T> fmt::Debug for Json<T> { | impl<T> fmt::Debug for Json<T> { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         f.debug_struct("Json") |         f.debug_struct("Json").finish() | ||||||
|             .finish() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -308,7 +304,9 @@ impl Future for Text { | |||||||
|         // a block because of borrow checker |         // a block because of borrow checker | ||||||
|         { |         { | ||||||
|             let (text, _, _) = self.encoding.decode(&bytes); |             let (text, _, _) = self.encoding.decode(&bytes); | ||||||
|             if let Cow::Owned(s) = text { return Ok(Async::Ready(s)) } |             if let Cow::Owned(s) = text { | ||||||
|  |                 return Ok(Async::Ready(s)); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         unsafe { |         unsafe { | ||||||
|             // decoding returned Cow::Borrowed, meaning these bytes |             // decoding returned Cow::Borrowed, meaning these bytes | ||||||
| @@ -357,9 +355,9 @@ impl ResponseBuilderExt for http::response::Builder { | |||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use url::Url; |     use super::{Response, ResponseBuilderExt, ResponseUrl}; | ||||||
|     use http::response::Builder; |     use http::response::Builder; | ||||||
|     use super::{Response, ResponseUrl, ResponseBuilderExt}; |     use url::Url; | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_response_builder_ext() { |     fn test_response_builder_ext() { | ||||||
| @@ -370,7 +368,10 @@ mod tests { | |||||||
|             .body(()) |             .body(()) | ||||||
|             .unwrap(); |             .unwrap(); | ||||||
|  |  | ||||||
|         assert_eq!(response.extensions().get::<ResponseUrl>(), Some(&ResponseUrl(url))); |         assert_eq!( | ||||||
|  |             response.extensions().get::<ResponseUrl>(), | ||||||
|  |             Some(&ResponseUrl(url)) | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								src/body.rs
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/body.rs
									
									
									
									
									
								
							| @@ -1,12 +1,12 @@ | |||||||
| use std::fs::File; |  | ||||||
| use std::fmt; | use std::fmt; | ||||||
|  | use std::fs::File; | ||||||
| use std::io::{self, Cursor, Read}; | use std::io::{self, Cursor, Read}; | ||||||
|  |  | ||||||
| use bytes::Bytes; | use bytes::Bytes; | ||||||
| use futures::{Future, try_ready}; | use futures::{try_ready, Future}; | ||||||
| use hyper::{self}; | use hyper::{self}; | ||||||
|  |  | ||||||
| use crate::{async_impl}; | use crate::async_impl; | ||||||
|  |  | ||||||
| /// The body of a `Request`. | /// The body of a `Request`. | ||||||
| /// | /// | ||||||
| @@ -102,7 +102,7 @@ impl Body { | |||||||
|                     tx, |                     tx, | ||||||
|                 }; |                 }; | ||||||
|                 (Some(tx), async_impl::Body::wrap(rx), len) |                 (Some(tx), async_impl::Body::wrap(rx), len) | ||||||
|             }, |             } | ||||||
|             Kind::Bytes(chunk) => { |             Kind::Bytes(chunk) => { | ||||||
|                 let len = chunk.len() as u64; |                 let len = chunk.len() as u64; | ||||||
|                 (None, async_impl::Body::reusable(chunk), Some(len)) |                 (None, async_impl::Body::reusable(chunk), Some(len)) | ||||||
| @@ -111,12 +111,10 @@ impl Body { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(crate) fn try_clone(&self) -> Option<Body> { |     pub(crate) fn try_clone(&self) -> Option<Body> { | ||||||
|         self.kind.try_clone() |         self.kind.try_clone().map(|kind| Body { kind }) | ||||||
|             .map(|kind| Body { kind }) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| enum Kind { | enum Kind { | ||||||
|     Reader(Box<dyn Read + Send>, Option<u64>), |     Reader(Box<dyn Read + Send>, Option<u64>), | ||||||
|     Bytes(Bytes), |     Bytes(Bytes), | ||||||
| @@ -147,7 +145,6 @@ impl From<String> for Body { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| impl From<&'static [u8]> for Body { | impl From<&'static [u8]> for Body { | ||||||
|     #[inline] |     #[inline] | ||||||
|     fn from(s: &'static [u8]) -> Body { |     fn from(s: &'static [u8]) -> Body { | ||||||
| @@ -177,7 +174,8 @@ impl From<File> for Body { | |||||||
| impl fmt::Debug for Kind { | impl fmt::Debug for Kind { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         match *self { |         match *self { | ||||||
|             Kind::Reader(_, ref v) => f.debug_struct("Reader") |             Kind::Reader(_, ref v) => f | ||||||
|  |                 .debug_struct("Reader") | ||||||
|                 .field("length", &DebugLength(v)) |                 .field("length", &DebugLength(v)) | ||||||
|                 .finish(), |                 .finish(), | ||||||
|             Kind::Bytes(ref v) => fmt::Debug::fmt(v, f), |             Kind::Bytes(ref v) => fmt::Debug::fmt(v, f), | ||||||
| @@ -218,10 +216,10 @@ pub(crate) struct Sender { | |||||||
| impl Sender { | impl Sender { | ||||||
|     // A `Future` that may do blocking read calls. |     // A `Future` that may do blocking read calls. | ||||||
|     // As a `Future`, this integrates easily with `wait::timeout`. |     // As a `Future`, this integrates easily with `wait::timeout`. | ||||||
|     pub(crate) fn send(self) -> impl Future<Item=(), Error=crate::Error> { |     pub(crate) fn send(self) -> impl Future<Item = (), Error = crate::Error> { | ||||||
|         use std::cmp; |  | ||||||
|         use bytes::{BufMut, BytesMut}; |         use bytes::{BufMut, BytesMut}; | ||||||
|         use futures::future; |         use futures::future; | ||||||
|  |         use std::cmp; | ||||||
|  |  | ||||||
|         let con_len = self.body.1; |         let con_len = self.body.1; | ||||||
|         let cap = cmp::min(self.body.1.unwrap_or(8192), 8192); |         let cap = cmp::min(self.body.1.unwrap_or(8192), 8192); | ||||||
| @@ -261,15 +259,12 @@ impl Sender { | |||||||
|                         // read. Return. |                         // read. Return. | ||||||
|                         return Ok(().into()); |                         return Ok(().into()); | ||||||
|                     } |                     } | ||||||
|                     Ok(n) => { |                     Ok(n) => unsafe { | ||||||
|                         unsafe { buf.advance_mut(n); } |                         buf.advance_mut(n); | ||||||
|                     } |                     }, | ||||||
|                     Err(e) => { |                     Err(e) => { | ||||||
|                         let ret = io::Error::new(e.kind(), e.to_string()); |                         let ret = io::Error::new(e.kind(), e.to_string()); | ||||||
|                         tx |                         tx.take().expect("tx only taken on error").abort(); | ||||||
|                             .take() |  | ||||||
|                             .expect("tx only taken on error") |  | ||||||
|                             .abort(); |  | ||||||
|                         return Err(crate::error::from(ret)); |                         return Err(crate::error::from(ret)); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -1,18 +1,18 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::sync::Arc; |  | ||||||
| use std::time::Duration; |  | ||||||
| use std::thread; |  | ||||||
| use std::net::IpAddr; | use std::net::IpAddr; | ||||||
|  | use std::sync::Arc; | ||||||
|  | use std::thread; | ||||||
|  | use std::time::Duration; | ||||||
|  |  | ||||||
| use futures::{Async, Future, Stream}; |  | ||||||
| use futures::future::{self, Either}; | use futures::future::{self, Either}; | ||||||
| use futures::sync::{mpsc, oneshot}; | use futures::sync::{mpsc, oneshot}; | ||||||
|  | use futures::{Async, Future, Stream}; | ||||||
|  |  | ||||||
| use log::{trace}; | use log::trace; | ||||||
|  |  | ||||||
| use crate::request::{Request, RequestBuilder}; | use crate::request::{Request, RequestBuilder}; | ||||||
| use crate::response::Response; | use crate::response::Response; | ||||||
| use crate::{async_impl, header, Method, IntoUrl, Proxy, RedirectPolicy, wait}; | use crate::{async_impl, header, wait, IntoUrl, Method, Proxy, RedirectPolicy}; | ||||||
| #[cfg(feature = "tls")] | #[cfg(feature = "tls")] | ||||||
| use crate::{Certificate, Identity}; | use crate::{Certificate, Identity}; | ||||||
|  |  | ||||||
| @@ -81,9 +81,7 @@ impl ClientBuilder { | |||||||
|     /// This method fails if TLS backend cannot be initialized, or the resolver |     /// This method fails if TLS backend cannot be initialized, or the resolver | ||||||
|     /// cannot load the system configuration. |     /// cannot load the system configuration. | ||||||
|     pub fn build(self) -> crate::Result<Client> { |     pub fn build(self) -> crate::Result<Client> { | ||||||
|         ClientHandle::new(self).map(|handle| Client { |         ClientHandle::new(self).map(|handle| Client { inner: handle }) | ||||||
|             inner: handle, |  | ||||||
|         }) |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Disable proxy setting. |     /// Disable proxy setting. | ||||||
| @@ -184,7 +182,6 @@ impl ClientBuilder { | |||||||
|         self.with_inner(move |inner| inner.identity(identity)) |         self.with_inner(move |inner| inner.identity(identity)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /// Controls the use of hostname verification. |     /// Controls the use of hostname verification. | ||||||
|     /// |     /// | ||||||
|     /// Defaults to `false`. |     /// Defaults to `false`. | ||||||
| @@ -399,7 +396,6 @@ impl ClientBuilder { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| impl Client { | impl Client { | ||||||
|     /// Constructs a new `Client`. |     /// Constructs a new `Client`. | ||||||
|     /// |     /// | ||||||
| @@ -411,9 +407,7 @@ impl Client { | |||||||
|     /// Use `Client::builder()` if you wish to handle the failure as an `Error` |     /// Use `Client::builder()` if you wish to handle the failure as an `Error` | ||||||
|     /// instead of panicking. |     /// instead of panicking. | ||||||
|     pub fn new() -> Client { |     pub fn new() -> Client { | ||||||
|         ClientBuilder::new() |         ClientBuilder::new().build().expect("Client::new()") | ||||||
|             .build() |  | ||||||
|             .expect("Client::new()") |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Creates a `ClientBuilder` to configure a `Client`. |     /// Creates a `ClientBuilder` to configure a `Client`. | ||||||
| @@ -486,9 +480,7 @@ impl Client { | |||||||
|     /// |     /// | ||||||
|     /// This method fails whenever supplied `Url` cannot be parsed. |     /// This method fails whenever supplied `Url` cannot be parsed. | ||||||
|     pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder { |     pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder { | ||||||
|         let req = url |         let req = url.into_url().map(move |url| Request::new(method, url)); | ||||||
|             .into_url() |  | ||||||
|             .map(move |url| Request::new(method, url)); |  | ||||||
|         RequestBuilder::new(self.clone(), req) |         RequestBuilder::new(self.clone(), req) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -521,22 +513,24 @@ impl fmt::Debug for Client { | |||||||
|  |  | ||||||
| impl fmt::Debug for ClientBuilder { | impl fmt::Debug for ClientBuilder { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         f.debug_struct("ClientBuilder") |         f.debug_struct("ClientBuilder").finish() | ||||||
|             .finish() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| struct ClientHandle { | struct ClientHandle { | ||||||
|     timeout: Timeout, |     timeout: Timeout, | ||||||
|     inner: Arc<InnerClientHandle> |     inner: Arc<InnerClientHandle>, | ||||||
| } | } | ||||||
|  |  | ||||||
| type ThreadSender = mpsc::UnboundedSender<(async_impl::Request, oneshot::Sender<crate::Result<async_impl::Response>>)>; | type ThreadSender = mpsc::UnboundedSender<( | ||||||
|  |     async_impl::Request, | ||||||
|  |     oneshot::Sender<crate::Result<async_impl::Response>>, | ||||||
|  | )>; | ||||||
|  |  | ||||||
| struct InnerClientHandle { | struct InnerClientHandle { | ||||||
|     tx: Option<ThreadSender>, |     tx: Option<ThreadSender>, | ||||||
|     thread: Option<thread::JoinHandle<()>> |     thread: Option<thread::JoinHandle<()>>, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Drop for InnerClientHandle { | impl Drop for InnerClientHandle { | ||||||
| @@ -552,7 +546,9 @@ impl ClientHandle { | |||||||
|         let builder = builder.inner; |         let builder = builder.inner; | ||||||
|         let (tx, rx) = mpsc::unbounded(); |         let (tx, rx) = mpsc::unbounded(); | ||||||
|         let (spawn_tx, spawn_rx) = oneshot::channel::<crate::Result<()>>(); |         let (spawn_tx, spawn_rx) = oneshot::channel::<crate::Result<()>>(); | ||||||
|         let handle = try_!(thread::Builder::new().name("reqwest-internal-sync-runtime".into()).spawn(move || { |         let handle = try_!(thread::Builder::new() | ||||||
|  |             .name("reqwest-internal-sync-runtime".into()) | ||||||
|  |             .spawn(move || { | ||||||
|                 use tokio::runtime::current_thread::Runtime; |                 use tokio::runtime::current_thread::Runtime; | ||||||
|  |  | ||||||
|                 let built = (|| { |                 let built = (|| { | ||||||
| @@ -567,7 +563,7 @@ impl ClientHandle { | |||||||
|                             return; |                             return; | ||||||
|                         } |                         } | ||||||
|                         (rt, c) |                         (rt, c) | ||||||
|                 }, |                     } | ||||||
|                     Err(e) => { |                     Err(e) => { | ||||||
|                         let _ = spawn_tx.send(Err(e)); |                         let _ = spawn_tx.send(Err(e)); | ||||||
|                         return; |                         return; | ||||||
| @@ -575,7 +571,8 @@ impl ClientHandle { | |||||||
|                 }; |                 }; | ||||||
|  |  | ||||||
|                 let work = rx.for_each(move |(req, tx)| { |                 let work = rx.for_each(move |(req, tx)| { | ||||||
|                 let mut tx_opt: Option<oneshot::Sender<crate::Result<async_impl::Response>>> = Some(tx); |                     let mut tx_opt: Option<oneshot::Sender<crate::Result<async_impl::Response>>> = | ||||||
|  |                         Some(tx); | ||||||
|                     let mut res_fut = client.execute(req); |                     let mut res_fut = client.execute(req); | ||||||
|  |  | ||||||
|                     let task = future::poll_fn(move || { |                     let task = future::poll_fn(move || { | ||||||
| @@ -596,10 +593,7 @@ impl ClientHandle { | |||||||
|                                 Err(err) => Err(err), |                                 Err(err) => Err(err), | ||||||
|                             }; |                             }; | ||||||
|  |  | ||||||
|                         let _ = tx_opt |                             let _ = tx_opt.take().expect("polled after complete").send(result); | ||||||
|                             .take() |  | ||||||
|                             .expect("polled after complete") |  | ||||||
|                             .send(result); |  | ||||||
|                             Ok(Async::Ready(())) |                             Ok(Async::Ready(())) | ||||||
|                         } |                         } | ||||||
|                     }); |                     }); | ||||||
| @@ -607,10 +601,8 @@ impl ClientHandle { | |||||||
|                     Ok(()) |                     Ok(()) | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
|  |  | ||||||
|                 // work is Future<(), ()>, and our closure will never return Err |                 // work is Future<(), ()>, and our closure will never return Err | ||||||
|             rt.block_on(work) |                 rt.block_on(work).expect("runtime unexpected error"); | ||||||
|                 .expect("runtime unexpected error"); |  | ||||||
|             })); |             })); | ||||||
|  |  | ||||||
|         // Wait for the runtime thread to start up... |         // Wait for the runtime thread to start up... | ||||||
| @@ -620,13 +612,11 @@ impl ClientHandle { | |||||||
|             Err(_canceled) => event_loop_panicked(), |             Err(_canceled) => event_loop_panicked(), | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|         let inner_handle = Arc::new(InnerClientHandle { |         let inner_handle = Arc::new(InnerClientHandle { | ||||||
|             tx: Some(tx), |             tx: Some(tx), | ||||||
|             thread: Some(handle) |             thread: Some(handle), | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |  | ||||||
|         Ok(ClientHandle { |         Ok(ClientHandle { | ||||||
|             timeout, |             timeout, | ||||||
|             inner: inner_handle, |             inner: inner_handle, | ||||||
| @@ -637,7 +627,8 @@ impl ClientHandle { | |||||||
|         let (tx, rx) = oneshot::channel(); |         let (tx, rx) = oneshot::channel(); | ||||||
|         let (req, body) = req.into_async(); |         let (req, body) = req.into_async(); | ||||||
|         let url = req.url().clone(); |         let url = req.url().clone(); | ||||||
|         self.inner.tx |         self.inner | ||||||
|  |             .tx | ||||||
|             .as_ref() |             .as_ref() | ||||||
|             .expect("core thread exited early") |             .expect("core thread exited early") | ||||||
|             .unbounded_send((req, tx)) |             .unbounded_send((req, tx)) | ||||||
| @@ -657,15 +648,17 @@ impl ClientHandle { | |||||||
|         let res = match wait::timeout(fut, self.timeout.0) { |         let res = match wait::timeout(fut, self.timeout.0) { | ||||||
|             Ok(res) => res, |             Ok(res) => res, | ||||||
|             Err(wait::Waited::TimedOut) => return Err(crate::error::timedout(Some(url))), |             Err(wait::Waited::TimedOut) => return Err(crate::error::timedout(Some(url))), | ||||||
|             Err(wait::Waited::Executor(err)) => { |             Err(wait::Waited::Executor(err)) => return Err(crate::error::from(err).with_url(url)), | ||||||
|                 return Err(crate::error::from(err).with_url(url)) |  | ||||||
|             }, |  | ||||||
|             Err(wait::Waited::Inner(err)) => { |             Err(wait::Waited::Inner(err)) => { | ||||||
|                 return Err(err.with_url(url)); |                 return Err(err.with_url(url)); | ||||||
|             }, |             } | ||||||
|         }; |         }; | ||||||
|         res.map(|res| { |         res.map(|res| { | ||||||
|             Response::new(res, self.timeout.0, KeepCoreThreadAlive(Some(self.inner.clone()))) |             Response::new( | ||||||
|  |                 res, | ||||||
|  |                 self.timeout.0, | ||||||
|  |                 KeepCoreThreadAlive(Some(self.inner.clone())), | ||||||
|  |             ) | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										301
									
								
								src/connect.rs
									
									
									
									
									
								
							
							
						
						
									
										301
									
								
								src/connect.rs
									
									
									
									
									
								
							| @@ -4,16 +4,16 @@ use hyper::client::connect::{Connect, Connected, Destination}; | |||||||
| use tokio_io::{AsyncRead, AsyncWrite}; | use tokio_io::{AsyncRead, AsyncWrite}; | ||||||
| use tokio_timer::Timeout; | use tokio_timer::Timeout; | ||||||
|  |  | ||||||
| #[cfg(feature = "default-tls")] |  | ||||||
| use native_tls::{TlsConnector, TlsConnectorBuilder}; |  | ||||||
| #[cfg(feature = "tls")] |  | ||||||
| use futures::Poll; |  | ||||||
| #[cfg(feature = "tls")] | #[cfg(feature = "tls")] | ||||||
| use bytes::BufMut; | use bytes::BufMut; | ||||||
|  | #[cfg(feature = "tls")] | ||||||
|  | use futures::Poll; | ||||||
|  | #[cfg(feature = "default-tls")] | ||||||
|  | use native_tls::{TlsConnector, TlsConnectorBuilder}; | ||||||
|  |  | ||||||
| use std::io; | use std::io; | ||||||
| use std::sync::Arc; |  | ||||||
| use std::net::IpAddr; | use std::net::IpAddr; | ||||||
|  | use std::sync::Arc; | ||||||
| use std::time::Duration; | use std::time::Duration; | ||||||
|  |  | ||||||
| #[cfg(feature = "trust-dns")] | #[cfg(feature = "trust-dns")] | ||||||
| @@ -25,13 +25,12 @@ type HttpConnector = hyper::client::HttpConnector<TrustDnsResolver>; | |||||||
| #[cfg(not(feature = "trust-dns"))] | #[cfg(not(feature = "trust-dns"))] | ||||||
| type HttpConnector = hyper::client::HttpConnector; | type HttpConnector = hyper::client::HttpConnector; | ||||||
|  |  | ||||||
|  |  | ||||||
| pub(crate) struct Connector { | pub(crate) struct Connector { | ||||||
|     inner: Inner, |     inner: Inner, | ||||||
|     proxies: Arc<Vec<Proxy>>, |     proxies: Arc<Vec<Proxy>>, | ||||||
|     timeout: Option<Duration>, |     timeout: Option<Duration>, | ||||||
|     #[cfg(feature = "tls")] |     #[cfg(feature = "tls")] | ||||||
|     nodelay: bool |     nodelay: bool, | ||||||
| } | } | ||||||
|  |  | ||||||
| enum Inner { | enum Inner { | ||||||
| @@ -43,17 +42,20 @@ enum Inner { | |||||||
|     RustlsTls { |     RustlsTls { | ||||||
|         http: HttpConnector, |         http: HttpConnector, | ||||||
|         tls: Arc<rustls::ClientConfig>, |         tls: Arc<rustls::ClientConfig>, | ||||||
|         tls_proxy: Arc<rustls::ClientConfig> |         tls_proxy: Arc<rustls::ClientConfig>, | ||||||
|     } |     }, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Connector { | impl Connector { | ||||||
|     #[cfg(not(feature = "tls"))] |     #[cfg(not(feature = "tls"))] | ||||||
|     pub(crate) fn new<T>(proxies: Arc<Vec<Proxy>>, local_addr: T, nodelay: bool) -> crate::Result<Connector> |     pub(crate) fn new<T>( | ||||||
|  |         proxies: Arc<Vec<Proxy>>, | ||||||
|  |         local_addr: T, | ||||||
|  |         nodelay: bool, | ||||||
|  |     ) -> crate::Result<Connector> | ||||||
|     where |     where | ||||||
|         T: Into<Option<IpAddr>> |         T: Into<Option<IpAddr>>, | ||||||
|     { |     { | ||||||
|  |  | ||||||
|         let mut http = http_connector()?; |         let mut http = http_connector()?; | ||||||
|         http.set_local_address(local_addr.into()); |         http.set_local_address(local_addr.into()); | ||||||
|         http.set_nodelay(nodelay); |         http.set_nodelay(nodelay); | ||||||
| @@ -69,7 +71,8 @@ impl Connector { | |||||||
|         tls: TlsConnectorBuilder, |         tls: TlsConnectorBuilder, | ||||||
|         proxies: Arc<Vec<Proxy>>, |         proxies: Arc<Vec<Proxy>>, | ||||||
|         local_addr: T, |         local_addr: T, | ||||||
|         nodelay: bool) -> crate::Result<Connector> |         nodelay: bool, | ||||||
|  |     ) -> crate::Result<Connector> | ||||||
|     where |     where | ||||||
|         T: Into<Option<IpAddr>>, |         T: Into<Option<IpAddr>>, | ||||||
|     { |     { | ||||||
| @@ -83,7 +86,7 @@ impl Connector { | |||||||
|             inner: Inner::DefaultTls(http, tls), |             inner: Inner::DefaultTls(http, tls), | ||||||
|             proxies, |             proxies, | ||||||
|             timeout: None, |             timeout: None, | ||||||
|             nodelay |             nodelay, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -92,7 +95,8 @@ impl Connector { | |||||||
|         tls: rustls::ClientConfig, |         tls: rustls::ClientConfig, | ||||||
|         proxies: Arc<Vec<Proxy>>, |         proxies: Arc<Vec<Proxy>>, | ||||||
|         local_addr: T, |         local_addr: T, | ||||||
|         nodelay: bool) -> crate::Result<Connector> |         nodelay: bool, | ||||||
|  |     ) -> crate::Result<Connector> | ||||||
|     where |     where | ||||||
|         T: Into<Option<IpAddr>>, |         T: Into<Option<IpAddr>>, | ||||||
|     { |     { | ||||||
| @@ -110,10 +114,14 @@ impl Connector { | |||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         Ok(Connector { |         Ok(Connector { | ||||||
|             inner: Inner::RustlsTls { http, tls, tls_proxy }, |             inner: Inner::RustlsTls { | ||||||
|  |                 http, | ||||||
|  |                 tls, | ||||||
|  |                 tls_proxy, | ||||||
|  |             }, | ||||||
|             proxies, |             proxies, | ||||||
|             timeout: None, |             timeout: None, | ||||||
|             nodelay |             nodelay, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -138,21 +146,25 @@ impl Connector { | |||||||
|                 } else { |                 } else { | ||||||
|                     Box::new($future) |                     Box::new($future) | ||||||
|                 } |                 } | ||||||
|             } |             }; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         let dns = match proxy { |         let dns = match proxy { | ||||||
|             ProxyScheme::Socks5 { remote_dns: false, .. } => socks::DnsResolve::Local, |             ProxyScheme::Socks5 { | ||||||
|             ProxyScheme::Socks5 { remote_dns: true, .. } => socks::DnsResolve::Proxy, |                 remote_dns: false, .. | ||||||
|  |             } => socks::DnsResolve::Local, | ||||||
|  |             ProxyScheme::Socks5 { | ||||||
|  |                 remote_dns: true, .. | ||||||
|  |             } => socks::DnsResolve::Proxy, | ||||||
|             ProxyScheme::Http { .. } => { |             ProxyScheme::Http { .. } => { | ||||||
|                 unreachable!("connect_socks is only called for socks proxies"); |                 unreachable!("connect_socks is only called for socks proxies"); | ||||||
|             }, |             } | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|  |  | ||||||
|         match &self.inner { |         match &self.inner { | ||||||
|             #[cfg(feature = "default-tls")] |             #[cfg(feature = "default-tls")] | ||||||
|             Inner::DefaultTls(_http, tls) => if dst.scheme() == "https" { |             Inner::DefaultTls(_http, tls) => { | ||||||
|  |                 if dst.scheme() == "https" { | ||||||
|                     use self::native_tls_async::TlsConnectorExt; |                     use self::native_tls_async::TlsConnectorExt; | ||||||
|  |  | ||||||
|                     let tls = tls.clone(); |                     let tls = tls.clone(); | ||||||
| @@ -163,11 +175,13 @@ impl Connector { | |||||||
|                             .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) |                             .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) | ||||||
|                             .map(move |io| (Box::new(io) as Conn, connected)) |                             .map(move |io| (Box::new(io) as Conn, connected)) | ||||||
|                     })); |                     })); | ||||||
|             }, |                 } | ||||||
|  |             } | ||||||
|             #[cfg(feature = "rustls-tls")] |             #[cfg(feature = "rustls-tls")] | ||||||
|             Inner::RustlsTls { tls_proxy, .. } => if dst.scheme() == "https" { |             Inner::RustlsTls { tls_proxy, .. } => { | ||||||
|                 use tokio_rustls::TlsConnector as RustlsConnector; |                 if dst.scheme() == "https" { | ||||||
|                     use tokio_rustls::webpki::DNSNameRef; |                     use tokio_rustls::webpki::DNSNameRef; | ||||||
|  |                     use tokio_rustls::TlsConnector as RustlsConnector; | ||||||
|  |  | ||||||
|                     let tls = tls_proxy.clone(); |                     let tls = tls_proxy.clone(); | ||||||
|                     let host = dst.host().to_owned(); |                     let host = dst.host().to_owned(); | ||||||
| @@ -178,16 +192,16 @@ impl Connector { | |||||||
|                             .map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid DNS Name")); |                             .map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid DNS Name")); | ||||||
|                         futures::future::result(maybe_dnsname) |                         futures::future::result(maybe_dnsname) | ||||||
|                             .and_then(move |dnsname| { |                             .and_then(move |dnsname| { | ||||||
|                             RustlsConnector::from(tls).connect(dnsname.as_ref(), conn) |                                 RustlsConnector::from(tls) | ||||||
|  |                                     .connect(dnsname.as_ref(), conn) | ||||||
|                                     .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) |                                     .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) | ||||||
|                             }) |                             }) | ||||||
|                         .map(move |io| { |                             .map(move |io| (Box::new(io) as Conn, connected)) | ||||||
|                             (Box::new(io) as Conn, connected) |  | ||||||
|                         }) |  | ||||||
|                     })); |                     })); | ||||||
|             }, |                 } | ||||||
|  |             } | ||||||
|             #[cfg(not(feature = "tls"))] |             #[cfg(not(feature = "tls"))] | ||||||
|             Inner::Http(_) => () |             Inner::Http(_) => (), | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // else no TLS |         // else no TLS | ||||||
| @@ -231,12 +245,13 @@ impl Connect for Connector { | |||||||
|                 } else { |                 } else { | ||||||
|                     Box::new($future) |                     Box::new($future) | ||||||
|                 } |                 } | ||||||
|             } |             }; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         macro_rules! connect { |         macro_rules! connect { | ||||||
|             ( $http:expr, $dst:expr, $proxy:expr ) => { |             ( $http:expr, $dst:expr, $proxy:expr ) => { | ||||||
|                 timeout!($http.connect($dst) |                 timeout!($http | ||||||
|  |                     .connect($dst) | ||||||
|                     .map(|(io, connected)| (Box::new(io) as Conn, connected.proxy($proxy)))) |                     .map(|(io, connected)| (Box::new(io) as Conn, connected.proxy($proxy)))) | ||||||
|             }; |             }; | ||||||
|             ( $dst:expr, $proxy:expr ) => { |             ( $dst:expr, $proxy:expr ) => { | ||||||
| @@ -250,8 +265,7 @@ impl Connect for Connector { | |||||||
|                         http.set_nodelay(nodelay || ($dst.scheme() == "https")); |                         http.set_nodelay(nodelay || ($dst.scheme() == "https")); | ||||||
|  |  | ||||||
|                         let http = hyper_tls::HttpsConnector::from((http, tls.clone())); |                         let http = hyper_tls::HttpsConnector::from((http, tls.clone())); | ||||||
|                         timeout!(http.connect($dst) |                         timeout!(http.connect($dst).and_then(move |(io, connected)| { | ||||||
|                             .and_then(move |(io, connected)| { |  | ||||||
|                             if let hyper_tls::MaybeHttpsStream::Https(stream) = &io { |                             if let hyper_tls::MaybeHttpsStream::Https(stream) = &io { | ||||||
|                                 if !nodelay { |                                 if !nodelay { | ||||||
|                                     stream.get_ref().get_ref().set_nodelay(false)?; |                                     stream.get_ref().get_ref().set_nodelay(false)?; | ||||||
| @@ -260,7 +274,7 @@ impl Connect for Connector { | |||||||
|  |  | ||||||
|                             Ok((Box::new(io) as Conn, connected.proxy($proxy))) |                             Ok((Box::new(io) as Conn, connected.proxy($proxy))) | ||||||
|                         })) |                         })) | ||||||
|                     }, |                     } | ||||||
|                     #[cfg(feature = "rustls-tls")] |                     #[cfg(feature = "rustls-tls")] | ||||||
|                     Inner::RustlsTls { http, tls, .. } => { |                     Inner::RustlsTls { http, tls, .. } => { | ||||||
|                         let mut http = http.clone(); |                         let mut http = http.clone(); | ||||||
| @@ -271,8 +285,7 @@ impl Connect for Connector { | |||||||
|                         http.set_nodelay(nodelay || ($dst.scheme() == "https")); |                         http.set_nodelay(nodelay || ($dst.scheme() == "https")); | ||||||
|  |  | ||||||
|                         let http = hyper_rustls::HttpsConnector::from((http, tls.clone())); |                         let http = hyper_rustls::HttpsConnector::from((http, tls.clone())); | ||||||
|                         timeout!(http.connect($dst) |                         timeout!(http.connect($dst).and_then(move |(io, connected)| { | ||||||
|                             .and_then(move |(io, connected)| { |  | ||||||
|                             if let hyper_rustls::MaybeHttpsStream::Https(stream) = &io { |                             if let hyper_rustls::MaybeHttpsStream::Https(stream) = &io { | ||||||
|                                 if !nodelay { |                                 if !nodelay { | ||||||
|                                     let (io, _) = stream.get_ref(); |                                     let (io, _) = stream.get_ref(); | ||||||
| @@ -299,10 +312,7 @@ impl Connect for Connector { | |||||||
|  |  | ||||||
|                 let mut ndst = dst.clone(); |                 let mut ndst = dst.clone(); | ||||||
|  |  | ||||||
|                 let new_scheme = puri |                 let new_scheme = puri.scheme_part().map(Scheme::as_str).unwrap_or("http"); | ||||||
|                     .scheme_part() |  | ||||||
|                     .map(Scheme::as_str) |  | ||||||
|                     .unwrap_or("http"); |  | ||||||
|                 ndst.set_scheme(new_scheme) |                 ndst.set_scheme(new_scheme) | ||||||
|                     .expect("proxy target scheme should be valid"); |                     .expect("proxy target scheme should be valid"); | ||||||
|  |  | ||||||
| @@ -316,7 +326,8 @@ impl Connect for Connector { | |||||||
|  |  | ||||||
|                 match &self.inner { |                 match &self.inner { | ||||||
|                     #[cfg(feature = "default-tls")] |                     #[cfg(feature = "default-tls")] | ||||||
|                     Inner::DefaultTls(http, tls) => if dst.scheme() == "https" { |                     Inner::DefaultTls(http, tls) => { | ||||||
|  |                         if dst.scheme() == "https" { | ||||||
|                             use self::native_tls_async::TlsConnectorExt; |                             use self::native_tls_async::TlsConnectorExt; | ||||||
|  |  | ||||||
|                             let host = dst.host().to_owned(); |                             let host = dst.host().to_owned(); | ||||||
| @@ -325,51 +336,71 @@ impl Connect for Connector { | |||||||
|                             http.set_nodelay(nodelay); |                             http.set_nodelay(nodelay); | ||||||
|                             let http = hyper_tls::HttpsConnector::from((http, tls.clone())); |                             let http = hyper_tls::HttpsConnector::from((http, tls.clone())); | ||||||
|                             let tls = tls.clone(); |                             let tls = tls.clone(); | ||||||
|                         return timeout!(http.connect(ndst).and_then(move |(conn, connected)| { |                             return timeout!(http.connect(ndst).and_then( | ||||||
|  |                                 move |(conn, connected)| { | ||||||
|                                     log::trace!("tunneling HTTPS over proxy"); |                                     log::trace!("tunneling HTTPS over proxy"); | ||||||
|                                     tunnel(conn, host.clone(), port, auth) |                                     tunnel(conn, host.clone(), port, auth) | ||||||
|                                         .and_then(move |tunneled| { |                                         .and_then(move |tunneled| { | ||||||
|                                     tls.connect_async(&host, tunneled) |                                             tls.connect_async(&host, tunneled).map_err(|e| { | ||||||
|                                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) |                                                 io::Error::new(io::ErrorKind::Other, e) | ||||||
|  |                                             }) | ||||||
|                                         }) |                                         }) | ||||||
|                                         .map(|io| (Box::new(io) as Conn, connected.proxy(true))) |                                         .map(|io| (Box::new(io) as Conn, connected.proxy(true))) | ||||||
|                         })); |                                 } | ||||||
|                     }, |                             )); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|                     #[cfg(feature = "rustls-tls")] |                     #[cfg(feature = "rustls-tls")] | ||||||
|                     Inner::RustlsTls { http, tls, tls_proxy } => if dst.scheme() == "https" { |                     Inner::RustlsTls { | ||||||
|  |                         http, | ||||||
|  |                         tls, | ||||||
|  |                         tls_proxy, | ||||||
|  |                     } => { | ||||||
|  |                         if dst.scheme() == "https" { | ||||||
|                             use rustls::Session; |                             use rustls::Session; | ||||||
|                         use tokio_rustls::TlsConnector as RustlsConnector; |  | ||||||
|                             use tokio_rustls::webpki::DNSNameRef; |                             use tokio_rustls::webpki::DNSNameRef; | ||||||
|  |                             use tokio_rustls::TlsConnector as RustlsConnector; | ||||||
|  |  | ||||||
|                             let host = dst.host().to_owned(); |                             let host = dst.host().to_owned(); | ||||||
|                             let port = dst.port().unwrap_or(443); |                             let port = dst.port().unwrap_or(443); | ||||||
|                             let mut http = http.clone(); |                             let mut http = http.clone(); | ||||||
|                             http.set_nodelay(nodelay); |                             http.set_nodelay(nodelay); | ||||||
|                         let http = hyper_rustls::HttpsConnector::from((http, tls_proxy.clone())); |                             let http = | ||||||
|  |                                 hyper_rustls::HttpsConnector::from((http, tls_proxy.clone())); | ||||||
|                             let tls = tls.clone(); |                             let tls = tls.clone(); | ||||||
|                         return timeout!(http.connect(ndst).and_then(move |(conn, connected)| { |                             return timeout!(http.connect(ndst).and_then( | ||||||
|  |                                 move |(conn, connected)| { | ||||||
|                                     log::trace!("tunneling HTTPS over proxy"); |                                     log::trace!("tunneling HTTPS over proxy"); | ||||||
|                                     let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host) |                                     let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host) | ||||||
|                                         .map(|dnsname| dnsname.to_owned()) |                                         .map(|dnsname| dnsname.to_owned()) | ||||||
|                                 .map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid DNS Name")); |                                         .map_err(|_| { | ||||||
|  |                                             io::Error::new(io::ErrorKind::Other, "Invalid DNS Name") | ||||||
|  |                                         }); | ||||||
|                                     tunnel(conn, host, port, auth) |                                     tunnel(conn, host, port, auth) | ||||||
|                                         .and_then(move |tunneled| Ok((maybe_dnsname?, tunneled))) |                                         .and_then(move |tunneled| Ok((maybe_dnsname?, tunneled))) | ||||||
|                                         .and_then(move |(dnsname, tunneled)| { |                                         .and_then(move |(dnsname, tunneled)| { | ||||||
|                                     RustlsConnector::from(tls).connect(dnsname.as_ref(), tunneled) |                                             RustlsConnector::from(tls) | ||||||
|                                         .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) |                                                 .connect(dnsname.as_ref(), tunneled) | ||||||
|  |                                                 .map_err(|e| { | ||||||
|  |                                                     io::Error::new(io::ErrorKind::Other, e) | ||||||
|  |                                                 }) | ||||||
|                                         }) |                                         }) | ||||||
|                                         .map(|io| { |                                         .map(|io| { | ||||||
|                                     let connected = if io.get_ref().1.get_alpn_protocol() == Some(b"h2") { |                                             let connected = if io.get_ref().1.get_alpn_protocol() | ||||||
|  |                                                 == Some(b"h2") | ||||||
|  |                                             { | ||||||
|                                                 connected.negotiated_h2() |                                                 connected.negotiated_h2() | ||||||
|                                             } else { |                                             } else { | ||||||
|                                                 connected |                                                 connected | ||||||
|                                             }; |                                             }; | ||||||
|                                             (Box::new(io) as Conn, connected.proxy(true)) |                                             (Box::new(io) as Conn, connected.proxy(true)) | ||||||
|                                         }) |                                         }) | ||||||
|                         })); |                                 } | ||||||
|                     }, |                             )); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|                     #[cfg(not(feature = "tls"))] |                     #[cfg(not(feature = "tls"))] | ||||||
|                     Inner::Http(_) => () |                     Inner::Http(_) => (), | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 return connect!(ndst, true); |                 return connect!(ndst, true); | ||||||
| @@ -384,14 +415,23 @@ pub(crate) trait AsyncConn: AsyncRead + AsyncWrite {} | |||||||
| impl<T: AsyncRead + AsyncWrite> AsyncConn for T {} | impl<T: AsyncRead + AsyncWrite> AsyncConn for T {} | ||||||
| pub(crate) type Conn = Box<dyn AsyncConn + Send + Sync + 'static>; | pub(crate) type Conn = Box<dyn AsyncConn + Send + Sync + 'static>; | ||||||
|  |  | ||||||
| pub(crate) type Connecting = Box<dyn Future<Item=(Conn, Connected), Error=io::Error> + Send>; | pub(crate) type Connecting = Box<dyn Future<Item = (Conn, Connected), Error = io::Error> + Send>; | ||||||
|  |  | ||||||
| #[cfg(feature = "tls")] | #[cfg(feature = "tls")] | ||||||
| fn tunnel<T>(conn: T, host: String, port: u16, auth: Option<http::header::HeaderValue>) -> Tunnel<T> { | fn tunnel<T>( | ||||||
|     let mut buf = format!("\ |     conn: T, | ||||||
|  |     host: String, | ||||||
|  |     port: u16, | ||||||
|  |     auth: Option<http::header::HeaderValue>, | ||||||
|  | ) -> Tunnel<T> { | ||||||
|  |     let mut buf = format!( | ||||||
|  |         "\ | ||||||
|          CONNECT {0}:{1} HTTP/1.1\r\n\ |          CONNECT {0}:{1} HTTP/1.1\r\n\ | ||||||
|          Host: {0}:{1}\r\n\ |          Host: {0}:{1}\r\n\ | ||||||
|     ", host, port).into_bytes(); |          ", | ||||||
|  |         host, port | ||||||
|  |     ) | ||||||
|  |     .into_bytes(); | ||||||
|  |  | ||||||
|     if let Some(value) = auth { |     if let Some(value) = auth { | ||||||
|         log::debug!("tunnel to {}:{} using basic auth", host, port); |         log::debug!("tunnel to {}:{} using basic auth", host, port); | ||||||
| @@ -420,7 +460,7 @@ struct Tunnel<T> { | |||||||
| #[cfg(feature = "tls")] | #[cfg(feature = "tls")] | ||||||
| enum TunnelState { | enum TunnelState { | ||||||
|     Writing, |     Writing, | ||||||
|     Reading |     Reading, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[cfg(feature = "tls")] | #[cfg(feature = "tls")] | ||||||
| @@ -442,7 +482,11 @@ where | |||||||
|                     return Err(tunnel_eof()); |                     return Err(tunnel_eof()); | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 let n = futures::try_ready!(self.conn.as_mut().unwrap().read_buf(&mut self.buf.get_mut())); |                 let n = futures::try_ready!(self | ||||||
|  |                     .conn | ||||||
|  |                     .as_mut() | ||||||
|  |                     .unwrap() | ||||||
|  |                     .read_buf(&mut self.buf.get_mut())); | ||||||
|                 let read = &self.buf.get_ref()[..]; |                 let read = &self.buf.get_ref()[..]; | ||||||
|                 if n == 0 { |                 if n == 0 { | ||||||
|                     return Err(tunnel_eof()); |                     return Err(tunnel_eof()); | ||||||
| @@ -453,7 +497,10 @@ where | |||||||
|                         } |                         } | ||||||
|                     // else read more |                     // else read more | ||||||
|                     } else if read.starts_with(b"HTTP/1.1 407") { |                     } else if read.starts_with(b"HTTP/1.1 407") { | ||||||
|                         return Err(io::Error::new(io::ErrorKind::Other, "proxy authentication required")); |                         return Err(io::Error::new( | ||||||
|  |                             io::ErrorKind::Other, | ||||||
|  |                             "proxy authentication required", | ||||||
|  |                         )); | ||||||
|                     } else if read.starts_with(b"HTTP/1.1 403") { |                     } else if read.starts_with(b"HTTP/1.1 403") { | ||||||
|                         return Err(io::Error::new( |                         return Err(io::Error::new( | ||||||
|                             io::ErrorKind::Other, |                             io::ErrorKind::Other, | ||||||
| @@ -477,7 +524,7 @@ where | |||||||
| fn tunnel_eof() -> io::Error { | fn tunnel_eof() -> io::Error { | ||||||
|     io::Error::new( |     io::Error::new( | ||||||
|         io::ErrorKind::UnexpectedEof, |         io::ErrorKind::UnexpectedEof, | ||||||
|         "unexpected eof while tunneling" |         "unexpected eof while tunneling", | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -485,9 +532,9 @@ fn tunnel_eof() -> io::Error { | |||||||
| mod native_tls_async { | mod native_tls_async { | ||||||
|     use std::io::{self, Read, Write}; |     use std::io::{self, Read, Write}; | ||||||
|  |  | ||||||
|     use futures::{Poll, Future, Async}; |     use futures::{Async, Future, Poll}; | ||||||
|     use native_tls::{self, HandshakeError, Error, TlsConnector}; |     use native_tls::{self, Error, HandshakeError, TlsConnector}; | ||||||
|     use tokio_io::{AsyncRead, AsyncWrite, try_nb}; |     use tokio_io::{try_nb, AsyncRead, AsyncWrite}; | ||||||
|  |  | ||||||
|     /// A wrapper around an underlying raw stream which implements the TLS or SSL |     /// A wrapper around an underlying raw stream which implements the TLS or SSL | ||||||
|     /// protocol. |     /// protocol. | ||||||
| @@ -533,7 +580,8 @@ mod native_tls_async { | |||||||
|         /// and `AsyncWrite` traits as well, otherwise this function will not work |         /// and `AsyncWrite` traits as well, otherwise this function will not work | ||||||
|         /// properly. |         /// properly. | ||||||
|         fn connect_async<S>(&self, domain: &str, stream: S) -> ConnectAsync<S> |         fn connect_async<S>(&self, domain: &str, stream: S) -> ConnectAsync<S> | ||||||
|             where S: Read + Write; // TODO: change to AsyncRead + AsyncWrite |         where | ||||||
|  |             S: Read + Write; // TODO: change to AsyncRead + AsyncWrite | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     mod sealed { |     mod sealed { | ||||||
| @@ -556,9 +604,7 @@ mod native_tls_async { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     impl<S: AsyncRead + AsyncWrite> AsyncRead for TlsStream<S> {} | ||||||
|     impl<S: AsyncRead + AsyncWrite> AsyncRead for TlsStream<S> { |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     impl<S: AsyncRead + AsyncWrite> AsyncWrite for TlsStream<S> { |     impl<S: AsyncRead + AsyncWrite> AsyncWrite for TlsStream<S> { | ||||||
|         fn shutdown(&mut self) -> Poll<(), io::Error> { |         fn shutdown(&mut self) -> Poll<(), io::Error> { | ||||||
| @@ -569,7 +615,8 @@ mod native_tls_async { | |||||||
|  |  | ||||||
|     impl TlsConnectorExt for TlsConnector { |     impl TlsConnectorExt for TlsConnector { | ||||||
|         fn connect_async<S>(&self, domain: &str, stream: S) -> ConnectAsync<S> |         fn connect_async<S>(&self, domain: &str, stream: S) -> ConnectAsync<S> | ||||||
|             where S: Read + Write, |         where | ||||||
|  |             S: Read + Write, | ||||||
|         { |         { | ||||||
|             ConnectAsync { |             ConnectAsync { | ||||||
|                 inner: MidHandshake { |                 inner: MidHandshake { | ||||||
| @@ -600,51 +647,42 @@ mod native_tls_async { | |||||||
|             match self.inner.take().expect("cannot poll MidHandshake twice") { |             match self.inner.take().expect("cannot poll MidHandshake twice") { | ||||||
|                 Ok(stream) => Ok(TlsStream { inner: stream }.into()), |                 Ok(stream) => Ok(TlsStream { inner: stream }.into()), | ||||||
|                 Err(HandshakeError::Failure(e)) => Err(e), |                 Err(HandshakeError::Failure(e)) => Err(e), | ||||||
|                 Err(HandshakeError::WouldBlock(s)) => { |                 Err(HandshakeError::WouldBlock(s)) => match s.handshake() { | ||||||
|                     match s.handshake() { |  | ||||||
|                     Ok(stream) => Ok(TlsStream { inner: stream }.into()), |                     Ok(stream) => Ok(TlsStream { inner: stream }.into()), | ||||||
|                     Err(HandshakeError::Failure(e)) => Err(e), |                     Err(HandshakeError::Failure(e)) => Err(e), | ||||||
|                     Err(HandshakeError::WouldBlock(s)) => { |                     Err(HandshakeError::WouldBlock(s)) => { | ||||||
|                         self.inner = Some(Err(HandshakeError::WouldBlock(s))); |                         self.inner = Some(Err(HandshakeError::WouldBlock(s))); | ||||||
|                         Ok(Async::NotReady) |                         Ok(Async::NotReady) | ||||||
|                     } |                     } | ||||||
|                     } |                 }, | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| #[cfg(feature = "socks")] | #[cfg(feature = "socks")] | ||||||
| mod socks { | mod socks { | ||||||
|     use std::io; |     use std::io; | ||||||
|  |  | ||||||
|     use futures::{Future, future}; |     use futures::{future, Future}; | ||||||
|     use hyper::client::connect::{Connected, Destination}; |     use hyper::client::connect::{Connected, Destination}; | ||||||
|     use socks::Socks5Stream; |     use socks::Socks5Stream; | ||||||
|     use std::net::ToSocketAddrs; |     use std::net::ToSocketAddrs; | ||||||
|     use tokio::{net::TcpStream, reactor}; |     use tokio::{net::TcpStream, reactor}; | ||||||
|  |  | ||||||
|     use super::{Connecting}; |     use super::Connecting; | ||||||
|     use crate::proxy::{ProxyScheme}; |     use crate::proxy::ProxyScheme; | ||||||
|  |  | ||||||
|     pub(super) enum DnsResolve { |     pub(super) enum DnsResolve { | ||||||
|         Local, |         Local, | ||||||
|         Proxy, |         Proxy, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(super) fn connect( |     pub(super) fn connect(proxy: ProxyScheme, dst: Destination, dns: DnsResolve) -> Connecting { | ||||||
|         proxy: ProxyScheme, |  | ||||||
|         dst: Destination, |  | ||||||
|         dns: DnsResolve, |  | ||||||
|     ) -> Connecting { |  | ||||||
|         let https = dst.scheme() == "https"; |         let https = dst.scheme() == "https"; | ||||||
|         let original_host = dst.host().to_owned(); |         let original_host = dst.host().to_owned(); | ||||||
|         let mut host = original_host.clone(); |         let mut host = original_host.clone(); | ||||||
|         let port = dst.port().unwrap_or_else(|| { |         let port = dst.port().unwrap_or_else(|| if https { 443 } else { 80 }); | ||||||
|             if https { 443 } else { 80 } |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         if let DnsResolve::Local = dns { |         if let DnsResolve::Local = dns { | ||||||
|             let maybe_new_target = match (host.as_str(), port).to_socket_addrs() { |             let maybe_new_target = match (host.as_str(), port).to_socket_addrs() { | ||||||
| @@ -664,20 +702,24 @@ mod socks { | |||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         // Get a Tokio TcpStream |         // Get a Tokio TcpStream | ||||||
|         let stream = future::result(if let Some((username, password)) = auth { |         let stream = future::result( | ||||||
|  |             if let Some((username, password)) = auth { | ||||||
|                 Socks5Stream::connect_with_password( |                 Socks5Stream::connect_with_password( | ||||||
|                 socket_addr, (host.as_str(), port), &username, &password |                     socket_addr, | ||||||
|  |                     (host.as_str(), port), | ||||||
|  |                     &username, | ||||||
|  |                     &password, | ||||||
|                 ) |                 ) | ||||||
|             } else { |             } else { | ||||||
|                 Socks5Stream::connect(socket_addr, (host.as_str(), port)) |                 Socks5Stream::connect(socket_addr, (host.as_str(), port)) | ||||||
|         }.and_then(|s| { |             } | ||||||
|  |             .and_then(|s| { | ||||||
|                 TcpStream::from_std(s.into_inner(), &reactor::Handle::default()) |                 TcpStream::from_std(s.into_inner(), &reactor::Handle::default()) | ||||||
|                     .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) |                     .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) | ||||||
|         })); |             }), | ||||||
|  |         ); | ||||||
|  |  | ||||||
|         Box::new( |         Box::new(stream.map(|s| (Box::new(s) as super::Conn, Connected::new()))) | ||||||
|             stream.map(|s| (Box::new(s) as super::Conn, Connected::new())) |  | ||||||
|         ) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -686,14 +728,14 @@ mod socks { | |||||||
| mod tests { | mod tests { | ||||||
|     extern crate tokio_tcp; |     extern crate tokio_tcp; | ||||||
|  |  | ||||||
|     use std::io::{Read, Write}; |  | ||||||
|     use std::net::TcpListener; |  | ||||||
|     use std::thread; |  | ||||||
|     use futures::Future; |  | ||||||
|     use tokio::runtime::current_thread::Runtime; |  | ||||||
|     use self::tokio_tcp::TcpStream; |     use self::tokio_tcp::TcpStream; | ||||||
|     use super::tunnel; |     use super::tunnel; | ||||||
|     use crate::proxy; |     use crate::proxy; | ||||||
|  |     use futures::Future; | ||||||
|  |     use std::io::{Read, Write}; | ||||||
|  |     use std::net::TcpListener; | ||||||
|  |     use std::thread; | ||||||
|  |     use tokio::runtime::current_thread::Runtime; | ||||||
|  |  | ||||||
|     static TUNNEL_OK: &[u8] = b"\ |     static TUNNEL_OK: &[u8] = b"\ | ||||||
|         HTTP/1.1 200 OK\r\n\ |         HTTP/1.1 200 OK\r\n\ | ||||||
| @@ -701,21 +743,27 @@ mod tests { | |||||||
|     "; |     "; | ||||||
|  |  | ||||||
|     macro_rules! mock_tunnel { |     macro_rules! mock_tunnel { | ||||||
|         () => ({ |         () => {{ | ||||||
|             mock_tunnel!(TUNNEL_OK) |             mock_tunnel!(TUNNEL_OK) | ||||||
|         }); |         }}; | ||||||
|         ($write:expr) => ({ |         ($write:expr) => {{ | ||||||
|             mock_tunnel!($write, "") |             mock_tunnel!($write, "") | ||||||
|         }); |         }}; | ||||||
|         ($write:expr, $auth:expr) => ({ |         ($write:expr, $auth:expr) => {{ | ||||||
|             let listener = TcpListener::bind("127.0.0.1:0").unwrap(); |             let listener = TcpListener::bind("127.0.0.1:0").unwrap(); | ||||||
|             let addr = listener.local_addr().unwrap(); |             let addr = listener.local_addr().unwrap(); | ||||||
|             let connect_expected = format!("\ |             let connect_expected = format!( | ||||||
|  |                 "\ | ||||||
|                  CONNECT {0}:{1} HTTP/1.1\r\n\ |                  CONNECT {0}:{1} HTTP/1.1\r\n\ | ||||||
|                  Host: {0}:{1}\r\n\ |                  Host: {0}:{1}\r\n\ | ||||||
|                  {2}\ |                  {2}\ | ||||||
|                  \r\n\ |                  \r\n\ | ||||||
|             ", addr.ip(), addr.port(), $auth).into_bytes(); |                  ", | ||||||
|  |                 addr.ip(), | ||||||
|  |                 addr.port(), | ||||||
|  |                 $auth | ||||||
|  |             ) | ||||||
|  |             .into_bytes(); | ||||||
|  |  | ||||||
|             thread::spawn(move || { |             thread::spawn(move || { | ||||||
|                 let (mut sock, _) = listener.accept().unwrap(); |                 let (mut sock, _) = listener.accept().unwrap(); | ||||||
| @@ -726,7 +774,7 @@ mod tests { | |||||||
|                 sock.write_all($write).unwrap(); |                 sock.write_all($write).unwrap(); | ||||||
|             }); |             }); | ||||||
|             addr |             addr | ||||||
|         }) |         }}; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
| @@ -737,9 +785,7 @@ mod tests { | |||||||
|         let work = TcpStream::connect(&addr); |         let work = TcpStream::connect(&addr); | ||||||
|         let host = addr.ip().to_string(); |         let host = addr.ip().to_string(); | ||||||
|         let port = addr.port(); |         let port = addr.port(); | ||||||
|         let work = work.and_then(|tcp| { |         let work = work.and_then(|tcp| tunnel(tcp, host, port, None)); | ||||||
|             tunnel(tcp, host, port, None) |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         rt.block_on(work).unwrap(); |         rt.block_on(work).unwrap(); | ||||||
|     } |     } | ||||||
| @@ -752,9 +798,7 @@ mod tests { | |||||||
|         let work = TcpStream::connect(&addr); |         let work = TcpStream::connect(&addr); | ||||||
|         let host = addr.ip().to_string(); |         let host = addr.ip().to_string(); | ||||||
|         let port = addr.port(); |         let port = addr.port(); | ||||||
|         let work = work.and_then(|tcp| { |         let work = work.and_then(|tcp| tunnel(tcp, host, port, None)); | ||||||
|             tunnel(tcp, host, port, None) |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         rt.block_on(work).unwrap_err(); |         rt.block_on(work).unwrap_err(); | ||||||
|     } |     } | ||||||
| @@ -767,28 +811,26 @@ mod tests { | |||||||
|         let work = TcpStream::connect(&addr); |         let work = TcpStream::connect(&addr); | ||||||
|         let host = addr.ip().to_string(); |         let host = addr.ip().to_string(); | ||||||
|         let port = addr.port(); |         let port = addr.port(); | ||||||
|         let work = work.and_then(|tcp| { |         let work = work.and_then(|tcp| tunnel(tcp, host, port, None)); | ||||||
|             tunnel(tcp, host, port, None) |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         rt.block_on(work).unwrap_err(); |         rt.block_on(work).unwrap_err(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_tunnel_proxy_unauthorized() { |     fn test_tunnel_proxy_unauthorized() { | ||||||
|         let addr = mock_tunnel!(b"\ |         let addr = mock_tunnel!( | ||||||
|  |             b"\ | ||||||
|             HTTP/1.1 407 Proxy Authentication Required\r\n\ |             HTTP/1.1 407 Proxy Authentication Required\r\n\ | ||||||
|             Proxy-Authenticate: Basic realm=\"nope\"\r\n\ |             Proxy-Authenticate: Basic realm=\"nope\"\r\n\ | ||||||
|             \r\n\ |             \r\n\ | ||||||
|         "); |         " | ||||||
|  |         ); | ||||||
|  |  | ||||||
|         let mut rt = Runtime::new().unwrap(); |         let mut rt = Runtime::new().unwrap(); | ||||||
|         let work = TcpStream::connect(&addr); |         let work = TcpStream::connect(&addr); | ||||||
|         let host = addr.ip().to_string(); |         let host = addr.ip().to_string(); | ||||||
|         let port = addr.port(); |         let port = addr.port(); | ||||||
|         let work = work.and_then(|tcp| { |         let work = work.and_then(|tcp| tunnel(tcp, host, port, None)); | ||||||
|             tunnel(tcp, host, port, None) |  | ||||||
|         }); |  | ||||||
|  |  | ||||||
|         let error = rt.block_on(work).unwrap_err(); |         let error = rt.block_on(work).unwrap_err(); | ||||||
|         assert_eq!(error.to_string(), "proxy authentication required"); |         assert_eq!(error.to_string(), "proxy authentication required"); | ||||||
| @@ -806,7 +848,12 @@ mod tests { | |||||||
|         let host = addr.ip().to_string(); |         let host = addr.ip().to_string(); | ||||||
|         let port = addr.port(); |         let port = addr.port(); | ||||||
|         let work = work.and_then(|tcp| { |         let work = work.and_then(|tcp| { | ||||||
|             tunnel(tcp, host, port, Some(proxy::encode_basic_auth("Aladdin", "open sesame"))) |             tunnel( | ||||||
|  |                 tcp, | ||||||
|  |                 host, | ||||||
|  |                 port, | ||||||
|  |                 Some(proxy::encode_basic_auth("Aladdin", "open sesame")), | ||||||
|  |             ) | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         rt.block_on(work).unwrap(); |         rt.block_on(work).unwrap(); | ||||||
|   | |||||||
| @@ -109,7 +109,9 @@ impl<'a> Cookie<'a> { | |||||||
|  |  | ||||||
|     /// Get the Max-Age information. |     /// Get the Max-Age information. | ||||||
|     pub fn max_age(&self) -> Option<std::time::Duration> { |     pub fn max_age(&self) -> Option<std::time::Duration> { | ||||||
|         self.0.max_age().map(|d| std::time::Duration::new(d.num_seconds() as u64, 0)) |         self.0 | ||||||
|  |             .max_age() | ||||||
|  |             .map(|d| std::time::Duration::new(d.num_seconds() as u64, 0)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// The cookie expiration time. |     /// The cookie expiration time. | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								src/dns.rs
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								src/dns.rs
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| use std::{io, vec}; |  | ||||||
| use std::net::IpAddr; | use std::net::IpAddr; | ||||||
| use std::sync::{Arc, Mutex, Once}; | use std::sync::{Arc, Mutex, Once}; | ||||||
|  | use std::{io, vec}; | ||||||
|  |  | ||||||
| use futures::{future, Future}; | use futures::{future, Future}; | ||||||
| use hyper::client::connect::dns as hyper_dns; | use hyper::client::connect::dns as hyper_dns; | ||||||
| @@ -13,7 +13,7 @@ use trust_dns_resolver::{system_conf, AsyncResolver, BackgroundLookupIp}; | |||||||
| // | // | ||||||
| // "Erasing" the internal resolver type saves us from this limit. | // "Erasing" the internal resolver type saves us from this limit. | ||||||
| type ErasedResolver = Box<dyn Fn(hyper_dns::Name) -> BackgroundLookupIp + Send + Sync>; | type ErasedResolver = Box<dyn Fn(hyper_dns::Name) -> BackgroundLookupIp + Send + Sync>; | ||||||
| type Background = Box<dyn Future<Item=(), Error=()> + Send>; | type Background = Box<dyn Future<Item = (), Error = ()> + Send>; | ||||||
|  |  | ||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| pub(crate) struct TrustDnsResolver { | pub(crate) struct TrustDnsResolver { | ||||||
| @@ -31,9 +31,7 @@ impl TrustDnsResolver { | |||||||
|         let (conf, opts) = system_conf::read_system_conf()?; |         let (conf, opts) = system_conf::read_system_conf()?; | ||||||
|         let (resolver, bg) = AsyncResolver::new(conf, opts); |         let (resolver, bg) = AsyncResolver::new(conf, opts); | ||||||
|  |  | ||||||
|         let resolver: ErasedResolver = Box::new(move |name| { |         let resolver: ErasedResolver = Box::new(move |name| resolver.lookup_ip(name.as_str())); | ||||||
|             resolver.lookup_ip(name.as_str()) |  | ||||||
|         }); |  | ||||||
|         let background = Mutex::new(Some(Box::new(bg) as Background)); |         let background = Mutex::new(Some(Box::new(bg) as Background)); | ||||||
|         let once = Once::new(); |         let once = Once::new(); | ||||||
|  |  | ||||||
| @@ -49,7 +47,7 @@ impl TrustDnsResolver { | |||||||
|  |  | ||||||
| impl hyper_dns::Resolve for TrustDnsResolver { | impl hyper_dns::Resolve for TrustDnsResolver { | ||||||
|     type Addrs = vec::IntoIter<IpAddr>; |     type Addrs = vec::IntoIter<IpAddr>; | ||||||
|     type Future = Box<dyn Future<Item=Self::Addrs, Error=io::Error> + Send>; |     type Future = Box<dyn Future<Item = Self::Addrs, Error = io::Error> + Send>; | ||||||
|  |  | ||||||
|     fn resolve(&self, name: hyper_dns::Name) -> Self::Future { |     fn resolve(&self, name: hyper_dns::Name) -> Self::Future { | ||||||
|         let inner = self.inner.clone(); |         let inner = self.inner.clone(); | ||||||
| @@ -70,16 +68,8 @@ impl hyper_dns::Resolve for TrustDnsResolver { | |||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             (inner.resolver)(name) |             (inner.resolver)(name) | ||||||
|                 .map(|lookup| { |                 .map(|lookup| lookup.iter().collect::<Vec<_>>().into_iter()) | ||||||
|                     lookup |                 .map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string())) | ||||||
|                         .iter() |  | ||||||
|                         .collect::<Vec<_>>() |  | ||||||
|                         .into_iter() |  | ||||||
|                 }) |  | ||||||
|                 .map_err(|err| { |  | ||||||
|                     io::Error::new(io::ErrorKind::Other, err.to_string()) |  | ||||||
|                 }) |  | ||||||
|         })) |         })) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										99
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										99
									
								
								src/error.rs
									
									
									
									
									
								
							| @@ -62,17 +62,13 @@ struct Inner { | |||||||
|     url: Option<Url>, |     url: Option<Url>, | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /// A `Result` alias where the `Err` case is `reqwest::Error`. | /// A `Result` alias where the `Err` case is `reqwest::Error`. | ||||||
| pub type Result<T> = std::result::Result<T, Error>; | pub type Result<T> = std::result::Result<T, Error>; | ||||||
|  |  | ||||||
| impl Error { | impl Error { | ||||||
|     fn new(kind: Kind, url: Option<Url>) -> Error { |     fn new(kind: Kind, url: Option<Url>) -> Error { | ||||||
|         Error { |         Error { | ||||||
|             inner: Box::new(Inner { |             inner: Box::new(Inner { kind, url }), | ||||||
|                 kind, |  | ||||||
|                 url, |  | ||||||
|             }), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -148,13 +144,13 @@ impl Error { | |||||||
|             Kind::Io(ref e) => Some(e), |             Kind::Io(ref e) => Some(e), | ||||||
|             Kind::UrlEncoded(ref e) => Some(e), |             Kind::UrlEncoded(ref e) => Some(e), | ||||||
|             Kind::Json(ref e) => Some(e), |             Kind::Json(ref e) => Some(e), | ||||||
|             Kind::UrlBadScheme | |             Kind::UrlBadScheme | ||||||
|             Kind::TooManyRedirects | |             | Kind::TooManyRedirects | ||||||
|             Kind::RedirectLoop | |             | Kind::RedirectLoop | ||||||
|             Kind::Status(_) | |             | Kind::Status(_) | ||||||
|             Kind::UnknownProxyScheme | |             | Kind::UnknownProxyScheme | ||||||
|             Kind::Timer | |             | Kind::Timer | ||||||
|             Kind::BlockingClientInFutureContext => None, |             | Kind::BlockingClientInFutureContext => None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -172,15 +168,11 @@ impl Error { | |||||||
|     pub fn is_timeout(&self) -> bool { |     pub fn is_timeout(&self) -> bool { | ||||||
|         match self.inner.kind { |         match self.inner.kind { | ||||||
|             Kind::Io(ref io) => io.kind() == io::ErrorKind::TimedOut, |             Kind::Io(ref io) => io.kind() == io::ErrorKind::TimedOut, | ||||||
|             Kind::Hyper(ref error) => { |             Kind::Hyper(ref error) => error | ||||||
|                 error |  | ||||||
|                 .source() |                 .source() | ||||||
|                     .and_then(|cause| { |                 .and_then(|cause| cause.downcast_ref::<io::Error>()) | ||||||
|                         cause.downcast_ref::<io::Error>() |  | ||||||
|                     }) |  | ||||||
|                 .map(|io| io.kind() == io::ErrorKind::TimedOut) |                 .map(|io| io.kind() == io::ErrorKind::TimedOut) | ||||||
|                     .unwrap_or(false) |                 .unwrap_or(false), | ||||||
|             }, |  | ||||||
|             _ => false, |             _ => false, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -189,8 +181,7 @@ impl Error { | |||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn is_serialization(&self) -> bool { |     pub fn is_serialization(&self) -> bool { | ||||||
|         match self.inner.kind { |         match self.inner.kind { | ||||||
|             Kind::Json(_) | |             Kind::Json(_) | Kind::UrlEncoded(_) => true, | ||||||
|             Kind::UrlEncoded(_) => true, |  | ||||||
|             _ => false, |             _ => false, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -199,8 +190,7 @@ impl Error { | |||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn is_redirect(&self) -> bool { |     pub fn is_redirect(&self) -> bool { | ||||||
|         match self.inner.kind { |         match self.inner.kind { | ||||||
|             Kind::TooManyRedirects | |             Kind::TooManyRedirects | Kind::RedirectLoop => true, | ||||||
|             Kind::RedirectLoop => true, |  | ||||||
|             _ => false, |             _ => false, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -241,9 +231,7 @@ impl fmt::Debug for Error { | |||||||
|                 .field(url) |                 .field(url) | ||||||
|                 .finish() |                 .finish() | ||||||
|         } else { |         } else { | ||||||
|             f.debug_tuple("Error") |             f.debug_tuple("Error").field(&self.inner.kind).finish() | ||||||
|                 .field(&self.inner.kind) |  | ||||||
|                 .finish() |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -269,9 +257,7 @@ impl fmt::Display for Error { | |||||||
|             #[cfg(feature = "rustls-tls")] |             #[cfg(feature = "rustls-tls")] | ||||||
|             Kind::Rustls(ref e) => fmt::Display::fmt(e, f), |             Kind::Rustls(ref e) => fmt::Display::fmt(e, f), | ||||||
|             #[cfg(feature = "trust-dns")] |             #[cfg(feature = "trust-dns")] | ||||||
|             Kind::DnsSystemConf(ref e) => { |             Kind::DnsSystemConf(ref e) => write!(f, "failed to load DNS system conf: {}", e), | ||||||
|                 write!(f, "failed to load DNS system conf: {}", e) |  | ||||||
|             }, |  | ||||||
|             Kind::Io(ref e) => fmt::Display::fmt(e, f), |             Kind::Io(ref e) => fmt::Display::fmt(e, f), | ||||||
|             Kind::UrlEncoded(ref e) => fmt::Display::fmt(e, f), |             Kind::UrlEncoded(ref e) => fmt::Display::fmt(e, f), | ||||||
|             Kind::Json(ref e) => fmt::Display::fmt(e, f), |             Kind::Json(ref e) => fmt::Display::fmt(e, f), | ||||||
| @@ -349,13 +335,13 @@ impl StdError for Error { | |||||||
|             Kind::Io(ref e) => e.cause(), |             Kind::Io(ref e) => e.cause(), | ||||||
|             Kind::UrlEncoded(ref e) => e.cause(), |             Kind::UrlEncoded(ref e) => e.cause(), | ||||||
|             Kind::Json(ref e) => e.cause(), |             Kind::Json(ref e) => e.cause(), | ||||||
|             Kind::UrlBadScheme | |             Kind::UrlBadScheme | ||||||
|             Kind::TooManyRedirects | |             | Kind::TooManyRedirects | ||||||
|             Kind::RedirectLoop | |             | Kind::RedirectLoop | ||||||
|             Kind::Status(_) | |             | Kind::Status(_) | ||||||
|             Kind::UnknownProxyScheme | |             | Kind::UnknownProxyScheme | ||||||
|             Kind::Timer | |             | Kind::Timer | ||||||
|             Kind::BlockingClientInFutureContext => None, |             | Kind::BlockingClientInFutureContext => None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -376,13 +362,13 @@ impl StdError for Error { | |||||||
|             Kind::Io(ref e) => e.source(), |             Kind::Io(ref e) => e.source(), | ||||||
|             Kind::UrlEncoded(ref e) => e.source(), |             Kind::UrlEncoded(ref e) => e.source(), | ||||||
|             Kind::Json(ref e) => e.source(), |             Kind::Json(ref e) => e.source(), | ||||||
|             Kind::UrlBadScheme | |             Kind::UrlBadScheme | ||||||
|             Kind::TooManyRedirects | |             | Kind::TooManyRedirects | ||||||
|             Kind::RedirectLoop | |             | Kind::RedirectLoop | ||||||
|             Kind::Status(_) | |             | Kind::Status(_) | ||||||
|             Kind::UnknownProxyScheme | |             | Kind::UnknownProxyScheme | ||||||
|             Kind::Timer | |             | Kind::Timer | ||||||
|             Kind::BlockingClientInFutureContext => None, |             | Kind::BlockingClientInFutureContext => None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -413,7 +399,6 @@ pub(crate) enum Kind { | |||||||
|     BlockingClientInFutureContext, |     BlockingClientInFutureContext, | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| impl From<http::Error> for Kind { | impl From<http::Error> for Kind { | ||||||
|     #[inline] |     #[inline] | ||||||
|     fn from(err: http::Error) -> Kind { |     fn from(err: http::Error) -> Kind { | ||||||
| @@ -478,7 +463,9 @@ impl From<rustls::TLSError> for Kind { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl<T> From<crate::wait::Waited<T>> for Kind | impl<T> From<crate::wait::Waited<T>> for Kind | ||||||
| where T: Into<Kind> { | where | ||||||
|  |     T: Into<Kind>, | ||||||
|  | { | ||||||
|     fn from(err: crate::wait::Waited<T>) -> Kind { |     fn from(err: crate::wait::Waited<T>) -> Kind { | ||||||
|         match err { |         match err { | ||||||
|             crate::wait::Waited::TimedOut => io_timeout().into(), |             crate::wait::Waited::TimedOut => io_timeout().into(), | ||||||
| @@ -542,8 +529,7 @@ pub(crate) fn into_io(e: Error) -> io::Error { | |||||||
|  |  | ||||||
| pub(crate) fn from_io(e: io::Error) -> Error { | pub(crate) fn from_io(e: io::Error) -> Error { | ||||||
|     if e.get_ref().map(|r| r.is::<Error>()).unwrap_or(false) { |     if e.get_ref().map(|r| r.is::<Error>()).unwrap_or(false) { | ||||||
|         *e |         *e.into_inner() | ||||||
|             .into_inner() |  | ||||||
|             .expect("io::Error::get_ref was Some(_)") |             .expect("io::Error::get_ref was Some(_)") | ||||||
|             .downcast::<Error>() |             .downcast::<Error>() | ||||||
|             .expect("StdError::is() was true") |             .expect("StdError::is() was true") | ||||||
| @@ -552,28 +538,30 @@ pub(crate) fn from_io(e: io::Error) -> Error { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| macro_rules! try_ { | macro_rules! try_ { | ||||||
|     ($e:expr) => ( |     ($e:expr) => { | ||||||
|         match $e { |         match $e { | ||||||
|             Ok(v) => v, |             Ok(v) => v, | ||||||
|             Err(err) => { |             Err(err) => { | ||||||
|                 return Err(crate::error::from(err)); |                 return Err(crate::error::from(err)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     ); |     }; | ||||||
|     ($e:expr, $url:expr) => ( |     ($e:expr, $url:expr) => { | ||||||
|         match $e { |         match $e { | ||||||
|             Ok(v) => v, |             Ok(v) => v, | ||||||
|             Err(err) => { |             Err(err) => { | ||||||
|                 return Err(crate::Error::from(crate::error::InternalFrom(err, Some($url.clone())))); |                 return Err(crate::Error::from(crate::error::InternalFrom( | ||||||
|  |                     err, | ||||||
|  |                     Some($url.clone()), | ||||||
|  |                 ))); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     ) |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| macro_rules! try_io { | macro_rules! try_io { | ||||||
|     ($e:expr) => ( |     ($e:expr) => { | ||||||
|         match $e { |         match $e { | ||||||
|             Ok(v) => v, |             Ok(v) => v, | ||||||
|             Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => { |             Err(ref err) if err.kind() == std::io::ErrorKind::WouldBlock => { | ||||||
| @@ -583,7 +571,7 @@ macro_rules! try_io { | |||||||
|                 return Err(crate::error::from_io(err)); |                 return Err(crate::error::from_io(err)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     ) |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) fn loop_detected(url: Url) -> Error { | pub(crate) fn loop_detected(url: Url) -> Error { | ||||||
| @@ -658,7 +646,6 @@ mod tests { | |||||||
|         assert!(err.cause().is_none()); |         assert!(err.cause().is_none()); | ||||||
|         assert_eq!(err.to_string(), "root"); |         assert_eq!(err.to_string(), "root"); | ||||||
|  |  | ||||||
|  |  | ||||||
|         let root = std::io::Error::new(std::io::ErrorKind::Other, Chain(None::<Error>)); |         let root = std::io::Error::new(std::io::ErrorKind::Other, Chain(None::<Error>)); | ||||||
|         let link = Chain(Some(root)); |         let link = Chain(Some(root)); | ||||||
|         let io = std::io::Error::new(std::io::ErrorKind::Other, link); |         let io = std::io::Error::new(std::io::ErrorKind::Other, link); | ||||||
|   | |||||||
| @@ -27,8 +27,7 @@ impl PolyfillTryInto for Url { | |||||||
|  |  | ||||||
| impl<'a> PolyfillTryInto for &'a str { | impl<'a> PolyfillTryInto for &'a str { | ||||||
|     fn into_url(self) -> crate::Result<Url> { |     fn into_url(self) -> crate::Result<Url> { | ||||||
|         try_!(Url::parse(self)) |         try_!(Url::parse(self)).into_url() | ||||||
|             .into_url() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -39,7 +38,9 @@ impl<'a> PolyfillTryInto for &'a String { | |||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) fn expect_uri(url: &Url) -> hyper::Uri { | pub(crate) fn expect_uri(url: &Url) -> hyper::Uri { | ||||||
|     url.as_str().parse().expect("a parsed Url should always be a valid Uri") |     url.as_str() | ||||||
|  |         .parse() | ||||||
|  |         .expect("a parsed Url should always be a valid Uri") | ||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) fn try_uri(url: &Url) -> Option<hyper::Uri> { | pub(crate) fn try_uri(url: &Url) -> Option<hyper::Uri> { | ||||||
| @@ -52,9 +53,10 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn into_url_file_scheme() { |     fn into_url_file_scheme() { | ||||||
|         let err = "file:///etc/hosts" |         let err = "file:///etc/hosts".into_url().unwrap_err(); | ||||||
|             .into_url() |         assert_eq!( | ||||||
|             .unwrap_err(); |             err.to_string(), | ||||||
|         assert_eq!(err.to_string(), "file:///etc/hosts: URL scheme is not allowed"); |             "file:///etc/hosts: URL scheme is not allowed" | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -187,12 +187,12 @@ doctest!("../README.md"); | |||||||
| pub use hyper::header; | pub use hyper::header; | ||||||
| pub use hyper::Method; | pub use hyper::Method; | ||||||
| pub use hyper::{StatusCode, Version}; | pub use hyper::{StatusCode, Version}; | ||||||
| pub use url::Url; |  | ||||||
| pub use url::ParseError as UrlError; | pub use url::ParseError as UrlError; | ||||||
|  | pub use url::Url; | ||||||
|  |  | ||||||
|  | pub use self::body::Body; | ||||||
| pub use self::client::{Client, ClientBuilder}; | pub use self::client::{Client, ClientBuilder}; | ||||||
| pub use self::error::{Error, Result}; | pub use self::error::{Error, Result}; | ||||||
| pub use self::body::Body; |  | ||||||
| pub use self::into_url::IntoUrl; | pub use self::into_url::IntoUrl; | ||||||
| pub use self::proxy::Proxy; | pub use self::proxy::Proxy; | ||||||
| pub use self::redirect::{RedirectAction, RedirectAttempt, RedirectPolicy}; | pub use self::redirect::{RedirectAction, RedirectAttempt, RedirectPolicy}; | ||||||
| @@ -206,9 +206,9 @@ pub use self::tls::{Certificate, Identity}; | |||||||
| mod error; | mod error; | ||||||
|  |  | ||||||
| mod async_impl; | mod async_impl; | ||||||
| mod connect; |  | ||||||
| mod body; | mod body; | ||||||
| mod client; | mod client; | ||||||
|  | mod connect; | ||||||
| pub mod cookie; | pub mod cookie; | ||||||
| #[cfg(feature = "trust-dns")] | #[cfg(feature = "trust-dns")] | ||||||
| mod dns; | mod dns; | ||||||
| @@ -226,16 +226,8 @@ pub mod multipart; | |||||||
| /// An 'async' implementation of the reqwest `Client`. | /// An 'async' implementation of the reqwest `Client`. | ||||||
| pub mod r#async { | pub mod r#async { | ||||||
|     pub use crate::async_impl::{ |     pub use crate::async_impl::{ | ||||||
|         Body, |         multipart, Body, Chunk, Client, ClientBuilder, Decoder, Request, RequestBuilder, Response, | ||||||
|         Chunk, |  | ||||||
|         Decoder, |  | ||||||
|         Client, |  | ||||||
|         ClientBuilder, |  | ||||||
|         Request, |  | ||||||
|         RequestBuilder, |  | ||||||
|         Response, |  | ||||||
|         ResponseBuilderExt, |         ResponseBuilderExt, | ||||||
|         multipart |  | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -269,10 +261,7 @@ pub mod r#async { | |||||||
| /// - redirect loop was detected | /// - redirect loop was detected | ||||||
| /// - redirect limit was exhausted | /// - redirect limit was exhausted | ||||||
| pub fn get<T: IntoUrl>(url: T) -> crate::Result<Response> { | pub fn get<T: IntoUrl>(url: T) -> crate::Result<Response> { | ||||||
|     Client::builder() |     Client::builder().build()?.get(url).send() | ||||||
|         .build()? |  | ||||||
|         .get(url) |  | ||||||
|         .send() |  | ||||||
| } | } | ||||||
|  |  | ||||||
| fn _assert_impls() { | fn _assert_impls() { | ||||||
|   | |||||||
| @@ -45,7 +45,7 @@ use std::path::Path; | |||||||
| use mime_guess::{self, Mime}; | use mime_guess::{self, Mime}; | ||||||
|  |  | ||||||
| use crate::async_impl::multipart::{FormParts, PartMetadata, PartProps}; | use crate::async_impl::multipart::{FormParts, PartMetadata, PartProps}; | ||||||
| use crate::{Body}; | use crate::Body; | ||||||
|  |  | ||||||
| /// A multipart/form-data request. | /// A multipart/form-data request. | ||||||
| pub struct Form { | pub struct Form { | ||||||
| @@ -82,7 +82,8 @@ impl Form { | |||||||
|     ///     .text("password", "secret"); |     ///     .text("password", "secret"); | ||||||
|     /// ``` |     /// ``` | ||||||
|     pub fn text<T, U>(self, name: T, value: U) -> Form |     pub fn text<T, U>(self, name: T, value: U) -> Form | ||||||
|     where T: Into<Cow<'static, str>>, |     where | ||||||
|  |         T: Into<Cow<'static, str>>, | ||||||
|         U: Into<Cow<'static, str>>, |         U: Into<Cow<'static, str>>, | ||||||
|     { |     { | ||||||
|         self.part(name, Part::text(value)) |         self.part(name, Part::text(value)) | ||||||
| @@ -106,8 +107,9 @@ impl Form { | |||||||
|     /// |     /// | ||||||
|     /// Errors when the file cannot be opened. |     /// Errors when the file cannot be opened. | ||||||
|     pub fn file<T, U>(self, name: T, path: U) -> io::Result<Form> |     pub fn file<T, U>(self, name: T, path: U) -> io::Result<Form> | ||||||
|     where T: Into<Cow<'static, str>>, |     where | ||||||
|           U: AsRef<Path> |         T: Into<Cow<'static, str>>, | ||||||
|  |         U: AsRef<Path>, | ||||||
|     { |     { | ||||||
|         Ok(self.part(name, Part::file(path)?)) |         Ok(self.part(name, Part::file(path)?)) | ||||||
|     } |     } | ||||||
| @@ -162,11 +164,11 @@ impl fmt::Debug for Form { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| impl Part { | impl Part { | ||||||
|     /// Makes a text parameter. |     /// Makes a text parameter. | ||||||
|     pub fn text<T>(value: T) -> Part |     pub fn text<T>(value: T) -> Part | ||||||
|     where T: Into<Cow<'static, str>>, |     where | ||||||
|  |         T: Into<Cow<'static, str>>, | ||||||
|     { |     { | ||||||
|         let body = match value.into() { |         let body = match value.into() { | ||||||
|             Cow::Borrowed(slice) => Body::from(slice), |             Cow::Borrowed(slice) => Body::from(slice), | ||||||
| @@ -177,7 +179,8 @@ impl Part { | |||||||
|  |  | ||||||
|     /// Makes a new parameter from arbitrary bytes. |     /// Makes a new parameter from arbitrary bytes. | ||||||
|     pub fn bytes<T>(value: T) -> Part |     pub fn bytes<T>(value: T) -> Part | ||||||
|     where T: Into<Cow<'static, [u8]>> |     where | ||||||
|  |         T: Into<Cow<'static, [u8]>>, | ||||||
|     { |     { | ||||||
|         let body = match value.into() { |         let body = match value.into() { | ||||||
|             Cow::Borrowed(slice) => Body::from(slice), |             Cow::Borrowed(slice) => Body::from(slice), | ||||||
| @@ -207,16 +210,14 @@ impl Part { | |||||||
|     /// Errors when the file cannot be opened. |     /// Errors when the file cannot be opened. | ||||||
|     pub fn file<T: AsRef<Path>>(path: T) -> io::Result<Part> { |     pub fn file<T: AsRef<Path>>(path: T) -> io::Result<Part> { | ||||||
|         let path = path.as_ref(); |         let path = path.as_ref(); | ||||||
|         let file_name = path.file_name().and_then(|filename| { |         let file_name = path | ||||||
|             Some(filename.to_string_lossy().into_owned()) |             .file_name() | ||||||
|         }); |             .map(|filename| filename.to_string_lossy().into_owned()); | ||||||
|         let ext = path.extension() |  | ||||||
|             .and_then(|ext| ext.to_str()) |         let ext = path.extension().and_then(|ext| ext.to_str()).unwrap_or(""); | ||||||
|             .unwrap_or(""); |  | ||||||
|         let mime = mime_guess::from_ext(ext).first_or_octet_stream(); |         let mime = mime_guess::from_ext(ext).first_or_octet_stream(); | ||||||
|         let file = File::open(path)?; |         let file = File::open(path)?; | ||||||
|         let field = Part::new(Body::from(file)) |         let field = Part::new(Body::from(file)).mime(mime); | ||||||
|             .mime(mime); |  | ||||||
|  |  | ||||||
|         Ok(if let Some(file_name) = file_name { |         Ok(if let Some(file_name) = file_name { | ||||||
|             field.file_name(file_name) |             field.file_name(file_name) | ||||||
| @@ -287,9 +288,7 @@ pub(crate) struct Reader { | |||||||
|  |  | ||||||
| impl fmt::Debug for Reader { | impl fmt::Debug for Reader { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         f.debug_struct("Reader") |         f.debug_struct("Reader").field("form", &self.form).finish() | ||||||
|             .field("form", &self.form) |  | ||||||
|             .finish() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -313,7 +312,10 @@ impl Reader { | |||||||
|                 let mut h = if !self.form.inner.computed_headers.is_empty() { |                 let mut h = if !self.form.inner.computed_headers.is_empty() { | ||||||
|                     self.form.inner.computed_headers.remove(0) |                     self.form.inner.computed_headers.remove(0) | ||||||
|                 } else { |                 } else { | ||||||
|                     self.form.inner.percent_encoding.encode_headers(&name, field.metadata()) |                     self.form | ||||||
|  |                         .inner | ||||||
|  |                         .percent_encoding | ||||||
|  |                         .encode_headers(&name, field.metadata()) | ||||||
|                 }; |                 }; | ||||||
|                 h.extend_from_slice(b"\r\n\r\n"); |                 h.extend_from_slice(b"\r\n\r\n"); | ||||||
|                 h |                 h | ||||||
| @@ -327,9 +329,10 @@ impl Reader { | |||||||
|             if !self.form.inner.fields.is_empty() { |             if !self.form.inner.fields.is_empty() { | ||||||
|                 Some(Box::new(reader)) |                 Some(Box::new(reader)) | ||||||
|             } else { |             } else { | ||||||
|                 Some(Box::new(reader.chain(Cursor::new( |                 Some(Box::new(reader.chain(Cursor::new(format!( | ||||||
|                     format!("--{}--\r\n", self.form.boundary()), |                     "--{}--\r\n", | ||||||
|                 )))) |                     self.form.boundary() | ||||||
|  |                 ))))) | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             None |             None | ||||||
| @@ -379,18 +382,13 @@ mod tests { | |||||||
|         let mut form = Form::new() |         let mut form = Form::new() | ||||||
|             .part("reader1", Part::reader(std::io::empty())) |             .part("reader1", Part::reader(std::io::empty())) | ||||||
|             .part("key1", Part::text("value1")) |             .part("key1", Part::text("value1")) | ||||||
|             .part( |             .part("key2", Part::text("value2").mime(mime::IMAGE_BMP)) | ||||||
|                 "key2", |  | ||||||
|                 Part::text("value2").mime(mime::IMAGE_BMP), |  | ||||||
|             ) |  | ||||||
|             .part("reader2", Part::reader(std::io::empty())) |             .part("reader2", Part::reader(std::io::empty())) | ||||||
|             .part( |             .part("key3", Part::text("value3").file_name("filename")); | ||||||
|                 "key3", |  | ||||||
|                 Part::text("value3").file_name("filename"), |  | ||||||
|             ); |  | ||||||
|         form.inner.boundary = "boundary".to_string(); |         form.inner.boundary = "boundary".to_string(); | ||||||
|         let length = form.compute_length(); |         let length = form.compute_length(); | ||||||
|         let expected = "--boundary\r\n\ |         let expected = | ||||||
|  |             "--boundary\r\n\ | ||||||
|              Content-Disposition: form-data; name=\"reader1\"\r\n\r\n\ |              Content-Disposition: form-data; name=\"reader1\"\r\n\r\n\ | ||||||
|              \r\n\ |              \r\n\ | ||||||
|              --boundary\r\n\ |              --boundary\r\n\ | ||||||
| @@ -422,17 +420,12 @@ mod tests { | |||||||
|         let mut output = Vec::new(); |         let mut output = Vec::new(); | ||||||
|         let mut form = Form::new() |         let mut form = Form::new() | ||||||
|             .text("key1", "value1") |             .text("key1", "value1") | ||||||
|             .part( |             .part("key2", Part::text("value2").mime(mime::IMAGE_BMP)) | ||||||
|                 "key2", |             .part("key3", Part::text("value3").file_name("filename")); | ||||||
|                 Part::text("value2").mime(mime::IMAGE_BMP), |  | ||||||
|             ) |  | ||||||
|             .part( |  | ||||||
|                 "key3", |  | ||||||
|                 Part::text("value3").file_name("filename"), |  | ||||||
|             ); |  | ||||||
|         form.inner.boundary = "boundary".to_string(); |         form.inner.boundary = "boundary".to_string(); | ||||||
|         let length = form.compute_length(); |         let length = form.compute_length(); | ||||||
|         let expected = "--boundary\r\n\ |         let expected = | ||||||
|  |             "--boundary\r\n\ | ||||||
|              Content-Disposition: form-data; name=\"key1\"\r\n\r\n\ |              Content-Disposition: form-data; name=\"key1\"\r\n\r\n\ | ||||||
|              value1\r\n\ |              value1\r\n\ | ||||||
|              --boundary\r\n\ |              --boundary\r\n\ | ||||||
|   | |||||||
							
								
								
									
										113
									
								
								src/proxy.rs
									
									
									
									
									
								
							
							
						
						
									
										113
									
								
								src/proxy.rs
									
									
									
									
									
								
							| @@ -1,16 +1,16 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::sync::Arc; |  | ||||||
| #[cfg(feature = "socks")] | #[cfg(feature = "socks")] | ||||||
| use std::net::{SocketAddr, ToSocketAddrs}; | use std::net::{SocketAddr, ToSocketAddrs}; | ||||||
|  | use std::sync::Arc; | ||||||
|  |  | ||||||
|  | use crate::{IntoUrl, Url}; | ||||||
| use http::{header::HeaderValue, Uri}; | use http::{header::HeaderValue, Uri}; | ||||||
| use hyper::client::connect::Destination; | use hyper::client::connect::Destination; | ||||||
| use url::percent_encoding::percent_decode; |  | ||||||
| use crate::{IntoUrl, Url}; |  | ||||||
| use std::collections::HashMap; | use std::collections::HashMap; | ||||||
| use std::env; | use std::env; | ||||||
| #[cfg(target_os = "windows")] | #[cfg(target_os = "windows")] | ||||||
| use std::error::Error; | use std::error::Error; | ||||||
|  | use url::percent_encoding::percent_decode; | ||||||
| #[cfg(target_os = "windows")] | #[cfg(target_os = "windows")] | ||||||
| use winreg::enums::HKEY_CURRENT_USER; | use winreg::enums::HKEY_CURRENT_USER; | ||||||
| #[cfg(target_os = "windows")] | #[cfg(target_os = "windows")] | ||||||
| @@ -98,7 +98,7 @@ impl Proxy { | |||||||
|     /// ``` |     /// ``` | ||||||
|     pub fn http<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy> { |     pub fn http<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy> { | ||||||
|         Ok(Proxy::new(Intercept::Http( |         Ok(Proxy::new(Intercept::Http( | ||||||
|             proxy_scheme.into_proxy_scheme()? |             proxy_scheme.into_proxy_scheme()?, | ||||||
|         ))) |         ))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -118,7 +118,7 @@ impl Proxy { | |||||||
|     /// ``` |     /// ``` | ||||||
|     pub fn https<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy> { |     pub fn https<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy> { | ||||||
|         Ok(Proxy::new(Intercept::Https( |         Ok(Proxy::new(Intercept::Https( | ||||||
|             proxy_scheme.into_proxy_scheme()? |             proxy_scheme.into_proxy_scheme()?, | ||||||
|         ))) |         ))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -138,7 +138,7 @@ impl Proxy { | |||||||
|     /// ``` |     /// ``` | ||||||
|     pub fn all<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy> { |     pub fn all<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy> { | ||||||
|         Ok(Proxy::new(Intercept::All( |         Ok(Proxy::new(Intercept::All( | ||||||
|             proxy_scheme.into_proxy_scheme()? |             proxy_scheme.into_proxy_scheme()?, | ||||||
|         ))) |         ))) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -163,12 +163,12 @@ impl Proxy { | |||||||
|     /// # } |     /// # } | ||||||
|     /// # fn main() {} |     /// # fn main() {} | ||||||
|     pub fn custom<F, U: IntoProxyScheme>(fun: F) -> Proxy |     pub fn custom<F, U: IntoProxyScheme>(fun: F) -> Proxy | ||||||
|     where F: Fn(&Url) -> Option<U> + Send + Sync + 'static { |     where | ||||||
|  |         F: Fn(&Url) -> Option<U> + Send + Sync + 'static, | ||||||
|  |     { | ||||||
|         Proxy::new(Intercept::Custom(Custom { |         Proxy::new(Intercept::Custom(Custom { | ||||||
|             auth: None, |             auth: None, | ||||||
|             func: Arc::new(move |url| { |             func: Arc::new(move |url| fun(url).map(IntoProxyScheme::into_proxy_scheme)), | ||||||
|                 fun(url).map(IntoProxyScheme::into_proxy_scheme) |  | ||||||
|             }), |  | ||||||
|         })) |         })) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -179,9 +179,7 @@ impl Proxy { | |||||||
|     */ |     */ | ||||||
|  |  | ||||||
|     fn new(intercept: Intercept) -> Proxy { |     fn new(intercept: Intercept) -> Proxy { | ||||||
|         Proxy { |         Proxy { intercept } | ||||||
|             intercept, |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Set the `Proxy-Authorization` header using Basic auth. |     /// Set the `Proxy-Authorization` header using Basic auth. | ||||||
| @@ -214,15 +212,13 @@ impl Proxy { | |||||||
|  |  | ||||||
|     pub(crate) fn http_basic_auth<D: Dst>(&self, uri: &D) -> Option<HeaderValue> { |     pub(crate) fn http_basic_auth<D: Dst>(&self, uri: &D) -> Option<HeaderValue> { | ||||||
|         match self.intercept { |         match self.intercept { | ||||||
|             Intercept::All(ProxyScheme::Http { ref auth, .. }) | |             Intercept::All(ProxyScheme::Http { ref auth, .. }) | ||||||
|             Intercept::Http(ProxyScheme::Http { ref auth, .. }) => auth.clone(), |             | Intercept::Http(ProxyScheme::Http { ref auth, .. }) => auth.clone(), | ||||||
|             Intercept::Custom(ref custom) => { |             Intercept::Custom(ref custom) => custom.call(uri).and_then(|scheme| match scheme { | ||||||
|                 custom.call(uri).and_then(|scheme| match scheme { |  | ||||||
|                 ProxyScheme::Http { auth, .. } => auth, |                 ProxyScheme::Http { auth, .. } => auth, | ||||||
|                 #[cfg(feature = "socks")] |                 #[cfg(feature = "socks")] | ||||||
|                 _ => None, |                 _ => None, | ||||||
|                 }) |             }), | ||||||
|             } |  | ||||||
|             _ => None, |             _ => None, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -236,14 +232,14 @@ impl Proxy { | |||||||
|                 } else { |                 } else { | ||||||
|                     None |                     None | ||||||
|                 } |                 } | ||||||
|             }, |             } | ||||||
|             Intercept::Https(ref u) => { |             Intercept::Https(ref u) => { | ||||||
|                 if uri.scheme() == "https" { |                 if uri.scheme() == "https" { | ||||||
|                     Some(u.clone()) |                     Some(u.clone()) | ||||||
|                 } else { |                 } else { | ||||||
|                     None |                     None | ||||||
|                 } |                 } | ||||||
|             }, |             } | ||||||
|             Intercept::Custom(ref custom) => custom.call(uri), |             Intercept::Custom(ref custom) => custom.call(uri), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -251,12 +247,8 @@ impl Proxy { | |||||||
|     pub(crate) fn is_match<D: Dst>(&self, uri: &D) -> bool { |     pub(crate) fn is_match<D: Dst>(&self, uri: &D) -> bool { | ||||||
|         match self.intercept { |         match self.intercept { | ||||||
|             Intercept::All(_) => true, |             Intercept::All(_) => true, | ||||||
|             Intercept::Http(_) => { |             Intercept::Http(_) => uri.scheme() == "http", | ||||||
|                 uri.scheme() == "http" |             Intercept::Https(_) => uri.scheme() == "https", | ||||||
|             }, |  | ||||||
|             Intercept::Https(_) => { |  | ||||||
|                 uri.scheme() == "https" |  | ||||||
|             }, |  | ||||||
|             Intercept::Custom(ref custom) => custom.call(uri).is_some(), |             Intercept::Custom(ref custom) => custom.call(uri).is_some(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -304,7 +296,11 @@ impl ProxyScheme { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Use a username and password when connecting to the proxy server |     /// Use a username and password when connecting to the proxy server | ||||||
|     fn with_basic_auth<T: Into<String>, U: Into<String>>(mut self, username: T, password: U) -> Self { |     fn with_basic_auth<T: Into<String>, U: Into<String>>( | ||||||
|  |         mut self, | ||||||
|  |         username: T, | ||||||
|  |         password: U, | ||||||
|  |     ) -> Self { | ||||||
|         self.set_basic_auth(username, password); |         self.set_basic_auth(username, password); | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
| @@ -314,7 +310,7 @@ impl ProxyScheme { | |||||||
|             ProxyScheme::Http { ref mut auth, .. } => { |             ProxyScheme::Http { ref mut auth, .. } => { | ||||||
|                 let header = encode_basic_auth(&username.into(), &password.into()); |                 let header = encode_basic_auth(&username.into(), &password.into()); | ||||||
|                 *auth = Some(header); |                 *auth = Some(header); | ||||||
|             }, |             } | ||||||
|             #[cfg(feature = "socks")] |             #[cfg(feature = "socks")] | ||||||
|             ProxyScheme::Socks5 { ref mut auth, .. } => { |             ProxyScheme::Socks5 { ref mut auth, .. } => { | ||||||
|                 *auth = Some((username.into(), password.into())); |                 *auth = Some((username.into(), password.into())); | ||||||
| @@ -332,12 +328,10 @@ impl ProxyScheme { | |||||||
|         let to_addr = || { |         let to_addr = || { | ||||||
|             let host_and_port = try_!(url.with_default_port(|url| match url.scheme() { |             let host_and_port = try_!(url.with_default_port(|url| match url.scheme() { | ||||||
|                 "socks5" | "socks5h" => Ok(1080), |                 "socks5" | "socks5h" => Ok(1080), | ||||||
|                 _ => Err(()) |                 _ => Err(()), | ||||||
|             })); |             })); | ||||||
|             let mut addr = try_!(host_and_port.to_socket_addrs()); |             let mut addr = try_!(host_and_port.to_socket_addrs()); | ||||||
|             addr |             addr.next().ok_or_else(crate::error::unknown_proxy_scheme) | ||||||
|                 .next() |  | ||||||
|                 .ok_or_else(crate::error::unknown_proxy_scheme) |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         let mut scheme = match url.scheme() { |         let mut scheme = match url.scheme() { | ||||||
| @@ -346,7 +340,7 @@ impl ProxyScheme { | |||||||
|             "socks5" => Self::socks5(to_addr()?)?, |             "socks5" => Self::socks5(to_addr()?)?, | ||||||
|             #[cfg(feature = "socks")] |             #[cfg(feature = "socks")] | ||||||
|             "socks5h" => Self::socks5h(to_addr()?)?, |             "socks5h" => Self::socks5h(to_addr()?)?, | ||||||
|             _ => return Err(crate::error::unknown_proxy_scheme()) |             _ => return Err(crate::error::unknown_proxy_scheme()), | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         if let Some(pwd) = url.password() { |         if let Some(pwd) = url.password() { | ||||||
| @@ -359,8 +353,6 @@ impl ProxyScheme { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| enum Intercept { | enum Intercept { | ||||||
|     All(ProxyScheme), |     All(ProxyScheme), | ||||||
| @@ -372,9 +364,9 @@ enum Intercept { | |||||||
| impl Intercept { | impl Intercept { | ||||||
|     fn set_basic_auth(&mut self, username: &str, password: &str) { |     fn set_basic_auth(&mut self, username: &str, password: &str) { | ||||||
|         match self { |         match self { | ||||||
|             Intercept::All(ref mut s) | |             Intercept::All(ref mut s) | ||||||
|             Intercept::Http(ref mut s) | |             | Intercept::Http(ref mut s) | ||||||
|             Intercept::Https(ref mut s) => s.set_basic_auth(username, password), |             | Intercept::Https(ref mut s) => s.set_basic_auth(username, password), | ||||||
|             Intercept::Custom(ref mut custom) => { |             Intercept::Custom(ref mut custom) => { | ||||||
|                 let header = encode_basic_auth(username, password); |                 let header = encode_basic_auth(username, password); | ||||||
|                 custom.auth = Some(header); |                 custom.auth = Some(header); | ||||||
| @@ -397,7 +389,8 @@ impl Custom { | |||||||
|             uri.scheme(), |             uri.scheme(), | ||||||
|             uri.host(), |             uri.host(), | ||||||
|             uri.port().map(|_| ":").unwrap_or(""), |             uri.port().map(|_| ":").unwrap_or(""), | ||||||
|             uri.port().map(|p| p.to_string()).unwrap_or_default()) |             uri.port().map(|p| p.to_string()).unwrap_or_default() | ||||||
|  |         ) | ||||||
|         .parse() |         .parse() | ||||||
|         .expect("should be valid Url"); |         .expect("should be valid Url"); | ||||||
|  |  | ||||||
| @@ -413,7 +406,7 @@ impl Custom { | |||||||
|                             uri, |                             uri, | ||||||
|                         } |                         } | ||||||
|                     } |                     } | ||||||
|                 }, |                 } | ||||||
|                 #[cfg(feature = "socks")] |                 #[cfg(feature = "socks")] | ||||||
|                 socks => socks, |                 socks => socks, | ||||||
|             }) |             }) | ||||||
| @@ -467,8 +460,7 @@ impl Dst for Uri { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn host(&self) -> &str { |     fn host(&self) -> &str { | ||||||
|         Uri::host(self) |         Uri::host(self).expect("<Uri as Dst>::host should have a str") | ||||||
|             .expect("<Uri as Dst>::host should have a str") |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn port(&self) -> Option<u16> { |     fn port(&self) -> Option<u16> { | ||||||
| @@ -499,8 +491,7 @@ pub fn get_proxies() -> HashMap<String, Url> { | |||||||
|     proxies |     proxies | ||||||
| } | } | ||||||
|  |  | ||||||
| fn insert_proxy(proxies: &mut HashMap<String, Url>, schema: String, addr: String) | fn insert_proxy(proxies: &mut HashMap<String, Url>, schema: String, addr: String) { | ||||||
| { |  | ||||||
|     if let Ok(valid_addr) = Url::parse(&addr) { |     if let Ok(valid_addr) = Url::parse(&addr) { | ||||||
|         proxies.insert(schema, valid_addr); |         proxies.insert(schema, valid_addr); | ||||||
|     } |     } | ||||||
| @@ -522,7 +513,6 @@ fn get_from_environment() -> HashMap<String, Url> { | |||||||
|     proxies |     proxies | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| #[cfg(target_os = "windows")] | #[cfg(target_os = "windows")] | ||||||
| fn get_from_registry_impl() -> Result<HashMap<String, Url>, Box<dyn Error>> { | fn get_from_registry_impl() -> Result<HashMap<String, Url>, Box<dyn Error>> { | ||||||
|     let hkcu = RegKey::predef(HKEY_CURRENT_USER); |     let hkcu = RegKey::predef(HKEY_CURRENT_USER); | ||||||
| @@ -543,7 +533,11 @@ fn get_from_registry_impl() -> Result<HashMap<String, Url>, Box<dyn Error>> { | |||||||
|             let protocol_parts: Vec<&str> = p.split("=").collect(); |             let protocol_parts: Vec<&str> = p.split("=").collect(); | ||||||
|             match protocol_parts.as_slice() { |             match protocol_parts.as_slice() { | ||||||
|                 [protocol, address] => { |                 [protocol, address] => { | ||||||
|                     insert_proxy(&mut proxies, String::from(*protocol), String::from(*address)); |                     insert_proxy( | ||||||
|  |                         &mut proxies, | ||||||
|  |                         String::from(*protocol), | ||||||
|  |                         String::from(*address), | ||||||
|  |                     ); | ||||||
|                 } |                 } | ||||||
|                 _ => { |                 _ => { | ||||||
|                     // Contains invalid protocol setting, just break the loop |                     // Contains invalid protocol setting, just break the loop | ||||||
| @@ -558,9 +552,21 @@ fn get_from_registry_impl() -> Result<HashMap<String, Url>, Box<dyn Error>> { | |||||||
|         if proxy_server.starts_with("http:") { |         if proxy_server.starts_with("http:") { | ||||||
|             insert_proxy(&mut proxies, String::from("http"), proxy_server); |             insert_proxy(&mut proxies, String::from("http"), proxy_server); | ||||||
|         } else { |         } else { | ||||||
|             insert_proxy(&mut proxies, String::from("http"), format!("http://{}", proxy_server)); |             insert_proxy( | ||||||
|             insert_proxy(&mut proxies, String::from("https"), format!("https://{}", proxy_server)); |                 &mut proxies, | ||||||
|             insert_proxy(&mut proxies, String::from("ftp"), format!("https://{}", proxy_server)); |                 String::from("http"), | ||||||
|  |                 format!("http://{}", proxy_server), | ||||||
|  |             ); | ||||||
|  |             insert_proxy( | ||||||
|  |                 &mut proxies, | ||||||
|  |                 String::from("https"), | ||||||
|  |                 format!("https://{}", proxy_server), | ||||||
|  |             ); | ||||||
|  |             insert_proxy( | ||||||
|  |                 &mut proxies, | ||||||
|  |                 String::from("ftp"), | ||||||
|  |                 format!("https://{}", proxy_server), | ||||||
|  |             ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     Ok(proxies) |     Ok(proxies) | ||||||
| @@ -581,8 +587,7 @@ mod tests { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         fn host(&self) -> &str { |         fn host(&self) -> &str { | ||||||
|             Url::host_str(self) |             Url::host_str(self).expect("<Url as Dst>::host should have a str") | ||||||
|                 .expect("<Url as Dst>::host should have a str") |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         fn port(&self) -> Option<u16> { |         fn port(&self) -> Option<u16> { | ||||||
| @@ -594,7 +599,6 @@ mod tests { | |||||||
|         s.parse().unwrap() |         s.parse().unwrap() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     fn intercepted_uri(p: &Proxy, s: &str) -> Uri { |     fn intercepted_uri(p: &Proxy, s: &str) -> Uri { | ||||||
|         match p.intercept(&url(s)).unwrap() { |         match p.intercept(&url(s)).unwrap() { | ||||||
|             ProxyScheme::Http { uri, .. } => uri, |             ProxyScheme::Http { uri, .. } => uri, | ||||||
| @@ -641,7 +645,6 @@ mod tests { | |||||||
|         assert_eq!(intercepted_uri(&p, other), target); |         assert_eq!(intercepted_uri(&p, other), target); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_custom() { |     fn test_custom() { | ||||||
|         let target1 = "http://example.domain/"; |         let target1 = "http://example.domain/"; | ||||||
| @@ -687,7 +690,7 @@ mod tests { | |||||||
|         // reset user setting. |         // reset user setting. | ||||||
|         match system_proxy { |         match system_proxy { | ||||||
|             Err(_) => env::remove_var("http_proxy"), |             Err(_) => env::remove_var("http_proxy"), | ||||||
|             Ok(proxy) => env::set_var("http_proxy", proxy) |             Ok(proxy) => env::set_var("http_proxy", proxy), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,13 +1,6 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
|  |  | ||||||
| use crate::header::{ | use crate::header::{HeaderMap, AUTHORIZATION, COOKIE, PROXY_AUTHORIZATION, WWW_AUTHENTICATE}; | ||||||
|     HeaderMap, |  | ||||||
|     AUTHORIZATION, |  | ||||||
|     COOKIE, |  | ||||||
|     PROXY_AUTHORIZATION, |  | ||||||
|     WWW_AUTHENTICATE, |  | ||||||
|  |  | ||||||
| }; |  | ||||||
| use hyper::StatusCode; | use hyper::StatusCode; | ||||||
|  |  | ||||||
| use crate::Url; | use crate::Url; | ||||||
| @@ -141,14 +134,8 @@ impl RedirectPolicy { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub(crate) fn check( |     pub(crate) fn check(&self, status: StatusCode, next: &Url, previous: &[Url]) -> Action { | ||||||
|         &self, |         self.redirect(RedirectAttempt { | ||||||
|         status: StatusCode, |  | ||||||
|         next: &Url, |  | ||||||
|         previous: &[Url], |  | ||||||
|     ) -> Action { |  | ||||||
|         self |  | ||||||
|             .redirect(RedirectAttempt { |  | ||||||
|             status, |             status, | ||||||
|             next, |             next, | ||||||
|             previous, |             previous, | ||||||
| @@ -239,11 +226,10 @@ pub(crate) enum Action { | |||||||
|     TooManyRedirects, |     TooManyRedirects, | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| pub(crate) fn remove_sensitive_headers(headers: &mut HeaderMap, next: &Url, previous: &[Url]) { | pub(crate) fn remove_sensitive_headers(headers: &mut HeaderMap, next: &Url, previous: &[Url]) { | ||||||
|     if let Some(previous) = previous.last() { |     if let Some(previous) = previous.last() { | ||||||
|         let cross_host = next.host_str() != previous.host_str() || |         let cross_host = next.host_str() != previous.host_str() | ||||||
|                          next.port_or_known_default() != previous.port_or_known_default(); |             || next.port_or_known_default() != previous.port_or_known_default(); | ||||||
|         if cross_host { |         if cross_host { | ||||||
|             headers.remove(AUTHORIZATION); |             headers.remove(AUTHORIZATION); | ||||||
|             headers.remove(COOKIE); |             headers.remove(COOKIE); | ||||||
| @@ -304,21 +290,15 @@ fn test_redirect_policy_custom() { | |||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     let next = Url::parse("http://bar/baz").unwrap(); |     let next = Url::parse("http://bar/baz").unwrap(); | ||||||
|     assert_eq!( |     assert_eq!(policy.check(StatusCode::FOUND, &next, &[]), Action::Follow); | ||||||
|         policy.check(StatusCode::FOUND, &next, &[]), |  | ||||||
|         Action::Follow |  | ||||||
|     ); |  | ||||||
|  |  | ||||||
|     let next = Url::parse("http://foo/baz").unwrap(); |     let next = Url::parse("http://foo/baz").unwrap(); | ||||||
|     assert_eq!( |     assert_eq!(policy.check(StatusCode::FOUND, &next, &[]), Action::Stop); | ||||||
|         policy.check(StatusCode::FOUND, &next, &[]), |  | ||||||
|         Action::Stop |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_remove_sensitive_headers() { | fn test_remove_sensitive_headers() { | ||||||
|     use hyper::header::{ACCEPT, AUTHORIZATION, COOKIE, HeaderValue}; |     use hyper::header::{HeaderValue, ACCEPT, AUTHORIZATION, COOKIE}; | ||||||
|  |  | ||||||
|     let mut headers = HeaderMap::new(); |     let mut headers = HeaderMap::new(); | ||||||
|     headers.insert(ACCEPT, HeaderValue::from_static("*/*")); |     headers.insert(ACCEPT, HeaderValue::from_static("*/*")); | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ use serde_urlencoded; | |||||||
|  |  | ||||||
| use crate::body::{self, Body}; | use crate::body::{self, Body}; | ||||||
| use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE}; | use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE}; | ||||||
| use http::HttpTryFrom; |  | ||||||
| use crate::{async_impl, Client, Method, Url}; | use crate::{async_impl, Client, Method, Url}; | ||||||
|  | use http::HttpTryFrom; | ||||||
|  |  | ||||||
| /// A request which can be executed with `Client::execute()`. | /// A request which can be executed with `Client::execute()`. | ||||||
| pub struct Request { | pub struct Request { | ||||||
| @@ -119,10 +119,7 @@ impl Request { | |||||||
|  |  | ||||||
| impl RequestBuilder { | impl RequestBuilder { | ||||||
|     pub(crate) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder { |     pub(crate) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder { | ||||||
|         RequestBuilder { |         RequestBuilder { client, request } | ||||||
|             client, |  | ||||||
|             request, |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Add a `Header` to this Request. |     /// Add a `Header` to this Request. | ||||||
| @@ -146,11 +143,11 @@ impl RequestBuilder { | |||||||
|         let mut error = None; |         let mut error = None; | ||||||
|         if let Ok(ref mut req) = self.request { |         if let Ok(ref mut req) = self.request { | ||||||
|             match <HeaderName as HttpTryFrom<K>>::try_from(key) { |             match <HeaderName as HttpTryFrom<K>>::try_from(key) { | ||||||
|                 Ok(key) => { |                 Ok(key) => match <HeaderValue as HttpTryFrom<V>>::try_from(value) { | ||||||
|                     match <HeaderValue as HttpTryFrom<V>>::try_from(value) { |                     Ok(value) => { | ||||||
|                         Ok(value) => { req.headers_mut().append(key, value); } |                         req.headers_mut().append(key, value); | ||||||
|                         Err(e) => error = Some(crate::error::from(e.into())), |  | ||||||
|                     } |                     } | ||||||
|  |                     Err(e) => error = Some(crate::error::from(e.into())), | ||||||
|                 }, |                 }, | ||||||
|                 Err(e) => error = Some(crate::error::from(e.into())), |                 Err(e) => error = Some(crate::error::from(e.into())), | ||||||
|             }; |             }; | ||||||
| @@ -236,7 +233,7 @@ impl RequestBuilder { | |||||||
|     { |     { | ||||||
|         let auth = match password { |         let auth = match password { | ||||||
|             Some(password) => format!("{}:{}", username, password), |             Some(password) => format!("{}:{}", username, password), | ||||||
|             None => format!("{}:", username) |             None => format!("{}:", username), | ||||||
|         }; |         }; | ||||||
|         let header_value = format!("Basic {}", encode(&auth)); |         let header_value = format!("Basic {}", encode(&auth)); | ||||||
|         self.header(crate::header::AUTHORIZATION, &*header_value) |         self.header(crate::header::AUTHORIZATION, &*header_value) | ||||||
| @@ -397,10 +394,10 @@ impl RequestBuilder { | |||||||
|                 Ok(body) => { |                 Ok(body) => { | ||||||
|                     req.headers_mut().insert( |                     req.headers_mut().insert( | ||||||
|                         CONTENT_TYPE, |                         CONTENT_TYPE, | ||||||
|                         HeaderValue::from_static("application/x-www-form-urlencoded") |                         HeaderValue::from_static("application/x-www-form-urlencoded"), | ||||||
|                     ); |                     ); | ||||||
|                     *req.body_mut() = Some(body.into()); |                     *req.body_mut() = Some(body.into()); | ||||||
|                 }, |                 } | ||||||
|                 Err(err) => error = Some(crate::error::from(err)), |                 Err(err) => error = Some(crate::error::from(err)), | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -440,12 +437,10 @@ impl RequestBuilder { | |||||||
|         if let Ok(ref mut req) = self.request { |         if let Ok(ref mut req) = self.request { | ||||||
|             match serde_json::to_vec(json) { |             match serde_json::to_vec(json) { | ||||||
|                 Ok(body) => { |                 Ok(body) => { | ||||||
|                     req.headers_mut().insert( |                     req.headers_mut() | ||||||
|                         CONTENT_TYPE, |                         .insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); | ||||||
|                         HeaderValue::from_static("application/json") |  | ||||||
|                     ); |  | ||||||
|                     *req.body_mut() = Some(body.into()); |                     *req.body_mut() = Some(body.into()); | ||||||
|                 }, |                 } | ||||||
|                 Err(err) => error = Some(crate::error::from(err)), |                 Err(err) => error = Some(crate::error::from(err)), | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -477,10 +472,7 @@ impl RequestBuilder { | |||||||
|     pub fn multipart(self, mut multipart: crate::multipart::Form) -> RequestBuilder { |     pub fn multipart(self, mut multipart: crate::multipart::Form) -> RequestBuilder { | ||||||
|         let mut builder = self.header( |         let mut builder = self.header( | ||||||
|             CONTENT_TYPE, |             CONTENT_TYPE, | ||||||
|             format!( |             format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(), | ||||||
|                 "multipart/form-data; boundary={}", |  | ||||||
|                 multipart.boundary() |  | ||||||
|             ).as_str() |  | ||||||
|         ); |         ); | ||||||
|         if let Ok(ref mut req) = builder.request { |         if let Ok(ref mut req) = builder.request { | ||||||
|             *req.body_mut() = Some(match multipart.compute_length() { |             *req.body_mut() = Some(match multipart.compute_length() { | ||||||
| @@ -552,26 +544,27 @@ impl RequestBuilder { | |||||||
|     /// # } |     /// # } | ||||||
|     /// ``` |     /// ``` | ||||||
|     pub fn try_clone(&self) -> Option<RequestBuilder> { |     pub fn try_clone(&self) -> Option<RequestBuilder> { | ||||||
|         self.request.as_ref() |         self.request | ||||||
|  |             .as_ref() | ||||||
|             .ok() |             .ok() | ||||||
|             .and_then(|req| req.try_clone()) |             .and_then(|req| req.try_clone()) | ||||||
|             .map(|req| { |             .map(|req| RequestBuilder { | ||||||
|                 RequestBuilder{ |  | ||||||
|                 client: self.client.clone(), |                 client: self.client.clone(), | ||||||
|                 request: Ok(req), |                 request: Ok(req), | ||||||
|                 } |  | ||||||
|             }) |             }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Debug for Request { | impl fmt::Debug for Request { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         fmt_request_fields(&mut f.debug_struct("Request"), self) |         fmt_request_fields(&mut f.debug_struct("Request"), self).finish() | ||||||
|             .finish() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn fmt_request_fields<'a, 'b>(f: &'a mut fmt::DebugStruct<'a, 'b>, req: &Request) -> &'a mut fmt::DebugStruct<'a, 'b> { | fn fmt_request_fields<'a, 'b>( | ||||||
|  |     f: &'a mut fmt::DebugStruct<'a, 'b>, | ||||||
|  |     req: &Request, | ||||||
|  | ) -> &'a mut fmt::DebugStruct<'a, 'b> { | ||||||
|     f.field("method", req.method()) |     f.field("method", req.method()) | ||||||
|         .field("url", req.url()) |         .field("url", req.url()) | ||||||
|         .field("headers", req.headers()) |         .field("headers", req.headers()) | ||||||
| @@ -579,12 +572,12 @@ fn fmt_request_fields<'a, 'b>(f: &'a mut fmt::DebugStruct<'a, 'b>, req: &Request | |||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|  |     use crate::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, HOST}; | ||||||
|     use crate::{body, Client, Method}; |     use crate::{body, Client, Method}; | ||||||
|     use crate::header::{ACCEPT, HOST, HeaderMap, HeaderValue, CONTENT_TYPE}; |  | ||||||
|     use std::collections::{BTreeMap, HashMap}; |  | ||||||
|     use serde::Serialize; |     use serde::Serialize; | ||||||
|     use serde_json; |     use serde_json; | ||||||
|     use serde_urlencoded; |     use serde_urlencoded; | ||||||
|  |     use std::collections::{BTreeMap, HashMap}; | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn basic_get_request() { |     fn basic_get_request() { | ||||||
| @@ -755,7 +748,10 @@ mod tests { | |||||||
|         let some_url = "https://google.com/"; |         let some_url = "https://google.com/"; | ||||||
|         let mut r = client.get(some_url); |         let mut r = client.get(some_url); | ||||||
|  |  | ||||||
|         let params = Params { foo: "bar".into(), qux: 3 }; |         let params = Params { | ||||||
|  |             foo: "bar".into(), | ||||||
|  |             qux: 3, | ||||||
|  |         }; | ||||||
|  |  | ||||||
|         r = r.query(¶ms); |         r = r.query(¶ms); | ||||||
|  |  | ||||||
| @@ -791,7 +787,10 @@ mod tests { | |||||||
|         let mut r = r.form(&form_data).build().unwrap(); |         let mut r = r.form(&form_data).build().unwrap(); | ||||||
|  |  | ||||||
|         // Make sure the content type was set |         // Make sure the content type was set | ||||||
|         assert_eq!(r.headers().get(CONTENT_TYPE).unwrap(), &"application/x-www-form-urlencoded"); |         assert_eq!( | ||||||
|  |             r.headers().get(CONTENT_TYPE).unwrap(), | ||||||
|  |             &"application/x-www-form-urlencoded" | ||||||
|  |         ); | ||||||
|  |  | ||||||
|         let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap(); |         let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap(); | ||||||
|  |  | ||||||
| @@ -821,12 +820,13 @@ mod tests { | |||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn add_json_fail() { |     fn add_json_fail() { | ||||||
|         use serde::{Serialize, Serializer}; |  | ||||||
|         use serde::ser::Error; |         use serde::ser::Error; | ||||||
|  |         use serde::{Serialize, Serializer}; | ||||||
|         struct MyStruct; |         struct MyStruct; | ||||||
|         impl Serialize for MyStruct { |         impl Serialize for MyStruct { | ||||||
|             fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error> |             fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error> | ||||||
|                 where S: Serializer |             where | ||||||
|  |                 S: Serializer, | ||||||
|             { |             { | ||||||
|                 Err(S::Error::custom("nope")) |                 Err(S::Error::custom("nope")) | ||||||
|             } |             } | ||||||
| @@ -858,11 +858,7 @@ mod tests { | |||||||
|  |  | ||||||
|         assert_eq!(req.headers()["im-a"], "keeper"); |         assert_eq!(req.headers()["im-a"], "keeper"); | ||||||
|  |  | ||||||
|         let foo = req |         let foo = req.headers().get_all("foo").iter().collect::<Vec<_>>(); | ||||||
|             .headers() |  | ||||||
|             .get_all("foo") |  | ||||||
|             .iter() |  | ||||||
|             .collect::<Vec<_>>(); |  | ||||||
|         assert_eq!(foo.len(), 2); |         assert_eq!(foo.len(), 2); | ||||||
|         assert_eq!(foo[0], "bar"); |         assert_eq!(foo[0], "bar"); | ||||||
|         assert_eq!(foo[1], "baz"); |         assert_eq!(foo[1], "baz"); | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| use std::mem; |  | ||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::io::{self, Read}; | use std::io::{self, Read}; | ||||||
|  | use std::mem; | ||||||
| use std::net::SocketAddr; | use std::net::SocketAddr; | ||||||
| use std::time::Duration; | use std::time::Duration; | ||||||
|  |  | ||||||
| @@ -8,10 +8,10 @@ use futures::{Async, Poll, Stream}; | |||||||
| use http; | use http; | ||||||
| use serde::de::DeserializeOwned; | use serde::de::DeserializeOwned; | ||||||
|  |  | ||||||
| use crate::cookie; |  | ||||||
| use crate::client::KeepCoreThreadAlive; | use crate::client::KeepCoreThreadAlive; | ||||||
|  | use crate::cookie; | ||||||
|  | use crate::{async_impl, wait, StatusCode, Url, Version}; | ||||||
| use hyper::header::HeaderMap; | use hyper::header::HeaderMap; | ||||||
| use crate::{async_impl, StatusCode, Url, Version, wait}; |  | ||||||
|  |  | ||||||
| /// A Response to a submitted `Request`. | /// A Response to a submitted `Request`. | ||||||
| pub struct Response { | pub struct Response { | ||||||
| @@ -28,7 +28,11 @@ impl fmt::Debug for Response { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl Response { | impl Response { | ||||||
|     pub(crate) fn new(res: async_impl::Response, timeout: Option<Duration>, thread: KeepCoreThreadAlive) -> Response { |     pub(crate) fn new( | ||||||
|  |         res: async_impl::Response, | ||||||
|  |         timeout: Option<Duration>, | ||||||
|  |         thread: KeepCoreThreadAlive, | ||||||
|  |     ) -> Response { | ||||||
|         Response { |         Response { | ||||||
|             inner: res, |             inner: res, | ||||||
|             body: None, |             body: None, | ||||||
| @@ -116,12 +120,10 @@ impl Response { | |||||||
|     /// Retrieve the cookies contained in the response. |     /// Retrieve the cookies contained in the response. | ||||||
|     /// |     /// | ||||||
|     /// Note that invalid 'Set-Cookie' headers will be ignored. |     /// Note that invalid 'Set-Cookie' headers will be ignored. | ||||||
|     pub fn cookies<'a>(&'a self) -> impl Iterator< Item = cookie::Cookie<'a> > + 'a { |     pub fn cookies<'a>(&'a self) -> impl Iterator<Item = cookie::Cookie<'a>> + 'a { | ||||||
|         cookie::extract_response_cookies(self.headers()) |         cookie::extract_response_cookies(self.headers()).filter_map(Result::ok) | ||||||
|             .filter_map(Result::ok) |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /// Get the HTTP `Version` of this `Response`. |     /// Get the HTTP `Version` of this `Response`. | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn version(&self) -> Version { |     pub fn version(&self) -> Version { | ||||||
| @@ -202,12 +204,10 @@ impl Response { | |||||||
|     /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html |     /// [`serde_json::from_reader`]: https://docs.serde.rs/serde_json/fn.from_reader.html | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn json<T: DeserializeOwned>(&mut self) -> crate::Result<T> { |     pub fn json<T: DeserializeOwned>(&mut self) -> crate::Result<T> { | ||||||
|         wait::timeout(self.inner.json(), self.timeout).map_err(|e| { |         wait::timeout(self.inner.json(), self.timeout).map_err(|e| match e { | ||||||
|             match e { |  | ||||||
|             wait::Waited::TimedOut => crate::error::timedout(None), |             wait::Waited::TimedOut => crate::error::timedout(None), | ||||||
|             wait::Waited::Executor(e) => crate::error::from(e), |             wait::Waited::Executor(e) => crate::error::from(e), | ||||||
|             wait::Waited::Inner(e) => e, |             wait::Waited::Inner(e) => e, | ||||||
|             } |  | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -291,7 +291,8 @@ impl Response { | |||||||
|     /// ``` |     /// ``` | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn copy_to<W: ?Sized>(&mut self, w: &mut W) -> crate::Result<u64> |     pub fn copy_to<W: ?Sized>(&mut self, w: &mut W) -> crate::Result<u64> | ||||||
|         where W: io::Write |     where | ||||||
|  |         W: io::Write, | ||||||
|     { |     { | ||||||
|         io::copy(self, w).map_err(crate::error::from) |         io::copy(self, w).map_err(crate::error::from) | ||||||
|     } |     } | ||||||
| @@ -314,14 +315,17 @@ impl Response { | |||||||
|     /// ``` |     /// ``` | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn error_for_status(self) -> crate::Result<Self> { |     pub fn error_for_status(self) -> crate::Result<Self> { | ||||||
|         let Response { body, inner, timeout, _thread_handle } = self; |         let Response { | ||||||
|         inner.error_for_status().map(move |inner| { |             body, | ||||||
|             Response { |             inner, | ||||||
|  |             timeout, | ||||||
|  |             _thread_handle, | ||||||
|  |         } = self; | ||||||
|  |         inner.error_for_status().map(move |inner| Response { | ||||||
|             inner, |             inner, | ||||||
|             body, |             body, | ||||||
|             timeout, |             timeout, | ||||||
|             _thread_handle, |             _thread_handle, | ||||||
|             } |  | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -353,7 +357,7 @@ impl Read for Response { | |||||||
|         if self.body.is_none() { |         if self.body.is_none() { | ||||||
|             let body = mem::replace(self.inner.body_mut(), async_impl::Decoder::empty()); |             let body = mem::replace(self.inner.body_mut(), async_impl::Decoder::empty()); | ||||||
|             let body = async_impl::ReadableChunks::new(WaitBody { |             let body = async_impl::ReadableChunks::new(WaitBody { | ||||||
|                 inner: wait::stream(body, self.timeout) |                 inner: wait::stream(body, self.timeout), | ||||||
|             }); |             }); | ||||||
|             self.body = Some(body); |             self.body = Some(body); | ||||||
|         } |         } | ||||||
| @@ -365,7 +369,7 @@ impl Read for Response { | |||||||
| } | } | ||||||
|  |  | ||||||
| struct WaitBody { | struct WaitBody { | ||||||
|     inner: wait::WaitStream<async_impl::Decoder> |     inner: wait::WaitStream<async_impl::Decoder>, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Stream for WaitBody { | impl Stream for WaitBody { | ||||||
| @@ -383,7 +387,7 @@ impl Stream for WaitBody { | |||||||
|                 }; |                 }; | ||||||
|  |  | ||||||
|                 Err(req_err) |                 Err(req_err) | ||||||
|             }, |             } | ||||||
|             None => Ok(Async::Ready(None)), |             None => Ok(Async::Ready(None)), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										81
									
								
								src/tls.rs
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								src/tls.rs
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| use std::fmt; |  | ||||||
| #[cfg(feature = "rustls-tls")] | #[cfg(feature = "rustls-tls")] | ||||||
| use rustls::{TLSError, ServerCertVerifier, RootCertStore, ServerCertVerified}; | use rustls::{RootCertStore, ServerCertVerified, ServerCertVerifier, TLSError}; | ||||||
|  | use std::fmt; | ||||||
| #[cfg(feature = "rustls-tls")] | #[cfg(feature = "rustls-tls")] | ||||||
| use tokio_rustls::webpki::DNSNameRef; | use tokio_rustls::webpki::DNSNameRef; | ||||||
|  |  | ||||||
| @@ -17,7 +17,7 @@ pub struct Certificate { | |||||||
| #[derive(Clone)] | #[derive(Clone)] | ||||||
| enum Cert { | enum Cert { | ||||||
|     Der(Vec<u8>), |     Der(Vec<u8>), | ||||||
|     Pem(Vec<u8>) |     Pem(Vec<u8>), | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Represent a private key and X509 cert as a client certificate. | /// Represent a private key and X509 cert as a client certificate. | ||||||
| @@ -32,7 +32,7 @@ enum ClientCert { | |||||||
|     Pem { |     Pem { | ||||||
|         key: rustls::PrivateKey, |         key: rustls::PrivateKey, | ||||||
|         certs: Vec<rustls::Certificate>, |         certs: Vec<rustls::Certificate>, | ||||||
|     } |     }, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Certificate { | impl Certificate { | ||||||
| @@ -61,7 +61,6 @@ impl Certificate { | |||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /// Create a `Certificate` from a PEM encoded certificate |     /// Create a `Certificate` from a PEM encoded certificate | ||||||
|     /// |     /// | ||||||
|     /// # Examples |     /// # Examples | ||||||
| @@ -83,36 +82,32 @@ impl Certificate { | |||||||
|             #[cfg(feature = "default-tls")] |             #[cfg(feature = "default-tls")] | ||||||
|             native: try_!(native_tls::Certificate::from_pem(pem)), |             native: try_!(native_tls::Certificate::from_pem(pem)), | ||||||
|             #[cfg(feature = "rustls-tls")] |             #[cfg(feature = "rustls-tls")] | ||||||
|             original: Cert::Pem(pem.to_owned()) |             original: Cert::Pem(pem.to_owned()), | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[cfg(feature = "default-tls")] |     #[cfg(feature = "default-tls")] | ||||||
|     pub(crate) fn add_to_native_tls( |     pub(crate) fn add_to_native_tls(self, tls: &mut native_tls::TlsConnectorBuilder) { | ||||||
|         self, |  | ||||||
|         tls: &mut native_tls::TlsConnectorBuilder, |  | ||||||
|     ) { |  | ||||||
|         tls.add_root_certificate(self.native); |         tls.add_root_certificate(self.native); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[cfg(feature = "rustls-tls")] |     #[cfg(feature = "rustls-tls")] | ||||||
|     pub(crate) fn add_to_rustls( |     pub(crate) fn add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()> { | ||||||
|         self, |  | ||||||
|         tls: &mut rustls::ClientConfig, |  | ||||||
|     ) -> crate::Result<()> { |  | ||||||
|         use std::io::Cursor; |  | ||||||
|         use rustls::internal::pemfile; |         use rustls::internal::pemfile; | ||||||
|  |         use std::io::Cursor; | ||||||
|  |  | ||||||
|         match self.original { |         match self.original { | ||||||
|             Cert::Der(buf) => try_!(tls.root_store.add(&::rustls::Certificate(buf)) |             Cert::Der(buf) => try_!(tls | ||||||
|  |                 .root_store | ||||||
|  |                 .add(&::rustls::Certificate(buf)) | ||||||
|                 .map_err(TLSError::WebPKIError)), |                 .map_err(TLSError::WebPKIError)), | ||||||
|             Cert::Pem(buf) => { |             Cert::Pem(buf) => { | ||||||
|                 let mut pem = Cursor::new(buf); |                 let mut pem = Cursor::new(buf); | ||||||
|                 let certs = try_!(pemfile::certs(&mut pem) |                 let certs = try_!(pemfile::certs(&mut pem).map_err(|_| TLSError::General( | ||||||
|                     .map_err(|_| TLSError::General(String::from("No valid certificate was found")))); |                     String::from("No valid certificate was found") | ||||||
|  |                 ))); | ||||||
|                 for c in certs { |                 for c in certs { | ||||||
|                     try_!(tls.root_store.add(&c) |                     try_!(tls.root_store.add(&c).map_err(TLSError::WebPKIError)); | ||||||
|                         .map_err(TLSError::WebPKIError)); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -151,9 +146,7 @@ impl Identity { | |||||||
|     #[cfg(feature = "default-tls")] |     #[cfg(feature = "default-tls")] | ||||||
|     pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> { |     pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> { | ||||||
|         Ok(Identity { |         Ok(Identity { | ||||||
|             inner: ClientCert::Pkcs12( |             inner: ClientCert::Pkcs12(try_!(native_tls::Identity::from_pkcs12(der, password))), | ||||||
|                 try_!(native_tls::Identity::from_pkcs12(der, password)) |  | ||||||
|             ), |  | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -178,8 +171,8 @@ impl Identity { | |||||||
|     /// ``` |     /// ``` | ||||||
|     #[cfg(feature = "rustls-tls")] |     #[cfg(feature = "rustls-tls")] | ||||||
|     pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> { |     pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> { | ||||||
|         use std::io::Cursor; |  | ||||||
|         use rustls::internal::pemfile; |         use rustls::internal::pemfile; | ||||||
|  |         use std::io::Cursor; | ||||||
|  |  | ||||||
|         let (key, certs) = { |         let (key, certs) = { | ||||||
|             let mut pem = Cursor::new(buf); |             let mut pem = Cursor::new(buf); | ||||||
| @@ -202,15 +195,14 @@ impl Identity { | |||||||
|             if let (Some(sk), false) = (sk.pop(), certs.is_empty()) { |             if let (Some(sk), false) = (sk.pop(), certs.is_empty()) { | ||||||
|                 (sk, certs) |                 (sk, certs) | ||||||
|             } else { |             } else { | ||||||
|                 return Err(crate::error::from(TLSError::General(String::from("private key or certificate not found")))); |                 return Err(crate::error::from(TLSError::General(String::from( | ||||||
|  |                     "private key or certificate not found", | ||||||
|  |                 )))); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         Ok(Identity { |         Ok(Identity { | ||||||
|             inner: ClientCert::Pem { |             inner: ClientCert::Pem { key, certs }, | ||||||
|                 key, |  | ||||||
|                 certs, |  | ||||||
|             }, |  | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -223,39 +215,34 @@ impl Identity { | |||||||
|             ClientCert::Pkcs12(id) => { |             ClientCert::Pkcs12(id) => { | ||||||
|                 tls.identity(id); |                 tls.identity(id); | ||||||
|                 Ok(()) |                 Ok(()) | ||||||
|             }, |             } | ||||||
|             #[cfg(feature = "rustls-tls")] |             #[cfg(feature = "rustls-tls")] | ||||||
|             ClientCert::Pem { .. } => Err(crate::error::from(crate::error::Kind::TlsIncompatible)) |             ClientCert::Pem { .. } => Err(crate::error::from(crate::error::Kind::TlsIncompatible)), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     #[cfg(feature = "rustls-tls")] |     #[cfg(feature = "rustls-tls")] | ||||||
|     pub(crate) fn add_to_rustls( |     pub(crate) fn add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()> { | ||||||
|         self, |  | ||||||
|         tls: &mut rustls::ClientConfig, |  | ||||||
|     ) -> crate::Result<()> { |  | ||||||
|         match self.inner { |         match self.inner { | ||||||
|             ClientCert::Pem { key, certs } => { |             ClientCert::Pem { key, certs } => { | ||||||
|                 tls.set_single_client_cert(certs, key); |                 tls.set_single_client_cert(certs, key); | ||||||
|                 Ok(()) |                 Ok(()) | ||||||
|             }, |             } | ||||||
|             #[cfg(feature = "default-tls")] |             #[cfg(feature = "default-tls")] | ||||||
|             ClientCert::Pkcs12(..) => Err(crate::error::from(crate::error::Kind::TlsIncompatible)) |             ClientCert::Pkcs12(..) => Err(crate::error::from(crate::error::Kind::TlsIncompatible)), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Debug for Certificate { | impl fmt::Debug for Certificate { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         f.debug_struct("Certificate") |         f.debug_struct("Certificate").finish() | ||||||
|             .finish() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Debug for Identity { | impl fmt::Debug for Identity { | ||||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
|         f.debug_struct("Identity") |         f.debug_struct("Identity").finish() | ||||||
|             .finish() |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -263,16 +250,20 @@ pub(crate) enum TlsBackend { | |||||||
|     #[cfg(feature = "default-tls")] |     #[cfg(feature = "default-tls")] | ||||||
|     Default, |     Default, | ||||||
|     #[cfg(feature = "rustls-tls")] |     #[cfg(feature = "rustls-tls")] | ||||||
|     Rustls |     Rustls, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Default for TlsBackend { | impl Default for TlsBackend { | ||||||
|     fn default() -> TlsBackend { |     fn default() -> TlsBackend { | ||||||
|         #[cfg(feature = "default-tls")] |         #[cfg(feature = "default-tls")] | ||||||
|         { TlsBackend::Default } |         { | ||||||
|  |             TlsBackend::Default | ||||||
|  |         } | ||||||
|  |  | ||||||
|         #[cfg(all(feature = "rustls-tls", not(feature = "default-tls")))] |         #[cfg(all(feature = "rustls-tls", not(feature = "default-tls")))] | ||||||
|         { TlsBackend::Rustls } |         { | ||||||
|  |             TlsBackend::Rustls | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -286,7 +277,7 @@ impl ServerCertVerifier for NoVerifier { | |||||||
|         _roots: &RootCertStore, |         _roots: &RootCertStore, | ||||||
|         _presented_certs: &[rustls::Certificate], |         _presented_certs: &[rustls::Certificate], | ||||||
|         _dns_name: DNSNameRef, |         _dns_name: DNSNameRef, | ||||||
|         _ocsp_response: &[u8] |         _ocsp_response: &[u8], | ||||||
|     ) -> Result<ServerCertVerified, TLSError> { |     ) -> Result<ServerCertVerified, TLSError> { | ||||||
|         Ok(ServerCertVerified::assertion()) |         Ok(ServerCertVerified::assertion()) | ||||||
|     } |     } | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								src/wait.rs
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/wait.rs
									
									
									
									
									
								
							| @@ -2,8 +2,8 @@ use std::sync::Arc; | |||||||
| use std::thread; | use std::thread; | ||||||
| use std::time::{Duration, Instant}; | use std::time::{Duration, Instant}; | ||||||
|  |  | ||||||
| use futures::{Async, Future, Poll, Stream}; |  | ||||||
| use futures::executor::{self, Notify}; | use futures::executor::{self, Notify}; | ||||||
|  | use futures::{Async, Future, Poll, Stream}; | ||||||
| use tokio_executor::{enter, EnterError}; | use tokio_executor::{enter, EnterError}; | ||||||
|  |  | ||||||
| pub(crate) fn timeout<F>(fut: F, timeout: Option<Duration>) -> Result<F::Item, Waited<F::Error>> | pub(crate) fn timeout<F>(fut: F, timeout: Option<Duration>) -> Result<F::Item, Waited<F::Error>> | ||||||
| @@ -11,13 +11,13 @@ where | |||||||
|     F: Future, |     F: Future, | ||||||
| { | { | ||||||
|     let mut spawn = executor::spawn(fut); |     let mut spawn = executor::spawn(fut); | ||||||
|     block_on(timeout, |notify| { |     block_on(timeout, |notify| spawn.poll_future_notify(notify, 0)) | ||||||
|         spawn.poll_future_notify(notify, 0) |  | ||||||
|     }) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| pub(crate) fn stream<S>(stream: S, timeout: Option<Duration>) -> WaitStream<S> | pub(crate) fn stream<S>(stream: S, timeout: Option<Duration>) -> WaitStream<S> | ||||||
| where S: Stream { | where | ||||||
|  |     S: Stream, | ||||||
|  | { | ||||||
|     WaitStream { |     WaitStream { | ||||||
|         stream: executor::spawn(stream), |         stream: executor::spawn(stream), | ||||||
|         timeout, |         timeout, | ||||||
| @@ -43,7 +43,9 @@ pub(crate) struct WaitStream<S> { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl<S> Iterator for WaitStream<S> | impl<S> Iterator for WaitStream<S> | ||||||
| where S: Stream { | where | ||||||
|  |     S: Stream, | ||||||
|  | { | ||||||
|     type Item = Result<S::Item, Waited<S::Error>>; |     type Item = Result<S::Item, Waited<S::Error>>; | ||||||
|  |  | ||||||
|     fn next(&mut self) -> Option<Self::Item> { |     fn next(&mut self) -> Option<Self::Item> { | ||||||
| @@ -74,9 +76,7 @@ where | |||||||
|     F: FnMut(&Arc<ThreadNotify>) -> Poll<U, E>, |     F: FnMut(&Arc<ThreadNotify>) -> Poll<U, E>, | ||||||
| { | { | ||||||
|     let _entered = enter().map_err(Waited::Executor)?; |     let _entered = enter().map_err(Waited::Executor)?; | ||||||
|     let deadline = timeout.map(|d| { |     let deadline = timeout.map(|d| Instant::now() + d); | ||||||
|         Instant::now() + d |  | ||||||
|     }); |  | ||||||
|     let notify = Arc::new(ThreadNotify { |     let notify = Arc::new(ThreadNotify { | ||||||
|         thread: thread::current(), |         thread: thread::current(), | ||||||
|     }); |     }); | ||||||
| @@ -99,5 +99,3 @@ where | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,10 +1,3 @@ | |||||||
| extern crate futures; |  | ||||||
| extern crate libflate; |  | ||||||
| extern crate reqwest; |  | ||||||
| extern crate hyper; |  | ||||||
| extern crate tokio; |  | ||||||
| extern crate bytes; |  | ||||||
|  |  | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod support; | mod support; | ||||||
|  |  | ||||||
| @@ -14,8 +7,8 @@ use std::time::Duration; | |||||||
| use futures::{Future, Stream}; | use futures::{Future, Stream}; | ||||||
| use tokio::runtime::current_thread::Runtime; | use tokio::runtime::current_thread::Runtime; | ||||||
|  |  | ||||||
| use reqwest::r#async::{Chunk, Client}; |  | ||||||
| use reqwest::r#async::multipart::{Form, Part}; | use reqwest::r#async::multipart::{Form, Part}; | ||||||
|  | use reqwest::r#async::{Chunk, Client}; | ||||||
|  |  | ||||||
| use bytes::Bytes; | use bytes::Bytes; | ||||||
|  |  | ||||||
| @@ -54,7 +47,8 @@ fn response_text() { | |||||||
|  |  | ||||||
|     let client = Client::new(); |     let client = Client::new(); | ||||||
|  |  | ||||||
|     let res_future = client.get(&format!("http://{}/text", server.addr())) |     let res_future = client | ||||||
|  |         .get(&format!("http://{}/text", server.addr())) | ||||||
|         .send() |         .send() | ||||||
|         .and_then(|mut res| res.text()) |         .and_then(|mut res| res.text()) | ||||||
|         .and_then(|text| { |         .and_then(|text| { | ||||||
| @@ -90,7 +84,8 @@ fn response_json() { | |||||||
|  |  | ||||||
|     let client = Client::new(); |     let client = Client::new(); | ||||||
|  |  | ||||||
|     let res_future = client.get(&format!("http://{}/json", server.addr())) |     let res_future = client | ||||||
|  |         .get(&format!("http://{}/json", server.addr())) | ||||||
|         .send() |         .send() | ||||||
|         .and_then(|mut res| res.json::<String>()) |         .and_then(|mut res| res.json::<String>()) | ||||||
|         .and_then(|text| { |         .and_then(|text| { | ||||||
| @@ -105,14 +100,14 @@ fn response_json() { | |||||||
| fn multipart() { | fn multipart() { | ||||||
|     let _ = env_logger::try_init(); |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|     let stream = futures::stream::once::<_, hyper::Error>(Ok(Chunk::from("part1 part2".to_owned()))); |     let stream = | ||||||
|  |         futures::stream::once::<_, hyper::Error>(Ok(Chunk::from("part1 part2".to_owned()))); | ||||||
|     let part = Part::stream(stream); |     let part = Part::stream(stream); | ||||||
|  |  | ||||||
|     let form = Form::new() |     let form = Form::new().text("foo", "bar").part("part_stream", part); | ||||||
|         .text("foo", "bar") |  | ||||||
|         .part("part_stream", part); |  | ||||||
|  |  | ||||||
|     let expected_body = format!("\ |     let expected_body = format!( | ||||||
|  |         "\ | ||||||
|          24\r\n\ |          24\r\n\ | ||||||
|          --{0}\r\n\r\n\ |          --{0}\r\n\r\n\ | ||||||
|          2E\r\n\ |          2E\r\n\ | ||||||
| @@ -132,7 +127,9 @@ fn multipart() { | |||||||
|          26\r\n\ |          26\r\n\ | ||||||
|          --{0}--\r\n\r\n\ |          --{0}--\r\n\r\n\ | ||||||
|          0\r\n\r\n\ |          0\r\n\r\n\ | ||||||
|     ", form.boundary()); |          ", | ||||||
|  |         form.boundary() | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: format!("\ |         request: format!("\ | ||||||
| @@ -160,10 +157,7 @@ fn multipart() { | |||||||
|  |  | ||||||
|     let client = Client::new(); |     let client = Client::new(); | ||||||
|  |  | ||||||
|     let res_future = client.post(&url) |     let res_future = client.post(&url).multipart(form).send().and_then(|res| { | ||||||
|         .multipart(form) |  | ||||||
|         .send() |  | ||||||
|         .and_then(|res| { |  | ||||||
|         assert_eq!(res.url().as_str(), &url); |         assert_eq!(res.url().as_str(), &url); | ||||||
|         assert_eq!(res.status(), reqwest::StatusCode::OK); |         assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|  |  | ||||||
| @@ -203,9 +197,7 @@ fn request_timeout() { | |||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|     let url = format!("http://{}/slow", server.addr()); |     let url = format!("http://{}/slow", server.addr()); | ||||||
|     let fut = client |     let fut = client.get(&url).send(); | ||||||
|         .get(&url) |  | ||||||
|         .send(); |  | ||||||
|  |  | ||||||
|     let err = rt.block_on(fut).unwrap_err(); |     let err = rt.block_on(fut).unwrap_err(); | ||||||
|  |  | ||||||
| @@ -254,7 +246,10 @@ fn response_timeout() { | |||||||
| } | } | ||||||
|  |  | ||||||
| fn gzip_case(response_size: usize, chunk_size: usize) { | fn gzip_case(response_size: usize, chunk_size: usize) { | ||||||
|     let content: String = (0..response_size).into_iter().map(|i| format!("test {}", i)).collect(); |     let content: String = (0..response_size) | ||||||
|  |         .into_iter() | ||||||
|  |         .map(|i| format!("test {}", i)) | ||||||
|  |         .collect(); | ||||||
|     let mut encoder = libflate::gzip::Encoder::new(Vec::new()).unwrap(); |     let mut encoder = libflate::gzip::Encoder::new(Vec::new()).unwrap(); | ||||||
|     match encoder.write(content.as_bytes()) { |     match encoder.write(content.as_bytes()) { | ||||||
|         Ok(n) => assert!(n > 0, "Failed to write to encoder."), |         Ok(n) => assert!(n > 0, "Failed to write to encoder."), | ||||||
| @@ -263,12 +258,15 @@ fn gzip_case(response_size: usize, chunk_size: usize) { | |||||||
|  |  | ||||||
|     let gzipped_content = encoder.finish().into_result().unwrap(); |     let gzipped_content = encoder.finish().into_result().unwrap(); | ||||||
|  |  | ||||||
|     let mut response = format!("\ |     let mut response = format!( | ||||||
|  |         "\ | ||||||
|          HTTP/1.1 200 OK\r\n\ |          HTTP/1.1 200 OK\r\n\ | ||||||
|          Server: test-accept\r\n\ |          Server: test-accept\r\n\ | ||||||
|          Content-Encoding: gzip\r\n\ |          Content-Encoding: gzip\r\n\ | ||||||
|          Content-Length: {}\r\n\ |          Content-Length: {}\r\n\ | ||||||
|             \r\n", &gzipped_content.len()) |          \r\n", | ||||||
|  |         &gzipped_content.len() | ||||||
|  |     ) | ||||||
|     .into_bytes(); |     .into_bytes(); | ||||||
|     response.extend(&gzipped_content); |     response.extend(&gzipped_content); | ||||||
|  |  | ||||||
| @@ -290,7 +288,8 @@ fn gzip_case(response_size: usize, chunk_size: usize) { | |||||||
|  |  | ||||||
|     let client = Client::new(); |     let client = Client::new(); | ||||||
|  |  | ||||||
|     let res_future = client.get(&format!("http://{}/gzip", server.addr())) |     let res_future = client | ||||||
|  |         .get(&format!("http://{}/gzip", server.addr())) | ||||||
|         .send() |         .send() | ||||||
|         .and_then(|res| { |         .and_then(|res| { | ||||||
|             let body = res.into_body(); |             let body = res.into_body(); | ||||||
| @@ -311,9 +310,11 @@ fn gzip_case(response_size: usize, chunk_size: usize) { | |||||||
| fn body_stream() { | fn body_stream() { | ||||||
|     let _ = env_logger::try_init(); |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|     let source: Box<dyn Stream<Item = Bytes, Error = io::Error> + Send> |     let source: Box<dyn Stream<Item = Bytes, Error = io::Error> + Send> = | ||||||
|         = Box::new(futures::stream::iter_ok::<_, io::Error>( |         Box::new(futures::stream::iter_ok::<_, io::Error>(vec![ | ||||||
|             vec![Bytes::from_static(b"123"), Bytes::from_static(b"4567")])); |             Bytes::from_static(b"123"), | ||||||
|  |             Bytes::from_static(b"4567"), | ||||||
|  |         ])); | ||||||
|  |  | ||||||
|     let expected_body = "3\r\n123\r\n4\r\n4567\r\n0\r\n\r\n"; |     let expected_body = "3\r\n123\r\n4\r\n4567\r\n0\r\n\r\n"; | ||||||
|  |  | ||||||
| @@ -342,10 +343,7 @@ fn body_stream() { | |||||||
|  |  | ||||||
|     let client = Client::new(); |     let client = Client::new(); | ||||||
|  |  | ||||||
|     let res_future = client.post(&url) |     let res_future = client.post(&url).body(source).send().and_then(|res| { | ||||||
|         .body(source) |  | ||||||
|         .send() |  | ||||||
|         .and_then(|res| { |  | ||||||
|         assert_eq!(res.url().as_str(), &url); |         assert_eq!(res.url().as_str(), &url); | ||||||
|         assert_eq!(res.status(), reqwest::StatusCode::OK); |         assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,11 +1,10 @@ | |||||||
| extern crate reqwest; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #[cfg(feature = "tls")] | #[cfg(feature = "tls")] | ||||||
| #[test] | #[test] | ||||||
| fn test_badssl_modern() { | fn test_badssl_modern() { | ||||||
|     let text = reqwest::get("https://mozilla-modern.badssl.com/").unwrap() |     let text = reqwest::get("https://mozilla-modern.badssl.com/") | ||||||
|         .text().unwrap(); |         .unwrap() | ||||||
|  |         .text() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     assert!(text.contains("<title>mozilla-modern.badssl.com</title>")); |     assert!(text.contains("<title>mozilla-modern.badssl.com</title>")); | ||||||
| } | } | ||||||
| @@ -15,10 +14,13 @@ fn test_badssl_modern() { | |||||||
| fn test_rustls_badssl_modern() { | fn test_rustls_badssl_modern() { | ||||||
|     let text = reqwest::Client::builder() |     let text = reqwest::Client::builder() | ||||||
|         .use_rustls_tls() |         .use_rustls_tls() | ||||||
|         .build().unwrap() |         .build() | ||||||
|  |         .unwrap() | ||||||
|         .get("https://mozilla-modern.badssl.com/") |         .get("https://mozilla-modern.badssl.com/") | ||||||
|         .send().unwrap() |         .send() | ||||||
|         .text().unwrap(); |         .unwrap() | ||||||
|  |         .text() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     assert!(text.contains("<title>mozilla-modern.badssl.com</title>")); |     assert!(text.contains("<title>mozilla-modern.badssl.com</title>")); | ||||||
| } | } | ||||||
| @@ -28,10 +30,13 @@ fn test_rustls_badssl_modern() { | |||||||
| fn test_badssl_self_signed() { | fn test_badssl_self_signed() { | ||||||
|     let text = reqwest::Client::builder() |     let text = reqwest::Client::builder() | ||||||
|         .danger_accept_invalid_certs(true) |         .danger_accept_invalid_certs(true) | ||||||
|         .build().unwrap() |         .build() | ||||||
|  |         .unwrap() | ||||||
|         .get("https://self-signed.badssl.com/") |         .get("https://self-signed.badssl.com/") | ||||||
|         .send().unwrap() |         .send() | ||||||
|         .text().unwrap(); |         .unwrap() | ||||||
|  |         .text() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     assert!(text.contains("<title>self-signed.badssl.com</title>")); |     assert!(text.contains("<title>self-signed.badssl.com</title>")); | ||||||
| } | } | ||||||
| @@ -41,17 +46,20 @@ fn test_badssl_self_signed() { | |||||||
| fn test_badssl_wrong_host() { | fn test_badssl_wrong_host() { | ||||||
|     let text = reqwest::Client::builder() |     let text = reqwest::Client::builder() | ||||||
|         .danger_accept_invalid_hostnames(true) |         .danger_accept_invalid_hostnames(true) | ||||||
|         .build().unwrap() |         .build() | ||||||
|  |         .unwrap() | ||||||
|         .get("https://wrong.host.badssl.com/") |         .get("https://wrong.host.badssl.com/") | ||||||
|         .send().unwrap() |         .send() | ||||||
|         .text().unwrap(); |         .unwrap() | ||||||
|  |         .text() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     assert!(text.contains("<title>wrong.host.badssl.com</title>")); |     assert!(text.contains("<title>wrong.host.badssl.com</title>")); | ||||||
|  |  | ||||||
|  |  | ||||||
|     let result = reqwest::Client::builder() |     let result = reqwest::Client::builder() | ||||||
|         .danger_accept_invalid_hostnames(true) |         .danger_accept_invalid_hostnames(true) | ||||||
|         .build().unwrap() |         .build() | ||||||
|  |         .unwrap() | ||||||
|         .get("https://self-signed.badssl.com/") |         .get("https://self-signed.badssl.com/") | ||||||
|         .send(); |         .send(); | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										105
									
								
								tests/client.rs
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								tests/client.rs
									
									
									
									
									
								
							| @@ -1,5 +1,3 @@ | |||||||
| extern crate reqwest; |  | ||||||
|  |  | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod support; | mod support; | ||||||
|  |  | ||||||
| @@ -30,7 +28,10 @@ fn test_response_text() { | |||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"5"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"5" | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     let body = res.text().unwrap(); |     let body = res.text().unwrap(); | ||||||
|     assert_eq!(b"Hello", body.as_bytes()); |     assert_eq!(b"Hello", body.as_bytes()); | ||||||
| @@ -62,7 +63,10 @@ fn test_response_non_utf_8_text() { | |||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"4"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"4" | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     let body = res.text().unwrap(); |     let body = res.text().unwrap(); | ||||||
|     assert_eq!("你好", &body); |     assert_eq!("你好", &body); | ||||||
| @@ -94,7 +98,10 @@ fn test_response_json() { | |||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"7"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"7" | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     let body = res.json::<String>().unwrap(); |     let body = res.json::<String>().unwrap(); | ||||||
|     assert_eq!("Hello", body); |     assert_eq!("Hello", body); | ||||||
| @@ -125,7 +132,10 @@ fn test_response_copy_to() { | |||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"5"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"5" | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     let mut buf: Vec<u8> = vec![]; |     let mut buf: Vec<u8> = vec![]; | ||||||
|     res.copy_to(&mut buf).unwrap(); |     res.copy_to(&mut buf).unwrap(); | ||||||
| @@ -157,7 +167,10 @@ fn test_get() { | |||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"0"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"0" | ||||||
|  |     ); | ||||||
|     assert_eq!(res.remote_addr(), Some(server.addr())); |     assert_eq!(res.remote_addr(), Some(server.addr())); | ||||||
|  |  | ||||||
|     let mut buf = [0; 1024]; |     let mut buf = [0; 1024]; | ||||||
| @@ -196,7 +209,10 @@ fn test_post() { | |||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"post"); |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"post"); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"0"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"0" | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     let mut buf = [0; 1024]; |     let mut buf = [0; 1024]; | ||||||
|     let n = res.read(&mut buf).unwrap(); |     let n = res.read(&mut buf).unwrap(); | ||||||
| @@ -293,7 +309,10 @@ fn test_error_for_status_5xx() { | |||||||
|  |  | ||||||
|     let err = res.error_for_status().err().unwrap(); |     let err = res.error_for_status().err().unwrap(); | ||||||
|     assert!(err.is_server_error()); |     assert!(err.is_server_error()); | ||||||
|     assert_eq!(err.status(), Some(reqwest::StatusCode::INTERNAL_SERVER_ERROR)); |     assert_eq!( | ||||||
|  |         err.status(), | ||||||
|  |         Some(reqwest::StatusCode::INTERNAL_SERVER_ERROR) | ||||||
|  |     ); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| @@ -303,7 +322,8 @@ fn test_default_headers() { | |||||||
|     headers.insert(header::COOKIE, header::HeaderValue::from_static("a=b;c=d")); |     headers.insert(header::COOKIE, header::HeaderValue::from_static("a=b;c=d")); | ||||||
|     let client = reqwest::Client::builder() |     let client = reqwest::Client::builder() | ||||||
|         .default_headers(headers) |         .default_headers(headers) | ||||||
|         .build().unwrap(); |         .build() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: b"\ |         request: b"\ | ||||||
| @@ -329,7 +349,10 @@ fn test_default_headers() { | |||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"0"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"0" | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: b"\ |         request: b"\ | ||||||
| @@ -355,17 +378,24 @@ fn test_default_headers() { | |||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"0"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"0" | ||||||
|  |     ); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_override_default_headers() { | fn test_override_default_headers() { | ||||||
|     use reqwest::header; |     use reqwest::header; | ||||||
|     let mut headers = header::HeaderMap::with_capacity(1); |     let mut headers = header::HeaderMap::with_capacity(1); | ||||||
|     headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("iamatoken")); |     headers.insert( | ||||||
|  |         header::AUTHORIZATION, | ||||||
|  |         header::HeaderValue::from_static("iamatoken"), | ||||||
|  |     ); | ||||||
|     let client = reqwest::Client::builder() |     let client = reqwest::Client::builder() | ||||||
|         .default_headers(headers) |         .default_headers(headers) | ||||||
|         .build().unwrap(); |         .build() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: b"\ |         request: b"\ | ||||||
| @@ -386,13 +416,22 @@ fn test_override_default_headers() { | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let url = format!("http://{}/3", server.addr()); |     let url = format!("http://{}/3", server.addr()); | ||||||
|     let res = client.get(&url).header(header::AUTHORIZATION, header::HeaderValue::from_static("secret")).send().unwrap(); |     let res = client | ||||||
|  |         .get(&url) | ||||||
|  |         .header( | ||||||
|  |             header::AUTHORIZATION, | ||||||
|  |             header::HeaderValue::from_static("secret"), | ||||||
|  |         ) | ||||||
|  |         .send() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"0"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"0" | ||||||
|  |     ); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| @@ -418,20 +457,32 @@ fn test_appended_headers_not_overwritten() { | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let url = format!("http://{}/4", server.addr()); |     let url = format!("http://{}/4", server.addr()); | ||||||
|     let res = client.get(&url).header(header::ACCEPT, "application/json").header(header::ACCEPT, "application/json+hal").send().unwrap(); |     let res = client | ||||||
|  |         .get(&url) | ||||||
|  |         .header(header::ACCEPT, "application/json") | ||||||
|  |         .header(header::ACCEPT, "application/json+hal") | ||||||
|  |         .send() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"0"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"0" | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     // make sure this also works with default headers |     // make sure this also works with default headers | ||||||
|     use reqwest::header; |     use reqwest::header; | ||||||
|     let mut headers = header::HeaderMap::with_capacity(1); |     let mut headers = header::HeaderMap::with_capacity(1); | ||||||
|     headers.insert(header::ACCEPT, header::HeaderValue::from_static("text/html")); |     headers.insert( | ||||||
|  |         header::ACCEPT, | ||||||
|  |         header::HeaderValue::from_static("text/html"), | ||||||
|  |     ); | ||||||
|     let client = reqwest::Client::builder() |     let client = reqwest::Client::builder() | ||||||
|         .default_headers(headers) |         .default_headers(headers) | ||||||
|         .build().unwrap(); |         .build() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: b"\ |         request: b"\ | ||||||
| @@ -452,10 +503,18 @@ fn test_appended_headers_not_overwritten() { | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let url = format!("http://{}/4", server.addr()); |     let url = format!("http://{}/4", server.addr()); | ||||||
|     let res = client.get(&url).header(header::ACCEPT, "application/json").header(header::ACCEPT, "application/json+hal").send().unwrap(); |     let res = client | ||||||
|  |         .get(&url) | ||||||
|  |         .header(header::ACCEPT, "application/json") | ||||||
|  |         .header(header::ACCEPT, "application/json+hal") | ||||||
|  |         .send() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); |     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test"); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"0"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"0" | ||||||
|  |     ); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| extern crate reqwest; |  | ||||||
|  |  | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod support; | mod support; | ||||||
|  |  | ||||||
| @@ -55,7 +53,10 @@ fn cookie_response_accessor() { | |||||||
|  |  | ||||||
|     // max-age |     // max-age | ||||||
|     assert_eq!(cookies[3].name(), "maxage"); |     assert_eq!(cookies[3].name(), "maxage"); | ||||||
|     assert_eq!(cookies[3].max_age().unwrap(), std::time::Duration::from_secs(100)); |     assert_eq!( | ||||||
|  |         cookies[3].max_age().unwrap(), | ||||||
|  |         std::time::Duration::from_secs(100) | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     // domain |     // domain | ||||||
|     assert_eq!(cookies[4].name(), "domain"); |     assert_eq!(cookies[4].name(), "domain"); | ||||||
| @@ -81,7 +82,10 @@ fn cookie_response_accessor() { | |||||||
| #[test] | #[test] | ||||||
| fn cookie_store_simple() { | fn cookie_store_simple() { | ||||||
|     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); | ||||||
|     let client = reqwest::r#async::Client::builder().cookie_store(true).build().unwrap(); |     let client = reqwest::r#async::Client::builder() | ||||||
|  |         .cookie_store(true) | ||||||
|  |         .build() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: b"\ |         request: b"\ | ||||||
| @@ -125,7 +129,10 @@ fn cookie_store_simple() { | |||||||
| #[test] | #[test] | ||||||
| fn cookie_store_overwrite_existing() { | fn cookie_store_overwrite_existing() { | ||||||
|     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); | ||||||
|     let client = reqwest::r#async::Client::builder().cookie_store(true).build().unwrap(); |     let client = reqwest::r#async::Client::builder() | ||||||
|  |         .cookie_store(true) | ||||||
|  |         .build() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: b"\ |         request: b"\ | ||||||
| @@ -189,7 +196,10 @@ fn cookie_store_overwrite_existing() { | |||||||
| #[test] | #[test] | ||||||
| fn cookie_store_max_age() { | fn cookie_store_max_age() { | ||||||
|     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); | ||||||
|     let client = reqwest::r#async::Client::builder().cookie_store(true).build().unwrap(); |     let client = reqwest::r#async::Client::builder() | ||||||
|  |         .cookie_store(true) | ||||||
|  |         .build() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: b"\ |         request: b"\ | ||||||
| @@ -232,7 +242,10 @@ fn cookie_store_max_age() { | |||||||
| #[test] | #[test] | ||||||
| fn cookie_store_expires() { | fn cookie_store_expires() { | ||||||
|     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); | ||||||
|     let client = reqwest::r#async::Client::builder().cookie_store(true).build().unwrap(); |     let client = reqwest::r#async::Client::builder() | ||||||
|  |         .cookie_store(true) | ||||||
|  |         .build() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: b"\ |         request: b"\ | ||||||
| @@ -275,7 +288,10 @@ fn cookie_store_expires() { | |||||||
| #[test] | #[test] | ||||||
| fn cookie_store_path() { | fn cookie_store_path() { | ||||||
|     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); |     let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt"); | ||||||
|     let client = reqwest::r#async::Client::builder().cookie_store(true).build().unwrap(); |     let client = reqwest::r#async::Client::builder() | ||||||
|  |         .cookie_store(true) | ||||||
|  |         .build() | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: b"\ |         request: b"\ | ||||||
|   | |||||||
| @@ -1,11 +1,8 @@ | |||||||
| extern crate reqwest; |  | ||||||
| extern crate libflate; |  | ||||||
|  |  | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod support; | mod support; | ||||||
|  |  | ||||||
| use std::time::Duration; |  | ||||||
| use std::io::{Read, Write}; | use std::io::{Read, Write}; | ||||||
|  | use std::time::Duration; | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_gzip_response() { | fn test_gzip_response() { | ||||||
| @@ -19,12 +16,15 @@ fn test_gzip_response() { | |||||||
|  |  | ||||||
|     let gzipped_content = encoder.finish().into_result().unwrap(); |     let gzipped_content = encoder.finish().into_result().unwrap(); | ||||||
|  |  | ||||||
|     let mut response = format!("\ |     let mut response = format!( | ||||||
|  |         "\ | ||||||
|          HTTP/1.1 200 OK\r\n\ |          HTTP/1.1 200 OK\r\n\ | ||||||
|          Server: test-accept\r\n\ |          Server: test-accept\r\n\ | ||||||
|          Content-Encoding: gzip\r\n\ |          Content-Encoding: gzip\r\n\ | ||||||
|          Content-Length: {}\r\n\ |          Content-Length: {}\r\n\ | ||||||
|             \r\n", &gzipped_content.len()) |          \r\n", | ||||||
|  |         &gzipped_content.len() | ||||||
|  |     ) | ||||||
|     .into_bytes(); |     .into_bytes(); | ||||||
|     response.extend(&gzipped_content); |     response.extend(&gzipped_content); | ||||||
|  |  | ||||||
| @@ -130,7 +130,10 @@ fn test_accept_header_is_not_changed_if_set() { | |||||||
|  |  | ||||||
|     let res = client |     let res = client | ||||||
|         .get(&format!("http://{}/accept", server.addr())) |         .get(&format!("http://{}/accept", server.addr())) | ||||||
|         .header(reqwest::header::ACCEPT, reqwest::header::HeaderValue::from_static("application/json")) |         .header( | ||||||
|  |             reqwest::header::ACCEPT, | ||||||
|  |             reqwest::header::HeaderValue::from_static("application/json"), | ||||||
|  |         ) | ||||||
|         .send() |         .send() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
| @@ -157,8 +160,12 @@ fn test_accept_encoding_header_is_not_changed_if_set() { | |||||||
|     }; |     }; | ||||||
|     let client = reqwest::Client::new(); |     let client = reqwest::Client::new(); | ||||||
|  |  | ||||||
|     let res = client.get(&format!("http://{}/accept-encoding", server.addr())) |     let res = client | ||||||
|         .header(reqwest::header::ACCEPT_ENCODING, reqwest::header::HeaderValue::from_static("identity")) |         .get(&format!("http://{}/accept-encoding", server.addr())) | ||||||
|  |         .header( | ||||||
|  |             reqwest::header::ACCEPT_ENCODING, | ||||||
|  |             reqwest::header::HeaderValue::from_static("identity"), | ||||||
|  |         ) | ||||||
|         .send() |         .send() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,3 @@ | |||||||
| extern crate env_logger; |  | ||||||
| extern crate reqwest; |  | ||||||
|  |  | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod support; | mod support; | ||||||
|  |  | ||||||
| @@ -8,15 +5,17 @@ mod support; | |||||||
| fn text_part() { | fn text_part() { | ||||||
|     let _ = env_logger::try_init(); |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|     let form = reqwest::multipart::Form::new() |     let form = reqwest::multipart::Form::new().text("foo", "bar"); | ||||||
|         .text("foo", "bar"); |  | ||||||
|  |  | ||||||
|     let expected_body = format!("\ |     let expected_body = format!( | ||||||
|  |         "\ | ||||||
|          --{0}\r\n\ |          --{0}\r\n\ | ||||||
|          Content-Disposition: form-data; name=\"foo\"\r\n\r\n\ |          Content-Disposition: form-data; name=\"foo\"\r\n\r\n\ | ||||||
|          bar\r\n\ |          bar\r\n\ | ||||||
|          --{0}--\r\n\ |          --{0}--\r\n\ | ||||||
|     ", form.boundary()); |          ", | ||||||
|  |         form.boundary() | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: format!("\ |         request: format!("\ | ||||||
| @@ -55,17 +54,22 @@ fn file() { | |||||||
|     let _ = env_logger::try_init(); |     let _ = env_logger::try_init(); | ||||||
|  |  | ||||||
|     let form = reqwest::multipart::Form::new() |     let form = reqwest::multipart::Form::new() | ||||||
|         .file("foo", "Cargo.lock").unwrap(); |         .file("foo", "Cargo.lock") | ||||||
|  |         .unwrap(); | ||||||
|  |  | ||||||
|     let fcontents = std::fs::read_to_string("Cargo.lock").unwrap(); |     let fcontents = std::fs::read_to_string("Cargo.lock").unwrap(); | ||||||
|  |  | ||||||
|     let expected_body = format!("\ |     let expected_body = format!( | ||||||
|  |         "\ | ||||||
|          --{0}\r\n\ |          --{0}\r\n\ | ||||||
|          Content-Disposition: form-data; name=\"foo\"; filename=\"Cargo.lock\"\r\n\ |          Content-Disposition: form-data; name=\"foo\"; filename=\"Cargo.lock\"\r\n\ | ||||||
|          Content-Type: application/octet-stream\r\n\r\n\ |          Content-Type: application/octet-stream\r\n\r\n\ | ||||||
|          {1}\r\n\ |          {1}\r\n\ | ||||||
|          --{0}--\r\n\ |          --{0}--\r\n\ | ||||||
|     ", form.boundary(), fcontents); |          ", | ||||||
|  |         form.boundary(), | ||||||
|  |         fcontents | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: format!("\ |         request: format!("\ | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| extern crate reqwest; |  | ||||||
|  |  | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod support; | mod support; | ||||||
|  |  | ||||||
| @@ -37,7 +35,10 @@ fn http_proxy() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"proxied"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::SERVER).unwrap(), | ||||||
|  |         &"proxied" | ||||||
|  |     ); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| @@ -67,7 +68,7 @@ fn http_proxy_basic_auth() { | |||||||
|         .proxy( |         .proxy( | ||||||
|             reqwest::Proxy::http(&proxy) |             reqwest::Proxy::http(&proxy) | ||||||
|                 .unwrap() |                 .unwrap() | ||||||
|             .basic_auth("Aladdin", "open sesame") |                 .basic_auth("Aladdin", "open sesame"), | ||||||
|         ) |         ) | ||||||
|         .build() |         .build() | ||||||
|         .unwrap() |         .unwrap() | ||||||
| @@ -77,7 +78,10 @@ fn http_proxy_basic_auth() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"proxied"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::SERVER).unwrap(), | ||||||
|  |         &"proxied" | ||||||
|  |     ); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| @@ -104,9 +108,7 @@ fn http_proxy_basic_auth_parsed() { | |||||||
|  |  | ||||||
|     let url = "http://hyper.rs/prox"; |     let url = "http://hyper.rs/prox"; | ||||||
|     let res = reqwest::Client::builder() |     let res = reqwest::Client::builder() | ||||||
|         .proxy( |         .proxy(reqwest::Proxy::http(&proxy).unwrap()) | ||||||
|             reqwest::Proxy::http(&proxy).unwrap() |  | ||||||
|         ) |  | ||||||
|         .build() |         .build() | ||||||
|         .unwrap() |         .unwrap() | ||||||
|         .get(url) |         .get(url) | ||||||
| @@ -115,7 +117,10 @@ fn http_proxy_basic_auth_parsed() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"proxied"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::SERVER).unwrap(), | ||||||
|  |         &"proxied" | ||||||
|  |     ); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| @@ -141,9 +146,7 @@ fn test_no_proxy() { | |||||||
|  |  | ||||||
|     // set up proxy and use no_proxy to clear up client builder proxies. |     // set up proxy and use no_proxy to clear up client builder proxies. | ||||||
|     let res = reqwest::Client::builder() |     let res = reqwest::Client::builder() | ||||||
|         .proxy( |         .proxy(reqwest::Proxy::http(&proxy).unwrap()) | ||||||
|             reqwest::Proxy::http(&proxy).unwrap() |  | ||||||
|         ) |  | ||||||
|         .no_proxy() |         .no_proxy() | ||||||
|         .build() |         .build() | ||||||
|         .unwrap() |         .unwrap() | ||||||
| @@ -189,11 +192,14 @@ fn test_using_system_proxy() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"proxied"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::SERVER).unwrap(), | ||||||
|  |         &"proxied" | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     // reset user setting. |     // reset user setting. | ||||||
|     match system_proxy { |     match system_proxy { | ||||||
|         Err(_) => env::remove_var("http_proxy"), |         Err(_) => env::remove_var("http_proxy"), | ||||||
|         Ok(proxy) => env::set_var("http_proxy", proxy) |         Ok(proxy) => env::set_var("http_proxy", proxy), | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,5 +1,3 @@ | |||||||
| extern crate reqwest; |  | ||||||
|  |  | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod support; | mod support; | ||||||
|  |  | ||||||
| @@ -47,12 +45,13 @@ fn test_redirect_301_and_302_and_303_changes_post_to_get() { | |||||||
|  |  | ||||||
|         let url = format!("http://{}/{}", redirect.addr(), code); |         let url = format!("http://{}/{}", redirect.addr(), code); | ||||||
|         let dst = format!("http://{}/{}", redirect.addr(), "dst"); |         let dst = format!("http://{}/{}", redirect.addr(), "dst"); | ||||||
|         let res = client.post(&url) |         let res = client.post(&url).send().unwrap(); | ||||||
|             .send() |  | ||||||
|             .unwrap(); |  | ||||||
|         assert_eq!(res.url().as_str(), dst); |         assert_eq!(res.url().as_str(), dst); | ||||||
|         assert_eq!(res.status(), reqwest::StatusCode::OK); |         assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|         assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test-dst"); |         assert_eq!( | ||||||
|  |             res.headers().get(reqwest::header::SERVER).unwrap(), | ||||||
|  |             &"test-dst" | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -99,12 +98,13 @@ fn test_redirect_307_and_308_tries_to_get_again() { | |||||||
|  |  | ||||||
|         let url = format!("http://{}/{}", redirect.addr(), code); |         let url = format!("http://{}/{}", redirect.addr(), code); | ||||||
|         let dst = format!("http://{}/{}", redirect.addr(), "dst"); |         let dst = format!("http://{}/{}", redirect.addr(), "dst"); | ||||||
|         let res = client.get(&url) |         let res = client.get(&url).send().unwrap(); | ||||||
|             .send() |  | ||||||
|             .unwrap(); |  | ||||||
|         assert_eq!(res.url().as_str(), dst); |         assert_eq!(res.url().as_str(), dst); | ||||||
|         assert_eq!(res.status(), reqwest::StatusCode::OK); |         assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|         assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test-dst"); |         assert_eq!( | ||||||
|  |             res.headers().get(reqwest::header::SERVER).unwrap(), | ||||||
|  |             &"test-dst" | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -155,13 +155,13 @@ fn test_redirect_307_and_308_tries_to_post_again() { | |||||||
|  |  | ||||||
|         let url = format!("http://{}/{}", redirect.addr(), code); |         let url = format!("http://{}/{}", redirect.addr(), code); | ||||||
|         let dst = format!("http://{}/{}", redirect.addr(), "dst"); |         let dst = format!("http://{}/{}", redirect.addr(), "dst"); | ||||||
|         let res = client.post(&url) |         let res = client.post(&url).body("Hello").send().unwrap(); | ||||||
|             .body("Hello") |  | ||||||
|             .send() |  | ||||||
|             .unwrap(); |  | ||||||
|         assert_eq!(res.url().as_str(), dst); |         assert_eq!(res.url().as_str(), dst); | ||||||
|         assert_eq!(res.status(), reqwest::StatusCode::OK); |         assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|         assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test-dst"); |         assert_eq!( | ||||||
|  |             res.headers().get(reqwest::header::SERVER).unwrap(), | ||||||
|  |             &"test-dst" | ||||||
|  |         ); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -204,8 +204,6 @@ fn test_redirect_307_does_not_try_if_reader_cannot_reset() { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_redirect_removes_sensitive_headers() { | fn test_redirect_removes_sensitive_headers() { | ||||||
|     let end_server = server! { |     let end_server = server! { | ||||||
| @@ -249,7 +247,10 @@ fn test_redirect_removes_sensitive_headers() { | |||||||
|         .build() |         .build() | ||||||
|         .unwrap() |         .unwrap() | ||||||
|         .get(&format!("http://{}/sensitive", mid_server.addr())) |         .get(&format!("http://{}/sensitive", mid_server.addr())) | ||||||
|         .header(reqwest::header::COOKIE, reqwest::header::HeaderValue::from_static("foo=bar")) |         .header( | ||||||
|  |             reqwest::header::COOKIE, | ||||||
|  |             reqwest::header::HeaderValue::from_static("foo=bar"), | ||||||
|  |         ) | ||||||
|         .send() |         .send() | ||||||
|         .unwrap(); |         .unwrap(); | ||||||
| } | } | ||||||
| @@ -310,7 +311,10 @@ fn test_redirect_policy_can_stop_redirects_without_an_error() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::FOUND); |     assert_eq!(res.status(), reqwest::StatusCode::FOUND); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test-dont"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::SERVER).unwrap(), | ||||||
|  |         &"test-dont" | ||||||
|  |     ); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| @@ -385,13 +389,19 @@ fn test_invalid_location_stops_redirect_gh484() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), url); |     assert_eq!(res.url().as_str(), url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::FOUND); |     assert_eq!(res.status(), reqwest::StatusCode::FOUND); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test-yikes"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::SERVER).unwrap(), | ||||||
|  |         &"test-yikes" | ||||||
|  |     ); | ||||||
| } | } | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_redirect_302_with_set_cookies() { | fn test_redirect_302_with_set_cookies() { | ||||||
|     let code = 302; |     let code = 302; | ||||||
|     let client = reqwest::ClientBuilder::new().cookie_store(true).build().unwrap(); |     let client = reqwest::ClientBuilder::new() | ||||||
|  |         .cookie_store(true) | ||||||
|  |         .build() | ||||||
|  |         .unwrap(); | ||||||
|     let server = server! { |     let server = server! { | ||||||
|         request: format!("\ |         request: format!("\ | ||||||
|             GET /{} HTTP/1.1\r\n\ |             GET /{} HTTP/1.1\r\n\ | ||||||
| @@ -432,11 +442,12 @@ fn test_redirect_302_with_set_cookies() { | |||||||
|  |  | ||||||
|     let url = format!("http://{}/{}", server.addr(), code); |     let url = format!("http://{}/{}", server.addr(), code); | ||||||
|     let dst = format!("http://{}/{}", server.addr(), "dst"); |     let dst = format!("http://{}/{}", server.addr(), "dst"); | ||||||
|     let res = client.get(&url) |     let res = client.get(&url).send().unwrap(); | ||||||
|         .send() |  | ||||||
|         .unwrap(); |  | ||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), dst); |     assert_eq!(res.url().as_str(), dst); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test-dst"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::SERVER).unwrap(), | ||||||
|  |         &"test-dst" | ||||||
|  |     ); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,9 +2,9 @@ | |||||||
|  |  | ||||||
| use std::io::{Read, Write}; | use std::io::{Read, Write}; | ||||||
| use std::net; | use std::net; | ||||||
| use std::time::Duration; |  | ||||||
| use std::sync::mpsc; | use std::sync::mpsc; | ||||||
| use std::thread; | use std::thread; | ||||||
|  | use std::time::Duration; | ||||||
|  |  | ||||||
| pub struct Server { | pub struct Server { | ||||||
|     addr: net::SocketAddr, |     addr: net::SocketAddr, | ||||||
| @@ -19,9 +19,8 @@ impl Server { | |||||||
|  |  | ||||||
| impl Drop for Server { | impl Drop for Server { | ||||||
|     fn drop(&mut self) { |     fn drop(&mut self) { | ||||||
|         if !::std::thread::panicking() { |         if !thread::panicking() { | ||||||
|             self |             self.panic_rx | ||||||
|                 .panic_rx |  | ||||||
|                 .recv_timeout(Duration::from_secs(3)) |                 .recv_timeout(Duration::from_secs(3)) | ||||||
|                 .expect("test server should not panic"); |                 .expect("test server should not panic"); | ||||||
|         } |         } | ||||||
| @@ -47,7 +46,10 @@ pub fn spawn(txns: Vec<Txn>) -> Server { | |||||||
|     let listener = net::TcpListener::bind("127.0.0.1:0").unwrap(); |     let listener = net::TcpListener::bind("127.0.0.1:0").unwrap(); | ||||||
|     let addr = listener.local_addr().unwrap(); |     let addr = listener.local_addr().unwrap(); | ||||||
|     let (panic_tx, panic_rx) = mpsc::channel(); |     let (panic_tx, panic_rx) = mpsc::channel(); | ||||||
|     let tname = format!("test({})-support-server", thread::current().name().unwrap_or("<unknown>")); |     let tname = format!( | ||||||
|  |         "test({})-support-server", | ||||||
|  |         thread::current().name().unwrap_or("<unknown>") | ||||||
|  |     ); | ||||||
|     thread::Builder::new().name(tname).spawn(move || { |     thread::Builder::new().name(tname).spawn(move || { | ||||||
|         'txns: for txn in txns { |         'txns: for txn in txns { | ||||||
|             let mut expected = txn.request; |             let mut expected = txn.request; | ||||||
| @@ -149,10 +151,7 @@ pub fn spawn(txns: Vec<Txn>) -> Server { | |||||||
|         let _ = panic_tx.send(()); |         let _ = panic_tx.send(()); | ||||||
|     }).expect("server thread spawn"); |     }).expect("server thread spawn"); | ||||||
|  |  | ||||||
|     Server { |     Server { addr, panic_rx } | ||||||
|         addr, |  | ||||||
|         panic_rx, |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
| fn replace_expected_vars(bytes: &mut Vec<u8>, host: &[u8], ua: &[u8]) { | fn replace_expected_vars(bytes: &mut Vec<u8>, host: &[u8], ua: &[u8]) { | ||||||
| @@ -210,16 +209,15 @@ macro_rules! __internal__txn { | |||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| #[macro_export] | #[macro_export] | ||||||
| macro_rules! __internal__prop { | macro_rules! __internal__prop { | ||||||
|     (request: $val:expr) => ( |     (request: $val:expr) => { | ||||||
|         From::from(&$val[..]) |         From::from(&$val[..]) | ||||||
|     ); |     }; | ||||||
|     (response: $val:expr) => ( |     (response: $val:expr) => { | ||||||
|         From::from(&$val[..]) |         From::from(&$val[..]) | ||||||
|     ); |     }; | ||||||
|     ($field:ident: $val:expr) => ( |     ($field:ident: $val:expr) => { | ||||||
|         From::from($val) |         From::from($val) | ||||||
|     ) |     }; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,3 @@ | |||||||
| extern crate env_logger; |  | ||||||
| extern crate reqwest; |  | ||||||
|  |  | ||||||
| #[macro_use] | #[macro_use] | ||||||
| mod support; | mod support; | ||||||
|  |  | ||||||
| @@ -38,10 +35,7 @@ fn timeout_closes_connection() { | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     let url = format!("http://{}/closes", server.addr()); |     let url = format!("http://{}/closes", server.addr()); | ||||||
|     let err = client |     let err = client.get(&url).send().unwrap_err(); | ||||||
|         .get(&url) |  | ||||||
|         .send() |  | ||||||
|         .unwrap_err(); |  | ||||||
|  |  | ||||||
|     assert_eq!(err.get_ref().unwrap().to_string(), "timed out"); |     assert_eq!(err.get_ref().unwrap().to_string(), "timed out"); | ||||||
|     assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); |     assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); | ||||||
| @@ -93,7 +87,6 @@ fn write_timeout_large_body() { | |||||||
|     assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); |     assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str())); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| #[test] | #[test] | ||||||
| fn test_response_timeout() { | fn test_response_timeout() { | ||||||
|     let _ = env_logger::try_init(); |     let _ = env_logger::try_init(); | ||||||
| @@ -158,7 +151,10 @@ fn test_read_timeout() { | |||||||
|  |  | ||||||
|     assert_eq!(res.url().as_str(), &url); |     assert_eq!(res.url().as_str(), &url); | ||||||
|     assert_eq!(res.status(), reqwest::StatusCode::OK); |     assert_eq!(res.status(), reqwest::StatusCode::OK); | ||||||
|     assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"5"); |     assert_eq!( | ||||||
|  |         res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), | ||||||
|  |         &"5" | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     let mut buf = [0; 1024]; |     let mut buf = [0; 1024]; | ||||||
|     let err = res.read(&mut buf).unwrap_err(); |     let err = res.read(&mut buf).unwrap_err(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user