Upgrade hyper to 0.12

Closes #304
This commit is contained in:
Yash Srivastav
2018-07-05 13:03:31 -04:00
committed by Sean McArthur
parent 7bd3619ece
commit c417d6dab8
27 changed files with 634 additions and 650 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "reqwest"
version = "0.8.6" # remember to update html_root_url
version = "0.9.0-pre" # remember to update html_root_url
description = "higher level HTTP client library"
keywords = ["http", "request", "client"]
repository = "https://github.com/seanmonstar/reqwest"
@@ -9,20 +9,26 @@ authors = ["Sean McArthur <sean@seanmonstar.com>"]
license = "MIT/Apache-2.0"
categories = ["web-programming::http-client"]
publish = false # pre
[dependencies]
base64 = "~0.6.0"
bytes = "0.4"
encoding_rs = "0.7"
futures = "0.1.15"
hyper = "0.11.22"
hyper-tls = "0.1.2"
futures = "0.1.21"
http = "0.1.5"
hyper = "0.12.2"
hyper-tls = "0.2.1"
libflate = "0.1.11"
log = "0.4"
mime_guess = "2.0.0-alpha.2"
mime = "0.3.7"
mime_guess = "2.0.0-alpha.4"
native-tls = "0.1.5"
serde = "1.0"
serde_json = "1.0"
serde_urlencoded = "0.5"
tokio-core = "0.1.6"
tokio-core = "0.1.17"
tokio = "0.1.7"
tokio-io = "0.1"
tokio-tls = "0.1"
url = "1.2"

View File

