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:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user