cargo fmt (#604)

Run rustfmt and setup CI to check for it.
This commit is contained in:
danieleades
2019-08-29 17:52:39 +01:00
committed by Sean McArthur
parent 81e0f1ff2a
commit cf8944a0f0
41 changed files with 1399 additions and 1378 deletions

View File

@@ -1 +0,0 @@
disable_all_formatting = true

View File

@@ -55,7 +55,9 @@ dist: trusty
env:
global:
- REQWEST_TEST_BODY_FULL=1
before_script:
- rustup component add rustfmt
script:
- cargo fmt -- --check
- cargo build $FEATURES
- cargo test -v $FEATURES -- --test-threads=1

View File

@@ -1,16 +1,11 @@
#![deny(warnings)]
extern crate futures;
extern crate reqwest;
extern crate tokio;
use std::mem;
use std::io::{self, Cursor};
use futures::{Future, Stream};
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()
.get("https://hyper.rs")
.send()
@@ -23,10 +18,9 @@ fn fetch() -> impl Future<Item=(), Error=()> {
.map_err(|err| println!("request error: {}", err))
.map(|body| {
let mut body = Cursor::new(body);
let _ = io::copy(&mut body, &mut io::stdout())
.map_err(|err| {
println!("stdout error: {}", err);
});
let _ = io::copy(&mut body, &mut io::stdout()).map_err(|err| {
println!("stdout error: {}", err);
});
})
}

View File

@@ -1,11 +1,5 @@
#![deny(warnings)]
extern crate futures;
extern crate reqwest;
extern crate tokio;
extern crate serde;
extern crate serde_json;
use futures::Future;
use reqwest::r#async::{Client, Response};
use serde::Deserialize;
@@ -21,27 +15,18 @@ struct SlideshowContainer {
slideshow: Slideshow,
}
fn fetch() -> impl Future<Item=(), Error=()> {
fn fetch() -> impl Future<Item = (), Error = ()> {
let client = Client::new();
let json = |mut res : Response | {
res.json::<SlideshowContainer>()
};
let json = |mut res: Response| res.json::<SlideshowContainer>();
let request1 =
client
.get("https://httpbin.org/json")
.send()
.and_then(json);
let request1 = client.get("https://httpbin.org/json").send().and_then(json);
let request2 =
client
.get("https://httpbin.org/json")
.send()
.and_then(json);
let request2 = client.get("https://httpbin.org/json").send().and_then(json);
request1.join(request2)
.map(|(res1, res2)|{
request1
.join(request2)
.map(|(res1, res2)| {
println!("{:?}", res1);
println!("{:?}", res2);
})

View File

@@ -1,18 +1,11 @@
#![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::mem;
use std::path::Path;
use bytes::Bytes;
use futures::{Async, Future, Poll, Stream};
use futures::{try_ready, Async, Future, Poll, Stream};
use reqwest::r#async::{Client, Decoder};
use tokio::fs::File;
use tokio::io::AsyncRead;

View File

@@ -1,5 +1,3 @@
extern crate reqwest;
fn main() {
reqwest::Client::new()
.post("http://www.baidu.com")

View File

@@ -3,19 +3,16 @@
//! 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
//! process it at runtime.
extern crate reqwest;
#[macro_use] extern crate serde_json;
use serde_json::json;
fn main() -> Result<(), reqwest::Error> {
let echo_json: serde_json::Value = reqwest::Client::new()
.post("https://jsonplaceholder.typicode.com/posts")
.json(
&json!({
"title": "Reqwest.rs",
"body": "https://docs.rs/reqwest",
"userId": 1
})
)
.json(&json!({
"title": "Reqwest.rs",
"body": "https://docs.rs/reqwest",
"userId": 1
}))
.send()?
.json()?;

View File

@@ -3,9 +3,6 @@
//! 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
//! more code.
extern crate reqwest;
extern crate serde;
extern crate serde_json;
use serde::{Deserialize, Serialize};
@@ -23,7 +20,7 @@ fn main() -> Result<(), reqwest::Error> {
id: None,
title: "Reqwest.rs".into(),
body: "https://docs.rs/reqwest".into(),
user_id: 1
user_id: 1,
};
let new_post: Post = reqwest::Client::new()
.post("https://jsonplaceholder.typicode.com/posts")

View File

@@ -2,9 +2,6 @@
//! `cargo run --example simple`
extern crate reqwest;
extern crate env_logger;
fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();

View File

@@ -1,6 +1,6 @@
pub use self::body::{Body, Chunk};
pub use self::decoder::{Decoder, ReadableChunks};
pub use self::client::{Client, ClientBuilder};
pub use self::decoder::{Decoder, ReadableChunks};
pub use self::request::{Request, RequestBuilder};
pub use self::response::{Response, ResponseBuilderExt};

View File

@@ -1,7 +1,7 @@
use std::fmt;
use futures::{Future, Stream, Poll, Async, try_ready};
use bytes::{Buf, Bytes};
use futures::{try_ready, Async, Future, Poll, Stream};
use hyper::body::Payload;
use tokio::timer::Delay;
@@ -15,7 +15,7 @@ enum Inner {
Hyper {
body: hyper::Body,
timeout: Option<Delay>,
}
},
}
impl Body {
@@ -29,10 +29,7 @@ impl Body {
#[inline]
pub(crate) fn response(body: hyper::Body, timeout: Option<Delay>) -> Body {
Body {
inner: Inner::Hyper {
body,
timeout,
},
inner: Inner::Hyper { body, timeout },
}
}
@@ -65,7 +62,7 @@ impl Body {
Inner::Hyper { body, timeout } => {
debug_assert!(timeout.is_none());
(None, body)
},
}
}
}
}
@@ -77,14 +74,17 @@ impl Stream for Body {
#[inline]
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
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 Async::Ready(()) = try_!(timeout.poll()) {
return Err(crate::error::timedout(None));
}
}
try_ready!(body.poll_data().map_err(crate::error::from))
},
}
Inner::Reusable(ref mut bytes) => {
return if bytes.is_empty() {
Ok(Async::Ready(None))
@@ -93,12 +93,10 @@ impl Stream for Body {
*bytes = Bytes::new();
Ok(Async::Ready(Some(chunk)))
};
},
}
};
Ok(Async::Ready(opt.map(|chunk| Chunk {
inner: chunk,
})))
Ok(Async::Ready(opt.map(|chunk| Chunk { inner: chunk })))
}
}
@@ -161,7 +159,7 @@ impl Chunk {
#[inline]
pub(crate) fn from_chunk(chunk: Bytes) -> 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 {
fn extend<T>(&mut self, iter: T)
where T: IntoIterator<Item=u8> {
where
T: IntoIterator<Item = u8>,
{
self.inner.extend(iter)
}
}
@@ -219,7 +219,9 @@ impl From<Vec<u8>> for Chunk {
impl From<&'static [u8]> for 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 {
fn from(slice: &'static str) -> Chunk {
Chunk { inner: slice.into() }
Chunk {
inner: slice.into(),
}
}
}
impl From<Bytes> for 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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Body")
.finish()
f.debug_struct("Body").finish()
}
}

View File

