diff --git a/actix-http/CHANGES.md b/actix-http/CHANGES.md index c4386bb4d..816d4b71f 100644 --- a/actix-http/CHANGES.md +++ b/actix-http/CHANGES.md @@ -1,6 +1,11 @@ # Changes ## Unreleased - 2022-xx-xx +### Added +- Implement `MessageBody` for `&mut B` where `B: MessageBody + Unpin`. [#2868] +- Implement `MessageBody` for `Pin` where `B::Target: MessageBody`. [#2868] + +[#2868]: https://github.com/actix/actix-web/pull/2868 ## 3.2.2 - 2022-09-11 diff --git a/actix-http/src/body/message_body.rs b/actix-http/src/body/message_body.rs index ab742b9cd..bac2fece1 100644 --- a/actix-http/src/body/message_body.rs +++ b/actix-http/src/body/message_body.rs @@ -120,8 +120,28 @@ pub trait MessageBody { } mod foreign_impls { + use std::ops::DerefMut; + use super::*; + impl MessageBody for &mut B + where + B: MessageBody + Unpin + ?Sized, + { + type Error = B::Error; + + fn size(&self) -> BodySize { + (&**self).size() + } + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { + Pin::new(&mut **self).poll_next(cx) + } + } + impl MessageBody for Infallible { type Error = Infallible; @@ -179,8 +199,9 @@ mod foreign_impls { } } - impl MessageBody for Pin> + impl MessageBody for Pin where + T: DerefMut + Unpin, B: MessageBody + ?Sized, { type Error = B::Error; @@ -445,6 +466,7 @@ mod tests { use actix_rt::pin; use actix_utils::future::poll_fn; use bytes::{Bytes, BytesMut}; + use futures_util::stream; use super::*; use crate::body::{self, EitherBody}; @@ -481,6 +503,34 @@ mod tests { assert_poll_next_none!(pl); } + #[actix_rt::test] + async fn mut_equivalence() { + assert_eq!(().size(), BodySize::Sized(0)); + assert_eq!(().size(), (&(&mut ())).size()); + + let pl = &mut (); + pin!(pl); + assert_poll_next_none!(pl); + + let pl = &mut Box::new(()); + pin!(pl); + assert_poll_next_none!(pl); + + let mut body = body::SizedStream::new( + 8, + stream::iter([ + Ok::<_, std::io::Error>(Bytes::from("1234")), + Ok(Bytes::from("5678")), + ]), + ); + let body = &mut body; + assert_eq!(body.size(), BodySize::Sized(8)); + pin!(body); + assert_poll_next!(body, Bytes::from_static(b"1234")); + assert_poll_next!(body, Bytes::from_static(b"5678")); + assert_poll_next_none!(body); + } + #[allow(clippy::let_unit_value)] #[actix_rt::test] async fn test_unit() { @@ -607,4 +657,18 @@ mod tests { let not_body = resp_body.downcast_ref::<()>(); assert!(not_body.is_none()); } + + #[actix_rt::test] + async fn non_owning_to_bytes() { + let mut body = BoxBody::new(()); + let bytes = body::to_bytes(&mut body).await.unwrap(); + assert_eq!(bytes, Bytes::new()); + + let mut body = body::BodyStream::new(stream::iter([ + Ok::<_, std::io::Error>(Bytes::from("1234")), + Ok(Bytes::from("5678")), + ])); + let bytes = body::to_bytes(&mut body).await.unwrap(); + assert_eq!(bytes, Bytes::from_static(b"12345678")); + } }