From 3986f773e50fb42b7985511811b8b552fc344c65 Mon Sep 17 00:00:00 2001 From: Eray Karatay <70590982+programatik29@users.noreply.github.com> Date: Tue, 31 Jan 2023 23:57:08 +0300 Subject: [PATCH] feat(rt): make private executor traits public (but sealed) in `rt::bounds` (#3127) Define public trait aliases that are sealed, but can be named externally, and have documentation showing how to provide a Executor to match the bounds, and how to express the bounds in your own API. Closes #2051 Closes #3097 Signed-off-by: Sven Pfennig --- src/common/exec.rs | 34 -------------------- src/proto/h2/server.rs | 8 ++--- src/rt/bounds.rs | 69 ++++++++++++++++++++++++++++++++++++++++ src/{rt.rs => rt/mod.rs} | 22 +++++++++++++ src/server/conn/http2.rs | 13 +++++--- src/service/mod.rs | 2 +- 6 files changed, 104 insertions(+), 44 deletions(-) create mode 100644 src/rt/bounds.rs rename src/{rt.rs => rt/mod.rs} (71%) diff --git a/src/common/exec.rs b/src/common/exec.rs index ebe87e8279..ef006c9d84 100644 --- a/src/common/exec.rs +++ b/src/common/exec.rs @@ -3,17 +3,8 @@ use std::future::Future; use std::pin::Pin; use std::sync::Arc; -#[cfg(feature = "server")] -use crate::body::Body; -#[cfg(all(feature = "http2", feature = "server"))] -use crate::proto::h2::server::H2Stream; use crate::rt::Executor; -#[cfg(feature = "server")] -pub trait ConnStreamExec: Clone { - fn execute_h2stream(&mut self, fut: H2Stream); -} - pub(crate) type BoxSendFuture = Pin + Send>>; // Executor must be provided by the user @@ -44,31 +35,6 @@ impl fmt::Debug for Exec { } } -#[cfg(feature = "server")] -impl ConnStreamExec for Exec -where - H2Stream: Future + Send + 'static, - B: Body, -{ - fn execute_h2stream(&mut self, fut: H2Stream) { - self.execute(fut) - } -} - -// ==== impl Executor ===== - -#[cfg(feature = "server")] -impl ConnStreamExec for E -where - E: Executor> + Clone, - H2Stream: Future, - B: Body, -{ - fn execute_h2stream(&mut self, fut: H2Stream) { - self.execute(fut) - } -} - // If http2 is not enable, we just have a stub here, so that the trait bounds // that *would* have been needed are still checked. Why? // diff --git a/src/proto/h2/server.rs b/src/proto/h2/server.rs index 2ec7601a6e..a5bd75f92c 100644 --- a/src/proto/h2/server.rs +++ b/src/proto/h2/server.rs @@ -13,7 +13,7 @@ use tracing::{debug, trace, warn}; use super::{ping, PipeToSendStream, SendBuf}; use crate::body::{Body, Incoming as IncomingBody}; -use crate::common::exec::ConnStreamExec; +use crate::rt::bounds::Http2ConnExec; use crate::common::time::Time; use crate::common::{date, task, Future, Pin, Poll}; use crate::ext::Protocol; @@ -110,7 +110,7 @@ where S: HttpService, S::Error: Into>, B: Body + 'static, - E: ConnStreamExec, + E: Http2ConnExec, { pub(crate) fn new( io: T, @@ -186,7 +186,7 @@ where S: HttpService, S::Error: Into>, B: Body + 'static, - E: ConnStreamExec, + E: Http2ConnExec, { type Output = crate::Result; @@ -240,7 +240,7 @@ where where S: HttpService, S::Error: Into>, - E: ConnStreamExec, + E: Http2ConnExec, { if self.closing.is_none() { loop { diff --git a/src/rt/bounds.rs b/src/rt/bounds.rs new file mode 100644 index 0000000000..69115ef2ca --- /dev/null +++ b/src/rt/bounds.rs @@ -0,0 +1,69 @@ +//! Trait aliases +//! +//! Traits in this module ease setting bounds and usually automatically +//! implemented by implementing another trait. + +#[cfg(all(feature = "server", feature = "http2"))] +pub use self::h2::Http2ConnExec; + +#[cfg(all(feature = "server", feature = "http2"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "server", feature = "http2"))))] +mod h2 { + use crate::{common::exec::Exec, proto::h2::server::H2Stream, rt::Executor}; + use http_body::Body; + use std::future::Future; + + /// An executor to spawn http2 connections. + /// + /// This trait is implemented for any type that implements [`Executor`] + /// trait for any future. + /// + /// This trait is sealed and cannot be implemented for types outside this crate. + /// + /// [`Executor`]: crate::rt::Executor + pub trait Http2ConnExec: sealed::Sealed<(F, B)> + Clone { + #[doc(hidden)] + fn execute_h2stream(&mut self, fut: H2Stream); + } + + impl Http2ConnExec for Exec + where + H2Stream: Future + Send + 'static, + B: Body, + { + fn execute_h2stream(&mut self, fut: H2Stream) { + self.execute(fut) + } + } + + impl sealed::Sealed<(F, B)> for Exec + where + H2Stream: Future + Send + 'static, + B: Body, + { + } + + #[doc(hidden)] + impl Http2ConnExec for E + where + E: Executor> + Clone, + H2Stream: Future, + B: Body, + { + fn execute_h2stream(&mut self, fut: H2Stream) { + self.execute(fut) + } + } + + impl sealed::Sealed<(F, B)> for E + where + E: Executor> + Clone, + H2Stream: Future, + B: Body, + { + } + + mod sealed { + pub trait Sealed {} + } +} diff --git a/src/rt.rs b/src/rt/mod.rs similarity index 71% rename from src/rt.rs rename to src/rt/mod.rs index accf0288d8..db03edc260 100644 --- a/src/rt.rs +++ b/src/rt/mod.rs @@ -5,6 +5,8 @@ //! If the `runtime` feature is disabled, the types in this module can be used //! to plug in other runtimes. +pub mod bounds; + use std::{ future::Future, pin::Pin, @@ -12,6 +14,26 @@ use std::{ }; /// An executor of futures. +/// +/// This trait should be implemented for any future. +/// +/// # Example +/// +/// ``` +/// # use hyper::rt::Executor; +/// # use std::future::Future; +/// struct TokioExecutor; +/// +/// impl Executor for TokioExecutor +/// where +/// F: Future + Send + 'static, +/// F::Output: Send + 'static, +/// { +/// fn execute(&self, future: F) { +/// tokio::spawn(future); +/// } +/// } +/// ``` pub trait Executor { /// Place the future into the executor to be run. fn execute(&self, fut: Fut); diff --git a/src/server/conn/http2.rs b/src/server/conn/http2.rs index 50cc12ccf9..45e0760956 100644 --- a/src/server/conn/http2.rs +++ b/src/server/conn/http2.rs @@ -9,9 +9,9 @@ use pin_project_lite::pin_project; use tokio::io::{AsyncRead, AsyncWrite}; use crate::body::{Body, Incoming as IncomingBody}; -use crate::common::exec::ConnStreamExec; use crate::common::{task, Future, Pin, Poll, Unpin}; use crate::proto; +use crate::rt::bounds::Http2ConnExec; use crate::service::HttpService; use crate::{common::time::Time, rt::Timer}; @@ -54,7 +54,7 @@ where I: AsyncRead + AsyncWrite + Unpin, B: Body + 'static, B::Error: Into>, - E: ConnStreamExec, + E: Http2ConnExec, { /// Start a graceful shutdown process for this connection. /// @@ -78,7 +78,7 @@ where I: AsyncRead + AsyncWrite + Unpin + 'static, B: Body + 'static, B::Error: Into>, - E: ConnStreamExec, + E: Http2ConnExec, { type Output = crate::Result<()>; @@ -99,7 +99,10 @@ where impl Builder { /// Create a new connection builder. /// - /// This starts with the default options, and an executor. + /// This starts with the default options, and an executor which is a type + /// that implements [`Http2ConnExec`] trait. + /// + /// [`Http2ConnExec`]: crate::rt::bounds::Http2ConnExec pub fn new(exec: E) -> Self { Self { exec: exec, @@ -259,7 +262,7 @@ impl Builder { Bd: Body + 'static, Bd::Error: Into>, I: AsyncRead + AsyncWrite + Unpin, - E: ConnStreamExec, + E: Http2ConnExec, { let proto = proto::h2::Server::new( io, diff --git a/src/service/mod.rs b/src/service/mod.rs index d149acf063..a3a3763430 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -26,7 +26,7 @@ mod service; mod util; #[cfg(all(any(feature = "http1", feature = "http2"), feature = "server"))] -pub(super) use self::http::HttpService; +pub use self::http::HttpService; #[cfg(all( any(feature = "http1", feature = "http2"), any(feature = "server", feature = "client")