committed by
					
						 Sean McArthur
						Sean McArthur
					
				
			
			
				
	
			
			
			
						parent
						
							7bd3619ece
						
					
				
				
					commit
					c417d6dab8
				
			| @@ -2,6 +2,7 @@ use std::fmt; | ||||
|  | ||||
| use futures::{Stream, Poll, Async}; | ||||
| use bytes::Bytes; | ||||
| use hyper::body::Payload; | ||||
|  | ||||
| /// An asynchronous `Stream`. | ||||
| pub struct Body { | ||||
| @@ -20,6 +21,13 @@ impl Body { | ||||
|             Inner::Reusable(_) => unreachable!(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn content_length(&self) -> Option<u64> { | ||||
|         match self.inner { | ||||
|             Inner::Reusable(ref bytes) => Some(bytes.len() as u64), | ||||
|             Inner::Hyper(ref body) => body.content_length(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Stream for Body { | ||||
|   | ||||
| @@ -4,11 +4,11 @@ use std::time::Duration; | ||||
|  | ||||
| use bytes::Bytes; | ||||
| use futures::{Async, Future, Poll}; | ||||
| use hyper::client::FutureResponse; | ||||
| use hyper::header::{Headers, Location, Referer, UserAgent, Accept, Encoding, | ||||
|                     AcceptEncoding, Range, qitem}; | ||||
| use hyper::client::ResponseFuture; | ||||
| use header::{HeaderMap, HeaderValue, LOCATION, USER_AGENT, REFERER, ACCEPT, | ||||
|              ACCEPT_ENCODING, RANGE}; | ||||
| use mime::{self}; | ||||
| use native_tls::{TlsConnector, TlsConnectorBuilder}; | ||||
| use tokio_core::reactor::Handle; | ||||
|  | ||||
|  | ||||
| use super::body; | ||||
| @@ -63,7 +63,7 @@ pub struct ClientBuilder { | ||||
|  | ||||
| struct Config { | ||||
|     gzip: bool, | ||||
|     headers: Headers, | ||||
|     headers: HeaderMap, | ||||
|     hostname_verification: bool, | ||||
|     proxies: Vec<Proxy>, | ||||
|     redirect_policy: RedirectPolicy, | ||||
| @@ -78,9 +78,9 @@ impl ClientBuilder { | ||||
|     pub fn new() -> ClientBuilder { | ||||
|         match TlsConnector::builder() { | ||||
|             Ok(tls_connector_builder) => { | ||||
|                 let mut headers = Headers::with_capacity(2); | ||||
|                 headers.set(UserAgent::new(DEFAULT_USER_AGENT)); | ||||
|                 headers.set(Accept::star()); | ||||
|                 let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2); | ||||
|                 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")); | ||||
|  | ||||
|                 ClientBuilder { | ||||
|                     config: Some(Config { | ||||
| @@ -114,7 +114,7 @@ impl ClientBuilder { | ||||
|     /// | ||||
|     /// This method consumes the internal state of the builder. | ||||
|     /// Trying to use this builder again after calling `build` will panic. | ||||
|     pub fn build(&mut self, handle: &Handle) -> ::Result<Client> { | ||||
|     pub fn build(&mut self) -> ::Result<Client> { | ||||
|         if let Some(err) = self.err.take() { | ||||
|             return Err(err); | ||||
|         } | ||||
| @@ -126,14 +126,13 @@ impl ClientBuilder { | ||||
|  | ||||
|         let proxies = Arc::new(config.proxies); | ||||
|  | ||||
|         let mut connector = Connector::new(config.dns_threads, tls, proxies.clone(), handle); | ||||
|         let mut connector = Connector::new(config.dns_threads, tls, proxies.clone()); | ||||
|         if !config.hostname_verification { | ||||
|             connector.danger_disable_hostname_verification(); | ||||
|         } | ||||
|  | ||||
|         let hyper_client = ::hyper::Client::configure() | ||||
|             .connector(connector) | ||||
|             .build(handle); | ||||
|         let hyper_client = ::hyper::Client::builder() | ||||
|             .build(connector); | ||||
|  | ||||
|         Ok(Client { | ||||
|             inner: Arc::new(ClientRef { | ||||
| @@ -200,9 +199,11 @@ impl ClientBuilder { | ||||
|  | ||||
|     /// Sets the default headers for every request. | ||||
|     #[inline] | ||||
|     pub fn default_headers(&mut self, headers: Headers) -> &mut ClientBuilder { | ||||
|     pub fn default_headers(&mut self, headers: HeaderMap) -> &mut ClientBuilder { | ||||
|         if let Some(config) = config_mut(&mut self.config, &self.err) { | ||||
|             config.headers.extend(headers.iter()); | ||||
|             for (key, value) in headers.iter() { | ||||
|                 config.headers.insert(key, value.clone()); | ||||
|             } | ||||
|         } | ||||
|         self | ||||
|     } | ||||
| @@ -287,9 +288,9 @@ impl Client { | ||||
|     /// initialized. Use `Client::builder()` if you wish to handle the failure | ||||
|     /// as an `Error` instead of panicking. | ||||
|     #[inline] | ||||
|     pub fn new(handle: &Handle) -> Client { | ||||
|     pub fn new() -> Client { | ||||
|         ClientBuilder::new() | ||||
|             .build(handle) | ||||
|             .build() | ||||
|             .expect("TLS failed to initialize") | ||||
|     } | ||||
|  | ||||
| @@ -305,7 +306,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Get, url) | ||||
|         self.request(Method::GET, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `POST` request to a URL. | ||||
| @@ -314,7 +315,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Post, url) | ||||
|         self.request(Method::POST, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `PUT` request to a URL. | ||||
| @@ -323,7 +324,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Put, url) | ||||
|         self.request(Method::PUT, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `PATCH` request to a URL. | ||||
| @@ -332,7 +333,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Patch, url) | ||||
|         self.request(Method::PATCH, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `DELETE` request to a URL. | ||||
| @@ -341,7 +342,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Delete, url) | ||||
|         self.request(Method::DELETE, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `HEAD` request to a URL. | ||||
| @@ -350,7 +351,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Head, url) | ||||
|         self.request(Method::HEAD, url) | ||||
|     } | ||||
|  | ||||
|     /// Start building a `Request` with the `Method` and `Url`. | ||||
| @@ -395,28 +396,35 @@ impl Client { | ||||
|         ) = request::pieces(req); | ||||
|  | ||||
|         let mut headers = self.inner.headers.clone(); // default headers | ||||
|         headers.extend(user_headers.iter()); | ||||
|         for (key, value) in user_headers.iter() { | ||||
|             headers.insert(key, value.clone()); | ||||
|         } | ||||
|  | ||||
|         if self.inner.gzip && | ||||
|             !headers.has::<AcceptEncoding>() && | ||||
|             !headers.has::<Range>() { | ||||
|             headers.set(AcceptEncoding(vec![qitem(Encoding::Gzip)])); | ||||
|             !headers.contains_key(ACCEPT_ENCODING) && | ||||
|             !headers.contains_key(RANGE) { | ||||
|             headers.insert(ACCEPT_ENCODING, HeaderValue::from_static("gzip")); | ||||
|         } | ||||
|  | ||||
|         let uri = to_uri(&url); | ||||
|         let mut req = ::hyper::Request::new(method.clone(), uri.clone()); | ||||
|         *req.headers_mut() = headers.clone(); | ||||
|         let body = body.map(|body| { | ||||
|             let (reusable, body) = body::into_hyper(body); | ||||
|             req.set_body(body); | ||||
|             reusable | ||||
|         }); | ||||
|  | ||||
|         if proxy::is_proxied(&self.inner.proxies, &url) { | ||||
|             if uri.scheme() == Some("http") { | ||||
|                 req.set_proxy(true); | ||||
|         let (reusable, body) = match body { | ||||
|             Some(body) => { | ||||
|                 let (reusable, body) = body::into_hyper(body); | ||||
|                 (Some(reusable), body) | ||||
|             }, | ||||
|             None => { | ||||
|                 (None, ::hyper::Body::empty()) | ||||
|             } | ||||
|         } | ||||
|         }; | ||||
|  | ||||
|         let mut req = ::hyper::Request::builder() | ||||
|             .method(method.clone()) | ||||
|             .uri(uri.clone()) | ||||
|             .body(body) | ||||
|             .expect("valid request parts"); | ||||
|  | ||||
|         *req.headers_mut() = headers.clone(); | ||||
|  | ||||
|         let in_flight = self.inner.hyper.request(req); | ||||
|  | ||||
| @@ -425,7 +433,7 @@ impl Client { | ||||
|                 method: method, | ||||
|                 url: url, | ||||
|                 headers: headers, | ||||
|                 body: body, | ||||
|                 body: reusable, | ||||
|  | ||||
|                 urls: Vec::new(), | ||||
|  | ||||
| @@ -456,7 +464,7 @@ impl fmt::Debug for ClientBuilder { | ||||
|  | ||||
| struct ClientRef { | ||||
|     gzip: bool, | ||||
|     headers: Headers, | ||||
|     headers: HeaderMap, | ||||
|     hyper: HyperClient, | ||||
|     proxies: Arc<Vec<Proxy>>, | ||||
|     redirect_policy: RedirectPolicy, | ||||
| @@ -475,14 +483,14 @@ enum PendingInner { | ||||
| pub struct PendingRequest { | ||||
|     method: Method, | ||||
|     url: Url, | ||||
|     headers: Headers, | ||||
|     headers: HeaderMap, | ||||
|     body: Option<Option<Bytes>>, | ||||
|  | ||||
|     urls: Vec<Url>, | ||||
|  | ||||
|     client: Arc<ClientRef>, | ||||
|  | ||||
|     in_flight: FutureResponse, | ||||
|     in_flight: ResponseFuture, | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -509,20 +517,20 @@ impl Future for PendingRequest { | ||||
|                 Async::NotReady => return Ok(Async::NotReady), | ||||
|             }; | ||||
|             let should_redirect = match res.status() { | ||||
|                 StatusCode::MovedPermanently | | ||||
|                 StatusCode::Found | | ||||
|                 StatusCode::SeeOther => { | ||||
|                 StatusCode::MOVED_PERMANENTLY | | ||||
|                 StatusCode::FOUND | | ||||
|                 StatusCode::SEE_OTHER => { | ||||
|                     self.body = None; | ||||
|                     match self.method { | ||||
|                         Method::Get | Method::Head => {}, | ||||
|                         Method::GET | Method::HEAD => {}, | ||||
|                         _ => { | ||||
|                             self.method = Method::Get; | ||||
|                             self.method = Method::GET; | ||||
|                         } | ||||
|                     } | ||||
|                     true | ||||
|                 }, | ||||
|                 StatusCode::TemporaryRedirect | | ||||
|                 StatusCode::PermanentRedirect => match self.body { | ||||
|                 StatusCode::TEMPORARY_REDIRECT | | ||||
|                 StatusCode::PERMANENT_REDIRECT => match self.body { | ||||
|                     Some(Some(_)) | None => true, | ||||
|                     Some(None) => false, | ||||
|                 }, | ||||
| @@ -530,12 +538,12 @@ impl Future for PendingRequest { | ||||
|             }; | ||||
|             if should_redirect { | ||||
|                 let loc = res.headers() | ||||
|                     .get::<Location>() | ||||
|                     .map(|loc| self.url.join(loc)); | ||||
|                     .get(LOCATION) | ||||
|                     .map(|loc| self.url.join(loc.to_str().expect(""))); | ||||
|                 if let Some(Ok(loc)) = loc { | ||||
|                     if self.client.referer { | ||||
|                         if let Some(referer) = make_referer(&loc, &self.url) { | ||||
|                             self.headers.set(referer); | ||||
|                             self.headers.insert(REFERER, referer); | ||||
|                         } | ||||
|                     } | ||||
|                     self.urls.push(self.url.clone()); | ||||
| @@ -553,19 +561,17 @@ impl Future for PendingRequest { | ||||
|                             remove_sensitive_headers(&mut self.headers, &self.url, &self.urls); | ||||
|                             debug!("redirecting to {:?} '{}'", self.method, self.url); | ||||
|                             let uri = to_uri(&self.url); | ||||
|                             let mut req = ::hyper::Request::new( | ||||
|                                 self.method.clone(), | ||||
|                                 uri.clone() | ||||
|                             ); | ||||
|                             let body = match self.body { | ||||
|                                 Some(Some(ref body)) => ::hyper::Body::from(body.clone()), | ||||
|                                 _ => ::hyper::Body::empty(), | ||||
|                             }; | ||||
|                             let mut req = ::hyper::Request::builder() | ||||
|                                 .method(self.method.clone()) | ||||
|                                 .uri(uri.clone()) | ||||
|                                 .body(body) | ||||
|                                 .expect("valid request parts"); | ||||
|  | ||||
|                             *req.headers_mut() = self.headers.clone(); | ||||
|                             if let Some(Some(ref body)) = self.body { | ||||
|                                 req.set_body(body.clone()); | ||||
|                             } | ||||
|                             if proxy::is_proxied(&self.client.proxies, &self.url) { | ||||
|                                 if uri.scheme() == Some("http") { | ||||
|                                     req.set_proxy(true); | ||||
|                                 } | ||||
|                             } | ||||
|                             self.in_flight = self.client.hyper.request(req); | ||||
|                             continue; | ||||
|                         }, | ||||
| @@ -607,7 +613,7 @@ impl fmt::Debug for Pending { | ||||
|     } | ||||
| } | ||||
|  | ||||
| fn make_referer(next: &Url, previous: &Url) -> Option<Referer> { | ||||
| fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> { | ||||
|     if next.scheme() == "http" && previous.scheme() == "https" { | ||||
|         return None; | ||||
|     } | ||||
| @@ -616,7 +622,7 @@ fn make_referer(next: &Url, previous: &Url) -> Option<Referer> { | ||||
|     let _ = referer.set_username(""); | ||||
|     let _ = referer.set_password(None); | ||||
|     referer.set_fragment(None); | ||||
|     Some(Referer::new(referer.into_string())) | ||||
|     referer.as_str().parse().ok() | ||||
| } | ||||
|  | ||||
| // pub(crate) | ||||
|   | ||||
| @@ -33,12 +33,12 @@ use tokio_io::AsyncRead; | ||||
| use tokio_io::io as async_io; | ||||
| use futures::{Async, Future, Poll, Stream}; | ||||
| use futures::stream::Concat2; | ||||
| use hyper::StatusCode; | ||||
| use hyper::{HeaderMap, StatusCode}; | ||||
| use hyper::header::{CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING, HeaderValue}; | ||||
| use serde::de::DeserializeOwned; | ||||
| use serde_json; | ||||
| use url::Url; | ||||
|  | ||||
| use header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding}; | ||||
| use super::{body, Body, Chunk}; | ||||
| use error; | ||||
|  | ||||
| @@ -111,6 +111,13 @@ impl Decoder { | ||||
|             inner: Inner::Pending(Pending::Gzip(ReadableChunks::new(body))) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn content_length(&self) -> Option<u64> { | ||||
|         match self.inner { | ||||
|             Inner::PlainText(ref body) => body.content_length(), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Stream for Decoder { | ||||
| @@ -407,31 +414,33 @@ impl<S> ReadableChunks<S> | ||||
| /// how to decode the content body of the request. | ||||
| /// | ||||
| /// Uses the correct variant by inspecting the Content-Encoding header. | ||||
| pub fn detect(headers: &mut Headers, body: Body, check_gzip: bool) -> Decoder { | ||||
| pub fn detect(headers: &mut HeaderMap, body: Body, check_gzip: bool) -> Decoder { | ||||
|     if !check_gzip { | ||||
|         return Decoder::plain_text(body); | ||||
|     } | ||||
|     let content_encoding_gzip: bool; | ||||
|     let mut is_gzip = { | ||||
|         content_encoding_gzip = headers | ||||
|             .get::<ContentEncoding>() | ||||
|             .map_or(false, |encs| encs.contains(&Encoding::Gzip)); | ||||
|             .get_all(CONTENT_ENCODING) | ||||
|             .iter() | ||||
|             .fold(false, |acc, enc| acc || enc == HeaderValue::from_static("gzip")); | ||||
|         content_encoding_gzip || | ||||
|         headers | ||||
|             .get::<TransferEncoding>() | ||||
|             .map_or(false, |encs| encs.contains(&Encoding::Gzip)) | ||||
|             .get_all(TRANSFER_ENCODING) | ||||
|             .iter() | ||||
|             .fold(false, |acc, enc| acc || enc == HeaderValue::from_static("gzip")) | ||||
|     }; | ||||
|     if is_gzip { | ||||
|         if let Some(content_length) = headers.get::<ContentLength>() { | ||||
|             if content_length.0 == 0 { | ||||
|         if let Some(content_length) = headers.get(CONTENT_LENGTH) { | ||||
|             if content_length == "0" { | ||||
|                 warn!("GZipped response with content-length of 0"); | ||||
|                 is_gzip = false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     if content_encoding_gzip { | ||||
|         headers.remove::<ContentEncoding>(); | ||||
|         headers.remove::<ContentLength>(); | ||||
|         headers.remove(CONTENT_ENCODING); | ||||
|         headers.remove(CONTENT_LENGTH); | ||||
|     } | ||||
|     if is_gzip { | ||||
|         Decoder::gzip(body) | ||||
|   | ||||
| @@ -1,19 +1,22 @@ | ||||
| use std::fmt; | ||||
|  | ||||
| use base64::{encode}; | ||||
| use mime::{self}; | ||||
| use serde::Serialize; | ||||
| use serde_json; | ||||
| use serde_urlencoded; | ||||
|  | ||||
| use super::body::{self, Body}; | ||||
| use super::client::{Client, Pending, pending_err}; | ||||
| use header::{ContentType, Headers}; | ||||
| use header::{CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue}; | ||||
| use http::HttpTryFrom; | ||||
| use {Method, Url}; | ||||
|  | ||||
| /// A request which can be executed with `Client::execute()`. | ||||
| pub struct Request { | ||||
|     method: Method, | ||||
|     url: Url, | ||||
|     headers: Headers, | ||||
|     headers: HeaderMap, | ||||
|     body: Option<Body>, | ||||
| } | ||||
|  | ||||
| @@ -31,7 +34,7 @@ impl Request { | ||||
|         Request { | ||||
|             method, | ||||
|             url, | ||||
|             headers: Headers::new(), | ||||
|             headers: HeaderMap::new(), | ||||
|             body: None, | ||||
|         } | ||||
|     } | ||||
| @@ -62,13 +65,13 @@ impl Request { | ||||
|  | ||||
|     /// Get the headers. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &Headers { | ||||
|     pub fn headers(&self) -> &HeaderMap { | ||||
|         &self.headers | ||||
|     } | ||||
|  | ||||
|     /// Get a mutable reference to the headers. | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut Headers { | ||||
|     pub fn headers_mut(&mut self) -> &mut HeaderMap { | ||||
|         &mut self.headers | ||||
|     } | ||||
|  | ||||
| @@ -87,21 +90,32 @@ impl Request { | ||||
|  | ||||
| impl RequestBuilder { | ||||
|     /// Add a `Header` to this Request. | ||||
|     pub fn header<H>(&mut self, header: H) -> &mut RequestBuilder | ||||
|     pub fn header<K, V>(&mut self, key: K, value: V) -> &mut RequestBuilder | ||||
|     where | ||||
|         H: ::header::Header, | ||||
|         HeaderName: HttpTryFrom<K>, | ||||
|         HeaderValue: HttpTryFrom<V>, | ||||
|     { | ||||
|         if let Some(req) = request_mut(&mut self.request, &self.err) { | ||||
|             req.headers_mut().set(header); | ||||
|             match <HeaderName as HttpTryFrom<K>>::try_from(key) { | ||||
|                 Ok(key) => { | ||||
|                     match <HeaderValue as HttpTryFrom<V>>::try_from(value) { | ||||
|                         Ok(value) => { req.headers_mut().append(key, value); } | ||||
|                         Err(e) => self.err = Some(::error::from(e.into())), | ||||
|                     } | ||||
|                 }, | ||||
|                 Err(e) => self.err = Some(::error::from(e.into())), | ||||
|             }; | ||||
|         } | ||||
|         self | ||||
|     } | ||||
|     /// Add a set of Headers to the existing ones on this Request. | ||||
|     /// | ||||
|     /// The headers will be merged in to any already set. | ||||
|     pub fn headers(&mut self, headers: ::header::Headers) -> &mut RequestBuilder { | ||||
|     pub fn headers(&mut self, headers: ::header::HeaderMap) -> &mut RequestBuilder { | ||||
|         if let Some(req) = request_mut(&mut self.request, &self.err) { | ||||
|             req.headers_mut().extend(headers.iter()); | ||||
|             for (key, value) in headers.iter() { | ||||
|                 req.headers_mut().insert(key, value.clone()); | ||||
|             } | ||||
|         } | ||||
|         self | ||||
|     } | ||||
| @@ -112,10 +126,10 @@ impl RequestBuilder { | ||||
|         U: Into<String>, | ||||
|         P: Into<String>, | ||||
|     { | ||||
|         self.header(::header::Authorization(::header::Basic { | ||||
|             username: username.into(), | ||||
|             password: password.map(|p| p.into()), | ||||
|         })) | ||||
|         let username = username.into(); | ||||
|         let password = password.map(|p| p.into()).unwrap_or(String::new()); | ||||
|         let header_value = format!("basic {}:{}", username, encode(&password)); | ||||
|         self.header(::header::AUTHORIZATION, HeaderValue::from_str(header_value.as_str()).expect("")) | ||||
|     } | ||||
|  | ||||
|     /// Set the request body. | ||||
| @@ -162,7 +176,7 @@ impl RequestBuilder { | ||||
|         if let Some(req) = request_mut(&mut self.request, &self.err) { | ||||
|             match serde_urlencoded::to_string(form) { | ||||
|                 Ok(body) => { | ||||
|                     req.headers_mut().set(ContentType::form_url_encoded()); | ||||
|                     req.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_str(mime::APPLICATION_WWW_FORM_URLENCODED.as_ref()).expect("")); | ||||
|                     *req.body_mut() = Some(body.into()); | ||||
|                 }, | ||||
|                 Err(err) => self.err = Some(::error::from(err)), | ||||
| @@ -181,7 +195,7 @@ impl RequestBuilder { | ||||
|         if let Some(req) = request_mut(&mut self.request, &self.err) { | ||||
|             match serde_json::to_vec(json) { | ||||
|                 Ok(body) => { | ||||
|                     req.headers_mut().set(ContentType::json()); | ||||
|                     req.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_str(mime::APPLICATION_JSON.as_ref()).expect("")); | ||||
|                     *req.body_mut() = Some(body.into()); | ||||
|                 }, | ||||
|                 Err(err) => self.err = Some(::error::from(err)), | ||||
| @@ -274,7 +288,7 @@ pub fn builder(client: Client, req: ::Result<Request>) -> RequestBuilder { | ||||
| } | ||||
|  | ||||
| #[inline] | ||||
| pub fn pieces(req: Request) -> (Method, Url, Headers, Option<Body>) { | ||||
| pub fn pieces(req: Request) -> (Method, Url, HeaderMap, Option<Body>) { | ||||
|     (req.method, req.url, req.headers, req.body) | ||||
| } | ||||
|  | ||||
| @@ -282,12 +296,10 @@ pub fn pieces(req: Request) -> (Method, Url, Headers, Option<Body>) { | ||||
| mod tests { | ||||
|     use super::Client; | ||||
|     use std::collections::BTreeMap; | ||||
|     use tokio_core::reactor::Core; | ||||
|  | ||||
|     #[test] | ||||
|     fn add_query_append() { | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let client = Client::new(&core.handle()); | ||||
|         let client = Client::new(); | ||||
|         let some_url = "https://google.com/"; | ||||
|         let mut r = client.get(some_url); | ||||
|  | ||||
| @@ -300,8 +312,7 @@ mod tests { | ||||
|  | ||||
|     #[test] | ||||
|     fn add_query_append_same() { | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let client = Client::new(&core.handle()); | ||||
|         let client = Client::new(); | ||||
|         let some_url = "https://google.com/"; | ||||
|         let mut r = client.get(some_url); | ||||
|  | ||||
| @@ -319,8 +330,7 @@ mod tests { | ||||
|             qux: i32, | ||||
|         } | ||||
|  | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let client = Client::new(&core.handle()); | ||||
|         let client = Client::new(); | ||||
|         let some_url = "https://google.com/"; | ||||
|         let mut r = client.get(some_url); | ||||
|  | ||||
| @@ -338,8 +348,7 @@ mod tests { | ||||
|         params.insert("foo", "bar"); | ||||
|         params.insert("qux", "three"); | ||||
|  | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let client = Client::new(&core.handle()); | ||||
|         let client = Client::new(); | ||||
|         let some_url = "https://google.com/"; | ||||
|         let mut r = client.get(some_url); | ||||
|  | ||||
|   | ||||
| @@ -13,7 +13,7 @@ use serde::de::DeserializeOwned; | ||||
| use serde_json; | ||||
| use url::Url; | ||||
|  | ||||
| use header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding}; | ||||
| use hyper::header::{HeaderMap, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING}; | ||||
| use super::{decoder, body, Body, Chunk, Decoder}; | ||||
| use error; | ||||
|  | ||||
| @@ -21,7 +21,7 @@ use error; | ||||
| /// A Response to a submitted `Request`. | ||||
| pub struct Response { | ||||
|     status: StatusCode, | ||||
|     headers: Headers, | ||||
|     headers: HeaderMap, | ||||
|     url: Url, | ||||
|     body: Decoder, | ||||
| } | ||||
| @@ -41,13 +41,13 @@ impl Response { | ||||
|  | ||||
|     /// Get the `Headers` of this `Response`. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &Headers { | ||||
|     pub fn headers(&self) -> &HeaderMap { | ||||
|         &self.headers | ||||
|     } | ||||
|  | ||||
|     /// Get a mutable reference to the `Headers` of this `Response`. | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut Headers { | ||||
|     pub fn headers_mut(&mut self) -> &mut HeaderMap { | ||||
|         &mut self.headers | ||||
|     } | ||||
|  | ||||
| @@ -99,7 +99,7 @@ impl Response { | ||||
|     ///             // it could be any status between 400...599 | ||||
|     ///             assert_eq!( | ||||
|     ///                 err.status(), | ||||
|     ///                 Some(reqwest::StatusCode::BadRequest) | ||||
|     ///                 Some(reqwest::StatusCode::BAD_REQUEST) | ||||
|     ///             ); | ||||
|     ///         } | ||||
|     ///     } | ||||
| @@ -152,10 +152,10 @@ impl<T> fmt::Debug for Json<T> { | ||||
|  | ||||
| // pub(crate) | ||||
|  | ||||
| pub fn new(mut res: ::hyper::client::Response, url: Url, gzip: bool) -> Response { | ||||
| pub fn new(mut res: ::hyper::Response<::hyper::Body>, url: Url, gzip: bool) -> Response { | ||||
|     let status = res.status(); | ||||
|     let mut headers = mem::replace(res.headers_mut(), Headers::new()); | ||||
|     let decoder = decoder::detect(&mut headers, body::wrap(res.body()), gzip); | ||||
|     let mut headers = mem::replace(res.headers_mut(), HeaderMap::new()); | ||||
|     let decoder = decoder::detect(&mut headers, body::wrap(res.into_body()), gzip); | ||||
|     debug!("Response: '{}' for {}", status, url); | ||||
|     Response { | ||||
|         status: status, | ||||
|   | ||||
							
								
								
									
										21
									
								
								src/body.rs
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/body.rs
									
									
									
									
									
								
							| @@ -3,9 +3,9 @@ use std::fmt; | ||||
| use std::io::{self, Cursor, Read}; | ||||
|  | ||||
| use bytes::Bytes; | ||||
| use hyper::{self, Chunk}; | ||||
| use hyper::{self}; | ||||
|  | ||||
| use {async_impl, wait}; | ||||
| use {async_impl}; | ||||
|  | ||||
| /// The body of a `Request`. | ||||
| /// | ||||
| @@ -184,7 +184,7 @@ impl Read for Reader { | ||||
|  | ||||
| pub struct Sender { | ||||
|     body: (Box<Read + Send>, Option<u64>), | ||||
|     tx: wait::WaitSink<::futures::sync::mpsc::Sender<hyper::Result<Chunk>>>, | ||||
|     tx: hyper::body::Sender, | ||||
| } | ||||
|  | ||||
| impl Sender { | ||||
| @@ -201,13 +201,8 @@ impl Sender { | ||||
|                 Ok(0) => return Ok(()), | ||||
|                 Ok(n) => { | ||||
|                     unsafe { buf.advance_mut(n); } | ||||
|                     if let Err(e) = tx.send(Ok(buf.take().freeze().into())) { | ||||
|                         if let wait::Waited::Err(_) = e { | ||||
|                             let epipe = io::Error::new(io::ErrorKind::BrokenPipe, "broken pipe"); | ||||
|                             return Err(::error::from(epipe)); | ||||
|                         } else { | ||||
|                             return Err(::error::timedout(None)); | ||||
|                         } | ||||
|                     if let Err(_) = tx.send_data(buf.take().freeze().into()) { | ||||
|                         return Err(::error::timedout(None)); | ||||
|                     } | ||||
|                     if buf.remaining_mut() == 0 { | ||||
|                         buf.reserve(8192); | ||||
| @@ -215,7 +210,7 @@ impl Sender { | ||||
|                 } | ||||
|                 Err(e) => { | ||||
|                     let ret = io::Error::new(e.kind(), e.to_string()); | ||||
|                     let _ = tx.send(Err(e.into())); | ||||
|                     tx.abort(); | ||||
|                     return Err(::error::from(ret)); | ||||
|                 } | ||||
|             } | ||||
| @@ -227,10 +222,10 @@ impl Sender { | ||||
| pub fn async(body: Body) -> (Option<Sender>, async_impl::Body, Option<u64>) { | ||||
|     match body.kind { | ||||
|         Kind::Reader(read, len) => { | ||||
|             let (tx, rx) = hyper::Body::pair(); | ||||
|             let (tx, rx) = hyper::Body::channel(); | ||||
|             let tx = Sender { | ||||
|                 body: (read, len), | ||||
|                 tx: wait::sink(tx, None), | ||||
|                 tx: tx, | ||||
|             }; | ||||
|             (Some(tx), async_impl::body::wrap(rx), len) | ||||
|         }, | ||||
|   | ||||
| @@ -174,8 +174,8 @@ impl ClientBuilder { | ||||
|     /// ```rust | ||||
|     /// use reqwest::header; | ||||
|     /// # fn build_client() -> Result<(), Box<std::error::Error>> { | ||||
|     /// let mut headers = header::Headers::new(); | ||||
|     /// headers.set(header::Authorization("secret".to_string())); | ||||
|     /// let mut headers = header::HeaderMap::new(); | ||||
|     /// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret")); | ||||
|     /// | ||||
|     /// // get a client builder | ||||
|     /// let client = reqwest::Client::builder() | ||||
| @@ -191,8 +191,8 @@ impl ClientBuilder { | ||||
|     /// ```rust | ||||
|     /// use reqwest::header; | ||||
|     /// # fn build_client() -> Result<(), Box<std::error::Error>> { | ||||
|     /// let mut headers = header::Headers::new(); | ||||
|     /// headers.set(header::Authorization("secret".to_string())); | ||||
|     /// let mut headers = header::HeaderMap::new(); | ||||
|     /// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret")); | ||||
|     /// | ||||
|     /// // get a client builder | ||||
|     /// let client = reqwest::Client::builder() | ||||
| @@ -200,13 +200,13 @@ impl ClientBuilder { | ||||
|     ///     .build()?; | ||||
|     /// let res = client | ||||
|     ///     .get("https://www.rust-lang.org") | ||||
|     ///     .header(header::Authorization("token".to_string())) | ||||
|     ///     .header(header::AUTHORIZATION, "token") | ||||
|     ///     .send()?; | ||||
|     /// # Ok(()) | ||||
|     /// # } | ||||
|     /// ``` | ||||
|     #[inline] | ||||
|     pub fn default_headers(&mut self, headers: header::Headers) -> &mut ClientBuilder { | ||||
|     pub fn default_headers(&mut self, headers: header::HeaderMap) -> &mut ClientBuilder { | ||||
|         self.inner.default_headers(headers); | ||||
|         self | ||||
|     } | ||||
| @@ -287,7 +287,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Get, url) | ||||
|         self.request(Method::GET, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `POST` request to a URL. | ||||
| @@ -296,7 +296,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Post, url) | ||||
|         self.request(Method::POST, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `PUT` request to a URL. | ||||
| @@ -305,7 +305,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Put, url) | ||||
|         self.request(Method::PUT, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `PATCH` request to a URL. | ||||
| @@ -314,7 +314,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Patch, url) | ||||
|         self.request(Method::PATCH, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `DELETE` request to a URL. | ||||
| @@ -323,7 +323,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Delete, url) | ||||
|         self.request(Method::DELETE, url) | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to make a `HEAD` request to a URL. | ||||
| @@ -332,7 +332,7 @@ impl Client { | ||||
|     /// | ||||
|     /// This method fails whenever supplied `Url` cannot be parsed. | ||||
|     pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder { | ||||
|         self.request(Method::Head, url) | ||||
|         self.request(Method::HEAD, url) | ||||
|     } | ||||
|  | ||||
|     /// Start building a `Request` with the `Method` and `Url`. | ||||
| @@ -412,22 +412,21 @@ impl ClientHandle { | ||||
|         let mut builder = async_impl::client::take_builder(&mut builder.inner); | ||||
|         let (tx, rx) = mpsc::unbounded(); | ||||
|         let (spawn_tx, spawn_rx) = oneshot::channel::<::Result<()>>(); | ||||
|         let handle = try_!(thread::Builder::new().name("reqwest-internal-sync-core".into()).spawn(move || { | ||||
|             use tokio_core::reactor::Core; | ||||
|         let handle = try_!(thread::Builder::new().name("reqwest-internal-sync-runtime".into()).spawn(move || { | ||||
|             use tokio::runtime::current_thread::Runtime; | ||||
|  | ||||
|             let built = (|| { | ||||
|                 let core = try_!(Core::new()); | ||||
|                 let handle = core.handle(); | ||||
|                 let client = builder.build(&handle)?; | ||||
|                 Ok((core, handle, client)) | ||||
|                 let rt = try_!(Runtime::new()); | ||||
|                 let client = builder.build()?; | ||||
|                 Ok((rt, client)) | ||||
|             })(); | ||||
|  | ||||
|             let (mut core, handle, client) = match built { | ||||
|                 Ok((a, b, c)) => { | ||||
|             let (mut rt, client) = match built { | ||||
|                 Ok((rt, c)) => { | ||||
|                     if let Err(_) = spawn_tx.send(Ok(())) { | ||||
|                         return; | ||||
|                     } | ||||
|                     (a, b, c) | ||||
|                     (rt, c) | ||||
|                 }, | ||||
|                 Err(e) => { | ||||
|                     let _ = spawn_tx.send(Err(e)); | ||||
| @@ -435,19 +434,22 @@ impl ClientHandle { | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             let work = rx.for_each(|(req, tx)| { | ||||
|             let work = rx.for_each(move |(req, tx)| { | ||||
|                 let tx: oneshot::Sender<::Result<async_impl::Response>> = tx; | ||||
|                 let task = client.execute(req) | ||||
|                     .then(move |x| tx.send(x).map_err(|_| ())); | ||||
|                 handle.spawn(task); | ||||
|                 ::tokio::spawn(task); | ||||
|                 Ok(()) | ||||
|             }); | ||||
|  | ||||
|  | ||||
|             // work is Future<(), ()>, and our closure will never return Err | ||||
|             let _ = core.run(work); | ||||
|             rt.spawn(work) | ||||
|                 .run() | ||||
|                 .expect("runtime unexpected error"); | ||||
|         })); | ||||
|  | ||||
|         wait::timeout(spawn_rx, timeout.0).expect("core thread cancelled")?; | ||||
|         wait::timeout(spawn_rx, timeout.0).expect("runtime thread cancelled")?; | ||||
|  | ||||
|         let inner_handle = Arc::new(InnerClientHandle { | ||||
|             tx: Some(tx), | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| use bytes::{Buf, BufMut, IntoBuf}; | ||||
| use futures::{Async, Future, Poll}; | ||||
| use hyper::client::{HttpConnector, Service}; | ||||
| use hyper::Uri; | ||||
| use http::uri::Scheme; | ||||
| use hyper::client::{HttpConnector}; | ||||
| use hyper::client::connect::{Connect, Connected, Destination}; | ||||
| use hyper_tls::{HttpsConnector, MaybeHttpsStream}; | ||||
| use native_tls::TlsConnector; | ||||
| use tokio_core::reactor::Handle; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
| use tokio_tls::{TlsConnectorExt, TlsStream}; | ||||
|  | ||||
| @@ -22,8 +22,8 @@ pub struct Connector { | ||||
| } | ||||
|  | ||||
| impl Connector { | ||||
|     pub fn new(threads: usize, tls: TlsConnector, proxies: Arc<Vec<Proxy>>, handle: &Handle) -> Connector { | ||||
|         let mut http = HttpConnector::new(threads, handle); | ||||
|     pub fn new(threads: usize, tls: TlsConnector, proxies: Arc<Vec<Proxy>>) -> Connector { | ||||
|         let mut http = HttpConnector::new(threads); | ||||
|         http.enforce_http(false); | ||||
|         let https = HttpsConnector::from((http, tls.clone())); | ||||
|  | ||||
| @@ -39,41 +39,53 @@ impl Connector { | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Service for Connector { | ||||
|     type Request = Uri; | ||||
|     type Response = Conn; | ||||
| impl Connect for Connector { | ||||
|     type Transport = Conn; | ||||
|     type Error = io::Error; | ||||
|     type Future = Connecting; | ||||
|  | ||||
|     fn call(&self, uri: Uri) -> Self::Future { | ||||
|     fn connect(&self, dst: Destination) -> Self::Future { | ||||
|         for prox in self.proxies.iter() { | ||||
|             if let Some(puri) = proxy::intercept(prox, &uri) { | ||||
|                 trace!("proxy({:?}) intercepts {:?}", puri, uri); | ||||
|                 if uri.scheme() == Some("https") { | ||||
|                     let host = uri.host().unwrap().to_owned(); | ||||
|                     let port = uri.port().unwrap_or(443); | ||||
|             if let Some(puri) = proxy::intercept(prox, &dst) { | ||||
|                 trace!("proxy({:?}) intercepts {:?}", puri, dst); | ||||
|                 let mut ndst = dst.clone(); | ||||
|                 let new_scheme = puri | ||||
|                     .scheme_part() | ||||
|                     .map(Scheme::as_str) | ||||
|                     .unwrap_or("http"); | ||||
|                 ndst.set_scheme(new_scheme) | ||||
|                     .expect("proxy target scheme should be valid"); | ||||
|  | ||||
|                 ndst.set_host(puri.host().expect("proxy target should have host")) | ||||
|                     .expect("proxy target host should be valid"); | ||||
|  | ||||
|                 ndst.set_port(puri.port()); | ||||
|  | ||||
|                 if dst.scheme() == "https" { | ||||
|                     let host = dst.host().to_owned(); | ||||
|                     let port = dst.port().unwrap_or(443); | ||||
|                     let tls = self.tls.clone(); | ||||
|                     return Box::new(self.https.call(puri).and_then(move |conn| { | ||||
|                     return Box::new(self.https.connect(ndst).and_then(move |(conn, connected)| { | ||||
|                         trace!("tunneling HTTPS over proxy"); | ||||
|                         tunnel(conn, host.clone(), port) | ||||
|                             .and_then(move |tunneled| { | ||||
|                                 tls.connect_async(&host, tunneled) | ||||
|                                     .map_err(|e| io::Error::new(io::ErrorKind::Other, e)) | ||||
|                             }) | ||||
|                             .map(|io| Conn::Proxied(io)) | ||||
|                             .map(|io| (Conn::Proxied(io), connected.proxy(true))) | ||||
|                     })); | ||||
|                 } | ||||
|                 return Box::new(self.https.call(puri).map(|io| Conn::Normal(io))); | ||||
|                 return Box::new(self.https.connect(ndst).map(|(io, connected)| (Conn::Normal(io), connected.proxy(true)))); | ||||
|             } | ||||
|         } | ||||
|         Box::new(self.https.call(uri).map(|io| Conn::Normal(io))) | ||||
|         Box::new(self.https.connect(dst).map(|(io, connected)| (Conn::Normal(io), connected))) | ||||
|     } | ||||
| } | ||||
|  | ||||
| type HttpStream = <HttpConnector as Service>::Response; | ||||
| type HttpStream = <HttpConnector as Connect>::Transport; | ||||
| type HttpsStream = MaybeHttpsStream<HttpStream>; | ||||
|  | ||||
| pub type Connecting = Box<Future<Item=Conn, Error=io::Error>>; | ||||
| pub type Connecting = Box<Future<Item=(Conn, Connected), Error=io::Error> + Send>; | ||||
|  | ||||
| pub enum Conn { | ||||
|     Normal(HttpsStream), | ||||
| @@ -214,8 +226,8 @@ mod tests { | ||||
|     use std::net::TcpListener; | ||||
|     use std::thread; | ||||
|     use futures::Future; | ||||
|     use tokio_core::reactor::Core; | ||||
|     use tokio_core::net::TcpStream; | ||||
|     use tokio::runtime::current_thread::Runtime; | ||||
|     use tokio::net::TcpStream; | ||||
|     use super::tunnel; | ||||
|  | ||||
|  | ||||
| @@ -251,44 +263,44 @@ mod tests { | ||||
|     fn test_tunnel() { | ||||
|         let addr = mock_tunnel!(); | ||||
|  | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let work = TcpStream::connect(&addr, &core.handle()); | ||||
|         let mut rt = Runtime::new().unwrap(); | ||||
|         let work = TcpStream::connect(&addr); | ||||
|         let host = addr.ip().to_string(); | ||||
|         let port = addr.port(); | ||||
|         let work = work.and_then(|tcp| { | ||||
|             tunnel(tcp, host, port) | ||||
|         }); | ||||
|  | ||||
|         core.run(work).unwrap(); | ||||
|         rt.block_on(work).unwrap(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_tunnel_eof() { | ||||
|         let addr = mock_tunnel!(b"HTTP/1.1 200 OK"); | ||||
|  | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let work = TcpStream::connect(&addr, &core.handle()); | ||||
|         let mut rt = Runtime::new().unwrap(); | ||||
|         let work = TcpStream::connect(&addr); | ||||
|         let host = addr.ip().to_string(); | ||||
|         let port = addr.port(); | ||||
|         let work = work.and_then(|tcp| { | ||||
|             tunnel(tcp, host, port) | ||||
|         }); | ||||
|  | ||||
|         core.run(work).unwrap_err(); | ||||
|         rt.block_on(work).unwrap_err(); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_tunnel_bad_response() { | ||||
|         let addr = mock_tunnel!(b"foo bar baz hallo"); | ||||
|  | ||||
|         let mut core = Core::new().unwrap(); | ||||
|         let work = TcpStream::connect(&addr, &core.handle()); | ||||
|         let mut rt = Runtime::new().unwrap(); | ||||
|         let work = TcpStream::connect(&addr); | ||||
|         let host = addr.ip().to_string(); | ||||
|         let port = addr.port(); | ||||
|         let work = work.and_then(|tcp| { | ||||
|             tunnel(tcp, host, port) | ||||
|         }); | ||||
|  | ||||
|         core.run(work).unwrap_err(); | ||||
|         rt.block_on(work).unwrap_err(); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										36
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								src/error.rs
									
									
									
									
									
								
							| @@ -112,6 +112,7 @@ impl Error { | ||||
|     pub fn get_ref(&self) -> Option<&(StdError + Send + Sync + 'static)> { | ||||
|         match self.kind { | ||||
|             Kind::Http(ref e) => Some(e), | ||||
|             Kind::Hyper(ref e) => Some(e), | ||||
|             Kind::Url(ref e) => Some(e), | ||||
|             Kind::Tls(ref e) => Some(e), | ||||
|             Kind::Io(ref e) => Some(e), | ||||
| @@ -129,6 +130,7 @@ impl Error { | ||||
|     pub fn is_http(&self) -> bool { | ||||
|         match self.kind { | ||||
|             Kind::Http(_) => true, | ||||
|             Kind::Hyper(_) => true, | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| @@ -190,6 +192,7 @@ impl fmt::Display for Error { | ||||
|         } | ||||
|         match self.kind { | ||||
|             Kind::Http(ref e) => fmt::Display::fmt(e, f), | ||||
|             Kind::Hyper(ref e) => fmt::Display::fmt(e, f), | ||||
|             Kind::Url(ref e) => fmt::Display::fmt(e, f), | ||||
|             Kind::Tls(ref e) => fmt::Display::fmt(e, f), | ||||
|             Kind::Io(ref e) => fmt::Display::fmt(e, f), | ||||
| @@ -213,6 +216,7 @@ impl StdError for Error { | ||||
|     fn description(&self) -> &str { | ||||
|         match self.kind { | ||||
|             Kind::Http(ref e) => e.description(), | ||||
|             Kind::Hyper(ref e) => e.description(), | ||||
|             Kind::Url(ref e) => e.description(), | ||||
|             Kind::Tls(ref e) => e.description(), | ||||
|             Kind::Io(ref e) => e.description(), | ||||
| @@ -228,6 +232,7 @@ impl StdError for Error { | ||||
|     fn cause(&self) -> Option<&StdError> { | ||||
|         match self.kind { | ||||
|             Kind::Http(ref e) => e.cause(), | ||||
|             Kind::Hyper(ref e) => e.cause(), | ||||
|             Kind::Url(ref e) => e.cause(), | ||||
|             Kind::Tls(ref e) => e.cause(), | ||||
|             Kind::Io(ref e) => e.cause(), | ||||
| @@ -245,7 +250,8 @@ impl StdError for Error { | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub enum Kind { | ||||
|     Http(::hyper::Error), | ||||
|     Http(::http::Error), | ||||
|     Hyper(::hyper::Error), | ||||
|     Url(::url::ParseError), | ||||
|     Tls(::native_tls::Error), | ||||
|     Io(io::Error), | ||||
| @@ -258,13 +264,18 @@ pub enum Kind { | ||||
| } | ||||
|  | ||||
|  | ||||
| impl From<::http::Error> for Kind { | ||||
|     #[inline] | ||||
|     fn from(err: ::http::Error) -> Kind { | ||||
|         Kind::Http(err) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<::hyper::Error> for Kind { | ||||
|     #[inline] | ||||
|     fn from(err: ::hyper::Error) -> Kind { | ||||
|         match err { | ||||
|             ::hyper::Error::Io(err) => Kind::Io(err), | ||||
|             //::hyper::Error::Uri(err) => Kind::Url(err), | ||||
|             other => Kind::Http(other), | ||||
|             other => Kind::Hyper(other), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -430,20 +441,6 @@ pub fn server_error(url: Url, status: StatusCode) -> Error { | ||||
| mod tests { | ||||
|     use super::*; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_error_get_ref_downcasts() { | ||||
|         let err: Error = from(::hyper::Error::Status); | ||||
|         let cause = err.get_ref() | ||||
|             .unwrap() | ||||
|             .downcast_ref::<::hyper::Error>() | ||||
|             .unwrap(); | ||||
|  | ||||
|         match cause { | ||||
|             &::hyper::Error::Status => (), | ||||
|             _ => panic!("unexpected downcast: {:?}", cause), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_cause_chain() { | ||||
|         #[derive(Debug)] | ||||
| @@ -476,9 +473,6 @@ mod tests { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let err = from(::hyper::Error::Status); | ||||
|         assert!(err.cause().is_none()); | ||||
|  | ||||
|         let root = Chain(None::<Error>); | ||||
|         let io = ::std::io::Error::new(::std::io::ErrorKind::Other, root); | ||||
|         let err = Error { kind: Kind::Io(io), url: None }; | ||||
|   | ||||
| @@ -36,7 +36,3 @@ impl<'a> PolyfillTryInto for &'a String { | ||||
| pub fn to_uri(url: &Url) -> ::hyper::Uri { | ||||
|     url.as_str().parse().expect("a parsed Url should always be a valid Uri") | ||||
| } | ||||
|  | ||||
| pub fn to_url(uri: &::hyper::Uri) -> Url { | ||||
|     uri.as_ref().parse().expect("reqwest Uris should only ever come from Urls") | ||||
| } | ||||
|   | ||||
| @@ -124,15 +124,18 @@ | ||||
| //! [serde]: http://serde.rs | ||||
| //! [cookiejar_issue]: https://github.com/seanmonstar/reqwest/issues/14 | ||||
|  | ||||
| extern crate base64; | ||||
| extern crate bytes; | ||||
| extern crate encoding_rs; | ||||
| #[macro_use] | ||||
| extern crate futures; | ||||
| extern crate http; | ||||
| extern crate hyper; | ||||
| extern crate hyper_tls; | ||||
| #[macro_use] | ||||
| extern crate log; | ||||
| extern crate libflate; | ||||
| extern crate mime; | ||||
| extern crate mime_guess; | ||||
| extern crate native_tls; | ||||
| extern crate serde; | ||||
| @@ -141,14 +144,13 @@ extern crate serde; | ||||
| extern crate serde_derive; | ||||
| extern crate serde_json; | ||||
| extern crate serde_urlencoded; | ||||
| extern crate tokio_core; | ||||
| extern crate tokio; | ||||
| extern crate tokio_io; | ||||
| extern crate tokio_tls; | ||||
| extern crate url; | ||||
| extern crate uuid; | ||||
|  | ||||
| pub use hyper::header; | ||||
| pub use hyper::mime; | ||||
| pub use hyper::Method; | ||||
| pub use hyper::StatusCode; | ||||
| pub use url::Url; | ||||
|   | ||||
| @@ -4,8 +4,7 @@ use std::fs::File; | ||||
| use std::io::{self, Cursor, Read}; | ||||
| use std::path::Path; | ||||
|  | ||||
| use mime::Mime; | ||||
| use mime_guess; | ||||
| use mime_guess::{self, Mime}; | ||||
| use url::percent_encoding; | ||||
| use uuid::Uuid; | ||||
|  | ||||
|   | ||||
							
								
								
									
										128
									
								
								src/proxy.rs
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								src/proxy.rs
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| use std::fmt; | ||||
| use std::sync::Arc; | ||||
|  | ||||
| use hyper::Uri; | ||||
| use hyper::client::connect::Destination; | ||||
| use {into_url, IntoUrl, Url}; | ||||
|  | ||||
| /// Configuration of a proxy that a `Client` should pass requests to. | ||||
| @@ -128,36 +128,36 @@ impl Proxy { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn proxies(&self, url: &Url) -> bool { | ||||
|         match self.intercept { | ||||
|             Intercept::All(..) => true, | ||||
|             Intercept::Http(..) => url.scheme() == "http", | ||||
|             Intercept::Https(..) => url.scheme() == "https", | ||||
|             Intercept::Custom(ref fun) => (fun.0)(url).is_some(), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     fn intercept(&self, uri: &Uri) -> Option<Uri> { | ||||
|     fn intercept<D: Dst>(&self, uri: &D) -> Option<::hyper::Uri> { | ||||
|         match self.intercept { | ||||
|             Intercept::All(ref u) => Some(u.clone()), | ||||
|             Intercept::Http(ref u) => { | ||||
|                 if uri.scheme() == Some("http") { | ||||
|                 if uri.scheme() == "http" { | ||||
|                     Some(u.clone()) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             }, | ||||
|             Intercept::Https(ref u) => { | ||||
|                 if uri.scheme() == Some("https") { | ||||
|                 if uri.scheme() == "https" { | ||||
|                     Some(u.clone()) | ||||
|                 } else { | ||||
|                     None | ||||
|                 } | ||||
|             }, | ||||
|             Intercept::Custom(ref fun) => { | ||||
|                 (fun.0)(&into_url::to_url(uri)) | ||||
|                     .map(|u| into_url::to_uri(&u)) | ||||
|                 (fun.0)( | ||||
|                     &format!( | ||||
|                         "{}://{}{}{}", | ||||
|                         uri.scheme(), | ||||
|                         uri.host(), | ||||
|                         uri.port().map(|_| ":").unwrap_or(""), | ||||
|                         uri.port().map(|p| p.to_string()).unwrap_or(String::new()) | ||||
|                     ) | ||||
|                         .parse() | ||||
|                         .expect("should be valid Url") | ||||
|                 ) | ||||
|                     .map(|u| into_url::to_uri(&u) ) | ||||
|             }, | ||||
|         } | ||||
|     } | ||||
| @@ -165,9 +165,9 @@ impl Proxy { | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| enum Intercept { | ||||
|     All(Uri), | ||||
|     Http(Uri), | ||||
|     Https(Uri), | ||||
|     All(::hyper::Uri), | ||||
|     Http(::hyper::Uri), | ||||
|     Https(::hyper::Uri), | ||||
|     Custom(Custom), | ||||
| } | ||||
|  | ||||
| @@ -182,20 +182,50 @@ impl fmt::Debug for Custom { | ||||
|  | ||||
| // pub(crate) | ||||
|  | ||||
| pub fn intercept(proxy: &Proxy, uri: &Uri) -> Option<Uri> { | ||||
|     proxy.intercept(uri) | ||||
| /// A helper trait to allow testing `Proxy::intercept` without having to | ||||
| /// construct `hyper::client::connect::Destination`s. | ||||
| trait Dst { | ||||
|     fn scheme(&self) -> &str; | ||||
|     fn host(&self) -> &str; | ||||
|     fn port(&self) -> Option<u16>; | ||||
| } | ||||
|  | ||||
| pub fn is_proxied(proxies: &[Proxy], uri: &Url) -> bool { | ||||
|     proxies.iter().any(|p| p.proxies(uri)) | ||||
| #[doc(hidden)] | ||||
| impl Dst for Destination { | ||||
|     fn scheme(&self) -> &str { | ||||
|         Destination::scheme(self) | ||||
|     } | ||||
|  | ||||
|     fn host(&self) -> &str { | ||||
|         Destination::host(self) | ||||
|     } | ||||
|  | ||||
|     fn port(&self) -> Option<u16> { | ||||
|         Destination::port(self) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn intercept(proxy: &Proxy, uri: &Destination) -> Option<::http::Uri> { | ||||
|     proxy.intercept(uri) | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|  | ||||
|     fn uri(s: &str) -> Uri { | ||||
|         s.parse().unwrap() | ||||
|     impl Dst for Url { | ||||
|         fn scheme(&self) -> &str { | ||||
|             Url::scheme(self) | ||||
|         } | ||||
|  | ||||
|         fn host(&self) -> &str { | ||||
|             Url::host_str(self) | ||||
|                 .expect("<Url as Dst>::host should have a str") | ||||
|         } | ||||
|  | ||||
|         fn port(&self) -> Option<u16> { | ||||
|             Url::port(self) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn url(s: &str) -> Url { | ||||
| @@ -210,10 +240,8 @@ mod tests { | ||||
|         let http = "http://hyper.rs"; | ||||
|         let other = "https://hyper.rs"; | ||||
|  | ||||
|         assert!(p.proxies(&url(http))); | ||||
|         assert_eq!(p.intercept(&uri(http)).unwrap(), target); | ||||
|         assert!(!p.proxies(&url(other))); | ||||
|         assert!(p.intercept(&uri(other)).is_none()); | ||||
|         assert_eq!(p.intercept(&url(http)).unwrap(), target); | ||||
|         assert!(p.intercept(&url(other)).is_none()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -224,10 +252,8 @@ mod tests { | ||||
|         let http = "http://hyper.rs"; | ||||
|         let other = "https://hyper.rs"; | ||||
|  | ||||
|         assert!(!p.proxies(&url(http))); | ||||
|         assert!(p.intercept(&uri(http)).is_none()); | ||||
|         assert!(p.proxies(&url(other))); | ||||
|         assert_eq!(p.intercept(&uri(other)).unwrap(), target); | ||||
|         assert!(p.intercept(&url(http)).is_none()); | ||||
|         assert_eq!(p.intercept(&url(other)).unwrap(), target); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -239,13 +265,9 @@ mod tests { | ||||
|         let https = "https://hyper.rs"; | ||||
|         let other = "x-youve-never-heard-of-me-mr-proxy://hyper.rs"; | ||||
|  | ||||
|         assert!(p.proxies(&url(http))); | ||||
|         assert!(p.proxies(&url(https))); | ||||
|         assert!(p.proxies(&url(other))); | ||||
|  | ||||
|         assert_eq!(p.intercept(&uri(http)).unwrap(), target); | ||||
|         assert_eq!(p.intercept(&uri(https)).unwrap(), target); | ||||
|         assert_eq!(p.intercept(&uri(other)).unwrap(), target); | ||||
|         assert_eq!(p.intercept(&url(http)).unwrap(), target); | ||||
|         assert_eq!(p.intercept(&url(https)).unwrap(), target); | ||||
|         assert_eq!(p.intercept(&url(other)).unwrap(), target); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -267,29 +289,9 @@ mod tests { | ||||
|         let https = "https://hyper.rs"; | ||||
|         let other = "x-youve-never-heard-of-me-mr-proxy://seanmonstar.com"; | ||||
|  | ||||
|         assert!(p.proxies(&url(http))); | ||||
|         assert!(p.proxies(&url(https))); | ||||
|         assert!(!p.proxies(&url(other))); | ||||
|  | ||||
|         assert_eq!(p.intercept(&uri(http)).unwrap(), target2); | ||||
|         assert_eq!(p.intercept(&uri(https)).unwrap(), target1); | ||||
|         assert!(p.intercept(&uri(other)).is_none()); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_is_proxied() { | ||||
|         let proxies = vec![ | ||||
|             Proxy::http("http://example.domain").unwrap(), | ||||
|             Proxy::https("http://other.domain").unwrap(), | ||||
|         ]; | ||||
|  | ||||
|         let http = "http://hyper.rs".parse().unwrap(); | ||||
|         let https = "https://hyper.rs".parse().unwrap(); | ||||
|         let other = "x-other://hyper.rs".parse().unwrap(); | ||||
|  | ||||
|         assert!(is_proxied(&proxies, &http)); | ||||
|         assert!(is_proxied(&proxies, &https)); | ||||
|         assert!(!is_proxied(&proxies, &other)); | ||||
|         assert_eq!(p.intercept(&url(http)).unwrap(), target2); | ||||
|         assert_eq!(p.intercept(&url(https)).unwrap(), target1); | ||||
|         assert!(p.intercept(&url(other)).is_none()); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| use std::fmt; | ||||
|  | ||||
| use hyper::header::Headers; | ||||
| use header::HeaderMap; | ||||
| use hyper::StatusCode; | ||||
|  | ||||
| use Url; | ||||
| @@ -228,15 +228,15 @@ pub fn check_redirect( | ||||
|         .inner | ||||
| } | ||||
|  | ||||
| pub fn remove_sensitive_headers(headers: &mut Headers, next: &Url, previous: &[Url]) { | ||||
| pub fn remove_sensitive_headers(headers: &mut HeaderMap, next: &Url, previous: &[Url]) { | ||||
|     if let Some(previous) = previous.last() { | ||||
|         let cross_host = next.host_str() != previous.host_str() || | ||||
|                          next.port_or_known_default() != previous.port_or_known_default(); | ||||
|         if cross_host { | ||||
|             headers.remove_raw("authorization"); | ||||
|             headers.remove_raw("cookie"); | ||||
|             headers.remove_raw("cookie2"); | ||||
|             headers.remove_raw("www-authenticate"); | ||||
|             headers.remove("authorization"); | ||||
|             headers.remove("cookie"); | ||||
|             headers.remove("cookie2"); | ||||
|             headers.remove("www-authenticate"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -268,14 +268,14 @@ fn test_redirect_policy_limit() { | ||||
|         .collect::<Vec<_>>(); | ||||
|  | ||||
|     assert_eq!( | ||||
|         check_redirect(&policy, StatusCode::Found, &next, &previous), | ||||
|         check_redirect(&policy, StatusCode::FOUND, &next, &previous), | ||||
|         Action::Follow | ||||
|     ); | ||||
|  | ||||
|     previous.push(Url::parse("http://a.b.d/e/33").unwrap()); | ||||
|  | ||||
|     assert_eq!( | ||||
|         check_redirect(&policy, StatusCode::Found, &next, &previous), | ||||
|         check_redirect(&policy, StatusCode::FOUND, &next, &previous), | ||||
|         Action::TooManyRedirects | ||||
|     ); | ||||
| } | ||||
| @@ -292,27 +292,25 @@ fn test_redirect_policy_custom() { | ||||
|  | ||||
|     let next = Url::parse("http://bar/baz").unwrap(); | ||||
|     assert_eq!( | ||||
|         check_redirect(&policy, StatusCode::Found, &next, &[]), | ||||
|         check_redirect(&policy, StatusCode::FOUND, &next, &[]), | ||||
|         Action::Follow | ||||
|     ); | ||||
|  | ||||
|     let next = Url::parse("http://foo/baz").unwrap(); | ||||
|     assert_eq!( | ||||
|         check_redirect(&policy, StatusCode::Found, &next, &[]), | ||||
|         check_redirect(&policy, StatusCode::FOUND, &next, &[]), | ||||
|         Action::Stop | ||||
|     ); | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_remove_sensitive_headers() { | ||||
|     use hyper::header::{Accept, Authorization, Cookie}; | ||||
|     use hyper::header::{ACCEPT, AUTHORIZATION, COOKIE, HeaderValue}; | ||||
|  | ||||
|     let mut headers = Headers::new(); | ||||
|     headers.set(Accept::star()); | ||||
|     headers.set(Authorization("let me in".to_owned())); | ||||
|     let mut cookie = Cookie::new(); | ||||
|     cookie.set("foo", "bar"); | ||||
|     headers.set(cookie); | ||||
|     let mut headers = HeaderMap::new(); | ||||
|     headers.insert(ACCEPT, HeaderValue::from_static("*/*")); | ||||
|     headers.insert(AUTHORIZATION, HeaderValue::from_static("let me in")); | ||||
|     headers.insert(COOKIE, HeaderValue::from_static("foo=bar")); | ||||
|  | ||||
|     let next = Url::parse("http://initial-domain.com/path").unwrap(); | ||||
|     let mut prev = vec![Url::parse("http://initial-domain.com/new_path").unwrap()]; | ||||
| @@ -322,8 +320,8 @@ fn test_remove_sensitive_headers() { | ||||
|     assert_eq!(headers, filtered_headers); | ||||
|  | ||||
|     prev.push(Url::parse("http://new-domain.com/path").unwrap()); | ||||
|     filtered_headers.remove::<Authorization<String>>(); | ||||
|     filtered_headers.remove::<Cookie>(); | ||||
|     filtered_headers.remove(AUTHORIZATION); | ||||
|     filtered_headers.remove(COOKIE); | ||||
|  | ||||
|     remove_sensitive_headers(&mut headers, &next, &prev); | ||||
|     assert_eq!(headers, filtered_headers); | ||||
|   | ||||
							
								
								
									
										104
									
								
								src/request.rs
									
									
									
									
									
								
							
							
						
						
									
										104
									
								
								src/request.rs
									
									
									
									
									
								
							| @@ -1,12 +1,13 @@ | ||||
| use std::fmt; | ||||
|  | ||||
| use hyper::header::ContentType; | ||||
| use base64::encode; | ||||
| use serde::Serialize; | ||||
| use serde_json; | ||||
| use serde_urlencoded; | ||||
|  | ||||
| use body::{self, Body}; | ||||
| use header::Headers; | ||||
| use header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE}; | ||||
| use http::HttpTryFrom; | ||||
| use {async_impl, Client, Method, Url}; | ||||
|  | ||||
| /// A request which can be executed with `Client::execute()`. | ||||
| @@ -58,13 +59,13 @@ impl Request { | ||||
|  | ||||
|     /// Get the headers. | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &Headers { | ||||
|     pub fn headers(&self) -> &HeaderMap { | ||||
|         self.inner.headers() | ||||
|     } | ||||
|  | ||||
|     /// Get a mutable reference to the headers. | ||||
|     #[inline] | ||||
|     pub fn headers_mut(&mut self) -> &mut Headers { | ||||
|     pub fn headers_mut(&mut self) -> &mut HeaderMap { | ||||
|         self.inner.headers_mut() | ||||
|     } | ||||
|  | ||||
| @@ -85,22 +86,31 @@ impl RequestBuilder { | ||||
|     /// Add a `Header` to this Request. | ||||
|     /// | ||||
|     /// ```rust | ||||
|     /// use reqwest::header::UserAgent; | ||||
|     /// use reqwest::header::USER_AGENT; | ||||
|     /// | ||||
|     /// # fn run() -> Result<(), Box<::std::error::Error>> { | ||||
|     /// let client = reqwest::Client::new(); | ||||
|     /// let res = client.get("https://www.rust-lang.org") | ||||
|     ///     .header(UserAgent::new("foo")) | ||||
|     ///     .header(USER_AGENT, "foo") | ||||
|     ///     .send()?; | ||||
|     /// # Ok(()) | ||||
|     /// # } | ||||
|     /// ``` | ||||
|     pub fn header<H>(&mut self, header: H) -> &mut RequestBuilder | ||||
|     pub fn header<K, V>(&mut self, key: K, value: V) -> &mut RequestBuilder | ||||
|     where | ||||
|         H: ::header::Header, | ||||
|         HeaderName: HttpTryFrom<K>, | ||||
|         HeaderValue: HttpTryFrom<V>, | ||||
|     { | ||||
|         if let Some(req) = request_mut(&mut self.request, &self.err) { | ||||
|             req.headers_mut().set(header); | ||||
|             match <HeaderName as HttpTryFrom<K>>::try_from(key) { | ||||
|                 Ok(key) => { | ||||
|                     match <HeaderValue as HttpTryFrom<V>>::try_from(value) { | ||||
|                         Ok(value) => { req.headers_mut().append(key, value); } | ||||
|                         Err(e) => self.err = Some(::error::from(e.into())), | ||||
|                     } | ||||
|                 }, | ||||
|                 Err(e) => self.err = Some(::error::from(e.into())), | ||||
|             }; | ||||
|         } | ||||
|         self | ||||
|     } | ||||
| @@ -110,13 +120,13 @@ impl RequestBuilder { | ||||
|     /// The headers will be merged in to any already set. | ||||
|     /// | ||||
|     /// ```rust | ||||
|     /// use reqwest::header::{Headers, UserAgent, ContentType}; | ||||
|     /// use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT, CONTENT_TYPE}; | ||||
|     /// # use std::fs; | ||||
|     /// | ||||
|     /// fn construct_headers() -> Headers { | ||||
|     ///     let mut headers = Headers::new(); | ||||
|     ///     headers.set(UserAgent::new("reqwest")); | ||||
|     ///     headers.set(ContentType::png()); | ||||
|     /// fn construct_headers() -> HeaderMap { | ||||
|     ///     let mut headers = HeaderMap::new(); | ||||
|     ///     headers.insert(USER_AGENT, HeaderValue::from_static("reqwest")); | ||||
|     ///     headers.insert(CONTENT_TYPE, HeaderValue::from_static("image/png")); | ||||
|     ///     headers | ||||
|     /// } | ||||
|     /// | ||||
| @@ -130,9 +140,11 @@ impl RequestBuilder { | ||||
|     /// # Ok(()) | ||||
|     /// # } | ||||
|     /// ``` | ||||
|     pub fn headers(&mut self, headers: ::header::Headers) -> &mut RequestBuilder { | ||||
|     pub fn headers(&mut self, headers: ::header::HeaderMap) -> &mut RequestBuilder { | ||||
|         if let Some(req) = request_mut(&mut self.request, &self.err) { | ||||
|             req.headers_mut().extend(headers.iter()); | ||||
|             for (key, value) in headers.iter() { | ||||
|                 req.headers_mut().insert(key, value.clone()); | ||||
|             } | ||||
|         } | ||||
|         self | ||||
|     } | ||||
| @@ -153,10 +165,10 @@ impl RequestBuilder { | ||||
|         U: Into<String>, | ||||
|         P: Into<String>, | ||||
|     { | ||||
|         self.header(::header::Authorization(::header::Basic { | ||||
|             username: username.into(), | ||||
|             password: password.map(|p| p.into()), | ||||
|         })) | ||||
|         let username = username.into(); | ||||
|         let password = password.map(|p| p.into()).unwrap_or(String::new()); | ||||
|         let header_value = format!("basic {}:{}", username, encode(&password)); | ||||
|         self.header(::header::AUTHORIZATION, HeaderValue::from_str(header_value.as_str()).expect("")) | ||||
|     } | ||||
|  | ||||
|     /// Set the request body. | ||||
| @@ -283,7 +295,7 @@ impl RequestBuilder { | ||||
|         if let Some(req) = request_mut(&mut self.request, &self.err) { | ||||
|             match serde_urlencoded::to_string(form) { | ||||
|                 Ok(body) => { | ||||
|                     req.headers_mut().set(ContentType::form_url_encoded()); | ||||
|                     req.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_str(::mime::APPLICATION_WWW_FORM_URLENCODED.as_ref()).expect("")); | ||||
|                     *req.body_mut() = Some(body.into()); | ||||
|                 }, | ||||
|                 Err(err) => self.err = Some(::error::from(err)), | ||||
| @@ -321,7 +333,7 @@ impl RequestBuilder { | ||||
|         if let Some(req) = request_mut(&mut self.request, &self.err) { | ||||
|             match serde_json::to_vec(json) { | ||||
|                 Ok(body) => { | ||||
|                     req.headers_mut().set(ContentType::json()); | ||||
|                     req.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_str(::mime::APPLICATION_JSON.as_ref()).expect("")); | ||||
|                     *req.body_mut() = Some(body.into()); | ||||
|                 }, | ||||
|                 Err(err) => self.err = Some(::error::from(err)), | ||||
| @@ -333,7 +345,6 @@ impl RequestBuilder { | ||||
|     /// Sends a multipart/form-data body. | ||||
|     /// | ||||
|     /// ``` | ||||
|     /// use reqwest::mime; | ||||
|     /// # use reqwest::Error; | ||||
|     /// | ||||
|     /// # fn run() -> Result<(), Box<std::error::Error>> { | ||||
| @@ -352,10 +363,14 @@ impl RequestBuilder { | ||||
|     /// See [`multipart`](multipart/) for more examples. | ||||
|     pub fn multipart(&mut self, mut multipart: ::multipart::Form) -> &mut RequestBuilder { | ||||
|         if let Some(req) = request_mut(&mut self.request, &self.err) { | ||||
|             req.headers_mut().set( | ||||
|                 ::header::ContentType(format!("multipart/form-data; boundary={}", ::multipart_::boundary(&multipart)) | ||||
|                     .parse().unwrap() | ||||
|                 ) | ||||
|             req.headers_mut().insert( | ||||
|                 ::header::CONTENT_TYPE, | ||||
|                 HeaderValue::from_str( | ||||
|                     format!( | ||||
|                         "multipart/form-data; boundary={}", | ||||
|                         ::multipart_::boundary(&multipart) | ||||
|                     ).as_str() | ||||
|                 ).expect("") | ||||
|             ); | ||||
|             *req.body_mut() = Some(match ::multipart_::compute_length(&mut multipart) { | ||||
|                 Some(length) => Body::sized(::multipart_::reader(multipart), length), | ||||
| @@ -450,13 +465,13 @@ pub fn builder(client: Client, req: ::Result<Request>) -> RequestBuilder { | ||||
|  | ||||
| #[inline] | ||||
| pub fn async(req: Request) -> (async_impl::Request, Option<body::Sender>) { | ||||
|     use header::ContentLength; | ||||
|     use header::CONTENT_LENGTH; | ||||
|  | ||||
|     let mut req_async = req.inner; | ||||
|     let body = req.body.and_then(|body| { | ||||
|         let (tx, body, len) = body::async(body); | ||||
|         if let Some(len) = len { | ||||
|             req_async.headers_mut().set(ContentLength(len)); | ||||
|             req_async.headers_mut().insert(CONTENT_LENGTH, HeaderValue::from_str(len.to_string().as_str()).expect("")); | ||||
|         } | ||||
|         *req_async.body_mut() = Some(body); | ||||
|         tx | ||||
| @@ -467,7 +482,7 @@ pub fn async(req: Request) -> (async_impl::Request, Option<body::Sender>) { | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use {body, Client, Method}; | ||||
|     use header::{Host, Headers, ContentType}; | ||||
|     use header::{HOST, HeaderMap, HeaderValue, CONTENT_TYPE}; | ||||
|     use std::collections::{BTreeMap, HashMap}; | ||||
|     use serde_json; | ||||
|     use serde_urlencoded; | ||||
| @@ -478,7 +493,7 @@ mod tests { | ||||
|         let some_url = "https://google.com/"; | ||||
|         let r = client.get(some_url).build().unwrap(); | ||||
|  | ||||
|         assert_eq!(r.method(), &Method::Get); | ||||
|         assert_eq!(r.method(), &Method::GET); | ||||
|         assert_eq!(r.url().as_str(), some_url); | ||||
|     } | ||||
|  | ||||
| @@ -488,7 +503,7 @@ mod tests { | ||||
|         let some_url = "https://google.com/"; | ||||
|         let r = client.head(some_url).build().unwrap(); | ||||
|  | ||||
|         assert_eq!(r.method(), &Method::Head); | ||||
|         assert_eq!(r.method(), &Method::HEAD); | ||||
|         assert_eq!(r.url().as_str(), some_url); | ||||
|     } | ||||
|  | ||||
| @@ -498,7 +513,7 @@ mod tests { | ||||
|         let some_url = "https://google.com/"; | ||||
|         let r = client.post(some_url).build().unwrap(); | ||||
|  | ||||
|         assert_eq!(r.method(), &Method::Post); | ||||
|         assert_eq!(r.method(), &Method::POST); | ||||
|         assert_eq!(r.url().as_str(), some_url); | ||||
|     } | ||||
|  | ||||
| @@ -508,7 +523,7 @@ mod tests { | ||||
|         let some_url = "https://google.com/"; | ||||
|         let r = client.put(some_url).build().unwrap(); | ||||
|  | ||||
|         assert_eq!(r.method(), &Method::Put); | ||||
|         assert_eq!(r.method(), &Method::PUT); | ||||
|         assert_eq!(r.url().as_str(), some_url); | ||||
|     } | ||||
|  | ||||
| @@ -518,7 +533,7 @@ mod tests { | ||||
|         let some_url = "https://google.com/"; | ||||
|         let r = client.patch(some_url).build().unwrap(); | ||||
|  | ||||
|         assert_eq!(r.method(), &Method::Patch); | ||||
|         assert_eq!(r.method(), &Method::PATCH); | ||||
|         assert_eq!(r.url().as_str(), some_url); | ||||
|     } | ||||
|  | ||||
| @@ -528,7 +543,7 @@ mod tests { | ||||
|         let some_url = "https://google.com/"; | ||||
|         let r = client.delete(some_url).build().unwrap(); | ||||
|  | ||||
|         assert_eq!(r.method(), &Method::Delete); | ||||
|         assert_eq!(r.method(), &Method::DELETE); | ||||
|         assert_eq!(r.url().as_str(), some_url); | ||||
|     } | ||||
|  | ||||
| @@ -538,13 +553,13 @@ mod tests { | ||||
|         let some_url = "https://google.com/"; | ||||
|         let mut r = client.post(some_url); | ||||
|  | ||||
|         let header = Host::new("google.com", None); | ||||
|         let header = HeaderValue::from_static("google.com"); | ||||
|  | ||||
|         // Add a copy of the header to the request builder | ||||
|         let r = r.header(header.clone()).build().unwrap(); | ||||
|         let r = r.header(HOST, header.clone()).build().unwrap(); | ||||
|  | ||||
|         // then check it was actually added | ||||
|         assert_eq!(r.headers().get::<Host>(), Some(&header)); | ||||
|         assert_eq!(r.headers().get(HOST), Some(&header)); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -553,10 +568,10 @@ mod tests { | ||||
|         let some_url = "https://google.com/"; | ||||
|         let mut r = client.post(some_url); | ||||
|  | ||||
|         let header = Host::new("google.com", None); | ||||
|         let header = HeaderValue::from_static("google.com"); | ||||
|  | ||||
|         let mut headers = Headers::new(); | ||||
|         headers.set(header); | ||||
|         let mut headers = HeaderMap::new(); | ||||
|         headers.insert(HOST, header); | ||||
|  | ||||
|         // Add a copy of the headers to the request builder | ||||
|         let r = r.headers(headers.clone()).build().unwrap(); | ||||
| @@ -653,8 +668,7 @@ mod tests { | ||||
|         let mut r = r.form(&form_data).build().unwrap(); | ||||
|  | ||||
|         // Make sure the content type was set | ||||
|         assert_eq!(r.headers().get::<ContentType>(), | ||||
|                    Some(&ContentType::form_url_encoded())); | ||||
|         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(); | ||||
|  | ||||
| @@ -674,7 +688,7 @@ mod tests { | ||||
|         let mut r = r.json(&json_data).build().unwrap(); | ||||
|  | ||||
|         // Make sure the content type was set | ||||
|         assert_eq!(r.headers().get::<ContentType>(), Some(&ContentType::json())); | ||||
|         assert_eq!(r.headers().get(CONTENT_TYPE).unwrap(), &"application/json"); | ||||
|  | ||||
|         let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap(); | ||||
|  | ||||
|   | ||||
| @@ -6,17 +6,19 @@ use std::borrow::Cow; | ||||
|  | ||||
| use encoding_rs::{Encoding, UTF_8}; | ||||
| use futures::{Async, Poll, Stream}; | ||||
| use mime::Mime; | ||||
| use serde::de::DeserializeOwned; | ||||
| use serde_json; | ||||
|  | ||||
| use client::KeepCoreThreadAlive; | ||||
| use header::Headers; | ||||
| use hyper::header::HeaderMap; | ||||
| use {async_impl, StatusCode, Url, wait}; | ||||
|  | ||||
| /// A Response to a submitted `Request`. | ||||
| pub struct Response { | ||||
|     inner: async_impl::Response, | ||||
|     body: async_impl::ReadableChunks<WaitBody>, | ||||
|     content_length: Option<u64>, | ||||
|     _thread_handle: KeepCoreThreadAlive, | ||||
| } | ||||
|  | ||||
| @@ -70,8 +72,8 @@ impl Response { | ||||
|     ///             .body("possibly too large") | ||||
|     ///             .send()?; | ||||
|     /// match resp.status() { | ||||
|     ///     StatusCode::Ok => println!("success!"), | ||||
|     ///     StatusCode::PayloadTooLarge => { | ||||
|     ///     StatusCode::OK => println!("success!"), | ||||
|     ///     StatusCode::PAYLOAD_TOO_LARGE => { | ||||
|     ///         println!("Request payload is too large!"); | ||||
|     ///     } | ||||
|     ///     s => println!("Received response status: {:?}", s), | ||||
| @@ -93,14 +95,15 @@ impl Response { | ||||
|     /// ```rust | ||||
|     /// # use std::io::{Read, Write}; | ||||
|     /// # use reqwest::Client; | ||||
|     /// # use reqwest::header::ContentLength; | ||||
|     /// # use reqwest::header::CONTENT_LENGTH; | ||||
|     /// # | ||||
|     /// # fn run() -> Result<(), Box<::std::error::Error>> { | ||||
|     /// let client = Client::new(); | ||||
|     /// let mut resp = client.head("http://httpbin.org/bytes/3000").send()?; | ||||
|     /// if resp.status().is_success() { | ||||
|     ///     let len = resp.headers().get::<ContentLength>() | ||||
|     ///                 .map(|ct_len| **ct_len) | ||||
|     ///     let len = resp.headers().get(CONTENT_LENGTH) | ||||
|     ///                 .and_then(|ct_len| ct_len.to_str().ok()) | ||||
|     ///                 .and_then(|ct_len| ct_len.parse().ok()) | ||||
|     ///                 .unwrap_or(0); | ||||
|     ///     // limit 1mb response | ||||
|     ///     if len <= 1_000_000 { | ||||
| @@ -115,7 +118,7 @@ impl Response { | ||||
|     /// # } | ||||
|     /// ``` | ||||
|     #[inline] | ||||
|     pub fn headers(&self) -> &Headers { | ||||
|     pub fn headers(&self) -> &HeaderMap { | ||||
|         self.inner.headers() | ||||
|     } | ||||
|  | ||||
| @@ -189,14 +192,21 @@ impl Response { | ||||
|     /// This consumes the body. Trying to read more, or use of `response.json()` | ||||
|     /// will return empty values. | ||||
|     pub fn text(&mut self) -> ::Result<String> { | ||||
|         let len = self.headers().get::<::header::ContentLength>() | ||||
|             .map(|ct_len| **ct_len) | ||||
|             .unwrap_or(0); | ||||
|         let len = self.content_length.unwrap_or(0); | ||||
|         let mut content = Vec::with_capacity(len as usize); | ||||
|         self.read_to_end(&mut content).map_err(::error::from)?; | ||||
|         let encoding_name = self.headers().get::<::header::ContentType>() | ||||
|             .and_then(|content_type| { | ||||
|                 content_type.get_param("charset") | ||||
|         let content_type = self.headers().get(::header::CONTENT_TYPE) | ||||
|             .and_then(|value| { | ||||
|                 value.to_str().ok() | ||||
|             }) | ||||
|             .and_then(|value| { | ||||
|                 value.parse::<Mime>().ok() | ||||
|             }); | ||||
|         let encoding_name = content_type | ||||
|             .as_ref() | ||||
|             .and_then(|mime| { | ||||
|                 mime | ||||
|                     .get_param("charset") | ||||
|                     .map(|charset| charset.as_str()) | ||||
|             }) | ||||
|             .unwrap_or("utf-8"); | ||||
| @@ -252,7 +262,7 @@ impl Response { | ||||
|     /// let res = reqwest::get("http://httpbin.org/status/400")? | ||||
|     ///     .error_for_status(); | ||||
|     /// if let Err(err) = res { | ||||
|     ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BadRequest)); | ||||
|     ///     assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST)); | ||||
|     /// } | ||||
|     /// # Ok(()) | ||||
|     /// # } | ||||
| @@ -260,12 +270,13 @@ impl Response { | ||||
|     /// ``` | ||||
|     #[inline] | ||||
|     pub fn error_for_status(self) -> ::Result<Self> { | ||||
|         let Response { body, inner, _thread_handle } = self; | ||||
|         let Response { body, content_length, inner, _thread_handle } = self; | ||||
|         inner.error_for_status().map(move |inner| { | ||||
|             Response { | ||||
|                 inner: inner, | ||||
|                 body: body, | ||||
|                 _thread_handle: _thread_handle, | ||||
|                 inner, | ||||
|                 body, | ||||
|                 content_length, | ||||
|                 _thread_handle, | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| @@ -306,6 +317,7 @@ impl Stream for WaitBody { | ||||
|  | ||||
| pub fn new(mut res: async_impl::Response, timeout: Option<Duration>, thread: KeepCoreThreadAlive) -> Response { | ||||
|     let body = mem::replace(res.body_mut(), async_impl::Decoder::empty()); | ||||
|     let len = body.content_length(); | ||||
|     let body = async_impl::ReadableChunks::new(WaitBody { | ||||
|         inner: wait::stream(body, timeout) | ||||
|     }); | ||||
| @@ -313,6 +325,7 @@ pub fn new(mut res: async_impl::Response, timeout: Option<Duration>, thread: Kee | ||||
|     Response { | ||||
|         inner: res, | ||||
|         body: body, | ||||
|         content_length: len, | ||||
|         _thread_handle: thread, | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										53
									
								
								src/wait.rs
									
									
									
									
									
								
							
							
						
						
									
										53
									
								
								src/wait.rs
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ use std::sync::Arc; | ||||
| use std::thread; | ||||
| use std::time::{Duration, Instant}; | ||||
|  | ||||
| use futures::{Async, AsyncSink, Future, Sink, Stream}; | ||||
| use futures::{Async, Future, Stream}; | ||||
| use futures::executor::{self, Notify}; | ||||
|  | ||||
| // pub(crate) | ||||
| @@ -43,14 +43,6 @@ where S: Stream { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn sink<S>(sink: S, timeout: Option<Duration>) -> WaitSink<S> | ||||
| where S: Sink { | ||||
|     WaitSink { | ||||
|         sink: executor::spawn(sink), | ||||
|         timeout: timeout, | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug)] | ||||
| pub enum Waited<E> { | ||||
|     TimedOut, | ||||
| @@ -113,49 +105,6 @@ where S: Stream { | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub struct WaitSink<S> { | ||||
|     sink: executor::Spawn<S>, | ||||
|     timeout: Option<Duration>, | ||||
| } | ||||
|  | ||||
| impl<S> WaitSink<S> | ||||
| where S: Sink { | ||||
|     pub fn send(&mut self, mut item: S::SinkItem) -> Result<(), Waited<S::SinkError>> { | ||||
|         if let Some(dur) = self.timeout { | ||||
|  | ||||
|             let start = Instant::now(); | ||||
|             let deadline = start + dur; | ||||
|             let notify = Arc::new(ThreadNotify { | ||||
|                 thread: thread::current(), | ||||
|             }); | ||||
|  | ||||
|             loop { | ||||
|                 let now = Instant::now(); | ||||
|                 if now >= deadline { | ||||
|                     return Err(Waited::TimedOut); | ||||
|                 } | ||||
|                 item = match self.sink.start_send_notify(item, ¬ify, 0)? { | ||||
|                     AsyncSink::Ready => return Ok(()), | ||||
|                     AsyncSink::NotReady(val) => val, | ||||
|                 }; | ||||
|                 thread::park_timeout(deadline - now); | ||||
|             } | ||||
|         } else { | ||||
|             let notify = Arc::new(ThreadNotify { | ||||
|                 thread: thread::current(), | ||||
|             }); | ||||
|  | ||||
|             loop { | ||||
|                 item = match self.sink.start_send_notify(item, ¬ify, 0)? { | ||||
|                     AsyncSink::Ready => return Ok(()), | ||||
|                     AsyncSink::NotReady(val) => val, | ||||
|                 }; | ||||
|                 thread::park(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct ThreadNotify { | ||||
|     thread: thread::Thread, | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user