use std::{ borrow::Cow, fmt, mem, pin::Pin, task::{Context, Poll}, }; use bytes::{Bytes, BytesMut}; use futures_core::Stream; use pin_project_lite::pin_project; use actix_http::body::{BodySize, BodyStream, BoxBody, MessageBody, SizedStream}; use crate::BoxError; pin_project! { /// Represents various types of HTTP message body. #[derive(Clone)] #[project = AnyBodyProj] pub enum AnyBody { /// Empty response. `Content-Length` header is not set. None, /// Complete, in-memory response body. Bytes { body: Bytes }, /// Generic / Other message body. Body { #[pin] body: B }, } } impl AnyBody { /// Constructs a "body" representing an empty response. pub fn none() -> Self { Self::None } /// Constructs a new, 0-length body. pub fn empty() -> Self { Self::Bytes { body: Bytes::new() } } /// Create boxed body from generic message body. pub fn new_boxed(body: B) -> Self where B: MessageBody + 'static, { Self::Body { body: body.boxed() } } /// Constructs new `AnyBody` instance from a slice of bytes by copying it. /// /// If your bytes container is owned, it may be cheaper to use a `From` impl. pub fn copy_from_slice(s: &[u8]) -> Self { Self::Bytes { body: Bytes::copy_from_slice(s), } } #[doc(hidden)] #[deprecated(since = "4.0.0", note = "Renamed to `copy_from_slice`.")] pub fn from_slice(s: &[u8]) -> Self { Self::Bytes { body: Bytes::copy_from_slice(s), } } } impl AnyBody { /// Create body from generic message body. pub fn new(body: B) -> Self { Self::Body { body } } } impl AnyBody where B: MessageBody + 'static, { /// Converts a [`MessageBody`] type into the best possible representation. /// /// Checks size for `None` and tries to convert to `Bytes`. Otherwise, uses the `Body` variant. pub fn from_message_body(body: B) -> Self where B: MessageBody, { if matches!(body.size(), BodySize::None) { return Self::None; } match body.try_into_bytes() { Ok(body) => Self::Bytes { body }, Err(body) => Self::new(body), } } pub fn into_boxed(self) -> AnyBody { match self { Self::None => AnyBody::None, Self::Bytes { body } => AnyBody::Bytes { body }, Self::Body { body } => AnyBody::new_boxed(body), } } } impl MessageBody for AnyBody where B: MessageBody, { type Error = crate::BoxError; fn size(&self) -> BodySize { match self { AnyBody::None => BodySize::None, AnyBody::Bytes { ref body } => BodySize::Sized(body.len() as u64), AnyBody::Body { ref body } => body.size(), } } fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { match self.project() { AnyBodyProj::None => Poll::Ready(None), AnyBodyProj::Bytes { body } => { let len = body.len(); if len == 0 { Poll::Ready(None) } else { Poll::Ready(Some(Ok(mem::take(body)))) } } AnyBodyProj::Body { body } => body.poll_next(cx).map_err(|err| err.into()), } } } impl PartialEq for AnyBody { fn eq(&self, other: &AnyBody) -> bool { match self { AnyBody::None => matches!(*other, AnyBody::None), AnyBody::Bytes { body } => match other { AnyBody::Bytes { body: b2 } => body == b2, _ => false, }, AnyBody::Body { .. } => false, } } } impl fmt::Debug for AnyBody { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { AnyBody::None => write!(f, "AnyBody::None"), AnyBody::Bytes { ref body } => write!(f, "AnyBody::Bytes({:?})", body), AnyBody::Body { ref body } => write!(f, "AnyBody::Message({:?})", body), } } } impl From<&'static str> for AnyBody { fn from(string: &'static str) -> Self { Self::Bytes { body: Bytes::from_static(string.as_ref()), } } } impl From<&'static [u8]> for AnyBody { fn from(bytes: &'static [u8]) -> Self { Self::Bytes { body: Bytes::from_static(bytes), } } } impl From> for AnyBody { fn from(vec: Vec) -> Self { Self::Bytes { body: Bytes::from(vec), } } } impl From for AnyBody { fn from(string: String) -> Self { Self::Bytes { body: Bytes::from(string), } } } impl From<&'_ String> for AnyBody { fn from(string: &String) -> Self { Self::Bytes { body: Bytes::copy_from_slice(AsRef::<[u8]>::as_ref(&string)), } } } impl From> for AnyBody { fn from(string: Cow<'_, str>) -> Self { match string { Cow::Owned(s) => Self::from(s), Cow::Borrowed(s) => Self::Bytes { body: Bytes::copy_from_slice(AsRef::<[u8]>::as_ref(s)), }, } } } impl From for AnyBody { fn from(bytes: Bytes) -> Self { Self::Bytes { body: bytes } } } impl From for AnyBody { fn from(bytes: BytesMut) -> Self { Self::Bytes { body: bytes.freeze(), } } } impl From> for AnyBody where S: Stream> + 'static, E: Into + 'static, { fn from(stream: SizedStream) -> Self { AnyBody::new_boxed(stream) } } impl From> for AnyBody where S: Stream> + 'static, E: Into + 'static, { fn from(stream: BodyStream) -> Self { AnyBody::new_boxed(stream) } } #[cfg(test)] mod tests { use std::marker::PhantomPinned; use static_assertions::{assert_impl_all, assert_not_impl_all}; use super::*; struct PinType(PhantomPinned); impl MessageBody for PinType { type Error = crate::BoxError; fn size(&self) -> BodySize { unimplemented!() } fn poll_next( self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll>> { unimplemented!() } } assert_impl_all!(AnyBody<()>: MessageBody, fmt::Debug, Send, Sync, Unpin); assert_impl_all!(AnyBody>: MessageBody, fmt::Debug, Send, Sync, Unpin); assert_impl_all!(AnyBody: MessageBody, fmt::Debug, Send, Sync, Unpin); assert_impl_all!(AnyBody: MessageBody, fmt::Debug, Unpin); assert_impl_all!(AnyBody: MessageBody); assert_not_impl_all!(AnyBody: Send, Sync, Unpin); assert_not_impl_all!(AnyBody: Send, Sync, Unpin); }