feat(lib): replace types with those from http crate
BREAKING CHANGE: `Method`, `Request`, `Response`, `StatusCode`, `Version`, and `Uri` have been replaced with types from the `http` crate. The `hyper::header` module is gone for now. Removed `Client::get`, since it needed to construct a `Request<B>` with an empty body. Just use `Client::request` instead. Removed `compat` cargo feature, and `compat` related API.
This commit is contained in:
@@ -1,55 +0,0 @@
|
||||
//! Wrappers to build compatibility with the `http` crate.
|
||||
|
||||
use futures::{Future, Poll, Stream};
|
||||
use http;
|
||||
use tokio_service::Service;
|
||||
|
||||
use client::{Connect, Client, FutureResponse};
|
||||
use error::Error;
|
||||
use proto::Body;
|
||||
|
||||
/// A Client to make outgoing HTTP requests.
|
||||
#[derive(Debug)]
|
||||
pub struct CompatClient<C, B = Body> {
|
||||
inner: Client<C, B>
|
||||
}
|
||||
|
||||
pub(super) fn client<C, B>(client: Client<C, B>) -> CompatClient<C, B> {
|
||||
CompatClient { inner: client }
|
||||
}
|
||||
|
||||
impl<C, B> Service for CompatClient<C, B>
|
||||
where C: Connect,
|
||||
B: Stream<Error=Error> + 'static,
|
||||
B::Item: AsRef<[u8]>,
|
||||
{
|
||||
type Request = http::Request<B>;
|
||||
type Response = http::Response<Body>;
|
||||
type Error = Error;
|
||||
type Future = CompatFutureResponse;
|
||||
|
||||
fn call(&self, req: Self::Request) -> Self::Future {
|
||||
future(self.inner.call(req.into()))
|
||||
}
|
||||
}
|
||||
|
||||
/// A `Future` that will resolve to an `http::Response`.
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
#[derive(Debug)]
|
||||
pub struct CompatFutureResponse {
|
||||
inner: FutureResponse
|
||||
}
|
||||
|
||||
pub(super) fn future(fut: FutureResponse) -> CompatFutureResponse {
|
||||
CompatFutureResponse { inner: fut }
|
||||
}
|
||||
|
||||
impl Future for CompatFutureResponse {
|
||||
type Item = http::Response<Body>;
|
||||
type Error = Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Error> {
|
||||
self.inner.poll()
|
||||
.map(|a| a.map(|r| r.into()))
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,8 @@ use futures::future::{self, Either};
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use proto;
|
||||
use super::{dispatch, Request, Response};
|
||||
use super::dispatch;
|
||||
use {Body, Request, Response, StatusCode};
|
||||
|
||||
/// Returns a `Handshake` future over some IO.
|
||||
///
|
||||
@@ -31,7 +32,7 @@ where
|
||||
|
||||
/// The sender side of an established connection.
|
||||
pub struct SendRequest<B> {
|
||||
dispatch: dispatch::Sender<proto::dispatch::ClientMsg<B>, ::Response>,
|
||||
dispatch: dispatch::Sender<proto::dispatch::ClientMsg<B>, Response<Body>>,
|
||||
|
||||
}
|
||||
|
||||
@@ -79,7 +80,7 @@ pub struct Handshake<T, B> {
|
||||
pub struct ResponseFuture {
|
||||
// for now, a Box is used to hide away the internal `B`
|
||||
// that can be returned if canceled
|
||||
inner: Box<Future<Item=Response, Error=::Error> + Send>,
|
||||
inner: Box<Future<Item=Response<Body>, Error=::Error> + Send>,
|
||||
}
|
||||
|
||||
/// Deconstructed parts of a `Connection`.
|
||||
@@ -158,17 +159,20 @@ where
|
||||
/// ```
|
||||
/// # extern crate futures;
|
||||
/// # extern crate hyper;
|
||||
/// # extern crate http;
|
||||
/// # use http::header::HOST;
|
||||
/// # use hyper::client::conn::SendRequest;
|
||||
/// # use hyper::Body;
|
||||
/// use futures::Future;
|
||||
/// use hyper::{Method, Request};
|
||||
/// use hyper::header::Host;
|
||||
/// use hyper::Request;
|
||||
///
|
||||
/// # fn doc(mut tx: SendRequest<Body>) {
|
||||
/// // build a Request
|
||||
/// let path = "/foo/bar".parse().expect("valid path");
|
||||
/// let mut req = Request::new(Method::Get, path);
|
||||
/// req.headers_mut().set(Host::new("hyper.rs", None));
|
||||
/// let req = Request::builder()
|
||||
/// .uri("/foo/bar")
|
||||
/// .header(HOST, "hyper.rs")
|
||||
/// .body(Body::empty())
|
||||
/// .unwrap();
|
||||
///
|
||||
/// // send it and get a future back
|
||||
/// let fut = tx.send_request(req)
|
||||
@@ -180,7 +184,8 @@ where
|
||||
/// # }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
pub fn send_request(&mut self, mut req: Request<B>) -> ResponseFuture {
|
||||
pub fn send_request(&mut self, req: Request<B>) -> ResponseFuture {
|
||||
/* TODO?
|
||||
// The Connection API does less things automatically than the Client
|
||||
// API does. For instance, right here, we always assume set_proxy, so
|
||||
// that if an absolute-form URI is provided, it is serialized as-is.
|
||||
@@ -191,9 +196,9 @@ where
|
||||
// It's important that this method isn't called directly from the
|
||||
// `Client`, so that `set_proxy` there is still respected.
|
||||
req.set_proxy(true);
|
||||
*/
|
||||
|
||||
let (head, body) = proto::request::split(req);
|
||||
let inner = match self.dispatch.send((head, body)) {
|
||||
let inner = match self.dispatch.send(req) {
|
||||
Ok(rx) => {
|
||||
Either::A(rx.then(move |res| {
|
||||
match res {
|
||||
@@ -210,15 +215,15 @@ where
|
||||
Either::B(future::err(err))
|
||||
}
|
||||
};
|
||||
|
||||
ResponseFuture {
|
||||
inner: Box::new(inner),
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: replace with `impl Future` when stable
|
||||
pub(crate) fn send_request_retryable(&mut self, req: Request<B>) -> Box<Future<Item=Response, Error=(::Error, Option<(::proto::RequestHead, Option<B>)>)>> {
|
||||
let (head, body) = proto::request::split(req);
|
||||
let inner = match self.dispatch.try_send((head, body)) {
|
||||
pub(crate) fn send_request_retryable(&mut self, req: Request<B>) -> Box<Future<Item=Response<Body>, Error=(::Error, Option<Request<B>>)>> {
|
||||
let inner = match self.dispatch.try_send(req) {
|
||||
Ok(rx) => {
|
||||
Either::A(rx.then(move |res| {
|
||||
match res {
|
||||
@@ -418,7 +423,7 @@ where
|
||||
B: Stream<Error=::Error> + 'static,
|
||||
B::Item: AsRef<[u8]>,
|
||||
R: proto::Http1Transaction<
|
||||
Incoming=proto::RawStatus,
|
||||
Incoming=StatusCode,
|
||||
Outgoing=proto::RequestLine,
|
||||
>,
|
||||
{
|
||||
@@ -451,7 +456,7 @@ where
|
||||
// ===== impl ResponseFuture
|
||||
|
||||
impl Future for ResponseFuture {
|
||||
type Item = Response;
|
||||
type Item = Response<Body>;
|
||||
type Error = ::Error;
|
||||
|
||||
#[inline]
|
||||
@@ -497,3 +502,4 @@ impl AssertSendSync for Builder {}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl AssertSend for ResponseFuture {}
|
||||
|
||||
|
||||
@@ -9,11 +9,12 @@ use futures::{Future, Poll, Async};
|
||||
use futures::future::{Executor, ExecuteError};
|
||||
use futures::sync::oneshot;
|
||||
use futures_cpupool::{Builder as CpuPoolBuilder};
|
||||
use http::Uri;
|
||||
use http::uri::Scheme;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tokio::reactor::Handle;
|
||||
use tokio::net::{TcpStream, TcpStreamNew};
|
||||
use tokio_service::Service;
|
||||
use Uri;
|
||||
|
||||
use super::dns;
|
||||
|
||||
@@ -118,10 +119,10 @@ impl Service for HttpConnector {
|
||||
trace!("Http::connect({:?})", uri);
|
||||
|
||||
if self.enforce_http {
|
||||
if uri.scheme() != Some("http") {
|
||||
if uri.scheme_part() != Some(&Scheme::HTTP) {
|
||||
return invalid_url(InvalidUrl::NotHttp, &self.handle);
|
||||
}
|
||||
} else if uri.scheme().is_none() {
|
||||
} else if uri.scheme_part().is_none() {
|
||||
return invalid_url(InvalidUrl::MissingScheme, &self.handle);
|
||||
}
|
||||
|
||||
@@ -131,10 +132,7 @@ impl Service for HttpConnector {
|
||||
};
|
||||
let port = match uri.port() {
|
||||
Some(port) => port,
|
||||
None => match uri.scheme() {
|
||||
Some("https") => 443,
|
||||
_ => 80,
|
||||
},
|
||||
None => if uri.scheme_part() == Some(&Scheme::HTTPS) { 443 } else { 80 },
|
||||
};
|
||||
|
||||
HttpConnecting {
|
||||
|
||||
@@ -9,21 +9,14 @@ use std::time::Duration;
|
||||
|
||||
use futures::{Async, Future, Poll, Stream};
|
||||
use futures::future::{self, Executor};
|
||||
#[cfg(feature = "compat")]
|
||||
use http;
|
||||
use http::{Method, Request, Response, Uri, Version};
|
||||
use http::header::{Entry, HeaderValue, HOST};
|
||||
use tokio::reactor::Handle;
|
||||
pub use tokio_service::Service;
|
||||
|
||||
use header::{Host};
|
||||
use proto;
|
||||
use proto::request;
|
||||
use method::Method;
|
||||
use proto::{self, Body};
|
||||
use self::pool::Pool;
|
||||
use uri::{self, Uri};
|
||||
use version::HttpVersion;
|
||||
|
||||
pub use proto::response::Response;
|
||||
pub use proto::request::Request;
|
||||
pub use self::connect::{HttpConnector, Connect};
|
||||
|
||||
use self::background::{bg, Background};
|
||||
@@ -34,8 +27,6 @@ mod connect;
|
||||
pub(crate) mod dispatch;
|
||||
mod dns;
|
||||
mod pool;
|
||||
#[cfg(feature = "compat")]
|
||||
pub mod compat;
|
||||
mod signal;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -113,14 +104,29 @@ where C: Connect,
|
||||
B: Stream<Error=::Error> + 'static,
|
||||
B::Item: AsRef<[u8]>,
|
||||
{
|
||||
/// Send a GET Request using this Client.
|
||||
#[inline]
|
||||
pub fn get(&self, url: Uri) -> FutureResponse {
|
||||
self.request(Request::new(Method::Get, url))
|
||||
|
||||
/// Send a `GET` request to the supplied `Uri`.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This requires that the `Entity` type have a `Default` implementation.
|
||||
/// It *should* return an "empty" version of itself, such that
|
||||
/// `Entity::is_end_stream` is `true`.
|
||||
pub fn get(&self, uri: Uri) -> FutureResponse
|
||||
where
|
||||
B: Default,
|
||||
{
|
||||
let body = B::default();
|
||||
if !body.is_end_stream() {
|
||||
warn!("default Entity used for get() does not return true for is_end_stream");
|
||||
}
|
||||
|
||||
let mut req = Request::new(body);
|
||||
*req.uri_mut() = uri;
|
||||
self.request(req)
|
||||
}
|
||||
|
||||
/// Send a constructed Request using this Client.
|
||||
#[inline]
|
||||
pub fn request(&self, mut req: Request<B>) -> FutureResponse {
|
||||
// TODO(0.12): do this at construction time.
|
||||
//
|
||||
@@ -131,23 +137,25 @@ where C: Connect,
|
||||
self.schedule_pool_timer();
|
||||
|
||||
match req.version() {
|
||||
HttpVersion::Http10 |
|
||||
HttpVersion::Http11 => (),
|
||||
Version::HTTP_10 |
|
||||
Version::HTTP_11 => (),
|
||||
other => {
|
||||
error!("Request has unsupported version \"{}\"", other);
|
||||
error!("Request has unsupported version \"{:?}\"", other);
|
||||
return FutureResponse(Box::new(future::err(::Error::Version)));
|
||||
}
|
||||
}
|
||||
|
||||
if req.method() == &Method::Connect {
|
||||
if req.method() == &Method::CONNECT {
|
||||
debug!("Client does not support CONNECT requests");
|
||||
return FutureResponse(Box::new(future::err(::Error::Method)));
|
||||
}
|
||||
|
||||
let domain = match uri::scheme_and_authority(req.uri()) {
|
||||
Some(uri) => uri,
|
||||
None => {
|
||||
debug!("request uri does not include scheme and authority");
|
||||
let uri = req.uri().clone();
|
||||
let domain = match (uri.scheme_part(), uri.authority_part()) {
|
||||
(Some(scheme), Some(auth)) => {
|
||||
format!("{}://{}", scheme, auth)
|
||||
}
|
||||
_ => {
|
||||
return FutureResponse(Box::new(future::err(::Error::Io(
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
@@ -156,45 +164,51 @@ where C: Connect,
|
||||
))));
|
||||
}
|
||||
};
|
||||
if self.set_host && !req.headers().has::<Host>() {
|
||||
let host = Host::new(
|
||||
domain.host().expect("authority implies host").to_owned(),
|
||||
domain.port(),
|
||||
);
|
||||
req.headers_mut().set_pos(0, host);
|
||||
|
||||
if self.set_host {
|
||||
if let Entry::Vacant(entry) = req.headers_mut().entry(HOST).expect("HOST is always valid header name") {
|
||||
let hostname = uri.host().expect("authority implies host");
|
||||
let host = if let Some(port) = uri.port() {
|
||||
let s = format!("{}:{}", hostname, port);
|
||||
HeaderValue::from_str(&s)
|
||||
} else {
|
||||
HeaderValue::from_str(hostname)
|
||||
}.expect("uri host is valid header value");
|
||||
entry.insert(host);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let client = self.clone();
|
||||
let is_proxy = req.is_proxy();
|
||||
let uri = req.uri().clone();
|
||||
//TODO: let is_proxy = req.is_proxy();
|
||||
//let uri = req.uri().clone();
|
||||
let fut = RetryableSendRequest {
|
||||
client: client,
|
||||
future: self.send_request(req, &domain),
|
||||
domain: domain,
|
||||
is_proxy: is_proxy,
|
||||
uri: uri,
|
||||
//is_proxy: is_proxy,
|
||||
//uri: uri,
|
||||
};
|
||||
FutureResponse(Box::new(fut))
|
||||
}
|
||||
|
||||
/// Send an `http::Request` using this Client.
|
||||
#[inline]
|
||||
#[cfg(feature = "compat")]
|
||||
pub fn request_compat(&self, req: http::Request<B>) -> compat::CompatFutureResponse {
|
||||
self::compat::future(self.call(req.into()))
|
||||
}
|
||||
|
||||
/// Convert into a client accepting `http::Request`.
|
||||
#[cfg(feature = "compat")]
|
||||
pub fn into_compat(self) -> compat::CompatClient<C, B> {
|
||||
self::compat::client(self)
|
||||
}
|
||||
|
||||
//TODO: replace with `impl Future` when stable
|
||||
fn send_request(&self, req: Request<B>, domain: &Uri) -> Box<Future<Item=Response, Error=ClientError<B>>> {
|
||||
//fn send_request(&self, req: Request<B>, domain: &Uri) -> Box<Future<Item=Response, Error=::Error>> {
|
||||
fn send_request(&self, mut req: Request<B>, domain: &str) -> Box<Future<Item=Response<Body>, Error=ClientError<B>>> {
|
||||
let url = req.uri().clone();
|
||||
let checkout = self.pool.checkout(domain.as_ref());
|
||||
|
||||
let path = match url.path_and_query() {
|
||||
Some(path) => {
|
||||
let mut parts = ::http::uri::Parts::default();
|
||||
parts.path_and_query = Some(path.clone());
|
||||
Uri::from_parts(parts).expect("path is valid uri")
|
||||
},
|
||||
None => {
|
||||
"/".parse().expect("/ is valid path")
|
||||
}
|
||||
};
|
||||
*req.uri_mut() = path;
|
||||
|
||||
let checkout = self.pool.checkout(domain);
|
||||
let connect = {
|
||||
let executor = self.executor.clone();
|
||||
let pool = self.pool.clone();
|
||||
@@ -228,7 +242,6 @@ where C: Connect,
|
||||
ClientError::Normal(e)
|
||||
});
|
||||
|
||||
|
||||
let executor = self.executor.clone();
|
||||
let resp = race.and_then(move |mut pooled| {
|
||||
let conn_reused = pooled.is_reused();
|
||||
@@ -284,7 +297,7 @@ where C: Connect,
|
||||
B::Item: AsRef<[u8]>,
|
||||
{
|
||||
type Request = Request<B>;
|
||||
type Response = Response;
|
||||
type Response = Response<Body>;
|
||||
type Error = ::Error;
|
||||
type Future = FutureResponse;
|
||||
|
||||
@@ -315,7 +328,7 @@ impl<C, B> fmt::Debug for Client<C, B> {
|
||||
|
||||
/// A `Future` that will resolve to an HTTP Response.
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
pub struct FutureResponse(Box<Future<Item=Response, Error=::Error> + 'static>);
|
||||
pub struct FutureResponse(Box<Future<Item=Response<Body>, Error=::Error> + 'static>);
|
||||
|
||||
impl fmt::Debug for FutureResponse {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
@@ -324,7 +337,7 @@ impl fmt::Debug for FutureResponse {
|
||||
}
|
||||
|
||||
impl Future for FutureResponse {
|
||||
type Item = Response;
|
||||
type Item = Response<Body>;
|
||||
type Error = ::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
@@ -334,10 +347,10 @@ impl Future for FutureResponse {
|
||||
|
||||
struct RetryableSendRequest<C, B> {
|
||||
client: Client<C, B>,
|
||||
domain: Uri,
|
||||
future: Box<Future<Item=Response, Error=ClientError<B>>>,
|
||||
is_proxy: bool,
|
||||
uri: Uri,
|
||||
domain: String,
|
||||
future: Box<Future<Item=Response<Body>, Error=ClientError<B>>>,
|
||||
//is_proxy: bool,
|
||||
//uri: Uri,
|
||||
}
|
||||
|
||||
impl<C, B> Future for RetryableSendRequest<C, B>
|
||||
@@ -346,7 +359,7 @@ where
|
||||
B: Stream<Error=::Error> + 'static,
|
||||
B::Item: AsRef<[u8]>,
|
||||
{
|
||||
type Item = Response;
|
||||
type Item = Response<Body>;
|
||||
type Error = ::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
@@ -367,9 +380,6 @@ where
|
||||
}
|
||||
|
||||
trace!("unstarted request canceled, trying again (reason={:?})", reason);
|
||||
let mut req = request::join(req);
|
||||
req.set_proxy(self.is_proxy);
|
||||
req.set_uri(self.uri.clone());
|
||||
self.future = self.client.send_request(req, &self.domain);
|
||||
}
|
||||
}
|
||||
@@ -394,7 +404,7 @@ pub(crate) enum ClientError<B> {
|
||||
Normal(::Error),
|
||||
Canceled {
|
||||
connection_reused: bool,
|
||||
req: (::proto::RequestHead, Option<B>),
|
||||
req: Request<B>,
|
||||
reason: ::Error,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,12 @@ fn retryable_request() {
|
||||
|
||||
|
||||
{
|
||||
let res1 = client.get("http://mock.local/a".parse().unwrap());
|
||||
|
||||
let req = Request::builder()
|
||||
.uri("http://mock.local/a")
|
||||
.body(Default::default())
|
||||
.unwrap();
|
||||
let res1 = client.request(req);
|
||||
let srv1 = poll_fn(|| {
|
||||
try_ready!(sock1.read(&mut [0u8; 512]));
|
||||
try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
|
||||
@@ -33,7 +38,11 @@ fn retryable_request() {
|
||||
}
|
||||
drop(sock1);
|
||||
|
||||
let res2 = client.get("http://mock.local/b".parse().unwrap())
|
||||
let req = Request::builder()
|
||||
.uri("http://mock.local/b")
|
||||
.body(Default::default())
|
||||
.unwrap();
|
||||
let res2 = client.request(req)
|
||||
.map(|res| {
|
||||
assert_eq!(res.status().as_u16(), 222);
|
||||
});
|
||||
@@ -61,7 +70,13 @@ fn conn_reset_after_write() {
|
||||
|
||||
|
||||
{
|
||||
let res1 = client.get("http://mock.local/a".parse().unwrap());
|
||||
let req = Request::builder()
|
||||
.uri("http://mock.local/a")
|
||||
//TODO: remove this header when auto lengths are fixed
|
||||
.header("content-length", "0")
|
||||
.body(Default::default())
|
||||
.unwrap();
|
||||
let res1 = client.request(req);
|
||||
let srv1 = poll_fn(|| {
|
||||
try_ready!(sock1.read(&mut [0u8; 512]));
|
||||
try_ready!(sock1.write(b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"));
|
||||
@@ -70,7 +85,11 @@ fn conn_reset_after_write() {
|
||||
core.run(res1.join(srv1)).expect("res1");
|
||||
}
|
||||
|
||||
let res2 = client.get("http://mock.local/a".parse().unwrap());
|
||||
let req = Request::builder()
|
||||
.uri("http://mock.local/a")
|
||||
.body(Default::default())
|
||||
.unwrap();
|
||||
let res2 = client.request(req);
|
||||
let mut sock1 = Some(sock1);
|
||||
let srv2 = poll_fn(|| {
|
||||
// We purposefully keep the socket open until the client
|
||||
|
||||
Reference in New Issue
Block a user