| //! HTTP body utilities. |
| |
| use crate::{BoxError, Error}; |
| use bytes::Bytes; |
| use bytes::{Buf, BufMut}; |
| use http_body::Body; |
| |
| /// A boxed [`Body`] trait object. |
| /// |
| /// This is used in axum as the response body type for applications. It's |
| /// necessary to unify multiple response bodies types into one. |
| pub type BoxBody = http_body::combinators::UnsyncBoxBody<Bytes, Error>; |
| |
| /// Convert a [`http_body::Body`] into a [`BoxBody`]. |
| pub fn boxed<B>(body: B) -> BoxBody |
| where |
| B: http_body::Body<Data = Bytes> + Send + 'static, |
| B::Error: Into<BoxError>, |
| { |
| try_downcast(body).unwrap_or_else(|body| body.map_err(Error::new).boxed_unsync()) |
| } |
| |
| pub(crate) fn try_downcast<T, K>(k: K) -> Result<T, K> |
| where |
| T: 'static, |
| K: Send + 'static, |
| { |
| let mut k = Some(k); |
| if let Some(k) = <dyn std::any::Any>::downcast_mut::<Option<T>>(&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<T>(body: T) -> Result<Bytes, T::Error> |
| where |
| T: 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()) |
| } |
| |
| #[test] |
| fn test_try_downcast() { |
| assert_eq!(try_downcast::<i32, _>(5_u32), Err(5_u32)); |
| assert_eq!(try_downcast::<i32, _>(5_i32), Ok(5_i32)); |
| } |