-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
89b971f
commit 1332c81
Showing
15 changed files
with
676 additions
and
143 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
use crate::http::{Request, Result}; | ||
use async_trait::async_trait; | ||
|
||
use std::future::Future; | ||
|
||
#[async_trait] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
use futures::prelude::*; | ||
use http_body_util::BodyExt; | ||
use hyper::body::Bytes; | ||
use hyper::body::{Body as HttpBody, Frame, Incoming, SizeHint}; | ||
use pin_project_lite::pin_project; | ||
use std::pin::Pin; | ||
use std::task::{Context, Poll}; | ||
use std::{error::Error as StdError, fmt}; | ||
use sync_wrapper::SyncWrapper; | ||
|
||
type BoxBody = http_body_util::combinators::UnsyncBoxBody<Bytes, Error>; | ||
type BoxError = Box<dyn std::error::Error + Send + Sync>; | ||
|
||
/// Errors that can happen when using axum. | ||
#[derive(Debug)] | ||
pub struct Error { | ||
inner: BoxError, | ||
} | ||
|
||
impl Error { | ||
/// Create a new `Error` from a boxable error. | ||
pub fn new(error: impl Into<BoxError>) -> Self { | ||
Self { | ||
inner: error.into(), | ||
} | ||
} | ||
|
||
/// Convert an `Error` back into the underlying boxed trait object. | ||
pub fn into_inner(self) -> BoxError { | ||
self.inner | ||
} | ||
} | ||
|
||
impl fmt::Display for Error { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
self.inner.fmt(f) | ||
} | ||
} | ||
|
||
impl StdError for Error { | ||
fn source(&self) -> Option<&(dyn StdError + 'static)> { | ||
Some(&*self.inner) | ||
} | ||
} | ||
|
||
fn boxed<B>(body: B) -> BoxBody | ||
where | ||
B: HttpBody<Data = Bytes> + Send + 'static, | ||
B::Error: Into<BoxError>, | ||
{ | ||
try_downcast(body) | ||
.unwrap_or_else(|body| body.map_err(Error::new).boxed_unsync()) | ||
} | ||
|
||
pub(crate) fn try_downcast<T, K>(k: K) -> Result<T, K> | ||
where | ||
T: 'static, | ||
K: Send + 'static, | ||
{ | ||
let mut k = Some(k); | ||
if let Some(k) = <dyn std::any::Any>::downcast_mut::<Option<T>>(&mut k) { | ||
Ok(k.take().unwrap()) | ||
} else { | ||
Err(k.unwrap()) | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct Body(BoxBody); | ||
|
||
impl Body { | ||
/// Create a new `Body` that wraps another [`HttpBody`]. | ||
pub fn new<B>(body: B) -> Self | ||
where | ||
B: HttpBody<Data = Bytes> + Send + 'static, | ||
B::Error: Into<BoxError>, | ||
{ | ||
try_downcast(body).unwrap_or_else(|body| Self(boxed(body))) | ||
} | ||
|
||
/// Create an empty body. | ||
pub fn empty() -> Self { | ||
Self::new(http_body_util::Empty::new()) | ||
} | ||
|
||
pub fn incoming(incoming: Incoming) -> Self { | ||
Self::new(incoming) | ||
} | ||
|
||
/// Create a new `Body` from a [`Stream`]. | ||
/// | ||
/// [`Stream`]: futures::stream::Stream | ||
pub fn from_stream<S>(stream: S) -> Self | ||
where | ||
S: TryStream + Send + 'static, | ||
S::Ok: Into<Bytes>, | ||
S::Error: Into<BoxError>, | ||
{ | ||
Self::new(StreamBody { | ||
stream: SyncWrapper::new(stream), | ||
}) | ||
} | ||
} | ||
|
||
impl Default for Body { | ||
fn default() -> Self { | ||
Self::empty() | ||
} | ||
} | ||
|
||
macro_rules! body_from_impl { | ||
($ty:ty) => { | ||
impl From<$ty> for Body { | ||
fn from(buf: $ty) -> Self { | ||
Self::new(http_body_util::Full::from(buf)) | ||
} | ||
} | ||
}; | ||
} | ||
|
||
body_from_impl!(&'static [u8]); | ||
body_from_impl!(std::borrow::Cow<'static, [u8]>); | ||
body_from_impl!(Vec<u8>); | ||
|
||
body_from_impl!(&'static str); | ||
body_from_impl!(std::borrow::Cow<'static, str>); | ||
body_from_impl!(String); | ||
|
||
body_from_impl!(Bytes); | ||
|
||
impl HttpBody for Body { | ||
type Data = Bytes; | ||
type Error = Error; | ||
|
||
#[inline] | ||
fn poll_frame( | ||
mut self: Pin<&mut Self>, | ||
cx: &mut Context<'_>, | ||
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> { | ||
Pin::new(&mut self.0).poll_frame(cx) | ||
} | ||
|
||
#[inline] | ||
fn size_hint(&self) -> SizeHint { | ||
self.0.size_hint() | ||
} | ||
|
||
#[inline] | ||
fn is_end_stream(&self) -> bool { | ||
self.0.is_end_stream() | ||
} | ||
} | ||
|
||
impl Stream for Body { | ||
type Item = Result<Bytes, Error>; | ||
|
||
#[inline] | ||
fn poll_next( | ||
mut self: Pin<&mut Self>, | ||
cx: &mut Context<'_>, | ||
) -> Poll<Option<Self::Item>> { | ||
loop { | ||
match futures::ready!(self.as_mut().poll_frame(cx)?) { | ||
Some(frame) => match frame.into_data() { | ||
Ok(data) => return Poll::Ready(Some(Ok(data))), | ||
Err(_frame) => {} | ||
}, | ||
None => return Poll::Ready(None), | ||
} | ||
} | ||
} | ||
} | ||
|
||
pin_project! { | ||
struct StreamBody<S> { | ||
#[pin] | ||
stream: SyncWrapper<S>, | ||
} | ||
} | ||
|
||
impl<S> HttpBody for StreamBody<S> | ||
where | ||
S: TryStream, | ||
S::Ok: Into<Bytes>, | ||
S::Error: Into<BoxError>, | ||
{ | ||
type Data = Bytes; | ||
type Error = Error; | ||
|
||
fn poll_frame( | ||
self: Pin<&mut Self>, | ||
cx: &mut Context<'_>, | ||
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> { | ||
let stream = self.project().stream.get_pin_mut(); | ||
match futures::ready!(stream.try_poll_next(cx)) { | ||
Some(Ok(chunk)) => Poll::Ready(Some(Ok(Frame::data(chunk.into())))), | ||
Some(Err(err)) => Poll::Ready(Some(Err(Error::new(err)))), | ||
None => Poll::Ready(None), | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_try_downcast() { | ||
assert_eq!(try_downcast::<i32, _>(5_u32), Err(5_u32)); | ||
assert_eq!(try_downcast::<i32, _>(5_i32), Ok(5_i32)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.