feat(body): introduce an Entity trait to represent bodies
				
					
				
			This dedicated `Entity` trait replaces the previous `Stream<Item=impl AsRef<[u8]>, Error=hyper::Error>`. This allows for several improvements immediately, and prepares for HTTP2 support. - The `Entity::is_end_stream` makes up for change away from `Option<Body>`, which was previously used to know if the body should be empty. Since `Request` and `Response` now require a body to be set, this method can be used to tell hyper that the body is actually empty. It also provides the possibility of slight optimizations when polling for data, by allowing to check `is_end_stream` before polling again. This can allow a consumer to know that a body stream has ended without polling for `None` afterwards. - The `Entity::content_length` method allows a body to automatically declare a size, in case a user doesn't set a `Content-Length` or `Transfer-Encoding` header. - It's now possible to send and receive trailers, though this will be for HTTP2 connections only. By being a trait owned by hyper, new methods can be added later as new features are wanted (with default implementations). The `hyper::Body` type now implements `Entity` instead of `Stream`, provides a better channel option, and is easier to use with custom streams via `Body::wrap_stream`. BREAKING CHANGE: All code that was assuming the body was a `Stream` must be adjusted to use an `Entity` instead. Using `hyper::Body` as a `Stream` can call `Body::into_stream` to get a stream wrapper. Passing a custom `impl Stream` will need to either implement `Entity`, or as an easier option, switch to `Body::wrap_stream`. `Body::pair` has been replaced with `Body::channel`, which returns a `hyper::body::Sender` instead of a `futures::sync::mpsc::Sender`. Closes #1438
This commit is contained in:
		| @@ -11,11 +11,12 @@ use std::fmt; | ||||
