Initial introduction of tower-service-util (#193)
This commit is contained in:
parent
50fc5e8e63
commit
acda5a75b9
|
@ -13,6 +13,7 @@ members = [
|
|||
"tower-reconnect",
|
||||
"tower-retry",
|
||||
"tower-service",
|
||||
"tower-service-util",
|
||||
"tower-timeout",
|
||||
"tower-util",
|
||||
"tower-watch",
|
||||
|
|
|
@ -11,7 +11,7 @@ rand = "0.6"
|
|||
tokio-timer = "0.2.4"
|
||||
tower-service = "0.2.0"
|
||||
tower-discover = { version = "0.1", path = "../tower-discover" }
|
||||
tower-util = { version = "0.1", path = "../tower-util" }
|
||||
tower-service-util = { version = "0.1", path = "../tower-service-util" }
|
||||
indexmap = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -9,7 +9,7 @@ extern crate rand;
|
|||
extern crate tokio_timer;
|
||||
extern crate tower_discover;
|
||||
extern crate tower_service;
|
||||
extern crate tower_util;
|
||||
extern crate tower_service_util;
|
||||
|
||||
use futures::{Async, Future, Poll};
|
||||
use indexmap::IndexMap;
|
||||
|
|
|
@ -18,7 +18,7 @@ use super::{Balance, Choose};
|
|||
use futures::{Async, Future, Poll};
|
||||
use tower_discover::{Change, Discover};
|
||||
use tower_service::Service;
|
||||
use tower_util::MakeService;
|
||||
use tower_service_util::MakeService;
|
||||
|
||||
enum Load {
|
||||
/// Load is low -- remove a service instance.
|
||||
|
|
|
@ -11,4 +11,3 @@ tower-layer = { version = "0.1", path = "../tower-layer" }
|
|||
|
||||
[dev-dependencies]
|
||||
tower-mock = { version = "0.1", path = "../tower-mock" }
|
||||
tower-util = { version = "0.1", path = "../tower-util" }
|
||||
|
|
|
@ -2,7 +2,6 @@ extern crate futures;
|
|||
extern crate tower_filter;
|
||||
extern crate tower_mock;
|
||||
extern crate tower_service;
|
||||
extern crate tower_util;
|
||||
|
||||
use futures::*;
|
||||
use tower_filter::error::Error;
|
||||
|
|
|
@ -8,4 +8,4 @@ publish = false
|
|||
log = "0.4.1"
|
||||
futures = "0.1"
|
||||
tower-service = "0.2.0"
|
||||
tower-util = { version = "0.1", path = "../tower-util" }
|
||||
tower-service-util = { version = "0.1", path = "../tower-service-util" }
|
||||
|
|
|
@ -2,7 +2,7 @@ extern crate futures;
|
|||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate tower_service;
|
||||
extern crate tower_util;
|
||||
extern crate tower_service_util;
|
||||
|
||||
pub mod future;
|
||||
|
||||
|
@ -10,7 +10,7 @@ use crate::future::ResponseFuture;
|
|||
|
||||
use futures::{Async, Future, Poll};
|
||||
use tower_service::Service;
|
||||
use tower_util::MakeService;
|
||||
use tower_service_util::MakeService;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
[package]
|
||||
|
||||
name = "tower-service-util"
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update documentation URL
|
||||
# - Create "v0.x.y" git tag.
|
||||
version = "0.1.0"
|
||||
authors = ["Carl Lerche <me@carllerche.com>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/tower-rs/tower"
|
||||
homepage = "https://github.com/tower-rs/tower"
|
||||
documentation = "https://docs.rs/tokio-service-util/0.1.0"
|
||||
description = """
|
||||
Utilities for working with tower-service.
|
||||
"""
|
||||
categories = ["asynchronous", "network-programming"]
|
||||
|
||||
[features]
|
||||
io = ["tokio-io"]
|
||||
|
||||
[dependencies]
|
||||
either = { version = "1.5.1", optional = true }
|
||||
futures = "0.1.23"
|
||||
tower-service = "0.2.0"
|
||||
tokio-io = { version = "0.1.12", optional = true }
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2019 Tower Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,13 @@
|
|||
# Tower Service Util
|
||||
|
||||
Utilities for working with `tower-service`.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Tower by you, shall be licensed as MIT, without any additional
|
||||
terms or conditions.
|
|
@ -0,0 +1,62 @@
|
|||
//! Trait object `Service` instances
|
||||
//!
|
||||
//! Dynamically dispatched `Service` objects allow for erasing the underlying
|
||||
//! `Service` type and using the `Service` instances as opaque handles. This can
|
||||
//! be useful when the service instance cannot be explicitly named for whatever
|
||||
//! reason.
|
||||
//!
|
||||
//! There are two variants of service objects. `BoxService` requires both the
|
||||
//! service and the response future to be `Send`. These values can move freely
|
||||
//! across threads. `UnsyncBoxService` requires both the service and the
|
||||
//! response future to remain on the current thread. This is useful for
|
||||
//! representing services that are backed by `Rc` or other non-`Send` types.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate futures;
|
||||
//! # extern crate tower_service;
|
||||
//! # extern crate tower_service_util;
|
||||
//! # use futures::*;
|
||||
//! # use futures::future::FutureResult;
|
||||
//! # use tower_service::Service;
|
||||
//! # use tower_service_util::BoxService;
|
||||
//! // Respond to requests using a closure. Since closures cannot be named,
|
||||
//! // `ServiceFn` cannot be named either
|
||||
//! pub struct ServiceFn<F> {
|
||||
//! f: F,
|
||||
//! }
|
||||
//!
|
||||
//! impl<F> Service<String> for ServiceFn<F>
|
||||
//! where F: Fn(String) -> String,
|
||||
//! {
|
||||
//! type Response = String;
|
||||
//! type Error = ();
|
||||
//! type Future = FutureResult<String, ()>;
|
||||
//!
|
||||
//! fn poll_ready(&mut self) -> Poll<(), ()> {
|
||||
//! Ok(().into())
|
||||
//! }
|
||||
//!
|
||||
//! fn call(&mut self, request: String) -> FutureResult<String, ()> {
|
||||
//! future::ok((self.f)(request))
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! pub fn main() {
|
||||
//! let f = |mut request: String| {
|
||||
//! request.push_str(" response");
|
||||
//! request
|
||||
//! };
|
||||
//!
|
||||
//! let service: BoxService<String, String, ()> =
|
||||
//! BoxService::new(ServiceFn { f });
|
||||
//! # drop(service);
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
mod sync;
|
||||
mod unsync;
|
||||
|
||||
pub use self::sync::BoxService;
|
||||
pub use self::unsync::UnsyncBoxService;
|
|
@ -0,0 +1,80 @@
|
|||
use futures::{Future, Poll};
|
||||
use tower_service::Service;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// A boxed `Service + Send` trait object.
|
||||
///
|
||||
/// `BoxService` turns a service into a trait object, allowing the response
|
||||
/// future type to be dynamic. This type requires both the service and the
|
||||
/// response future to be `Send`.
|
||||
///
|
||||
/// See module level documentation for more details.
|
||||
pub struct BoxService<T, U, E> {
|
||||
inner: Box<Service<T, Response = U, Error = E, Future = BoxFuture<U, E>> + Send>,
|
||||
}
|
||||
|
||||
/// A boxed `Future + Send` trait object.
|
||||
///
|
||||
/// This type alias represents a boxed future that is `Send` and can be moved
|
||||
/// across threads.
|
||||
type BoxFuture<T, E> = Box<Future<Item = T, Error = E> + Send>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Boxed<S> {
|
||||
inner: S,
|
||||
}
|
||||
|
||||
impl<T, U, E> BoxService<T, U, E> {
|
||||
pub fn new<S>(inner: S) -> Self
|
||||
where
|
||||
S: Service<T, Response = U, Error = E> + Send + 'static,
|
||||
S::Future: Send + 'static,
|
||||
{
|
||||
let inner = Box::new(Boxed { inner });
|
||||
BoxService { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, E> Service<T> for BoxService<T, U, E> {
|
||||
type Response = U;
|
||||
type Error = E;
|
||||
type Future = BoxFuture<U, E>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), E> {
|
||||
self.inner.poll_ready()
|
||||
}
|
||||
|
||||
fn call(&mut self, request: T) -> BoxFuture<U, E> {
|
||||
self.inner.call(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, E> fmt::Debug for BoxService<T, U, E>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
E: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("BoxService").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, Request> Service<Request> for Boxed<S>
|
||||
where
|
||||
S: Service<Request> + 'static,
|
||||
S::Future: Send + 'static,
|
||||
{
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = Box<Future<Item = S::Response, Error = S::Error> + Send>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
self.inner.poll_ready()
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Request) -> Self::Future {
|
||||
Box::new(self.inner.call(request))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
use futures::{Future, Poll};
|
||||
use tower_service::Service;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// A boxed `Service` trait object.
|
||||
pub struct UnsyncBoxService<T, U, E> {
|
||||
inner: Box<Service<T, Response = U, Error = E, Future = UnsyncBoxFuture<U, E>>>,
|
||||
}
|
||||
|
||||
/// A boxed `Future` trait object.
|
||||
///
|
||||
/// This type alias represents a boxed future that is *not* `Send` and must
|
||||
/// remain on the current thread.
|
||||
type UnsyncBoxFuture<T, E> = Box<Future<Item = T, Error = E>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct UnsyncBoxed<S> {
|
||||
inner: S,
|
||||
}
|
||||
|
||||
impl<T, U, E> UnsyncBoxService<T, U, E> {
|
||||
pub fn new<S>(inner: S) -> Self
|
||||
where
|
||||
S: Service<T, Response = U, Error = E> + 'static,
|
||||
S::Future: 'static,
|
||||
{
|
||||
let inner = Box::new(UnsyncBoxed { inner });
|
||||
UnsyncBoxService { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, E> Service<T> for UnsyncBoxService<T, U, E> {
|
||||
type Response = U;
|
||||
type Error = E;
|
||||
type Future = UnsyncBoxFuture<U, E>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), E> {
|
||||
self.inner.poll_ready()
|
||||
}
|
||||
|
||||
fn call(&mut self, request: T) -> UnsyncBoxFuture<U, E> {
|
||||
self.inner.call(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, E> fmt::Debug for UnsyncBoxService<T, U, E>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
E: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("UnsyncBoxService").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, Request> Service<Request> for UnsyncBoxed<S>
|
||||
where
|
||||
S: Service<Request> + 'static,
|
||||
S::Future: 'static,
|
||||
{
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = Box<Future<Item = S::Response, Error = S::Error>>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
self.inner.poll_ready()
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Request) -> Self::Future {
|
||||
Box::new(self.inner.call(request))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
use super::Error;
|
||||
use _either::Either;
|
||||
use futures::{Future, Poll};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ResponseFuture<A, B> {
|
||||
inner: Either<A, B>,
|
||||
}
|
||||
|
||||
impl<A, B> ResponseFuture<A, B> {
|
||||
pub(crate) fn new_left(left: A) -> ResponseFuture<A, B> {
|
||||
ResponseFuture {
|
||||
inner: Either::Left(left),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_right(right: B) -> ResponseFuture<A, B> {
|
||||
ResponseFuture {
|
||||
inner: Either::Right(right),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> Future for ResponseFuture<A, B>
|
||||
where
|
||||
A: Future,
|
||||
A::Error: Into<Error>,
|
||||
B: Future<Item = A::Item>,
|
||||
B::Error: Into<Error>,
|
||||
{
|
||||
type Item = A::Item;
|
||||
type Error = Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
use self::Either::*;
|
||||
|
||||
match &mut self.inner {
|
||||
Left(fut) => fut.poll().map_err(Into::into),
|
||||
Right(fut) => fut.poll().map_err(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
//! Contains `EitherService` and related types and functions.
|
||||
//!
|
||||
//! See `EitherService` documentation for more details.
|
||||
|
||||
pub mod future;
|
||||
|
||||
use self::future::ResponseFuture;
|
||||
use _either::Either;
|
||||
use futures::Poll;
|
||||
use tower_service::Service;
|
||||
|
||||
/// Combine two different service types into a single type.
|
||||
///
|
||||
/// Both services must be of the same request, response, and error types.
|
||||
/// `EitherService` is useful for handling conditional branching in service
|
||||
/// middleware to different inner service types.
|
||||
pub struct EitherService<A, B> {
|
||||
inner: Either<A, B>,
|
||||
}
|
||||
|
||||
type Error = Box<::std::error::Error + Send + Sync>;
|
||||
|
||||
impl<A, B> EitherService<A, B> {
|
||||
pub fn new<Request>(inner: Either<A, B>) -> EitherService<A, B>
|
||||
where
|
||||
A: Service<Request>,
|
||||
A::Error: Into<Error>,
|
||||
B: Service<Request, Response = A::Response>,
|
||||
B::Error: Into<Error>,
|
||||
{
|
||||
EitherService { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, Request> Service<Request> for EitherService<A, B>
|
||||
where
|
||||
A: Service<Request>,
|
||||
A::Error: Into<Error>,
|
||||
B: Service<Request, Response = A::Response>,
|
||||
B::Error: Into<Error>,
|
||||
{
|
||||
type Response = A::Response;
|
||||
type Error = Error;
|
||||
type Future = ResponseFuture<A::Future, B::Future>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
use self::Either::*;
|
||||
|
||||
match &mut self.inner {
|
||||
Left(service) => service.poll_ready().map_err(Into::into),
|
||||
Right(service) => service.poll_ready().map_err(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Request) -> Self::Future {
|
||||
use self::Either::*;
|
||||
|
||||
match &mut self.inner {
|
||||
Left(service) => ResponseFuture::new_left(service.call(request)),
|
||||
Right(service) => ResponseFuture::new_right(service.call(request)),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
//! Various utility types and functions that are generally with Tower.
|
||||
|
||||
#[cfg(feature = "either")]
|
||||
extern crate either as _either;
|
||||
extern crate futures;
|
||||
#[cfg(feature = "io")]
|
||||
extern crate tokio_io;
|
||||
extern crate tower_service;
|
||||
|
||||
mod boxed;
|
||||
#[cfg(feature = "either")]
|
||||
mod either;
|
||||
#[cfg(feature = "io")]
|
||||
mod make_connection;
|
||||
mod make_service;
|
||||
mod option;
|
||||
mod sealed;
|
||||
mod service_fn;
|
||||
|
||||
pub use crate::boxed::{BoxService, UnsyncBoxService};
|
||||
#[cfg(feature = "either")]
|
||||
pub use crate::either::EitherService;
|
||||
#[cfg(feature = "io")]
|
||||
pub use crate::make_connection::MakeConnection;
|
||||
pub use crate::make_service::MakeService;
|
||||
pub use crate::option::OptionService;
|
||||
pub use crate::service_fn::ServiceFn;
|
||||
|
||||
pub mod error {
|
||||
//! Error types
|
||||
|
||||
pub use crate::option::error as option;
|
||||
}
|
||||
|
||||
pub mod future {
|
||||
//! Future types
|
||||
|
||||
#[cfg(feature = "either")]
|
||||
pub use crate::either::future as either;
|
||||
pub use crate::option::future as option;
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use crate::sealed::Sealed;
|
||||
use futures::Future;
|
||||
use tokio_io::{AsyncRead, AsyncWrite};
|
||||
use tower_service::Service;
|
||||
|
@ -7,7 +8,7 @@ use tower_service::Service;
|
|||
/// The goal of this service is to allow composable methods for creating
|
||||
/// `AsyncRead + AsyncWrite` transports. This could mean creating a TLS
|
||||
/// based connection or using some other method to authenticate the connection.
|
||||
pub trait MakeConnection<Request> {
|
||||
pub trait MakeConnection<Request>: Sealed<(Request,)> {
|
||||
/// The transport provided by this service
|
||||
type Response: AsyncRead + AsyncWrite;
|
||||
|
||||
|
@ -21,7 +22,7 @@ pub trait MakeConnection<Request> {
|
|||
fn make_connection(&mut self, target: Request) -> Self::Future;
|
||||
}
|
||||
|
||||
impl<S, Request> self::sealed::Sealed<Request> for S where S: Service<Request> {}
|
||||
impl<S, Request> Sealed<(Request,)> for S where S: Service<Request> {}
|
||||
|
||||
impl<C, Request> MakeConnection<Request> for C
|
||||
where
|
||||
|
@ -36,7 +37,3 @@ where
|
|||
Service::call(self, target)
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed<A> {}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use crate::sealed::Sealed;
|
||||
use futures::{Future, Poll};
|
||||
use tower_service::Service;
|
||||
|
||||
|
@ -10,7 +11,7 @@ use tower_service::Service;
|
|||
/// requests on that new TCP stream.
|
||||
///
|
||||
/// This is essentially a trait alias for a `Service` of `Service`s.
|
||||
pub trait MakeService<Target, Request>: self::sealed::Sealed<Target, Request> {
|
||||
pub trait MakeService<Target, Request>: Sealed<(Target, Request)> {
|
||||
/// Responses given by the service
|
||||
type Response;
|
||||
|
||||
|
@ -41,7 +42,7 @@ pub trait MakeService<Target, Request>: self::sealed::Sealed<Target, Request> {
|
|||
fn make_service(&mut self, target: Target) -> Self::Future;
|
||||
}
|
||||
|
||||
impl<M, S, Target, Request> self::sealed::Sealed<Target, Request> for M
|
||||
impl<M, S, Target, Request> Sealed<(Target, Request)> for M
|
||||
where
|
||||
M: Service<Target, Response = S>,
|
||||
S: Service<Request>,
|
||||
|
@ -67,7 +68,3 @@ where
|
|||
Service::call(self, target)
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed<A, B> {}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
use std::error;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct None(());
|
||||
|
||||
pub(crate) type Error = Box<error::Error + Send + Sync>;
|
||||
|
||||
impl None {
|
||||
pub(crate) fn new() -> None {
|
||||
None(())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for None {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(fmt, "None")
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for None {}
|
|
@ -0,0 +1,29 @@
|
|||
use super::{error, Error};
|
||||
use futures::{Future, Poll};
|
||||
|
||||
/// Response future returned by `OptionService`.
|
||||
pub struct ResponseFuture<T> {
|
||||
inner: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> ResponseFuture<T> {
|
||||
pub(crate) fn new(inner: Option<T>) -> ResponseFuture<T> {
|
||||
ResponseFuture { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Future for ResponseFuture<T>
|
||||
where
|
||||
T: Future,
|
||||
T::Error: Into<Error>,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
match self.inner {
|
||||
Some(ref mut inner) => inner.poll().map_err(Into::into),
|
||||
None => Err(error::None::new().into()),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
//! Contains `OptionService` and related types and functions.
|
||||
//!
|
||||
//! See `OptionService` documentation for more details.
|
||||
//!
|
||||
|
||||
pub mod error;
|
||||
pub mod future;
|
||||
|
||||
use self::error::Error;
|
||||
use self::future::ResponseFuture;
|
||||
use futures::Poll;
|
||||
use tower_service::Service;
|
||||
|
||||
/// Optionally forwards requests to an inner service.
|
||||
///
|
||||
/// If the inner service is `None`, `Error::None` is returned as the response.
|
||||
pub struct OptionService<T> {
|
||||
inner: Option<T>,
|
||||
}
|
||||
|
||||
// ===== impl OptionService =====
|
||||
|
||||
impl<T> OptionService<T> {
|
||||
/// Create a new `OptionService`
|
||||
pub fn new<Request>(inner: Option<T>) -> OptionService<T>
|
||||
where
|
||||
T: Service<Request>,
|
||||
T::Error: Into<Error>,
|
||||
{
|
||||
OptionService { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Request> Service<Request> for OptionService<T>
|
||||
where
|
||||
T: Service<Request>,
|
||||
T::Error: Into<Error>,
|
||||
{
|
||||
type Response = T::Response;
|
||||
type Error = Error;
|
||||
type Future = ResponseFuture<T::Future>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
match self.inner {
|
||||
Some(ref mut inner) => inner.poll_ready().map_err(Into::into),
|
||||
// None services are always ready
|
||||
None => Ok(().into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Request) -> Self::Future {
|
||||
let inner = self.inner.as_mut().map(|i| i.call(request));
|
||||
ResponseFuture::new(inner)
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub trait Sealed<T> {}
|
|
@ -0,0 +1,32 @@
|
|||
use futures::{IntoFuture, Poll};
|
||||
use tower_service::Service;
|
||||
|
||||
/// A `Service` implemented by a closure.
|
||||
pub struct ServiceFn<T> {
|
||||
f: T,
|
||||
}
|
||||
|
||||
impl<T> ServiceFn<T> {
|
||||
/// Returns a new `NewServiceFn` with the given closure.
|
||||
pub fn new(f: T) -> Self {
|
||||
ServiceFn { f }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, F, Request> Service<Request> for ServiceFn<T>
|
||||
where
|
||||
T: Fn(Request) -> F,
|
||||
F: IntoFuture,
|
||||
{
|
||||
type Response = F::Item;
|
||||
type Error = F::Error;
|
||||
type Future = F::Future;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), F::Error> {
|
||||
Ok(().into())
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Request) -> Self::Future {
|
||||
(self.f)(req).into_future()
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ publish = false
|
|||
[dependencies]
|
||||
futures = "0.1"
|
||||
tower-service = "0.2.0"
|
||||
tokio-io = "0.1"
|
||||
tower-service-util = { version = "0.1.0", path = "../tower-service-util", features = ["io", "either"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-mock-task = "0.1"
|
||||
|
|
|
@ -1,215 +0,0 @@
|
|||
//! Trait object `Service` instances
|
||||
//!
|
||||
//! Dynamically dispatched `Service` objects allow for erasing the underlying
|
||||
//! `Service` type and using the `Service` instances as opaque handles. This can
|
||||
//! be useful when the service instance cannot be explicitly named for whatever
|
||||
//! reason.
|
||||
//!
|
||||
//! There are two variants of service objects. `BoxService` requires both the
|
||||
//! service and the response future to be `Send`. These values can move freely
|
||||
//! across threads. `UnsyncBoxService` requires both the service and the
|
||||
//! response future to remain on the current thread. This is useful for
|
||||
//! representing services that are backed by `Rc` or other non-`Send` types.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate futures;
|
||||
//! # extern crate tower_service;
|
||||
//! # extern crate tower_util;
|
||||
//! # use futures::*;
|
||||
//! # use futures::future::FutureResult;
|
||||
//! # use tower_service::*;
|
||||
//! # use tower_util::*;
|
||||
//! // Respond to requests using a closure. Since closures cannot be named,
|
||||
//! // `ServiceFn` cannot be named either
|
||||
//! pub struct ServiceFn<F> {
|
||||
//! f: F,
|
||||
//! }
|
||||
//!
|
||||
//! impl<F> Service<String> for ServiceFn<F>
|
||||
//! where F: Fn(String) -> String,
|
||||
//! {
|
||||
//! type Response = String;
|
||||
//! type Error = ();
|
||||
//! type Future = FutureResult<String, ()>;
|
||||
//!
|
||||
//! fn poll_ready(&mut self) -> Poll<(), ()> {
|
||||
//! Ok(().into())
|
||||
//! }
|
||||
//!
|
||||
//! fn call(&mut self, request: String) -> FutureResult<String, ()> {
|
||||
//! future::ok((self.f)(request))
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! pub fn main() {
|
||||
//! let f = |mut request: String| {
|
||||
//! request.push_str(" response");
|
||||
//! request
|
||||
//! };
|
||||
//!
|
||||
//! let service: BoxService<String, String, ()> =
|
||||
//! BoxService::new(ServiceFn { f });
|
||||
//! # drop(service);
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use futures::{Future, Poll};
|
||||
use tower_service::Service;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// A boxed `Service + Send` trait object.
|
||||
///
|
||||
/// `BoxService` turns a service into a trait object, allowing the response
|
||||
/// future type to be dynamic. This type requires both the service and the
|
||||
/// response future to be `Send`.
|
||||
///
|
||||
/// See module level documentation for more details.
|
||||
pub struct BoxService<T, U, E> {
|
||||
inner: Box<Service<T, Response = U, Error = E, Future = BoxFuture<U, E>> + Send>,
|
||||
}
|
||||
|
||||
/// A boxed `Future + Send` trait object.
|
||||
///
|
||||
/// This type alias represents a boxed future that is `Send` and can be moved
|
||||
/// across threads.
|
||||
pub type BoxFuture<T, E> = Box<Future<Item = T, Error = E> + Send>;
|
||||
|
||||
/// A boxed `Service` trait object.
|
||||
pub struct UnsyncBoxService<T, U, E> {
|
||||
inner: Box<Service<T, Response = U, Error = E, Future = UnsyncBoxFuture<U, E>>>,
|
||||
}
|
||||
|
||||
/// A boxed `Future` trait object.
|
||||
///
|
||||
/// This type alias represents a boxed future that is *not* `Send` and must
|
||||
/// remain on the current thread.
|
||||
pub type UnsyncBoxFuture<T, E> = Box<Future<Item = T, Error = E>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Boxed<S> {
|
||||
inner: S,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct UnsyncBoxed<S> {
|
||||
inner: S,
|
||||
}
|
||||
|
||||
// ===== impl BoxService =====
|
||||
|
||||
impl<T, U, E> BoxService<T, U, E> {
|
||||
pub fn new<S>(inner: S) -> Self
|
||||
where
|
||||
S: Service<T, Response = U, Error = E> + Send + 'static,
|
||||
S::Future: Send + 'static,
|
||||
{
|
||||
let inner = Box::new(Boxed { inner });
|
||||
BoxService { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, E> Service<T> for BoxService<T, U, E> {
|
||||
type Response = U;
|
||||
type Error = E;
|
||||
type Future = BoxFuture<U, E>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), E> {
|
||||
self.inner.poll_ready()
|
||||
}
|
||||
|
||||
fn call(&mut self, request: T) -> BoxFuture<U, E> {
|
||||
self.inner.call(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, E> fmt::Debug for BoxService<T, U, E>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
E: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("BoxService").finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl UnsyncBoxService =====
|
||||
|
||||
impl<T, U, E> UnsyncBoxService<T, U, E> {
|
||||
pub fn new<S>(inner: S) -> Self
|
||||
where
|
||||
S: Service<T, Response = U, Error = E> + 'static,
|
||||
S::Future: 'static,
|
||||
{
|
||||
let inner = Box::new(UnsyncBoxed { inner });
|
||||
UnsyncBoxService { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, E> Service<T> for UnsyncBoxService<T, U, E> {
|
||||
type Response = U;
|
||||
type Error = E;
|
||||
type Future = UnsyncBoxFuture<U, E>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), E> {
|
||||
self.inner.poll_ready()
|
||||
}
|
||||
|
||||
fn call(&mut self, request: T) -> UnsyncBoxFuture<U, E> {
|
||||
self.inner.call(request)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U, E> fmt::Debug for UnsyncBoxService<T, U, E>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
U: fmt::Debug,
|
||||
E: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt.debug_struct("UnsyncBoxService").finish()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Boxed =====
|
||||
|
||||
impl<S, Request> Service<Request> for Boxed<S>
|
||||
where
|
||||
S: Service<Request> + 'static,
|
||||
S::Future: Send + 'static,
|
||||
{
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = Box<Future<Item = S::Response, Error = S::Error> + Send>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
self.inner.poll_ready()
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Request) -> Self::Future {
|
||||
Box::new(self.inner.call(request))
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl UnsyncBoxed =====
|
||||
|
||||
impl<S, Request> Service<Request> for UnsyncBoxed<S>
|
||||
where
|
||||
S: Service<Request> + 'static,
|
||||
S::Future: 'static,
|
||||
{
|
||||
type Response = S::Response;
|
||||
type Error = S::Error;
|
||||
type Future = Box<Future<Item = S::Response, Error = S::Error>>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
self.inner.poll_ready()
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Request) -> Self::Future {
|
||||
Box::new(self.inner.call(request))
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
//! Contains `EitherService` and related types and functions.
|
||||
//!
|
||||
//! See `EitherService` documentation for more details.
|
||||
|
||||
use futures::future::Either;
|
||||
use futures::Poll;
|
||||
use tower_service::Service;
|
||||
|
||||
/// Combine two different service types into a single type.
|
||||
///
|
||||
/// Both services must be of the same request, response, and error types.
|
||||
/// `EitherService` is useful for handling conditional branching in service
|
||||
/// middleware to different inner service types.
|
||||
pub enum EitherService<A, B> {
|
||||
A(A),
|
||||
B(B),
|
||||
}
|
||||
|
||||
impl<A, B, Request> Service<Request> for EitherService<A, B>
|
||||
where
|
||||
A: Service<Request>,
|
||||
B: Service<Request, Response = A::Response, Error = A::Error>,
|
||||
{
|
||||
type Response = A::Response;
|
||||
type Error = A::Error;
|
||||
type Future = Either<A::Future, B::Future>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
use self::EitherService::*;
|
||||
|
||||
match *self {
|
||||
A(ref mut service) => service.poll_ready(),
|
||||
B(ref mut service) => service.poll_ready(),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Request) -> Self::Future {
|
||||
use self::EitherService::*;
|
||||
|
||||
match *self {
|
||||
A(ref mut service) => Either::A(service.call(request)),
|
||||
B(ref mut service) => Either::B(service.call(request)),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,23 +2,17 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate futures;
|
||||
extern crate tokio_io;
|
||||
#[cfg(test)]
|
||||
extern crate tokio_mock_task;
|
||||
extern crate tower_service;
|
||||
extern crate tower_service_util;
|
||||
|
||||
pub mod boxed;
|
||||
pub mod either;
|
||||
pub mod ext;
|
||||
mod make_connection;
|
||||
mod make_service;
|
||||
pub mod option;
|
||||
mod service_fn;
|
||||
|
||||
pub use boxed::BoxService;
|
||||
pub use either::EitherService;
|
||||
pub use ext::ServiceExt;
|
||||
pub use make_connection::MakeConnection;
|
||||
pub use make_service::MakeService;
|
||||
pub use option::OptionService;
|
||||
pub use service_fn::ServiceFn;
|
||||
pub use tower_service_util::BoxService;
|
||||
pub use tower_service_util::EitherService;
|
||||
pub use tower_service_util::MakeConnection;
|
||||
pub use tower_service_util::MakeService;
|
||||
pub use tower_service_util::OptionService;
|
||||
pub use tower_service_util::ServiceFn;
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
//! Contains `OptionService` and related types and functions.
|
||||
//!
|
||||
//! See `OptionService` documentation for more details.
|
||||
//!
|
||||
use futures::{Future, Poll};
|
||||
use tower_service::Service;
|
||||
|
||||
/// Optionally forwards requests to an inner service.
|
||||
///
|
||||
/// If the inner service is `None`, `Error::None` is returned as the response.
|
||||
pub struct OptionService<T> {
|
||||
inner: Option<T>,
|
||||
}
|
||||
|
||||
/// Response future returned by `OptionService`.
|
||||
pub struct ResponseFuture<T> {
|
||||
inner: Option<T>,
|
||||
}
|
||||
|
||||
/// Error produced by `OptionService` responding to a request.
|
||||
#[derive(Debug)]
|
||||
pub enum Error<T> {
|
||||
Inner(T),
|
||||
None,
|
||||
}
|
||||
|
||||
// ===== impl OptionService =====
|
||||
|
||||
impl<T> OptionService<T> {
|
||||
/// Returns an `OptionService` that forwards requests to `inner`.
|
||||
pub fn some(inner: T) -> Self {
|
||||
OptionService { inner: Some(inner) }
|
||||
}
|
||||
|
||||
/// Returns an `OptionService` that responds to all requests with
|
||||
/// `Error::None`.
|
||||
pub fn none() -> Self {
|
||||
OptionService { inner: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Request> Service<Request> for OptionService<T>
|
||||
where
|
||||
T: Service<Request>,
|
||||
{
|
||||
type Response = T::Response;
|
||||
type Error = Error<T::Error>;
|
||||
type Future = ResponseFuture<T::Future>;
|
||||
|
||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||
match self.inner {
|
||||
Some(ref mut inner) => inner.poll_ready().map_err(Error::Inner),
|
||||
// None services are always ready
|
||||
None => Ok(().into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&mut self, request: Request) -> Self::Future {
|
||||
let inner = self.inner.as_mut().map(|i| i.call(request));
|
||||
ResponseFuture { inner }
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl ResponseFuture =====
|
||||
|
||||
impl<T> Future for ResponseFuture<T>
|
||||
where
|
||||
T: Future,
|
||||
{
|
||||
type Item = T::Item;
|
||||
type Error = Error<T::Error>;
|
||||
|
||||
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
|
||||
match self.inner {
|
||||
Some(ref mut inner) => inner.poll().map_err(Error::Inner),
|
||||
None => Err(Error::None),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue