diff --git a/.travis.yml b/.travis.yml index 767e9fde6..a2321cddf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ matrix: env: global: - # - RUSTFLAGS="-C link-dead-code" + - RUSTFLAGS="-C link-dead-code" - OPENSSL_VERSION=openssl-1.0.2 before_install: @@ -33,6 +33,7 @@ script: if [[ "$TRAVIS_RUST_VERSION" != "nightly" ]]; then cargo clean cargo test --features="ssl,tls,rust-tls" -- --nocapture + cd actix-service && cargo test fi - | if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then @@ -40,6 +41,7 @@ script: cargo tarpaulin --features="ssl,tls,rust-tls" --out Xml bash <(curl -s https://codecov.io/bash) echo "Uploaded code coverage" + cd actix-service && cargo tarpaulin --out Xml && bash <(curl -s https://codecov.io/bash) fi # Upload docs diff --git a/Cargo.toml b/Cargo.toml index 3194b6eeb..90f7865ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,12 @@ license = "MIT/Apache-2.0" exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" +[workspace] +members = [ + "./", + "actix-service", +] + [package.metadata.docs.rs] features = ["ssl", "tls", "rust-tls"] @@ -41,6 +47,8 @@ cell = [] [dependencies] actix = "0.7.6" +# actix-service = "0.1" +actix-service = { path="./actix-service" } log = "0.4" num_cpus = "1.0" diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml new file mode 100644 index 000000000..d9900c4df --- /dev/null +++ b/actix-service/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "actix-service" +version = "0.1.0" +authors = ["Nikolay Kim "] +description = "Actix Service" +keywords = ["network", "framework", "async", "futures"] +homepage = "https://actix.rs" +repository = "https://github.com/actix/actix-net.git" +documentation = "https://docs.rs/actix-service/" +categories = ["network-programming", "asynchronous"] +license = "MIT/Apache-2.0" +exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] +edition = "2018" +workspace = "../" + +[badges] +travis-ci = { repository = "actix/actix-service", branch = "master" } +# appveyor = { repository = "fafhrd91/actix-web-hdy9d" } +codecov = { repository = "actix/actix-service", branch = "master", service = "github" } + +[lib] +name = "actix_service" +path = "src/lib.rs" + +[dependencies] +futures = "0.1.24" diff --git a/src/service/and_then.rs b/actix-service/src/and_then.rs similarity index 97% rename from src/service/and_then.rs rename to actix-service/src/and_then.rs index f640975e1..1e0649bda 100644 --- a/src/service/and_then.rs +++ b/actix-service/src/and_then.rs @@ -46,7 +46,7 @@ where fn poll_ready(&mut self) -> Poll<(), Self::Error> { try_ready!(self.a.poll_ready()); - self.b.borrow_mut().poll_ready() + self.b.get_mut().poll_ready() } fn call(&mut self, req: Request) -> Self::Future { @@ -82,7 +82,6 @@ impl Future for AndThenFuture where A: Service, B: Service, - B::Error: Into, { type Item = B::Response; type Error = A::Error; @@ -94,7 +93,7 @@ where match self.fut_a.poll() { Ok(Async::Ready(resp)) => { - self.fut_b = Some(self.b.borrow_mut().call(resp)); + self.fut_b = Some(self.b.get_mut().call(resp)); self.poll() } Ok(Async::NotReady) => Ok(Async::NotReady), @@ -219,7 +218,7 @@ mod tests { use std::rc::Rc; use super::*; - use crate::service::{NewServiceExt, Service, ServiceExt}; + use crate::{NewService, Service}; struct Srv1(Rc>); impl Service<&'static str> for Srv1 { diff --git a/src/service/apply.rs b/actix-service/src/apply.rs similarity index 94% rename from src/service/apply.rs rename to actix-service/src/apply.rs index 071eb79c6..b355c1a9e 100644 --- a/src/service/apply.rs +++ b/actix-service/src/apply.rs @@ -50,8 +50,8 @@ where F: Fn(In, &mut T) -> Out, Out: IntoFuture, { - type Response = ::Item; - type Error = ::Error; + type Response = Out::Item; + type Error = Out::Error; type Future = Out::Future; fn poll_ready(&mut self) -> Poll<(), Self::Error> { @@ -110,8 +110,8 @@ where F: Fn(In, &mut T::Service) -> Out + Clone, Out: IntoFuture, { - type Response = ::Item; - type Error = ::Error; + type Response = Out::Item; + type Error = Out::Error; type Service = Apply; type InitError = T::InitError; @@ -171,9 +171,7 @@ mod tests { use futures::future::{ok, FutureResult}; use futures::{Async, Future, Poll}; - use crate::service::{ - IntoNewService, IntoService, NewService, NewServiceExt, Service, ServiceExt, - }; + use crate::{IntoNewService, IntoService, NewService, Service}; #[derive(Clone)] struct Srv; diff --git a/actix-service/src/cell.rs b/actix-service/src/cell.rs new file mode 100644 index 000000000..6136a7167 --- /dev/null +++ b/actix-service/src/cell.rs @@ -0,0 +1,32 @@ +//! Custom cell impl +use std::{cell::UnsafeCell, fmt, rc::Rc}; + +pub(crate) struct Cell { + inner: Rc>, +} + +impl Clone for Cell { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} + +impl fmt::Debug for Cell { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl Cell { + pub(crate) fn new(inner: T) -> Self { + Self { + inner: Rc::new(UnsafeCell::new(inner)), + } + } + + pub(crate) fn get_mut(&mut self) -> &mut T { + unsafe { &mut *self.inner.as_ref().get() } + } +} diff --git a/src/service/fn_service.rs b/actix-service/src/fn_service.rs similarity index 100% rename from src/service/fn_service.rs rename to actix-service/src/fn_service.rs diff --git a/src/service/from_err.rs b/actix-service/src/from_err.rs similarity index 98% rename from src/service/from_err.rs rename to actix-service/src/from_err.rs index 37e7c6933..7a4832bc9 100644 --- a/src/service/from_err.rs +++ b/actix-service/src/from_err.rs @@ -159,7 +159,7 @@ mod tests { use futures::future::{err, FutureResult}; use super::*; - use crate::service::{IntoNewService, NewServiceExt, Service, ServiceExt}; + use crate::{IntoNewService, NewService, Service}; struct Srv; impl Service<()> for Srv { diff --git a/src/service/mod.rs b/actix-service/src/lib.rs similarity index 59% rename from src/service/mod.rs rename to actix-service/src/lib.rs index 3952a8016..452ccea70 100644 --- a/src/service/mod.rs +++ b/actix-service/src/lib.rs @@ -1,10 +1,7 @@ -use futures::{Future, IntoFuture}; - -/// re-export for convinience -pub use tower_service::Service; - +use futures::{Future, IntoFuture, Poll}; mod and_then; mod apply; +mod cell; mod fn_service; mod from_err; mod map; @@ -21,9 +18,39 @@ pub use self::map_err::{MapErr, MapErrNewService}; pub use self::map_init_err::MapInitErr; pub use self::then::{Then, ThenNewService}; -/// An extension trait for `Service`s that provides a variety of convenient -/// adapters -pub trait ServiceExt: Service { +/// An asynchronous function from `Request` to a `Response`. +pub trait Service { + /// Responses given by the service. + type Response; + + /// Errors produced by the service. + type Error; + + /// The future response value. + type Future: Future; + + /// Returns `Ready` when the service is able to process requests. + /// + /// If the service is at capacity, then `NotReady` is returned and the task + /// is notified when the service becomes ready again. This function is + /// expected to be called while on a task. + /// + /// This is a **best effort** implementation. False positives are permitted. + /// It is permitted for the service to return `Ready` from a `poll_ready` + /// call and the next invocation of `call` results in an error. + fn poll_ready(&mut self) -> Poll<(), Self::Error>; + + /// Process the request and return the response asynchronously. + /// + /// This function is expected to be callable off task. As such, + /// 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. + /// + /// Calling `call` without calling `poll_ready` is permitted. The + /// implementation must be resilient to this fact. + fn call(&mut self, req: Request) -> Self::Future; + /// Apply function to specified service and use it as a next service in /// chain. fn apply( @@ -33,7 +60,7 @@ pub trait ServiceExt: Service { ) -> AndThen> where Self: Sized, - T: Service, + T: Service, I: IntoService, F: Fn(Self::Response, &mut T) -> Out, Out: IntoFuture, @@ -59,11 +86,11 @@ pub trait ServiceExt: Service { AndThen::new(self, service.into_service()) } - // /// Map this service's error to any error implementing `From` for - // /// this service`s `Error`. - // /// - // /// Note that this function consumes the receiving service and returns a - // /// wrapped version of it. + /// Map this service's error to any error implementing `From` for + /// this service`s `Error`. + /// + /// Note that this function consumes the receiving service and returns a + /// wrapped version of it. fn from_err(self) -> FromErr where Self: Sized, @@ -72,11 +99,11 @@ pub trait ServiceExt: Service { FromErr::new(self) } - // /// Chain on a computation for when a call to the service finished, - // /// passing the result of the call to the next service `B`. - // /// - // /// Note that this function consumes the receiving future and returns a - // /// wrapped version of it. + /// Chain on a computation for when a call to the service finished, + /// passing the result of the call to the next service `B`. + /// + /// Note that this function consumes the receiving service and returns a + /// wrapped version of it. fn then(self, service: B) -> Then where Self: Sized, @@ -85,15 +112,15 @@ pub trait ServiceExt: Service { Then::new(self, service) } - // /// Map this service's output to a different type, returning a new service - // /// of the resulting type. - // /// - // /// This function is similar to the `Option::map` or `Iterator::map` where - // /// it will change the type of the underlying service. - // /// - // /// Note that this function consumes the receiving service and returns a - // /// wrapped version of it, similar to the existing `map` methods in the - // /// standard library. + /// Map this service's output to a different type, returning a new service + /// of the resulting type. + /// + /// This function is similar to the `Option::map` or `Iterator::map` where + /// it will change the type of the underlying service. + /// + /// Note that this function consumes the receiving service and returns a + /// wrapped version of it, similar to the existing `map` methods in the + /// standard library. fn map(self, f: F) -> Map where Self: Sized, @@ -102,14 +129,14 @@ pub trait ServiceExt: Service { Map::new(self, f) } - // /// Map this service's error to a different error, returning a new service. - // /// - // /// This function is similar to the `Result::map_err` where it will change - // /// the error type of the underlying service. This is useful for example to - // /// ensure that services have the same error type. - // /// - // /// Note that this function consumes the receiving service and returns a - // /// wrapped version of it. + /// Map this service's error to a different error, returning a new service. + /// + /// This function is similar to the `Result::map_err` where it will change + /// the error type of the underlying service. This is useful for example to + /// ensure that services have the same error type. + /// + /// Note that this function consumes the receiving service and returns a + /// wrapped version of it. fn map_err(self, f: F) -> MapErr where Self: Sized, @@ -146,9 +173,9 @@ pub trait NewService { /// Create and return a new service value asynchronously. fn new_service(&self) -> Self::Future; -} -pub trait NewServiceExt: NewService { + /// Apply function to specified service and use it as a next service in + /// chain. fn apply( self, service: I, @@ -156,7 +183,7 @@ pub trait NewServiceExt: NewService { ) -> AndThenNewService> where Self: Sized, - T: NewService, + T: NewService, I: IntoNewService, F: Fn(Self::Response, &mut T::Service) -> Out + Clone, Out: IntoFuture, @@ -164,6 +191,7 @@ pub trait NewServiceExt: NewService { self.and_then(ApplyNewService::new(service, f)) } + /// Call another service after call to this one has resolved successfully. fn and_then(self, new_service: F) -> AndThenNewService where Self: Sized, @@ -173,12 +201,12 @@ pub trait NewServiceExt: NewService { AndThenNewService::new(self, new_service) } - // /// `NewService` that create service to map this service's error - // /// and new service's init error to any error - // /// implementing `From` for this service`s `Error`. - // /// - // /// Note that this function consumes the receiving new service and returns a - // /// wrapped version of it. + /// `NewService` that create service to map this service's error + /// and new service's init error to any error + /// implementing `From` for this service`s `Error`. + /// + /// Note that this function consumes the receiving new service and returns a + /// wrapped version of it. fn from_err(self) -> FromErrNewService where Self: Sized, @@ -187,12 +215,12 @@ pub trait NewServiceExt: NewService { FromErrNewService::new(self) } - // /// Create `NewService` to chain on a computation for when a call to the - // /// service finished, passing the result of the call to the next - // /// service `B`. - // /// - // /// Note that this function consumes the receiving future and returns a - // /// wrapped version of it. + /// Create `NewService` to chain on a computation for when a call to the + /// service finished, passing the result of the call to the next + /// service `B`. + /// + /// Note that this function consumes the receiving future and returns a + /// wrapped version of it. fn then(self, new_service: F) -> ThenNewService where Self: Sized, @@ -206,6 +234,8 @@ pub trait NewServiceExt: NewService { ThenNewService::new(self, new_service) } + /// Map this service's output to a different type, returning a new service + /// of the resulting type. fn map(self, f: F) -> MapNewService where Self: Sized, @@ -214,6 +244,7 @@ pub trait NewServiceExt: NewService { MapNewService::new(self, f) } + /// Map this service's error to a different error, returning a new service. fn map_err(self, f: F) -> MapErrNewService where Self: Sized, @@ -222,6 +253,7 @@ pub trait NewServiceExt: NewService { MapErrNewService::new(self, f) } + /// Map this service's init error to a different error, returning a new service. fn map_init_err(self, f: F) -> MapInitErr where Self: Sized, @@ -231,6 +263,23 @@ pub trait NewServiceExt: NewService { } } +impl<'a, S, Request> Service for &'a mut S +where + S: Service + 'a, +{ + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self) -> Poll<(), S::Error> { + (**self).poll_ready() + } + + fn call(&mut self, request: Request) -> S::Future { + (**self).call(request) + } +} + impl NewService for F where F: Fn() -> R, @@ -248,9 +297,6 @@ where } } -impl ServiceExt for T where T: Service {} -impl NewServiceExt for T where T: NewService {} - /// Trait for types that can be converted to a `Service` pub trait IntoService where diff --git a/src/service/map.rs b/actix-service/src/map.rs similarity index 98% rename from src/service/map.rs rename to actix-service/src/map.rs index 3e19c15dd..c6c23cf46 100644 --- a/src/service/map.rs +++ b/actix-service/src/map.rs @@ -189,7 +189,7 @@ mod tests { use futures::future::{ok, FutureResult}; use super::*; - use crate::service::{IntoNewService, NewServiceExt, Service, ServiceExt}; + use crate::{IntoNewService, Service}; struct Srv; impl Service<()> for Srv { diff --git a/src/service/map_err.rs b/actix-service/src/map_err.rs similarity index 98% rename from src/service/map_err.rs rename to actix-service/src/map_err.rs index 813440898..ae3ed92e2 100644 --- a/src/service/map_err.rs +++ b/actix-service/src/map_err.rs @@ -190,7 +190,7 @@ mod tests { use futures::future::{err, FutureResult}; use super::*; - use crate::service::{IntoNewService, NewServiceExt, Service, ServiceExt}; + use crate::{IntoNewService, NewService, Service}; struct Srv; diff --git a/src/service/map_init_err.rs b/actix-service/src/map_init_err.rs similarity index 100% rename from src/service/map_init_err.rs rename to actix-service/src/map_init_err.rs diff --git a/src/service/then.rs b/actix-service/src/then.rs similarity index 97% rename from src/service/then.rs rename to actix-service/src/then.rs index 44f8b4ce2..1cdb62147 100644 --- a/src/service/then.rs +++ b/actix-service/src/then.rs @@ -46,7 +46,7 @@ where fn poll_ready(&mut self) -> Poll<(), Self::Error> { try_ready!(self.a.poll_ready()); - self.b.borrow_mut().poll_ready() + self.b.get_mut().poll_ready() } fn call(&mut self, req: Request) -> Self::Future { @@ -93,11 +93,11 @@ where match self.fut_a.poll() { Ok(Async::Ready(resp)) => { - self.fut_b = Some(self.b.borrow_mut().call(Ok(resp))); + self.fut_b = Some(self.b.get_mut().call(Ok(resp))); self.poll() } Err(err) => { - self.fut_b = Some(self.b.borrow_mut().call(Err(err))); + self.fut_b = Some(self.b.get_mut().call(Err(err))); self.poll() } Ok(Async::NotReady) => Ok(Async::NotReady), @@ -226,7 +226,6 @@ mod tests { use std::rc::Rc; use super::*; - use crate::service::{NewServiceExt, ServiceExt}; #[derive(Clone)] struct Srv1(Rc>); diff --git a/src/lib.rs b/src/lib.rs index fe16a4265..7e99b8afd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,6 @@ pub mod inflight; pub mod keepalive; pub mod resolver; pub mod server; -pub mod service; pub mod ssl; pub mod stream; pub mod time;