mirror of
https://github.com/tower-rs/tower.git
synced 2025-10-02 15:25:11 +00:00
make: Add Shared
(#533)
* make: Add `Shared` Fixes https://github.com/tower-rs/tower/issues/262 `Shared` is a `MakeService` that produces services by cloning an inner service. * Fix build with different set of features * Formatting * Make `Shared` generic over any target * Fix tests * Move `Shared` into its own file * Add example
This commit is contained in:
parent
e49700a79d
commit
f90f518f9f
@ -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<Layer>` 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)
|
||||
|
||||
|
@ -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"]
|
||||
|
@ -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]
|
||||
|
@ -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`]
|
||||
|
146
tower/src/make/make_service/shared.rs
Normal file
146
tower/src/make/make_service/shared.rs
Normal file
@ -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<Request> for MyService {
|
||||
/// type Response = Response;
|
||||
/// type Error = Infallible;
|
||||
/// type Future = Ready<Result<Response, Infallible>>;
|
||||
///
|
||||
/// fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
/// 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: Make)
|
||||
/// where
|
||||
/// Make: MakeService<Connection, Request>
|
||||
/// {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// # 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<S> {
|
||||
service: S,
|
||||
}
|
||||
|
||||
impl<S> Shared<S> {
|
||||
/// Create a new [`Shared`] from a service.
|
||||
pub fn new(service: S) -> Self {
|
||||
Self { service }
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> Service<T> for Shared<S>
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
type Response = S;
|
||||
type Error = Infallible;
|
||||
type Future = SharedFuture<S>;
|
||||
|
||||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
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<S> = futures_util::future::Ready<Result<S, Infallible>>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::make::MakeService;
|
||||
use crate::service_fn;
|
||||
use futures::future::poll_fn;
|
||||
|
||||
async fn echo<R>(req: R) -> Result<R, Infallible> {
|
||||
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");
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user