#[doc(hidden)]
pub use http_body::Body as HttpBody;
pub use hyper::body::Body;
use bytes::Bytes;
use crate::error::{BoxError, Error};
pub type BoxBody = http_body::combinators::UnsyncBoxBody<Bytes, Error>;
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())
}
#[doc(hidden)]
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())
}
}
pub(crate) fn empty() -> BoxBody {
boxed(http_body::Empty::new())
}
#[doc(hidden)]
pub fn to_boxed<B>(body: B) -> BoxBody
where
Body: From<B>,
{
boxed(Body::from(body))
}
use std::fmt;
use std::future::poll_fn;
use std::pin::Pin;
#[doc(hidden)]
#[derive(Debug, Clone, Copy)]
pub struct BodyLimitExceeded {
pub limit: usize,
}
impl fmt::Display for BodyLimitExceeded {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"request body exceeded the configured maximum of {} bytes",
self.limit
)
}
}
impl std::error::Error for BodyLimitExceeded {}
#[doc(hidden)]
#[derive(Debug)]
pub enum CollectBodyError<E> {
Body(E),
TooLarge(BodyLimitExceeded),
}
impl<E: fmt::Display> fmt::Display for CollectBodyError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Body(e) => write!(f, "error reading request body: {e}"),
Self::TooLarge(e) => e.fmt(f),
}
}
}
impl<E: std::error::Error + 'static> std::error::Error for CollectBodyError<E> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Body(e) => Some(e),
Self::TooLarge(e) => Some(e),
}
}
}
#[doc(hidden)]
pub async fn collect_body_limited<B>(body: B, limit: usize) -> Result<Bytes, CollectBodyError<B::Error>>
where
B: HttpBody,
{
let lower = body.size_hint().lower() as usize;
if lower > limit && limit > 0 {
return Err(CollectBodyError::TooLarge(BodyLimitExceeded { limit }));
}
let mut body: Pin<Box<B>> = Box::pin(body);
let mut buf: Vec<u8> = Vec::with_capacity(lower);
loop {
let chunk_opt = poll_fn(|cx| body.as_mut().poll_data(cx)).await;
match chunk_opt {
None => break,
Some(Err(e)) => return Err(CollectBodyError::Body(e)),
Some(Ok(mut chunk)) => {
use bytes::Buf;
let chunk_len = chunk.remaining();
if limit > 0 && buf.len().saturating_add(chunk_len) > limit {
return Err(CollectBodyError::TooLarge(BodyLimitExceeded { limit }));
}
buf.reserve(chunk_len);
while chunk.has_remaining() {
let slice = chunk.chunk();
let len = slice.len();
buf.extend_from_slice(slice);
chunk.advance(len);
}
}
}
}
Ok(Bytes::from(buf))
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_collect_body_limited_under_limit() {
let body = Body::from("hello");
let result = collect_body_limited(body, 1024).await;
assert_eq!(result.unwrap(), Bytes::from("hello"));
}
#[tokio::test]
async fn test_collect_body_limited_over_limit() {
let body = Body::from("this is way too long");
let result = collect_body_limited(body, 5).await;
assert!(matches!(
result,
Err(CollectBodyError::TooLarge(BodyLimitExceeded { limit: 5 }))
));
}
#[tokio::test]
async fn test_collect_body_limited_chunked_exceeds_mid_stream() {
use futures_util::stream;
let chunks: Vec<Result<&[u8], std::io::Error>> = vec![
Ok(b"aaaa"), Ok(b"bbbb"), ];
let body = Body::wrap_stream(stream::iter(chunks));
let result = collect_body_limited(body, 6).await;
assert!(matches!(result, Err(CollectBodyError::TooLarge(_))));
}
#[tokio::test]
async fn test_collect_body_limited_empty_body() {
let body = Body::empty();
let result = collect_body_limited(body, 100).await;
assert_eq!(result.unwrap(), Bytes::from(""));
}
}