mirror of
https://github.com/tower-rs/tower.git
synced 2025-09-27 13:00:43 +00:00
builder,util: add convenience methods for boxing services (#616)
* builder,util: add convenience methods for boxing services This adds a couple of new methods to `ServiceBuilder` and `ServiceExt`: - `ServiceBuilder::boxed` - `ServiceExt::boxed` - `ServiceBuilder::clone_boxed` - `ServiceExt::clone_boxed` They apply `BoxService::layer` and `CloneBoxService::layer` respectively. * fix doc links * add missing `cfg`s * Update tower/CHANGELOG.md Co-authored-by: Eliza Weisman <eliza@buoyant.io> * Apply suggestions from code review Co-authored-by: Eliza Weisman <eliza@buoyant.io> * not sure why rustdoc cannot infer these * line breaks * trailing whitespace * make docs a bit more consistent * fix doc links * update tokio * don't pull in old version of tower * Don't run `cargo deny check bans` as it hangs Co-authored-by: Eliza Weisman <eliza@buoyant.io>
This commit is contained in:
parent
48f8ae90a4
commit
4d80f7ed90
2
.github/workflows/CI.yml
vendored
2
.github/workflows/CI.yml
vendored
@ -96,3 +96,5 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
with:
|
||||
command: check advisories licenses sources
|
||||
|
@ -9,7 +9,7 @@ edition = "2018"
|
||||
[dev-dependencies]
|
||||
tower = { version = "0.4", path = "../tower", features = ["full"] }
|
||||
tower-service = "0.3"
|
||||
tokio = { version = "0.3", features = ["full"] }
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
rand = "0.8"
|
||||
pin-project = "1.0"
|
||||
futures = "0.3"
|
||||
|
@ -24,5 +24,5 @@ edition = "2018"
|
||||
[dependencies]
|
||||
|
||||
[dev-dependencies]
|
||||
tower-service = { version = "0.3.0" }
|
||||
tower = { version = "0.3" }
|
||||
tower-service = { version = "0.3.0", path = "../tower-service" }
|
||||
tower = { version = "0.4", path = "../tower" }
|
||||
|
@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
# Unreleased
|
||||
|
||||
- **util**: Add `CloneBoxService` which is a `Clone + Send` boxed `Service`.
|
||||
- **util:** Add `ServiceExt::boxed` and `ServiceExt::clone_boxed` for applying the
|
||||
`BoxService` and `CloneBoxService` middleware.
|
||||
- **builder:** Add `ServiceBuilder::boxed` and `ServiceBuilder::clone_boxed` for
|
||||
applying `BoxService` and `CloneBoxService` layers.
|
||||
- **util**: Remove unnecessary `Debug` bounds from `impl Debug for BoxService`.
|
||||
- **util**: Remove unnecessary `Debug` bounds from `impl Debug for UnsyncBoxService`.
|
||||
|
||||
|
@ -688,6 +688,126 @@ impl<L> ServiceBuilder<L> {
|
||||
{
|
||||
self
|
||||
}
|
||||
|
||||
/// This wraps the inner service with the [`Layer`] returned by [`BoxService::layer()`].
|
||||
///
|
||||
/// See that method for more details.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tower::{Service, ServiceBuilder, BoxError, util::BoxService};
|
||||
/// use std::time::Duration;
|
||||
/// #
|
||||
/// # struct Request;
|
||||
/// # struct Response;
|
||||
/// # impl Response {
|
||||
/// # fn new() -> Self { Self }
|
||||
/// # }
|
||||
///
|
||||
/// let service: BoxService<Request, Response, BoxError> = ServiceBuilder::new()
|
||||
/// .boxed()
|
||||
/// .load_shed()
|
||||
/// .concurrency_limit(64)
|
||||
/// .timeout(Duration::from_secs(10))
|
||||
/// .service_fn(|req: Request| async {
|
||||
/// Ok::<_, BoxError>(Response::new())
|
||||
/// });
|
||||
/// # let service = assert_service(service);
|
||||
/// # fn assert_service<S, R>(svc: S) -> S
|
||||
/// # where S: Service<R> { svc }
|
||||
/// ```
|
||||
///
|
||||
/// [`BoxService::layer()`]: crate::util::BoxService::layer()
|
||||
#[cfg(feature = "util")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "util")))]
|
||||
pub fn boxed<S, R>(
|
||||
self,
|
||||
) -> ServiceBuilder<
|
||||
Stack<
|
||||
tower_layer::LayerFn<
|
||||
fn(
|
||||
L::Service,
|
||||
) -> crate::util::BoxService<
|
||||
R,
|
||||
<L::Service as Service<R>>::Response,
|
||||
<L::Service as Service<R>>::Error,
|
||||
>,
|
||||
>,
|
||||
L,
|
||||
>,
|
||||
>
|
||||
where
|
||||
L: Layer<S>,
|
||||
L::Service: Service<R> + Send + 'static,
|
||||
<L::Service as Service<R>>::Future: Send + 'static,
|
||||
{
|
||||
self.layer(crate::util::BoxService::layer())
|
||||
}
|
||||
|
||||
/// This wraps the inner service with the [`Layer`] returned by [`CloneBoxService::layer()`].
|
||||
///
|
||||
/// This is similar to the [`boxed`] method, but it requires that `Self` implement
|
||||
/// [`Clone`], and the returned boxed service implements [`Clone`].
|
||||
///
|
||||
/// See [`CloneBoxService`] for more details.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tower::{Service, ServiceBuilder, BoxError, util::CloneBoxService};
|
||||
/// use std::time::Duration;
|
||||
/// #
|
||||
/// # struct Request;
|
||||
/// # struct Response;
|
||||
/// # impl Response {
|
||||
/// # fn new() -> Self { Self }
|
||||
/// # }
|
||||
///
|
||||
/// let service: CloneBoxService<Request, Response, BoxError> = ServiceBuilder::new()
|
||||
/// .clone_boxed()
|
||||
/// .load_shed()
|
||||
/// .concurrency_limit(64)
|
||||
/// .timeout(Duration::from_secs(10))
|
||||
/// .service_fn(|req: Request| async {
|
||||
/// Ok::<_, BoxError>(Response::new())
|
||||
/// });
|
||||
/// # let service = assert_service(service);
|
||||
///
|
||||
/// // The boxed service can still be cloned.
|
||||
/// service.clone();
|
||||
/// # fn assert_service<S, R>(svc: S) -> S
|
||||
/// # where S: Service<R> { svc }
|
||||
/// ```
|
||||
///
|
||||
/// [`CloneBoxService::layer()`]: crate::util::CloneBoxService::layer()
|
||||
/// [`CloneBoxService`]: crate::util::CloneBoxService
|
||||
/// [`boxed`]: Self::boxed
|
||||
#[cfg(feature = "util")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "util")))]
|
||||
pub fn clone_boxed<S, R>(
|
||||
self,
|
||||
) -> ServiceBuilder<
|
||||
Stack<
|
||||
tower_layer::LayerFn<
|
||||
fn(
|
||||
L::Service,
|
||||
) -> crate::util::CloneBoxService<
|
||||
R,
|
||||
<L::Service as Service<R>>::Response,
|
||||
<L::Service as Service<R>>::Error,
|
||||
>,
|
||||
>,
|
||||
L,
|
||||
>,
|
||||
>
|
||||
where
|
||||
L: Layer<S>,
|
||||
L::Service: Service<R> + Clone + Send + 'static,
|
||||
<L::Service as Service<R>>::Future: Send + 'static,
|
||||
{
|
||||
self.layer(crate::util::CloneBoxService::layer())
|
||||
}
|
||||
}
|
||||
|
||||
impl<L: fmt::Debug> fmt::Debug for ServiceBuilder<L> {
|
||||
|
@ -953,6 +953,103 @@ pub trait ServiceExt<Request>: tower_service::Service<Request> {
|
||||
{
|
||||
MapFuture::new(self, f)
|
||||
}
|
||||
|
||||
/// Convert the service into a [`Service`] + [`Send`] trait object.
|
||||
///
|
||||
/// See [`BoxService`] for more details.
|
||||
///
|
||||
/// If `Self` implements the [`Clone`] trait, the [`clone_boxed`] method
|
||||
/// can be used instead, to produce a boxed service which will also
|
||||
/// implement [`Clone`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tower::{Service, ServiceExt, BoxError, service_fn, util::BoxService};
|
||||
/// #
|
||||
/// # struct Request;
|
||||
/// # struct Response;
|
||||
/// # impl Response {
|
||||
/// # fn new() -> Self { Self }
|
||||
/// # }
|
||||
///
|
||||
/// let service = service_fn(|req: Request| async {
|
||||
/// Ok::<_, BoxError>(Response::new())
|
||||
/// });
|
||||
///
|
||||
/// let service: BoxService<Request, Response, BoxError> = service
|
||||
/// .map_request(|req| {
|
||||
/// println!("received request");
|
||||
/// req
|
||||
/// })
|
||||
/// .map_response(|res| {
|
||||
/// println!("response produced");
|
||||
/// res
|
||||
/// })
|
||||
/// .boxed();
|
||||
/// # let service = assert_service(service);
|
||||
/// # fn assert_service<S, R>(svc: S) -> S
|
||||
/// # where S: Service<R> { svc }
|
||||
/// ```
|
||||
///
|
||||
/// [`Service`]: crate::Service
|
||||
/// [`clone_boxed`]: Self::clone_boxed
|
||||
fn boxed(self) -> BoxService<Request, Self::Response, Self::Error>
|
||||
where
|
||||
Self: Sized + Send + 'static,
|
||||
Self::Future: Send + 'static,
|
||||
{
|
||||
BoxService::new(self)
|
||||
}
|
||||
|
||||
/// Convert the service into a [`Service`] + [`Clone`] + [`Send`] trait object.
|
||||
///
|
||||
/// This is similar to the [`boxed`] method, but it requires that `Self` implement
|
||||
/// [`Clone`], and the returned boxed service implements [`Clone`].
|
||||
/// See [`CloneBoxService`] for more details.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tower::{Service, ServiceExt, BoxError, service_fn, util::CloneBoxService};
|
||||
/// #
|
||||
/// # struct Request;
|
||||
/// # struct Response;
|
||||
/// # impl Response {
|
||||
/// # fn new() -> Self { Self }
|
||||
/// # }
|
||||
///
|
||||
/// let service = service_fn(|req: Request| async {
|
||||
/// Ok::<_, BoxError>(Response::new())
|
||||
/// });
|
||||
///
|
||||
/// let service: CloneBoxService<Request, Response, BoxError> = service
|
||||
/// .map_request(|req| {
|
||||
/// println!("received request");
|
||||
/// req
|
||||
/// })
|
||||
/// .map_response(|res| {
|
||||
/// println!("response produced");
|
||||
/// res
|
||||
/// })
|
||||
/// .clone_boxed();
|
||||
///
|
||||
/// // The boxed service can still be cloned.
|
||||
/// service.clone();
|
||||
/// # let service = assert_service(service);
|
||||
/// # fn assert_service<S, R>(svc: S) -> S
|
||||
/// # where S: Service<R> { svc }
|
||||
/// ```
|
||||
///
|
||||
/// [`Service`]: crate::Service
|
||||
/// [`boxed`]: Self::boxed
|
||||
fn clone_boxed(self) -> CloneBoxService<Request, Self::Response, Self::Error>
|
||||
where
|
||||
Self: Clone + Sized + Send + 'static,
|
||||
Self::Future: Send + 'static,
|
||||
{
|
||||
CloneBoxService::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized, Request> ServiceExt<Request> for T where T: tower_service::Service<Request> {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user