diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index 011e2c608..598ef9c0e 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -28,6 +28,7 @@ * `Request::take_req_data()`. [#2487] * `impl Clone` for `RequestHead`. [#2487] * New methods on `MessageBody` trait, `is_complete_body` and `take_complete_body`, both with default implementations, for optimisations on body types that are done in exactly one poll/chunk. [#2497] +* New `boxed` method on `MessageBody` trait for wrapping body type. [#2520] ### Changed * Rename `body::BoxBody::{from_body => new}`. [#2468] @@ -56,6 +57,7 @@ [#2488]: https://github.com/actix/actix-web/pull/2488 [#2491]: https://github.com/actix/actix-web/pull/2491 [#2497]: https://github.com/actix/actix-web/pull/2497 +[#2520]: https://github.com/actix/actix-web/pull/2520 ## 3.0.0-beta.14 - 2021-11-30 diff --git a/actix-http/src/body/boxed.rs b/actix-http/src/body/boxed.rs index d4737aab8..7581bec88 100644 --- a/actix-http/src/body/boxed.rs +++ b/actix-http/src/body/boxed.rs @@ -14,7 +14,11 @@ use crate::Error; pub struct BoxBody(Pin>>>); impl BoxBody { - /// Boxes a `MessageBody` and any errors it generates. + /// Same as `MessageBody::boxed`. + /// + /// If the body type to wrap is unknown or generic it is better to use [`MessageBody::boxed`] to + /// avoid double boxing. + #[inline] pub fn new(body: B) -> Self where B: MessageBody + 'static, @@ -24,6 +28,7 @@ impl BoxBody { } /// Returns a mutable pinned reference to the inner message body type. + #[inline] pub fn as_pin_mut(&mut self) -> Pin<&mut (dyn MessageBody>)> { self.0.as_mut() } @@ -38,10 +43,12 @@ impl fmt::Debug for BoxBody { impl MessageBody for BoxBody { type Error = Error; + #[inline] fn size(&self) -> BodySize { self.0.size() } + #[inline] fn poll_next( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -52,13 +59,20 @@ impl MessageBody for BoxBody { .map_err(|err| Error::new_body().with_cause(err)) } + #[inline] fn is_complete_body(&self) -> bool { self.0.is_complete_body() } + #[inline] fn take_complete_body(&mut self) -> Bytes { self.0.take_complete_body() } + + #[inline] + fn boxed(self) -> BoxBody { + self + } } #[cfg(test)] diff --git a/actix-http/src/body/either.rs b/actix-http/src/body/either.rs index 103b39c5d..3a4082dc9 100644 --- a/actix-http/src/body/either.rs +++ b/actix-http/src/body/either.rs @@ -88,6 +88,14 @@ where EitherBody::Right { body } => body.take_complete_body(), } } + + #[inline] + fn boxed(self) -> BoxBody { + match self { + EitherBody::Left { body } => body.boxed(), + EitherBody::Right { body } => body.boxed(), + } + } } #[cfg(test)] diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index 10a7260f4..075ae7220 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -12,7 +12,7 @@ use bytes::{Bytes, BytesMut}; use futures_core::ready; use pin_project_lite::pin_project; -use super::BodySize; +use super::{BodySize, BoxBody}; /// An interface types that can converted to bytes and used as response bodies. // TODO: examples @@ -77,6 +77,15 @@ pub trait MessageBody { std::any::type_name::() ); } + + /// Converts this body into `BoxBody`. + #[inline] + fn boxed(self) -> BoxBody + where + Self: Sized + 'static, + { + BoxBody::new(self) + } } mod foreign_impls { @@ -656,7 +665,7 @@ mod tests { assert_eq!(body.take_complete_body(), b"test".as_ref()); // subsequent poll_next returns None - let waker = futures_util::task::noop_waker(); + let waker = futures_task::noop_waker(); let mut cx = Context::from_waker(&waker); assert!(Pin::new(&mut body).poll_next(&mut cx).map_err(drop) == Poll::Ready(None)); } diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 9f799f669..aee9e80b4 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -194,7 +194,7 @@ impl Response { where B: MessageBody + 'static, { - self.map_body(|_, body| BoxBody::new(body)) + self.map_body(|_, body| body.boxed()) } /// Returns body, consuming this response. diff --git a/awc/src/any_body.rs b/awc/src/any_body.rs index cb9038ff3..2ffeb5074 100644 --- a/awc/src/any_body.rs +++ b/awc/src/any_body.rs @@ -45,9 +45,7 @@ impl AnyBody { where B: MessageBody + 'static, { - Self::Body { - body: BoxBody::new(body), - } + Self::Body { body: body.boxed() } } /// Constructs new `AnyBody` instance from a slice of bytes by copying it. diff --git a/src/response/response.rs b/src/response/response.rs index 1900dd845..4fb4b44b6 100644 --- a/src/response/response.rs +++ b/src/response/response.rs @@ -244,8 +244,7 @@ impl HttpResponse { where B: MessageBody + 'static, { - // TODO: avoid double boxing with down-casting, if it improves perf - self.map_body(|_, body| BoxBody::new(body)) + self.map_body(|_, body| body.boxed()) } /// Extract response body diff --git a/src/service.rs b/src/service.rs index 36b3858e6..9ccf5274d 100644 --- a/src/service.rs +++ b/src/service.rs @@ -451,7 +451,7 @@ impl ServiceResponse { where B: MessageBody + 'static, { - self.map_body(|_, body| BoxBody::new(body)) + self.map_body(|_, body| body.boxed()) } }