pub use http_body_alt::{Body, BodyExt, Frame, SizeHint, util::*};
use core::{
pin::Pin,
task::{Context, Poll},
};
use std::{borrow::Cow, error};
use pin_project_lite::pin_project;
use super::{
bytes::{Buf, Bytes, BytesMut},
error::BodyError,
};
#[derive(Default)]
pub enum RequestBody {
#[cfg(feature = "http2")]
H2(super::h2::RequestBody),
#[cfg(feature = "http3")]
H3(super::h3::RequestBody),
Boxed(BoxBody),
#[default]
None,
}
impl RequestBody {
pub fn into_boxed(self) -> BoxBody {
match self {
#[cfg(feature = "http2")]
Self::H2(body) => BoxBody::new(body),
#[cfg(feature = "http3")]
Self::H3(body) => BoxBody::new(body),
Self::Boxed(body) => body,
Self::None => BoxBody::new(Empty::<Bytes>::new()),
}
}
}
impl Body for RequestBody {
type Data = Bytes;
type Error = BodyError;
#[inline]
fn poll_frame(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
match self.get_mut() {
#[cfg(feature = "http2")]
Self::H2(body) => Pin::new(body).poll_frame(cx),
#[cfg(feature = "http3")]
Self::H3(body) => Pin::new(body).poll_frame(cx),
Self::Boxed(body) => Pin::new(body).poll_frame(cx),
Self::None => Poll::Ready(None),
}
}
#[inline]
fn is_end_stream(&self) -> bool {
match self {
#[cfg(feature = "http2")]
Self::H2(body) => body.is_end_stream(),
#[cfg(feature = "http3")]
Self::H3(body) => body.is_end_stream(),
Self::Boxed(body) => body.is_end_stream(),
Self::None => true,
}
}
#[inline]
fn size_hint(&self) -> SizeHint {
match self {
#[cfg(feature = "http2")]
Self::H2(body) => body.size_hint(),
#[cfg(feature = "http3")]
Self::H3(body) => body.size_hint(),
Self::Boxed(body) => body.size_hint(),
Self::None => SizeHint::None,
}
}
}
impl From<Bytes> for RequestBody {
fn from(bytes: Bytes) -> Self {
Self::from(BoxBody::new(Full::new(bytes)))
}
}
impl From<BoxBody> for RequestBody {
fn from(body: BoxBody) -> Self {
Self::Boxed(body)
}
}
macro_rules! req_bytes_impl {
($ty: ty) => {
impl From<$ty> for RequestBody {
fn from(item: $ty) -> Self {
Self::from(Bytes::from(item))
}
}
};
}
req_bytes_impl!(&'static [u8]);
req_bytes_impl!(Box<[u8]>);
req_bytes_impl!(Vec<u8>);
req_bytes_impl!(String);
pub struct BoxBody(Pin<Box<dyn Body<Data = Bytes, Error = BodyError>>>);
impl Default for BoxBody {
fn default() -> Self {
Self::new(Empty::<Bytes>::new())
}
}
impl BoxBody {
#[inline]
pub fn new<B>(body: B) -> Self
where
B: Body + 'static,
B::Data: Into<Bytes> + 'static,
B::Error: Into<BodyError> + 'static,
{
pin_project! {
struct MapBody<B> {
#[pin]
body: B
}
}
impl<B, T, E> Body for MapBody<B>
where
B: Body<Data = T, Error = E>,
T: Into<Bytes>,
E: Into<BodyError>,
{
type Data = Bytes;
type Error = BodyError;
#[inline]
fn poll_frame(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Result<Frame<Bytes>, BodyError>>> {
self.project()
.body
.poll_frame(cx)
.map_ok(|frame| match frame {
Frame::Data(data) => Frame::Data(data.into()),
Frame::Trailers(trailers) => Frame::Trailers(trailers),
})
.map_err(Into::into)
}
#[inline]
fn is_end_stream(&self) -> bool {
self.body.is_end_stream()
}
#[inline]
fn size_hint(&self) -> SizeHint {
self.body.size_hint()
}
}
Self(Box::pin(MapBody { body }))
}
}
impl Body for BoxBody {
type Data = Bytes;
type Error = BodyError;
#[inline]
fn poll_frame(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Result<Frame<Bytes>, BodyError>>> {
self.get_mut().0.as_mut().poll_frame(cx)
}
#[inline]
fn is_end_stream(&self) -> bool {
self.0.is_end_stream()
}
#[inline]
fn size_hint(&self) -> SizeHint {
self.0.size_hint()
}
}
pin_project! {
pub struct ResponseBody<B = BoxBody> {
#[pin]
inner: ResponseBodyInner<B>
}
}
pin_project! {
#[project = ResponseBodyProj]
#[project_replace = ResponseBodyProjReplace]
enum ResponseBodyInner<B> {
None,
Bytes {
bytes: Bytes,
},
Body {
#[pin]
stream: B,
},
}
}
impl<B> Default for ResponseBody<B> {
fn default() -> Self {
Self::empty()
}
}
impl ResponseBody {
#[inline]
pub fn boxed<B, T, E>(body: B) -> Self
where
B: Body<Data = T, Error = E> + 'static,
T: Into<Bytes> + 'static,
E: Into<BodyError> + 'static,
{
Self::body(BoxBody::new(body))
}
}
impl<B> ResponseBody<B> {
#[inline]
pub const fn empty() -> Self {
Self {
inner: ResponseBodyInner::None,
}
}
#[inline]
pub const fn body(stream: B) -> Self {
Self {
inner: ResponseBodyInner::Body { stream },
}
}
#[inline]
pub fn bytes<B2>(bytes: B2) -> Self
where
Bytes: From<B2>,
{
Self {
inner: ResponseBodyInner::Bytes {
bytes: Bytes::from(bytes),
},
}
}
#[inline]
pub fn into_boxed<T, E>(self) -> ResponseBody
where
B: Body<Data = T, Error = E> + 'static,
T: Into<Bytes> + Buf + 'static,
E: error::Error + Send + Sync + 'static,
{
match self.inner {
ResponseBodyInner::None => ResponseBody::empty(),
ResponseBodyInner::Bytes { bytes } => ResponseBody::bytes(bytes),
ResponseBodyInner::Body { stream } => ResponseBody::boxed(stream),
}
}
}
impl<B, E> Body for ResponseBody<B>
where
B: Body<Data = Bytes, Error = E>,
{
type Data = Bytes;
type Error = E;
fn poll_frame(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Result<Frame<Bytes>, E>>> {
let mut inner = self.project().inner;
match inner.as_mut().project() {
ResponseBodyProj::None => Poll::Ready(None),
ResponseBodyProj::Bytes { .. } => match inner.project_replace(ResponseBodyInner::None) {
ResponseBodyProjReplace::Bytes { bytes } => Poll::Ready(Some(Ok(Frame::Data(bytes)))),
_ => unreachable!(),
},
ResponseBodyProj::Body { stream } => stream.poll_frame(cx),
}
}
fn is_end_stream(&self) -> bool {
match self.inner {
ResponseBodyInner::None => true,
ResponseBodyInner::Bytes { .. } => false,
ResponseBodyInner::Body { ref stream } => stream.is_end_stream(),
}
}
fn size_hint(&self) -> SizeHint {
match self.inner {
ResponseBodyInner::None => SizeHint::None,
ResponseBodyInner::Bytes { ref bytes } => SizeHint::Exact(bytes.len() as u64),
ResponseBodyInner::Body { ref stream } => stream.size_hint(),
}
}
}
impl From<BoxBody> for ResponseBody {
fn from(body: BoxBody) -> Self {
Self::boxed(body)
}
}
macro_rules! res_bytes_impl {
($ty: ty) => {
impl<B> From<$ty> for ResponseBody<B> {
fn from(item: $ty) -> Self {
Self::bytes(item)
}
}
};
}
res_bytes_impl!(Bytes);
res_bytes_impl!(BytesMut);
res_bytes_impl!(&'static [u8]);
res_bytes_impl!(&'static str);
res_bytes_impl!(Box<[u8]>);
res_bytes_impl!(Vec<u8>);
res_bytes_impl!(String);
impl<B> From<Box<str>> for ResponseBody<B> {
fn from(str: Box<str>) -> Self {
Self::from(Box::<[u8]>::from(str))
}
}
impl<B> From<Cow<'static, str>> for ResponseBody<B> {
fn from(str: Cow<'static, str>) -> Self {
match str {
Cow::Owned(str) => Self::from(str),
Cow::Borrowed(str) => Self::from(str),
}
}
}