Remove `ReadyService` (#68)
The value added by having a separate trait is not obvious. Equivalent behavior can be provided by a `Service` implementation that is always "ready".
This commit is contained in:
parent
5369879af6
commit
295ae583d4
|
@ -8,7 +8,6 @@ members = [
|
|||
"tower-in-flight-limit",
|
||||
"tower-mock",
|
||||
"tower-rate-limit",
|
||||
"tower-ready-service",
|
||||
"tower-reconnect",
|
||||
"tower-router",
|
||||
"tower-service",
|
||||
|
|
|
@ -7,7 +7,6 @@ publish = false
|
|||
[dependencies]
|
||||
futures = "0.1"
|
||||
tower-service = { version = "0.1", path = "../tower-service" }
|
||||
tower-ready-service = { version = "0.1", path = "../tower-ready-service" }
|
||||
|
||||
[dev-dependencies]
|
||||
tower-mock = { version = "0.1", path = "../tower-mock" }
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
//! service.
|
||||
|
||||
extern crate futures;
|
||||
extern crate tower_ready_service;
|
||||
extern crate tower_service;
|
||||
|
||||
use tower_ready_service::ReadyService;
|
||||
use tower_service::Service;
|
||||
|
||||
use futures::{Future, Poll, Async};
|
||||
|
@ -79,29 +77,6 @@ impl<T> InFlightLimit<T> {
|
|||
pub fn into_inner(self) -> T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
fn call2<F, R>(&mut self, f: F) -> ResponseFuture<R>
|
||||
where F: FnOnce(&mut Self) -> R,
|
||||
{
|
||||
// In this implementation, `poll_ready` is not expected to be called
|
||||
// first (though, it might have been).
|
||||
if self.state.reserved {
|
||||
self.state.reserved = false;
|
||||
} else {
|
||||
// Try to reserve
|
||||
if !self.state.shared.reserve() {
|
||||
return ResponseFuture {
|
||||
inner: None,
|
||||
shared: self.state.shared.clone(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
ResponseFuture {
|
||||
inner: Some(f(self)),
|
||||
shared: self.state.shared.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Service for InFlightLimit<S>
|
||||
|
@ -131,20 +106,24 @@ where S: Service
|
|||
}
|
||||
|
||||
fn call(&mut self, request: Self::Request) -> Self::Future {
|
||||
self.call2(|me| me.inner.call(request))
|
||||
}
|
||||
}
|
||||
// In this implementation, `poll_ready` is not expected to be called
|
||||
// first (though, it might have been).
|
||||
if self.state.reserved {
|
||||
self.state.reserved = false;
|
||||
} else {
|
||||
// Try to reserve
|
||||
if !self.state.shared.reserve() {
|
||||
return ResponseFuture {
|
||||
inner: None,
|
||||
shared: self.state.shared.clone(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> ReadyService for InFlightLimit<S>
|
||||
where S: ReadyService
|
||||
{
|
||||
type Request = S::Request;
|
||||
type Response = S::Response;
|
||||
type Error = Error<S::Error>;
|
||||
type Future = ResponseFuture<S::Future>;
|
||||
|
||||
fn call(&mut self, request: Self::Request) -> Self::Future {
|
||||
self.call2(|me| me.inner.call(request))
|
||||
ResponseFuture {
|
||||
inner: Some(self.inner.call(request)),
|
||||
shared: self.state.shared.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
[package]
|
||||
name = "tower-ready-service"
|
||||
version = "0.1.0"
|
||||
authors = ["Carl Lerche <me@carllerche.com>"]
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
futures = "0.1"
|
||||
tower-service = { version = "0.1", path = "../tower-service" }
|
|
@ -1,3 +0,0 @@
|
|||
extern crate tower_service;
|
||||
|
||||
pub use tower_service::ReadyService;
|
|
@ -189,47 +189,9 @@ pub trait Service {
|
|||
/// implementations should take care to not call `poll_ready`. If the
|
||||
/// service is at capacity and the request is unable to be handled, the
|
||||
/// returned `Future` should resolve to an error.
|
||||
fn call(&mut self, req: Self::Request) -> Self::Future;
|
||||
}
|
||||
|
||||
/// An asynchronous function from `Request` to a `Response` that is always ready
|
||||
/// to process a request.
|
||||
///
|
||||
/// `ReadyService` is similar to `Service`, except that it is always able to
|
||||
/// accept a request. This request may either complete successfully or resolve
|
||||
/// to an error, i.e., `ReadyService` implementations may handle out of capacity
|
||||
/// situations by returning a response future that immediately resolves to an
|
||||
/// error.
|
||||
///
|
||||
/// The `Service` trait should be prefered over this one. `ReadyService` should
|
||||
/// only be used in situations where there is no way to handle back pressure.
|
||||
/// When usin a `ReadyService` implementation, back pressure needs to be handled
|
||||
/// via some other strategy, such as limiting the total number of in flight
|
||||
/// requests.
|
||||
///
|
||||
/// One situation in which there is no way to handle back pressure is routing.
|
||||
/// A router service receives inbound requests and dispatches them to one of N
|
||||
/// inner services. In this case, one of the inner services may become "not
|
||||
/// ready" while the others remain ready. It would not be ideal for the router
|
||||
/// service to flag itself as "not ready" when only one of the inner services is
|
||||
/// not ready, but there is no way for the router to communicate to the caller
|
||||
/// which requests will be accepted and which will be rejected. The router
|
||||
/// service will implement `ReadyService`, indicating to the user that they are
|
||||
/// responsible for handling back pressure via some other strategy.
|
||||
pub trait ReadyService {
|
||||
/// Requests handled by the service.
|
||||
type Request;
|
||||
|
||||
/// Responses returned by the service.
|
||||
type Response;
|
||||
|
||||
/// Errors produced by the service.
|
||||
type Error;
|
||||
|
||||
/// The future response value.
|
||||
type Future: Future<Item = Self::Response, Error = Self::Error>;
|
||||
|
||||
/// Process the request and return the response asynchronously.
|
||||
///
|
||||
/// Calling `call` without calling `poll_ready` is permitted. The
|
||||
/// implementation must be resilient to this fact.
|
||||
fn call(&mut self, req: Self::Request) -> Self::Future;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,4 +7,3 @@ publish = false
|
|||
[dependencies]
|
||||
futures = "0.1"
|
||||
tower-service = { version = "0.1", path = "../tower-service" }
|
||||
tower-ready-service = { version = "0.1", path = "../tower-ready-service" }
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//! Various utility types and functions that are generally with Tower.
|
||||
|
||||
extern crate futures;
|
||||
extern crate tower_ready_service;
|
||||
extern crate tower_service;
|
||||
|
||||
pub mod either;
|
||||
|
@ -11,5 +10,5 @@ mod service_fn;
|
|||
|
||||
pub use boxed::BoxService;
|
||||
pub use either::EitherService;
|
||||
pub use service_fn::{ServiceFn, NewServiceFn};
|
||||
pub use service_fn::{NewServiceFn};
|
||||
pub use option::OptionService;
|
||||
|
|
|
@ -1,51 +1,11 @@
|
|||
use futures::IntoFuture;
|
||||
use tower_ready_service::ReadyService;
|
||||
use tower_service::{Service, NewService};
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
/// A `Service` implemented by a closure.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServiceFn<T, R> {
|
||||
f: T,
|
||||
// don't impose Sync on R
|
||||
_ty: PhantomData<fn() -> R>,
|
||||
}
|
||||
|
||||
/// A `NewService` implemented by a closure.
|
||||
pub struct NewServiceFn<T> {
|
||||
f: T,
|
||||
}
|
||||
|
||||
// ===== impl ServiceFn =====
|
||||
|
||||
impl<T, R, S> ServiceFn<T, R>
|
||||
where T: FnMut(R) -> S,
|
||||
S: IntoFuture,
|
||||
{
|
||||
/// Create a new `ServiceFn` backed by the given closure
|
||||
pub fn new(f: T) -> Self {
|
||||
ServiceFn {
|
||||
f,
|
||||
_ty: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R, S> ReadyService for ServiceFn<T, R>
|
||||
where T: FnMut(R) -> S,
|
||||
S: IntoFuture,
|
||||
{
|
||||
type Request = R;
|
||||
type Response = S::Item;
|
||||
type Error = S::Error;
|
||||
type Future = S::Future;
|
||||
|
||||
fn call(&mut self, request: Self::Request) -> Self::Future {
|
||||
(self.f)(request).into_future()
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl NewServiceFn =====
|
||||
|
||||
impl<T, N> NewServiceFn<T>
|
||||
|
|
Loading…
Reference in New Issue