mirror of https://github.com/fafhrd91/actix-web
226 lines
5.4 KiB
Rust
226 lines
5.4 KiB
Rust
use std::{
|
|
borrow::Cow,
|
|
error::Error as StdError,
|
|
fmt, mem,
|
|
pin::Pin,
|
|
task::{Context, Poll},
|
|
};
|
|
|
|
use bytes::{Bytes, BytesMut};
|
|
use futures_core::Stream;
|
|
|
|
use crate::error::Error;
|
|
|
|
use super::{BodySize, BodyStream, MessageBody, MessageBodyMapErr, SizedStream};
|
|
|
|
pub type Body = AnyBody;
|
|
|
|
/// Represents various types of HTTP message body.
|
|
pub enum AnyBody {
|
|
/// Empty response. `Content-Length` header is not set.
|
|
None,
|
|
|
|
/// Specific response body.
|
|
Bytes(Bytes),
|
|
|
|
/// Generic message body.
|
|
Stream(BoxAnyBody),
|
|
}
|
|
|
|
impl AnyBody {
|
|
/// Constructs a new, empty body.
|
|
pub fn empty() -> Self {
|
|
Self::Bytes(Bytes::new())
|
|
}
|
|
|
|
/// Create body from slice (copy)
|
|
pub fn from_slice(s: &[u8]) -> Self {
|
|
Self::Bytes(Bytes::copy_from_slice(s))
|
|
}
|
|
|
|
/// Create body from generic message body.
|
|
pub fn from_message<B>(body: B) -> Self
|
|
where
|
|
B: MessageBody + 'static,
|
|
B::Error: Into<Box<dyn StdError + 'static>>,
|
|
{
|
|
Self::Stream(BoxAnyBody::from_body(body))
|
|
}
|
|
}
|
|
|
|
impl MessageBody for AnyBody {
|
|
type Error = Error;
|
|
|
|
fn size(&self) -> BodySize {
|
|
match self {
|
|
AnyBody::None => BodySize::None,
|
|
AnyBody::Bytes(ref bin) => BodySize::Sized(bin.len() as u64),
|
|
AnyBody::Stream(ref body) => body.size(),
|
|
}
|
|
}
|
|
|
|
fn poll_next(
|
|
self: Pin<&mut Self>,
|
|
cx: &mut Context<'_>,
|
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
|
match self.get_mut() {
|
|
AnyBody::None => Poll::Ready(None),
|
|
AnyBody::Bytes(ref mut bin) => {
|
|
let len = bin.len();
|
|
if len == 0 {
|
|
Poll::Ready(None)
|
|
} else {
|
|
Poll::Ready(Some(Ok(mem::take(bin))))
|
|
}
|
|
}
|
|
|
|
AnyBody::Stream(body) => body
|
|
.as_pin_mut()
|
|
.poll_next(cx)
|
|
.map_err(|err| Error::new_body().with_cause(err)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialEq for AnyBody {
|
|
fn eq(&self, other: &Body) -> bool {
|
|
match *self {
|
|
AnyBody::None => matches!(*other, AnyBody::None),
|
|
AnyBody::Bytes(ref b) => match *other {
|
|
AnyBody::Bytes(ref b2) => b == b2,
|
|
_ => false,
|
|
},
|
|
AnyBody::Stream(_) => 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 b) => write!(f, "AnyBody::Bytes({:?})", b),
|
|
AnyBody::Stream(_) => write!(f, "AnyBody::Message(_)"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&'static str> for AnyBody {
|
|
fn from(s: &'static str) -> Body {
|
|
AnyBody::Bytes(Bytes::from_static(s.as_ref()))
|
|
}
|
|
}
|
|
|
|
impl From<&'static [u8]> for AnyBody {
|
|
fn from(s: &'static [u8]) -> Body {
|
|
AnyBody::Bytes(Bytes::from_static(s))
|
|
}
|
|
}
|
|
|
|
impl From<Vec<u8>> for AnyBody {
|
|
fn from(vec: Vec<u8>) -> Body {
|
|
AnyBody::Bytes(Bytes::from(vec))
|
|
}
|
|
}
|
|
|
|
impl From<String> for AnyBody {
|
|
fn from(s: String) -> Body {
|
|
s.into_bytes().into()
|
|
}
|
|
}
|
|
|
|
impl From<&'_ String> for AnyBody {
|
|
fn from(s: &String) -> Body {
|
|
AnyBody::Bytes(Bytes::copy_from_slice(AsRef::<[u8]>::as_ref(&s)))
|
|
}
|
|
}
|
|
|
|
impl From<Cow<'_, str>> for AnyBody {
|
|
fn from(s: Cow<'_, str>) -> Body {
|
|
match s {
|
|
Cow::Owned(s) => AnyBody::from(s),
|
|
Cow::Borrowed(s) => {
|
|
AnyBody::Bytes(Bytes::copy_from_slice(AsRef::<[u8]>::as_ref(s)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Bytes> for AnyBody {
|
|
fn from(s: Bytes) -> Body {
|
|
AnyBody::Bytes(s)
|
|
}
|
|
}
|
|
|
|
impl From<BytesMut> for AnyBody {
|
|
fn from(s: BytesMut) -> Body {
|
|
AnyBody::Bytes(s.freeze())
|
|
}
|
|
}
|
|
|
|
impl<S, E> From<SizedStream<S>> for AnyBody
|
|
where
|
|
S: Stream<Item = Result<Bytes, E>> + 'static,
|
|
E: Into<Box<dyn StdError>> + 'static,
|
|
{
|
|
fn from(s: SizedStream<S>) -> Body {
|
|
AnyBody::from_message(s)
|
|
}
|
|
}
|
|
|
|
impl<S, E> From<BodyStream<S>> for AnyBody
|
|
where
|
|
S: Stream<Item = Result<Bytes, E>> + 'static,
|
|
E: Into<Box<dyn StdError>> + 'static,
|
|
{
|
|
fn from(s: BodyStream<S>) -> Body {
|
|
AnyBody::from_message(s)
|
|
}
|
|
}
|
|
|
|
/// A boxed message body with boxed errors.
|
|
pub struct BoxAnyBody(Pin<Box<dyn MessageBody<Error = Box<dyn StdError + 'static>>>>);
|
|
|
|
impl BoxAnyBody {
|
|
/// Boxes a `MessageBody` and any errors it generates.
|
|
pub fn from_body<B>(body: B) -> Self
|
|
where
|
|
B: MessageBody + 'static,
|
|
B::Error: Into<Box<dyn StdError + 'static>>,
|
|
{
|
|
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<Error = Box<dyn StdError + 'static>>)> {
|
|
self.0.as_mut()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for BoxAnyBody {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.write_str("BoxAnyBody(dyn MessageBody)")
|
|
}
|
|
}
|
|
|
|
impl MessageBody for BoxAnyBody {
|
|
type Error = Error;
|
|
|
|
fn size(&self) -> BodySize {
|
|
self.0.size()
|
|
}
|
|
|
|
fn poll_next(
|
|
mut self: Pin<&mut Self>,
|
|
cx: &mut Context<'_>,
|
|
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
|
self.0
|
|
.as_mut()
|
|
.poll_next(cx)
|
|
.map_err(|err| Error::new_body().with_cause(err))
|
|
}
|
|
}
|