From 26e31132a52092419149a6289f6ef9ee6ba0302e Mon Sep 17 00:00:00 2001 From: Vladimir Voitenko Date: Thu, 26 Mar 2026 15:22:02 +0500 Subject: [PATCH] feat(server,http1,http2,upgrade): remove body static lifetime constraint Before this change, hyper expects body trait implementation to have 'static lifetime, which prevents use cases where one might have StreamBody that references certain state that outlives hyper connection object. This change adds lifetime parameter 'body to varies hyper objects that replaces 'static. Signed-off-by: Vladimir Voitenko --- src/server/conn/auto/mod.rs | 88 ++++++++++++++++++++----------------- src/server/graceful.rs | 49 +++++++++++---------- 2 files changed, 74 insertions(+), 63 deletions(-) diff --git a/src/server/conn/auto/mod.rs b/src/server/conn/auto/mod.rs index 01ec8e1..ca44831 100644 --- a/src/server/conn/auto/mod.rs +++ b/src/server/conn/auto/mod.rs @@ -219,12 +219,16 @@ impl Builder { } /// Bind a connection together with a [`Service`]. - pub fn serve_connection(&self, io: I, service: S) -> Connection<'_, I, S, E> + pub fn serve_connection<'body, I, S, B>( + &self, + io: I, + service: S, + ) -> Connection<'_, 'body, I, S, E> where S: Service, Response = Response>, S::Future: 'static, S::Error: Into>, - B: Body + 'static, + B: Body + 'body, B::Error: Into>, I: Read + Write + Unpin + 'static, E: HttpServerConnExec, @@ -262,16 +266,16 @@ impl Builder { /// instead. See the documentation of the latter to understand why. /// /// [`hyper_util::server::conn::auto::upgrade::downcast`]: crate::server::conn::auto::upgrade::downcast - pub fn serve_connection_with_upgrades( + pub fn serve_connection_with_upgrades<'body, I, S, B>( &self, io: I, service: S, - ) -> UpgradeableConnection<'_, I, S, E> + ) -> UpgradeableConnection<'_, 'body, I, S, E> where S: Service, Response = Response>, S::Future: 'static, S::Error: Into>, - B: Body + 'static, + B: Body + 'body, B::Error: Into>, I: Read + Write + Unpin + Send + 'static, E: HttpServerConnExec, @@ -387,12 +391,12 @@ pin_project! { /// To drive HTTP on this connection this future **must be polled**, typically with /// `.await`. If it isn't polled, no progress will be made on this connection. #[must_use = "futures do nothing unless polled"] - pub struct Connection<'a, I, S, E> + pub struct Connection<'a, 'body, I, S, E> where S: HttpService, { #[pin] - state: ConnState<'a, I, S, E>, + state: ConnState<'a, 'body, I, S, E>, } } @@ -413,20 +417,21 @@ impl std::ops::Deref for Cow<'_, T> { } #[cfg(feature = "http1")] -type Http1Connection = hyper::server::conn::http1::Connection, S>; +type Http1Connection<'body, I, S> = hyper::server::conn::http1::Connection<'body, Rewind, S>; #[cfg(not(feature = "http1"))] type Http1Connection = (PhantomData, PhantomData); #[cfg(feature = "http2")] -type Http2Connection = hyper::server::conn::http2::Connection, S, E>; +type Http2Connection<'body, I, S, E> = + hyper::server::conn::http2::Connection<'body, Rewind, S, E>; #[cfg(not(feature = "http2"))] -type Http2Connection = (PhantomData, PhantomData, PhantomData); +type Http2Connection<'body, I, S, E> = (PhantomData, PhantomData, PhantomData); pin_project! { #[project = ConnStateProj] - enum ConnState<'a, I, S, E> + enum ConnState<'a, 'body, I, S, E> where S: HttpService, { @@ -438,21 +443,21 @@ pin_project! { }, H1 { #[pin] - conn: Http1Connection, + conn: Http1Connection<'body, I, S>, }, H2 { #[pin] - conn: Http2Connection, + conn: Http2Connection<'body, I, S, E>, }, } } -impl Connection<'_, I, S, E> +impl<'body, I, S, E, B> Connection<'_, 'body, I, S, E> where - S: HttpService, + S: HttpService + 'body, S::Error: Into>, - I: Read + Write + Unpin, - B: Body + 'static, + I: Read + Write + Unpin + 'body, + B: Body + 'body, B::Error: Into>, E: HttpServerConnExec, { @@ -477,7 +482,7 @@ where } /// Make this Connection static, instead of borrowing from Builder. - pub fn into_owned(self) -> Connection<'static, I, S, E> + pub fn into_owned(self) -> Connection<'static, 'body, I, S, E> where Builder: Clone, { @@ -503,12 +508,12 @@ where } } -impl Future for Connection<'_, I, S, E> +impl<'body, I, S, E, B> Future for Connection<'_, 'body, I, S, E> where S: Service, Response = Response>, S::Future: 'static, S::Error: Into>, - B: Body + 'static, + B: Body + 'body, B::Error: Into>, I: Read + Write + Unpin + 'static, E: HttpServerConnExec, @@ -564,24 +569,25 @@ pin_project! { /// To drive HTTP on this connection this future **must be polled**, typically with /// `.await`. If it isn't polled, no progress will be made on this connection. #[must_use = "futures do nothing unless polled"] - pub struct UpgradeableConnection<'a, I, S, E> + pub struct UpgradeableConnection<'a, 'body, I, S, E> where S: HttpService, { #[pin] - state: UpgradeableConnState<'a, I, S, E>, + state: UpgradeableConnState<'a, 'body, I, S, E>, } } #[cfg(feature = "http1")] -type Http1UpgradeableConnection = hyper::server::conn::http1::UpgradeableConnection; +type Http1UpgradeableConnection<'body, I, S> = + hyper::server::conn::http1::UpgradeableConnection<'body, I, S>; #[cfg(not(feature = "http1"))] type Http1UpgradeableConnection = (PhantomData, PhantomData); pin_project! { #[project = UpgradeableConnStateProj] - enum UpgradeableConnState<'a, I, S, E> + enum UpgradeableConnState<'a, 'body, I, S, E> where S: HttpService, { @@ -593,21 +599,21 @@ pin_project! { }, H1 { #[pin] - conn: Http1UpgradeableConnection, S>, + conn: Http1UpgradeableConnection<'body, Rewind, S>, }, H2 { #[pin] - conn: Http2Connection, + conn: Http2Connection<'body, I, S, E>, }, } } -impl UpgradeableConnection<'_, I, S, E> +impl<'body, I, S, E, B> UpgradeableConnection<'_, 'body, I, S, E> where - S: HttpService, + S: HttpService + 'body, S::Error: Into>, - I: Read + Write + Unpin, - B: Body + 'static, + I: Read + Write + Unpin + 'body, + B: Body + 'body, B::Error: Into>, E: HttpServerConnExec, { @@ -632,7 +638,7 @@ where } /// Make this Connection static, instead of borrowing from Builder. - pub fn into_owned(self) -> UpgradeableConnection<'static, I, S, E> + pub fn into_owned(self) -> UpgradeableConnection<'static, 'body, I, S, E> where Builder: Clone, { @@ -658,12 +664,12 @@ where } } -impl Future for UpgradeableConnection<'_, I, S, E> +impl<'body, I, S, E, B> Future for UpgradeableConnection<'_, 'body, I, S, E> where - S: Service, Response = Response>, + S: Service, Response = Response> + 'body, S::Future: 'static, S::Error: Into>, - B: Body + 'static, + B: Body + 'body, B::Error: Into>, I: Read + Write + Unpin + Send + 'static, E: HttpServerConnExec, @@ -913,11 +919,11 @@ impl Http1Builder<'_, E> { /// handle HTTP upgrades. This requires that the IO object implements /// `Send`. #[cfg(feature = "http2")] - pub fn serve_connection_with_upgrades( + pub fn serve_connection_with_upgrades<'body, I, S, B>( &self, io: I, service: S, - ) -> UpgradeableConnection<'_, I, S, E> + ) -> UpgradeableConnection<'_, 'body, I, S, E> where S: Service, Response = Response>, S::Future: 'static, @@ -1099,12 +1105,12 @@ impl Http2Builder<'_, E> { } /// Bind a connection together with a [`Service`]. - pub async fn serve_connection(&self, io: I, service: S) -> Result<()> + pub async fn serve_connection<'body, I, S, B>(&self, io: I, service: S) -> Result<()> where S: Service, Response = Response>, S::Future: 'static, S::Error: Into>, - B: Body + 'static, + B: Body + 'body, B::Error: Into>, I: Read + Write + Unpin + 'static, E: HttpServerConnExec, @@ -1115,16 +1121,16 @@ impl Http2Builder<'_, E> { /// Bind a connection together with a [`Service`], with the ability to /// handle HTTP upgrades. This requires that the IO object implements /// `Send`. - pub fn serve_connection_with_upgrades( + pub fn serve_connection_with_upgrades<'body, I, S, B>( &self, io: I, service: S, - ) -> UpgradeableConnection<'_, I, S, E> + ) -> UpgradeableConnection<'_, 'body, I, S, E> where S: Service, Response = Response>, S::Future: 'static, S::Error: Into>, - B: Body + 'static, + B: Body + 'body, B::Error: Into>, I: Read + Write + Unpin + Send + 'static, E: HttpServerConnExec, diff --git a/src/server/graceful.rs b/src/server/graceful.rs index b367fc8..6725ec4 100644 --- a/src/server/graceful.rs +++ b/src/server/graceful.rs @@ -163,12 +163,12 @@ pub trait GracefulConnection: Future> + private } #[cfg(feature = "http1")] -impl GracefulConnection for hyper::server::conn::http1::Connection +impl<'body, I, B, S> GracefulConnection for hyper::server::conn::http1::Connection<'body, I, S> where - S: hyper::service::HttpService, + S: hyper::service::HttpService + 'body, S::Error: Into>, I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static, - B: hyper::body::Body + 'static, + B: hyper::body::Body + 'body, B::Error: Into>, { type Error = hyper::Error; @@ -179,12 +179,13 @@ where } #[cfg(feature = "http2")] -impl GracefulConnection for hyper::server::conn::http2::Connection +impl<'body, I, B, S, E> GracefulConnection + for hyper::server::conn::http2::Connection<'body, I, S, E> where S: hyper::service::HttpService, S::Error: Into>, I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static, - B: hyper::body::Body + 'static, + B: hyper::body::Body + 'body, B::Error: Into>, E: hyper::rt::bounds::Http2ServerConnExec, { @@ -196,13 +197,15 @@ where } #[cfg(feature = "server-auto")] -impl GracefulConnection for crate::server::conn::auto::Connection<'_, I, S, E> +impl<'body, I, B, S, E> GracefulConnection + for crate::server::conn::auto::Connection<'_, 'body, I, S, E> where - S: hyper::service::Service, Response = http::Response>, + S: hyper::service::Service, Response = http::Response> + + 'body, S::Error: Into>, S::Future: 'static, I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static, - B: hyper::body::Body + 'static, + B: hyper::body::Body + 'body, B::Error: Into>, E: hyper::rt::bounds::Http2ServerConnExec, { @@ -214,10 +217,11 @@ where } #[cfg(feature = "server-auto")] -impl GracefulConnection - for crate::server::conn::auto::UpgradeableConnection<'_, I, S, E> +impl<'body, I, B, S, E> GracefulConnection + for crate::server::conn::auto::UpgradeableConnection<'_, 'body, I, S, E> where - S: hyper::service::Service, Response = http::Response>, + S: hyper::service::Service, Response = http::Response> + + 'body, S::Error: Into>, S::Future: 'static, I: hyper::rt::Read + hyper::rt::Write + Unpin + Send + 'static, @@ -236,41 +240,41 @@ mod private { pub trait Sealed {} #[cfg(feature = "http1")] - impl Sealed for hyper::server::conn::http1::Connection + impl<'body, I, B, S> Sealed for hyper::server::conn::http1::Connection<'body, I, S> where S: hyper::service::HttpService, S::Error: Into>, I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static, - B: hyper::body::Body + 'static, + B: hyper::body::Body + 'body, B::Error: Into>, { } #[cfg(feature = "http1")] - impl Sealed for hyper::server::conn::http1::UpgradeableConnection + impl<'body, I, B, S> Sealed for hyper::server::conn::http1::UpgradeableConnection<'body, I, S> where - S: hyper::service::HttpService, + S: hyper::service::HttpService + 'body, S::Error: Into>, I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static, - B: hyper::body::Body + 'static, + B: hyper::body::Body + 'body, B::Error: Into>, { } #[cfg(feature = "http2")] - impl Sealed for hyper::server::conn::http2::Connection + impl<'body, I, B, S, E> Sealed for hyper::server::conn::http2::Connection<'body, I, S, E> where S: hyper::service::HttpService, S::Error: Into>, I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static, - B: hyper::body::Body + 'static, + B: hyper::body::Body + 'body, B::Error: Into>, E: hyper::rt::bounds::Http2ServerConnExec, { } #[cfg(feature = "server-auto")] - impl Sealed for crate::server::conn::auto::Connection<'_, I, S, E> + impl<'body, I, B, S, E> Sealed for crate::server::conn::auto::Connection<'_, 'body, I, S, E> where S: hyper::service::Service< http::Request, @@ -279,14 +283,15 @@ mod private { S::Error: Into>, S::Future: 'static, I: hyper::rt::Read + hyper::rt::Write + Unpin + 'static, - B: hyper::body::Body + 'static, + B: hyper::body::Body + 'body, B::Error: Into>, E: hyper::rt::bounds::Http2ServerConnExec, { } #[cfg(feature = "server-auto")] - impl Sealed for crate::server::conn::auto::UpgradeableConnection<'_, I, S, E> + impl<'body, I, B, S, E> Sealed + for crate::server::conn::auto::UpgradeableConnection<'_, 'body, I, S, E> where S: hyper::service::Service< http::Request, @@ -295,7 +300,7 @@ mod private { S::Error: Into>, S::Future: 'static, I: hyper::rt::Read + hyper::rt::Write + Unpin + Send + 'static, - B: hyper::body::Body + 'static, + B: hyper::body::Body + 'body, B::Error: Into>, E: hyper::rt::bounds::Http2ServerConnExec, {