diff --git a/tower/CHANGELOG.md b/tower/CHANGELOG.md index 672d9b19..0f3ae813 100644 --- a/tower/CHANGELOG.md +++ b/tower/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **util**: Add `option_layer` to convert an `Option` into a `Layer`. ([#555]) - **builder**: Add `ServiceBuilder::option_layer` to optionally add a layer. ([#555]) - **steer**: `Steer` now implements `Clone +- **make**: Added `Shared` which lets you implement `MakeService` by cloning a + service. [#542]: https://github.com/tower-rs/tower/pull/542 [#555]: https://github.com/tower-rs/tower/pull/555 @@ -34,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#532]: https://github.com/tower-rs/tower/pull/532 [#535]: https://github.com/tower-rs/tower/pull/535 [#538]: https://github.com/tower-rs/tower/pull/538 +>>>>>>> master # 0.4.3 (January 13, 2021) diff --git a/tower/Cargo.toml b/tower/Cargo.toml index f9f8b33f..fa35c402 100644 --- a/tower/Cargo.toml +++ b/tower/Cargo.toml @@ -52,7 +52,7 @@ hedge = ["util", "filter", "futures-util", "hdrhistogram", "tokio/time", "tracin limit = ["tokio/time", "tokio/sync", "tokio-util", "tracing"] load = ["tokio/time", "tracing"] load-shed = [] -make = ["tokio/io-std"] +make = ["tokio/io-std", "futures-util"] ready-cache = ["futures-util", "indexmap", "tokio/sync", "tracing"] reconnect = ["make", "tokio/io-std", "tracing"] retry = ["tokio/time"] diff --git a/tower/src/macros.rs b/tower/src/macros.rs index 29c27b10..e57c522d 100644 --- a/tower/src/macros.rs +++ b/tower/src/macros.rs @@ -1,4 +1,9 @@ -#[cfg(any(feature = "util", feature = "spawn-ready", feature = "filter"))] +#[cfg(any( + feature = "util", + feature = "spawn-ready", + feature = "filter", + feature = "make" +))] macro_rules! opaque_future { ($(#[$m:meta])* pub type $name:ident<$($param:ident),+> = $actual:ty;) => { #[pin_project::pin_project] diff --git a/tower/src/make/make_service.rs b/tower/src/make/make_service.rs index 213a4817..aa519d68 100644 --- a/tower/src/make/make_service.rs +++ b/tower/src/make/make_service.rs @@ -7,6 +7,8 @@ use std::marker::PhantomData; use std::task::{Context, Poll}; use tower_service::Service; +pub(crate) mod shared; + /// Creates new [`Service`] values. /// /// Acts as a service factory. This is useful for cases where new [`Service`] diff --git a/tower/src/make/make_service/shared.rs b/tower/src/make/make_service/shared.rs new file mode 100644 index 00000000..6c427fe7 --- /dev/null +++ b/tower/src/make/make_service/shared.rs @@ -0,0 +1,146 @@ +use std::convert::Infallible; +use std::task::{Context, Poll}; +use tower_service::Service; + +/// A [`MakeService`] that produces services by cloning an inner service. +/// +/// [`MakeService`]: super::MakeService +/// +/// # Example +/// +/// ``` +/// # use std::task::{Context, Poll}; +/// # use std::pin::Pin; +/// # use std::convert::Infallible; +/// use tower::make::{MakeService, Shared}; +/// use tower::buffer::Buffer; +/// use tower::Service; +/// use futures::future::{Ready, ready}; +/// +/// // An example connection type +/// struct Connection {} +/// +/// // An example request type +/// struct Request {} +/// +/// // An example response type +/// struct Response {} +/// +/// // Some service that doesn't implement `Clone` +/// struct MyService; +/// +/// impl Service for MyService { +/// type Response = Response; +/// type Error = Infallible; +/// type Future = Ready>; +/// +/// fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { +/// Poll::Ready(Ok(())) +/// } +/// +/// fn call(&mut self, req: Request) -> Self::Future { +/// ready(Ok(Response {})) +/// } +/// } +/// +/// // Example function that runs a service by accepting new connections and using +/// // `Make` to create new services that might be bound to the connection. +/// // +/// // This is similar to what you might find in hyper. +/// async fn serve_make_service(make: Make) +/// where +/// Make: MakeService +/// { +/// // ... +/// } +/// +/// # async { +/// // Our service +/// let svc = MyService; +/// +/// // Make it `Clone` by putting a channel in front +/// let buffered = Buffer::new(svc, 1024); +/// +/// // Convert it into a `MakeService` +/// let make = Shared::new(buffered); +/// +/// // Run the service and just ignore the `Connection`s as `MyService` doesn't need them +/// serve_make_service(make).await; +/// # }; +/// ``` +#[derive(Debug, Clone, Copy)] +pub struct Shared { + service: S, +} + +impl Shared { + /// Create a new [`Shared`] from a service. + pub fn new(service: S) -> Self { + Self { service } + } +} + +impl Service for Shared +where + S: Clone, +{ + type Response = S; + type Error = Infallible; + type Future = SharedFuture; + + fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) + } + + fn call(&mut self, _target: T) -> Self::Future { + SharedFuture(futures_util::future::ready(Ok(self.service.clone()))) + } +} + +opaque_future! { + /// Response future from [`Shared`] services. + pub type SharedFuture = futures_util::future::Ready>; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::make::MakeService; + use crate::service_fn; + use futures::future::poll_fn; + + async fn echo(req: R) -> Result { + Ok(req) + } + + #[tokio::test] + async fn as_make_service() { + let mut shared = Shared::new(service_fn(echo::<&'static str>)); + + poll_fn(|cx| MakeService::<(), _>::poll_ready(&mut shared, cx)) + .await + .unwrap(); + let mut svc = shared.make_service(()).await.unwrap(); + + poll_fn(|cx| svc.poll_ready(cx)).await.unwrap(); + let res = svc.call("foo").await.unwrap(); + + assert_eq!(res, "foo"); + } + + #[tokio::test] + async fn as_make_service_into_service() { + let shared = Shared::new(service_fn(echo::<&'static str>)); + let mut shared = MakeService::<(), _>::into_service(shared); + + poll_fn(|cx| Service::<()>::poll_ready(&mut shared, cx)) + .await + .unwrap(); + let mut svc = shared.call(()).await.unwrap(); + + poll_fn(|cx| svc.poll_ready(cx)).await.unwrap(); + let res = svc.call("foo").await.unwrap(); + + assert_eq!(res, "foo"); + } +} diff --git a/tower/src/make/mod.rs b/tower/src/make/mod.rs index 9a922727..a377f2a2 100644 --- a/tower/src/make/mod.rs +++ b/tower/src/make/mod.rs @@ -1,8 +1,14 @@ //! Trait aliases for Services that produce specific types of Responses. mod make_connection; - mod make_service; pub use self::make_connection::MakeConnection; +pub use self::make_service::shared::Shared; pub use self::make_service::{AsService, IntoService, MakeService}; + +pub mod future { + //! Future types + + pub use super::make_service::shared::SharedFuture; +}