From 5658067c9e29a05092e859550118ee717c0c3954 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Mon, 23 Dec 2024 11:18:36 -0500 Subject: [PATCH] fix: propagate Body::size_hint when wrapping bodies --- src/async_impl/body.rs | 53 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/src/async_impl/body.rs b/src/async_impl/body.rs index c2f1257c1..454046dd0 100644 --- a/src/async_impl/body.rs +++ b/src/async_impl/body.rs @@ -148,10 +148,7 @@ impl Body { { use http_body_util::BodyExt; - let boxed = inner - .map_frame(|f| f.map_data(Into::into)) - .map_err(Into::into) - .boxed(); + let boxed = IntoBytesBody { inner }.map_err(Into::into).boxed(); Body { inner: Inner::Streaming(boxed), @@ -461,6 +458,47 @@ where } } +// ===== impl IntoBytesBody ===== + +pin_project! { + struct IntoBytesBody { + #[pin] + inner: B, + } +} + +// We can't use `map_frame()` because that loses the hint data (for good reason). +// But we aren't transforming the data. +impl hyper::body::Body for IntoBytesBody +where + B: hyper::body::Body, + B::Data: Into, +{ + type Data = Bytes; + type Error = B::Error; + + fn poll_frame( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Poll, Self::Error>>> { + match futures_core::ready!(self.project().inner.poll_frame(cx)) { + Some(Ok(f)) => Poll::Ready(Some(Ok(f.map_data(Into::into)))), + Some(Err(e)) => Poll::Ready(Some(Err(e))), + None => Poll::Ready(None), + } + } + + #[inline] + fn size_hint(&self) -> http_body::SizeHint { + self.inner.size_hint() + } + + #[inline] + fn is_end_stream(&self) -> bool { + self.inner.is_end_stream() + } +} + #[cfg(test)] mod tests { use http_body::Body as _; @@ -484,8 +522,9 @@ mod tests { assert!(!bytes_body.is_end_stream()); assert_eq!(bytes_body.size_hint().exact(), Some(3)); - let stream_body = Body::wrap(bytes_body); - assert!(!stream_body.is_end_stream()); - assert_eq!(stream_body.size_hint().exact(), None); + // can delegate even when wrapped + let stream_body = Body::wrap(empty_body); + assert!(stream_body.is_end_stream()); + assert_eq!(stream_body.size_hint().exact(), Some(0)); } }