refine async API

- Converted `Response::text` and `Response::json` to `async fn`
- Added `Response::bytes` async fn as a counterpat to `text`.
- Added `Response::chunk` async fn to stream chunks of the response body.
- Added `From<Response> for Body` to allow piping a response as a request body.
- Removed `Decoder` from public API
- Removed body accessor methods from `Response`
- Removed `Chunk` type, replaced with `bytes::Bytes`.
- Removed public `impl Stream for Body`.
This commit is contained in:
Sean McArthur
2019-09-10 11:39:50 -07:00
parent 87a09322d6
commit 5356776834
11 changed files with 337 additions and 370 deletions

View File

@@ -1,4 +1,4 @@
use bytes::{Buf, Bytes};
use bytes::Bytes;
use futures::Stream;
use hyper::body::Payload;
use std::fmt;
@@ -7,11 +7,14 @@ use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::timer::Delay;
/// An asynchronous `Stream`.
/// An asynchronous request body.
pub struct Body {
inner: Inner,
}
// The `Stream` trait isn't stable, so the impl isn't public.
pub(crate) struct ImplStream(Body);
enum Inner {
Reusable(Bytes),
Hyper {
@@ -21,13 +24,6 @@ enum Inner {
}
impl Body {
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.size_hint().exact(),
}
}
/// Wrap a futures `Stream` in a box inside `Body`.
///
/// # Example
@@ -56,14 +52,12 @@ impl Body {
Body::wrap(hyper::body::Body::wrap_stream(stream))
}
#[inline]
pub(crate) fn response(body: hyper::Body, timeout: Option<Delay>) -> Body {
Body {
inner: Inner::Hyper { body, timeout },
}
}
#[inline]
pub(crate) fn wrap(body: hyper::Body) -> Body {
Body {
inner: Inner::Hyper {
@@ -73,19 +67,16 @@ impl Body {
}
}
#[inline]
pub(crate) fn empty() -> Body {
Body::wrap(hyper::Body::empty())
}
#[inline]
pub(crate) fn reusable(chunk: Bytes) -> Body {
Body {
inner: Inner::Reusable(chunk),
}
}
#[inline]
pub(crate) fn into_hyper(self) -> (Option<Bytes>, hyper::Body) {
match self.inner {
Inner::Reusable(chunk) => (Some(chunk.clone()), chunk.into()),
@@ -96,44 +87,15 @@ impl Body {
}
}
fn inner(self: Pin<&mut Self>) -> Pin<&mut Inner> {
unsafe { Pin::map_unchecked_mut(self, |x| &mut x.inner) }
pub(crate) fn into_stream(self) -> ImplStream {
ImplStream(self)
}
}
impl Stream for Body {
type Item = Result<Chunk, crate::Error>;
#[inline]
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
let opt_try_chunk = match self.inner().get_mut() {
Inner::Hyper {
ref mut body,
ref mut timeout,
} => {
if let Some(ref mut timeout) = timeout {
if let Poll::Ready(()) = Pin::new(timeout).poll(cx) {
return Poll::Ready(Some(Err(crate::error::timedout(None))));
}
}
futures::ready!(Pin::new(body).poll_data(cx)).map(|opt_chunk| {
opt_chunk
.map(|c| Chunk { inner: c })
.map_err(crate::error::from)
})
}
Inner::Reusable(ref mut bytes) => {
if bytes.is_empty() {
None
} else {
let chunk = Chunk::from_chunk(bytes.clone());
*bytes = Bytes::new();
Some(Ok(chunk))
}
}
};
Poll::Ready(opt_try_chunk)
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.size_hint().exact(),
}
}
}
@@ -172,126 +134,41 @@ impl From<&'static str> for Body {
}
}
/// A chunk of bytes for a `Body`.
///
/// A `Chunk` can be treated like `&[u8]`.
#[derive(Default)]
pub struct Chunk {
inner: hyper::Chunk,
}
impl Chunk {
#[inline]
pub(crate) fn from_chunk(chunk: Bytes) -> Chunk {
Chunk {
inner: hyper::Chunk::from(chunk),
}
}
}
impl Buf for Chunk {
fn bytes(&self) -> &[u8] {
self.inner.bytes()
}
fn remaining(&self) -> usize {
self.inner.remaining()
}
fn advance(&mut self, n: usize) {
self.inner.advance(n);
}
}
impl AsRef<[u8]> for Chunk {
#[inline]
fn as_ref(&self) -> &[u8] {
&*self
}
}
impl std::ops::Deref for Chunk {
type Target = [u8];
#[inline]
fn deref(&self) -> &Self::Target {
self.inner.as_ref()
}
}
impl Extend<u8> for Chunk {
fn extend<T>(&mut self, iter: T)
where
T: IntoIterator<Item = u8>,
{
self.inner.extend(iter)
}
}
impl IntoIterator for Chunk {
type Item = u8;
//XXX: exposing type from hyper!
type IntoIter = <hyper::Chunk as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl From<Vec<u8>> for Chunk {
fn from(v: Vec<u8>) -> Chunk {
Chunk { inner: v.into() }
}
}
impl From<&'static [u8]> for Chunk {
fn from(slice: &'static [u8]) -> Chunk {
Chunk {
inner: slice.into(),
}
}
}
impl From<String> for Chunk {
fn from(s: String) -> Chunk {
Chunk { inner: s.into() }
}
}
impl From<&'static str> for Chunk {
fn from(slice: &'static str) -> Chunk {
Chunk {
inner: slice.into(),
}
}
}
impl From<Bytes> for Chunk {
fn from(bytes: Bytes) -> Chunk {
Chunk {
inner: bytes.into(),
}
}
}
impl From<Chunk> for Bytes {
fn from(chunk: Chunk) -> Bytes {
chunk.inner.into()
}
}
impl From<Chunk> for hyper::Chunk {
fn from(val: Chunk) -> hyper::Chunk {
val.inner
}
}
impl fmt::Debug for Body {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Body").finish()
}
}
impl fmt::Debug for Chunk {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.inner, f)
// ===== impl ImplStream =====
impl Stream for ImplStream {
type Item = Result<Bytes, crate::Error>;
#[inline]
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
let opt_try_chunk = match self.0.inner {
Inner::Hyper {
ref mut body,
ref mut timeout,
} => {
if let Some(ref mut timeout) = timeout {
if let Poll::Ready(()) = Pin::new(timeout).poll(cx) {
return Poll::Ready(Some(Err(crate::error::timedout(None))));
}
}
futures::ready!(Pin::new(body).poll_data(cx))
.map(|opt_chunk| opt_chunk.map(Into::into).map_err(crate::error::from))
}
Inner::Reusable(ref mut bytes) => {
if bytes.is_empty() {
None
} else {
Some(Ok(std::mem::replace(bytes, Bytes::new())))
}
}
};
Poll::Ready(opt_try_chunk)
}
}