use std::{ borrow::Cow, error::Error as StdError, fmt, mem, pin::Pin, task::{Context, Poll}, }; use bytes::{Bytes, BytesMut}; use futures_core::Stream; use pin_project::pin_project; use crate::error::Error; use super::{BodySize, BodyStream, MessageBody, MessageBodyMapErr, SizedStream}; #[deprecated(since = "4.0.0", note = "Renamed to `AnyBody`.")] pub type Body = AnyBody; /// Represents various types of HTTP message body. #[pin_project(project = AnyBodyProj)] #[derive(Clone)] pub enum AnyBody { /// Empty response. `Content-Length` header is not set. None, /// Complete, in-memory response body. Bytes(Bytes), /// Generic / Other message body. Body(#[pin] B), } impl AnyBody { /// Constructs a new, empty body. pub fn empty() -> Self { Self::Bytes(Bytes::new()) } /// Create boxed body from generic message body. pub fn new_boxed(body: B) -> Self where B: MessageBody + 'static, B::Error: Into>, { Self::Body(BoxBody::from_body(body)) } /// 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(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(Bytes::copy_from_slice(s)) } } impl AnyBody where B: MessageBody + 'static, B::Error: Into>, { /// Create body from generic message body. pub fn new(body: B) -> Self { Self::Body(body) } pub fn into_boxed(self) -> AnyBody { match self { Self::None => AnyBody::None, Self::Bytes(bytes) => AnyBody::Bytes(bytes), Self::Body(body) => AnyBody::new_boxed(body), } } } impl MessageBody for AnyBody where B: MessageBody, B::Error: Into> + 'static, { type Error = Error; fn size(&self) -> BodySize { match self { AnyBody::None => BodySize::None, AnyBody::Bytes(ref bin) => BodySize::Sized(bin.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(bin) => { let len = bin.len(); if len == 0 { Poll::Ready(None) } else { Poll::Ready(Some(Ok(mem::take(bin)))) } } AnyBodyProj::Body(body) => body .poll_next(cx) .map_err(|err| Error::new_body().with_cause(err)), } } } impl PartialEq for AnyBody { fn eq(&self, other: &AnyBody) -> bool { match *self { AnyBody::None => matches!(*other, AnyBody::None), AnyBody::Bytes(ref b) => match *other { AnyBody::Bytes(ref b2) => b == 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 bytes) => write!(f, "AnyBody::Bytes({:?})", bytes), AnyBody::Body(ref stream) => write!(f, "AnyBody::Message({:?})", stream), } } } impl From<&'static str> for AnyBody { fn from(string: &'static str) -> AnyBody { AnyBody::Bytes(Bytes::from_static(string.as_ref())) } } impl From<&'static [u8]> for AnyBody { fn from(bytes: &'static [u8]) -> AnyBody { AnyBody::Bytes(Bytes::from_static(bytes)) } } impl From> for AnyBody { fn from(vec: Vec) -> AnyBody { AnyBody::Bytes(Bytes::from(vec)) } } impl From for AnyBody { fn from(string: String) -> AnyBody { string.into_bytes().into() } } impl From<&'_ String> for AnyBody { fn from(string: &String) -> AnyBody { AnyBody::Bytes(Bytes::copy_from_slice(AsRef::<[u8]>::as_ref(&string))) } } impl From> for AnyBody { fn from(string: Cow<'_, str>) -> AnyBody { match string { Cow::Owned(s) => AnyBody::from(s), Cow::Borrowed(s) => { AnyBody::Bytes(Bytes::copy_from_slice(AsRef::<[u8]>::as_ref(s))) } } } } impl From for AnyBody { fn from(bytes: Bytes) -> Self { AnyBody::Bytes(bytes) } } impl From for AnyBody { fn from(bytes: BytesMut) -> Self { AnyBody::Bytes(bytes.freeze()) } } impl From> for AnyBody> where S: Stream> + 'static, E: Into> + 'static, { fn from(stream: SizedStream) -> Self { AnyBody::new(stream) } } 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(stream) } } impl From> for AnyBody where S: Stream> + 'static, E: Into> + 'static, { fn from(stream: BodyStream) -> Self { AnyBody::new_boxed(stream) } } /// A boxed message body with boxed errors. pub struct BoxBody(Pin>>>); impl BoxBody { /// Boxes a `MessageBody` and any errors it generates. pub fn from_body(body: B) -> Self where B: MessageBody + 'static, B::Error: Into>, { let body = MessageBodyMapErr::new(body, Into::into); Self(Box::pin(body)) } /// Returns a mutable pinned reference to the inner message body type. pub fn as_pin_mut( &mut self, ) -> Pin<&mut (dyn MessageBody>)> { self.0.as_mut() } } impl fmt::Debug for BoxBody { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("BoxAnyBody(dyn MessageBody)") } } impl MessageBody for BoxBody { type Error = Error; fn size(&self) -> BodySize { self.0.size() } fn poll_next( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { self.0 .as_mut() .poll_next(cx) .map_err(|err| Error::new_body().with_cause(err)) } } #[cfg(test)] mod tests { use std::marker::PhantomPinned; use static_assertions::{assert_impl_all, assert_not_impl_all}; use super::*; use crate::body::to_bytes; struct PinType(PhantomPinned); impl MessageBody for PinType { type Error = crate::Error; 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!(BoxBody: MessageBody, fmt::Debug, Unpin); assert_impl_all!(AnyBody: MessageBody); assert_not_impl_all!(AnyBody: Send, Sync, Unpin); assert_not_impl_all!(BoxBody: Send, Sync, Unpin); assert_not_impl_all!(AnyBody: Send, Sync, Unpin); #[actix_rt::test] async fn nested_boxed_body() { let body = AnyBody::copy_from_slice(&[1, 2, 3]); let boxed_body = BoxBody::from_body(BoxBody::from_body(body)); assert_eq!( to_bytes(boxed_body).await.unwrap(), Bytes::from(vec![1, 2, 3]), ); } }