//! HTTP body utilities. use crate::{BoxError, Error}; use bytes::Bytes; use bytes::{Buf, BufMut}; use futures_util::stream::Stream; use futures_util::TryStream; use http::HeaderMap; use http_body::Body as _; use pin_project_lite::pin_project; use std::pin::Pin; use std::task::{Context, Poll}; use sync_wrapper::SyncWrapper; type BoxBody = http_body::combinators::UnsyncBoxBody; fn boxed(body: B) -> BoxBody where B: http_body::Body + Send + 'static, B::Error: Into, { try_downcast(body).unwrap_or_else(|body| body.map_err(Error::new).boxed_unsync()) } pub(crate) fn try_downcast(k: K) -> Result where T: 'static, K: Send + 'static, { let mut k = Some(k); if let Some(k) = ::downcast_mut::>(&mut k) { Ok(k.take().unwrap()) } else { Err(k.unwrap()) } } // copied from hyper under the following license: // Copyright (c) 2014-2021 Sean McArthur // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. pub(crate) async fn to_bytes(body: T) -> Result where T: http_body::Body, { futures_util::pin_mut!(body); // If there's only 1 chunk, we can just return Buf::to_bytes() let mut first = if let Some(buf) = body.data().await { buf? } else { return Ok(Bytes::new()); }; let second = if let Some(buf) = body.data().await { buf? } else { return Ok(first.copy_to_bytes(first.remaining())); }; // With more than 1 buf, we gotta flatten into a Vec first. let cap = first.remaining() + second.remaining() + body.size_hint().lower() as usize; let mut vec = Vec::with_capacity(cap); vec.put(first); vec.put(second); while let Some(buf) = body.data().await { vec.put(buf?); } Ok(vec.into()) } /// The body type used in axum requests and responses. #[derive(Debug)] pub struct Body(BoxBody); impl Body { /// Create a new `Body` that wraps another [`http_body::Body`]. pub fn new(body: B) -> Self where B: http_body::Body + Send + 'static, B::Error: Into, { try_downcast(body).unwrap_or_else(|body| Self(boxed(body))) } /// Create an empty body. pub fn empty() -> Self { Self::new(http_body::Empty::new()) } /// Create a new `Body` from a [`Stream`]. /// /// [`Stream`]: futures_util::stream::Stream pub fn from_stream(stream: S) -> Self where S: TryStream + Send + 'static, S::Ok: Into, S::Error: Into, { 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::Full::from(buf)) } } }; } body_from_impl!(&'static [u8]); body_from_impl!(std::borrow::Cow<'static, [u8]>); body_from_impl!(Vec); body_from_impl!(&'static str); body_from_impl!(std::borrow::Cow<'static, str>); body_from_impl!(String); body_from_impl!(Bytes); impl http_body::Body for Body { type Data = Bytes; type Error = Error; #[inline] fn poll_data( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> std::task::Poll>> { Pin::new(&mut self.0).poll_data(cx) } #[inline] fn poll_trailers( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> std::task::Poll, Self::Error>> { Pin::new(&mut self.0).poll_trailers(cx) } #[inline] fn size_hint(&self) -> http_body::SizeHint { self.0.size_hint() } #[inline] fn is_end_stream(&self) -> bool { self.0.is_end_stream() } } impl Stream for Body { type Item = Result; #[inline] fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.poll_data(cx) } } pin_project! { struct StreamBody { #[pin] stream: SyncWrapper, } } impl http_body::Body for StreamBody where S: TryStream, S::Ok: Into, S::Error: Into, { type Data = Bytes; type Error = Error; fn poll_data( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { let stream = self.project().stream.get_pin_mut(); match futures_util::ready!(stream.try_poll_next(cx)) { Some(Ok(chunk)) => Poll::Ready(Some(Ok(chunk.into()))), Some(Err(err)) => Poll::Ready(Some(Err(Error::new(err)))), None => Poll::Ready(None), } } #[inline] fn poll_trailers( self: Pin<&mut Self>, _cx: &mut Context<'_>, ) -> Poll, Self::Error>> { Poll::Ready(Ok(None)) } } #[test] fn test_try_downcast() { assert_eq!(try_downcast::(5_u32), Err(5_u32)); assert_eq!(try_downcast::(5_i32), Ok(5_i32)); }