| use std::marker::PhantomData; | ||||
|  | ||||
| use bytes::Bytes; | ||||
| use futures::{Async, Future, Poll, Stream}; | ||||
| use futures::{Async, Future, Poll}; | ||||
| use futures::future::{self, Either}; | ||||
| use tokio_io::{AsyncRead, AsyncWrite}; | ||||
|  | ||||
| use proto; | ||||
| use proto::body::Entity; | ||||
| use super::dispatch; | ||||
| use {Body, Request, Response, StatusCode}; | ||||
|  | ||||
| @@ -44,14 +45,13 @@ pub struct SendRequest<B> { | ||||
| pub struct Connection<T, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite, | ||||
|     B: Stream<Error=::Error> + 'static, | ||||
|     B::Item: AsRef<[u8]>, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
| { | ||||
|     inner: proto::dispatch::Dispatcher< | ||||
|         proto::dispatch::Client<B>, | ||||
|         B, | ||||
|         T, | ||||
|         B::Item, | ||||
|         B::Data, | ||||
|         proto::ClientUpgradeTransaction, | ||||
|     >, | ||||
| } | ||||
| @@ -134,8 +134,7 @@ impl<B> SendRequest<B> | ||||
|  | ||||
| impl<B> SendRequest<B> | ||||
| where | ||||
|     B: Stream<Error=::Error> + 'static, | ||||
|     B::Item: AsRef<[u8]>, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
| { | ||||
|     /// Sends a `Request` on the associated connection. | ||||
|     /// | ||||
| @@ -152,7 +151,7 @@ where | ||||
|     ///   the `Host` header based on it. You must add a `Host` header yourself | ||||
|     ///   before calling this method. | ||||
|     /// - Since absolute-form `Uri`s are not required, if received, they will | ||||
|     ///   be serialized as-is, irregardless of calling `Request::set_proxy`. | ||||
|     ///   be serialized as-is. | ||||
|     /// | ||||
|     /// # Example | ||||
|     /// | ||||
| @@ -185,19 +184,6 @@ where | ||||
|     /// # fn main() {} | ||||
|     /// ``` | ||||
|     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. | ||||
|         // | ||||
|         // Part of the reason for this is to prepare for the change to `http` | ||||
|         // types, where there is no more set_proxy. | ||||
|         // | ||||
|         // 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 inner = match self.dispatch.send(req) { | ||||
|             Ok(rx) => { | ||||
|                 Either::A(rx.then(move |res| { | ||||
| @@ -269,8 +255,7 @@ impl<B> fmt::Debug for SendRequest<B> { | ||||
| impl<T, B> Connection<T, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite, | ||||
|     B: Stream<Error=::Error> + 'static, | ||||
|     B::Item: AsRef<[u8]>, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
| { | ||||
|     /// Return the inner IO object, and additional information. | ||||
|     pub fn into_parts(self) -> Parts<T> { | ||||
| @@ -297,8 +282,7 @@ where | ||||
| impl<T, B> Future for Connection<T, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite, | ||||
|     B: Stream<Error=::Error> + 'static, | ||||
|     B::Item: AsRef<[u8]>, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
| { | ||||
|     type Item = (); | ||||
|     type Error = ::Error; | ||||
| @@ -311,8 +295,7 @@ where | ||||
| impl<T, B> fmt::Debug for Connection<T, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite + fmt::Debug, | ||||
|     B: Stream<Error=::Error> + 'static, | ||||
|     B::Item: AsRef<[u8]>, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
| { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("Connection") | ||||
| @@ -341,8 +324,7 @@ impl Builder { | ||||
|     pub fn handshake<T, B>(&self, io: T) -> Handshake<T, B> | ||||
|     where | ||||
|         T: AsyncRead + AsyncWrite, | ||||
|         B: Stream<Error=::Error> + 'static, | ||||
|         B::Item: AsRef<[u8]>, | ||||
|         B: Entity<Error=::Error> + 'static, | ||||
|     { | ||||
|         Handshake { | ||||
|             inner: HandshakeInner { | ||||
| @@ -356,8 +338,7 @@ impl Builder { | ||||
|     pub(super) fn handshake_no_upgrades<T, B>(&self, io: T) -> HandshakeNoUpgrades<T, B> | ||||
|     where | ||||
|         T: AsyncRead + AsyncWrite, | ||||
|         B: Stream<Error=::Error> + 'static, | ||||
|         B::Item: AsRef<[u8]>, | ||||
|         B: Entity<Error=::Error> + 'static, | ||||
|     { | ||||
|         HandshakeNoUpgrades { | ||||
|             inner: HandshakeInner { | ||||
| @@ -374,8 +355,7 @@ impl Builder { | ||||
| impl<T, B> Future for Handshake<T, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite, | ||||
|     B: Stream<Error=::Error> + 'static, | ||||
|     B::Item: AsRef<[u8]>, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
| { | ||||
|     type Item = (SendRequest<B>, Connection<T, B>); | ||||
|     type Error = ::Error; | ||||
| @@ -400,14 +380,13 @@ impl<T, B> fmt::Debug for Handshake<T, B> { | ||||
| impl<T, B> Future for HandshakeNoUpgrades<T, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite, | ||||
|     B: Stream<Error=::Error> + 'static, | ||||
|     B::Item: AsRef<[u8]>, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
| { | ||||
|     type Item = (SendRequest<B>, proto::dispatch::Dispatcher< | ||||
|         proto::dispatch::Client<B>, | ||||
|         B, | ||||
|         T, | ||||
|         B::Item, | ||||
|         B::Data, | ||||
|         proto::ClientTransaction, | ||||
|     >); | ||||
|     type Error = ::Error; | ||||
| @@ -420,8 +399,7 @@ where | ||||
| impl<T, B, R> Future for HandshakeInner<T, B, R> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite, | ||||
|     B: Stream<Error=::Error> + 'static, | ||||
|     B::Item: AsRef<[u8]>, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
|     R: proto::Http1Transaction< | ||||
|         Incoming=StatusCode, | ||||
|         Outgoing=proto::RequestLine, | ||||
| @@ -431,7 +409,7 @@ where | ||||
|         proto::dispatch::Client<B>, | ||||
|         B, | ||||
|         T, | ||||
|         B::Item, | ||||
|         B::Data, | ||||
|         R, | ||||
|     >); | ||||
|     type Error = ::Error; | ||||
| @@ -485,16 +463,16 @@ impl<B: Send> AssertSendSync for SendRequest<B> {} | ||||
| impl<T: Send, B: Send> AssertSend for Connection<T, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite, | ||||
|     B: Stream<Error=::Error>, | ||||
|     B::Item: AsRef<[u8]> + Send, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
|     B::Data: Send + 'static, | ||||
| {} | ||||
|  | ||||
| #[doc(hidden)] | ||||
| impl<T: Send + Sync, B: Send + Sync> AssertSendSync for Connection<T, B> | ||||
| where | ||||
|     T: AsyncRead + AsyncWrite, | ||||
|     B: Stream<Error=::Error>, | ||||
|     B::Item: AsRef<[u8]> + Send + Sync, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
|     B::Data: Send + Sync + 'static, | ||||
| {} | ||||
|  | ||||
| #[doc(hidden)] | ||||
|   | ||||
| @@ -7,14 +7,15 @@ use std::rc::Rc; | ||||
| use std::sync::Arc; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use futures::{Async, Future, Poll, Stream}; | ||||
| use futures::{Async, Future, Poll}; | ||||
| use futures::future::{self, Executor}; | ||||
| use http::{Method, Request, Response, Uri, Version}; | ||||
| use http::header::{Entry, HeaderValue, HOST}; | ||||
| use tokio::reactor::Handle; | ||||
| pub use tokio_service::Service; | ||||
|  | ||||
| use proto::{self, Body}; | ||||
| use proto::body::{Body, Entity}; | ||||
| use proto; | ||||
| use self::pool::Pool; | ||||
|  | ||||
| pub use self::connect::{HttpConnector, Connect}; | ||||
| @@ -101,8 +102,7 @@ impl<C, B> Client<C, B> { | ||||
|  | ||||
| impl<C, B> Client<C, B> | ||||
| where C: Connect, | ||||
|       B: Stream<Error=::Error> + 'static, | ||||
|       B::Item: AsRef<[u8]>, | ||||
|       B: Entity<Error=::Error> + 'static, | ||||
| { | ||||
|  | ||||
|     /// Send a `GET` request to the supplied `Uri`. | ||||
| @@ -181,13 +181,13 @@ where C: Connect, | ||||
|  | ||||
|         let client = self.clone(); | ||||
|         //TODO: let is_proxy = req.is_proxy(); | ||||
|         //let uri = req.uri().clone(); | ||||
|         let uri = req.uri().clone(); | ||||
|         let fut = RetryableSendRequest { | ||||
|             client: client, | ||||
|             future: self.send_request(req, &domain), | ||||
|             domain: domain, | ||||
|             //is_proxy: is_proxy, | ||||
|             //uri: uri, | ||||
|             uri: uri, | ||||
|         }; | ||||
|         FutureResponse(Box::new(fut)) | ||||
|     } | ||||
| @@ -293,8 +293,7 @@ where C: Connect, | ||||
|  | ||||
| impl<C, B> Service for Client<C, B> | ||||
| where C: Connect, | ||||
|       B: Stream<Error=::Error> + 'static, | ||||
|       B::Item: AsRef<[u8]>, | ||||
|       B: Entity<Error=::Error> + 'static, | ||||
| { | ||||
|     type Request = Request<B>; | ||||
|     type Response = Response<Body>; | ||||
| @@ -350,14 +349,13 @@ struct RetryableSendRequest<C, B> { | ||||
|     domain: String, | ||||
|     future: Box<Future<Item=Response<Body>, Error=ClientError<B>>>, | ||||
|     //is_proxy: bool, | ||||
|     //uri: Uri, | ||||
|     uri: Uri, | ||||
| } | ||||
|  | ||||
| impl<C, B> Future for RetryableSendRequest<C, B> | ||||
| where | ||||
|     C: Connect, | ||||
|     B: Stream<Error=::Error> + 'static, | ||||
|     B::Item: AsRef<[u8]>, | ||||
|     B: Entity<Error=::Error> + 'static, | ||||
| { | ||||
|     type Item = Response<Body>; | ||||
|     type Error = ::Error; | ||||
| @@ -370,7 +368,7 @@ where | ||||
|                 Err(ClientError::Normal(err)) => return Err(err), | ||||
|                 Err(ClientError::Canceled { | ||||
|                     connection_reused, | ||||
|                     req, | ||||
|                     mut req, | ||||
|                     reason, | ||||
|                 }) => { | ||||
|                     if !self.client.retry_canceled_requests || !connection_reused { | ||||
| @@ -380,6 +378,7 @@ where | ||||
|                     } | ||||
|  | ||||
|                     trace!("unstarted request canceled, trying again (reason={:?})", reason); | ||||
|                     *req.uri_mut() = self.uri.clone(); | ||||
|                     self.future = self.client.send_request(req, &self.domain); | ||||
|                 } | ||||
|             } | ||||
| @@ -547,8 +546,7 @@ impl<C, B> Config<C, B> { | ||||
|  | ||||
| impl<C, B> Config<C, B> | ||||
| where C: Connect, | ||||
|       B: Stream<Error=::Error>, | ||||
|       B::Item: AsRef<[u8]>, | ||||
|       B: Entity<Error=::Error>, | ||||
| { | ||||
|     /// Construct the Client with this configuration. | ||||
|     #[inline] | ||||
| @@ -569,8 +567,7 @@ where C: Connect, | ||||
| } | ||||
|  | ||||
| impl<B> Config<UseDefaultConnector, B> | ||||
| where B: Stream<Error=::Error>, | ||||
|       B::Item: AsRef<[u8]>, | ||||
| where B: Entity<Error=::Error>, | ||||
| { | ||||
|     /// Construct the Client with this configuration. | ||||
|     #[inline] | ||||
|   | ||||
		Reference in New Issue
	
	Block a user