//! 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; /// Convert a [`http_body::Body`] into a [`BoxBody`]. pub 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: 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::(5_u32), Err(5_u32)); assert_eq!(try_downcast::(5_i32), Ok(5_i32)); }