diff --git a/src/body/body.rs b/src/body/body.rs index 4d20e4d1..b726e2fa 100644 --- a/src/body/body.rs +++ b/src/body/body.rs @@ -285,6 +285,14 @@ impl Body { } } } + + pub(super) fn take_full_data(&mut self) -> Option { + if let Kind::Once(ref mut chunk) = self.kind { + chunk.take() + } else { + None + } + } } impl Default for Body { diff --git a/src/body/mod.rs b/src/body/mod.rs index e9d8135b..0f7350f0 100644 --- a/src/body/mod.rs +++ b/src/body/mod.rs @@ -14,6 +14,7 @@ //! and returned by hyper as a "receive stream" (so, for server requests and //! client responses). It is also a decent default implementation if you don't //! have very custom needs of your send streams. + pub use self::body::{Body, Sender}; pub use self::chunk::Chunk; pub use self::payload::Payload; @@ -22,6 +23,27 @@ mod body; mod chunk; mod payload; +/// An optimization to try to take a full body if immediately available. +/// +/// This is currently limited to *only* `hyper::Body`s. +pub(crate) fn take_full_data(body: &mut T) -> Option { + use std::any::{Any, TypeId}; + + // This static type check can be optimized at compile-time. + if TypeId::of::() == TypeId::of::() { + let mut full = (body as &mut dyn Any) + .downcast_mut::() + .expect("must be Body") + .take_full_data(); + (&mut full as &mut dyn Any) + .downcast_mut::>() + .expect("must be T::Data") + .take() + } else { + None + } +} + // The full_data API is not stable, so these types are to try to prevent // users from being able to: // diff --git a/src/proto/h1/dispatch.rs b/src/proto/h1/dispatch.rs index fc68e4ac..9bb34612 100644 --- a/src/proto/h1/dispatch.rs +++ b/src/proto/h1/dispatch.rs @@ -248,7 +248,16 @@ where return Poll::Ready(Ok(())); } else if self.body_rx.is_none() && self.conn.can_write_head() && self.dispatch.should_poll() { if let Some(msg) = ready!(self.dispatch.poll_msg(cx)) { - let (head, body) = msg.map_err(crate::Error::new_user_service)?; + let (head, mut body) = msg.map_err(crate::Error::new_user_service)?; + + // Check if the body knows its full data immediately. + // + // If so, we can skip a bit of bookkeeping that streaming + // bodies need to do. + if let Some(full) = crate::body::take_full_data(&mut body) { + self.conn.write_full_msg(head, full); + return Poll::Ready(Ok(())); + } let body_type = if body.is_end_stream() { self.body_rx.set(None);