@@ -1,26 +1,14 @@
use std::{fmt, str};
use std::net::IpAddr;
use std::sync::{Arc, RwLock};
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 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 hyper::client::ResponseFuture;
use mime;
@@ -28,24 +16,22 @@ use mime;
use native_tls::TlsConnector;
use tokio::{clock, timer::Delay};
use log::{debug};
use log::debug;
use super::request::{Request, RequestBuilder};
use super::response::Response;
use crate::connect::Connector;
use crate::into_url::{expect_uri, try_uri};
use crate::cookie;
use crate::redirect::{self, RedirectPolicy, remove_sensitive_headers};
use crate::{IntoUrl, Method, Proxy, StatusCode, Url};
use crate::into_url::{expect_uri, try_uri};
use crate::proxy::get_proxies;
#[cfg(feature = "tls")]
use crate::{Certificate, Identity};
use crate::redirect::{self, remove_sensitive_headers, RedirectPolicy};
#[cfg(feature = "tls")]
use crate::tls::TlsBackend;
#[cfg(feature = "tls")]
use crate::{Certificate, Identity};
use crate::{IntoUrl, Method, Proxy, StatusCode, Url};
static DEFAULT_USER_AGENT: &str =
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
static DEFAULT_USER_AGENT: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
/// An asynchronous `Client` to make Requests with.
///
@@ -98,7 +84,10 @@ impl ClientBuilder {
pub fn new() -> ClientBuilder {
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"));
headers.insert(
ACCEPT,
HeaderValue::from_str(mime::STAR_STAR.as_ref()).expect("unable to parse mime"),
);
ClientBuilder {
config: Config {
@@ -156,8 +145,13 @@ impl ClientBuilder {
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")]
TlsBackend::Rustls => {
use crate::tls::NoVerifier;
@@ -166,15 +160,14 @@ impl ClientBuilder {
if config.http2_only {
tls.set_protocols(&["h2".into()]);
} else {
tls.set_protocols(&[
"h2".into(),
"http/1.1".into(),
]);
tls.set_protocols(&["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 {
tls.dangerous().set_certificate_verifier(Arc::new(NoVerifier));
tls.dangerous()
.set_certificate_verifier(Arc::new(NoVerifier));
}
for cert in config.root_certs {
@@ -185,7 +178,12 @@ impl ClientBuilder {
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 proxies_maybe_http_auth = proxies
.iter()
.any(|p| p.maybe_has_http_auth());
let proxies_maybe_http_auth = proxies.iter().any(|p| p.maybe_has_http_auth());
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
/// significant vulnerability to man-in-the-middle attacks.
#[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
}
@@ -299,7 +298,6 @@ impl ClientBuilder {
self
}
/// Sets the default headers for every request.
pub fn default_headers(mut self, headers: HeaderMap) -> ClientBuilder {
for (key, value) in headers.iter() {
@@ -349,7 +347,6 @@ impl ClientBuilder {
self
}
/// Set a `RedirectPolicy` for this client.
///
/// 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`
/// instead of panicking.
pub fn new() -> Client {
ClientBuilder::new()
.build()
.expect("Client::new()")
ClientBuilder::new().build().expect("Client::new()")
}
/// Creates a `ClientBuilder` to configure a `Client`.
@@ -529,9 +524,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
let req = url
.into_url()
.map(move |url| Request::new(method, url));
let req = url.into_url().map(move |url| Request::new(method, url));
RequestBuilder::new(self.clone(), req)
}
@@ -551,14 +544,8 @@ impl Client {
self.execute_request(request)
}
pub(super) fn execute_request(&self, req: Request) -> Pending {
let (
method,
url,
mut headers,
body
) = req.pieces();
let (method, url, mut headers, body) = req.pieces();
// insert default headers in the request headers
// without overwriting already appended headers.
@@ -576,9 +563,8 @@ impl Client {
}
}
if self.inner.gzip &&
!headers.contains_key(ACCEPT_ENCODING) &&
!headers.contains_key(RANGE) {
if self.inner.gzip && !headers.contains_key(ACCEPT_ENCODING) && !headers.contains_key(RANGE)
{
headers.insert(ACCEPT_ENCODING, HeaderValue::from_static("gzip"));
}
@@ -588,10 +574,8 @@ impl Client {
Some(body) => {
let (reusable, body) = body.into_hyper();
(Some(reusable), body)
},
None => {
(None, hyper::Body::empty())
}
None => (None, hyper::Body::empty()),
};
self.proxy_auth(&uri, &mut headers);
@@ -606,9 +590,10 @@ impl Client {
let in_flight = self.inner.hyper.request(req);
let timeout = self.inner.request_timeout.map(|dur| {
Delay::new(clock::now() + dur)
});
let timeout = self
.inner
.request_timeout
.map(|dur| Delay::new(clock::now() + dur));
Pending {
inner: PendingInner::Request(PendingRequest {
@@ -643,14 +628,10 @@ impl Client {
return;
}
for proxy in self.inner.proxies.iter() {
if proxy.is_match(dst) {
if let Some(header) = proxy.http_basic_auth(dst) {
headers.insert(
PROXY_AUTHORIZATION,
header,
);
headers.insert(PROXY_AUTHORIZATION, header);
}
break;
@@ -671,8 +652,7 @@ impl fmt::Debug for Client {
impl fmt::Debug for ClientBuilder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ClientBuilder")
.finish()
f.debug_struct("ClientBuilder").finish()
}
}
@@ -726,7 +706,9 @@ impl Future for Pending {
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.inner {
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,56 +737,58 @@ impl Future for PendingRequest {
store.0.store_response_cookies(cookies, &self.url);
}
let should_redirect = match res.status() {
StatusCode::MOVED_PERMANENTLY |
StatusCode::FOUND |
StatusCode::SEE_OTHER => {
StatusCode::MOVED_PERMANENTLY | StatusCode::FOUND | StatusCode::SEE_OTHER => {
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);
}
match self.method {
Method::GET | Method::HEAD => {},
Method::GET | Method::HEAD => {}
_ => {
self.method = Method::GET;
}
}
true
},
StatusCode::TEMPORARY_REDIRECT |
StatusCode::PERMANENT_REDIRECT => match self.body {
Some(Some(_)) | None => true,
Some(None) => false,
},
}
StatusCode::TEMPORARY_REDIRECT | StatusCode::PERMANENT_REDIRECT => {
match self.body {
Some(Some(_)) | None => true,
Some(None) => false,
}
}
_ => false,
};
if should_redirect {
let loc = res.headers()
.get(LOCATION)
.and_then(|val| {
let loc = (|| -> Option<Url> {
// Some sites may send a utf-8 Location header,
// even though we're supposed to treat those bytes
// as opaque, we'll check specifically for utf8.
self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
})();
let loc = res.headers().get(LOCATION).and_then(|val| {
let loc = (|| -> Option<Url> {
// Some sites may send a utf-8 Location header,
// even though we're supposed to treat those bytes
// as opaque, we'll check specifically for utf8.
self.url.join(str::from_utf8(val.as_bytes()).ok()?).ok()
})();
// Check that the `url` is also a valid `http::Uri`.
//
// If not, just log it and skip the redirect.
let loc = loc.and_then(|url| {
if try_uri(&url).is_some() {
Some(url)
} else {
None
}
});
if loc.is_none() {
debug!("Location header had invalid URI: {:?}", val);
// Check that the `url` is also a valid `http::Uri`.
//
// If not, just log it and skip the redirect.
let loc = loc.and_then(|url| {
if try_uri(&url).is_some() {
Some(url)
} else {
None
}
loc
});
if loc.is_none() {
debug!("Location header had invalid URI: {:?}", val);
}
loc
});
if let Some(loc) = loc {
if self.client.referer {
if let Some(referer) = make_referer(&loc, &self.url) {
@@ -812,11 +796,10 @@ impl Future for PendingRequest {
}
}
self.urls.push(self.url.clone());
let action = self.client.redirect_policy.check(
res.status(),
&loc,
&self.urls,
);
let action = self
.client
.redirect_policy
.check(res.status(), &loc, &self.urls);
match action {
redirect::Action::Follow => {
@@ -844,13 +827,13 @@ impl Future for PendingRequest {
*req.headers_mut() = self.headers.clone();
self.in_flight = self.client.hyper.request(req);
continue;
},
}
redirect::Action::Stop => {
debug!("redirect_policy disallowed redirection to '{}'", loc);
},
}
redirect::Action::LoopDetected => {
return Err(crate::error::loop_detected(self.url.clone()));
},
}
redirect::Action::TooManyRedirects => {
return Err(crate::error::too_many_redirects(self.url.clone()));
}
@@ -866,17 +849,12 @@ impl Future for PendingRequest {
impl fmt::Debug for Pending {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.inner {
PendingInner::Request(ref req) => {
f.debug_struct("Pending")
.field("method", &req.method)
.field("url", &req.url)
.finish()
},
PendingInner::Error(ref err) => {
f.debug_struct("Pending")
.field("error", err)
.finish()
}
PendingInner::Request(ref req) => f
.debug_struct("Pending")
.field("method", &req.method)
.field("url", &req.url)
.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() {
headers.insert(
crate::header::COOKIE,
HeaderValue::from_bytes(header.as_bytes()).unwrap()
HeaderValue::from_bytes(header.as_bytes()).unwrap(),
);
}
}

View File

@@ -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
*/
use std::fmt;
use std::mem;
use std::cmp;
use std::fmt;
use std::io::{self, Read};
use std::mem;
use bytes::{Buf, BufMut, BytesMut};
use flate2::read::GzDecoder;
use futures::{Async, Future, Poll, Stream};
use hyper::{HeaderMap};
use hyper::header::{CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING};
use hyper::HeaderMap;
use log::{warn};
use log::warn;
use super::{Body, Chunk};
use crate::error;
@@ -42,7 +42,7 @@ const INIT_BUFFER_SIZE: usize = 8192;
///
/// The inner decoder may be constructed asynchronously.
pub struct Decoder {
inner: Inner
inner: Inner,
}
enum Inner {
@@ -51,7 +51,7 @@ enum Inner {
/// A `Gzip` decoder will uncompress the gzipped response content before returning it.
Gzip(Gzip),
/// 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.
@@ -68,8 +68,7 @@ struct Gzip {
impl fmt::Debug for Decoder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Decoder")
.finish()
f.debug_struct("Decoder").finish()
}
}
@@ -80,7 +79,7 @@ impl Decoder {
#[inline]
pub fn empty() -> Decoder {
Decoder {
inner: Inner::PlainText(Body::empty())
inner: Inner::PlainText(Body::empty()),
}
}
@@ -90,7 +89,7 @@ impl Decoder {
#[inline]
fn plain_text(body: Body) -> Decoder {
Decoder {
inner: Inner::PlainText(body)
inner: Inner::PlainText(body),
}
}
@@ -100,7 +99,9 @@ impl Decoder {
#[inline]
fn gzip(body: Body) -> Decoder {
Decoder {
inner: Inner::Pending(Pending { body: ReadableChunks::new(body) })
inner: Inner::Pending(Pending {
body: ReadableChunks::new(body),
}),
}
}
@@ -120,11 +121,11 @@ impl Decoder {
.get_all(CONTENT_ENCODING)
.iter()
.any(|enc| enc == "gzip");
content_encoding_gzip ||
headers
.get_all(TRANSFER_ENCODING)
.iter()
.any(|enc| enc == "gzip")
content_encoding_gzip
|| headers
.get_all(TRANSFER_ENCODING)
.iter()
.any(|enc| enc == "gzip")
};
if is_gzip {
if let Some(content_length) = headers.get(CONTENT_LENGTH) {
@@ -153,15 +154,13 @@ impl Stream for Decoder {
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
// Do a read or poll for a pendidng decoder value.
let new_value = match self.inner {
Inner::Pending(ref mut future) => {
match future.poll() {
Ok(Async::Ready(inner)) => inner,
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(e) => return Err(e)
}
Inner::Pending(ref mut future) => match future.poll() {
Ok(Async::Ready(inner)) => inner,
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(e) => return Err(e),
},
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;
@@ -177,13 +176,13 @@ impl Future for Pending {
let body_state = match self.body.poll_stream() {
Ok(Async::Ready(state)) => state,
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()));
match body_state {
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.
HasMore,
/// No more bytes can be read from the stream.
Eof
Eof,
}
impl<S> ReadableChunks<S> {
@@ -273,8 +272,7 @@ impl<S> ReadableChunks<S> {
impl<S> fmt::Debug for ReadableChunks<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ReadableChunks")
.finish()
f.debug_struct("ReadableChunks").finish()
}
}
@@ -296,20 +294,12 @@ where
} else {
return Ok(len);
}
},
ReadState::NotReady => {
match self.poll_stream() {
Ok(Async::Ready(StreamState::HasMore)) => continue,
Ok(Async::Ready(StreamState::Eof)) => {
return Ok(0)
},
Ok(Async::NotReady) => {
return Err(io::ErrorKind::WouldBlock.into())
},
Err(e) => {
return Err(error::into_io(e))
}
}
}
ReadState::NotReady => match self.poll_stream() {
Ok(Async::Ready(StreamState::HasMore)) => continue,
Ok(Async::Ready(StreamState::Eof)) => return Ok(0),
Ok(Async::NotReady) => return Err(io::ErrorKind::WouldBlock.into()),
Err(e) => return Err(error::into_io(e)),
},
ReadState::Eof => return Ok(0),
}
@@ -320,7 +310,8 @@ where
}
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.
///
@@ -332,16 +323,14 @@ impl<S> ReadableChunks<S>
self.state = ReadState::Ready(chunk);
Ok(Async::Ready(StreamState::HasMore))
},
}
Ok(Async::Ready(None)) => {
self.state = ReadState::Eof;
Ok(Async::Ready(StreamState::Eof))
},
Ok(Async::NotReady) => {
Ok(Async::NotReady)
},
Err(e) => Err(e)
}
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(e) => Err(e),
}
}
}

View File

@@ -2,10 +2,10 @@
use std::borrow::Cow;
use std::fmt;
use http::HeaderMap;
use mime_guess::Mime;
use url::percent_encoding::{self, EncodeSet, PATH_SEGMENT_ENCODE_SET};
use uuid::Uuid;
use http::HeaderMap;
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.
pub(crate) fn stream(mut self) -> hyper::Body {
if self.inner.fields.is_empty(){
if self.inner.fields.is_empty() {
return hyper::Body::empty();
}
@@ -117,7 +117,7 @@ impl Form {
hyper::Body::wrap_stream(stream.chain(last))
}
/// Generate a hyper::Body stream for a single Part instance of a Form request.
/// Generate a hyper::Body stream for a single Part instance of a Form request.
pub(crate) fn part_stream<T>(&mut self, name: T, part: Part) -> hyper::Body
where
T: Into<Cow<'static, str>>,
@@ -126,12 +126,20 @@ impl Form {
let boundary = hyper::Body::from(format!("--{}\r\n", self.boundary()));
// append headers
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
});
// 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> {
@@ -188,7 +196,9 @@ impl Part {
T::Item: Into<Chunk>,
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 {
@@ -306,7 +316,13 @@ impl<P: PartProps> FormParts<P> {
// 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
// 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,
}
@@ -340,7 +356,7 @@ impl PartMetadata {
PartMetadata {
mime: None,
file_name: None,
headers: HeaderMap::default()
headers: HeaderMap::default(),
}
}
@@ -358,11 +374,10 @@ impl PartMetadata {
}
}
impl PartMetadata {
pub(crate) fn fmt_fields<'f, 'fa, 'fb>(
&self,
debug_struct: &'f mut fmt::DebugStruct<'fa, 'fb>
debug_struct: &'f mut fmt::DebugStruct<'fa, 'fb>,
) -> &'f mut fmt::DebugStruct<'fa, 'fb> {
debug_struct
.field("mime", &self.mime)
@@ -377,22 +392,24 @@ pub(crate) struct AttrCharEncodeSet;
impl EncodeSet for AttrCharEncodeSet {
fn contains(&self, ch: u8) -> bool {
match ch as char {
'!' => false,
'#' => false,
'$' => false,
'&' => false,
'+' => false,
'-' => false,
'.' => false,
'^' => false,
'_' => false,
'`' => false,
'|' => false,
'~' => false,
_ => {
let is_alpha_numeric = ch >= 0x41 && ch <= 0x5a || ch >= 0x61 && ch <= 0x7a || ch >= 0x30 && ch <= 0x39;
!is_alpha_numeric
}
'!' => false,
'#' => false,
'$' => false,
'&' => false,
'+' => false,
'-' => false,
'.' => false,
'^' => false,
'_' => false,
'`' => false,
'|' => false,
'~' => false,
_ => {
let is_alpha_numeric = ch >= 0x41 && ch <= 0x5a
|| ch >= 0x61 && ch <= 0x7a
|| ch >= 0x30 && ch <= 0x39;
!is_alpha_numeric
}
}
}
}
@@ -417,36 +434,38 @@ impl PercentEncoding {
None => "".to_string(),
},
);
field.headers.iter().fold(s.into_bytes(), |mut header, (k,v)| {
header.extend_from_slice(b"\r\n");
header.extend_from_slice(k.as_str().as_bytes());
header.extend_from_slice(b": ");
header.extend_from_slice(v.as_bytes());
header
})
field
.headers
.iter()
.fold(s.into_bytes(), |mut header, (k, v)| {
header.extend_from_slice(b"\r\n");
header.extend_from_slice(k.as_str().as_bytes());
header.extend_from_slice(b": ");
header.extend_from_slice(v.as_bytes());
header
})
}
// According to RFC7578 Section 4.2, `filename*=` syntax is invalid.
// See https://github.com/seanmonstar/reqwest/issues/419.
fn format_filename(&self, filename: &str) -> String {
let legal_filename = filename.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\r", "\\\r")
.replace("\n", "\\\n");
let legal_filename = filename
.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\r", "\\\r")
.replace("\n", "\\\n");
format!("filename=\"{}\"", legal_filename)
}
fn format_parameter(&self, name: &str, value: &str) -> String {
let legal_value = match *self {
PercentEncoding::PathSegment => {
percent_encoding::utf8_percent_encode(value, PATH_SEGMENT_ENCODE_SET)
.to_string()
},
percent_encoding::utf8_percent_encode(value, PATH_SEGMENT_ENCODE_SET).to_string()
}
PercentEncoding::AttrChar => {
percent_encoding::utf8_percent_encode(value, AttrCharEncodeSet)
.to_string()
},
PercentEncoding::NoOp => { value.to_string() },
percent_encoding::utf8_percent_encode(value, AttrCharEncodeSet).to_string()
}
PercentEncoding::NoOp => value.to_string(),
};
if value.len() == legal_value.len() {
// nothing has been percent encoded
@@ -477,38 +496,45 @@ mod tests {
#[test]
fn stream_to_end() {
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(
"key2",
Part::text("value2").mime(mime::IMAGE_BMP),
"reader1",
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(
"key3",
Part::text("value3").file_name("filename"),
);
"reader2",
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();
let expected = "--boundary\r\n\
Content-Disposition: form-data; name=\"reader1\"\r\n\r\n\
part1\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key1\"\r\n\r\n\
value1\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key2\"\r\n\
Content-Type: image/bmp\r\n\r\n\
value2\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"reader2\"\r\n\r\n\
part2\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key3\"; filename=\"filename\"\r\n\r\n\
value3\r\n--boundary--\r\n";
let expected =
"--boundary\r\n\
Content-Disposition: form-data; name=\"reader1\"\r\n\r\n\
part1\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key1\"\r\n\r\n\
value1\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key2\"\r\n\
Content-Type: image/bmp\r\n\r\n\
value2\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"reader2\"\r\n\r\n\
part2\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key3\"; filename=\"filename\"\r\n\r\n\
value3\r\n--boundary--\r\n";
let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt");
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
println!(
"START REAL\n{}\nEND REAL",
@@ -534,7 +560,9 @@ mod tests {
let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt");
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
println!(
"START REAL\n{}\nEND REAL",

View File

@@ -1,18 +1,18 @@
use std::fmt;
use base64::{encode};
use base64::encode;
use futures::Future;
use serde::Serialize;
use serde_json;
use serde_urlencoded;
use super::body::{Body};
use super::body::Body;
use super::client::{Client, Pending};
use super::multipart;
use super::response::Response;
use crate::header::{CONTENT_LENGTH, CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue};
use http::HttpTryFrom;
use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_LENGTH, CONTENT_TYPE};
use crate::{Method, Url};
use http::HttpTryFrom;
/// A request which can be executed with `Client::execute()`.
pub struct Request {
@@ -95,10 +95,7 @@ impl Request {
impl RequestBuilder {
pub(super) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
RequestBuilder {
client,
request,
}
RequestBuilder { client, request }
}
/// Add a `Header` to this Request.
@@ -110,11 +107,11 @@ impl RequestBuilder {
let mut error = None;
if let Ok(ref mut req) = self.request {
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) => error = Some(crate::error::from(e.into())),
Ok(key) => match <HeaderValue as HttpTryFrom<V>>::try_from(value) {
Ok(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())),
};
@@ -168,7 +165,7 @@ impl RequestBuilder {
{
let auth = match password {
Some(password) => format!("{}:{}", username, password),
None => format!("{}:", username)
None => format!("{}:", username),
};
let header_value = format!("Basic {}", encode(&auth));
self.header(crate::header::AUTHORIZATION, &*header_value)
@@ -221,10 +218,7 @@ impl RequestBuilder {
pub fn multipart(self, mut multipart: multipart::Form) -> RequestBuilder {
let mut builder = self.header(
CONTENT_TYPE,
format!(
"multipart/form-data; boundary={}",
multipart.boundary()
).as_str()
format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
);
builder = match multipart.compute_length() {
@@ -286,10 +280,10 @@ impl RequestBuilder {
Ok(body) => {
req.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("application/x-www-form-urlencoded")
HeaderValue::from_static("application/x-www-form-urlencoded"),
);
*req.body_mut() = Some(body.into());
},
}
Err(err) => error = Some(crate::error::from(err)),
}
}
@@ -310,12 +304,10 @@ impl RequestBuilder {
if let Ok(ref mut req) = self.request {
match serde_json::to_vec(json) {
Ok(body) => {
req.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("application/json")
);
req.headers_mut()
.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
*req.body_mut() = Some(body.into());
},
}
Err(err) => error = Some(crate::error::from(err)),
}
}
@@ -368,8 +360,7 @@ impl RequestBuilder {
impl fmt::Debug for Request {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_request_fields(&mut f.debug_struct("Request"), self)
.finish()
fmt_request_fields(&mut f.debug_struct("Request"), self).finish()
}
}
@@ -377,27 +368,22 @@ impl fmt::Debug for RequestBuilder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut builder = f.debug_struct("RequestBuilder");
match self.request {
Ok(ref req) => {
fmt_request_fields(&mut builder, req)
.finish()
},
Err(ref err) => {
builder
.field("error", err)
.finish()
}
Ok(ref req) => fmt_request_fields(&mut builder, req).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)
.field("url", &req.url)
.field("headers", &req.headers)
}
pub(crate) fn replace_headers(dst: &mut HeaderMap, src: HeaderMap) {
// IntoIter of HeaderMap yields (Option<HeaderName>, HeaderValue).
// 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
@@ -413,11 +399,11 @@ pub(crate) fn replace_headers(dst: &mut HeaderMap, src: HeaderMap) {
Some(key) => {
dst.insert(key.clone(), value);
prev_name = Some(key);
},
}
None => match prev_name {
Some(ref key) => {
dst.append(key.clone(), value);
},
}
None => unreachable!("HeaderMap::into_iter yielded None first"),
},
}
@@ -427,8 +413,8 @@ pub(crate) fn replace_headers(dst: &mut HeaderMap, src: HeaderMap) {
#[cfg(test)]
mod tests {
use super::Client;
use std::collections::BTreeMap;
use serde::Serialize;
use std::collections::BTreeMap;
#[test]
fn add_query_append() {
@@ -467,7 +453,10 @@ mod tests {
let some_url = "https://google.com/";
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(&params);
@@ -510,11 +499,7 @@ mod tests {
assert_eq!(req.headers()["im-a"], "keeper");
let foo = req
.headers()
.get_all("foo")
.iter()
.collect::<Vec<_>>();
let foo = req.headers().get_all("foo").iter().collect::<Vec<_>>();
assert_eq!(foo.len(), 2);
assert_eq!(foo[0], "bar");
assert_eq!(foo[1], "baz");

View File

@@ -1,28 +1,26 @@
use std::fmt;
use std::mem;
use std::marker::PhantomData;
use std::net::SocketAddr;
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 futures::{Async, Future, Poll, Stream, try_ready};
use futures::stream::Concat2;
use futures::{try_ready, Async, Future, Poll, Stream};
use http;
use hyper::{HeaderMap, StatusCode, Version};
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 tokio::timer::Delay;
use serde::de::DeserializeOwned;
use serde_json;
use tokio::timer::Delay;
use url::Url;
use log::{debug};
use crate::cookie;
use super::Decoder;
use super::body::Body;
use super::Decoder;
use crate::cookie;
/// A Response to a submitted `Request`.
pub struct Response {
@@ -37,7 +35,12 @@ pub struct 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 status = parts.status;
let version = parts.version;
@@ -57,7 +60,6 @@ impl Response {
}
}
/// Get the `StatusCode` of this `Response`.
#[inline]
pub fn status(&self) -> StatusCode {
@@ -77,11 +79,10 @@ impl Response {
}
/// Retrieve the cookies contained in the response.
///
///
/// Note that invalid 'Set-Cookie' headers will be ignored.
pub fn cookies<'a>(&'a self) -> impl Iterator<Item = cookie::Cookie<'a>> + 'a {
cookie::extract_response_cookies(&self.headers)
.filter_map(Result::ok)
cookie::extract_response_cookies(&self.headers).filter_map(Result::ok)
}
/// Get the final `Url` of this `Response`.
@@ -92,8 +93,7 @@ impl Response {
/// Get the remote address used to get this `Response`.
pub fn remote_addr(&self) -> Option<SocketAddr> {
self
.extensions
self.extensions
.get::<HttpInfo>()
.map(|info| info.remote_addr())
}
@@ -106,8 +106,7 @@ impl Response {
/// - The response is gzipped and automatically decoded (thus changing
/// the actual decoded length).
pub fn content_length(&self) -> Option<u64> {
self
.headers()
self.headers()
.get(CONTENT_LENGTH)
.and_then(|ct_len| ct_len.to_str().ok())
.and_then(|ct_len| ct_len.parse().ok())
@@ -145,27 +144,24 @@ impl Response {
}
/// 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 content_type = self.headers.get(crate::header::CONTENT_TYPE)
.and_then(|value| {
value.to_str().ok()
})
.and_then(|value| {
value.parse::<Mime>().ok()
});
let content_type = self
.headers
.get(crate::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())
})
.and_then(|mime| mime.get_param("charset").map(|charset| charset.as_str()))
.unwrap_or(default_encoding);
let encoding = Encoding::for_label(encoding_name.as_bytes()).unwrap_or(UTF_8);
Text {
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 body = body.into();
let body = Decoder::detect(&mut parts.headers, body, false);
let url = parts.extensions
let url = parts
.extensions
.remove::<ResponseUrl>()
.unwrap_or_else(|| ResponseUrl(Url::parse("http://no.url.provided.local").unwrap()));
let url = url.0;
@@ -289,8 +286,7 @@ impl<T: DeserializeOwned> Future for Json<T> {
impl<T> fmt::Debug for Json<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Json")
.finish()
f.debug_struct("Json").finish()
}
}
@@ -308,7 +304,9 @@ impl Future for Text {
// a block because of borrow checker
{
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 {
// decoding returned Cow::Borrowed, meaning these bytes
@@ -357,9 +355,9 @@ impl ResponseBuilderExt for http::response::Builder {
#[cfg(test)]
mod tests {
use url::Url;
use super::{Response, ResponseBuilderExt, ResponseUrl};
use http::response::Builder;
use super::{Response, ResponseUrl, ResponseBuilderExt};
use url::Url;
#[test]
fn test_response_builder_ext() {
@@ -370,7 +368,10 @@ mod tests {
.body(())
.unwrap();
assert_eq!(response.extensions().get::<ResponseUrl>(), Some(&ResponseUrl(url)));
assert_eq!(
response.extensions().get::<ResponseUrl>(),
Some(&ResponseUrl(url))
);
}
#[test]

View File

@@ -1,12 +1,12 @@
use std::fs::File;
use std::fmt;
use std::fs::File;
use std::io::{self, Cursor, Read};
use bytes::Bytes;
use futures::{Future, try_ready};
use futures::{try_ready, Future};
use hyper::{self};
use crate::{async_impl};
use crate::async_impl;
/// The body of a `Request`.
///
@@ -102,7 +102,7 @@ impl Body {
tx,
};
(Some(tx), async_impl::Body::wrap(rx), len)
},
}
Kind::Bytes(chunk) => {
let len = chunk.len() as u64;
(None, async_impl::Body::reusable(chunk), Some(len))
@@ -111,12 +111,10 @@ impl Body {
}
pub(crate) fn try_clone(&self) -> Option<Body> {
self.kind.try_clone()
.map(|kind| Body { kind })
self.kind.try_clone().map(|kind| Body { kind })
}
}
enum Kind {
Reader(Box<dyn Read + Send>, Option<u64>),
Bytes(Bytes),
@@ -147,7 +145,6 @@ impl From<String> for Body {
}
}
impl From<&'static [u8]> for Body {
#[inline]
fn from(s: &'static [u8]) -> Body {
@@ -177,7 +174,8 @@ impl From<File> for Body {
impl fmt::Debug for Kind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Kind::Reader(_, ref v) => f.debug_struct("Reader")
Kind::Reader(_, ref v) => f
.debug_struct("Reader")
.field("length", &DebugLength(v))
.finish(),
Kind::Bytes(ref v) => fmt::Debug::fmt(v, f),
@@ -218,10 +216,10 @@ pub(crate) struct Sender {
impl Sender {
// A `Future` that may do blocking read calls.
// As a `Future`, this integrates easily with `wait::timeout`.
pub(crate) fn send(self) -> impl Future<Item=(), Error=crate::Error> {
use std::cmp;
pub(crate) fn send(self) -> impl Future<Item = (), Error = crate::Error> {
use bytes::{BufMut, BytesMut};
use futures::future;
use std::cmp;
let con_len = self.body.1;
let cap = cmp::min(self.body.1.unwrap_or(8192), 8192);
@@ -261,15 +259,12 @@ impl Sender {
// read. Return.
return Ok(().into());
}
Ok(n) => {
unsafe { buf.advance_mut(n); }
}
Ok(n) => unsafe {
buf.advance_mut(n);
},
Err(e) => {
let ret = io::Error::new(e.kind(), e.to_string());
tx
.take()
.expect("tx only taken on error")
.abort();
tx.take().expect("tx only taken on error").abort();
return Err(crate::error::from(ret));
}
}
@@ -297,8 +292,8 @@ impl Sender {
pub(crate) fn read_to_string(mut body: Body) -> io::Result<String> {
let mut s = String::new();
match body.kind {
Kind::Reader(ref mut reader, _) => reader.read_to_string(&mut s),
Kind::Bytes(ref mut bytes) => (&**bytes).read_to_string(&mut s),
}
.map(|_| s)
Kind::Reader(ref mut reader, _) => reader.read_to_string(&mut s),
Kind::Bytes(ref mut bytes) => (&**bytes).read_to_string(&mut s),
}
.map(|_| s)
}

View File

@@ -1,18 +1,18 @@
use std::fmt;
use std::sync::Arc;
use std::time::Duration;
use std::thread;
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::sync::{mpsc, oneshot};
use futures::{Async, Future, Stream};
use log::{trace};
use log::trace;
use crate::request::{Request, RequestBuilder};
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")]
use crate::{Certificate, Identity};
@@ -81,9 +81,7 @@ impl ClientBuilder {
/// This method fails if TLS backend cannot be initialized, or the resolver
/// cannot load the system configuration.
pub fn build(self) -> crate::Result<Client> {
ClientHandle::new(self).map(|handle| Client {
inner: handle,
})
ClientHandle::new(self).map(|handle| Client { inner: handle })
}
/// Disable proxy setting.
@@ -184,7 +182,6 @@ impl ClientBuilder {
self.with_inner(move |inner| inner.identity(identity))
}
/// Controls the use of hostname verification.
///
/// Defaults to `false`.
@@ -399,7 +396,6 @@ impl ClientBuilder {
}
}
impl Client {
/// Constructs a new `Client`.
///
@@ -411,9 +407,7 @@ impl Client {
/// Use `Client::builder()` if you wish to handle the failure as an `Error`
/// instead of panicking.
pub fn new() -> Client {
ClientBuilder::new()
.build()
.expect("Client::new()")
ClientBuilder::new().build().expect("Client::new()")
}
/// Creates a `ClientBuilder` to configure a `Client`.
@@ -486,9 +480,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
let req = url
.into_url()
.map(move |url| Request::new(method, url));
let req = url.into_url().map(move |url| Request::new(method, url));
RequestBuilder::new(self.clone(), req)
}
@@ -521,22 +513,24 @@ impl fmt::Debug for Client {
impl fmt::Debug for ClientBuilder {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ClientBuilder")
.finish()
f.debug_struct("ClientBuilder").finish()
}
}
#[derive(Clone)]
struct ClientHandle {
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 {
tx: Option<ThreadSender>,
thread: Option<thread::JoinHandle<()>>
thread: Option<thread::JoinHandle<()>>,
}
impl Drop for InnerClientHandle {
@@ -552,66 +546,64 @@ impl ClientHandle {
let builder = builder.inner;
let (tx, rx) = mpsc::unbounded();
let (spawn_tx, spawn_rx) = oneshot::channel::<crate::Result<()>>();
let handle = try_!(thread::Builder::new().name("reqwest-internal-sync-runtime".into()).spawn(move || {
use tokio::runtime::current_thread::Runtime;
let handle = try_!(thread::Builder::new()
.name("reqwest-internal-sync-runtime".into())
.spawn(move || {
use tokio::runtime::current_thread::Runtime;
let built = (|| {
let rt = try_!(Runtime::new());
let client = builder.build()?;
Ok((rt, client))
})();
let built = (|| {
let rt = try_!(Runtime::new());
let client = builder.build()?;
Ok((rt, client))
})();
let (mut rt, client) = match built {
Ok((rt, c)) => {
if spawn_tx.send(Ok(())).is_err() {
let (mut rt, client) = match built {
Ok((rt, c)) => {
if spawn_tx.send(Ok(())).is_err() {
return;
}
(rt, c)
}
Err(e) => {
let _ = spawn_tx.send(Err(e));
return;
}
(rt, c)
},
Err(e) => {
let _ = spawn_tx.send(Err(e));
return;
}
};
};
let work = rx.for_each(move |(req, tx)| {
let mut tx_opt: Option<oneshot::Sender<crate::Result<async_impl::Response>>> = Some(tx);
let mut res_fut = client.execute(req);
let work = rx.for_each(move |(req, tx)| {
let mut tx_opt: Option<oneshot::Sender<crate::Result<async_impl::Response>>> =
Some(tx);
let mut res_fut = client.execute(req);
let task = future::poll_fn(move || {
let canceled = tx_opt
.as_mut()
.expect("polled after complete")
.poll_cancel()
.expect("poll_cancel cannot error")
.is_ready();
if canceled {
trace!("response receiver is canceled");
Ok(Async::Ready(()))
} else {
let result = match res_fut.poll() {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready(res)) => Ok(res),
Err(err) => Err(err),
};
let _ = tx_opt
.take()
let task = future::poll_fn(move || {
let canceled = tx_opt
.as_mut()
.expect("polled after complete")
.send(result);
Ok(Async::Ready(()))
}
.poll_cancel()
.expect("poll_cancel cannot error")
.is_ready();
if canceled {
trace!("response receiver is canceled");
Ok(Async::Ready(()))
} else {
let result = match res_fut.poll() {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready(res)) => Ok(res),
Err(err) => Err(err),
};
let _ = tx_opt.take().expect("polled after complete").send(result);
Ok(Async::Ready(()))
}
});
tokio::spawn(task);
Ok(())
});
tokio::spawn(task);
Ok(())
});
// work is Future<(), ()>, and our closure will never return Err
rt.block_on(work)
.expect("runtime unexpected error");
}));
// work is Future<(), ()>, and our closure will never return Err
rt.block_on(work).expect("runtime unexpected error");
}));
// Wait for the runtime thread to start up...
match spawn_rx.wait() {
@@ -620,13 +612,11 @@ impl ClientHandle {
Err(_canceled) => event_loop_panicked(),
}
let inner_handle = Arc::new(InnerClientHandle {
tx: Some(tx),
thread: Some(handle)
thread: Some(handle),
});
Ok(ClientHandle {
timeout,
inner: inner_handle,
@@ -637,7 +627,8 @@ impl ClientHandle {
let (tx, rx) = oneshot::channel();
let (req, body) = req.into_async();
let url = req.url().clone();
self.inner.tx
self.inner
.tx
.as_ref()
.expect("core thread exited early")
.unbounded_send((req, tx))
@@ -645,7 +636,7 @@ impl ClientHandle {
let write = if let Some(body) = body {
Either::A(body.send())
//try_!(body.send(self.timeout.0), &url);
//try_!(body.send(self.timeout.0), &url);
} else {
Either::B(future::ok(()))
};
@@ -657,15 +648,17 @@ impl ClientHandle {
let res = match wait::timeout(fut, self.timeout.0) {
Ok(res) => res,
Err(wait::Waited::TimedOut) => return Err(crate::error::timedout(Some(url))),
Err(wait::Waited::Executor(err)) => {
return Err(crate::error::from(err).with_url(url))
},
Err(wait::Waited::Executor(err)) => return Err(crate::error::from(err).with_url(url)),
Err(wait::Waited::Inner(err)) => {
return Err(err.with_url(url));
},
}
};
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())),
)
})
}
}

View File

@@ -4,16 +4,16 @@ use hyper::client::connect::{Connect, Connected, Destination};
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_timer::Timeout;
#[cfg(feature = "default-tls")]
use native_tls::{TlsConnector, TlsConnectorBuilder};
#[cfg(feature = "tls")]
use futures::Poll;
#[cfg(feature = "tls")]
use bytes::BufMut;
#[cfg(feature = "tls")]
use futures::Poll;
#[cfg(feature = "default-tls")]
use native_tls::{TlsConnector, TlsConnectorBuilder};
use std::io;
use std::sync::Arc;
use std::net::IpAddr;
use std::sync::Arc;
use std::time::Duration;
#[cfg(feature = "trust-dns")]
@@ -25,13 +25,12 @@ type HttpConnector = hyper::client::HttpConnector<TrustDnsResolver>;
#[cfg(not(feature = "trust-dns"))]
type HttpConnector = hyper::client::HttpConnector;
pub(crate) struct Connector {
inner: Inner,
proxies: Arc<Vec<Proxy>>,
timeout: Option<Duration>,
#[cfg(feature = "tls")]
nodelay: bool
nodelay: bool,
}
enum Inner {
@@ -43,17 +42,20 @@ enum Inner {
RustlsTls {
http: HttpConnector,
tls: Arc<rustls::ClientConfig>,
tls_proxy: Arc<rustls::ClientConfig>
}
tls_proxy: Arc<rustls::ClientConfig>,
},
}
impl Connector {
#[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
T: Into<Option<IpAddr>>
T: Into<Option<IpAddr>>,
{
let mut http = http_connector()?;
http.set_local_address(local_addr.into());
http.set_nodelay(nodelay);
@@ -69,9 +71,10 @@ impl Connector {
tls: TlsConnectorBuilder,
proxies: Arc<Vec<Proxy>>,
local_addr: T,
nodelay: bool) -> crate::Result<Connector>
where
T: Into<Option<IpAddr>>,
nodelay: bool,
) -> crate::Result<Connector>
where
T: Into<Option<IpAddr>>,
{
let tls = try_!(tls.build());
@@ -83,7 +86,7 @@ impl Connector {
inner: Inner::DefaultTls(http, tls),
proxies,
timeout: None,
nodelay
nodelay,
})
}
@@ -92,9 +95,10 @@ impl Connector {
tls: rustls::ClientConfig,
proxies: Arc<Vec<Proxy>>,
local_addr: T,
nodelay: bool) -> crate::Result<Connector>
where
T: Into<Option<IpAddr>>,
nodelay: bool,
) -> crate::Result<Connector>
where
T: Into<Option<IpAddr>>,
{
let mut http = http_connector()?;
http.set_local_address(local_addr.into());
@@ -110,10 +114,14 @@ impl Connector {
};
Ok(Connector {
inner: Inner::RustlsTls { http, tls, tls_proxy },
inner: Inner::RustlsTls {
http,
tls,
tls_proxy,
},
proxies,
timeout: None,
nodelay
nodelay,
})
}
@@ -138,56 +146,62 @@ impl Connector {
} else {
Box::new($future)
}
}
};
}
let dns = match proxy {
ProxyScheme::Socks5 { remote_dns: false, .. } => socks::DnsResolve::Local,
ProxyScheme::Socks5 { remote_dns: true, .. } => socks::DnsResolve::Proxy,
ProxyScheme::Socks5 {
remote_dns: false, ..
} => socks::DnsResolve::Local,
ProxyScheme::Socks5 {
remote_dns: true, ..
} => socks::DnsResolve::Proxy,
ProxyScheme::Http { .. } => {
unreachable!("connect_socks is only called for socks proxies");
},
}
};
match &self.inner {
#[cfg(feature = "default-tls")]
Inner::DefaultTls(_http, tls) => if dst.scheme() == "https" {
use self::native_tls_async::TlsConnectorExt;
Inner::DefaultTls(_http, tls) => {
if dst.scheme() == "https" {
use self::native_tls_async::TlsConnectorExt;
let tls = tls.clone();
let host = dst.host().to_owned();
let socks_connecting = socks::connect(proxy, dst, dns);
return timeout!(socks_connecting.and_then(move |(conn, connected)| {
tls.connect_async(&host, conn)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
.map(move |io| (Box::new(io) as Conn, connected))
}));
},
let tls = tls.clone();
let host = dst.host().to_owned();
let socks_connecting = socks::connect(proxy, dst, dns);
return timeout!(socks_connecting.and_then(move |(conn, connected)| {
tls.connect_async(&host, conn)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
.map(move |io| (Box::new(io) as Conn, connected))
}));
}
}
#[cfg(feature = "rustls-tls")]
Inner::RustlsTls { tls_proxy, .. } => if dst.scheme() == "https" {
use tokio_rustls::TlsConnector as RustlsConnector;
use tokio_rustls::webpki::DNSNameRef;
Inner::RustlsTls { tls_proxy, .. } => {
if dst.scheme() == "https" {
use tokio_rustls::webpki::DNSNameRef;
use tokio_rustls::TlsConnector as RustlsConnector;
let tls = tls_proxy.clone();
let host = dst.host().to_owned();
let socks_connecting = socks::connect(proxy, dst, dns);
return timeout!(socks_connecting.and_then(move |(conn, connected)| {
let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host)
.map(|dnsname| dnsname.to_owned())
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid DNS Name"));
futures::future::result(maybe_dnsname)
.and_then(move |dnsname| {
RustlsConnector::from(tls).connect(dnsname.as_ref(), conn)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
})
.map(move |io| {
(Box::new(io) as Conn, connected)
})
}));
},
let tls = tls_proxy.clone();
let host = dst.host().to_owned();
let socks_connecting = socks::connect(proxy, dst, dns);
return timeout!(socks_connecting.and_then(move |(conn, connected)| {
let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host)
.map(|dnsname| dnsname.to_owned())
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid DNS Name"));
futures::future::result(maybe_dnsname)
.and_then(move |dnsname| {
RustlsConnector::from(tls)
.connect(dnsname.as_ref(), conn)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
})
.map(move |io| (Box::new(io) as Conn, connected))
}));
}
}
#[cfg(not(feature = "tls"))]
Inner::Http(_) => ()
Inner::Http(_) => (),
}
// else no TLS
@@ -231,12 +245,13 @@ impl Connect for Connector {
} else {
Box::new($future)
}
}
};
}
macro_rules! connect {
( $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))))
};
( $dst:expr, $proxy:expr ) => {
@@ -250,17 +265,16 @@ impl Connect for Connector {
http.set_nodelay(nodelay || ($dst.scheme() == "https"));
let http = hyper_tls::HttpsConnector::from((http, tls.clone()));
timeout!(http.connect($dst)
.and_then(move |(io, connected)| {
if let hyper_tls::MaybeHttpsStream::Https(stream) = &io {
if !nodelay {
stream.get_ref().get_ref().set_nodelay(false)?;
}
timeout!(http.connect($dst).and_then(move |(io, connected)| {
if let hyper_tls::MaybeHttpsStream::Https(stream) = &io {
if !nodelay {
stream.get_ref().get_ref().set_nodelay(false)?;
}
}
Ok((Box::new(io) as Conn, connected.proxy($proxy)))
}))
},
Ok((Box::new(io) as Conn, connected.proxy($proxy)))
}))
}
#[cfg(feature = "rustls-tls")]
Inner::RustlsTls { http, tls, .. } => {
let mut http = http.clone();
@@ -271,17 +285,16 @@ impl Connect for Connector {
http.set_nodelay(nodelay || ($dst.scheme() == "https"));
let http = hyper_rustls::HttpsConnector::from((http, tls.clone()));
timeout!(http.connect($dst)
.and_then(move |(io, connected)| {
if let hyper_rustls::MaybeHttpsStream::Https(stream) = &io {
if !nodelay {
let (io, _) = stream.get_ref();
io.set_nodelay(false)?;
}
timeout!(http.connect($dst).and_then(move |(io, connected)| {
if let hyper_rustls::MaybeHttpsStream::Https(stream) = &io {
if !nodelay {
let (io, _) = stream.get_ref();
io.set_nodelay(false)?;
}
}
Ok((Box::new(io) as Conn, connected.proxy($proxy)))
}))
Ok((Box::new(io) as Conn, connected.proxy($proxy)))
}))
}
}
};
@@ -299,10 +312,7 @@ impl Connect for Connector {
let mut ndst = dst.clone();
let new_scheme = puri
.scheme_part()
.map(Scheme::as_str)
.unwrap_or("http");
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");
@@ -316,60 +326,81 @@ impl Connect for Connector {
match &self.inner {
#[cfg(feature = "default-tls")]
Inner::DefaultTls(http, tls) => if dst.scheme() == "https" {
use self::native_tls_async::TlsConnectorExt;
Inner::DefaultTls(http, tls) => {
if dst.scheme() == "https" {
use self::native_tls_async::TlsConnectorExt;
let host = dst.host().to_owned();
let port = dst.port().unwrap_or(443);
let mut http = http.clone();
http.set_nodelay(nodelay);
let http = hyper_tls::HttpsConnector::from((http, tls.clone()));
let tls = tls.clone();
return timeout!(http.connect(ndst).and_then(move |(conn, connected)| {
log::trace!("tunneling HTTPS over proxy");
tunnel(conn, host.clone(), port, auth)
.and_then(move |tunneled| {
tls.connect_async(&host, tunneled)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
})
.map(|io| (Box::new(io) as Conn, connected.proxy(true)))
}));
},
let host = dst.host().to_owned();
let port = dst.port().unwrap_or(443);
let mut http = http.clone();
http.set_nodelay(nodelay);
let http = hyper_tls::HttpsConnector::from((http, tls.clone()));
let tls = tls.clone();
return timeout!(http.connect(ndst).and_then(
move |(conn, connected)| {
log::trace!("tunneling HTTPS over proxy");
tunnel(conn, host.clone(), port, auth)
.and_then(move |tunneled| {
tls.connect_async(&host, tunneled).map_err(|e| {
io::Error::new(io::ErrorKind::Other, e)
})
})
.map(|io| (Box::new(io) as Conn, connected.proxy(true)))
}
));
}
}
#[cfg(feature = "rustls-tls")]
Inner::RustlsTls { http, tls, tls_proxy } => if dst.scheme() == "https" {
use rustls::Session;
use tokio_rustls::TlsConnector as RustlsConnector;
use tokio_rustls::webpki::DNSNameRef;
Inner::RustlsTls {
http,
tls,
tls_proxy,
} => {
if dst.scheme() == "https" {
use rustls::Session;
use tokio_rustls::webpki::DNSNameRef;
use tokio_rustls::TlsConnector as RustlsConnector;
let host = dst.host().to_owned();
let port = dst.port().unwrap_or(443);
let mut http = http.clone();
http.set_nodelay(nodelay);
let http = hyper_rustls::HttpsConnector::from((http, tls_proxy.clone()));
let tls = tls.clone();
return timeout!(http.connect(ndst).and_then(move |(conn, connected)| {
log::trace!("tunneling HTTPS over proxy");
let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host)
.map(|dnsname| dnsname.to_owned())
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Invalid DNS Name"));
tunnel(conn, host, port, auth)
.and_then(move |tunneled| Ok((maybe_dnsname?, tunneled)))
.and_then(move |(dnsname, tunneled)| {
RustlsConnector::from(tls).connect(dnsname.as_ref(), tunneled)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
})
.map(|io| {
let connected = if io.get_ref().1.get_alpn_protocol() == Some(b"h2") {
connected.negotiated_h2()
} else {
connected
};
(Box::new(io) as Conn, connected.proxy(true))
})
}));
},
let host = dst.host().to_owned();
let port = dst.port().unwrap_or(443);
let mut http = http.clone();
http.set_nodelay(nodelay);
let http =
hyper_rustls::HttpsConnector::from((http, tls_proxy.clone()));
let tls = tls.clone();
return timeout!(http.connect(ndst).and_then(
move |(conn, connected)| {
log::trace!("tunneling HTTPS over proxy");
let maybe_dnsname = DNSNameRef::try_from_ascii_str(&host)
.map(|dnsname| dnsname.to_owned())
.map_err(|_| {
io::Error::new(io::ErrorKind::Other, "Invalid DNS Name")
});
tunnel(conn, host, port, auth)
.and_then(move |tunneled| Ok((maybe_dnsname?, tunneled)))
.and_then(move |(dnsname, tunneled)| {
RustlsConnector::from(tls)
.connect(dnsname.as_ref(), tunneled)
.map_err(|e| {
io::Error::new(io::ErrorKind::Other, e)
})
})
.map(|io| {
let connected = if io.get_ref().1.get_alpn_protocol()
== Some(b"h2")
{
connected.negotiated_h2()
} else {
connected
};
(Box::new(io) as Conn, connected.proxy(true))
})
}
));
}
}
#[cfg(not(feature = "tls"))]
Inner::Http(_) => ()
Inner::Http(_) => (),
}
return connect!(ndst, true);
@@ -384,21 +415,30 @@ pub(crate) trait AsyncConn: AsyncRead + AsyncWrite {}
impl<T: AsyncRead + AsyncWrite> AsyncConn for T {}
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")]
fn tunnel<T>(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\
Host: {0}:{1}\r\n\
", host, port).into_bytes();
fn tunnel<T>(
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\
Host: {0}:{1}\r\n\
",
host, port
)
.into_bytes();
if let Some(value) = auth {
log::debug!("tunnel to {}:{} using basic auth", host, port);
buf.extend_from_slice(b"Proxy-Authorization: ");
buf.extend_from_slice(value.as_bytes());
buf.extend_from_slice(b"\r\n");
}
if let Some(value) = auth {
log::debug!("tunnel to {}:{} using basic auth", host, port);
buf.extend_from_slice(b"Proxy-Authorization: ");
buf.extend_from_slice(value.as_bytes());
buf.extend_from_slice(b"\r\n");
}
// headers end
buf.extend_from_slice(b"\r\n");
@@ -420,7 +460,7 @@ struct Tunnel<T> {
#[cfg(feature = "tls")]
enum TunnelState {
Writing,
Reading
Reading,
}
#[cfg(feature = "tls")]
@@ -442,7 +482,11 @@ where
return Err(tunnel_eof());
}
} 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()[..];
if n == 0 {
return Err(tunnel_eof());
@@ -451,9 +495,12 @@ where
if read.ends_with(b"\r\n\r\n") {
return Ok(self.conn.take().unwrap().into());
}
// else read more
// else read more
} 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") {
return Err(io::Error::new(
io::ErrorKind::Other,
@@ -477,7 +524,7 @@ where
fn tunnel_eof() -> io::Error {
io::Error::new(
io::ErrorKind::UnexpectedEof,
"unexpected eof while tunneling"
"unexpected eof while tunneling",
)
}
@@ -485,9 +532,9 @@ fn tunnel_eof() -> io::Error {
mod native_tls_async {
use std::io::{self, Read, Write};
use futures::{Poll, Future, Async};
use native_tls::{self, HandshakeError, Error, TlsConnector};
use tokio_io::{AsyncRead, AsyncWrite, try_nb};
use futures::{Async, Future, Poll};
use native_tls::{self, Error, HandshakeError, TlsConnector};
use tokio_io::{try_nb, AsyncRead, AsyncWrite};
/// A wrapper around an underlying raw stream which implements the TLS or SSL
/// protocol.
@@ -533,7 +580,8 @@ mod native_tls_async {
/// and `AsyncWrite` traits as well, otherwise this function will not work
/// properly.
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 {
@@ -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> {
fn shutdown(&mut self) -> Poll<(), io::Error> {
@@ -569,7 +615,8 @@ mod native_tls_async {
impl TlsConnectorExt for TlsConnector {
fn connect_async<S>(&self, domain: &str, stream: S) -> ConnectAsync<S>
where S: Read + Write,
where
S: Read + Write,
{
ConnectAsync {
inner: MidHandshake {
@@ -600,51 +647,42 @@ mod native_tls_async {
match self.inner.take().expect("cannot poll MidHandshake twice") {
Ok(stream) => Ok(TlsStream { inner: stream }.into()),
Err(HandshakeError::Failure(e)) => Err(e),
Err(HandshakeError::WouldBlock(s)) => {
match s.handshake() {
Ok(stream) => Ok(TlsStream { inner: stream }.into()),
Err(HandshakeError::Failure(e)) => Err(e),
Err(HandshakeError::WouldBlock(s)) => {
self.inner = Some(Err(HandshakeError::WouldBlock(s)));
Ok(Async::NotReady)
}
Err(HandshakeError::WouldBlock(s)) => match s.handshake() {
Ok(stream) => Ok(TlsStream { inner: stream }.into()),
Err(HandshakeError::Failure(e)) => Err(e),
Err(HandshakeError::WouldBlock(s)) => {
self.inner = Some(Err(HandshakeError::WouldBlock(s)));
Ok(Async::NotReady)
}
}
},
}
}
}
}
#[cfg(feature = "socks")]
mod socks {
use std::io;
use futures::{Future, future};
use futures::{future, Future};
use hyper::client::connect::{Connected, Destination};
use socks::Socks5Stream;
use std::net::ToSocketAddrs;
use tokio::{net::TcpStream, reactor};
use super::{Connecting};
use crate::proxy::{ProxyScheme};
use super::Connecting;
use crate::proxy::ProxyScheme;
pub(super) enum DnsResolve {
Local,
Proxy,
}
pub(super) fn connect(
proxy: ProxyScheme,
dst: Destination,
dns: DnsResolve,
) -> Connecting {
pub(super) fn connect(proxy: ProxyScheme, dst: Destination, dns: DnsResolve) -> Connecting {
let https = dst.scheme() == "https";
let original_host = dst.host().to_owned();
let mut host = original_host.clone();
let port = dst.port().unwrap_or_else(|| {
if https { 443 } else { 80 }
});
let port = dst.port().unwrap_or_else(|| if https { 443 } else { 80 });
if let DnsResolve::Local = dns {
let maybe_new_target = match (host.as_str(), port).to_socket_addrs() {
@@ -664,20 +702,24 @@ mod socks {
};
// Get a Tokio TcpStream
let stream = future::result(if let Some((username, password)) = auth {
Socks5Stream::connect_with_password(
socket_addr, (host.as_str(), port), &username, &password
)
} else {
Socks5Stream::connect(socket_addr, (host.as_str(), port))
}.and_then(|s| {
TcpStream::from_std(s.into_inner(), &reactor::Handle::default())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
}));
let stream = future::result(
if let Some((username, password)) = auth {
Socks5Stream::connect_with_password(
socket_addr,
(host.as_str(), port),
&username,
&password,
)
} else {
Socks5Stream::connect(socket_addr, (host.as_str(), port))
}
.and_then(|s| {
TcpStream::from_std(s.into_inner(), &reactor::Handle::default())
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
}),
);
Box::new(
stream.map(|s| (Box::new(s) as super::Conn, Connected::new()))
)
Box::new(stream.map(|s| (Box::new(s) as super::Conn, Connected::new())))
}
}
@@ -686,14 +728,14 @@ mod socks {
mod tests {
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 super::tunnel;
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"\
HTTP/1.1 200 OK\r\n\
@@ -701,21 +743,27 @@ mod tests {
";
macro_rules! mock_tunnel {
() => ({
() => {{
mock_tunnel!(TUNNEL_OK)
});
($write:expr) => ({
}};
($write:expr) => {{
mock_tunnel!($write, "")
});
($write:expr, $auth:expr) => ({
}};
($write:expr, $auth:expr) => {{
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let connect_expected = format!("\
CONNECT {0}:{1} HTTP/1.1\r\n\
Host: {0}:{1}\r\n\
{2}\
\r\n\
", addr.ip(), addr.port(), $auth).into_bytes();
let connect_expected = format!(
"\
CONNECT {0}:{1} HTTP/1.1\r\n\
Host: {0}:{1}\r\n\
{2}\
\r\n\
",
addr.ip(),
addr.port(),
$auth
)
.into_bytes();
thread::spawn(move || {
let (mut sock, _) = listener.accept().unwrap();
@@ -726,7 +774,7 @@ mod tests {
sock.write_all($write).unwrap();
});
addr
})
}};
}
#[test]
@@ -737,9 +785,7 @@ mod tests {
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, None)
});
let work = work.and_then(|tcp| tunnel(tcp, host, port, None));
rt.block_on(work).unwrap();
}
@@ -752,9 +798,7 @@ mod tests {
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, None)
});
let work = work.and_then(|tcp| tunnel(tcp, host, port, None));
rt.block_on(work).unwrap_err();
}
@@ -767,28 +811,26 @@ mod tests {
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, None)
});
let work = work.and_then(|tcp| tunnel(tcp, host, port, None));
rt.block_on(work).unwrap_err();
}
#[test]
fn test_tunnel_proxy_unauthorized() {
let addr = mock_tunnel!(b"\
let addr = mock_tunnel!(
b"\
HTTP/1.1 407 Proxy Authentication Required\r\n\
Proxy-Authenticate: Basic realm=\"nope\"\r\n\
\r\n\
");
"
);
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, None)
});
let work = work.and_then(|tcp| tunnel(tcp, host, port, None));
let error = rt.block_on(work).unwrap_err();
assert_eq!(error.to_string(), "proxy authentication required");
@@ -806,7 +848,12 @@ mod tests {
let host = addr.ip().to_string();
let port = addr.port();
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();

View File

@@ -109,7 +109,9 @@ impl<'a> Cookie<'a> {
/// Get the Max-Age information.
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.

View File

@@ -1,6 +1,6 @@
use std::{io, vec};
use std::net::IpAddr;
use std::sync::{Arc, Mutex, Once};
use std::{io, vec};
use futures::{future, Future};
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.
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)]
pub(crate) struct TrustDnsResolver {
@@ -31,9 +31,7 @@ impl TrustDnsResolver {
let (conf, opts) = system_conf::read_system_conf()?;
let (resolver, bg) = AsyncResolver::new(conf, opts);
let resolver: ErasedResolver = Box::new(move |name| {
resolver.lookup_ip(name.as_str())
});
let resolver: ErasedResolver = Box::new(move |name| resolver.lookup_ip(name.as_str()));
let background = Mutex::new(Some(Box::new(bg) as Background));
let once = Once::new();
@@ -49,7 +47,7 @@ impl TrustDnsResolver {
impl hyper_dns::Resolve for TrustDnsResolver {
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 {
let inner = self.inner.clone();
@@ -70,16 +68,8 @@ impl hyper_dns::Resolve for TrustDnsResolver {
});
(inner.resolver)(name)
.map(|lookup| {
lookup
.iter()
.collect::<Vec<_>>()
.into_iter()
})
.map_err(|err| {
io::Error::new(io::ErrorKind::Other, err.to_string())
})
.map(|lookup| lookup.iter().collect::<Vec<_>>().into_iter())
.map_err(|err| io::Error::new(io::ErrorKind::Other, err.to_string()))
}))
}
}

View File

@@ -62,17 +62,13 @@ struct Inner {
url: Option<Url>,
}
/// A `Result` alias where the `Err` case is `reqwest::Error`.
pub type Result<T> = std::result::Result<T, Error>;
impl Error {
fn new(kind: Kind, url: Option<Url>) -> Error {
Error {
inner: Box::new(Inner {
kind,
url,
}),
inner: Box::new(Inner { kind, url }),
}
}
@@ -148,13 +144,13 @@ impl Error {
Kind::Io(ref e) => Some(e),
Kind::UrlEncoded(ref e) => Some(e),
Kind::Json(ref e) => Some(e),
Kind::UrlBadScheme |
Kind::TooManyRedirects |
Kind::RedirectLoop |
Kind::Status(_) |
Kind::UnknownProxyScheme |
Kind::Timer |
Kind::BlockingClientInFutureContext => None,
Kind::UrlBadScheme
| Kind::TooManyRedirects
| Kind::RedirectLoop
| Kind::Status(_)
| Kind::UnknownProxyScheme
| Kind::Timer
| Kind::BlockingClientInFutureContext => None,
}
}
@@ -172,15 +168,11 @@ impl Error {
pub fn is_timeout(&self) -> bool {
match self.inner.kind {
Kind::Io(ref io) => io.kind() == io::ErrorKind::TimedOut,
Kind::Hyper(ref error) => {
error
.source()
.and_then(|cause| {
cause.downcast_ref::<io::Error>()
})
.map(|io| io.kind() == io::ErrorKind::TimedOut)
.unwrap_or(false)
},
Kind::Hyper(ref error) => error
.source()
.and_then(|cause| cause.downcast_ref::<io::Error>())
.map(|io| io.kind() == io::ErrorKind::TimedOut)
.unwrap_or(false),
_ => false,
}
}
@@ -189,8 +181,7 @@ impl Error {
#[inline]
pub fn is_serialization(&self) -> bool {
match self.inner.kind {
Kind::Json(_) |
Kind::UrlEncoded(_) => true,
Kind::Json(_) | Kind::UrlEncoded(_) => true,
_ => false,
}
}
@@ -199,8 +190,7 @@ impl Error {
#[inline]
pub fn is_redirect(&self) -> bool {
match self.inner.kind {
Kind::TooManyRedirects |
Kind::RedirectLoop => true,
Kind::TooManyRedirects | Kind::RedirectLoop => true,
_ => false,
}
}
@@ -241,9 +231,7 @@ impl fmt::Debug for Error {
.field(url)
.finish()
} else {
f.debug_tuple("Error")
.field(&self.inner.kind)
.finish()
f.debug_tuple("Error").field(&self.inner.kind).finish()
}
}
}
@@ -269,9 +257,7 @@ impl fmt::Display for Error {
#[cfg(feature = "rustls-tls")]
Kind::Rustls(ref e) => fmt::Display::fmt(e, f),
#[cfg(feature = "trust-dns")]
Kind::DnsSystemConf(ref e) => {
write!(f, "failed to load DNS system conf: {}", e)
},
Kind::DnsSystemConf(ref e) => write!(f, "failed to load DNS system conf: {}", e),
Kind::Io(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),
@@ -349,13 +335,13 @@ impl StdError for Error {
Kind::Io(ref e) => e.cause(),
Kind::UrlEncoded(ref e) => e.cause(),
Kind::Json(ref e) => e.cause(),
Kind::UrlBadScheme |
Kind::TooManyRedirects |
Kind::RedirectLoop |
Kind::Status(_) |
Kind::UnknownProxyScheme |
Kind::Timer |
Kind::BlockingClientInFutureContext => None,
Kind::UrlBadScheme
| Kind::TooManyRedirects
| Kind::RedirectLoop
| Kind::Status(_)
| Kind::UnknownProxyScheme
| Kind::Timer
| Kind::BlockingClientInFutureContext => None,
}
}
@@ -376,13 +362,13 @@ impl StdError for Error {
Kind::Io(ref e) => e.source(),
Kind::UrlEncoded(ref e) => e.source(),
Kind::Json(ref e) => e.source(),
Kind::UrlBadScheme |
Kind::TooManyRedirects |
Kind::RedirectLoop |
Kind::Status(_) |
Kind::UnknownProxyScheme |
Kind::Timer |
Kind::BlockingClientInFutureContext => None,
Kind::UrlBadScheme
| Kind::TooManyRedirects
| Kind::RedirectLoop
| Kind::Status(_)
| Kind::UnknownProxyScheme
| Kind::Timer
| Kind::BlockingClientInFutureContext => None,
}
}
}
@@ -413,7 +399,6 @@ pub(crate) enum Kind {
BlockingClientInFutureContext,
}
impl From<http::Error> for Kind {
#[inline]
fn from(err: http::Error) -> Kind {
@@ -478,10 +463,12 @@ impl From<rustls::TLSError> 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 {
match err {
crate::wait::Waited::TimedOut => io_timeout().into(),
crate::wait::Waited::TimedOut => io_timeout().into(),
crate::wait::Waited::Executor(e) => e.into(),
crate::wait::Waited::Inner(e) => e.into(),
}
@@ -542,8 +529,7 @@ pub(crate) fn into_io(e: Error) -> io::Error {
pub(crate) fn from_io(e: io::Error) -> Error {
if e.get_ref().map(|r| r.is::<Error>()).unwrap_or(false) {
*e
.into_inner()
*e.into_inner()
.expect("io::Error::get_ref was Some(_)")
.downcast::<Error>()
.expect("StdError::is() was true")
@@ -552,28 +538,30 @@ pub(crate) fn from_io(e: io::Error) -> Error {
}
}
macro_rules! try_ {
($e:expr) => (
($e:expr) => {
match $e {
Ok(v) => v,
Err(err) => {
return Err(crate::error::from(err));
}
}
);
($e:expr, $url:expr) => (
};
($e:expr, $url:expr) => {
match $e {
Ok(v) => v,
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 {
($e:expr) => (
($e:expr) => {
match $e {
Ok(v) => v,
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));
}
}
)
};
}
pub(crate) fn loop_detected(url: Url) -> Error {
@@ -625,7 +613,7 @@ mod tests {
#[derive(Debug)]
struct Chain<T>(Option<T>);
impl<T: fmt::Display> fmt::Display for Chain<T> {
impl<T: fmt::Display> fmt::Display for Chain<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref link) = self.0 {
write!(f, "chain: {}", link)
@@ -658,7 +646,6 @@ mod tests {
assert!(err.cause().is_none());
assert_eq!(err.to_string(), "root");
let root = std::io::Error::new(std::io::ErrorKind::Other, Chain(None::<Error>));
let link = Chain(Some(root));
let io = std::io::Error::new(std::io::ErrorKind::Other, link);

View File

@@ -27,8 +27,7 @@ impl PolyfillTryInto for Url {
impl<'a> PolyfillTryInto for &'a str {
fn into_url(self) -> crate::Result<Url> {
try_!(Url::parse(self))
.into_url()
try_!(Url::parse(self)).into_url()
}
}
@@ -39,7 +38,9 @@ impl<'a> PolyfillTryInto for &'a String {
}
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> {
@@ -52,9 +53,10 @@ mod tests {
#[test]
fn into_url_file_scheme() {
let err = "file:///etc/hosts"
.into_url()
.unwrap_err();
assert_eq!(err.to_string(), "file:///etc/hosts: URL scheme is not allowed");
let err = "file:///etc/hosts".into_url().unwrap_err();
assert_eq!(
err.to_string(),
"file:///etc/hosts: URL scheme is not allowed"
);
}
}

View File

@@ -187,12 +187,12 @@ doctest!("../README.md");
pub use hyper::header;
pub use hyper::Method;
pub use hyper::{StatusCode, Version};
pub use url::Url;
pub use url::ParseError as UrlError;
pub use url::Url;
pub use self::body::Body;
pub use self::client::{Client, ClientBuilder};
pub use self::error::{Error, Result};
pub use self::body::Body;
pub use self::into_url::IntoUrl;
pub use self::proxy::Proxy;
pub use self::redirect::{RedirectAction, RedirectAttempt, RedirectPolicy};
@@ -206,9 +206,9 @@ pub use self::tls::{Certificate, Identity};
mod error;
mod async_impl;
mod connect;
mod body;
mod client;
mod connect;
pub mod cookie;
#[cfg(feature = "trust-dns")]
mod dns;
@@ -226,16 +226,8 @@ pub mod multipart;
/// An 'async' implementation of the reqwest `Client`.
pub mod r#async {
pub use crate::async_impl::{
Body,
Chunk,
Decoder,
Client,
ClientBuilder,
Request,
RequestBuilder,
Response,
multipart, Body, Chunk, Client, ClientBuilder, Decoder, Request, RequestBuilder, Response,
ResponseBuilderExt,
multipart
};
}
@@ -269,10 +261,7 @@ pub mod r#async {
/// - redirect loop was detected
/// - redirect limit was exhausted
pub fn get<T: IntoUrl>(url: T) -> crate::Result<Response> {
Client::builder()
.build()?
.get(url)
.send()
Client::builder().build()?.get(url).send()
}
fn _assert_impls() {

View File

@@ -45,7 +45,7 @@ use std::path::Path;
use mime_guess::{self, Mime};
use crate::async_impl::multipart::{FormParts, PartMetadata, PartProps};
use crate::{Body};
use crate::Body;
/// A multipart/form-data request.
pub struct Form {
@@ -82,8 +82,9 @@ impl Form {
/// .text("password", "secret");
/// ```
pub fn text<T, U>(self, name: T, value: U) -> Form
where T: Into<Cow<'static, str>>,
U: Into<Cow<'static, str>>,
where
T: Into<Cow<'static, str>>,
U: Into<Cow<'static, str>>,
{
self.part(name, Part::text(value))
}
@@ -106,8 +107,9 @@ impl Form {
///
/// Errors when the file cannot be opened.
pub fn file<T, U>(self, name: T, path: U) -> io::Result<Form>
where T: Into<Cow<'static, str>>,
U: AsRef<Path>
where
T: Into<Cow<'static, str>>,
U: AsRef<Path>,
{
Ok(self.part(name, Part::file(path)?))
}
@@ -162,11 +164,11 @@ impl fmt::Debug for Form {
}
}
impl Part {
/// Makes a text parameter.
pub fn text<T>(value: T) -> Part
where T: Into<Cow<'static, str>>,
where
T: Into<Cow<'static, str>>,
{
let body = match value.into() {
Cow::Borrowed(slice) => Body::from(slice),
@@ -177,7 +179,8 @@ impl Part {
/// Makes a new parameter from arbitrary bytes.
pub fn bytes<T>(value: T) -> Part
where T: Into<Cow<'static, [u8]>>
where
T: Into<Cow<'static, [u8]>>,
{
let body = match value.into() {
Cow::Borrowed(slice) => Body::from(slice),
@@ -207,16 +210,14 @@ impl Part {
/// Errors when the file cannot be opened.
pub fn file<T: AsRef<Path>>(path: T) -> io::Result<Part> {
let path = path.as_ref();
let file_name = path.file_name().and_then(|filename| {
Some(filename.to_string_lossy().into_owned())
});
let ext = path.extension()
.and_then(|ext| ext.to_str())
.unwrap_or("");
let file_name = path
.file_name()
.map(|filename| filename.to_string_lossy().into_owned());
let ext = path.extension().and_then(|ext| ext.to_str()).unwrap_or("");
let mime = mime_guess::from_ext(ext).first_or_octet_stream();
let file = File::open(path)?;
let field = Part::new(Body::from(file))
.mime(mime);
let field = Part::new(Body::from(file)).mime(mime);
Ok(if let Some(file_name) = file_name {
field.file_name(file_name)
@@ -287,9 +288,7 @@ pub(crate) struct Reader {
impl fmt::Debug for Reader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Reader")
.field("form", &self.form)
.finish()
f.debug_struct("Reader").field("form", &self.form).finish()
}
}
@@ -313,7 +312,10 @@ impl Reader {
let mut h = if !self.form.inner.computed_headers.is_empty() {
self.form.inner.computed_headers.remove(0)
} 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
@@ -327,9 +329,10 @@ impl Reader {
if !self.form.inner.fields.is_empty() {
Some(Box::new(reader))
} else {
Some(Box::new(reader.chain(Cursor::new(
format!("--{}--\r\n", self.form.boundary()),
))))
Some(Box::new(reader.chain(Cursor::new(format!(
"--{}--\r\n",
self.form.boundary()
)))))
}
} else {
None
@@ -379,33 +382,28 @@ mod tests {
let mut form = Form::new()
.part("reader1", Part::reader(std::io::empty()))
.part("key1", Part::text("value1"))
.part(
"key2",
Part::text("value2").mime(mime::IMAGE_BMP),
)
.part("key2", Part::text("value2").mime(mime::IMAGE_BMP))
.part("reader2", Part::reader(std::io::empty()))
.part(
"key3",
Part::text("value3").file_name("filename"),
);
.part("key3", Part::text("value3").file_name("filename"));
form.inner.boundary = "boundary".to_string();
let length = form.compute_length();
let expected = "--boundary\r\n\
Content-Disposition: form-data; name=\"reader1\"\r\n\r\n\
\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key1\"\r\n\r\n\
value1\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key2\"\r\n\
Content-Type: image/bmp\r\n\r\n\
value2\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"reader2\"\r\n\r\n\
\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key3\"; filename=\"filename\"\r\n\r\n\
value3\r\n--boundary--\r\n";
let expected =
"--boundary\r\n\
Content-Disposition: form-data; name=\"reader1\"\r\n\r\n\
\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key1\"\r\n\r\n\
value1\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key2\"\r\n\
Content-Type: image/bmp\r\n\r\n\
value2\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"reader2\"\r\n\r\n\
\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key3\"; filename=\"filename\"\r\n\r\n\
value3\r\n--boundary--\r\n";
form.reader().read_to_end(&mut output).unwrap();
// These prints are for debug purposes in case the test fails
println!(
@@ -422,26 +420,21 @@ mod tests {
let mut output = Vec::new();
let mut form = Form::new()
.text("key1", "value1")
.part(
"key2",
Part::text("value2").mime(mime::IMAGE_BMP),
)
.part(
"key3",
Part::text("value3").file_name("filename"),
);
.part("key2", Part::text("value2").mime(mime::IMAGE_BMP))
.part("key3", Part::text("value3").file_name("filename"));
form.inner.boundary = "boundary".to_string();
let length = form.compute_length();
let expected = "--boundary\r\n\
Content-Disposition: form-data; name=\"key1\"\r\n\r\n\
value1\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key2\"\r\n\
Content-Type: image/bmp\r\n\r\n\
value2\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key3\"; filename=\"filename\"\r\n\r\n\
value3\r\n--boundary--\r\n";
let expected =
"--boundary\r\n\
Content-Disposition: form-data; name=\"key1\"\r\n\r\n\
value1\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key2\"\r\n\
Content-Type: image/bmp\r\n\r\n\
value2\r\n\
--boundary\r\n\
Content-Disposition: form-data; name=\"key3\"; filename=\"filename\"\r\n\r\n\
value3\r\n--boundary--\r\n";
form.reader().read_to_end(&mut output).unwrap();
// These prints are for debug purposes in case the test fails
println!(

View File

@@ -1,16 +1,16 @@
use std::fmt;
use std::sync::Arc;
#[cfg(feature = "socks")]
use std::net::{SocketAddr, ToSocketAddrs};
use std::sync::Arc;
use crate::{IntoUrl, Url};
use http::{header::HeaderValue, Uri};
use hyper::client::connect::Destination;
use url::percent_encoding::percent_decode;
use crate::{IntoUrl, Url};
use std::collections::HashMap;
use std::env;
#[cfg(target_os = "windows")]
use std::error::Error;
use url::percent_encoding::percent_decode;
#[cfg(target_os = "windows")]
use winreg::enums::HKEY_CURRENT_USER;
#[cfg(target_os = "windows")]
@@ -98,7 +98,7 @@ impl Proxy {
/// ```
pub fn http<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy> {
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> {
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> {
Ok(Proxy::new(Intercept::All(
proxy_scheme.into_proxy_scheme()?
proxy_scheme.into_proxy_scheme()?,
)))
}
@@ -163,12 +163,12 @@ impl Proxy {
/// # }
/// # fn main() {}
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 {
auth: None,
func: Arc::new(move |url| {
fun(url).map(IntoProxyScheme::into_proxy_scheme)
}),
func: Arc::new(move |url| fun(url).map(IntoProxyScheme::into_proxy_scheme)),
}))
}
@@ -179,9 +179,7 @@ impl Proxy {
*/
fn new(intercept: Intercept) -> Proxy {
Proxy {
intercept,
}
Proxy { intercept }
}
/// 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> {
match self.intercept {
Intercept::All(ProxyScheme::Http { ref auth, .. }) |
Intercept::Http(ProxyScheme::Http { ref auth, .. }) => auth.clone(),
Intercept::Custom(ref custom) => {
custom.call(uri).and_then(|scheme| match scheme {
ProxyScheme::Http { auth, .. } => auth,
#[cfg(feature = "socks")]
_ => None,
})
}
Intercept::All(ProxyScheme::Http { ref auth, .. })
| Intercept::Http(ProxyScheme::Http { ref auth, .. }) => auth.clone(),
Intercept::Custom(ref custom) => custom.call(uri).and_then(|scheme| match scheme {
ProxyScheme::Http { auth, .. } => auth,
#[cfg(feature = "socks")]
_ => None,
}),
_ => None,
}
}
@@ -236,14 +232,14 @@ impl Proxy {
} else {
None
}
},
}
Intercept::Https(ref u) => {
if uri.scheme() == "https" {
Some(u.clone())
} else {
None
}
},
}
Intercept::Custom(ref custom) => custom.call(uri),
}
}
@@ -251,12 +247,8 @@ impl Proxy {
pub(crate) fn is_match<D: Dst>(&self, uri: &D) -> bool {
match self.intercept {
Intercept::All(_) => true,
Intercept::Http(_) => {
uri.scheme() == "http"
},
Intercept::Https(_) => {
uri.scheme() == "https"
},
Intercept::Http(_) => uri.scheme() == "http",
Intercept::Https(_) => uri.scheme() == "https",
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
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
}
@@ -314,7 +310,7 @@ impl ProxyScheme {
ProxyScheme::Http { ref mut auth, .. } => {
let header = encode_basic_auth(&username.into(), &password.into());
*auth = Some(header);
},
}
#[cfg(feature = "socks")]
ProxyScheme::Socks5 { ref mut auth, .. } => {
*auth = Some((username.into(), password.into()));
@@ -332,12 +328,10 @@ impl ProxyScheme {
let to_addr = || {
let host_and_port = try_!(url.with_default_port(|url| match url.scheme() {
"socks5" | "socks5h" => Ok(1080),
_ => Err(())
_ => Err(()),
}));
let mut addr = try_!(host_and_port.to_socket_addrs());
addr
.next()
.ok_or_else(crate::error::unknown_proxy_scheme)
addr.next().ok_or_else(crate::error::unknown_proxy_scheme)
};
let mut scheme = match url.scheme() {
@@ -346,7 +340,7 @@ impl ProxyScheme {
"socks5" => Self::socks5(to_addr()?)?,
#[cfg(feature = "socks")]
"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() {
@@ -359,8 +353,6 @@ impl ProxyScheme {
}
}
#[derive(Clone, Debug)]
enum Intercept {
All(ProxyScheme),
@@ -372,9 +364,9 @@ enum Intercept {
impl Intercept {
fn set_basic_auth(&mut self, username: &str, password: &str) {
match self {
Intercept::All(ref mut s) |
Intercept::Http(ref mut s) |
Intercept::Https(ref mut s) => s.set_basic_auth(username, password),
Intercept::All(ref mut s)
| Intercept::Http(ref mut s)
| Intercept::Https(ref mut s) => s.set_basic_auth(username, password),
Intercept::Custom(ref mut custom) => {
let header = encode_basic_auth(username, password);
custom.auth = Some(header);
@@ -397,9 +389,10 @@ impl Custom {
uri.scheme(),
uri.host(),
uri.port().map(|_| ":").unwrap_or(""),
uri.port().map(|p| p.to_string()).unwrap_or_default())
.parse()
.expect("should be valid Url");
uri.port().map(|p| p.to_string()).unwrap_or_default()
)
.parse()
.expect("should be valid Url");
(self.func)(&url)
.and_then(|result| result.ok())
@@ -413,7 +406,7 @@ impl Custom {
uri,
}
}
},
}
#[cfg(feature = "socks")]
socks => socks,
})
@@ -467,8 +460,7 @@ impl Dst for Uri {
}
fn host(&self) -> &str {
Uri::host(self)
.expect("<Uri as Dst>::host should have a str")
Uri::host(self).expect("<Uri as Dst>::host should have a str")
}
fn port(&self) -> Option<u16> {
@@ -499,8 +491,7 @@ pub fn get_proxies() -> HashMap<String, Url> {
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) {
proxies.insert(schema, valid_addr);
}
@@ -522,7 +513,6 @@ fn get_from_environment() -> HashMap<String, Url> {
proxies
}
#[cfg(target_os = "windows")]
fn get_from_registry_impl() -> Result<HashMap<String, Url>, Box<dyn Error>> {
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();
match protocol_parts.as_slice() {
[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
@@ -558,9 +552,21 @@ fn get_from_registry_impl() -> Result<HashMap<String, Url>, Box<dyn Error>> {
if proxy_server.starts_with("http:") {
insert_proxy(&mut proxies, String::from("http"), proxy_server);
} else {
insert_proxy(&mut proxies, 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));
insert_proxy(
&mut proxies,
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)
@@ -581,8 +587,7 @@ mod tests {
}
fn host(&self) -> &str {
Url::host_str(self)
.expect("<Url as Dst>::host should have a str")
Url::host_str(self).expect("<Url as Dst>::host should have a str")
}
fn port(&self) -> Option<u16> {
@@ -594,7 +599,6 @@ mod tests {
s.parse().unwrap()
}
fn intercepted_uri(p: &Proxy, s: &str) -> Uri {
match p.intercept(&url(s)).unwrap() {
ProxyScheme::Http { uri, .. } => uri,
@@ -641,7 +645,6 @@ mod tests {
assert_eq!(intercepted_uri(&p, other), target);
}
#[test]
fn test_custom() {
let target1 = "http://example.domain/";
@@ -687,7 +690,7 @@ mod tests {
// reset user setting.
match system_proxy {
Err(_) => env::remove_var("http_proxy"),
Ok(proxy) => env::set_var("http_proxy", proxy)
Ok(proxy) => env::set_var("http_proxy", proxy),
}
}
}

View File

@@ -1,13 +1,6 @@
use std::fmt;
use crate::header::{
HeaderMap,
AUTHORIZATION,
COOKIE,
PROXY_AUTHORIZATION,
WWW_AUTHENTICATE,
};
use crate::header::{HeaderMap, AUTHORIZATION, COOKIE, PROXY_AUTHORIZATION, WWW_AUTHENTICATE};
use hyper::StatusCode;
use crate::Url;
@@ -141,19 +134,13 @@ impl RedirectPolicy {
}
}
pub(crate) fn check(
&self,
status: StatusCode,
next: &Url,
previous: &[Url],
) -> Action {
self
.redirect(RedirectAttempt {
status,
next,
previous,
})
.inner
pub(crate) fn check(&self, status: StatusCode, next: &Url, previous: &[Url]) -> Action {
self.redirect(RedirectAttempt {
status,
next,
previous,
})
.inner
}
}
@@ -239,11 +226,10 @@ pub(crate) enum Action {
TooManyRedirects,
}
pub(crate) 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();
let cross_host = next.host_str() != previous.host_str()
|| next.port_or_known_default() != previous.port_or_known_default();
if cross_host {
headers.remove(AUTHORIZATION);
headers.remove(COOKIE);
@@ -304,21 +290,15 @@ fn test_redirect_policy_custom() {
});
let next = Url::parse("http://bar/baz").unwrap();
assert_eq!(
policy.check(StatusCode::FOUND, &next, &[]),
Action::Follow
);
assert_eq!(policy.check(StatusCode::FOUND, &next, &[]), Action::Follow);
let next = Url::parse("http://foo/baz").unwrap();
assert_eq!(
policy.check(StatusCode::FOUND, &next, &[]),
Action::Stop
);
assert_eq!(policy.check(StatusCode::FOUND, &next, &[]), Action::Stop);
}
#[test]
fn test_remove_sensitive_headers() {
use hyper::header::{ACCEPT, AUTHORIZATION, COOKIE, HeaderValue};
use hyper::header::{HeaderValue, ACCEPT, AUTHORIZATION, COOKIE};
let mut headers = HeaderMap::new();
headers.insert(ACCEPT, HeaderValue::from_static("*/*"));

View File

@@ -7,8 +7,8 @@ use serde_urlencoded;
use crate::body::{self, Body};
use crate::header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
use http::HttpTryFrom;
use crate::{async_impl, Client, Method, Url};
use http::HttpTryFrom;
/// A request which can be executed with `Client::execute()`.
pub struct Request {
@@ -119,10 +119,7 @@ impl Request {
impl RequestBuilder {
pub(crate) fn new(client: Client, request: crate::Result<Request>) -> RequestBuilder {
RequestBuilder {
client,
request,
}
RequestBuilder { client, request }
}
/// Add a `Header` to this Request.
@@ -146,11 +143,11 @@ impl RequestBuilder {
let mut error = None;
if let Ok(ref mut req) = self.request {
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) => error = Some(crate::error::from(e.into())),
Ok(key) => match <HeaderValue as HttpTryFrom<V>>::try_from(value) {
Ok(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())),
};
@@ -236,7 +233,7 @@ impl RequestBuilder {
{
let auth = match password {
Some(password) => format!("{}:{}", username, password),
None => format!("{}:", username)
None => format!("{}:", username),
};
let header_value = format!("Basic {}", encode(&auth));
self.header(crate::header::AUTHORIZATION, &*header_value)
@@ -397,10 +394,10 @@ impl RequestBuilder {
Ok(body) => {
req.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("application/x-www-form-urlencoded")
HeaderValue::from_static("application/x-www-form-urlencoded"),
);
*req.body_mut() = Some(body.into());
},
}
Err(err) => error = Some(crate::error::from(err)),
}
}
@@ -440,12 +437,10 @@ impl RequestBuilder {
if let Ok(ref mut req) = self.request {
match serde_json::to_vec(json) {
Ok(body) => {
req.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("application/json")
);
req.headers_mut()
.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
*req.body_mut() = Some(body.into());
},
}
Err(err) => error = Some(crate::error::from(err)),
}
}
@@ -477,10 +472,7 @@ impl RequestBuilder {
pub fn multipart(self, mut multipart: crate::multipart::Form) -> RequestBuilder {
let mut builder = self.header(
CONTENT_TYPE,
format!(
"multipart/form-data; boundary={}",
multipart.boundary()
).as_str()
format!("multipart/form-data; boundary={}", multipart.boundary()).as_str(),
);
if let Ok(ref mut req) = builder.request {
*req.body_mut() = Some(match multipart.compute_length() {
@@ -552,26 +544,27 @@ impl RequestBuilder {
/// # }
/// ```
pub fn try_clone(&self) -> Option<RequestBuilder> {
self.request.as_ref()
self.request
.as_ref()
.ok()
.and_then(|req| req.try_clone())
.map(|req| {
RequestBuilder{
client: self.client.clone(),
request: Ok(req),
}
.map(|req| RequestBuilder {
client: self.client.clone(),
request: Ok(req),
})
}
}
impl fmt::Debug for Request {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_request_fields(&mut f.debug_struct("Request"), self)
.finish()
fmt_request_fields(&mut f.debug_struct("Request"), self).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())
.field("url", req.url())
.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)]
mod tests {
use crate::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE, HOST};
use crate::{body, Client, Method};
use crate::header::{ACCEPT, HOST, HeaderMap, HeaderValue, CONTENT_TYPE};
use std::collections::{BTreeMap, HashMap};
use serde::Serialize;
use serde_json;
use serde_urlencoded;
use std::collections::{BTreeMap, HashMap};
#[test]
fn basic_get_request() {
@@ -755,7 +748,10 @@ mod tests {
let some_url = "https://google.com/";
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(&params);
@@ -791,7 +787,10 @@ mod tests {
let mut r = r.form(&form_data).build().unwrap();
// 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();
@@ -821,15 +820,16 @@ mod tests {
#[test]
fn add_json_fail() {
use serde::{Serialize, Serializer};
use serde::ser::Error;
use serde::{Serialize, Serializer};
struct MyStruct;
impl Serialize for MyStruct {
fn serialize<S>(&self, _serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
Err(S::Error::custom("nope"))
}
where
S: Serializer,
{
Err(S::Error::custom("nope"))
}
}
let client = Client::new();
@@ -858,11 +858,7 @@ mod tests {
assert_eq!(req.headers()["im-a"], "keeper");
let foo = req
.headers()
.get_all("foo")
.iter()
.collect::<Vec<_>>();
let foo = req.headers().get_all("foo").iter().collect::<Vec<_>>();
assert_eq!(foo.len(), 2);
assert_eq!(foo[0], "bar");
assert_eq!(foo[1], "baz");

View File

@@ -1,6 +1,6 @@
use std::mem;
use std::fmt;
use std::io::{self, Read};
use std::mem;
use std::net::SocketAddr;
use std::time::Duration;
@@ -8,10 +8,10 @@ use futures::{Async, Poll, Stream};
use http;
use serde::de::DeserializeOwned;
use crate::cookie;
use crate::client::KeepCoreThreadAlive;
use crate::cookie;
use crate::{async_impl, wait, StatusCode, Url, Version};
use hyper::header::HeaderMap;
use crate::{async_impl, StatusCode, Url, Version, wait};
/// A Response to a submitted `Request`.
pub struct Response {
@@ -28,7 +28,11 @@ impl fmt::Debug for 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 {
inner: res,
body: None,
@@ -116,12 +120,10 @@ impl Response {
/// Retrieve the cookies contained in the response.
///
/// Note that invalid 'Set-Cookie' headers will be ignored.
pub fn cookies<'a>(&'a self) -> impl Iterator< Item = cookie::Cookie<'a> > + 'a {
cookie::extract_response_cookies(self.headers())
.filter_map(Result::ok)
pub fn cookies<'a>(&'a self) -> impl Iterator<Item = cookie::Cookie<'a>> + 'a {
cookie::extract_response_cookies(self.headers()).filter_map(Result::ok)
}
/// Get the HTTP `Version` of this `Response`.
#[inline]
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
#[inline]
pub fn json<T: DeserializeOwned>(&mut self) -> crate::Result<T> {
wait::timeout(self.inner.json(), self.timeout).map_err(|e| {
match e {
wait::Waited::TimedOut => crate::error::timedout(None),
wait::Waited::Executor(e) => crate::error::from(e),
wait::Waited::Inner(e) => e,
}
wait::timeout(self.inner.json(), self.timeout).map_err(|e| match e {
wait::Waited::TimedOut => crate::error::timedout(None),
wait::Waited::Executor(e) => crate::error::from(e),
wait::Waited::Inner(e) => e,
})
}
@@ -291,7 +291,8 @@ impl Response {
/// ```
#[inline]
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)
}
@@ -314,14 +315,17 @@ impl Response {
/// ```
#[inline]
pub fn error_for_status(self) -> crate::Result<Self> {
let Response { body, inner, timeout, _thread_handle } = self;
inner.error_for_status().map(move |inner| {
Response {
inner,
body,
timeout,
_thread_handle,
}
let Response {
body,
inner,
timeout,
_thread_handle,
} = self;
inner.error_for_status().map(move |inner| Response {
inner,
body,
timeout,
_thread_handle,
})
}
@@ -353,7 +357,7 @@ impl Read for Response {
if self.body.is_none() {
let body = mem::replace(self.inner.body_mut(), async_impl::Decoder::empty());
let body = async_impl::ReadableChunks::new(WaitBody {
inner: wait::stream(body, self.timeout)
inner: wait::stream(body, self.timeout),
});
self.body = Some(body);
}
@@ -365,7 +369,7 @@ impl Read for Response {
}
struct WaitBody {
inner: wait::WaitStream<async_impl::Decoder>
inner: wait::WaitStream<async_impl::Decoder>,
}
impl Stream for WaitBody {
@@ -383,7 +387,7 @@ impl Stream for WaitBody {
};
Err(req_err)
},
}
None => Ok(Async::Ready(None)),
}
}

View File

@@ -1,6 +1,6 @@
use std::fmt;
#[cfg(feature = "rustls-tls")]
use rustls::{TLSError, ServerCertVerifier, RootCertStore, ServerCertVerified};
use rustls::{RootCertStore, ServerCertVerified, ServerCertVerifier, TLSError};
use std::fmt;
#[cfg(feature = "rustls-tls")]
use tokio_rustls::webpki::DNSNameRef;
@@ -17,7 +17,7 @@ pub struct Certificate {
#[derive(Clone)]
enum Cert {
Der(Vec<u8>),
Pem(Vec<u8>)
Pem(Vec<u8>),
}
/// Represent a private key and X509 cert as a client certificate.
@@ -32,7 +32,7 @@ enum ClientCert {
Pem {
key: rustls::PrivateKey,
certs: Vec<rustls::Certificate>,
}
},
}
impl Certificate {
@@ -61,7 +61,6 @@ impl Certificate {
})
}
/// Create a `Certificate` from a PEM encoded certificate
///
/// # Examples
@@ -83,36 +82,32 @@ impl Certificate {
#[cfg(feature = "default-tls")]
native: try_!(native_tls::Certificate::from_pem(pem)),
#[cfg(feature = "rustls-tls")]
original: Cert::Pem(pem.to_owned())
original: Cert::Pem(pem.to_owned()),
})
}
#[cfg(feature = "default-tls")]
pub(crate) fn add_to_native_tls(
self,
tls: &mut native_tls::TlsConnectorBuilder,
) {
pub(crate) fn add_to_native_tls(self, tls: &mut native_tls::TlsConnectorBuilder) {
tls.add_root_certificate(self.native);
}
#[cfg(feature = "rustls-tls")]
pub(crate) fn add_to_rustls(
self,
tls: &mut rustls::ClientConfig,
) -> crate::Result<()> {
use std::io::Cursor;
pub(crate) fn add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()> {
use rustls::internal::pemfile;
use std::io::Cursor;
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)),
Cert::Pem(buf) => {
let mut pem = Cursor::new(buf);
let certs = try_!(pemfile::certs(&mut pem)
.map_err(|_| TLSError::General(String::from("No valid certificate was found"))));
let certs = try_!(pemfile::certs(&mut pem).map_err(|_| TLSError::General(
String::from("No valid certificate was found")
)));
for c in certs {
try_!(tls.root_store.add(&c)
.map_err(TLSError::WebPKIError));
try_!(tls.root_store.add(&c).map_err(TLSError::WebPKIError));
}
}
}
@@ -151,9 +146,7 @@ impl Identity {
#[cfg(feature = "default-tls")]
pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> {
Ok(Identity {
inner: ClientCert::Pkcs12(
try_!(native_tls::Identity::from_pkcs12(der, password))
),
inner: ClientCert::Pkcs12(try_!(native_tls::Identity::from_pkcs12(der, password))),
})
}
@@ -178,8 +171,8 @@ impl Identity {
/// ```
#[cfg(feature = "rustls-tls")]
pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> {
use std::io::Cursor;
use rustls::internal::pemfile;
use std::io::Cursor;
let (key, certs) = {
let mut pem = Cursor::new(buf);
@@ -202,15 +195,14 @@ impl Identity {
if let (Some(sk), false) = (sk.pop(), certs.is_empty()) {
(sk, certs)
} 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 {
inner: ClientCert::Pem {
key,
certs,
},
inner: ClientCert::Pem { key, certs },
})
}
@@ -223,39 +215,34 @@ impl Identity {
ClientCert::Pkcs12(id) => {
tls.identity(id);
Ok(())
},
}
#[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")]
pub(crate) fn add_to_rustls(
self,
tls: &mut rustls::ClientConfig,
) -> crate::Result<()> {
pub(crate) fn add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()> {
match self.inner {
ClientCert::Pem { key, certs } => {
tls.set_single_client_cert(certs, key);
Ok(())
},
}
#[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 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Certificate")
.finish()
f.debug_struct("Certificate").finish()
}
}
impl fmt::Debug for Identity {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Identity")
.finish()
f.debug_struct("Identity").finish()
}
}
@@ -263,16 +250,20 @@ pub(crate) enum TlsBackend {
#[cfg(feature = "default-tls")]
Default,
#[cfg(feature = "rustls-tls")]
Rustls
Rustls,
}
impl Default for TlsBackend {
fn default() -> TlsBackend {
#[cfg(feature = "default-tls")]
{ TlsBackend::Default }
{
TlsBackend::Default
}
#[cfg(all(feature = "rustls-tls", not(feature = "default-tls")))]
{ TlsBackend::Rustls }
{
TlsBackend::Rustls
}
}
}
@@ -286,7 +277,7 @@ impl ServerCertVerifier for NoVerifier {
_roots: &RootCertStore,
_presented_certs: &[rustls::Certificate],
_dns_name: DNSNameRef,
_ocsp_response: &[u8]
_ocsp_response: &[u8],
) -> Result<ServerCertVerified, TLSError> {
Ok(ServerCertVerified::assertion())
}

View File

@@ -2,8 +2,8 @@ use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
use futures::{Async, Future, Poll, Stream};
use futures::executor::{self, Notify};
use futures::{Async, Future, Poll, Stream};
use tokio_executor::{enter, EnterError};
pub(crate) fn timeout<F>(fut: F, timeout: Option<Duration>) -> Result<F::Item, Waited<F::Error>>
@@ -11,13 +11,13 @@ where
F: Future,
{
let mut spawn = executor::spawn(fut);
block_on(timeout, |notify| {
spawn.poll_future_notify(notify, 0)
})
block_on(timeout, |notify| spawn.poll_future_notify(notify, 0))
}
pub(crate) fn stream<S>(stream: S, timeout: Option<Duration>) -> WaitStream<S>
where S: Stream {
where
S: Stream,
{
WaitStream {
stream: executor::spawn(stream),
timeout,
@@ -43,7 +43,9 @@ pub(crate) struct WaitStream<S> {
}
impl<S> Iterator for WaitStream<S>
where S: Stream {
where
S: Stream,
{
type Item = Result<S::Item, Waited<S::Error>>;
fn next(&mut self) -> Option<Self::Item> {
@@ -74,9 +76,7 @@ where
F: FnMut(&Arc<ThreadNotify>) -> Poll<U, E>,
{
let _entered = enter().map_err(Waited::Executor)?;
let deadline = timeout.map(|d| {
Instant::now() + d
});
let deadline = timeout.map(|d| Instant::now() + d);
let notify = Arc::new(ThreadNotify {
thread: thread::current(),
});
@@ -99,5 +99,3 @@ where
}
}
}

View File

@@ -1,10 +1,3 @@
extern crate futures;
extern crate libflate;
extern crate reqwest;
extern crate hyper;
extern crate tokio;
extern crate bytes;
#[macro_use]
mod support;
@@ -14,8 +7,8 @@ use std::time::Duration;
use futures::{Future, Stream};
use tokio::runtime::current_thread::Runtime;
use reqwest::r#async::{Chunk, Client};
use reqwest::r#async::multipart::{Form, Part};
use reqwest::r#async::{Chunk, Client};
use bytes::Bytes;
@@ -54,7 +47,8 @@ fn response_text() {
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()
.and_then(|mut res| res.text())
.and_then(|text| {
@@ -90,7 +84,8 @@ fn response_json() {
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()
.and_then(|mut res| res.json::<String>())
.and_then(|text| {
@@ -105,34 +100,36 @@ fn response_json() {
fn multipart() {
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 form = Form::new()
.text("foo", "bar")
.part("part_stream", part);
let form = Form::new().text("foo", "bar").part("part_stream", part);
let expected_body = format!("\
24\r\n\
--{0}\r\n\r\n\
2E\r\n\
Content-Disposition: form-data; name=\"foo\"\r\n\r\n\r\n\
3\r\n\
bar\r\n\
2\r\n\
\r\n\r\n\
24\r\n\
--{0}\r\n\r\n\
36\r\n\
Content-Disposition: form-data; name=\"part_stream\"\r\n\r\n\r\n\
B\r\n\
part1 part2\r\n\
2\r\n\
\r\n\r\n\
26\r\n\
--{0}--\r\n\r\n\
0\r\n\r\n\
", form.boundary());
let expected_body = format!(
"\
24\r\n\
--{0}\r\n\r\n\
2E\r\n\
Content-Disposition: form-data; name=\"foo\"\r\n\r\n\r\n\
3\r\n\
bar\r\n\
2\r\n\
\r\n\r\n\
24\r\n\
--{0}\r\n\r\n\
36\r\n\
Content-Disposition: form-data; name=\"part_stream\"\r\n\r\n\r\n\
B\r\n\
part1 part2\r\n\
2\r\n\
\r\n\r\n\
26\r\n\
--{0}--\r\n\r\n\
0\r\n\r\n\
",
form.boundary()
);
let server = server! {
request: format!("\
@@ -160,15 +157,12 @@ fn multipart() {
let client = Client::new();
let res_future = client.post(&url)
.multipart(form)
.send()
.and_then(|res| {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
let res_future = client.post(&url).multipart(form).send().and_then(|res| {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
Ok(())
});
Ok(())
});
rt.block_on(res_future).unwrap();
}
@@ -203,9 +197,7 @@ fn request_timeout() {
.unwrap();
let url = format!("http://{}/slow", server.addr());
let fut = client
.get(&url)
.send();
let fut = client.get(&url).send();
let err = rt.block_on(fut).unwrap_err();
@@ -254,7 +246,10 @@ fn response_timeout() {
}
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();
match encoder.write(content.as_bytes()) {
Ok(n) => assert!(n > 0, "Failed to write to encoder."),
@@ -263,13 +258,16 @@ fn gzip_case(response_size: usize, chunk_size: usize) {
let gzipped_content = encoder.finish().into_result().unwrap();
let mut response = format!("\
HTTP/1.1 200 OK\r\n\
Server: test-accept\r\n\
Content-Encoding: gzip\r\n\
Content-Length: {}\r\n\
\r\n", &gzipped_content.len())
.into_bytes();
let mut response = format!(
"\
HTTP/1.1 200 OK\r\n\
Server: test-accept\r\n\
Content-Encoding: gzip\r\n\
Content-Length: {}\r\n\
\r\n",
&gzipped_content.len()
)
.into_bytes();
response.extend(&gzipped_content);
let server = server! {
@@ -290,7 +288,8 @@ fn gzip_case(response_size: usize, chunk_size: usize) {
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()
.and_then(|res| {
let body = res.into_body();
@@ -311,9 +310,11 @@ fn gzip_case(response_size: usize, chunk_size: usize) {
fn body_stream() {
let _ = env_logger::try_init();
let source: Box<dyn Stream<Item = Bytes, Error = io::Error> + Send>
= Box::new(futures::stream::iter_ok::<_, io::Error>(
vec![Bytes::from_static(b"123"), Bytes::from_static(b"4567")]));
let source: Box<dyn Stream<Item = Bytes, Error = io::Error> + Send> =
Box::new(futures::stream::iter_ok::<_, io::Error>(vec![
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";
@@ -342,15 +343,12 @@ fn body_stream() {
let client = Client::new();
let res_future = client.post(&url)
.body(source)
.send()
.and_then(|res| {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
let res_future = client.post(&url).body(source).send().and_then(|res| {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
Ok(())
});
Ok(())
});
rt.block_on(res_future).unwrap();
}

View File

@@ -1,11 +1,10 @@
extern crate reqwest;
#[cfg(feature = "tls")]
#[test]
fn test_badssl_modern() {
let text = reqwest::get("https://mozilla-modern.badssl.com/").unwrap()
.text().unwrap();
let text = reqwest::get("https://mozilla-modern.badssl.com/")
.unwrap()
.text()
.unwrap();
assert!(text.contains("<title>mozilla-modern.badssl.com</title>"));
}
@@ -15,10 +14,13 @@ fn test_badssl_modern() {
fn test_rustls_badssl_modern() {
let text = reqwest::Client::builder()
.use_rustls_tls()
.build().unwrap()
.build()
.unwrap()
.get("https://mozilla-modern.badssl.com/")
.send().unwrap()
.text().unwrap();
.send()
.unwrap()
.text()
.unwrap();
assert!(text.contains("<title>mozilla-modern.badssl.com</title>"));
}
@@ -28,10 +30,13 @@ fn test_rustls_badssl_modern() {
fn test_badssl_self_signed() {
let text = reqwest::Client::builder()
.danger_accept_invalid_certs(true)
.build().unwrap()
.build()
.unwrap()
.get("https://self-signed.badssl.com/")
.send().unwrap()
.text().unwrap();
.send()
.unwrap()
.text()
.unwrap();
assert!(text.contains("<title>self-signed.badssl.com</title>"));
}
@@ -41,17 +46,20 @@ fn test_badssl_self_signed() {
fn test_badssl_wrong_host() {
let text = reqwest::Client::builder()
.danger_accept_invalid_hostnames(true)
.build().unwrap()
.build()
.unwrap()
.get("https://wrong.host.badssl.com/")
.send().unwrap()
.text().unwrap();
.send()
.unwrap()
.text()
.unwrap();
assert!(text.contains("<title>wrong.host.badssl.com</title>"));
let result = reqwest::Client::builder()
.danger_accept_invalid_hostnames(true)
.build().unwrap()
.build()
.unwrap()
.get("https://self-signed.badssl.com/")
.send();

View File

@@ -1,5 +1,3 @@
extern crate reqwest;
#[macro_use]
mod support;
@@ -30,7 +28,10 @@ fn test_response_text() {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
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();
assert_eq!(b"Hello", body.as_bytes());
@@ -62,11 +63,14 @@ fn test_response_non_utf_8_text() {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
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();
assert_eq!("你好", &body);
assert_eq!(b"\xe4\xbd\xa0\xe5\xa5\xbd", body.as_bytes()); // Now it's utf-8
assert_eq!(b"\xe4\xbd\xa0\xe5\xa5\xbd", body.as_bytes()); // Now it's utf-8
}
#[test]
@@ -94,7 +98,10 @@ fn test_response_json() {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
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();
assert_eq!("Hello", body);
@@ -125,7 +132,10 @@ fn test_response_copy_to() {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
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![];
res.copy_to(&mut buf).unwrap();
@@ -157,7 +167,10 @@ fn test_get() {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
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()));
let mut buf = [0; 1024];
@@ -196,7 +209,10 @@ fn test_post() {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
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 n = res.read(&mut buf).unwrap();
@@ -293,7 +309,10 @@ fn test_error_for_status_5xx() {
let err = res.error_for_status().err().unwrap();
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]
@@ -303,7 +322,8 @@ fn test_default_headers() {
headers.insert(header::COOKIE, header::HeaderValue::from_static("a=b;c=d"));
let client = reqwest::Client::builder()
.default_headers(headers)
.build().unwrap();
.build()
.unwrap();
let server = server! {
request: b"\
@@ -329,7 +349,10 @@ fn test_default_headers() {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
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! {
request: b"\
@@ -355,17 +378,24 @@ fn test_default_headers() {
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::OK);
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]
fn test_override_default_headers() {
use reqwest::header;
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()
.default_headers(headers)
.build().unwrap();
.build()
.unwrap();
let server = server! {
request: b"\
@@ -386,13 +416,22 @@ fn test_override_default_headers() {
};
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.status(), reqwest::StatusCode::OK);
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]
@@ -418,20 +457,32 @@ fn test_appended_headers_not_overwritten() {
};
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.status(), reqwest::StatusCode::OK);
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
use reqwest::header;
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()
.default_headers(headers)
.build().unwrap();
.build()
.unwrap();
let server = server! {
request: b"\
@@ -452,10 +503,18 @@ fn test_appended_headers_not_overwritten() {
};
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.status(), reqwest::StatusCode::OK);
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"
);
}

View File

@@ -1,5 +1,3 @@
extern crate reqwest;
#[macro_use]
mod support;
@@ -45,7 +43,7 @@ fn cookie_response_accessor() {
// expires
assert_eq!(cookies[1].name(), "expires");
assert_eq!(
cookies[1].expires().unwrap(),
cookies[1].expires().unwrap(),
std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1445412480)
);
@@ -55,7 +53,10 @@ fn cookie_response_accessor() {
// max-age
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
assert_eq!(cookies[4].name(), "domain");
@@ -81,7 +82,10 @@ fn cookie_response_accessor() {
#[test]
fn cookie_store_simple() {
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! {
request: b"\
@@ -125,7 +129,10 @@ fn cookie_store_simple() {
#[test]
fn cookie_store_overwrite_existing() {
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! {
request: b"\
@@ -189,7 +196,10 @@ fn cookie_store_overwrite_existing() {
#[test]
fn cookie_store_max_age() {
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! {
request: b"\
@@ -232,7 +242,10 @@ fn cookie_store_max_age() {
#[test]
fn cookie_store_expires() {
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! {
request: b"\
@@ -275,7 +288,10 @@ fn cookie_store_expires() {
#[test]
fn cookie_store_path() {
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! {
request: b"\

View File

@@ -1,11 +1,8 @@
extern crate reqwest;
extern crate libflate;
#[macro_use]
mod support;
use std::time::Duration;
use std::io::{Read, Write};
use std::time::Duration;
#[test]
fn test_gzip_response() {
@@ -19,13 +16,16 @@ fn test_gzip_response() {
let gzipped_content = encoder.finish().into_result().unwrap();
let mut response = format!("\
HTTP/1.1 200 OK\r\n\
Server: test-accept\r\n\
Content-Encoding: gzip\r\n\
Content-Length: {}\r\n\
\r\n", &gzipped_content.len())
.into_bytes();
let mut response = format!(
"\
HTTP/1.1 200 OK\r\n\
Server: test-accept\r\n\
Content-Encoding: gzip\r\n\
Content-Length: {}\r\n\
\r\n",
&gzipped_content.len()
)
.into_bytes();
response.extend(&gzipped_content);
let server = server! {
@@ -130,7 +130,10 @@ fn test_accept_header_is_not_changed_if_set() {
let res = client
.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()
.unwrap();
@@ -157,8 +160,12 @@ fn test_accept_encoding_header_is_not_changed_if_set() {
};
let client = reqwest::Client::new();
let res = client.get(&format!("http://{}/accept-encoding", server.addr()))
.header(reqwest::header::ACCEPT_ENCODING, reqwest::header::HeaderValue::from_static("identity"))
let res = client
.get(&format!("http://{}/accept-encoding", server.addr()))
.header(
reqwest::header::ACCEPT_ENCODING,
reqwest::header::HeaderValue::from_static("identity"),
)
.send()
.unwrap();

View File

@@ -1,6 +1,3 @@
extern crate env_logger;
extern crate reqwest;
#[macro_use]
mod support;
@@ -8,15 +5,17 @@ mod support;
fn text_part() {
let _ = env_logger::try_init();
let form = reqwest::multipart::Form::new()
.text("foo", "bar");
let form = reqwest::multipart::Form::new().text("foo", "bar");
let expected_body = format!("\
--{0}\r\n\
Content-Disposition: form-data; name=\"foo\"\r\n\r\n\
bar\r\n\
--{0}--\r\n\
", form.boundary());
let expected_body = format!(
"\
--{0}\r\n\
Content-Disposition: form-data; name=\"foo\"\r\n\r\n\
bar\r\n\
--{0}--\r\n\
",
form.boundary()
);
let server = server! {
request: format!("\
@@ -55,17 +54,22 @@ fn file() {
let _ = env_logger::try_init();
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 expected_body = format!("\
--{0}\r\n\
Content-Disposition: form-data; name=\"foo\"; filename=\"Cargo.lock\"\r\n\
Content-Type: application/octet-stream\r\n\r\n\
{1}\r\n\
--{0}--\r\n\
", form.boundary(), fcontents);
let expected_body = format!(
"\
--{0}\r\n\
Content-Disposition: form-data; name=\"foo\"; filename=\"Cargo.lock\"\r\n\
Content-Type: application/octet-stream\r\n\r\n\
{1}\r\n\
--{0}--\r\n\
",
form.boundary(),
fcontents
);
let server = server! {
request: format!("\

View File

@@ -1,5 +1,3 @@
extern crate reqwest;
#[macro_use]
mod support;
@@ -37,7 +35,10 @@ fn http_proxy() {
assert_eq!(res.url().as_str(), url);
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]
@@ -66,8 +67,8 @@ fn http_proxy_basic_auth() {
let res = reqwest::Client::builder()
.proxy(
reqwest::Proxy::http(&proxy)
.unwrap()
.basic_auth("Aladdin", "open sesame")
.unwrap()
.basic_auth("Aladdin", "open sesame"),
)
.build()
.unwrap()
@@ -77,7 +78,10 @@ fn http_proxy_basic_auth() {
assert_eq!(res.url().as_str(), url);
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]
@@ -104,9 +108,7 @@ fn http_proxy_basic_auth_parsed() {
let url = "http://hyper.rs/prox";
let res = reqwest::Client::builder()
.proxy(
reqwest::Proxy::http(&proxy).unwrap()
)
.proxy(reqwest::Proxy::http(&proxy).unwrap())
.build()
.unwrap()
.get(url)
@@ -115,7 +117,10 @@ fn http_proxy_basic_auth_parsed() {
assert_eq!(res.url().as_str(), url);
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]
@@ -141,9 +146,7 @@ fn test_no_proxy() {
// set up proxy and use no_proxy to clear up client builder proxies.
let res = reqwest::Client::builder()
.proxy(
reqwest::Proxy::http(&proxy).unwrap()
)
.proxy(reqwest::Proxy::http(&proxy).unwrap())
.no_proxy()
.build()
.unwrap()
@@ -189,11 +192,14 @@ fn test_using_system_proxy() {
assert_eq!(res.url().as_str(), url);
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.
match system_proxy {
Err(_) => env::remove_var("http_proxy"),
Ok(proxy) => env::set_var("http_proxy", proxy)
Ok(proxy) => env::set_var("http_proxy", proxy),
}
}

View File

@@ -1,5 +1,3 @@
extern crate reqwest;
#[macro_use]
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 dst = format!("http://{}/{}", redirect.addr(), "dst");
let res = client.post(&url)
.send()
.unwrap();
let res = client.post(&url).send().unwrap();
assert_eq!(res.url().as_str(), dst);
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 dst = format!("http://{}/{}", redirect.addr(), "dst");
let res = client.get(&url)
.send()
.unwrap();
let res = client.get(&url).send().unwrap();
assert_eq!(res.url().as_str(), dst);
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 dst = format!("http://{}/{}", redirect.addr(), "dst");
let res = client.post(&url)
.body("Hello")
.send()
.unwrap();
let res = client.post(&url).body("Hello").send().unwrap();
assert_eq!(res.url().as_str(), dst);
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]
fn test_redirect_removes_sensitive_headers() {
let end_server = server! {
@@ -249,7 +247,10 @@ fn test_redirect_removes_sensitive_headers() {
.build()
.unwrap()
.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()
.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.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]
@@ -385,58 +389,65 @@ fn test_invalid_location_stops_redirect_gh484() {
assert_eq!(res.url().as_str(), url);
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]
fn test_redirect_302_with_set_cookies() {
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! {
request: format!("\
GET /{} HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: format!("\
HTTP/1.1 {} reason\r\n\
Server: test-redirect\r\n\
Content-Length: 0\r\n\
Location: /dst\r\n\
Connection: close\r\n\
Set-Cookie: key=value\r\n\
\r\n\
", code)
;
request: format!("\
GET /{} HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: format!("\
HTTP/1.1 {} reason\r\n\
Server: test-redirect\r\n\
Content-Length: 0\r\n\
Location: /dst\r\n\
Connection: close\r\n\
Set-Cookie: key=value\r\n\
\r\n\
", code)
;
request: format!("\
GET /dst HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
referer: http://$HOST/{}\r\n\
cookie: key=value\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: b"\
HTTP/1.1 200 OK\r\n\
Server: test-dst\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
request: format!("\
GET /dst HTTP/1.1\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
referer: http://$HOST/{}\r\n\
cookie: key=value\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: b"\
HTTP/1.1 200 OK\r\n\
Server: test-dst\r\n\
Content-Length: 0\r\n\
\r\n\
"
};
let url = format!("http://{}/{}", server.addr(), code);
let dst = format!("http://{}/{}", server.addr(), "dst");
let res = client.get(&url)
.send()
.unwrap();
let res = client.get(&url).send().unwrap();
assert_eq!(res.url().as_str(), dst);
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"
);
}

View File

@@ -2,9 +2,9 @@
use std::io::{Read, Write};
use std::net;
use std::time::Duration;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
pub struct Server {
addr: net::SocketAddr,
@@ -19,9 +19,8 @@ impl Server {
impl Drop for Server {
fn drop(&mut self) {
if !::std::thread::panicking() {
self
.panic_rx
if !thread::panicking() {
self.panic_rx
.recv_timeout(Duration::from_secs(3))
.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 addr = listener.local_addr().unwrap();
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 || {
'txns: for txn in txns {
let mut expected = txn.request;
@@ -149,10 +151,7 @@ pub fn spawn(txns: Vec<Txn>) -> Server {
let _ = panic_tx.send(());
}).expect("server thread spawn");
Server {
addr,
panic_rx,
}
Server { addr, panic_rx }
}
fn replace_expected_vars(bytes: &mut Vec<u8>, host: &[u8], ua: &[u8]) {
@@ -210,16 +209,15 @@ macro_rules! __internal__txn {
)
}
#[macro_export]
macro_rules! __internal__prop {
(request: $val:expr) => (
(request: $val:expr) => {
From::from(&$val[..])
);
(response: $val:expr) => (
};
(response: $val:expr) => {
From::from(&$val[..])
);
($field:ident: $val:expr) => (
};
($field:ident: $val:expr) => {
From::from($val)
)
};
}

View File

@@ -1,6 +1,3 @@
extern crate env_logger;
extern crate reqwest;
#[macro_use]
mod support;
@@ -38,10 +35,7 @@ fn timeout_closes_connection() {
};
let url = format!("http://{}/closes", server.addr());
let err = client
.get(&url)
.send()
.unwrap_err();
let err = client.get(&url).send().unwrap_err();
assert_eq!(err.get_ref().unwrap().to_string(), "timed out");
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()));
}
#[test]
fn test_response_timeout() {
let _ = env_logger::try_init();
@@ -158,7 +151,10 @@ fn test_read_timeout() {
assert_eq!(res.url().as_str(), &url);
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 err = res.read(&mut buf).unwrap_err();