From 2981d85039259e162f1162fb7422f94adc5f3bdb Mon Sep 17 00:00:00 2001 From: Vladimir Voitenko Date: Thu, 26 Mar 2026 15:25:28 +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. This change contains no breaking changes for existing use cases, however it requires appropriate change in hyper-util crate Signed-off-by: Vladimir Voitenko --- src/client/conn/http1.rs | 2 +- src/proto/h1/dispatch.rs | 12 ++++++----- src/proto/h2/server.rs | 18 +++++++++------- src/server/conn/http1.rs | 45 ++++++++++++++++++++-------------------- src/server/conn/http2.rs | 18 ++++++++-------- 5 files changed, 50 insertions(+), 45 deletions(-) diff --git a/src/client/conn/http1.rs b/src/client/conn/http1.rs index 85c1fdc665..b2149f2510 100644 --- a/src/client/conn/http1.rs +++ b/src/client/conn/http1.rs @@ -17,7 +17,7 @@ use crate::body::{Body, Incoming as IncomingBody}; use crate::proto; type Dispatcher = - proto::dispatch::Dispatcher, B, T, proto::h1::ClientTransaction>; + proto::dispatch::Dispatcher<'static, proto::dispatch::Client, B, T, proto::h1::ClientTransaction>; /// The sender side of an established connection. pub struct SendRequest { diff --git a/src/proto/h1/dispatch.rs b/src/proto/h1/dispatch.rs index 5daeb5ebf6..6e1f3ebb73 100644 --- a/src/proto/h1/dispatch.rs +++ b/src/proto/h1/dispatch.rs @@ -19,12 +19,13 @@ use crate::common::task; use crate::proto::{BodyLength, Conn, Dispatched, MessageHead, RequestHead}; use crate::upgrade::OnUpgrade; -pub(crate) struct Dispatcher { +pub(crate) struct Dispatcher<'body, D, Bs: Body + 'body, I, T> { conn: Conn, dispatch: D, body_tx: Option, body_rx: Pin>>, is_closing: bool, + _a: std::marker::PhantomData<&'body ()> } pub(crate) trait Dispatch { @@ -64,7 +65,7 @@ cfg_client! { type ClientRx = crate::client::dispatch::Receiver, http::Response>; } -impl Dispatcher +impl<'body, D, Bs, I, T> Dispatcher<'body, D, Bs, I, T> where D: Dispatch< PollItem = MessageHead, @@ -74,7 +75,7 @@ where D::PollError: Into>, I: Read + Write + Unpin, T: Http1Transaction + Unpin, - Bs: Body + 'static, + Bs: Body + 'body, Bs::Error: Into>, { pub(crate) fn new(dispatch: D, conn: Conn) -> Self { @@ -84,6 +85,7 @@ where body_tx: None, body_rx: Box::pin(None), is_closing: false, + _a: Default::default() } } @@ -451,7 +453,7 @@ where } } -impl Future for Dispatcher +impl<'body, D, Bs, I, T> Future for Dispatcher<'body, D, Bs, I, T> where D: Dispatch< PollItem = MessageHead, @@ -461,7 +463,7 @@ where D::PollError: Into>, I: Read + Write + Unpin, T: Http1Transaction + Unpin, - Bs: Body + 'static, + Bs: Body + 'body, Bs::Error: Into>, { type Output = crate::Result; diff --git a/src/proto/h2/server.rs b/src/proto/h2/server.rs index 483ed96dd9..c7ab5a42b4 100644 --- a/src/proto/h2/server.rs +++ b/src/proto/h2/server.rs @@ -78,7 +78,7 @@ impl Default for Config { } pin_project! { - pub(crate) struct Server + pub(crate) struct Server<'body, T, S, B, E> where S: HttpService, B: Body, @@ -88,7 +88,8 @@ pin_project! { service: S, state: State, date_header: bool, - close_pending: bool + close_pending: bool, + _v: std::marker::PhantomData<&'body ()> } } @@ -113,12 +114,12 @@ where date_header: bool, } -impl Server +impl<'body, T, S, B, E> Server<'body, T, S, B, E> where T: Read + Write + Unpin, S: HttpService, S::Error: Into>, - B: Body + 'static, + B: Body + 'body, E: Http2ServerConnExec, { pub(crate) fn new( @@ -127,7 +128,7 @@ where config: &Config, exec: E, timer: Time, - ) -> Server { + ) -> Server<'body, T, S, B, E> { let mut builder = h2::server::Builder::default(); builder .initial_window_size(config.initial_stream_window_size) @@ -172,6 +173,7 @@ where service, date_header: config.date_header, close_pending: false, + _v: Default::default() } } @@ -190,12 +192,12 @@ where } } -impl Future for Server +impl<'body, T, S, B, E> Future for Server<'body, T, S, B, E> where T: Read + Write + Unpin, S: HttpService, S::Error: Into>, - B: Body + 'static, + B: Body + 'body, E: Http2ServerConnExec, { type Output = crate::Result; @@ -239,7 +241,7 @@ where impl Serving where T: Read + Write + Unpin, - B: Body + 'static, + B: Body, { fn poll_server( &mut self, diff --git a/src/server/conn/http1.rs b/src/server/conn/http1.rs index 36dda98069..50c2a2f8c0 100644 --- a/src/server/conn/http1.rs +++ b/src/server/conn/http1.rs @@ -21,7 +21,8 @@ use crate::{ rt::Timer, }; -type Http1Dispatcher = proto::h1::Dispatcher< +type Http1Dispatcher<'body, T, B, S> = proto::h1::Dispatcher< + 'body, proto::h1::dispatch::Server, B, T, @@ -36,11 +37,11 @@ pin_project_lite::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 + pub struct Connection<'body, T, S> where S: HttpService, { - conn: Http1Dispatcher, + conn: Http1Dispatcher<'body, T, S::ResBody, S>, } } @@ -107,7 +108,7 @@ pub struct Parts { // ===== impl Connection ===== -impl fmt::Debug for Connection +impl<'body, I, S> fmt::Debug for Connection<'body, I, S> where S: HttpService, { @@ -116,12 +117,12 @@ where } } -impl Connection +impl<'body, I, B, S> Connection<'body, I, S> 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>, { /// Start a graceful shutdown process for this connection. @@ -177,7 +178,7 @@ where /// # Error /// /// This errors if the underlying connection protocol is not HTTP/1. - pub fn without_shutdown(self) -> impl Future>> { + pub fn without_shutdown(self) -> impl Future>> + 'body { let mut zelf = Some(self); crate::common::future::poll_fn(move |cx| { ready!(zelf.as_mut().unwrap().conn.poll_without_shutdown(cx))?; @@ -188,7 +189,7 @@ where /// Enable this connection to support higher-level HTTP upgrades. /// /// See [the `upgrade` module](crate::upgrade) for more. - pub fn with_upgrades(self) -> UpgradeableConnection + pub fn with_upgrades(self) -> UpgradeableConnection<'body, I, S> where I: Send, { @@ -196,12 +197,12 @@ where } } -impl Future for Connection +impl<'body, I, B, S> Future for Connection<'body, I, S> where S: HttpService, S::Error: Into>, I: Read + Write + Unpin, - B: Body + 'static, + B: Body + 'body, B::Error: Into>, { type Output = crate::Result<()>; @@ -441,11 +442,11 @@ impl Builder { /// # } /// # fn main() {} /// ``` - pub fn serve_connection(&self, io: I, service: S) -> Connection + pub fn serve_connection<'body, I, S>(&self, io: I, service: S) -> Connection<'body, I, S> where S: HttpService, S::Error: Into>, - S::ResBody: 'static, + S::ResBody: 'body, ::Error: Into>, I: Read + Write + Unpin, { @@ -496,19 +497,19 @@ impl Builder { /// A future binding a connection with a Service with Upgrade support. #[must_use = "futures do nothing unless polled"] #[allow(missing_debug_implementations)] -pub struct UpgradeableConnection +pub struct UpgradeableConnection<'body, T, S> where S: HttpService, { - pub(super) inner: Option>, + pub(super) inner: Option>, } -impl UpgradeableConnection +impl<'body, I, B, S> UpgradeableConnection<'body, I, S> 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>, { /// Start a graceful shutdown process for this connection. @@ -524,12 +525,12 @@ where } } -impl Future for UpgradeableConnection +impl<'body, I, B, S> Future for UpgradeableConnection<'body, I, S> where S: HttpService, S::Error: Into>, I: Read + Write + Unpin + Send + 'static, - B: Body + 'static, + B: Body + 'body, B::Error: Into>, { type Output = crate::Result<()>; diff --git a/src/server/conn/http2.rs b/src/server/conn/http2.rs index 0452109c3c..e1c743ecb3 100644 --- a/src/server/conn/http2.rs +++ b/src/server/conn/http2.rs @@ -26,11 +26,11 @@ 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 + pub struct Connection<'body, T, S, E> where S: HttpService, { - conn: proto::h2::Server, + conn: proto::h2::Server<'body, T, S, S::ResBody, E>, } } @@ -47,7 +47,7 @@ pub struct Builder { // ===== impl Connection ===== -impl fmt::Debug for Connection +impl<'body, I, S, E> fmt::Debug for Connection<'body, I, S, E> where S: HttpService, { @@ -56,12 +56,12 @@ where } } -impl Connection +impl<'body, I, B, S, E> Connection<'body, I, S, E> where S: HttpService, S::Error: Into>, I: Read + Write + Unpin, - B: Body + 'static, + B: Body + 'body, B::Error: Into>, E: Http2ServerConnExec, { @@ -80,12 +80,12 @@ where } } -impl Future for Connection +impl<'body, I, B, S, E> Future for Connection<'body, I, S, E> where S: HttpService, S::Error: Into>, I: Read + Write + Unpin, - B: Body + 'static, + B: Body + 'body, B::Error: Into>, E: Http2ServerConnExec, { @@ -291,11 +291,11 @@ impl Builder { /// /// This returns a Future that must be polled in order for HTTP to be /// driven on the connection. - pub fn serve_connection(&self, io: I, service: S) -> Connection + pub fn serve_connection<'body, S, I, Bd>(&self, io: I, service: S) -> Connection<'body, I, S, E> where S: HttpService, S::Error: Into>, - Bd: Body + 'static, + Bd: Body + 'body, Bd::Error: Into>, I: Read + Write + Unpin, E: Http2ServerConnExec,