@@ -20,7 +20,7 @@ error_chain! {
fn run() -> Result<()> {
let mut core = tokio_core::reactor::Core::new()?;
let client = Client::new(&core.handle());
let client = Client::new();
let work = client.get("https://hyper.rs")
.send()

View File

@@ -22,7 +22,7 @@ fn run() -> Result<()> {
let mut res = reqwest::get("https://www.rust-lang.org/en-US/")?;
println!("Status: {}", res.status());
println!("Headers:\n{}", res.headers());
println!("Headers:\n{:?}", res.headers());
// copy the response body directly to stdout
let _ = std::io::copy(&mut res, &mut std::io::stdout())?;

View File

@@ -2,6 +2,7 @@ use std::fmt;
use futures::{Stream, Poll, Async};
use bytes::Bytes;
use hyper::body::Payload;
/// An asynchronous `Stream`.
pub struct Body {
@@ -20,6 +21,13 @@ impl Body {
Inner::Reusable(_) => unreachable!(),
}
}
pub(crate) fn content_length(&self) -> Option<u64> {
match self.inner {
Inner::Reusable(ref bytes) => Some(bytes.len() as u64),
Inner::Hyper(ref body) => body.content_length(),
}
}
}
impl Stream for Body {

View File

@@ -4,11 +4,11 @@ use std::time::Duration;
use bytes::Bytes;
use futures::{Async, Future, Poll};
use hyper::client::FutureResponse;
use hyper::header::{Headers, Location, Referer, UserAgent, Accept, Encoding,
AcceptEncoding, Range, qitem};
use hyper::client::ResponseFuture;
use header::{HeaderMap, HeaderValue, LOCATION, USER_AGENT, REFERER, ACCEPT,
ACCEPT_ENCODING, RANGE};
use mime::{self};
use native_tls::{TlsConnector, TlsConnectorBuilder};
use tokio_core::reactor::Handle;
use super::body;
@@ -63,7 +63,7 @@ pub struct ClientBuilder {
struct Config {
gzip: bool,
headers: Headers,
headers: HeaderMap,
hostname_verification: bool,
proxies: Vec<Proxy>,
redirect_policy: RedirectPolicy,
@@ -78,9 +78,9 @@ impl ClientBuilder {
pub fn new() -> ClientBuilder {
match TlsConnector::builder() {
Ok(tls_connector_builder) => {
let mut headers = Headers::with_capacity(2);
headers.set(UserAgent::new(DEFAULT_USER_AGENT));
headers.set(Accept::star());
let mut headers: HeaderMap<HeaderValue> = HeaderMap::with_capacity(2);
headers.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT));
headers.insert(ACCEPT, HeaderValue::from_str(mime::STAR_STAR.as_ref()).expect("unable to parse mime"));
ClientBuilder {
config: Some(Config {
@@ -114,7 +114,7 @@ impl ClientBuilder {
///
/// This method consumes the internal state of the builder.
/// Trying to use this builder again after calling `build` will panic.
pub fn build(&mut self, handle: &Handle) -> ::Result<Client> {
pub fn build(&mut self) -> ::Result<Client> {
if let Some(err) = self.err.take() {
return Err(err);
}
@@ -126,14 +126,13 @@ impl ClientBuilder {
let proxies = Arc::new(config.proxies);
let mut connector = Connector::new(config.dns_threads, tls, proxies.clone(), handle);
let mut connector = Connector::new(config.dns_threads, tls, proxies.clone());
if !config.hostname_verification {
connector.danger_disable_hostname_verification();
}
let hyper_client = ::hyper::Client::configure()
.connector(connector)
.build(handle);
let hyper_client = ::hyper::Client::builder()
.build(connector);
Ok(Client {
inner: Arc::new(ClientRef {
@@ -200,9 +199,11 @@ impl ClientBuilder {
/// Sets the default headers for every request.
#[inline]
pub fn default_headers(&mut self, headers: Headers) -> &mut ClientBuilder {
pub fn default_headers(&mut self, headers: HeaderMap) -> &mut ClientBuilder {
if let Some(config) = config_mut(&mut self.config, &self.err) {
config.headers.extend(headers.iter());
for (key, value) in headers.iter() {
config.headers.insert(key, value.clone());
}
}
self
}
@@ -287,9 +288,9 @@ impl Client {
/// initialized. Use `Client::builder()` if you wish to handle the failure
/// as an `Error` instead of panicking.
#[inline]
pub fn new(handle: &Handle) -> Client {
pub fn new() -> Client {
ClientBuilder::new()
.build(handle)
.build()
.expect("TLS failed to initialize")
}
@@ -305,7 +306,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Get, url)
self.request(Method::GET, url)
}
/// Convenience method to make a `POST` request to a URL.
@@ -314,7 +315,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Post, url)
self.request(Method::POST, url)
}
/// Convenience method to make a `PUT` request to a URL.
@@ -323,7 +324,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Put, url)
self.request(Method::PUT, url)
}
/// Convenience method to make a `PATCH` request to a URL.
@@ -332,7 +333,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Patch, url)
self.request(Method::PATCH, url)
}
/// Convenience method to make a `DELETE` request to a URL.
@@ -341,7 +342,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Delete, url)
self.request(Method::DELETE, url)
}
/// Convenience method to make a `HEAD` request to a URL.
@@ -350,7 +351,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Head, url)
self.request(Method::HEAD, url)
}
/// Start building a `Request` with the `Method` and `Url`.
@@ -395,28 +396,35 @@ impl Client {
) = request::pieces(req);
let mut headers = self.inner.headers.clone(); // default headers
headers.extend(user_headers.iter());
for (key, value) in user_headers.iter() {
headers.insert(key, value.clone());
}
if self.inner.gzip &&
!headers.has::<AcceptEncoding>() &&
!headers.has::<Range>() {
headers.set(AcceptEncoding(vec![qitem(Encoding::Gzip)]));
!headers.contains_key(ACCEPT_ENCODING) &&
!headers.contains_key(RANGE) {
headers.insert(ACCEPT_ENCODING, HeaderValue::from_static("gzip"));
}
let uri = to_uri(&url);
let mut req = ::hyper::Request::new(method.clone(), uri.clone());
*req.headers_mut() = headers.clone();
let body = body.map(|body| {
let (reusable, body) = body::into_hyper(body);
req.set_body(body);
reusable
});
if proxy::is_proxied(&self.inner.proxies, &url) {
if uri.scheme() == Some("http") {
req.set_proxy(true);
}
let (reusable, body) = match body {
Some(body) => {
let (reusable, body) = body::into_hyper(body);
(Some(reusable), body)
},
None => {
(None, ::hyper::Body::empty())
}
};
let mut req = ::hyper::Request::builder()
.method(method.clone())
.uri(uri.clone())
.body(body)
.expect("valid request parts");
*req.headers_mut() = headers.clone();
let in_flight = self.inner.hyper.request(req);
@@ -425,7 +433,7 @@ impl Client {
method: method,
url: url,
headers: headers,
body: body,
body: reusable,
urls: Vec::new(),
@@ -456,7 +464,7 @@ impl fmt::Debug for ClientBuilder {
struct ClientRef {
gzip: bool,
headers: Headers,
headers: HeaderMap,
hyper: HyperClient,
proxies: Arc<Vec<Proxy>>,
redirect_policy: RedirectPolicy,
@@ -475,14 +483,14 @@ enum PendingInner {
pub struct PendingRequest {
method: Method,
url: Url,
headers: Headers,
headers: HeaderMap,
body: Option<Option<Bytes>>,
urls: Vec<Url>,
client: Arc<ClientRef>,
in_flight: FutureResponse,
in_flight: ResponseFuture,
}
@@ -509,20 +517,20 @@ impl Future for PendingRequest {
Async::NotReady => return Ok(Async::NotReady),
};
let should_redirect = match res.status() {
StatusCode::MovedPermanently |
StatusCode::Found |
StatusCode::SeeOther => {
StatusCode::MOVED_PERMANENTLY |
StatusCode::FOUND |
StatusCode::SEE_OTHER => {
self.body = None;
match self.method {
Method::Get | Method::Head => {},
Method::GET | Method::HEAD => {},
_ => {
self.method = Method::Get;
self.method = Method::GET;
}
}
true
},
StatusCode::TemporaryRedirect |
StatusCode::PermanentRedirect => match self.body {
StatusCode::TEMPORARY_REDIRECT |
StatusCode::PERMANENT_REDIRECT => match self.body {
Some(Some(_)) | None => true,
Some(None) => false,
},
@@ -530,12 +538,12 @@ impl Future for PendingRequest {
};
if should_redirect {
let loc = res.headers()
.get::<Location>()
.map(|loc| self.url.join(loc));
.get(LOCATION)
.map(|loc| self.url.join(loc.to_str().expect("")));
if let Some(Ok(loc)) = loc {
if self.client.referer {
if let Some(referer) = make_referer(&loc, &self.url) {
self.headers.set(referer);
self.headers.insert(REFERER, referer);
}
}
self.urls.push(self.url.clone());
@@ -553,19 +561,17 @@ impl Future for PendingRequest {
remove_sensitive_headers(&mut self.headers, &self.url, &self.urls);
debug!("redirecting to {:?} '{}'", self.method, self.url);
let uri = to_uri(&self.url);
let mut req = ::hyper::Request::new(
self.method.clone(),
uri.clone()
);
let body = match self.body {
Some(Some(ref body)) => ::hyper::Body::from(body.clone()),
_ => ::hyper::Body::empty(),
};
let mut req = ::hyper::Request::builder()
.method(self.method.clone())
.uri(uri.clone())
.body(body)
.expect("valid request parts");
*req.headers_mut() = self.headers.clone();
if let Some(Some(ref body)) = self.body {
req.set_body(body.clone());
}
if proxy::is_proxied(&self.client.proxies, &self.url) {
if uri.scheme() == Some("http") {
req.set_proxy(true);
}
}
self.in_flight = self.client.hyper.request(req);
continue;
},
@@ -607,7 +613,7 @@ impl fmt::Debug for Pending {
}
}
fn make_referer(next: &Url, previous: &Url) -> Option<Referer> {
fn make_referer(next: &Url, previous: &Url) -> Option<HeaderValue> {
if next.scheme() == "http" && previous.scheme() == "https" {
return None;
}
@@ -616,7 +622,7 @@ fn make_referer(next: &Url, previous: &Url) -> Option<Referer> {
let _ = referer.set_username("");
let _ = referer.set_password(None);
referer.set_fragment(None);
Some(Referer::new(referer.into_string()))
referer.as_str().parse().ok()
}
// pub(crate)

View File

@@ -33,12 +33,12 @@ use tokio_io::AsyncRead;
use tokio_io::io as async_io;
use futures::{Async, Future, Poll, Stream};
use futures::stream::Concat2;
use hyper::StatusCode;
use hyper::{HeaderMap, StatusCode};
use hyper::header::{CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING, HeaderValue};
use serde::de::DeserializeOwned;
use serde_json;
use url::Url;
use header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding};
use super::{body, Body, Chunk};
use error;
@@ -111,6 +111,13 @@ impl Decoder {
inner: Inner::Pending(Pending::Gzip(ReadableChunks::new(body)))
}
}
pub(crate) fn content_length(&self) -> Option<u64> {
match self.inner {
Inner::PlainText(ref body) => body.content_length(),
_ => None,
}
}
}
impl Stream for Decoder {
@@ -407,31 +414,33 @@ impl<S> ReadableChunks<S>
/// how to decode the content body of the request.
///
/// Uses the correct variant by inspecting the Content-Encoding header.
pub fn detect(headers: &mut Headers, body: Body, check_gzip: bool) -> Decoder {
pub fn detect(headers: &mut HeaderMap, body: Body, check_gzip: bool) -> Decoder {
if !check_gzip {
return Decoder::plain_text(body);
}
let content_encoding_gzip: bool;
let mut is_gzip = {
content_encoding_gzip = headers
.get::<ContentEncoding>()
.map_or(false, |encs| encs.contains(&Encoding::Gzip));
.get_all(CONTENT_ENCODING)
.iter()
.fold(false, |acc, enc| acc || enc == HeaderValue::from_static("gzip"));
content_encoding_gzip ||
headers
.get::<TransferEncoding>()
.map_or(false, |encs| encs.contains(&Encoding::Gzip))
.get_all(TRANSFER_ENCODING)
.iter()
.fold(false, |acc, enc| acc || enc == HeaderValue::from_static("gzip"))
};
if is_gzip {
if let Some(content_length) = headers.get::<ContentLength>() {
if content_length.0 == 0 {
if let Some(content_length) = headers.get(CONTENT_LENGTH) {
if content_length == "0" {
warn!("GZipped response with content-length of 0");
is_gzip = false;
}
}
}
if content_encoding_gzip {
headers.remove::<ContentEncoding>();
headers.remove::<ContentLength>();
headers.remove(CONTENT_ENCODING);
headers.remove(CONTENT_LENGTH);
}
if is_gzip {
Decoder::gzip(body)

View File

@@ -1,19 +1,22 @@
use std::fmt;
use base64::{encode};
use mime::{self};
use serde::Serialize;
use serde_json;
use serde_urlencoded;
use super::body::{self, Body};
use super::client::{Client, Pending, pending_err};
use header::{ContentType, Headers};
use header::{CONTENT_TYPE, HeaderMap, HeaderName, HeaderValue};
use http::HttpTryFrom;
use {Method, Url};
/// A request which can be executed with `Client::execute()`.
pub struct Request {
method: Method,
url: Url,
headers: Headers,
headers: HeaderMap,
body: Option<Body>,
}
@@ -31,7 +34,7 @@ impl Request {
Request {
method,
url,
headers: Headers::new(),
headers: HeaderMap::new(),
body: None,
}
}
@@ -62,13 +65,13 @@ impl Request {
/// Get the headers.
#[inline]
pub fn headers(&self) -> &Headers {
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
/// Get a mutable reference to the headers.
#[inline]
pub fn headers_mut(&mut self) -> &mut Headers {
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
@@ -87,21 +90,32 @@ impl Request {
impl RequestBuilder {
/// Add a `Header` to this Request.
pub fn header<H>(&mut self, header: H) -> &mut RequestBuilder
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut RequestBuilder
where
H: ::header::Header,
HeaderName: HttpTryFrom<K>,
HeaderValue: HttpTryFrom<V>,
{
if let Some(req) = request_mut(&mut self.request, &self.err) {
req.headers_mut().set(header);
match <HeaderName as HttpTryFrom<K>>::try_from(key) {
Ok(key) => {
match <HeaderValue as HttpTryFrom<V>>::try_from(value) {
Ok(value) => { req.headers_mut().append(key, value); }
Err(e) => self.err = Some(::error::from(e.into())),
}
},
Err(e) => self.err = Some(::error::from(e.into())),
};
}
self
}
/// Add a set of Headers to the existing ones on this Request.
///
/// The headers will be merged in to any already set.
pub fn headers(&mut self, headers: ::header::Headers) -> &mut RequestBuilder {
pub fn headers(&mut self, headers: ::header::HeaderMap) -> &mut RequestBuilder {
if let Some(req) = request_mut(&mut self.request, &self.err) {
req.headers_mut().extend(headers.iter());
for (key, value) in headers.iter() {
req.headers_mut().insert(key, value.clone());
}
}
self
}
@@ -112,10 +126,10 @@ impl RequestBuilder {
U: Into<String>,
P: Into<String>,
{
self.header(::header::Authorization(::header::Basic {
username: username.into(),
password: password.map(|p| p.into()),
}))
let username = username.into();
let password = password.map(|p| p.into()).unwrap_or(String::new());
let header_value = format!("basic {}:{}", username, encode(&password));
self.header(::header::AUTHORIZATION, HeaderValue::from_str(header_value.as_str()).expect(""))
}
/// Set the request body.
@@ -162,7 +176,7 @@ impl RequestBuilder {
if let Some(req) = request_mut(&mut self.request, &self.err) {
match serde_urlencoded::to_string(form) {
Ok(body) => {
req.headers_mut().set(ContentType::form_url_encoded());
req.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_str(mime::APPLICATION_WWW_FORM_URLENCODED.as_ref()).expect(""));
*req.body_mut() = Some(body.into());
},
Err(err) => self.err = Some(::error::from(err)),
@@ -181,7 +195,7 @@ impl RequestBuilder {
if let Some(req) = request_mut(&mut self.request, &self.err) {
match serde_json::to_vec(json) {
Ok(body) => {
req.headers_mut().set(ContentType::json());
req.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_str(mime::APPLICATION_JSON.as_ref()).expect(""));
*req.body_mut() = Some(body.into());
},
Err(err) => self.err = Some(::error::from(err)),
@@ -274,7 +288,7 @@ pub fn builder(client: Client, req: ::Result<Request>) -> RequestBuilder {
}
#[inline]
pub fn pieces(req: Request) -> (Method, Url, Headers, Option<Body>) {
pub fn pieces(req: Request) -> (Method, Url, HeaderMap, Option<Body>) {
(req.method, req.url, req.headers, req.body)
}
@@ -282,12 +296,10 @@ pub fn pieces(req: Request) -> (Method, Url, Headers, Option<Body>) {
mod tests {
use super::Client;
use std::collections::BTreeMap;
use tokio_core::reactor::Core;
#[test]
fn add_query_append() {
let mut core = Core::new().unwrap();
let client = Client::new(&core.handle());
let client = Client::new();
let some_url = "https://google.com/";
let mut r = client.get(some_url);
@@ -300,8 +312,7 @@ mod tests {
#[test]
fn add_query_append_same() {
let mut core = Core::new().unwrap();
let client = Client::new(&core.handle());
let client = Client::new();
let some_url = "https://google.com/";
let mut r = client.get(some_url);
@@ -319,8 +330,7 @@ mod tests {
qux: i32,
}
let mut core = Core::new().unwrap();
let client = Client::new(&core.handle());
let client = Client::new();
let some_url = "https://google.com/";
let mut r = client.get(some_url);
@@ -338,8 +348,7 @@ mod tests {
params.insert("foo", "bar");
params.insert("qux", "three");
let mut core = Core::new().unwrap();
let client = Client::new(&core.handle());
let client = Client::new();
let some_url = "https://google.com/";
let mut r = client.get(some_url);

View File

@@ -13,7 +13,7 @@ use serde::de::DeserializeOwned;
use serde_json;
use url::Url;
use header::{Headers, ContentEncoding, ContentLength, Encoding, TransferEncoding};
use hyper::header::{HeaderMap, CONTENT_ENCODING, CONTENT_LENGTH, TRANSFER_ENCODING};
use super::{decoder, body, Body, Chunk, Decoder};
use error;
@@ -21,7 +21,7 @@ use error;
/// A Response to a submitted `Request`.
pub struct Response {
status: StatusCode,
headers: Headers,
headers: HeaderMap,
url: Url,
body: Decoder,
}
@@ -41,13 +41,13 @@ impl Response {
/// Get the `Headers` of this `Response`.
#[inline]
pub fn headers(&self) -> &Headers {
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
/// Get a mutable reference to the `Headers` of this `Response`.
#[inline]
pub fn headers_mut(&mut self) -> &mut Headers {
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
@@ -99,7 +99,7 @@ impl Response {
/// // it could be any status between 400...599
/// assert_eq!(
/// err.status(),
/// Some(reqwest::StatusCode::BadRequest)
/// Some(reqwest::StatusCode::BAD_REQUEST)
/// );
/// }
/// }
@@ -152,10 +152,10 @@ impl<T> fmt::Debug for Json<T> {
// pub(crate)
pub fn new(mut res: ::hyper::client::Response, url: Url, gzip: bool) -> Response {
pub fn new(mut res: ::hyper::Response<::hyper::Body>, url: Url, gzip: bool) -> Response {
let status = res.status();
let mut headers = mem::replace(res.headers_mut(), Headers::new());
let decoder = decoder::detect(&mut headers, body::wrap(res.body()), gzip);
let mut headers = mem::replace(res.headers_mut(), HeaderMap::new());
let decoder = decoder::detect(&mut headers, body::wrap(res.into_body()), gzip);
debug!("Response: '{}' for {}", status, url);
Response {
status: status,

View File

@@ -3,9 +3,9 @@ use std::fmt;
use std::io::{self, Cursor, Read};
use bytes::Bytes;
use hyper::{self, Chunk};
use hyper::{self};
use {async_impl, wait};
use {async_impl};
/// The body of a `Request`.
///
@@ -184,7 +184,7 @@ impl Read for Reader {
pub struct Sender {
body: (Box<Read + Send>, Option<u64>),
tx: wait::WaitSink<::futures::sync::mpsc::Sender<hyper::Result<Chunk>>>,
tx: hyper::body::Sender,
}
impl Sender {
@@ -201,21 +201,16 @@ impl Sender {
Ok(0) => return Ok(()),
Ok(n) => {
unsafe { buf.advance_mut(n); }
if let Err(e) = tx.send(Ok(buf.take().freeze().into())) {
if let wait::Waited::Err(_) = e {
let epipe = io::Error::new(io::ErrorKind::BrokenPipe, "broken pipe");
return Err(::error::from(epipe));
} else {
if let Err(_) = tx.send_data(buf.take().freeze().into()) {
return Err(::error::timedout(None));
}
}
if buf.remaining_mut() == 0 {
buf.reserve(8192);
}
}
Err(e) => {
let ret = io::Error::new(e.kind(), e.to_string());
let _ = tx.send(Err(e.into()));
tx.abort();
return Err(::error::from(ret));
}
}
@@ -227,10 +222,10 @@ impl Sender {
pub fn async(body: Body) -> (Option<Sender>, async_impl::Body, Option<u64>) {
match body.kind {
Kind::Reader(read, len) => {
let (tx, rx) = hyper::Body::pair();
let (tx, rx) = hyper::Body::channel();
let tx = Sender {
body: (read, len),
tx: wait::sink(tx, None),
tx: tx,
};
(Some(tx), async_impl::body::wrap(rx), len)
},

View File

@@ -174,8 +174,8 @@ impl ClientBuilder {
/// ```rust
/// use reqwest::header;
/// # fn build_client() -> Result<(), Box<std::error::Error>> {
/// let mut headers = header::Headers::new();
/// headers.set(header::Authorization("secret".to_string()));
/// let mut headers = header::HeaderMap::new();
/// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret"));
///
/// // get a client builder
/// let client = reqwest::Client::builder()
@@ -191,8 +191,8 @@ impl ClientBuilder {
/// ```rust
/// use reqwest::header;
/// # fn build_client() -> Result<(), Box<std::error::Error>> {
/// let mut headers = header::Headers::new();
/// headers.set(header::Authorization("secret".to_string()));
/// let mut headers = header::HeaderMap::new();
/// headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("secret"));
///
/// // get a client builder
/// let client = reqwest::Client::builder()
@@ -200,13 +200,13 @@ impl ClientBuilder {
/// .build()?;
/// let res = client
/// .get("https://www.rust-lang.org")
/// .header(header::Authorization("token".to_string()))
/// .header(header::AUTHORIZATION, "token")
/// .send()?;
/// # Ok(())
/// # }
/// ```
#[inline]
pub fn default_headers(&mut self, headers: header::Headers) -> &mut ClientBuilder {
pub fn default_headers(&mut self, headers: header::HeaderMap) -> &mut ClientBuilder {
self.inner.default_headers(headers);
self
}
@@ -287,7 +287,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn get<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Get, url)
self.request(Method::GET, url)
}
/// Convenience method to make a `POST` request to a URL.
@@ -296,7 +296,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn post<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Post, url)
self.request(Method::POST, url)
}
/// Convenience method to make a `PUT` request to a URL.
@@ -305,7 +305,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn put<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Put, url)
self.request(Method::PUT, url)
}
/// Convenience method to make a `PATCH` request to a URL.
@@ -314,7 +314,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn patch<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Patch, url)
self.request(Method::PATCH, url)
}
/// Convenience method to make a `DELETE` request to a URL.
@@ -323,7 +323,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn delete<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Delete, url)
self.request(Method::DELETE, url)
}
/// Convenience method to make a `HEAD` request to a URL.
@@ -332,7 +332,7 @@ impl Client {
///
/// This method fails whenever supplied `Url` cannot be parsed.
pub fn head<U: IntoUrl>(&self, url: U) -> RequestBuilder {
self.request(Method::Head, url)
self.request(Method::HEAD, url)
}
/// Start building a `Request` with the `Method` and `Url`.
@@ -412,22 +412,21 @@ impl ClientHandle {
let mut builder = async_impl::client::take_builder(&mut builder.inner);
let (tx, rx) = mpsc::unbounded();
let (spawn_tx, spawn_rx) = oneshot::channel::<::Result<()>>();
let handle = try_!(thread::Builder::new().name("reqwest-internal-sync-core".into()).spawn(move || {
use tokio_core::reactor::Core;
let handle = try_!(thread::Builder::new().name("reqwest-internal-sync-runtime".into()).spawn(move || {
use tokio::runtime::current_thread::Runtime;
let built = (|| {
let core = try_!(Core::new());
let handle = core.handle();
let client = builder.build(&handle)?;
Ok((core, handle, client))
let rt = try_!(Runtime::new());
let client = builder.build()?;
Ok((rt, client))
})();
let (mut core, handle, client) = match built {
Ok((a, b, c)) => {
let (mut rt, client) = match built {
Ok((rt, c)) => {
if let Err(_) = spawn_tx.send(Ok(())) {
return;
}
(a, b, c)
(rt, c)
},
Err(e) => {
let _ = spawn_tx.send(Err(e));
@@ -435,19 +434,22 @@ impl ClientHandle {
}
};
let work = rx.for_each(|(req, tx)| {
let work = rx.for_each(move |(req, tx)| {
let tx: oneshot::Sender<::Result<async_impl::Response>> = tx;
let task = client.execute(req)
.then(move |x| tx.send(x).map_err(|_| ()));
handle.spawn(task);
::tokio::spawn(task);
Ok(())
});
// work is Future<(), ()>, and our closure will never return Err
let _ = core.run(work);
rt.spawn(work)
.run()
.expect("runtime unexpected error");
}));
wait::timeout(spawn_rx, timeout.0).expect("core thread cancelled")?;
wait::timeout(spawn_rx, timeout.0).expect("runtime thread cancelled")?;
let inner_handle = Arc::new(InnerClientHandle {
tx: Some(tx),

View File

@@ -1,10 +1,10 @@
use bytes::{Buf, BufMut, IntoBuf};
use futures::{Async, Future, Poll};
use hyper::client::{HttpConnector, Service};
use hyper::Uri;
use http::uri::Scheme;
use hyper::client::{HttpConnector};
use hyper::client::connect::{Connect, Connected, Destination};
use hyper_tls::{HttpsConnector, MaybeHttpsStream};
use native_tls::TlsConnector;
use tokio_core::reactor::Handle;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_tls::{TlsConnectorExt, TlsStream};
@@ -22,8 +22,8 @@ pub struct Connector {
}
impl Connector {
pub fn new(threads: usize, tls: TlsConnector, proxies: Arc<Vec<Proxy>>, handle: &Handle) -> Connector {
let mut http = HttpConnector::new(threads, handle);
pub fn new(threads: usize, tls: TlsConnector, proxies: Arc<Vec<Proxy>>) -> Connector {
let mut http = HttpConnector::new(threads);
http.enforce_http(false);
let https = HttpsConnector::from((http, tls.clone()));
@@ -39,41 +39,53 @@ impl Connector {
}
}
impl Service for Connector {
type Request = Uri;
type Response = Conn;
impl Connect for Connector {
type Transport = Conn;
type Error = io::Error;
type Future = Connecting;
fn call(&self, uri: Uri) -> Self::Future {
fn connect(&self, dst: Destination) -> Self::Future {
for prox in self.proxies.iter() {
if let Some(puri) = proxy::intercept(prox, &uri) {
trace!("proxy({:?}) intercepts {:?}", puri, uri);
if uri.scheme() == Some("https") {
let host = uri.host().unwrap().to_owned();
let port = uri.port().unwrap_or(443);
if let Some(puri) = proxy::intercept(prox, &dst) {
trace!("proxy({:?}) intercepts {:?}", puri, dst);
let mut ndst = dst.clone();
let new_scheme = puri
.scheme_part()
.map(Scheme::as_str)
.unwrap_or("http");
ndst.set_scheme(new_scheme)
.expect("proxy target scheme should be valid");
ndst.set_host(puri.host().expect("proxy target should have host"))
.expect("proxy target host should be valid");
ndst.set_port(puri.port());
if dst.scheme() == "https" {
let host = dst.host().to_owned();
let port = dst.port().unwrap_or(443);
let tls = self.tls.clone();
return Box::new(self.https.call(puri).and_then(move |conn| {
return Box::new(self.https.connect(ndst).and_then(move |(conn, connected)| {
trace!("tunneling HTTPS over proxy");
tunnel(conn, host.clone(), port)
.and_then(move |tunneled| {
tls.connect_async(&host, tunneled)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))
})
.map(|io| Conn::Proxied(io))
.map(|io| (Conn::Proxied(io), connected.proxy(true)))
}));
}
return Box::new(self.https.call(puri).map(|io| Conn::Normal(io)));
return Box::new(self.https.connect(ndst).map(|(io, connected)| (Conn::Normal(io), connected.proxy(true))));
}
}
Box::new(self.https.call(uri).map(|io| Conn::Normal(io)))
Box::new(self.https.connect(dst).map(|(io, connected)| (Conn::Normal(io), connected)))
}
}
type HttpStream = <HttpConnector as Service>::Response;
type HttpStream = <HttpConnector as Connect>::Transport;
type HttpsStream = MaybeHttpsStream<HttpStream>;
pub type Connecting = Box<Future<Item=Conn, Error=io::Error>>;
pub type Connecting = Box<Future<Item=(Conn, Connected), Error=io::Error> + Send>;
pub enum Conn {
Normal(HttpsStream),
@@ -214,8 +226,8 @@ mod tests {
use std::net::TcpListener;
use std::thread;
use futures::Future;
use tokio_core::reactor::Core;
use tokio_core::net::TcpStream;
use tokio::runtime::current_thread::Runtime;
use tokio::net::TcpStream;
use super::tunnel;
@@ -251,44 +263,44 @@ mod tests {
fn test_tunnel() {
let addr = mock_tunnel!();
let mut core = Core::new().unwrap();
let work = TcpStream::connect(&addr, &core.handle());
let mut rt = Runtime::new().unwrap();
let work = TcpStream::connect(&addr);
let host = addr.ip().to_string();
let port = addr.port();
let work = work.and_then(|tcp| {
tunnel(tcp, host, port)
});
core.run(work).unwrap();
rt.block_on(work).unwrap();
}
#[test]
fn test_tunnel_eof() {
let addr = mock_tunnel!(b"HTTP/1.1 200 OK");
let mut core = Core::new().unwrap();
let work = TcpStream::connect(&addr, &core.handle());
let mut rt = Runtime::new().unwrap();
let work = TcpStream::connect(&addr);
let host = addr.ip().to_string();
let port = addr.port();
let work = work.and_then(|tcp| {
tunnel(tcp, host, port)
});
core.run(work).unwrap_err();
rt.block_on(work).unwrap_err();
}
#[test]
fn test_tunnel_bad_response() {
let addr = mock_tunnel!(b"foo bar baz hallo");
let mut core = Core::new().unwrap();
let work = TcpStream::connect(&addr, &core.handle());
let mut rt = Runtime::new().unwrap();
let work = TcpStream::connect(&addr);
let host = addr.ip().to_string();
let port = addr.port();
let work = work.and_then(|tcp| {
tunnel(tcp, host, port)
});
core.run(work).unwrap_err();
rt.block_on(work).unwrap_err();
}
}

View File

@@ -112,6 +112,7 @@ impl Error {
pub fn get_ref(&self) -> Option<&(StdError + Send + Sync + 'static)> {
match self.kind {
Kind::Http(ref e) => Some(e),
Kind::Hyper(ref e) => Some(e),
Kind::Url(ref e) => Some(e),
Kind::Tls(ref e) => Some(e),
Kind::Io(ref e) => Some(e),
@@ -129,6 +130,7 @@ impl Error {
pub fn is_http(&self) -> bool {
match self.kind {
Kind::Http(_) => true,
Kind::Hyper(_) => true,
_ => false,
}
}
@@ -190,6 +192,7 @@ impl fmt::Display for Error {
}
match self.kind {
Kind::Http(ref e) => fmt::Display::fmt(e, f),
Kind::Hyper(ref e) => fmt::Display::fmt(e, f),
Kind::Url(ref e) => fmt::Display::fmt(e, f),
Kind::Tls(ref e) => fmt::Display::fmt(e, f),
Kind::Io(ref e) => fmt::Display::fmt(e, f),
@@ -213,6 +216,7 @@ impl StdError for Error {
fn description(&self) -> &str {
match self.kind {
Kind::Http(ref e) => e.description(),
Kind::Hyper(ref e) => e.description(),
Kind::Url(ref e) => e.description(),
Kind::Tls(ref e) => e.description(),
Kind::Io(ref e) => e.description(),
@@ -228,6 +232,7 @@ impl StdError for Error {
fn cause(&self) -> Option<&StdError> {
match self.kind {
Kind::Http(ref e) => e.cause(),
Kind::Hyper(ref e) => e.cause(),
Kind::Url(ref e) => e.cause(),
Kind::Tls(ref e) => e.cause(),
Kind::Io(ref e) => e.cause(),
@@ -245,7 +250,8 @@ impl StdError for Error {
#[derive(Debug)]
pub enum Kind {
Http(::hyper::Error),
Http(::http::Error),
Hyper(::hyper::Error),
Url(::url::ParseError),
Tls(::native_tls::Error),
Io(io::Error),
@@ -258,13 +264,18 @@ pub enum Kind {
}
impl From<::http::Error> for Kind {
#[inline]
fn from(err: ::http::Error) -> Kind {
Kind::Http(err)
}
}
impl From<::hyper::Error> for Kind {
#[inline]
fn from(err: ::hyper::Error) -> Kind {
match err {
::hyper::Error::Io(err) => Kind::Io(err),
//::hyper::Error::Uri(err) => Kind::Url(err),
other => Kind::Http(other),
other => Kind::Hyper(other),
}
}
}
@@ -430,20 +441,6 @@ pub fn server_error(url: Url, status: StatusCode) -> Error {
mod tests {
use super::*;
#[test]
fn test_error_get_ref_downcasts() {
let err: Error = from(::hyper::Error::Status);
let cause = err.get_ref()
.unwrap()
.downcast_ref::<::hyper::Error>()
.unwrap();
match cause {
&::hyper::Error::Status => (),
_ => panic!("unexpected downcast: {:?}", cause),
}
}
#[test]
fn test_cause_chain() {
#[derive(Debug)]
@@ -476,9 +473,6 @@ mod tests {
}
}
let err = from(::hyper::Error::Status);
assert!(err.cause().is_none());
let root = Chain(None::<Error>);
let io = ::std::io::Error::new(::std::io::ErrorKind::Other, root);
let err = Error { kind: Kind::Io(io), url: None };

View File

@@ -36,7 +36,3 @@ impl<'a> PolyfillTryInto for &'a String {
pub fn to_uri(url: &Url) -> ::hyper::Uri {
url.as_str().parse().expect("a parsed Url should always be a valid Uri")
}
pub fn to_url(uri: &::hyper::Uri) -> Url {
uri.as_ref().parse().expect("reqwest Uris should only ever come from Urls")
}

View File

@@ -124,15 +124,18 @@
//! [serde]: http://serde.rs
//! [cookiejar_issue]: https://github.com/seanmonstar/reqwest/issues/14
extern crate base64;
extern crate bytes;
extern crate encoding_rs;
#[macro_use]
extern crate futures;
extern crate http;
extern crate hyper;
extern crate hyper_tls;
#[macro_use]
extern crate log;
extern crate libflate;
extern crate mime;
extern crate mime_guess;
extern crate native_tls;
extern crate serde;
@@ -141,14 +144,13 @@ extern crate serde;
extern crate serde_derive;
extern crate serde_json;
extern crate serde_urlencoded;
extern crate tokio_core;
extern crate tokio;
extern crate tokio_io;
extern crate tokio_tls;
extern crate url;
extern crate uuid;
pub use hyper::header;
pub use hyper::mime;
pub use hyper::Method;
pub use hyper::StatusCode;
pub use url::Url;

View File

@@ -4,8 +4,7 @@ use std::fs::File;
use std::io::{self, Cursor, Read};
use std::path::Path;
use mime::Mime;
use mime_guess;
use mime_guess::{self, Mime};
use url::percent_encoding;
use uuid::Uuid;

View File

@@ -1,7 +1,7 @@
use std::fmt;
use std::sync::Arc;
use hyper::Uri;
use hyper::client::connect::Destination;
use {into_url, IntoUrl, Url};
/// Configuration of a proxy that a `Client` should pass requests to.
@@ -128,35 +128,35 @@ impl Proxy {
}
}
fn proxies(&self, url: &Url) -> bool {
match self.intercept {
Intercept::All(..) => true,
Intercept::Http(..) => url.scheme() == "http",
Intercept::Https(..) => url.scheme() == "https",
Intercept::Custom(ref fun) => (fun.0)(url).is_some(),
}
}
fn intercept(&self, uri: &Uri) -> Option<Uri> {
fn intercept<D: Dst>(&self, uri: &D) -> Option<::hyper::Uri> {
match self.intercept {
Intercept::All(ref u) => Some(u.clone()),
Intercept::Http(ref u) => {
if uri.scheme() == Some("http") {
if uri.scheme() == "http" {
Some(u.clone())
} else {
None
}
},
Intercept::Https(ref u) => {
if uri.scheme() == Some("https") {
if uri.scheme() == "https" {
Some(u.clone())
} else {
None
}
},
Intercept::Custom(ref fun) => {
(fun.0)(&into_url::to_url(uri))
(fun.0)(
&format!(
"{}://{}{}{}",
uri.scheme(),
uri.host(),
uri.port().map(|_| ":").unwrap_or(""),
uri.port().map(|p| p.to_string()).unwrap_or(String::new())
)
.parse()
.expect("should be valid Url")
)
.map(|u| into_url::to_uri(&u) )
},
}
@@ -165,9 +165,9 @@ impl Proxy {
#[derive(Clone, Debug)]
enum Intercept {
All(Uri),
Http(Uri),
Https(Uri),
All(::hyper::Uri),
Http(::hyper::Uri),
Https(::hyper::Uri),
Custom(Custom),
}
@@ -182,20 +182,50 @@ impl fmt::Debug for Custom {
// pub(crate)
pub fn intercept(proxy: &Proxy, uri: &Uri) -> Option<Uri> {
proxy.intercept(uri)
/// A helper trait to allow testing `Proxy::intercept` without having to
/// construct `hyper::client::connect::Destination`s.
trait Dst {
fn scheme(&self) -> &str;
fn host(&self) -> &str;
fn port(&self) -> Option<u16>;
}
pub fn is_proxied(proxies: &[Proxy], uri: &Url) -> bool {
proxies.iter().any(|p| p.proxies(uri))
#[doc(hidden)]
impl Dst for Destination {
fn scheme(&self) -> &str {
Destination::scheme(self)
}
fn host(&self) -> &str {
Destination::host(self)
}
fn port(&self) -> Option<u16> {
Destination::port(self)
}
}
pub fn intercept(proxy: &Proxy, uri: &Destination) -> Option<::http::Uri> {
proxy.intercept(uri)
}
#[cfg(test)]
mod tests {
use super::*;
fn uri(s: &str) -> Uri {
s.parse().unwrap()
impl Dst for Url {
fn scheme(&self) -> &str {
Url::scheme(self)
}
fn host(&self) -> &str {
Url::host_str(self)
.expect("<Url as Dst>::host should have a str")
}
fn port(&self) -> Option<u16> {
Url::port(self)
}
}
fn url(s: &str) -> Url {
@@ -210,10 +240,8 @@ mod tests {
let http = "http://hyper.rs";
let other = "https://hyper.rs";
assert!(p.proxies(&url(http)));
assert_eq!(p.intercept(&uri(http)).unwrap(), target);
assert!(!p.proxies(&url(other)));
assert!(p.intercept(&uri(other)).is_none());
assert_eq!(p.intercept(&url(http)).unwrap(), target);
assert!(p.intercept(&url(other)).is_none());
}
#[test]
@@ -224,10 +252,8 @@ mod tests {
let http = "http://hyper.rs";
let other = "https://hyper.rs";
assert!(!p.proxies(&url(http)));
assert!(p.intercept(&uri(http)).is_none());
assert!(p.proxies(&url(other)));
assert_eq!(p.intercept(&uri(other)).unwrap(), target);
assert!(p.intercept(&url(http)).is_none());
assert_eq!(p.intercept(&url(other)).unwrap(), target);
}
#[test]
@@ -239,13 +265,9 @@ mod tests {
let https = "https://hyper.rs";
let other = "x-youve-never-heard-of-me-mr-proxy://hyper.rs";
assert!(p.proxies(&url(http)));
assert!(p.proxies(&url(https)));
assert!(p.proxies(&url(other)));
assert_eq!(p.intercept(&uri(http)).unwrap(), target);
assert_eq!(p.intercept(&uri(https)).unwrap(), target);
assert_eq!(p.intercept(&uri(other)).unwrap(), target);
assert_eq!(p.intercept(&url(http)).unwrap(), target);
assert_eq!(p.intercept(&url(https)).unwrap(), target);
assert_eq!(p.intercept(&url(other)).unwrap(), target);
}
@@ -267,29 +289,9 @@ mod tests {
let https = "https://hyper.rs";
let other = "x-youve-never-heard-of-me-mr-proxy://seanmonstar.com";
assert!(p.proxies(&url(http)));
assert!(p.proxies(&url(https)));
assert!(!p.proxies(&url(other)));
assert_eq!(p.intercept(&uri(http)).unwrap(), target2);
assert_eq!(p.intercept(&uri(https)).unwrap(), target1);
assert!(p.intercept(&uri(other)).is_none());
}
#[test]
fn test_is_proxied() {
let proxies = vec![
Proxy::http("http://example.domain").unwrap(),
Proxy::https("http://other.domain").unwrap(),
];
let http = "http://hyper.rs".parse().unwrap();
let https = "https://hyper.rs".parse().unwrap();
let other = "x-other://hyper.rs".parse().unwrap();
assert!(is_proxied(&proxies, &http));
assert!(is_proxied(&proxies, &https));
assert!(!is_proxied(&proxies, &other));
assert_eq!(p.intercept(&url(http)).unwrap(), target2);
assert_eq!(p.intercept(&url(https)).unwrap(), target1);
assert!(p.intercept(&url(other)).is_none());
}
}

View File

@@ -1,6 +1,6 @@
use std::fmt;
use hyper::header::Headers;
use header::HeaderMap;
use hyper::StatusCode;
use Url;
@@ -228,15 +228,15 @@ pub fn check_redirect(
.inner
}
pub fn remove_sensitive_headers(headers: &mut Headers, next: &Url, previous: &[Url]) {
pub fn remove_sensitive_headers(headers: &mut HeaderMap, next: &Url, previous: &[Url]) {
if let Some(previous) = previous.last() {
let cross_host = next.host_str() != previous.host_str() ||
next.port_or_known_default() != previous.port_or_known_default();
if cross_host {
headers.remove_raw("authorization");
headers.remove_raw("cookie");
headers.remove_raw("cookie2");
headers.remove_raw("www-authenticate");
headers.remove("authorization");
headers.remove("cookie");
headers.remove("cookie2");
headers.remove("www-authenticate");
}
}
}
@@ -268,14 +268,14 @@ fn test_redirect_policy_limit() {
.collect::<Vec<_>>();
assert_eq!(
check_redirect(&policy, StatusCode::Found, &next, &previous),
check_redirect(&policy, StatusCode::FOUND, &next, &previous),
Action::Follow
);
previous.push(Url::parse("http://a.b.d/e/33").unwrap());
assert_eq!(
check_redirect(&policy, StatusCode::Found, &next, &previous),
check_redirect(&policy, StatusCode::FOUND, &next, &previous),
Action::TooManyRedirects
);
}
@@ -292,27 +292,25 @@ fn test_redirect_policy_custom() {
let next = Url::parse("http://bar/baz").unwrap();
assert_eq!(
check_redirect(&policy, StatusCode::Found, &next, &[]),
check_redirect(&policy, StatusCode::FOUND, &next, &[]),
Action::Follow
);
let next = Url::parse("http://foo/baz").unwrap();
assert_eq!(
check_redirect(&policy, StatusCode::Found, &next, &[]),
check_redirect(&policy, StatusCode::FOUND, &next, &[]),
Action::Stop
);
}
#[test]
fn test_remove_sensitive_headers() {
use hyper::header::{Accept, Authorization, Cookie};
use hyper::header::{ACCEPT, AUTHORIZATION, COOKIE, HeaderValue};
let mut headers = Headers::new();
headers.set(Accept::star());
headers.set(Authorization("let me in".to_owned()));
let mut cookie = Cookie::new();
cookie.set("foo", "bar");
headers.set(cookie);
let mut headers = HeaderMap::new();
headers.insert(ACCEPT, HeaderValue::from_static("*/*"));
headers.insert(AUTHORIZATION, HeaderValue::from_static("let me in"));
headers.insert(COOKIE, HeaderValue::from_static("foo=bar"));
let next = Url::parse("http://initial-domain.com/path").unwrap();
let mut prev = vec![Url::parse("http://initial-domain.com/new_path").unwrap()];
@@ -322,8 +320,8 @@ fn test_remove_sensitive_headers() {
assert_eq!(headers, filtered_headers);
prev.push(Url::parse("http://new-domain.com/path").unwrap());
filtered_headers.remove::<Authorization<String>>();
filtered_headers.remove::<Cookie>();
filtered_headers.remove(AUTHORIZATION);
filtered_headers.remove(COOKIE);
remove_sensitive_headers(&mut headers, &next, &prev);
assert_eq!(headers, filtered_headers);

View File

@@ -1,12 +1,13 @@
use std::fmt;
use hyper::header::ContentType;
use base64::encode;
use serde::Serialize;
use serde_json;
use serde_urlencoded;
use body::{self, Body};
use header::Headers;
use header::{HeaderMap, HeaderName, HeaderValue, CONTENT_TYPE};
use http::HttpTryFrom;
use {async_impl, Client, Method, Url};
/// A request which can be executed with `Client::execute()`.
@@ -58,13 +59,13 @@ impl Request {
/// Get the headers.
#[inline]
pub fn headers(&self) -> &Headers {
pub fn headers(&self) -> &HeaderMap {
self.inner.headers()
}
/// Get a mutable reference to the headers.
#[inline]
pub fn headers_mut(&mut self) -> &mut Headers {
pub fn headers_mut(&mut self) -> &mut HeaderMap {
self.inner.headers_mut()
}
@@ -85,22 +86,31 @@ impl RequestBuilder {
/// Add a `Header` to this Request.
///
/// ```rust
/// use reqwest::header::UserAgent;
/// use reqwest::header::USER_AGENT;
///
/// # fn run() -> Result<(), Box<::std::error::Error>> {
/// let client = reqwest::Client::new();
/// let res = client.get("https://www.rust-lang.org")
/// .header(UserAgent::new("foo"))
/// .header(USER_AGENT, "foo")
/// .send()?;
/// # Ok(())
/// # }
/// ```
pub fn header<H>(&mut self, header: H) -> &mut RequestBuilder
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut RequestBuilder
where
H: ::header::Header,
HeaderName: HttpTryFrom<K>,
HeaderValue: HttpTryFrom<V>,
{
if let Some(req) = request_mut(&mut self.request, &self.err) {
req.headers_mut().set(header);
match <HeaderName as HttpTryFrom<K>>::try_from(key) {
Ok(key) => {
match <HeaderValue as HttpTryFrom<V>>::try_from(value) {
Ok(value) => { req.headers_mut().append(key, value); }
Err(e) => self.err = Some(::error::from(e.into())),
}
},
Err(e) => self.err = Some(::error::from(e.into())),
};
}
self
}
@@ -110,13 +120,13 @@ impl RequestBuilder {
/// The headers will be merged in to any already set.
///
/// ```rust
/// use reqwest::header::{Headers, UserAgent, ContentType};
/// use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT, CONTENT_TYPE};
/// # use std::fs;
///
/// fn construct_headers() -> Headers {
/// let mut headers = Headers::new();
/// headers.set(UserAgent::new("reqwest"));
/// headers.set(ContentType::png());
/// fn construct_headers() -> HeaderMap {
/// let mut headers = HeaderMap::new();
/// headers.insert(USER_AGENT, HeaderValue::from_static("reqwest"));
/// headers.insert(CONTENT_TYPE, HeaderValue::from_static("image/png"));
/// headers
/// }
///
@@ -130,9 +140,11 @@ impl RequestBuilder {
/// # Ok(())
/// # }
/// ```
pub fn headers(&mut self, headers: ::header::Headers) -> &mut RequestBuilder {
pub fn headers(&mut self, headers: ::header::HeaderMap) -> &mut RequestBuilder {
if let Some(req) = request_mut(&mut self.request, &self.err) {
req.headers_mut().extend(headers.iter());
for (key, value) in headers.iter() {
req.headers_mut().insert(key, value.clone());
}
}
self
}
@@ -153,10 +165,10 @@ impl RequestBuilder {
U: Into<String>,
P: Into<String>,
{
self.header(::header::Authorization(::header::Basic {
username: username.into(),
password: password.map(|p| p.into()),
}))
let username = username.into();
let password = password.map(|p| p.into()).unwrap_or(String::new());
let header_value = format!("basic {}:{}", username, encode(&password));
self.header(::header::AUTHORIZATION, HeaderValue::from_str(header_value.as_str()).expect(""))
}
/// Set the request body.
@@ -283,7 +295,7 @@ impl RequestBuilder {
if let Some(req) = request_mut(&mut self.request, &self.err) {
match serde_urlencoded::to_string(form) {
Ok(body) => {
req.headers_mut().set(ContentType::form_url_encoded());
req.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_str(::mime::APPLICATION_WWW_FORM_URLENCODED.as_ref()).expect(""));
*req.body_mut() = Some(body.into());
},
Err(err) => self.err = Some(::error::from(err)),
@@ -321,7 +333,7 @@ impl RequestBuilder {
if let Some(req) = request_mut(&mut self.request, &self.err) {
match serde_json::to_vec(json) {
Ok(body) => {
req.headers_mut().set(ContentType::json());
req.headers_mut().insert(CONTENT_TYPE, HeaderValue::from_str(::mime::APPLICATION_JSON.as_ref()).expect(""));
*req.body_mut() = Some(body.into());
},
Err(err) => self.err = Some(::error::from(err)),
@@ -333,7 +345,6 @@ impl RequestBuilder {
/// Sends a multipart/form-data body.
///
/// ```
/// use reqwest::mime;
/// # use reqwest::Error;
///
/// # fn run() -> Result<(), Box<std::error::Error>> {
@@ -352,10 +363,14 @@ impl RequestBuilder {
/// See [`multipart`](multipart/) for more examples.
pub fn multipart(&mut self, mut multipart: ::multipart::Form) -> &mut RequestBuilder {
if let Some(req) = request_mut(&mut self.request, &self.err) {
req.headers_mut().set(
::header::ContentType(format!("multipart/form-data; boundary={}", ::multipart_::boundary(&multipart))
.parse().unwrap()
)
req.headers_mut().insert(
::header::CONTENT_TYPE,
HeaderValue::from_str(
format!(
"multipart/form-data; boundary={}",
::multipart_::boundary(&multipart)
).as_str()
).expect("")
);
*req.body_mut() = Some(match ::multipart_::compute_length(&mut multipart) {
Some(length) => Body::sized(::multipart_::reader(multipart), length),
@@ -450,13 +465,13 @@ pub fn builder(client: Client, req: ::Result<Request>) -> RequestBuilder {
#[inline]
pub fn async(req: Request) -> (async_impl::Request, Option<body::Sender>) {
use header::ContentLength;
use header::CONTENT_LENGTH;
let mut req_async = req.inner;
let body = req.body.and_then(|body| {
let (tx, body, len) = body::async(body);
if let Some(len) = len {
req_async.headers_mut().set(ContentLength(len));
req_async.headers_mut().insert(CONTENT_LENGTH, HeaderValue::from_str(len.to_string().as_str()).expect(""));
}
*req_async.body_mut() = Some(body);
tx
@@ -467,7 +482,7 @@ pub fn async(req: Request) -> (async_impl::Request, Option<body::Sender>) {
#[cfg(test)]
mod tests {
use {body, Client, Method};
use header::{Host, Headers, ContentType};
use header::{HOST, HeaderMap, HeaderValue, CONTENT_TYPE};
use std::collections::{BTreeMap, HashMap};
use serde_json;
use serde_urlencoded;
@@ -478,7 +493,7 @@ mod tests {
let some_url = "https://google.com/";
let r = client.get(some_url).build().unwrap();
assert_eq!(r.method(), &Method::Get);
assert_eq!(r.method(), &Method::GET);
assert_eq!(r.url().as_str(), some_url);
}
@@ -488,7 +503,7 @@ mod tests {
let some_url = "https://google.com/";
let r = client.head(some_url).build().unwrap();
assert_eq!(r.method(), &Method::Head);
assert_eq!(r.method(), &Method::HEAD);
assert_eq!(r.url().as_str(), some_url);
}
@@ -498,7 +513,7 @@ mod tests {
let some_url = "https://google.com/";
let r = client.post(some_url).build().unwrap();
assert_eq!(r.method(), &Method::Post);
assert_eq!(r.method(), &Method::POST);
assert_eq!(r.url().as_str(), some_url);
}
@@ -508,7 +523,7 @@ mod tests {
let some_url = "https://google.com/";
let r = client.put(some_url).build().unwrap();
assert_eq!(r.method(), &Method::Put);
assert_eq!(r.method(), &Method::PUT);
assert_eq!(r.url().as_str(), some_url);
}
@@ -518,7 +533,7 @@ mod tests {
let some_url = "https://google.com/";
let r = client.patch(some_url).build().unwrap();
assert_eq!(r.method(), &Method::Patch);
assert_eq!(r.method(), &Method::PATCH);
assert_eq!(r.url().as_str(), some_url);
}
@@ -528,7 +543,7 @@ mod tests {
let some_url = "https://google.com/";
let r = client.delete(some_url).build().unwrap();
assert_eq!(r.method(), &Method::Delete);
assert_eq!(r.method(), &Method::DELETE);
assert_eq!(r.url().as_str(), some_url);
}
@@ -538,13 +553,13 @@ mod tests {
let some_url = "https://google.com/";
let mut r = client.post(some_url);
let header = Host::new("google.com", None);
let header = HeaderValue::from_static("google.com");
// Add a copy of the header to the request builder
let r = r.header(header.clone()).build().unwrap();
let r = r.header(HOST, header.clone()).build().unwrap();
// then check it was actually added
assert_eq!(r.headers().get::<Host>(), Some(&header));
assert_eq!(r.headers().get(HOST), Some(&header));
}
#[test]
@@ -553,10 +568,10 @@ mod tests {
let some_url = "https://google.com/";
let mut r = client.post(some_url);
let header = Host::new("google.com", None);
let header = HeaderValue::from_static("google.com");
let mut headers = Headers::new();
headers.set(header);
let mut headers = HeaderMap::new();
headers.insert(HOST, header);
// Add a copy of the headers to the request builder
let r = r.headers(headers.clone()).build().unwrap();
@@ -653,8 +668,7 @@ mod tests {
let mut r = r.form(&form_data).build().unwrap();
// Make sure the content type was set
assert_eq!(r.headers().get::<ContentType>(),
Some(&ContentType::form_url_encoded()));
assert_eq!(r.headers().get(CONTENT_TYPE).unwrap(), &"application/x-www-form-urlencoded");
let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap();
@@ -674,7 +688,7 @@ mod tests {
let mut r = r.json(&json_data).build().unwrap();
// Make sure the content type was set
assert_eq!(r.headers().get::<ContentType>(), Some(&ContentType::json()));
assert_eq!(r.headers().get(CONTENT_TYPE).unwrap(), &"application/json");
let buf = body::read_to_string(r.body_mut().take().unwrap()).unwrap();

View File

@@ -6,17 +6,19 @@ use std::borrow::Cow;
use encoding_rs::{Encoding, UTF_8};
use futures::{Async, Poll, Stream};
use mime::Mime;
use serde::de::DeserializeOwned;
use serde_json;
use client::KeepCoreThreadAlive;
use header::Headers;
use hyper::header::HeaderMap;
use {async_impl, StatusCode, Url, wait};
/// A Response to a submitted `Request`.
pub struct Response {
inner: async_impl::Response,
body: async_impl::ReadableChunks<WaitBody>,
content_length: Option<u64>,
_thread_handle: KeepCoreThreadAlive,
}
@@ -70,8 +72,8 @@ impl Response {
/// .body("possibly too large")
/// .send()?;
/// match resp.status() {
/// StatusCode::Ok => println!("success!"),
/// StatusCode::PayloadTooLarge => {
/// StatusCode::OK => println!("success!"),
/// StatusCode::PAYLOAD_TOO_LARGE => {
/// println!("Request payload is too large!");
/// }
/// s => println!("Received response status: {:?}", s),
@@ -93,14 +95,15 @@ impl Response {
/// ```rust
/// # use std::io::{Read, Write};
/// # use reqwest::Client;
/// # use reqwest::header::ContentLength;
/// # use reqwest::header::CONTENT_LENGTH;
/// #
/// # fn run() -> Result<(), Box<::std::error::Error>> {
/// let client = Client::new();
/// let mut resp = client.head("http://httpbin.org/bytes/3000").send()?;
/// if resp.status().is_success() {
/// let len = resp.headers().get::<ContentLength>()
/// .map(|ct_len| **ct_len)
/// let len = resp.headers().get(CONTENT_LENGTH)
/// .and_then(|ct_len| ct_len.to_str().ok())
/// .and_then(|ct_len| ct_len.parse().ok())
/// .unwrap_or(0);
/// // limit 1mb response
/// if len <= 1_000_000 {
@@ -115,7 +118,7 @@ impl Response {
/// # }
/// ```
#[inline]
pub fn headers(&self) -> &Headers {
pub fn headers(&self) -> &HeaderMap {
self.inner.headers()
}
@@ -189,14 +192,21 @@ impl Response {
/// This consumes the body. Trying to read more, or use of `response.json()`
/// will return empty values.
pub fn text(&mut self) -> ::Result<String> {
let len = self.headers().get::<::header::ContentLength>()
.map(|ct_len| **ct_len)
.unwrap_or(0);
let len = self.content_length.unwrap_or(0);
let mut content = Vec::with_capacity(len as usize);
self.read_to_end(&mut content).map_err(::error::from)?;
let encoding_name = self.headers().get::<::header::ContentType>()
.and_then(|content_type| {
content_type.get_param("charset")
let content_type = self.headers().get(::header::CONTENT_TYPE)
.and_then(|value| {
value.to_str().ok()
})
.and_then(|value| {
value.parse::<Mime>().ok()
});
let encoding_name = content_type
.as_ref()
.and_then(|mime| {
mime
.get_param("charset")
.map(|charset| charset.as_str())
})
.unwrap_or("utf-8");
@@ -252,7 +262,7 @@ impl Response {
/// let res = reqwest::get("http://httpbin.org/status/400")?
/// .error_for_status();
/// if let Err(err) = res {
/// assert_eq!(err.status(), Some(reqwest::StatusCode::BadRequest));
/// assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
/// }
/// # Ok(())
/// # }
@@ -260,12 +270,13 @@ impl Response {
/// ```
#[inline]
pub fn error_for_status(self) -> ::Result<Self> {
let Response { body, inner, _thread_handle } = self;
let Response { body, content_length, inner, _thread_handle } = self;
inner.error_for_status().map(move |inner| {
Response {
inner: inner,
body: body,
_thread_handle: _thread_handle,
inner,
body,
content_length,
_thread_handle,
}
})
}
@@ -306,6 +317,7 @@ impl Stream for WaitBody {
pub fn new(mut res: async_impl::Response, timeout: Option<Duration>, thread: KeepCoreThreadAlive) -> Response {
let body = mem::replace(res.body_mut(), async_impl::Decoder::empty());
let len = body.content_length();
let body = async_impl::ReadableChunks::new(WaitBody {
inner: wait::stream(body, timeout)
});
@@ -313,6 +325,7 @@ pub fn new(mut res: async_impl::Response, timeout: Option<Duration>, thread: Kee
Response {
inner: res,
body: body,
content_length: len,
_thread_handle: thread,
}
}

View File

@@ -2,7 +2,7 @@ use std::sync::Arc;
use std::thread;
use std::time::{Duration, Instant};
use futures::{Async, AsyncSink, Future, Sink, Stream};
use futures::{Async, Future, Stream};
use futures::executor::{self, Notify};
// pub(crate)
@@ -43,14 +43,6 @@ where S: Stream {
}
}
pub fn sink<S>(sink: S, timeout: Option<Duration>) -> WaitSink<S>
where S: Sink {
WaitSink {
sink: executor::spawn(sink),
timeout: timeout,
}
}
#[derive(Debug)]
pub enum Waited<E> {
TimedOut,
@@ -113,49 +105,6 @@ where S: Stream {
}
}
pub struct WaitSink<S> {
sink: executor::Spawn<S>,
timeout: Option<Duration>,
}
impl<S> WaitSink<S>
where S: Sink {
pub fn send(&mut self, mut item: S::SinkItem) -> Result<(), Waited<S::SinkError>> {
if let Some(dur) = self.timeout {
let start = Instant::now();
let deadline = start + dur;
let notify = Arc::new(ThreadNotify {
thread: thread::current(),
});
loop {
let now = Instant::now();
if now >= deadline {
return Err(Waited::TimedOut);
}
item = match self.sink.start_send_notify(item, &notify, 0)? {
AsyncSink::Ready => return Ok(()),
AsyncSink::NotReady(val) => val,
};
thread::park_timeout(deadline - now);
}
} else {
let notify = Arc::new(ThreadNotify {
thread: thread::current(),
});
loop {
item = match self.sink.start_send_notify(item, &notify, 0)? {
AsyncSink::Ready => return Ok(()),
AsyncSink::NotReady(val) => val,
};
thread::park();
}
}
}
}
struct ThreadNotify {
thread: thread::Thread,
}

View File

@@ -1,16 +1,15 @@
#![cfg(feature="unstable")]
extern crate futures;
extern crate tokio_core;
extern crate reqwest;
extern crate libflate;
extern crate reqwest;
extern crate tokio;
#[macro_use]
mod support;
use reqwest::unstable::async::Client;
use futures::{Future, Stream};
use tokio_core::reactor::Core;
use std::io::Write;
use std::time::Duration;
@@ -46,10 +45,10 @@ fn test_gzip(response_size: usize, chunk_size: usize) {
let server = server! {
request: b"\
GET /gzip HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
chunk_size: chunk_size,
@@ -57,9 +56,9 @@ fn test_gzip(response_size: usize, chunk_size: usize) {
response: response
};
let mut core = Core::new().unwrap();
let mut rt = tokio::runtime::current_thread::Runtime::new().expect("new rt");
let client = Client::new(&core.handle());
let client = Client::new();
let res_future = client.get(&format!("http://{}/gzip", server.addr()))
.send()
@@ -75,5 +74,5 @@ fn test_gzip(response_size: usize, chunk_size: usize) {
Ok(())
});
core.run(res_future).unwrap();
rt.block_on(res_future).unwrap();
}

View File

@@ -10,10 +10,10 @@ fn test_response_text() {
let server = server! {
request: b"\
GET /text HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -28,11 +28,9 @@ fn test_response_text() {
let url = format!("http://{}/text", server.addr());
let mut res = reqwest::get(&url).unwrap();
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("test".to_string())));
assert_eq!(res.headers().get(),
Some(&reqwest::header::ContentLength(5)));
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");
let body = res.text().unwrap();
assert_eq!(b"Hello", body.as_bytes());
@@ -43,10 +41,10 @@ fn test_response_non_utf_8_text() {
let server = server! {
request: b"\
GET /text HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -62,11 +60,9 @@ fn test_response_non_utf_8_text() {
let url = format!("http://{}/text", server.addr());
let mut res = reqwest::get(&url).unwrap();
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("test".to_string())));
assert_eq!(res.headers().get(),
Some(&reqwest::header::ContentLength(4)));
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");
let body = res.text().unwrap();
assert_eq!("你好", &body);
@@ -78,10 +74,10 @@ fn test_response_copy_to() {
let server = server! {
request: b"\
GET /1 HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -96,11 +92,9 @@ fn test_response_copy_to() {
let url = format!("http://{}/1", server.addr());
let mut res = reqwest::get(&url).unwrap();
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("test".to_string())));
assert_eq!(res.headers().get(),
Some(&reqwest::header::ContentLength(5)));
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");
let mut buf: Vec<u8> = vec![];
res.copy_to(&mut buf).unwrap();
@@ -112,10 +106,10 @@ fn test_get() {
let server = server! {
request: b"\
GET /1 HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -130,11 +124,9 @@ fn test_get() {
let mut res = reqwest::get(&url).unwrap();
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("test".to_string())));
assert_eq!(res.headers().get(),
Some(&reqwest::header::ContentLength(0)));
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");
let mut buf = [0; 1024];
let n = res.read(&mut buf).unwrap();
@@ -146,11 +138,11 @@ fn test_post() {
let server = server! {
request: b"\
POST /2 HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Content-Length: 5\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
content-length: 5\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
Hello\
",
@@ -170,11 +162,9 @@ fn test_post() {
.unwrap();
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("post")));
assert_eq!(res.headers().get(),
Some(&reqwest::header::ContentLength(0)));
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");
let mut buf = [0; 1024];
let n = res.read(&mut buf).unwrap();
@@ -188,10 +178,10 @@ fn test_error_for_status_4xx() {
let server = server! {
request: b"\
GET /1 HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -207,7 +197,7 @@ fn test_error_for_status_4xx() {
let err = res.error_for_status().err().unwrap();
assert!(err.is_client_error());
assert_eq!(err.status(), Some(reqwest::StatusCode::BadRequest));
assert_eq!(err.status(), Some(reqwest::StatusCode::BAD_REQUEST));
}
/// Calling `Response::error_for_status`` on a response with status in 5xx
@@ -217,10 +207,10 @@ fn test_error_for_status_5xx() {
let server = server! {
request: b"\
GET /1 HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -236,17 +226,14 @@ 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::InternalServerError));
assert_eq!(err.status(), Some(reqwest::StatusCode::INTERNAL_SERVER_ERROR));
}
#[test]
fn test_default_headers() {
use reqwest::header;
let mut headers = header::Headers::with_capacity(1);
let mut cookies = header::Cookie::new();
cookies.set("a", "b");
cookies.set("c", "d");
headers.set(cookies);
let mut headers = header::HeaderMap::with_capacity(1);
headers.insert(header::COOKIE, header::HeaderValue::from_static("a=b;c=d"));
let client = reqwest::Client::builder()
.default_headers(headers)
.build().unwrap();
@@ -254,11 +241,11 @@ fn test_default_headers() {
let server = server! {
request: b"\
GET /1 HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Cookie: a=b; c=d\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
cookie: a=b;c=d\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -273,20 +260,18 @@ fn test_default_headers() {
let res = client.get(&url).send().unwrap();
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("test")));
assert_eq!(res.headers().get(),
Some(&reqwest::header::ContentLength(0)));
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");
let server = server! {
request: b"\
GET /2 HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Cookie: a=b; c=d\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
cookie: a=b;c=d\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -301,18 +286,16 @@ fn test_default_headers() {
let res = client.get(&url).send().unwrap();
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("test")));
assert_eq!(res.headers().get(),
Some(&reqwest::header::ContentLength(0)));
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");
}
#[test]
fn test_override_default_headers() {
use reqwest::header;
let mut headers = header::Headers::with_capacity(1);
headers.set(header::Authorization("iamatoken".to_string()));
let mut headers = header::HeaderMap::with_capacity(1);
headers.insert(header::AUTHORIZATION, header::HeaderValue::from_static("iamatoken"));
let client = reqwest::Client::builder()
.default_headers(headers)
.build().unwrap();
@@ -320,11 +303,11 @@ fn test_override_default_headers() {
let server = server! {
request: b"\
GET /3 HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Authorization: secret\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
authorization: secret\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -336,13 +319,11 @@ fn test_override_default_headers() {
};
let url = format!("http://{}/3", server.addr());
let res = client.get(&url).header(header::Authorization("secret".to_string())).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(),
Some(&reqwest::header::Server::new("test")));
assert_eq!(res.headers().get(),
Some(&reqwest::header::ContentLength(0)));
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");
}

View File

@@ -31,10 +31,10 @@ fn test_gzip_response() {
let server = server! {
request: b"\
GET /gzip HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
chunk_size: chunk_size,
@@ -54,10 +54,10 @@ fn test_gzip_empty_body() {
let server = server! {
request: b"\
HEAD /gzip HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -85,10 +85,10 @@ fn test_gzip_invalid_body() {
let server = server! {
request: b"\
GET /gzip HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -113,10 +113,10 @@ fn test_accept_header_is_not_changed_if_set() {
let server = server! {
request: b"\
GET /accept HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: application/json\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: application/json\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -130,11 +130,11 @@ fn test_accept_header_is_not_changed_if_set() {
let res = client
.get(&format!("http://{}/accept", server.addr()))
.header(reqwest::header::Accept::json())
.header(reqwest::header::ACCEPT, reqwest::header::HeaderValue::from_static("application/json"))
.send()
.unwrap();
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}
#[test]
@@ -142,10 +142,10 @@ fn test_accept_encoding_header_is_not_changed_if_set() {
let server = server! {
request: b"\
GET /accept-encoding HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: identity\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: identity\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -158,11 +158,9 @@ 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::AcceptEncoding(
vec![reqwest::header::qitem(reqwest::header::Encoding::Identity)]
))
.header(reqwest::header::ACCEPT_ENCODING, reqwest::header::HeaderValue::from_static("identity"))
.send()
.unwrap();
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}

View File

@@ -21,12 +21,12 @@ fn test_multipart() {
let server = server! {
request: format!("\
POST /multipart/1 HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Content-Type: multipart/form-data; boundary={}\r\n\
Content-Length: 123\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
content-type: multipart/form-data; boundary={}\r\n\
content-length: 123\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
{}\
", form.boundary(), expected_body),
@@ -47,5 +47,5 @@ fn test_multipart() {
.unwrap();
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.status(), reqwest::StatusCode::OK);
}

View File

@@ -8,10 +8,10 @@ fn test_http_proxy() {
let server = server! {
request: b"\
GET http://hyper.rs/prox HTTP/1.1\r\n\
Host: hyper.rs\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: hyper.rs\r\n\
\r\n\
",
response: b"\
@@ -34,7 +34,6 @@ fn test_http_proxy() {
.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("proxied")));
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"proxied");
}

View File

@@ -12,10 +12,10 @@ fn test_redirect_301_and_302_and_303_changes_post_to_get() {
let redirect = server! {
request: format!("\
POST /{} HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: format!("\
@@ -29,11 +29,11 @@ fn test_redirect_301_and_302_and_303_changes_post_to_get() {
request: format!("\
GET /dst HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
Referer: http://$HOST/{}\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
referer: http://$HOST/{}\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: b"\
@@ -50,9 +50,8 @@ fn test_redirect_301_and_302_and_303_changes_post_to_get() {
.send()
.unwrap();
assert_eq!(res.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("test-dst".to_string())));
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test-dst");
}
}
@@ -64,10 +63,10 @@ fn test_redirect_307_and_308_tries_to_get_again() {
let redirect = server! {
request: format!("\
GET /{} HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: format!("\
@@ -81,11 +80,11 @@ fn test_redirect_307_and_308_tries_to_get_again() {
request: format!("\
GET /dst HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
Referer: http://$HOST/{}\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
referer: http://$HOST/{}\r\n\
host: $HOST\r\n\
\r\n\
", code),
response: b"\
@@ -102,9 +101,8 @@ fn test_redirect_307_and_308_tries_to_get_again() {
.send()
.unwrap();
assert_eq!(res.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("test-dst".to_string())));
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test-dst");
}
}
@@ -116,11 +114,11 @@ fn test_redirect_307_and_308_tries_to_post_again() {
let redirect = server! {
request: format!("\
POST /{} HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Content-Length: 5\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
content-length: 5\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
Hello\
", code),
@@ -135,12 +133,12 @@ fn test_redirect_307_and_308_tries_to_post_again() {
request: format!("\
POST /dst HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Content-Length: 5\r\n\
Accept-Encoding: gzip\r\n\
Referer: http://$HOST/{}\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
content-length: 5\r\n\
accept-encoding: gzip\r\n\
referer: http://$HOST/{}\r\n\
host: $HOST\r\n\
\r\n\
Hello\
", code),
@@ -159,9 +157,8 @@ fn test_redirect_307_and_308_tries_to_post_again() {
.send()
.unwrap();
assert_eq!(res.url().as_str(), dst);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("test-dst".to_string())));
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test-dst");
}
}
@@ -173,11 +170,11 @@ fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
let redirect = server! {
request: format!("\
POST /{} HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
Transfer-Encoding: chunked\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
transfer-encoding: chunked\r\n\
\r\n\
5\r\n\
Hello\r\n\
@@ -200,7 +197,7 @@ fn test_redirect_307_does_not_try_if_reader_cannot_reset() {
.send()
.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::try_from(code).unwrap());
assert_eq!(res.status(), reqwest::StatusCode::from_u16(code).unwrap());
}
}
@@ -211,10 +208,10 @@ fn test_redirect_removes_sensitive_headers() {
let end_server = server! {
request: b"\
GET /otherhost HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -228,11 +225,11 @@ fn test_redirect_removes_sensitive_headers() {
let mid_server = server! {
request: b"\
GET /sensitive HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Cookie: foo=bar\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
cookie: foo=bar\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: format!("\
@@ -244,14 +241,12 @@ fn test_redirect_removes_sensitive_headers() {
", end_server.addr())
};
let mut cookie = reqwest::header::Cookie::new();
cookie.set("foo", "bar");
reqwest::Client::builder()
.referer(false)
.build()
.unwrap()
.get(&format!("http://{}/sensitive", mid_server.addr()))
.header(cookie)
.header(reqwest::header::COOKIE, reqwest::header::HeaderValue::from_static("foo=bar"))
.send()
.unwrap();
}
@@ -261,10 +256,10 @@ fn test_redirect_policy_can_return_errors() {
let server = server! {
request: b"\
GET /loop HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -285,10 +280,10 @@ fn test_redirect_policy_can_stop_redirects_without_an_error() {
let server = server! {
request: b"\
GET /no-redirect HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -311,9 +306,8 @@ fn test_redirect_policy_can_stop_redirects_without_an_error() {
.unwrap();
assert_eq!(res.url().as_str(), url);
assert_eq!(res.status(), reqwest::StatusCode::Found);
assert_eq!(res.headers().get(),
Some(&reqwest::header::Server::new("test-dont".to_string())));
assert_eq!(res.status(), reqwest::StatusCode::FOUND);
assert_eq!(res.headers().get(reqwest::header::SERVER).unwrap(), &"test-dont");
}
#[test]
@@ -321,10 +315,10 @@ fn test_referer_is_not_set_if_disabled() {
let server = server! {
request: b"\
GET /no-refer HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -338,10 +332,10 @@ fn test_referer_is_not_set_if_disabled() {
request: b"\
GET /dst HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\

View File

@@ -11,11 +11,11 @@ fn test_write_timeout() {
let server = server! {
request: b"\
POST /write-timeout HTTP/1.1\r\n\
Host: $HOST\r\n\
Content-Length: 5\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
content-length: 5\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
Hello\
",
@@ -34,7 +34,7 @@ fn test_write_timeout() {
.build()
.unwrap()
.post(&url)
.header(reqwest::header::ContentLength(5))
.header(reqwest::header::CONTENT_LENGTH, reqwest::header::HeaderValue::from_static("5"))
.body(reqwest::Body::new(&b"Hello"[..]))
.send()
.unwrap_err();
@@ -49,10 +49,10 @@ fn test_response_timeout() {
let server = server! {
request: b"\
GET /response-timeout HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -80,10 +80,10 @@ fn test_read_timeout() {
let server = server! {
request: b"\
GET /read-timeout HTTP/1.1\r\n\
Host: $HOST\r\n\
User-Agent: $USERAGENT\r\n\
Accept: */*\r\n\
Accept-Encoding: gzip\r\n\
user-agent: $USERAGENT\r\n\
accept: */*\r\n\
accept-encoding: gzip\r\n\
host: $HOST\r\n\
\r\n\
",
response: b"\
@@ -105,9 +105,8 @@ fn test_read_timeout() {
.unwrap();
assert_eq!(res.url().as_str(), &url);
assert_eq!(res.status(), reqwest::StatusCode::Ok);
assert_eq!(res.headers().get(),
Some(&reqwest::header::ContentLength(5)));
assert_eq!(res.status(), reqwest::StatusCode::OK);
assert_eq!(res.headers().get(reqwest::header::CONTENT_LENGTH).unwrap(), &"5");
let mut buf = [0; 1024];
let err = res.read(&mut buf).unwrap_err();