diff --git a/actix-tower/src/lib.rs b/actix-tower/src/lib.rs index 1e985db2..891b5017 100644 --- a/actix-tower/src/lib.rs +++ b/actix-tower/src/lib.rs @@ -1,4 +1,60 @@ -//! Utilities to ease interoperability with services based on the `tower-service` crate. +//! Utilities to provide interoperability between services based on the +//! `actix-service` and `tower-service` crates. +//! +//! ## Example +//! +//! In the following example, we take a `RandomService`—which will always +//! return 4—and wraps it with a middleware that will always add 1 to the +//! result. This pattern can be further used to wrap services from either +//! `tower-service` or `actix-service` with middleware provided by the other. +//! +//! ``` +//! use actix_tower::ActixServiceExt; +//! # use futures::{Async, Future}; +//! use actix_service::Service; +//! +//! struct RandomService; +//! impl Service for RandomService { +//! // … +//! # type Request = (); +//! # type Response = u32; +//! # type Error = (); +//! # type Future = futures::future::FutureResult; +//! # +//! # fn poll_ready(&mut self) -> futures::Poll<(), Self::Error> { +//! # Ok(Async::Ready(())) +//! # } +//! # +//! # fn call(&mut self, _req: Self::Request) -> Self::Future { +//! # futures::finished(4) +//! # } +//! } +//! +//! struct AddOneMiddleware(S); +//! impl tower_service::Service for AddOneMiddleware +//! where +//! S: tower_service::Service, +//! S::Future: 'static, +//! { +//! /// … +//! # type Response = u32; +//! # type Error = S::Error; +//! # type Future = Box>; +//! # +//! # fn poll_ready(&mut self) -> futures::Poll<(), Self::Error> { +//! # self.0.poll_ready() +//! # } +//! # +//! # fn call(&mut self, req: R) -> Self::Future { +//! # let fut = self.0.call(req).map(|x| x + 1); +//! # Box::new(fut) +//! # } +//! } +//! +//! let mut s = RandomService.wrap_with_tower_middleware(AddOneMiddleware); +//! assert_eq!(Ok(Async::Ready(())), s.poll_ready()); +//! assert_eq!(Ok(Async::Ready(5)), s.call(()).poll()); +//! ``` use actix_service::Service as ActixService; use std::marker::PhantomData; @@ -6,40 +62,123 @@ use tower_service::Service as TowerService; /// Compatibility wrapper associating a `tower_service::Service` with a particular /// `Request` type, so that it can be used as an `actix_service::Service`. -pub struct TowerCompat { +/// +/// Generally created through convenience methods on the `TowerServiceExt` trait. +pub struct ActixCompat { inner: S, _phantom: PhantomData, } -impl TowerCompat { +impl ActixCompat { /// Wraps a `tower_service::Service` in a compatibility wrapper. pub fn new(inner: S) -> Self { - TowerCompat { + ActixCompat { inner, _phantom: PhantomData, } } } -/// Extension trait for wrapping `tower_service::Service` instances for use as +/// Extension trait for wrapping a `tower_service::Service` instance for use as /// an `actix_service::Service`. -pub trait TowerServiceExt { +pub trait TowerServiceExt : TowerService + Sized { /// Wraps a `tower_service::Service` in a compatibility wrapper. - fn compat(self) -> TowerCompat - where - Self: TowerService + Sized; -} + /// + /// ``` + /// use actix_service::Service; + /// use actix_tower::TowerServiceExt; + /// # use futures::{Async, Future}; + /// + /// struct RandomService; + /// impl tower_service::Service for RandomService { + /// // … + /// # type Response = u32; + /// # type Error = (); + /// # type Future = futures::future::FutureResult; + /// # + /// # fn poll_ready(&mut self) -> futures::Poll<(), Self::Error> { + /// # Ok(Async::Ready(())) + /// # } + /// # + /// # fn call(&mut self, _req: R) -> Self::Future { + /// # futures::finished(4) + /// # } + /// } + /// + /// let mut s = RandomService.into_actix_service(); + /// assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + /// assert_eq!(Ok(Async::Ready(4)), s.call(()).poll()); + /// ``` + fn into_actix_service(self) -> ActixCompat { + ActixCompat::new(self) + } -impl TowerServiceExt for S { - fn compat(self) -> TowerCompat + /// Takes a function that, when provided with an `actix_service::Service` wraps it + /// and returns a new service. Useful for wrapping a `tower_service::Service` with + /// middleware built for `actix_service`. + /// + /// ``` + /// use actix_tower::TowerServiceExt; + /// # use futures::{Async, Future}; + /// use tower_service::Service; + /// + /// struct RandomService; + /// impl Service for RandomService { + /// // … + /// # type Response = u32; + /// # type Error = (); + /// # type Future = futures::future::FutureResult; + /// # + /// # fn poll_ready(&mut self) -> futures::Poll<(), Self::Error> { + /// # Ok(Async::Ready(())) + /// # } + /// # + /// # fn call(&mut self, _req: R) -> Self::Future { + /// # futures::finished(4) + /// # } + /// } + /// + /// struct AddOneTransform(S); + /// impl actix_service::Service for AddOneTransform + /// where + /// S: actix_service::Service, + /// S::Future: 'static, + /// { + /// /// … + /// # type Request = S::Request; + /// # type Response = u32; + /// # type Error = S::Error; + /// # type Future = Box>; + /// # + /// # fn poll_ready(&mut self) -> futures::Poll<(), Self::Error> { + /// # self.0.poll_ready() + /// # } + /// # + /// # fn call(&mut self, req: Self::Request) -> Self::Future { + /// # let fut = self.0.call(req).map(|x| x + 1); + /// # Box::new(fut) + /// # } + /// } + /// + /// let mut s = RandomService.wrap_with_actix_middleware(AddOneTransform); + /// assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + /// assert_eq!(Ok(Async::Ready(5)), s.call(()).poll()); + /// ``` + fn wrap_with_actix_middleware(self, f: F) -> TowerCompat where - Self: TowerService + Sized + F: FnOnce(ActixCompat) -> U, + U: ActixService { - TowerCompat::new(self) + f(self.into_actix_service()).into_tower_service() } } -impl ActixService for TowerCompat +impl TowerServiceExt for S +where + S: TowerService + Sized +{} + +impl ActixService for ActixCompat where S: TowerService, { @@ -57,135 +196,392 @@ where } } +/// Compatibility wrapper associating an `actix_service::Service` with a particular +/// `Request` type, so that it can be used as a `tower_service::Service`. +/// +/// Generally created through convenience methods on the `ActixServiceExt` trait. +pub struct TowerCompat { + inner: S, +} + +impl TowerCompat { + /// Wraps an `actix_service::Service` in a compatibility wrapper. + pub fn new(inner: S) -> Self { + TowerCompat { + inner, + } + } +} + +/// Extension trait for wrapping an `actix_service::Service` instance for use as +/// a `tower_service::Service`. +pub trait ActixServiceExt: ActixService + Sized { + /// Wraps a `tower_service::Service` in a compatibility wrapper. + /// + /// ``` + /// use actix_tower::ActixServiceExt; + /// # use futures::{Async, Future}; + /// use tower_service::Service; + /// + /// struct RandomService; + /// impl actix_service::Service for RandomService { + /// // … + /// # type Request = (); + /// # type Response = u32; + /// # type Error = (); + /// # type Future = futures::future::FutureResult; + /// # + /// # fn poll_ready(&mut self) -> futures::Poll<(), Self::Error> { + /// # Ok(Async::Ready(())) + /// # } + /// # + /// # fn call(&mut self, _req: Self::Request) -> Self::Future { + /// # futures::finished(4) + /// # } + /// } + /// + /// let mut s = RandomService.into_tower_service(); + /// assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + /// assert_eq!(Ok(Async::Ready(4)), s.call(()).poll()); + /// ``` + fn into_tower_service(self) -> TowerCompat { + TowerCompat::new(self) + } + + /// Takes a function that, when provided with a `tower_service::Service` wraps it + /// and returns a new service. Useful for wrapping an `actix_service::Service` with + /// middleware built for `tower_service`. + /// + /// ``` + /// use actix_tower::ActixServiceExt; + /// # use futures::{Async, Future}; + /// use actix_service::Service; + /// + /// struct RandomService; + /// impl Service for RandomService { + /// // … + /// # type Request = (); + /// # type Response = u32; + /// # type Error = (); + /// # type Future = futures::future::FutureResult; + /// # + /// # fn poll_ready(&mut self) -> futures::Poll<(), Self::Error> { + /// # Ok(Async::Ready(())) + /// # } + /// # + /// # fn call(&mut self, _req: Self::Request) -> Self::Future { + /// # futures::finished(4) + /// # } + /// } + /// + /// struct AddOneMiddleware(S); + /// impl tower_service::Service for AddOneMiddleware + /// where + /// S: tower_service::Service, + /// S::Future: 'static, + /// { + /// /// … + /// # type Response = u32; + /// # type Error = S::Error; + /// # type Future = Box>; + /// # + /// # fn poll_ready(&mut self) -> futures::Poll<(), Self::Error> { + /// # self.0.poll_ready() + /// # } + /// # + /// # fn call(&mut self, req: R) -> Self::Future { + /// # let fut = self.0.call(req).map(|x| x + 1); + /// # Box::new(fut) + /// # } + /// } + /// + /// let mut s = RandomService.wrap_with_tower_middleware(AddOneMiddleware); + /// assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + /// assert_eq!(Ok(Async::Ready(5)), s.call(()).poll()); + /// ``` + fn wrap_with_tower_middleware(self, f: F) -> ActixCompat + where + F: FnOnce(TowerCompat) -> U, + U: TowerService + { + f(self.into_tower_service()).into_actix_service() + } +} + +impl ActixServiceExt for S +where + S: ActixService + Sized +{} + +impl TowerService for TowerCompat +where + S: ActixService, +{ + type Response = S::Response; + type Error = S::Error; + type Future = S::Future; + + fn poll_ready(&mut self) -> futures::Poll<(), Self::Error> { + ActixService::poll_ready(&mut self.inner) + } + + fn call(&mut self, req: S::Request) -> Self::Future { + ActixService::call(&mut self.inner, req) + } +} + #[cfg(test)] mod tests { - use super::TowerServiceExt; - use actix_service::{Service as ActixService, ServiceExt, Transform}; - use futures::{future::FutureResult, Async, Poll, Future}; - use tower_service::Service as TowerService; + mod tower_service_into_actix_service { + use crate::TowerServiceExt; + use actix_service::{Service as ActixService, ServiceExt, Transform}; + use futures::{future::FutureResult, Async, Poll, Future}; + use tower_service::Service as TowerService; - struct RandomService; - impl TowerService for RandomService { - type Response = u32; - type Error = (); - type Future = FutureResult; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + #[test] + fn random_service_returns_4() { + let mut s = RandomService.into_actix_service(); + + assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + + assert_eq!(Ok(Async::Ready(4)), s.call(()).poll()); } - fn call(&mut self, _req: R) -> Self::Future { - futures::finished(4) + #[test] + fn random_service_can_combine() { + let mut s = RandomService.into_actix_service().map(|x| x + 1); + + assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + + assert_eq!(Ok(Async::Ready(5)), s.call(()).poll()); + } + + #[test] + fn random_service_can_use_actix_middleware() { + let mut s = RandomService.wrap_with_actix_middleware(DoMathTransform); + + assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + + assert_eq!(Ok(Async::Ready(68)), s.call(()).poll()); + } + + #[test] + fn random_service_and_add_service_chained() { + let s1 = RandomService.into_actix_service(); + let s2 = AddOneService.into_actix_service(); + let s3 = AddOneService.into_actix_service(); + + let mut s = s1.and_then(s2).and_then(s3); + + assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + + assert_eq!(Ok(Async::Ready(6)), s.call(()).poll()); + } + + #[test] + fn random_service_and_add_service_and_ignoring_service_chained() { + let s1 = RandomService.into_actix_service(); + let s2 = AddOneService.into_actix_service(); + let s3 = AddOneService.into_actix_service(); + let s4 = RandomService.into_actix_service(); + + let mut s = s1.and_then(s2).and_then(s3).and_then(s4); + + assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + + assert_eq!(Ok(Async::Ready(4)), s.call(()).poll()); + } + + #[test] + fn random_service_can_be_transformed_to_do_math() { + let transform = DoMath; + + let mut s = transform.new_transform(RandomService.into_actix_service()).wait().unwrap(); + + assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + + assert_eq!(Ok(Async::Ready(68)), s.call(()).poll()); + } + + struct RandomService; + impl TowerService for RandomService { + type Response = u32; + type Error = (); + type Future = FutureResult; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + Ok(Async::Ready(())) + } + + fn call(&mut self, _req: R) -> Self::Future { + futures::finished(4) + } + } + + struct AddOneService; + impl TowerService for AddOneService { + type Response = u32; + type Error = (); + type Future = FutureResult; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + Ok(Async::Ready(())) + } + + fn call(&mut self, req: u32) -> Self::Future { + futures::finished(req + 1) + } + } + + struct DoMathTransform(S); + impl ActixService for DoMathTransform + where + S: ActixService, + S::Future: 'static, + { + type Request = S::Request; + type Response = u32; + type Error = S::Error; + type Future = Box>; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + self.0.poll_ready() + } + + fn call(&mut self, req: Self::Request) -> Self::Future { + let fut = self.0.call(req).map(|x| x * 17); + Box::new(fut) + } + } + + struct DoMath; + impl Transform for DoMath + where + S: ActixService, + S::Future: 'static, + { + type Request = S::Request; + type Response = u32; + type Error = S::Error; + type Transform = DoMathTransform; + type InitError = (); + type Future = FutureResult; + + fn new_transform(&self, service: S) -> Self::Future { + futures::finished(DoMathTransform(service)) + } } } - #[test] - fn tower_service_as_actix_service_returns_4() { - let mut s = RandomService.compat(); + mod actix_service_into_tower_service { + use crate::{ActixServiceExt, TowerServiceExt}; + use actix_service::{Service as ActixService, ServiceExt}; + use futures::{future::FutureResult, Async, Poll, Future}; + use tower_service::Service as TowerService; - assert_eq!(Ok(Async::Ready(())), s.poll_ready()); - assert_eq!(Ok(Async::Ready(4)), s.call(()).poll()); - } + #[test] + fn random_service_returns_4() { + let mut s = RandomService.into_tower_service(); - #[test] - fn tower_service_as_actix_service_can_combine() { - let mut s = RandomService.compat().map(|x| x + 1); + assert_eq!(Ok(Async::Ready(())), s.poll_ready()); - assert_eq!(Ok(Async::Ready(())), s.poll_ready()); - - assert_eq!(Ok(Async::Ready(5)), s.call(()).poll()); - } - - struct AddOneService; - impl TowerService for AddOneService { - type Response = u32; - type Error = (); - type Future = FutureResult; - - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + assert_eq!(Ok(Async::Ready(4)), s.call(()).poll()); } - fn call(&mut self, req: u32) -> Self::Future { - futures::finished(req + 1) - } - } + #[test] + fn random_service_can_use_tower_middleware() { + let mut s = AddOneService::wrap(RandomService.into_tower_service()).into_actix_service(); - #[test] - fn tower_services_as_actix_services_chained() { - let s1 = RandomService.compat(); - let s2 = AddOneService.compat(); - let s3 = AddOneService.compat(); + assert_eq!(Ok(Async::Ready(())), s.poll_ready()); - let mut s = s1.and_then(s2).and_then(s3); - - assert_eq!(Ok(Async::Ready(())), s.poll_ready()); - - assert_eq!(Ok(Async::Ready(6)), s.call(()).poll()); - } - - #[test] - fn tower_services_as_actix_services_chained_2() { - let s1 = RandomService.compat(); - let s2 = AddOneService.compat(); - let s3 = AddOneService.compat(); - let s4 = RandomService.compat(); - - let mut s = s1.and_then(s2).and_then(s3).and_then(s4); - - assert_eq!(Ok(Async::Ready(())), s.poll_ready()); - - assert_eq!(Ok(Async::Ready(4)), s.call(()).poll()); - } - - struct DoMathTransform(S); - impl ActixService for DoMathTransform - where - S: ActixService, - S::Future: 'static, - { - type Request = S::Request; - type Response = u32; - type Error = S::Error; - type Future = Box>; - - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.0.poll_ready() + assert_eq!(Ok(Async::Ready(5)), s.call(()).poll()); } - fn call(&mut self, req: Self::Request) -> Self::Future { - let fut = self.0.call(req).map(|x| x * 17); - Box::new(fut) + #[test] + fn do_math_service_can_use_tower_middleware() { + let mut s = AddOneService::wrap(DoMathService.into_tower_service()).into_actix_service(); + + assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + + assert_eq!(Ok(Async::Ready(188)), s.call(11).poll()); } - } - struct DoMath; - impl Transform for DoMath - where - S: ActixService, - S::Future: 'static, - { - type Request = S::Request; - type Response = u32; - type Error = S::Error; - type Transform = DoMathTransform; - type InitError = (); - type Future = FutureResult; + #[test] + fn random_service_and_add_service_and_ignoring_service_chained() { + let s1 = RandomService.wrap_with_tower_middleware(AddOneService::wrap); + let s2 = DoMathService.wrap_with_tower_middleware(AddOneService::wrap); - fn new_transform(&self, service: S) -> Self::Future { - futures::finished(DoMathTransform(service)) + let mut s = s1.and_then(s2); + + assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + + assert_eq!(Ok(Async::Ready(86)), s.call(()).poll()); } - } - #[test] - fn tower_service_as_actix_service_can_be_transformed() { - let transform = DoMath; + struct RandomService; + impl ActixService for RandomService { + type Request = (); + type Response = u32; + type Error = (); + type Future = FutureResult; - let mut s = transform.new_transform(RandomService.compat()).wait().unwrap(); + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + Ok(Async::Ready(())) + } - assert_eq!(Ok(Async::Ready(())), s.poll_ready()); + fn call(&mut self, _req: Self::Request) -> Self::Future { + futures::finished(4) + } + } - assert_eq!(Ok(Async::Ready(68)), s.call(()).poll()); - } + struct AddOneService { + inner: S + } + + impl AddOneService { + fn wrap(inner: S) -> Self { + AddOneService { + inner, + } + } + } + + impl TowerService for AddOneService + where + S: TowerService, + S::Future: 'static, + { + type Response = u32; + type Error = S::Error; + type Future = Box>; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + self.inner.poll_ready() + } + + fn call(&mut self, req: R) -> Self::Future { + let fut = self.inner.call(req) + .map(|x| x + 1); + + Box::new(fut) + } + } + + struct DoMathService; + impl ActixService for DoMathService { + type Request = u32; + type Response = u32; + type Error = (); + type Future = FutureResult; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + Ok(Async::Ready(())) + } + + fn call(&mut self, req: Self::Request) -> Self::Future { + futures::finished(req * 17) + } + }} } \ No newline at end of file