Skip to content

Commit fbc449e

Browse files
committedMar 19, 2018
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
1 parent 3cd48b4 commit fbc449e

File tree

18 files changed

+807
-481
lines changed

18 files changed

+807
-481
lines changed
 

Diff for: ‎examples/client.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ fn main() {
4040
println!("Response: {}", res.status());
4141
println!("Headers: {:#?}", res.headers());
4242

43-
res.into_parts().1.for_each(|chunk| {
43+
res.into_parts().1.into_stream().for_each(|chunk| {
4444
io::stdout().write_all(&chunk).map_err(From::from)
4545
})
4646
}).map(|_| {

Diff for: ‎examples/params.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl Service for ParamExample {
3030
Box::new(futures::future::ok(Response::new(INDEX.into())))
3131
},
3232
(&Method::POST, "/post") => {
33-
Box::new(req.into_parts().1.concat2().map(|b| {
33+
Box::new(req.into_parts().1.into_stream().concat2().map(|b| {
3434
// Parse the request body. form_urlencoded::parse
3535
// always succeeds, but in general parsing may
3636
// fail (for example, an invalid post of json), so

Diff for: ‎examples/send_file.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ extern crate futures;
33
extern crate hyper;
44
extern crate pretty_env_logger;
55

6-
use futures::{Future, Sink};
6+
use futures::{Future/*, Sink*/};
77
use futures::sync::oneshot;
88

9-
use hyper::{Body, Chunk, Method, Request, Response, StatusCode};
9+
use hyper::{Body, /*Chunk,*/ Method, Request, Response, StatusCode};
1010
use hyper::error::Error;
1111
use hyper::server::{Http, Service};
1212

1313
use std::fs::File;
14-
use std::io::{self, copy, Read};
14+
use std::io::{self, copy/*, Read*/};
1515
use std::thread;
1616

1717
static NOTFOUND: &[u8] = b"Not Found";
@@ -80,7 +80,7 @@ impl Service for ResponseExamples {
8080
// a small test file.
8181
let (tx, rx) = oneshot::channel();
8282
thread::spawn(move || {
83-
let mut file = match File::open(INDEX) {
83+
let _file = match File::open(INDEX) {
8484
Ok(f) => f,
8585
Err(_) => {
8686
tx.send(Response::builder()
@@ -91,9 +91,10 @@ impl Service for ResponseExamples {
9191
return;
9292
},
9393
};
94-
let (mut tx_body, rx_body) = Body::pair();
94+
let (_tx_body, rx_body) = Body::channel();
9595
let res = Response::new(rx_body.into());
9696
tx.send(res).expect("Send error on successful file read");
97+
/* TODO: fix once we have futures 0.2 Sink working
9798
let mut buf = [0u8; 16];
9899
loop {
99100
match file.read(&mut buf) {
@@ -104,7 +105,7 @@ impl Service for ResponseExamples {
104105
break;
105106
} else {
106107
let chunk: Chunk = buf[0..n].to_vec().into();
107-
match tx_body.send(Ok(chunk)).wait() {
108+
match tx_body.send_data(chunk).wait() {
108109
Ok(t) => { tx_body = t; },
109110
Err(_) => { break; }
110111
};
@@ -113,6 +114,7 @@ impl Service for ResponseExamples {
113114
Err(_) => { break; }
114115
}
115116
}
117+
*/
116118
});
117119

118120
Box::new(rx.map_err(|e| Error::from(io::Error::new(io::ErrorKind::Other, e))))

Diff for: ‎examples/server.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ impl Service for Echo {
2424
Response::new(INDEX.into())
2525
},
2626
(&Method::POST, "/echo") => {
27-
Response::new(req.into_parts().1)
27+
Response::new(req.into_body())
2828
},
2929
_ => {
3030
let mut res = Response::new(Body::empty());

Diff for: ‎examples/web_api.rs

+5-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ extern crate tokio_core;
77
use futures::{Future, Stream};
88

99
use hyper::{Body, Chunk, Client, Method, Request, Response, StatusCode};
10-
use hyper::error::Error;
1110
use hyper::server::{Http, Service};
1211

1312
#[allow(unused)]
@@ -18,20 +17,18 @@ static URL: &str = "http://127.0.0.1:1337/web_api";
1817
static INDEX: &[u8] = b"<a href=\"test.html\">test.html</a>";
1918
static LOWERCASE: &[u8] = b"i am a lower case string";
2019

21-
pub type ResponseStream = Box<Stream<Item=Chunk, Error=Error>>;
22-
2320
struct ResponseExamples(tokio_core::reactor::Handle);
2421

2522
impl Service for ResponseExamples {
2623
type Request = Request<Body>;
27-
type Response = Response<ResponseStream>;
24+
type Response = Response<Body>;
2825
type Error = hyper::Error;
2926
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
3027

3128
fn call(&self, req: Self::Request) -> Self::Future {
3229
match (req.method(), req.uri().path()) {
3330
(&Method::GET, "/") | (&Method::GET, "/index.html") => {
34-
let body: ResponseStream = Box::new(Body::from(INDEX));
31+
let body = Body::from(INDEX);
3532
Box::new(futures::future::ok(Response::new(body)))
3633
},
3734
(&Method::GET, "/test.html") => {
@@ -45,7 +42,7 @@ impl Service for ResponseExamples {
4542
let web_res_future = client.request(req);
4643

4744
Box::new(web_res_future.map(|web_res| {
48-
let body: ResponseStream = Box::new(web_res.into_parts().1.map(|b| {
45+
let body = Body::wrap_stream(web_res.into_body().into_stream().map(|b| {
4946
Chunk::from(format!("before: '{:?}'<br>after: '{:?}'",
5047
std::str::from_utf8(LOWERCASE).unwrap(),
5148
std::str::from_utf8(&b).unwrap()))
@@ -55,15 +52,15 @@ impl Service for ResponseExamples {
5552
},
5653
(&Method::POST, "/web_api") => {
5754
// A web api to run against. Simple upcasing of the body.
58-
let body: ResponseStream = Box::new(req.into_parts().1.map(|chunk| {
55+
let body = Body::wrap_stream(req.into_body().into_stream().map(|chunk| {
5956
let upper = chunk.iter().map(|byte| byte.to_ascii_uppercase())
6057
.collect::<Vec<u8>>();
6158
Chunk::from(upper)
6259
}));
6360
Box::new(futures::future::ok(Response::new(body)))
6461
},
6562
_ => {
66-
let body: ResponseStream = Box::new(Body::from(NOTFOUND));
63+
let body = Body::from(NOTFOUND);
6764
Box::new(futures::future::ok(Response::builder()
6865
.status(StatusCode::NOT_FOUND)
6966
.body(body)

Diff for: ‎src/client/conn.rs

+20-42
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ use std::fmt;
1111
use std::marker::PhantomData;
1212

1313
use bytes::Bytes;
14-
use futures::{Async, Future, Poll, Stream};
14+
use futures::{Async, Future, Poll};
1515
use futures::future::{self, Either};
1616
use tokio_io::{AsyncRead, AsyncWrite};
1717

1818
use proto;
19+
use proto::body::Entity;
1920
use super::dispatch;
2021
use {Body, Request, Response, StatusCode};
2122

@@ -44,14 +45,13 @@ pub struct SendRequest<B> {
4445
pub struct Connection<T, B>
4546
where
4647
T: AsyncRead + AsyncWrite,
47-
B: Stream<Error=::Error> + 'static,
48-
B::Item: AsRef<[u8]>,
48+
B: Entity<Error=::Error> + 'static,
4949
{
5050
inner: proto::dispatch::Dispatcher<
5151
proto::dispatch::Client<B>,
5252
B,
5353
T,
54-
B::Item,
54+
B::Data,
5555
proto::ClientUpgradeTransaction,
5656
>,
5757
}
@@ -134,8 +134,7 @@ impl<B> SendRequest<B>
134134

135135
impl<B> SendRequest<B>
136136
where
137-
B: Stream<Error=::Error> + 'static,
138-
B::Item: AsRef<[u8]>,
137+
B: Entity<Error=::Error> + 'static,
139138
{
140139
/// Sends a `Request` on the associated connection.
141140
///
@@ -152,7 +151,7 @@ where
152151
/// the `Host` header based on it. You must add a `Host` header yourself
153152
/// before calling this method.
154153
/// - Since absolute-form `Uri`s are not required, if received, they will
155-
/// be serialized as-is, irregardless of calling `Request::set_proxy`.
154+
/// be serialized as-is.
156155
///
157156
/// # Example
158157
///
@@ -185,19 +184,6 @@ where
185184
/// # fn main() {}
186185
/// ```
187186
pub fn send_request(&mut self, req: Request<B>) -> ResponseFuture {
188-
/* TODO?
189-
// The Connection API does less things automatically than the Client
190-
// API does. For instance, right here, we always assume set_proxy, so
191-
// that if an absolute-form URI is provided, it is serialized as-is.
192-
//
193-
// Part of the reason for this is to prepare for the change to `http`
194-
// types, where there is no more set_proxy.
195-
//
196-
// It's important that this method isn't called directly from the
197-
// `Client`, so that `set_proxy` there is still respected.
198-
req.set_proxy(true);
199-
*/
200-
201187
let inner = match self.dispatch.send(req) {
202188
Ok(rx) => {
203189
Either::A(rx.then(move |res| {
@@ -269,8 +255,7 @@ impl<B> fmt::Debug for SendRequest<B> {
269255
impl<T, B> Connection<T, B>
270256
where
271257
T: AsyncRead + AsyncWrite,
272-
B: Stream<Error=::Error> + 'static,
273-
B::Item: AsRef<[u8]>,
258+
B: Entity<Error=::Error> + 'static,
274259
{
275260
/// Return the inner IO object, and additional information.
276261
pub fn into_parts(self) -> Parts<T> {
@@ -297,8 +282,7 @@ where
297282
impl<T, B> Future for Connection<T, B>
298283
where
299284
T: AsyncRead + AsyncWrite,
300-
B: Stream<Error=::Error> + 'static,
301-
B::Item: AsRef<[u8]>,
285+
B: Entity<Error=::Error> + 'static,
302286
{
303287
type Item = ();
304288
type Error = ::Error;
@@ -311,8 +295,7 @@ where
311295
impl<T, B> fmt::Debug for Connection<T, B>
312296
where
313297
T: AsyncRead + AsyncWrite + fmt::Debug,
314-
B: Stream<Error=::Error> + 'static,
315-
B::Item: AsRef<[u8]>,
298+
B: Entity<Error=::Error> + 'static,
316299
{
317300
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
318301
f.debug_struct("Connection")
@@ -341,8 +324,7 @@ impl Builder {
341324
pub fn handshake<T, B>(&self, io: T) -> Handshake<T, B>
342325
where
343326
T: AsyncRead + AsyncWrite,
344-
B: Stream<Error=::Error> + 'static,
345-
B::Item: AsRef<[u8]>,
327+
B: Entity<Error=::Error> + 'static,
346328
{
347329
Handshake {
348330
inner: HandshakeInner {
@@ -356,8 +338,7 @@ impl Builder {
356338
pub(super) fn handshake_no_upgrades<T, B>(&self, io: T) -> HandshakeNoUpgrades<T, B>
357339
where
358340
T: AsyncRead + AsyncWrite,
359-
B: Stream<Error=::Error> + 'static,
360-
B::Item: AsRef<[u8]>,
341+
B: Entity<Error=::Error> + 'static,
361342
{
362343
HandshakeNoUpgrades {
363344
inner: HandshakeInner {
@@ -374,8 +355,7 @@ impl Builder {
374355
impl<T, B> Future for Handshake<T, B>
375356
where
376357
T: AsyncRead + AsyncWrite,
377-
B: Stream<Error=::Error> + 'static,
378-
B::Item: AsRef<[u8]>,
358+
B: Entity<Error=::Error> + 'static,
379359
{
380360
type Item = (SendRequest<B>, Connection<T, B>);
381361
type Error = ::Error;
@@ -400,14 +380,13 @@ impl<T, B> fmt::Debug for Handshake<T, B> {
400380
impl<T, B> Future for HandshakeNoUpgrades<T, B>
401381
where
402382
T: AsyncRead + AsyncWrite,
403-
B: Stream<Error=::Error> + 'static,
404-
B::Item: AsRef<[u8]>,
383+
B: Entity<Error=::Error> + 'static,
405384
{
406385
type Item = (SendRequest<B>, proto::dispatch::Dispatcher<
407386
proto::dispatch::Client<B>,
408387
B,
409388
T,
410-
B::Item,
389+
B::Data,
411390
proto::ClientTransaction,
412391
>);
413392
type Error = ::Error;
@@ -420,8 +399,7 @@ where
420399
impl<T, B, R> Future for HandshakeInner<T, B, R>
421400
where
422401
T: AsyncRead + AsyncWrite,
423-
B: Stream<Error=::Error> + 'static,
424-
B::Item: AsRef<[u8]>,
402+
B: Entity<Error=::Error> + 'static,
425403
R: proto::Http1Transaction<
426404
Incoming=StatusCode,
427405
Outgoing=proto::RequestLine,
@@ -431,7 +409,7 @@ where
431409
proto::dispatch::Client<B>,
432410
B,
433411
T,
434-
B::Item,
412+
B::Data,
435413
R,
436414
>);
437415
type Error = ::Error;
@@ -485,16 +463,16 @@ impl<B: Send> AssertSendSync for SendRequest<B> {}
485463
impl<T: Send, B: Send> AssertSend for Connection<T, B>
486464
where
487465
T: AsyncRead + AsyncWrite,
488-
B: Stream<Error=::Error>,
489-
B::Item: AsRef<[u8]> + Send,
466+
B: Entity<Error=::Error> + 'static,
467+
B::Data: Send + 'static,
490468
{}
491469

492470
#[doc(hidden)]
493471
impl<T: Send + Sync, B: Send + Sync> AssertSendSync for Connection<T, B>
494472
where
495473
T: AsyncRead + AsyncWrite,
496-
B: Stream<Error=::Error>,
497-
B::Item: AsRef<[u8]> + Send + Sync,
474+
B: Entity<Error=::Error> + 'static,
475+
B::Data: Send + Sync + 'static,
498476
{}
499477

500478
#[doc(hidden)]

0 commit comments

Comments
 (0)