diff --git a/.gitignore b/.gitignore index 42d0755d..a6909f1f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ guide/build/ # These are backup files generated by rustfmt **/*.rs.bk + +.idea diff --git a/.travis.yml b/.travis.yml index 6eaf6d1e..5e18c3f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,10 +10,9 @@ matrix: include: - rust: stable - rust: beta - - rust: 1.37.0 - - rust: nightly-2019-06-15 + - rust: nightly-2019-11-07 allow_failures: - - rust: nightly-2019-06-15 + - rust: nightly-2019-11-07 env: global: @@ -26,7 +25,7 @@ before_install: - sudo apt-get install -y openssl libssl-dev libelf-dev libdw-dev cmake gcc binutils-dev libiberty-dev before_cache: | - if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-06-15" ]]; then + if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-11-07" ]]; then RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install --version 0.6.11 cargo-tarpaulin fi @@ -36,14 +35,14 @@ before_script: script: - | - if [[ "$TRAVIS_RUST_VERSION" != "nightly-2019-06-15" ]]; then + if [[ "$TRAVIS_RUST_VERSION" != "nightly-2019-11-07" ]]; then cargo clean cargo test --all --all-features -- --nocapture fi after_success: - | - if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-06-15" ]]; then + if [[ "$TRAVIS_RUST_VERSION" == "nightly-2019-11-07" ]]; then taskset -c 0 cargo tarpaulin --all --all-features --out Xml echo "Uploaded code coverage" bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index 5c100657..91ae2613 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,8 @@ Actix net - framework for composable network services ## Documentation & community resources -* [API Documentation (Development)](https://actix.rs/actix-net/actix_net/) * [Chat on gitter](https://gitter.im/actix/actix) -* Cargo package: [actix-net](https://crates.io/crates/actix-net) -* Minimum supported Rust version: 1.37 or later +* Minimum supported Rust version: 1.39 or later ## Example diff --git a/actix-codec/Cargo.toml b/actix-codec/Cargo.toml index 376e4b31..51a7b21b 100644 --- a/actix-codec/Cargo.toml +++ b/actix-codec/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-codec" -version = "0.1.2" +version = "0.2.0-alpha.1" authors = ["Nikolay Kim "] description = "Utilities for encoding and decoding frames" keywords = ["network", "framework", "async", "futures"] @@ -19,7 +19,8 @@ path = "src/lib.rs" [dependencies] bytes = "0.4.12" -futures = "0.1.24" -tokio-io = "0.1.12" -tokio-codec = "0.1.1" +pin-utils = "0.1.0-alpha.4" +futures = "0.3.1" +tokio-io = "0.2.0-alpha.6" +tokio-codec = "0.2.0-alpha.6" log = "0.4" \ No newline at end of file diff --git a/actix-codec/src/framed.rs b/actix-codec/src/framed.rs index 7fbb4f27..7152b459 100644 --- a/actix-codec/src/framed.rs +++ b/actix-codec/src/framed.rs @@ -2,9 +2,11 @@ use std::fmt; use std::io::{self, Read, Write}; +use std::pin::Pin; +use std::task::{Context, Poll}; use bytes::BytesMut; -use futures::{Poll, Sink, StartSend, Stream}; +use futures::{Sink, Stream}; use tokio_codec::{Decoder, Encoder}; use tokio_io::{AsyncRead, AsyncWrite}; @@ -26,8 +28,8 @@ pub struct Fuse(pub T, pub U); impl Framed where - T: AsyncRead + AsyncWrite, - U: Decoder + Encoder, + T: AsyncRead + AsyncWrite + Unpin, + U: Decoder + Encoder + Unpin, { /// Provides a `Stream` and `Sink` interface for reading and writing to this /// `Io` object, using `Decode` and `Encode` to read and write the raw data. @@ -221,41 +223,70 @@ impl Framed { } } -impl Stream for Framed -where - T: AsyncRead, - U: Decoder, -{ - type Item = U::Item; - type Error = U::Error; +impl Framed { + pub fn next_item(&mut self, cx: &mut Context<'_>) -> Poll>> + where + T: AsyncRead + Unpin, + U: Decoder + Unpin, + { + Pin::new(&mut self.inner).poll_next(cx) + } - fn poll(&mut self) -> Poll, Self::Error> { - self.inner.poll() + pub fn flush(&mut self, cx: &mut Context<'_>) -> Poll> + where + T: AsyncWrite + Unpin, + U: Encoder + Unpin, + { + Pin::new(self.inner.get_mut()).poll_flush(cx) } } -impl Sink for Framed +impl Stream for Framed where - T: AsyncWrite, - U: Encoder, + T: AsyncRead + Unpin, + U: Decoder + Unpin, +{ + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + Pin::new(&mut self.as_mut().inner).poll_next(cx) + } +} + +impl Sink for Framed +where + T: AsyncWrite + Unpin, + U: Encoder + Unpin, U::Error: From, { - type SinkItem = U::Item; - type SinkError = U::Error; + type Error = U::Error; + + fn poll_ready( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Pin::new(&mut self.as_mut().inner.get_mut()).poll_ready(cx) + } fn start_send( - &mut self, - item: Self::SinkItem, - ) -> StartSend { - self.inner.get_mut().start_send(item) + mut self: Pin<&mut Self>, + item: ::Item, + ) -> Result<(), Self::Error> { + Pin::new(&mut self.as_mut().inner.get_mut()).start_send(item) } - fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { - self.inner.get_mut().poll_complete() + fn poll_flush( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Pin::new(&mut self.as_mut().inner.get_mut()).poll_flush(cx) } - fn close(&mut self) -> Poll<(), Self::SinkError> { - self.inner.get_mut().close() + fn poll_close( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + Pin::new(&mut self.as_mut().inner.get_mut()).poll_close(cx) } } @@ -284,6 +315,14 @@ impl AsyncRead for Fuse { unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { self.0.prepare_uninitialized_buffer(buf) } + + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + unsafe { self.map_unchecked_mut(|s| &mut s.0).poll_read(cx, buf) } + } } impl Write for Fuse { @@ -297,8 +336,20 @@ impl Write for Fuse { } impl AsyncWrite for Fuse { - fn shutdown(&mut self) -> Poll<(), io::Error> { - self.0.shutdown() + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + unsafe { self.map_unchecked_mut(|s| &mut s.0).poll_write(cx, buf) } + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { self.map_unchecked_mut(|s| &mut s.0).poll_flush(cx) } + } + + fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { self.map_unchecked_mut(|s| &mut s.0).poll_shutdown(cx) } } } diff --git a/actix-codec/src/framed_read.rs b/actix-codec/src/framed_read.rs index b470d1bf..e4dc206e 100644 --- a/actix-codec/src/framed_read.rs +++ b/actix-codec/src/framed_read.rs @@ -1,7 +1,9 @@ use std::fmt; +use std::pin::Pin; +use std::task::{Context, Poll}; use bytes::BytesMut; -use futures::{try_ready, Async, Poll, Sink, StartSend, Stream}; +use futures::{Sink, Stream}; use log::trace; use tokio_codec::Decoder; use tokio_io::AsyncRead; @@ -83,34 +85,45 @@ where T: AsyncRead, D: Decoder, { - type Item = D::Item; - type Error = D::Error; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { - self.inner.poll() + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { self.map_unchecked_mut(|s| &mut s.inner).poll_next(cx) } } } -impl Sink for FramedRead +impl Sink for FramedRead where - T: Sink, + T: Sink, { - type SinkItem = T::SinkItem; - type SinkError = T::SinkError; + type Error = T::Error; - fn start_send( - &mut self, - item: Self::SinkItem, - ) -> StartSend { - self.inner.inner.0.start_send(item) + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { + self.map_unchecked_mut(|s| &mut s.inner.inner.0) + .poll_ready(cx) + } } - fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { - self.inner.inner.0.poll_complete() + fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> { + unsafe { + self.map_unchecked_mut(|s| &mut s.inner.inner.0) + .start_send(item) + } } - fn close(&mut self) -> Poll<(), Self::SinkError> { - self.inner.inner.0.close() + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { + self.map_unchecked_mut(|s| &mut s.inner.inner.0) + .poll_flush(cx) + } + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { + self.map_unchecked_mut(|s| &mut s.inner.inner.0) + .poll_close(cx) + } } } @@ -174,46 +187,61 @@ impl FramedRead2 { impl Stream for FramedRead2 where - T: AsyncRead + Decoder, + T: tokio_io::AsyncRead + Decoder, { - type Item = T::Item; - type Error = T::Error; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = unsafe { self.get_unchecked_mut() }; loop { // Repeatedly call `decode` or `decode_eof` as long as it is // "readable". Readable is defined as not having returned `None`. If // the upstream has returned EOF, and the decoder is no longer // readable, it can be assumed that the decoder will never become // readable again, at which point the stream is terminated. - if self.is_readable { - if self.eof { - let frame = self.inner.decode_eof(&mut self.buffer)?; - return Ok(Async::Ready(frame)); + + if this.is_readable { + if this.eof { + match this.inner.decode_eof(&mut this.buffer) { + Ok(Some(frame)) => return Poll::Ready(Some(Ok(frame))), + Ok(None) => return Poll::Ready(None), + Err(e) => return Poll::Ready(Some(Err(e))), + } } trace!("attempting to decode a frame"); - if let Some(frame) = self.inner.decode(&mut self.buffer)? { - trace!("frame decoded from buffer"); - return Ok(Async::Ready(Some(frame))); + match this.inner.decode(&mut this.buffer) { + Ok(Some(frame)) => { + trace!("frame decoded from buffer"); + return Poll::Ready(Some(Ok(frame))); + } + Err(e) => return Poll::Ready(Some(Err(e))), + _ => { + // Need more data + } } - self.is_readable = false; + this.is_readable = false; } - assert!(!self.eof); + assert!(!this.eof); // Otherwise, try to read more data and try again. Make sure we've // got room for at least one byte to read to ensure that we don't // get a spurious 0 that looks like EOF - self.buffer.reserve(1); - if 0 == try_ready!(self.inner.read_buf(&mut self.buffer)) { - trace!("read 0 bytes, mark stream as eof"); - self.eof = true; + this.buffer.reserve(1); + unsafe { + match Pin::new_unchecked(&mut this.inner).poll_read(cx, &mut this.buffer) { + Poll::Pending => return Poll::Pending, + Poll::Ready(Err(e)) => return Poll::Ready(Some(Err(e.into()))), + Poll::Ready(Ok(0)) => { + this.eof = true; + } + Poll::Ready(Ok(_cnt)) => {} + } } - - self.is_readable = true; + this.is_readable = true; } } } diff --git a/actix-codec/src/framed_write.rs b/actix-codec/src/framed_write.rs index 6b5c2b19..ce60d1eb 100644 --- a/actix-codec/src/framed_write.rs +++ b/actix-codec/src/framed_write.rs @@ -1,8 +1,10 @@ use std::fmt; use std::io::{self, Read}; +use std::pin::Pin; +use std::task::{Context, Poll}; use bytes::BytesMut; -use futures::{try_ready, Async, AsyncSink, Poll, Sink, StartSend, Stream}; +use futures::{ready, Sink, Stream}; use log::trace; use tokio_codec::{Decoder, Encoder}; use tokio_io::{AsyncRead, AsyncWrite}; @@ -95,24 +97,27 @@ where } } -impl Sink for FramedWrite +impl Sink for FramedWrite where T: AsyncWrite, E: Encoder, { - type SinkItem = E::Item; - type SinkError = E::Error; + type Error = E::Error; - fn start_send(&mut self, item: E::Item) -> StartSend { - self.inner.start_send(item) + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { self.map_unchecked_mut(|s| &mut s.inner).poll_ready(cx) } } - fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { - self.inner.poll_complete() + fn start_send(self: Pin<&mut Self>, item: ::Item) -> Result<(), Self::Error> { + unsafe { self.map_unchecked_mut(|s| &mut s.inner).start_send(item) } } - fn close(&mut self) -> Poll<(), Self::SinkError> { - Ok(self.inner.close()?) + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { self.map_unchecked_mut(|s| &mut s.inner).poll_flush(cx) } + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { self.map_unchecked_mut(|s| &mut s.inner).poll_close(cx) } } } @@ -121,10 +126,12 @@ where T: Stream, { type Item = T::Item; - type Error = T::Error; - fn poll(&mut self) -> Poll, Self::Error> { - self.inner.inner.0.poll() + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unsafe { + self.map_unchecked_mut(|s| &mut s.inner.inner.0) + .poll_next(cx) + } } } @@ -220,60 +227,75 @@ where } } -impl Sink for FramedWrite2 +impl Sink for FramedWrite2 where T: AsyncWrite + Encoder, { - type SinkItem = T::Item; - type SinkError = T::Error; + type Error = T::Error; - fn start_send(&mut self, item: T::Item) -> StartSend { - // Check the buffer capacity + fn poll_ready(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { let len = self.buffer.len(); if len >= self.high_watermark { - return Ok(AsyncSink::NotReady(item)); + return Poll::Pending; + } else { + return Poll::Ready(Ok(())); } - if len < self.low_watermark { - self.buffer.reserve(self.high_watermark - len) - } - - self.inner.encode(item, &mut self.buffer)?; - - Ok(AsyncSink::Ready) } - fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { + fn start_send(self: Pin<&mut Self>, item: ::Item) -> Result<(), Self::Error> { + let this = unsafe { self.get_unchecked_mut() }; + + // Check the buffer capacity + let len = this.buffer.len(); + if len < this.low_watermark { + this.buffer.reserve(this.high_watermark - len) + } + + this.inner.encode(item, &mut this.buffer)?; + + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = unsafe { self.get_unchecked_mut() }; trace!("flushing framed transport"); - while !self.buffer.is_empty() { - trace!("writing; remaining={}", self.buffer.len()); + while !this.buffer.is_empty() { + trace!("writing; remaining={}", this.buffer.len()); - let n = try_ready!(self.inner.poll_write(&self.buffer)); + let n = ready!( + unsafe { Pin::new_unchecked(&mut this.inner) }.poll_write(cx, &this.buffer) + )?; if n == 0 { - return Err(io::Error::new( + return Poll::Ready(Err(io::Error::new( io::ErrorKind::WriteZero, "failed to \ write frame to transport", ) - .into()); + .into())); } // TODO: Add a way to `bytes` to do this w/o returning the drained // data. - let _ = self.buffer.split_to(n); + let _ = this.buffer.split_to(n); } // Try flushing the underlying IO - try_ready!(self.inner.poll_flush()); + ready!(unsafe { Pin::new_unchecked(&mut this.inner) }.poll_flush(cx))?; trace!("framed transport flushed"); - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } - fn close(&mut self) -> Poll<(), Self::SinkError> { - try_ready!(self.poll_complete()); - Ok(self.inner.shutdown()?) + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = unsafe { self.get_unchecked_mut() }; + ready!( + unsafe { Pin::new_unchecked(&mut this).map_unchecked_mut(|s| *s) }.poll_flush(cx) + )?; + ready!(unsafe { Pin::new_unchecked(&mut this.inner) }.poll_shutdown(cx))?; + + Poll::Ready(Ok(())) } } @@ -300,4 +322,12 @@ impl AsyncRead for FramedWrite2 { unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool { self.inner.prepare_uninitialized_buffer(buf) } + + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + unsafe { self.map_unchecked_mut(|s| &mut s.inner).poll_read(cx, buf) } + } } diff --git a/actix-codec/src/lib.rs b/actix-codec/src/lib.rs index 77102a8c..06c66112 100644 --- a/actix-codec/src/lib.rs +++ b/actix-codec/src/lib.rs @@ -21,4 +21,5 @@ pub use self::framed_read::FramedRead; pub use self::framed_write::FramedWrite; pub use tokio_codec::{Decoder, Encoder}; +// TODO: Migrate to futures asyncRead pub use tokio_io::{AsyncRead, AsyncWrite}; diff --git a/actix-connect/Cargo.toml b/actix-connect/Cargo.toml index 0fa5c903..4ede79b8 100644 --- a/actix-connect/Cargo.toml +++ b/actix-connect/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "actix-connect" -version = "0.3.0" +version = "1.0.0-alpha.1" authors = ["Nikolay Kim "] -description = "Actix Connector - tcp connector service" +description = "Actix connect - tcp connector service" keywords = ["network", "framework", "async", "futures"] homepage = "https://actix.rs" repository = "https://github.com/actix/actix-net.git" @@ -14,7 +14,7 @@ edition = "2018" workspace = ".." [package.metadata.docs.rs] -features = ["ssl", "uri"] +features = ["openssl", "rustls", "uri"] [lib] name = "actix_connect" @@ -24,38 +24,41 @@ path = "src/lib.rs" default = ["uri"] # openssl -ssl = ["openssl", "tokio-openssl"] +openssl = ["open-ssl", "tokio-openssl"] #rustls -rust-tls = ["rustls", "tokio-rustls", "webpki"] +rustls = ["rust-tls", "tokio-rustls", "webpki"] # support http::Uri as connect address uri = ["http"] [dependencies] -actix-service = "0.4.0" -actix-codec = "0.1.2" -actix-utils = "0.4.0" -actix-rt = "0.2.5" +actix-service = "1.0.0-alpha.1" +actix-codec = "0.2.0-alpha.1" +actix-utils = "0.5.0-alpha.1" +actix-rt = "1.0.0-alpha.1" derive_more = "0.15" either = "1.5.2" -futures = "0.1.25" +futures = "0.3.1" +pin-project = "0.4.5" http = { version = "0.1.17", optional = true } log = "0.4" -tokio-tcp = "0.1.3" -tokio-current-thread = "0.1.5" -trust-dns-resolver = { version="0.11.0", default-features = false } +tokio-net = "=0.2.0-alpha.6" +tokio-executor = "=0.2.0-alpha.6" +trust-dns-resolver = { version="0.18.0-alpha.1", default-features = false } # openssl -openssl = { version="0.10", optional = true } -tokio-openssl = { version="0.3", optional = true } +open-ssl = { version="0.10", package = "openssl", optional = true } +tokio-openssl = { version = "0.4.0-alpha.6", optional = true } #rustls -rustls = { version = "0.16.0", optional = true } -tokio-rustls = { version = "0.10.0", optional = true } +rust-tls = { version = "0.16.0", package = "rustls", optional = true } +# tokio-rustls = { version = "0.10.0", optional = true } +tokio-rustls = { git = "https://github.com/quininer/tokio-rustls.git", branch = "tokio-0.2", optional = true } webpki = { version = "0.21", optional = true } [dev-dependencies] bytes = "0.4" -actix-testing = { version="0.2.0" } -actix-server-config = "0.2.0" +actix-testing = { version="0.3.0-alpha.1" } +actix-server-config = "0.3.0-alpha.1" +tokio = "0.2.0-alpha.6" diff --git a/actix-connect/src/connector.rs b/actix-connect/src/connector.rs index ecb61996..4b811bca 100644 --- a/actix-connect/src/connector.rs +++ b/actix-connect/src/connector.rs @@ -1,11 +1,15 @@ use std::collections::VecDeque; +use std::future::Future; +use std::io; use std::marker::PhantomData; use std::net::SocketAddr; +use std::pin::Pin; +use std::task::{Context, Poll}; -use actix_service::{NewService, Service}; -use futures::future::{err, ok, Either, FutureResult}; -use futures::{Async, Future, Poll}; -use tokio_tcp::{ConnectFuture, TcpStream}; +use actix_service::{Service, ServiceFactory}; +use futures::future::{err, ok, BoxFuture, Either, FutureExt, Ready}; +use pin_project::pin_project; +use tokio_net::tcp::TcpStream; use super::connect::{Address, Connect, Connection}; use super::error::ConnectError; @@ -37,14 +41,14 @@ impl Clone for TcpConnectorFactory { } } -impl NewService for TcpConnectorFactory { +impl ServiceFactory for TcpConnectorFactory { type Request = Connect; type Response = Connection; type Error = ConnectError; type Config = (); type Service = TcpConnector; type InitError = (); - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &()) -> Self::Future { ok(self.service()) @@ -71,10 +75,10 @@ impl Service for TcpConnector { type Request = Connect; type Response = Connection; type Error = ConnectError; - type Future = Either, FutureResult>; + type Future = Either, Ready>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: Connect) -> Self::Future { @@ -82,21 +86,22 @@ impl Service for TcpConnector { let Connect { req, addr, .. } = req; if let Some(addr) = addr { - Either::A(TcpConnectorResponse::new(req, port, addr)) + Either::Left(TcpConnectorResponse::new(req, port, addr)) } else { error!("TCP connector: got unresolved address"); - Either::B(err(ConnectError::Unresolverd)) + Either::Right(err(ConnectError::Unresolverd)) } } } +#[pin_project] #[doc(hidden)] /// Tcp stream connector response future pub struct TcpConnectorResponse { req: Option, port: u16, addrs: Option>, - stream: Option, + stream: Option>>, } impl TcpConnectorResponse { @@ -116,7 +121,7 @@ impl TcpConnectorResponse { req: Some(req), port, addrs: None, - stream: Some(TcpStream::connect(&addr)), + stream: Some(TcpStream::connect(addr).boxed()), }, either::Either::Right(addrs) => TcpConnectorResponse { req: Some(req), @@ -129,40 +134,40 @@ impl TcpConnectorResponse { } impl Future for TcpConnectorResponse { - type Item = Connection; - type Error = ConnectError; + type Output = Result, ConnectError>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); - fn poll(&mut self) -> Poll { // connect loop { - if let Some(new) = self.stream.as_mut() { - match new.poll() { - Ok(Async::Ready(sock)) => { - let req = self.req.take().unwrap(); + if let Some(new) = this.stream.as_mut() { + match new.as_mut().poll(cx) { + Poll::Ready(Ok(sock)) => { + let req = this.req.take().unwrap(); trace!( "TCP connector - successfully connected to connecting to {:?} - {:?}", req.host(), sock.peer_addr() ); - return Ok(Async::Ready(Connection::new(sock, req))); + return Poll::Ready(Ok(Connection::new(sock, req))); } - Ok(Async::NotReady) => return Ok(Async::NotReady), - Err(err) => { + Poll::Pending => return Poll::Pending, + Poll::Ready(Err(err)) => { trace!( "TCP connector - failed to connect to connecting to {:?} port: {}", - self.req.as_ref().unwrap().host(), - self.port, + this.req.as_ref().unwrap().host(), + this.port, ); - if self.addrs.is_none() || self.addrs.as_ref().unwrap().is_empty() { - return Err(err.into()); + if this.addrs.is_none() || this.addrs.as_ref().unwrap().is_empty() { + return Poll::Ready(Err(err.into())); } } } } // try to connect - self.stream = Some(TcpStream::connect( - &self.addrs.as_mut().unwrap().pop_front().unwrap(), - )); + let addr = this.addrs.as_mut().unwrap().pop_front().unwrap(); + *this.stream = Some(TcpStream::connect(addr).boxed()); } } } diff --git a/actix-connect/src/lib.rs b/actix-connect/src/lib.rs index dbeff07c..6663206a 100644 --- a/actix-connect/src/lib.rs +++ b/actix-connect/src/lib.rs @@ -31,12 +31,12 @@ pub use self::resolver::{Resolver, ResolverFactory}; pub use self::service::{ConnectService, ConnectServiceFactory, TcpConnectService}; use actix_rt::Arbiter; -use actix_service::{NewService, Service, ServiceExt}; -use tokio_tcp::TcpStream; +use actix_service::{pipeline, pipeline_factory, Service, ServiceFactory}; +use tokio_net::tcp::TcpStream; pub fn start_resolver(cfg: ResolverConfig, opts: ResolverOpts) -> AsyncResolver { let (resolver, bg) = AsyncResolver::new(cfg, opts); - tokio_current_thread::spawn(bg); + tokio_executor::current_thread::spawn(bg); resolver } @@ -55,7 +55,7 @@ pub(crate) fn get_default_resolver() -> AsyncResolver { }; let (resolver, bg) = AsyncResolver::new(cfg, opts); - tokio_current_thread::spawn(bg); + tokio_executor::current_thread::spawn(bg); Arbiter::set_item(DefaultResolver(resolver.clone())); resolver @@ -70,37 +70,37 @@ pub fn start_default_resolver() -> AsyncResolver { pub fn new_connector( resolver: AsyncResolver, ) -> impl Service, Response = Connection, Error = ConnectError> - + Clone { - Resolver::new(resolver).and_then(TcpConnector::new()) +{ + pipeline(Resolver::new(resolver)).and_then(TcpConnector::new()) } /// Create tcp connector service pub fn new_connector_factory( resolver: AsyncResolver, -) -> impl NewService< +) -> impl ServiceFactory< Config = (), Request = Connect, Response = Connection, Error = ConnectError, InitError = (), -> + Clone { - ResolverFactory::new(resolver).and_then(TcpConnectorFactory::new()) +> { + pipeline_factory(ResolverFactory::new(resolver)).and_then(TcpConnectorFactory::new()) } /// Create connector service with default parameters pub fn default_connector( ) -> impl Service, Response = Connection, Error = ConnectError> - + Clone { - Resolver::default().and_then(TcpConnector::new()) +{ + pipeline(Resolver::default()).and_then(TcpConnector::new()) } /// Create connector service factory with default parameters -pub fn default_connector_factory() -> impl NewService< +pub fn default_connector_factory() -> impl ServiceFactory< Config = (), Request = Connect, Response = Connection, Error = ConnectError, InitError = (), -> + Clone { - ResolverFactory::default().and_then(TcpConnectorFactory::new()) +> { + pipeline_factory(ResolverFactory::default()).and_then(TcpConnectorFactory::new()) } diff --git a/actix-connect/src/resolver.rs b/actix-connect/src/resolver.rs index 0d0234a2..56df979e 100644 --- a/actix-connect/src/resolver.rs +++ b/actix-connect/src/resolver.rs @@ -1,9 +1,12 @@ +use std::future::Future; use std::marker::PhantomData; use std::net::SocketAddr; +use std::pin::Pin; +use std::task::{Context, Poll}; -use actix_service::{NewService, Service}; -use futures::future::{ok, Either, FutureResult}; -use futures::{Async, Future, Poll}; +use actix_service::{Service, ServiceFactory}; +use futures::future::{ok, Either, Ready}; +use pin_project::pin_project; use trust_dns_resolver::lookup_ip::LookupIpFuture; use trust_dns_resolver::{AsyncResolver, Background}; @@ -52,14 +55,14 @@ impl Clone for ResolverFactory { } } -impl NewService for ResolverFactory { +impl ServiceFactory for ResolverFactory { type Request = Connect; type Response = Connect; type Error = ConnectError; type Config = (); type Service = Resolver; type InitError = (); - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &()) -> Self::Future { ok(self.service()) @@ -104,32 +107,34 @@ impl Service for Resolver { type Request = Connect; type Response = Connect; type Error = ConnectError; - type Future = Either, FutureResult, Self::Error>>; + type Future = Either, Ready, Self::Error>>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, mut req: Connect) -> Self::Future { if req.addr.is_some() { - Either::B(ok(req)) + Either::Right(ok(req)) } else if let Ok(ip) = req.host().parse() { req.addr = Some(either::Either::Left(SocketAddr::new(ip, req.port()))); - Either::B(ok(req)) + Either::Right(ok(req)) } else { trace!("DNS resolver: resolving host {:?}", req.host()); if self.resolver.is_none() { self.resolver = Some(get_default_resolver()); } - Either::A(ResolverFuture::new(req, self.resolver.as_ref().unwrap())) + Either::Left(ResolverFuture::new(req, self.resolver.as_ref().unwrap())) } } } +#[pin_project] #[doc(hidden)] /// Resolver future pub struct ResolverFuture { req: Option>, + #[pin] lookup: Background, } @@ -149,22 +154,15 @@ impl ResolverFuture { } impl Future for ResolverFuture { - type Item = Connect; - type Error = ConnectError; + type Output = Result, ConnectError>; - fn poll(&mut self) -> Poll { - match self.lookup.poll().map_err(|e| { - trace!( - "DNS resolver: failed to resolve host {:?} err: {}", - self.req.as_ref().unwrap().host(), - e - ); - e - })? { - Async::NotReady => Ok(Async::NotReady), - Async::Ready(ips) => { - let req = self.req.take().unwrap(); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); + match this.lookup.poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(ips)) => { + let req = this.req.take().unwrap(); let port = req.port(); let req = req.set_addrs(ips.iter().map(|ip| SocketAddr::new(ip, port))); @@ -175,11 +173,19 @@ impl Future for ResolverFuture { ); if req.addr.is_none() { - Err(ConnectError::NoRecords) + Poll::Ready(Err(ConnectError::NoRecords)) } else { - Ok(Async::Ready(req)) + Poll::Ready(Ok(req)) } } + Poll::Ready(Err(e)) => { + trace!( + "DNS resolver: failed to resolve host {:?} err: {}", + this.req.as_ref().unwrap().host(), + e + ); + Poll::Ready(Err(e.into())) + } } } } diff --git a/actix-connect/src/service.rs b/actix-connect/src/service.rs index 8bd3cf0d..917c849d 100644 --- a/actix-connect/src/service.rs +++ b/actix-connect/src/service.rs @@ -1,7 +1,11 @@ -use actix_service::{NewService, Service}; -use futures::future::{ok, FutureResult}; -use futures::{try_ready, Async, Future, Poll}; -use tokio_tcp::TcpStream; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use actix_service::{Service, ServiceFactory}; +use either::Either; +use futures::future::{ok, Ready}; +use tokio_net::tcp::TcpStream; use trust_dns_resolver::AsyncResolver; use crate::connect::{Address, Connect, Connection}; @@ -14,7 +18,7 @@ pub struct ConnectServiceFactory { resolver: ResolverFactory, } -impl ConnectServiceFactory { +impl ConnectServiceFactory { /// Construct new ConnectService factory pub fn new() -> Self { ConnectServiceFactory { @@ -66,14 +70,14 @@ impl Clone for ConnectServiceFactory { } } -impl NewService for ConnectServiceFactory { +impl ServiceFactory for ConnectServiceFactory { type Request = Connect; type Response = Connection; type Error = ConnectError; type Config = (); type Service = ConnectService; type InitError = (); - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &()) -> Self::Future { ok(self.service()) @@ -86,47 +90,66 @@ pub struct ConnectService { resolver: Resolver, } -impl Service for ConnectService { +impl Service for ConnectService { type Request = Connect; type Response = Connection; type Error = ConnectError; type Future = ConnectServiceResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: Connect) -> Self::Future { ConnectServiceResponse { - fut1: Some(self.resolver.call(req)), - fut2: None, + state: ConnectState::Resolve(self.resolver.call(req)), tcp: self.tcp.clone(), } } } -pub struct ConnectServiceResponse { - fut1: Option< as Service>::Future>, - fut2: Option< as Service>::Future>, +enum ConnectState { + Resolve( as Service>::Future), + Connect( as Service>::Future), +} + +impl ConnectState { + fn poll( + &mut self, + cx: &mut Context, + ) -> Either, ConnectError>>, Connect> { + match self { + ConnectState::Resolve(ref mut fut) => match Pin::new(fut).poll(cx) { + Poll::Pending => Either::Left(Poll::Pending), + Poll::Ready(Ok(res)) => Either::Right(res), + Poll::Ready(Err(err)) => Either::Left(Poll::Ready(Err(err))), + }, + ConnectState::Connect(ref mut fut) => Either::Left(Pin::new(fut).poll(cx)), + } + } +} + +pub struct ConnectServiceResponse { + state: ConnectState, tcp: TcpConnector, } -impl Future for ConnectServiceResponse { - type Item = Connection; - type Error = ConnectError; +impl Future for ConnectServiceResponse { + type Output = Result, ConnectError>; - fn poll(&mut self) -> Poll { - if let Some(ref mut fut) = self.fut1 { - let res = try_ready!(fut.poll()); - let _ = self.fut1.take(); - self.fut2 = Some(self.tcp.call(res)); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let res = match self.state.poll(cx) { + Either::Right(res) => { + self.state = ConnectState::Connect(self.tcp.call(res)); + self.state.poll(cx) + } + Either::Left(res) => return res, + }; + + match res { + Either::Left(res) => res, + Either::Right(_) => panic!(), } - - if let Some(ref mut fut) = self.fut2 { - return fut.poll(); - } - - Ok(Async::NotReady) } } @@ -136,48 +159,73 @@ pub struct TcpConnectService { resolver: Resolver, } -impl Service for TcpConnectService { +impl Service for TcpConnectService { type Request = Connect; type Response = TcpStream; type Error = ConnectError; type Future = TcpConnectServiceResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: Connect) -> Self::Future { TcpConnectServiceResponse { - fut1: Some(self.resolver.call(req)), - fut2: None, + state: TcpConnectState::Resolve(self.resolver.call(req)), tcp: self.tcp.clone(), } } } -pub struct TcpConnectServiceResponse { - fut1: Option< as Service>::Future>, - fut2: Option< as Service>::Future>, +enum TcpConnectState { + Resolve( as Service>::Future), + Connect( as Service>::Future), +} + +impl TcpConnectState { + fn poll( + &mut self, + cx: &mut Context, + ) -> Either>, Connect> { + match self { + TcpConnectState::Resolve(ref mut fut) => match Pin::new(fut).poll(cx) { + Poll::Pending => (), + Poll::Ready(Ok(res)) => return Either::Right(res), + Poll::Ready(Err(err)) => return Either::Left(Poll::Ready(Err(err))), + }, + TcpConnectState::Connect(ref mut fut) => { + if let Poll::Ready(res) = Pin::new(fut).poll(cx) { + return match res { + Ok(conn) => Either::Left(Poll::Ready(Ok(conn.into_parts().0))), + Err(err) => Either::Left(Poll::Ready(Err(err))), + }; + } + } + } + Either::Left(Poll::Pending) + } +} + +pub struct TcpConnectServiceResponse { + state: TcpConnectState, tcp: TcpConnector, } -impl Future for TcpConnectServiceResponse { - type Item = TcpStream; - type Error = ConnectError; +impl Future for TcpConnectServiceResponse { + type Output = Result; - fn poll(&mut self) -> Poll { - if let Some(ref mut fut) = self.fut1 { - let res = try_ready!(fut.poll()); - let _ = self.fut1.take(); - self.fut2 = Some(self.tcp.call(res)); - } - - if let Some(ref mut fut) = self.fut2 { - if let Async::Ready(conn) = fut.poll()? { - return Ok(Async::Ready(conn.into_parts().0)); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let res = match self.state.poll(cx) { + Either::Right(res) => { + self.state = TcpConnectState::Connect(self.tcp.call(res)); + self.state.poll(cx) } - } + Either::Left(res) => return res, + }; - Ok(Async::NotReady) + match res { + Either::Left(res) => res, + Either::Right(_) => panic!(), + } } } diff --git a/actix-connect/src/ssl/mod.rs b/actix-connect/src/ssl/mod.rs index 6c02848c..8b3bd2e8 100644 --- a/actix-connect/src/ssl/mod.rs +++ b/actix-connect/src/ssl/mod.rs @@ -1,12 +1,13 @@ //! SSL Services -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] mod openssl; -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] pub use self::openssl::{ OpensslConnectService, OpensslConnectServiceFactory, OpensslConnector, }; -#[cfg(feature = "rust-tls")] + +#[cfg(feature = "rustls")] mod rustls; -#[cfg(feature = "rust-tls")] +#[cfg(feature = "rustls")] pub use self::rustls::RustlsConnector; diff --git a/actix-connect/src/ssl/openssl.rs b/actix-connect/src/ssl/openssl.rs index ff1f846d..9f46808c 100644 --- a/actix-connect/src/ssl/openssl.rs +++ b/actix-connect/src/ssl/openssl.rs @@ -1,12 +1,15 @@ +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::{fmt, io}; use actix_codec::{AsyncRead, AsyncWrite}; -use actix_service::{NewService, Service}; -use futures::{future::ok, future::FutureResult, try_ready, Async, Future, Poll}; -use openssl::ssl::{HandshakeError, SslConnector}; -use tokio_openssl::{ConnectAsync, SslConnectorExt, SslStream}; -use tokio_tcp::TcpStream; +use actix_service::{Service, ServiceFactory}; +use futures::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready}; +use open_ssl::ssl::SslConnector; +use tokio_net::tcp::TcpStream; +use tokio_openssl::{HandshakeError, SslStream}; use trust_dns_resolver::AsyncResolver; use crate::{ @@ -30,15 +33,15 @@ impl OpensslConnector { impl OpensslConnector where - T: Address, - U: AsyncRead + AsyncWrite + fmt::Debug, + T: Address + Unpin + 'static, + U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, { pub fn service( connector: SslConnector, ) -> impl Service< Request = Connection, Response = Connection>, - Error = HandshakeError, + Error = io::Error, > { OpensslConnectorService { connector: connector, @@ -56,17 +59,17 @@ impl Clone for OpensslConnector { } } -impl NewService for OpensslConnector +impl ServiceFactory for OpensslConnector where - U: AsyncRead + AsyncWrite + fmt::Debug, + U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, { type Request = Connection; type Response = Connection>; - type Error = HandshakeError; + type Error = io::Error; type Config = (); type Service = OpensslConnectorService; type InitError = (); - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &()) -> Self::Future { ok(OpensslConnectorService { @@ -90,52 +93,62 @@ impl Clone for OpensslConnectorService { } } -impl Service for OpensslConnectorService +impl Service for OpensslConnectorService where - U: AsyncRead + AsyncWrite + fmt::Debug, + U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, { type Request = Connection; type Response = Connection>; - type Error = HandshakeError; - type Future = ConnectAsyncExt; + type Error = io::Error; + type Future = Either, Ready>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, stream: Connection) -> Self::Future { trace!("SSL Handshake start for: {:?}", stream.host()); let (io, stream) = stream.replace(()); - ConnectAsyncExt { - fut: SslConnectorExt::connect_async(&self.connector, stream.host(), io), - stream: Some(stream), + let host = stream.host().to_string(); + + match self.connector.configure() { + Err(e) => Either::Right(err(io::Error::new(io::ErrorKind::Other, e))), + Ok(config) => Either::Left(ConnectAsyncExt { + fut: async move { tokio_openssl::connect(config, &host, io).await } + .boxed_local(), + stream: Some(stream), + _t: PhantomData, + }), } } } pub struct ConnectAsyncExt { - fut: ConnectAsync, + fut: LocalBoxFuture<'static, Result, HandshakeError>>, stream: Option>, + _t: PhantomData, } -impl Future for ConnectAsyncExt +impl Future for ConnectAsyncExt where - U: AsyncRead + AsyncWrite + fmt::Debug, + U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static, { - type Item = Connection>; - type Error = HandshakeError; + type Output = Result>, io::Error>; - fn poll(&mut self) -> Poll { - match self.fut.poll().map_err(|e| { - trace!("SSL Handshake error: {:?}", e); - e - })? { - Async::Ready(stream) => { - let s = self.stream.take().unwrap(); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + + match Pin::new(&mut this.fut).poll(cx) { + Poll::Ready(Ok(stream)) => { + let s = this.stream.take().unwrap(); trace!("SSL Handshake success: {:?}", s.host()); - Ok(Async::Ready(s.replace(stream).1)) + Poll::Ready(Ok(s.replace(stream).1)) } - Async::NotReady => Ok(Async::NotReady), + Poll::Ready(Err(e)) => { + trace!("SSL Handshake error: {:?}", e); + Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, format!("{}", e)))) + } + Poll::Pending => Poll::Pending, } } } @@ -145,7 +158,7 @@ pub struct OpensslConnectServiceFactory { openssl: OpensslConnector, } -impl OpensslConnectServiceFactory { +impl OpensslConnectServiceFactory { /// Construct new OpensslConnectService factory pub fn new(connector: SslConnector) -> Self { OpensslConnectServiceFactory { @@ -183,14 +196,14 @@ impl Clone for OpensslConnectServiceFactory { } } -impl NewService for OpensslConnectServiceFactory { +impl ServiceFactory for OpensslConnectServiceFactory { type Request = Connect; type Response = SslStream; type Error = ConnectError; type Config = (); type Service = OpensslConnectService; type InitError = (); - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &()) -> Self::Future { ok(self.service()) @@ -203,14 +216,14 @@ pub struct OpensslConnectService { openssl: OpensslConnectorService, } -impl Service for OpensslConnectService { +impl Service for OpensslConnectService { type Request = Connect; type Response = SslStream; type Error = ConnectError; type Future = OpensslConnectServiceResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: Connect) -> Self::Future { @@ -222,30 +235,36 @@ impl Service for OpensslConnectService { } } -pub struct OpensslConnectServiceResponse { +pub struct OpensslConnectServiceResponse { fut1: Option< as Service>::Future>, fut2: Option< as Service>::Future>, openssl: OpensslConnectorService, } -impl Future for OpensslConnectServiceResponse { - type Item = SslStream; - type Error = ConnectError; +impl Future for OpensslConnectServiceResponse { + type Output = Result, ConnectError>; - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { if let Some(ref mut fut) = self.fut1 { - let res = try_ready!(fut.poll()); - let _ = self.fut1.take(); - self.fut2 = Some(self.openssl.call(res)); + match futures::ready!(Pin::new(fut).poll(cx)) { + Ok(res) => { + let _ = self.fut1.take(); + self.fut2 = Some(self.openssl.call(res)); + } + Err(e) => return Poll::Ready(Err(e.into())), + } } if let Some(ref mut fut) = self.fut2 { - let connect = try_ready!(fut - .poll() - .map_err(|e| ConnectError::Io(io::Error::new(io::ErrorKind::Other, e)))); - Ok(Async::Ready(connect.into_parts().0)) + match futures::ready!(Pin::new(fut).poll(cx)) { + Ok(connect) => Poll::Ready(Ok(connect.into_parts().0)), + Err(e) => Poll::Ready(Err(ConnectError::Io(io::Error::new( + io::ErrorKind::Other, + e, + )))), + } } else { - Ok(Async::NotReady) + Poll::Pending } } } diff --git a/actix-connect/src/ssl/rustls.rs b/actix-connect/src/ssl/rustls.rs index 465ef1e4..c1145ae4 100644 --- a/actix-connect/src/ssl/rustls.rs +++ b/actix-connect/src/ssl/rustls.rs @@ -1,10 +1,13 @@ use std::fmt; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite}; -use actix_service::{NewService, Service}; -use futures::{future::ok, future::FutureResult, Async, Future, Poll}; -use std::sync::Arc; +use actix_service::{Service, ServiceFactory}; +use futures::future::{ok, Ready}; use tokio_rustls::{client::TlsStream, rustls::ClientConfig, Connect, TlsConnector}; use webpki::DNSNameRef; @@ -27,8 +30,8 @@ impl RustlsConnector { impl RustlsConnector where - T: Address, - U: AsyncRead + AsyncWrite + fmt::Debug, + T: Address + Unpin, + U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, { pub fn service( connector: Arc, @@ -53,9 +56,9 @@ impl Clone for RustlsConnector { } } -impl NewService for RustlsConnector +impl ServiceFactory for RustlsConnector where - U: AsyncRead + AsyncWrite + fmt::Debug, + U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, { type Request = Connection; type Response = Connection>; @@ -63,7 +66,7 @@ where type Config = (); type Service = RustlsConnectorService; type InitError = (); - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &()) -> Self::Future { ok(RustlsConnectorService { @@ -78,17 +81,17 @@ pub struct RustlsConnectorService { _t: PhantomData<(T, U)>, } -impl Service for RustlsConnectorService +impl Service for RustlsConnectorService where - U: AsyncRead + AsyncWrite + fmt::Debug, + U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, { type Request = Connection; type Response = Connection>; type Error = std::io::Error; type Future = ConnectAsyncExt; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, stream: Connection) -> Self::Future { @@ -108,24 +111,20 @@ pub struct ConnectAsyncExt { stream: Option>, } -impl Future for ConnectAsyncExt +impl Future for ConnectAsyncExt where - U: AsyncRead + AsyncWrite + fmt::Debug, + U: AsyncRead + AsyncWrite + Unpin + fmt::Debug, { - type Item = Connection>; - type Error = std::io::Error; + type Output = Result>, std::io::Error>; - fn poll(&mut self) -> Poll { - match self.fut.poll().map_err(|e| { - trace!("SSL Handshake error: {:?}", e); - e - })? { - Async::Ready(stream) => { - let s = self.stream.take().unwrap(); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + Poll::Ready( + futures::ready!(Pin::new(&mut this.fut).poll(cx)).map(|stream| { + let s = this.stream.take().unwrap(); trace!("SSL Handshake success: {:?}", s.host()); - Ok(Async::Ready(s.replace(stream).1)) - } - Async::NotReady => Ok(Async::NotReady), - } + s.replace(stream).1 + }), + ) } } diff --git a/actix-connect/tests/test_connect.rs b/actix-connect/tests/test_connect.rs index 471dd314..f3bd5818 100644 --- a/actix-connect/tests/test_connect.rs +++ b/actix-connect/tests/test_connect.rs @@ -1,135 +1,137 @@ +use std::io; + use actix_codec::{BytesCodec, Framed}; use actix_server_config::Io; -use actix_service::{service_fn, NewService, Service}; +use actix_service::{service_fn, Service, ServiceFactory}; use actix_testing::{self as test, TestServer}; use bytes::Bytes; -use futures::{future::lazy, Future, Sink}; -use http::{HttpTryFrom, Uri}; +use futures::SinkExt; use trust_dns_resolver::config::{ResolverConfig, ResolverOpts}; -use actix_connect::{default_connector, Connect}; +use actix_connect::Connect; -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] #[test] fn test_string() { let srv = TestServer::with(|| { - service_fn(|io: Io| { - Framed::new(io.into_parts().0, BytesCodec) - .send(Bytes::from_static(b"test")) - .then(|_| Ok::<_, ()>(())) + service_fn(|io: Io| { + async { + let mut framed = Framed::new(io.into_parts().0, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + } }) }); - let mut conn = default_connector(); + let mut conn = actix_connect::default_connector(); let addr = format!("localhost:{}", srv.port()); let con = test::call_service(&mut conn, addr.into()); assert_eq!(con.peer_addr().unwrap(), srv.addr()); } -#[cfg(feature = "rust-tls")] +#[cfg(feature = "rustls")] #[test] fn test_rustls_string() { let srv = TestServer::with(|| { - service_fn(|io: Io| { - Framed::new(io.into_parts().0, BytesCodec) - .send(Bytes::from_static(b"test")) - .then(|_| Ok::<_, ()>(())) + service_fn(|io: Io| { + async { + let mut framed = Framed::new(io.into_parts().0, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + } }) }); - let mut conn = default_connector(); + let mut conn = actix_connect::default_connector(); let addr = format!("localhost:{}", srv.port()); let con = test::call_service(&mut conn, addr.into()); assert_eq!(con.peer_addr().unwrap(), srv.addr()); } -#[test] -fn test_static_str() { +#[tokio::test] +async fn test_static_str() { let srv = TestServer::with(|| { - service_fn(|io: Io| { - Framed::new(io.into_parts().0, BytesCodec) - .send(Bytes::from_static(b"test")) - .then(|_| Ok::<_, ()>(())) + service_fn(|io: Io| { + async { + let mut framed = Framed::new(io.into_parts().0, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + } }) }); - let resolver = test::block_on(lazy( - || Ok::<_, ()>(actix_connect::start_default_resolver()), - )) - .unwrap(); + let resolver = actix_connect::start_default_resolver(); + let mut conn = actix_connect::new_connector(resolver.clone()); - let mut conn = test::block_on(lazy(|| { - Ok::<_, ()>(actix_connect::new_connector(resolver.clone())) - })) - .unwrap(); - - let con = test::block_on(conn.call(Connect::with("10", srv.addr()))).unwrap(); + let con = conn.call(Connect::with("10", srv.addr())).await.unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); let connect = Connect::new(srv.host().to_owned()); - let mut conn = - test::block_on(lazy(|| Ok::<_, ()>(actix_connect::new_connector(resolver)))).unwrap(); - let con = test::block_on(conn.call(connect)); + let mut conn = actix_connect::new_connector(resolver); + let con = conn.call(connect).await; assert!(con.is_err()); } #[test] fn test_new_service() { let srv = TestServer::with(|| { - service_fn(|io: Io| { - Framed::new(io.into_parts().0, BytesCodec) - .send(Bytes::from_static(b"test")) - .then(|_| Ok::<_, ()>(())) + service_fn(|io: Io| { + async { + let mut framed = Framed::new(io.into_parts().0, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + } }) }); - let resolver = test::block_on(lazy(|| { - Ok::<_, ()>(actix_connect::start_resolver( - ResolverConfig::default(), - ResolverOpts::default(), - )) - })) - .unwrap(); - let factory = test::block_on(lazy(|| { - Ok::<_, ()>(actix_connect::new_connector_factory(resolver)) - })) - .unwrap(); + let resolver = test::block_on(async { + actix_connect::start_resolver(ResolverConfig::default(), ResolverOpts::default()) + }); + let factory = test::block_on(async { actix_connect::new_connector_factory(resolver) }); let mut conn = test::block_on(factory.new_service(&())).unwrap(); let con = test::block_on(conn.call(Connect::with("10", srv.addr()))).unwrap(); assert_eq!(con.peer_addr().unwrap(), srv.addr()); } -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] #[test] fn test_uri() { + use http::HttpTryFrom; + let srv = TestServer::with(|| { - service_fn(|io: Io| { - Framed::new(io.into_parts().0, BytesCodec) - .send(Bytes::from_static(b"test")) - .then(|_| Ok::<_, ()>(())) + service_fn(|io: Io| { + async { + let mut framed = Framed::new(io.into_parts().0, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + } }) }); - let mut conn = default_connector(); - let addr = Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); + let mut conn = actix_connect::default_connector(); + let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); let con = test::call_service(&mut conn, addr.into()); assert_eq!(con.peer_addr().unwrap(), srv.addr()); } -#[cfg(feature = "rust-tls")] +#[cfg(feature = "rustls")] #[test] fn test_rustls_uri() { + use http::HttpTryFrom; + let srv = TestServer::with(|| { - service_fn(|io: Io| { - Framed::new(io.into_parts().0, BytesCodec) - .send(Bytes::from_static(b"test")) - .then(|_| Ok::<_, ()>(())) + service_fn(|io: Io| { + async { + let mut framed = Framed::new(io.into_parts().0, BytesCodec); + framed.send(Bytes::from_static(b"test")).await?; + Ok::<_, io::Error>(()) + } }) }); - let mut conn = default_connector(); - let addr = Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); + let mut conn = actix_connect::default_connector(); + let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap(); let con = test::call_service(&mut conn, addr.into()); assert_eq!(con.peer_addr().unwrap(), srv.addr()); } diff --git a/actix-ioframe/Cargo.toml b/actix-ioframe/Cargo.toml index 97e32152..df86ad13 100644 --- a/actix-ioframe/Cargo.toml +++ b/actix-ioframe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-ioframe" -version = "0.1.1" +version = "0.3.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix framed service" keywords = ["network", "framework", "async", "futures"] @@ -18,18 +18,20 @@ name = "actix_ioframe" path = "src/lib.rs" [dependencies] -actix-service = "0.4.1" -actix-codec = "0.1.2" +actix-service = "1.0.0-alpha.1" +actix-codec = "0.2.0-alpha.1" +actix-utils = "0.5.0-alpha.1" bytes = "0.4" either = "1.5.2" -futures = "0.1.25" -tokio-current-thread = "0.1.4" +futures = "0.3.1" +pin-project = "0.4.5" +tokio-executor = "=0.2.0-alpha.6" log = "0.4" [dev-dependencies] -actix-rt = "0.2.2" -actix-connect = "0.3.0" -actix-testing = "0.2.0" -actix-server-config = "0.2.0" -tokio-tcp = "0.1" -tokio-timer = "0.2" +actix-rt = "1.0.0-alpha.1" +actix-connect = "1.0.0-alpha.1" +actix-testing = "0.3.0-alpha.1" +actix-server-config = "0.3.0-alpha.1" +tokio-net = "=0.2.0-alpha.6" +tokio-timer = "=0.3.0-alpha.6" diff --git a/actix-ioframe/src/connect.rs b/actix-ioframe/src/connect.rs index d7f403c2..703c3b14 100644 --- a/actix-ioframe/src/connect.rs +++ b/actix-ioframe/src/connect.rs @@ -1,7 +1,11 @@ use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed}; -use futures::unsync::mpsc; +use actix_utils::mpsc; +use futures::Stream; +use pin_project::pin_project; use crate::dispatcher::FramedMessage; use crate::sink::Sink; @@ -13,7 +17,7 @@ pub struct Connect { impl Connect where - Io: AsyncRead + AsyncWrite, + Io: AsyncRead + AsyncWrite + Unpin, { pub(crate) fn new(io: Io) -> Self { Self { @@ -24,9 +28,9 @@ where pub fn codec(self, codec: Codec) -> ConnectResult where - Codec: Encoder + Decoder, + Codec: Encoder + Decoder + Unpin, { - let (tx, rx) = mpsc::unbounded(); + let (tx, rx) = mpsc::channel(); let sink = Sink::new(tx); ConnectResult { @@ -38,10 +42,12 @@ where } } +#[pin_project] pub struct ConnectResult { pub(crate) state: St, + #[pin] pub(crate) framed: Framed, - pub(crate) rx: mpsc::UnboundedReceiver::Item>>, + pub(crate) rx: mpsc::Receiver::Item>>, pub(crate) sink: Sink<::Item>, } @@ -72,39 +78,41 @@ impl ConnectResult { } } -impl futures::Stream for ConnectResult +impl Stream for ConnectResult where - Io: AsyncRead + AsyncWrite, - Codec: Encoder + Decoder, + Io: AsyncRead + AsyncWrite + Unpin, + Codec: Encoder + Decoder + Unpin, { - type Item = ::Item; - type Error = ::Error; + type Item = Result<::Item, ::Error>; - fn poll(&mut self) -> futures::Poll, Self::Error> { - self.framed.poll() + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.project().framed.poll_next(cx) } } -impl futures::Sink for ConnectResult +impl futures::Sink<::Item> for ConnectResult where - Io: AsyncRead + AsyncWrite, - Codec: Encoder + Decoder, + Io: AsyncRead + AsyncWrite + Unpin, + Codec: Encoder + Decoder + Unpin, { - type SinkItem = ::Item; - type SinkError = ::Error; + type Error = ::Error; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().framed.poll_ready(cx) + } fn start_send( - &mut self, - item: Self::SinkItem, - ) -> futures::StartSend { - self.framed.start_send(item) + self: Pin<&mut Self>, + item: ::Item, + ) -> Result<(), Self::Error> { + self.project().framed.start_send(item) } - fn poll_complete(&mut self) -> futures::Poll<(), Self::SinkError> { - self.framed.poll_complete() + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().framed.poll_flush(cx) } - fn close(&mut self) -> futures::Poll<(), Self::SinkError> { - self.framed.close() + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.project().framed.poll_close(cx) } } diff --git a/actix-ioframe/src/dispatcher.rs b/actix-ioframe/src/dispatcher.rs index 05a7d05f..da9957b3 100644 --- a/actix-ioframe/src/dispatcher.rs +++ b/actix-ioframe/src/dispatcher.rs @@ -1,13 +1,17 @@ //! Framed dispatcher service and related utilities use std::collections::VecDeque; +use std::future::Future; use std::mem; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed}; use actix_service::{IntoService, Service}; -use futures::task::AtomicTask; -use futures::unsync::{mpsc, oneshot}; -use futures::{Async, Future, Poll, Sink as FutureSink, Stream}; +use actix_utils::task::LocalWaker; +use actix_utils::{mpsc, oneshot}; +use futures::future::ready; +use futures::{FutureExt, Sink as FutureSink, Stream}; use log::debug; use crate::cell::Cell; @@ -27,13 +31,14 @@ pub(crate) enum FramedMessage { /// FramedTransport - is a future that reads frames from Framed object /// and pass then to the service. +#[pin_project::pin_project] pub(crate) struct FramedDispatcher where S: Service, Response = Option>>, S::Error: 'static, S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Encoder + Decoder, + T: AsyncRead + AsyncWrite + Unpin, + U: Encoder + Decoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { @@ -42,7 +47,7 @@ where state: State, dispatch_state: FramedState, framed: Framed, - rx: Option::Item>>>, + rx: Option::Item>>>, inner: Cell::Item, S::Error>>, disconnect: Option>, } @@ -52,8 +57,8 @@ where S: Service, Response = Option>>, S::Error: 'static, S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Decoder + Encoder, + T: AsyncRead + AsyncWrite + Unpin, + U: Decoder + Encoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { @@ -61,7 +66,7 @@ where framed: Framed, state: State, service: F, - rx: mpsc::UnboundedReceiver::Item>>, + rx: mpsc::Receiver::Item>>, sink: Sink<::Item>, disconnect: Option>, ) -> Self { @@ -75,13 +80,13 @@ where dispatch_state: FramedState::Processing, inner: Cell::new(FramedDispatcherInner { buf: VecDeque::new(), - task: AtomicTask::new(), + task: LocalWaker::new(), }), } } } -enum FramedState { +enum FramedState { Processing, Error(ServiceError), FramedError(ServiceError), @@ -89,7 +94,7 @@ enum FramedState { Stopping, } -impl FramedState { +impl FramedState { fn stop(&mut self, tx: Option>) { match self { FramedState::FlushAndStop(ref mut vec) => { @@ -115,149 +120,7 @@ impl FramedState { struct FramedDispatcherInner { buf: VecDeque>, - task: AtomicTask, -} - -impl FramedDispatcher -where - S: Service, Response = Option>>, - S::Error: 'static, - S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Decoder + Encoder, - ::Item: 'static, - ::Error: std::fmt::Debug, -{ - fn disconnect(&mut self, error: bool) { - if let Some(ref disconnect) = self.disconnect { - (&*disconnect)(&mut *self.state.get_mut(), error); - } - } - - fn poll_read(&mut self) -> bool { - loop { - match self.service.poll_ready() { - Ok(Async::Ready(_)) => { - let item = match self.framed.poll() { - Ok(Async::Ready(Some(el))) => el, - Err(err) => { - self.dispatch_state = - FramedState::FramedError(ServiceError::Decoder(err)); - return true; - } - Ok(Async::NotReady) => return false, - Ok(Async::Ready(None)) => { - log::trace!("Client disconnected"); - self.dispatch_state = FramedState::Stopping; - return true; - } - }; - - let mut cell = self.inner.clone(); - tokio_current_thread::spawn( - self.service - .call(Item::new(self.state.clone(), self.sink.clone(), item)) - .then(move |item| { - let item = match item { - Ok(Some(item)) => Ok(item), - Ok(None) => return Ok(()), - Err(err) => Err(err), - }; - unsafe { - let inner = cell.get_mut(); - inner.buf.push_back(item); - inner.task.notify(); - } - Ok(()) - }), - ); - } - Ok(Async::NotReady) => return false, - Err(err) => { - self.dispatch_state = FramedState::Error(ServiceError::Service(err)); - return true; - } - } - } - } - - /// write to framed object - fn poll_write(&mut self) -> bool { - let inner = unsafe { self.inner.get_mut() }; - let mut rx_done = self.rx.is_none(); - let mut buf_empty = inner.buf.is_empty(); - loop { - while !self.framed.is_write_buf_full() { - if !buf_empty { - match inner.buf.pop_front().unwrap() { - Ok(msg) => { - if let Err(err) = self.framed.force_send(msg) { - self.dispatch_state = - FramedState::FramedError(ServiceError::Encoder(err)); - return true; - } - buf_empty = inner.buf.is_empty(); - } - Err(err) => { - self.dispatch_state = - FramedState::Error(ServiceError::Service(err)); - return true; - } - } - } - - if !rx_done && self.rx.is_some() { - match self.rx.as_mut().unwrap().poll() { - Ok(Async::Ready(Some(FramedMessage::Message(msg)))) => { - if let Err(err) = self.framed.force_send(msg) { - self.dispatch_state = - FramedState::FramedError(ServiceError::Encoder(err)); - return true; - } - } - Ok(Async::Ready(Some(FramedMessage::Close))) => { - self.dispatch_state.stop(None); - return true; - } - Ok(Async::Ready(Some(FramedMessage::WaitClose(tx)))) => { - self.dispatch_state.stop(Some(tx)); - return true; - } - Ok(Async::Ready(None)) => { - rx_done = true; - let _ = self.rx.take(); - } - Ok(Async::NotReady) => rx_done = true, - Err(_e) => { - rx_done = true; - let _ = self.rx.take(); - } - } - } - - if rx_done && buf_empty { - break; - } - } - - if !self.framed.is_write_buf_empty() { - match self.framed.poll_complete() { - Ok(Async::NotReady) => break, - Err(err) => { - debug!("Error sending data: {:?}", err); - self.dispatch_state = - FramedState::FramedError(ServiceError::Encoder(err)); - return true; - } - Ok(Async::Ready(_)) => (), - } - } else { - break; - } - } - - false - } + task: LocalWaker, } impl Future for FramedDispatcher @@ -265,63 +128,266 @@ where S: Service, Response = Option>>, S::Error: 'static, S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Decoder + Encoder, + T: AsyncRead + AsyncWrite + Unpin, + U: Decoder + Encoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { - type Item = (); - type Error = ServiceError; + type Output = Result<(), ServiceError>; - fn poll(&mut self) -> Poll { - unsafe { self.inner.get_ref().task.register() }; + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + unsafe { self.inner.get_ref().task.register(cx.waker()) }; - match mem::replace(&mut self.dispatch_state, FramedState::Processing) { - FramedState::Processing => { - if self.poll_read() || self.poll_write() { - self.poll() - } else { - Ok(Async::NotReady) - } + let this = self.project(); + poll( + cx, + this.service, + this.state, + this.sink, + this.framed, + this.dispatch_state, + this.rx, + this.inner, + this.disconnect, + ) + } +} + +fn poll( + cx: &mut Context, + srv: &mut S, + state: &mut State, + sink: &mut Sink<::Item>, + framed: &mut Framed, + dispatch_state: &mut FramedState, + rx: &mut Option::Item>>>, + inner: &mut Cell::Item, S::Error>>, + disconnect: &mut Option>, +) -> Poll>> +where + S: Service, Response = Option>>, + S::Error: 'static, + S::Future: 'static, + T: AsyncRead + AsyncWrite + Unpin, + U: Decoder + Encoder + Unpin, + ::Item: 'static, + ::Error: std::fmt::Debug, +{ + match mem::replace(dispatch_state, FramedState::Processing) { + FramedState::Processing => { + if poll_read(cx, srv, state, sink, framed, dispatch_state, inner) + || poll_write(cx, framed, dispatch_state, rx, inner) + { + poll( + cx, + srv, + state, + sink, + framed, + dispatch_state, + rx, + inner, + disconnect, + ) + } else { + Poll::Pending } - FramedState::Error(err) => { - if self.framed.is_write_buf_empty() - || (self.poll_write() || self.framed.is_write_buf_empty()) - { - self.disconnect(true); - Err(err) - } else { - self.dispatch_state = FramedState::Error(err); - Ok(Async::NotReady) + } + FramedState::Error(err) => { + if framed.is_write_buf_empty() + || (poll_write(cx, framed, dispatch_state, rx, inner) + || framed.is_write_buf_empty()) + { + if let Some(ref disconnect) = disconnect { + (&*disconnect)(&mut *state.get_mut(), true); } + Poll::Ready(Err(err)) + } else { + *dispatch_state = FramedState::Error(err); + Poll::Pending } - FramedState::FlushAndStop(mut vec) => { - if !self.framed.is_write_buf_empty() { - match self.framed.poll_complete() { - Err(err) => { - debug!("Error sending data: {:?}", err); - } - Ok(Async::NotReady) => { - self.dispatch_state = FramedState::FlushAndStop(vec); - return Ok(Async::NotReady); - } - Ok(Async::Ready(_)) => (), + } + FramedState::FlushAndStop(mut vec) => { + if !framed.is_write_buf_empty() { + match Pin::new(framed).poll_flush(cx) { + Poll::Ready(Err(err)) => { + debug!("Error sending data: {:?}", err); + } + Poll::Pending => { + *dispatch_state = FramedState::FlushAndStop(vec); + return Poll::Pending; + } + Poll::Ready(_) => (), + } + }; + for tx in vec.drain(..) { + let _ = tx.send(()); + } + if let Some(ref disconnect) = disconnect { + (&*disconnect)(&mut *state.get_mut(), false); + } + Poll::Ready(Ok(())) + } + FramedState::FramedError(err) => { + if let Some(ref disconnect) = disconnect { + (&*disconnect)(&mut *state.get_mut(), true); + } + Poll::Ready(Err(err)) + } + FramedState::Stopping => { + if let Some(ref disconnect) = disconnect { + (&*disconnect)(&mut *state.get_mut(), false); + } + Poll::Ready(Ok(())) + } + } +} + +fn poll_read( + cx: &mut Context, + srv: &mut S, + state: &mut State, + sink: &mut Sink<::Item>, + framed: &mut Framed, + dispatch_state: &mut FramedState, + inner: &mut Cell::Item, S::Error>>, +) -> bool +where + S: Service, Response = Option>>, + S::Error: 'static, + S::Future: 'static, + T: AsyncRead + AsyncWrite + Unpin, + U: Decoder + Encoder + Unpin, + ::Item: 'static, + ::Error: std::fmt::Debug, +{ + loop { + match srv.poll_ready(cx) { + Poll::Ready(Ok(_)) => { + let item = match framed.next_item(cx) { + Poll::Ready(Some(Ok(el))) => el, + Poll::Ready(Some(Err(err))) => { + *dispatch_state = FramedState::FramedError(ServiceError::Decoder(err)); + return true; + } + Poll::Pending => return false, + Poll::Ready(None) => { + log::trace!("Client disconnected"); + *dispatch_state = FramedState::Stopping; + return true; } }; - for tx in vec.drain(..) { - let _ = tx.send(()); - } - self.disconnect(false); - Ok(Async::Ready(())) + + let mut cell = inner.clone(); + tokio_executor::current_thread::spawn( + srv.call(Item::new(state.clone(), sink.clone(), item)) + .then(move |item| { + let item = match item { + Ok(Some(item)) => Ok(item), + Ok(None) => return ready(()), + Err(err) => Err(err), + }; + unsafe { + let inner = cell.get_mut(); + inner.buf.push_back(item); + inner.task.wake(); + } + ready(()) + }), + ); } - FramedState::FramedError(err) => { - self.disconnect(true); - Err(err) - } - FramedState::Stopping => { - self.disconnect(false); - Ok(Async::Ready(())) + Poll::Pending => return false, + Poll::Ready(Err(err)) => { + *dispatch_state = FramedState::Error(ServiceError::Service(err)); + return true; } } } } + +/// write to framed object +fn poll_write( + cx: &mut Context, + framed: &mut Framed, + dispatch_state: &mut FramedState, + rx: &mut Option::Item>>>, + inner: &mut Cell::Item, S::Error>>, +) -> bool +where + S: Service, Response = Option>>, + S::Error: 'static, + S::Future: 'static, + T: AsyncRead + AsyncWrite + Unpin, + U: Decoder + Encoder + Unpin, + ::Item: 'static, + ::Error: std::fmt::Debug, +{ + let inner = unsafe { inner.get_mut() }; + let mut rx_done = rx.is_none(); + let mut buf_empty = inner.buf.is_empty(); + loop { + while !framed.is_write_buf_full() { + if !buf_empty { + match inner.buf.pop_front().unwrap() { + Ok(msg) => { + if let Err(err) = framed.force_send(msg) { + *dispatch_state = + FramedState::FramedError(ServiceError::Encoder(err)); + return true; + } + buf_empty = inner.buf.is_empty(); + } + Err(err) => { + *dispatch_state = FramedState::Error(ServiceError::Service(err)); + return true; + } + } + } + + if !rx_done && rx.is_some() { + match Pin::new(rx.as_mut().unwrap()).poll_next(cx) { + Poll::Ready(Some(FramedMessage::Message(msg))) => { + if let Err(err) = framed.force_send(msg) { + *dispatch_state = + FramedState::FramedError(ServiceError::Encoder(err)); + return true; + } + } + Poll::Ready(Some(FramedMessage::Close)) => { + dispatch_state.stop(None); + return true; + } + Poll::Ready(Some(FramedMessage::WaitClose(tx))) => { + dispatch_state.stop(Some(tx)); + return true; + } + Poll::Ready(None) => { + rx_done = true; + let _ = rx.take(); + } + Poll::Pending => rx_done = true, + } + } + + if rx_done && buf_empty { + break; + } + } + + if !framed.is_write_buf_empty() { + match framed.flush(cx) { + Poll::Pending => break, + Poll::Ready(Err(err)) => { + debug!("Error sending data: {:?}", err); + *dispatch_state = FramedState::FramedError(ServiceError::Encoder(err)); + return true; + } + Poll::Ready(_) => (), + } + } else { + break; + } + } + + false +} diff --git a/actix-ioframe/src/service.rs b/actix-ioframe/src/service.rs index 98dbd366..96cdab47 100644 --- a/actix-ioframe/src/service.rs +++ b/actix-ioframe/src/service.rs @@ -1,9 +1,14 @@ +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder}; -use actix_service::{IntoNewService, IntoService, NewService, Service}; -use futures::{Async, Future, Poll}; +use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory}; +use either::Either; +use futures::future::{FutureExt, LocalBoxFuture}; +use pin_project::{pin_project, project}; use crate::connect::{Connect, ConnectResult}; use crate::dispatcher::FramedDispatcher; @@ -27,9 +32,9 @@ impl Builder { pub fn service(self, connect: F) -> ServiceBuilder where F: IntoService, - Io: AsyncRead + AsyncWrite, + Io: AsyncRead + AsyncWrite + Unpin, C: Service, Response = ConnectResult>, - Codec: Decoder + Encoder, + Codec: Decoder + Encoder + Unpin, { ServiceBuilder { connect: connect.into_service(), @@ -41,19 +46,19 @@ impl Builder { /// Construct framed handler new service with specified connect service pub fn factory(self, connect: F) -> NewServiceBuilder where - F: IntoNewService, - Io: AsyncRead + AsyncWrite, - C: NewService< + F: IntoServiceFactory, + Io: AsyncRead + AsyncWrite + Unpin, + C: ServiceFactory< Config = (), Request = Connect, Response = ConnectResult, >, C::Error: 'static, C::Future: 'static, - Codec: Decoder + Encoder, + Codec: Decoder + Encoder + Unpin, { NewServiceBuilder { - connect: connect.into_new_service(), + connect: connect.into_factory(), disconnect: None, _t: PhantomData, } @@ -69,10 +74,10 @@ pub struct ServiceBuilder { impl ServiceBuilder where St: 'static, - Io: AsyncRead + AsyncWrite, + Io: AsyncRead + AsyncWrite + Unpin, C: Service, Response = ConnectResult>, C::Error: 'static, - Codec: Decoder + Encoder, + Codec: Decoder + Encoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { @@ -93,8 +98,8 @@ where service: F, ) -> impl Service> where - F: IntoNewService, - T: NewService< + F: IntoServiceFactory, + T: ServiceFactory< Config = St, Request = RequestItem, Response = ResponseItem, @@ -104,7 +109,7 @@ where { FramedServiceImpl { connect: self.connect, - handler: Rc::new(service.into_new_service()), + handler: Rc::new(service.into_factory()), disconnect: self.disconnect.clone(), _t: PhantomData, } @@ -120,11 +125,15 @@ pub struct NewServiceBuilder { impl NewServiceBuilder where St: 'static, - Io: AsyncRead + AsyncWrite, - C: NewService, Response = ConnectResult>, + Io: AsyncRead + AsyncWrite + Unpin, + C: ServiceFactory< + Config = (), + Request = Connect, + Response = ConnectResult, + >, C::Error: 'static, C::Future: 'static, - Codec: Decoder + Encoder, + Codec: Decoder + Encoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { @@ -142,15 +151,15 @@ where pub fn finish( self, service: F, - ) -> impl NewService< + ) -> impl ServiceFactory< Config = Cfg, Request = Io, Response = (), Error = ServiceError, > where - F: IntoNewService, - T: NewService< + F: IntoServiceFactory, + T: ServiceFactory< Config = St, Request = RequestItem, Response = ResponseItem, @@ -160,7 +169,7 @@ where { FramedService { connect: self.connect, - handler: Rc::new(service.into_new_service()), + handler: Rc::new(service.into_factory()), disconnect: self.disconnect, _t: PhantomData, } @@ -174,21 +183,25 @@ pub(crate) struct FramedService { _t: PhantomData<(St, Io, Codec, Cfg)>, } -impl NewService for FramedService +impl ServiceFactory for FramedService where St: 'static, - Io: AsyncRead + AsyncWrite, - C: NewService, Response = ConnectResult>, + Io: AsyncRead + AsyncWrite + Unpin, + C: ServiceFactory< + Config = (), + Request = Connect, + Response = ConnectResult, + >, C::Error: 'static, C::Future: 'static, - T: NewService< + T: ServiceFactory< Config = St, Request = RequestItem, Response = ResponseItem, Error = C::Error, InitError = C::Error, > + 'static, - Codec: Decoder + Encoder, + Codec: Decoder + Encoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { @@ -198,23 +211,24 @@ where type Error = ServiceError; type InitError = C::InitError; type Service = FramedServiceImpl; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, _: &Cfg) -> Self::Future { let handler = self.handler.clone(); let disconnect = self.disconnect.clone(); // create connect service and then create service impl - Box::new( - self.connect - .new_service(&()) - .map(move |connect| FramedServiceImpl { + self.connect + .new_service(&()) + .map(move |result| { + result.map(move |connect| FramedServiceImpl { connect, handler, disconnect, _t: PhantomData, - }), - ) + }) + }) + .boxed_local() } } @@ -227,18 +241,18 @@ pub struct FramedServiceImpl { impl Service for FramedServiceImpl where - Io: AsyncRead + AsyncWrite, + Io: AsyncRead + AsyncWrite + Unpin, C: Service, Response = ConnectResult>, C::Error: 'static, - T: NewService< + T: ServiceFactory< Config = St, Request = RequestItem, Response = ResponseItem, Error = C::Error, InitError = C::Error, >, - <::Service as Service>::Future: 'static, - Codec: Decoder + Encoder, + <::Service as Service>::Future: 'static, + Codec: Decoder + Encoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { @@ -247,8 +261,8 @@ where type Error = ServiceError; type Future = FramedServiceImplResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.connect.poll_ready().map_err(|e| e.into()) + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.connect.poll_ready(cx).map_err(|e| e.into()) } fn call(&mut self, req: Io) -> Self::Future { @@ -256,108 +270,155 @@ where inner: FramedServiceImplResponseInner::Connect( self.connect.call(Connect::new(req)), self.handler.clone(), + self.disconnect.clone(), ), - disconnect: self.disconnect.clone(), } } } +#[pin_project] pub struct FramedServiceImplResponse where C: Service, Response = ConnectResult>, C::Error: 'static, - T: NewService< + T: ServiceFactory< Config = St, Request = RequestItem, Response = ResponseItem, Error = C::Error, InitError = C::Error, >, - <::Service as Service>::Future: 'static, - Io: AsyncRead + AsyncWrite, - Codec: Encoder + Decoder, + <::Service as Service>::Future: 'static, + Io: AsyncRead + AsyncWrite + Unpin, + Codec: Encoder + Decoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { inner: FramedServiceImplResponseInner, - disconnect: Option>, -} - -enum FramedServiceImplResponseInner -where - C: Service, Response = ConnectResult>, - C::Error: 'static, - T: NewService< - Config = St, - Request = RequestItem, - Response = ResponseItem, - Error = C::Error, - InitError = C::Error, - >, - <::Service as Service>::Future: 'static, - Io: AsyncRead + AsyncWrite, - Codec: Encoder + Decoder, - ::Item: 'static, - ::Error: std::fmt::Debug, -{ - Connect(C::Future, Rc), - Handler(T::Future, Option>), - Dispatcher(FramedDispatcher), } impl Future for FramedServiceImplResponse where C: Service, Response = ConnectResult>, C::Error: 'static, - T: NewService< + T: ServiceFactory< Config = St, Request = RequestItem, Response = ResponseItem, Error = C::Error, InitError = C::Error, >, - <::Service as Service>::Future: 'static, - Io: AsyncRead + AsyncWrite, - Codec: Encoder + Decoder, + <::Service as Service>::Future: 'static, + Io: AsyncRead + AsyncWrite + Unpin, + Codec: Encoder + Decoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { - type Item = (); - type Error = ServiceError; + type Output = Result<(), ServiceError>; - fn poll(&mut self) -> Poll { - match self.inner { - FramedServiceImplResponseInner::Connect(ref mut fut, ref handler) => { - match fut.poll()? { - Async::Ready(res) => { - self.inner = FramedServiceImplResponseInner::Handler( - handler.new_service(&res.state), - Some(res), - ); - self.poll() - } - Async::NotReady => Ok(Async::NotReady), - } - } - FramedServiceImplResponseInner::Handler(ref mut fut, ref mut res) => { - match fut.poll()? { - Async::Ready(handler) => { - let res = res.take().unwrap(); - self.inner = - FramedServiceImplResponseInner::Dispatcher(FramedDispatcher::new( - res.framed, - State::new(res.state), - handler, - res.rx, - res.sink, - self.disconnect.clone(), - )); - self.poll() - } - Async::NotReady => Ok(Async::NotReady), - } - } - FramedServiceImplResponseInner::Dispatcher(ref mut fut) => fut.poll(), + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.get_mut(); + + loop { + match unsafe { Pin::new_unchecked(&mut this.inner) }.poll(cx) { + Either::Left(new) => this.inner = new, + Either::Right(poll) => return poll, + }; + } + } +} + +#[pin_project] +enum FramedServiceImplResponseInner +where + C: Service, Response = ConnectResult>, + C::Error: 'static, + T: ServiceFactory< + Config = St, + Request = RequestItem, + Response = ResponseItem, + Error = C::Error, + InitError = C::Error, + >, + <::Service as Service>::Future: 'static, + Io: AsyncRead + AsyncWrite + Unpin, + Codec: Encoder + Decoder + Unpin, + ::Item: 'static, + ::Error: std::fmt::Debug, +{ + Connect(#[pin] C::Future, Rc, Option>), + Handler( + #[pin] T::Future, + Option>, + Option>, + ), + Dispatcher(FramedDispatcher), +} + +impl FramedServiceImplResponseInner +where + C: Service, Response = ConnectResult>, + C::Error: 'static, + T: ServiceFactory< + Config = St, + Request = RequestItem, + Response = ResponseItem, + Error = C::Error, + InitError = C::Error, + >, + <::Service as Service>::Future: 'static, + Io: AsyncRead + AsyncWrite + Unpin, + Codec: Encoder + Decoder + Unpin, + ::Item: 'static, + ::Error: std::fmt::Debug, +{ + #[project] + fn poll( + self: Pin<&mut Self>, + cx: &mut Context, + ) -> Either< + FramedServiceImplResponseInner, + Poll>>, + > { + #[project] + match self.project() { + FramedServiceImplResponseInner::Connect( + ref mut fut, + ref handler, + ref mut disconnect, + ) => match Pin::new(fut).poll(cx) { + Poll::Ready(Ok(res)) => Either::Left(FramedServiceImplResponseInner::Handler( + handler.new_service(&res.state), + Some(res), + disconnect.take(), + )), + Poll::Pending => Either::Right(Poll::Pending), + Poll::Ready(Err(e)) => Either::Right(Poll::Ready(Err(e.into()))), + }, + FramedServiceImplResponseInner::Handler( + ref mut fut, + ref mut res, + ref mut disconnect, + ) => match Pin::new(fut).poll(cx) { + Poll::Ready(Ok(handler)) => { + let res = res.take().unwrap(); + Either::Left(FramedServiceImplResponseInner::Dispatcher( + FramedDispatcher::new( + res.framed, + State::new(res.state), + handler, + res.rx, + res.sink, + disconnect.take(), + ), + )) + } + Poll::Pending => Either::Right(Poll::Pending), + Poll::Ready(Err(e)) => Either::Right(Poll::Ready(Err(e.into()))), + }, + FramedServiceImplResponseInner::Dispatcher(ref mut fut) => { + Either::Right(Pin::new(fut).poll(cx)) + } } } } diff --git a/actix-ioframe/src/sink.rs b/actix-ioframe/src/sink.rs index 4ebf7909..b3ecc968 100644 --- a/actix-ioframe/src/sink.rs +++ b/actix-ioframe/src/sink.rs @@ -1,11 +1,11 @@ use std::fmt; -use futures::unsync::{mpsc, oneshot}; -use futures::Future; +use actix_utils::{mpsc, oneshot}; +use futures::future::{Future, FutureExt}; use crate::dispatcher::FramedMessage; -pub struct Sink(mpsc::UnboundedSender>); +pub struct Sink(mpsc::Sender>); impl Clone for Sink { fn clone(&self) -> Self { @@ -14,26 +14,26 @@ impl Clone for Sink { } impl Sink { - pub(crate) fn new(tx: mpsc::UnboundedSender>) -> Self { + pub(crate) fn new(tx: mpsc::Sender>) -> Self { Sink(tx) } /// Close connection pub fn close(&self) { - let _ = self.0.unbounded_send(FramedMessage::Close); + let _ = self.0.send(FramedMessage::Close); } /// Close connection - pub fn wait_close(&self) -> impl Future { + pub fn wait_close(&self) -> impl Future { let (tx, rx) = oneshot::channel(); - let _ = self.0.unbounded_send(FramedMessage::WaitClose(tx)); + let _ = self.0.send(FramedMessage::WaitClose(tx)); - rx.map_err(|_| ()) + rx.map(|_| ()) } /// Send item pub fn send(&self, item: T) { - let _ = self.0.unbounded_send(FramedMessage::Message(item)); + let _ = self.0.send(FramedMessage::Message(item)); } } diff --git a/actix-ioframe/tests/test_server.rs b/actix-ioframe/tests/test_server.rs index c197bb56..f0e0d697 100644 --- a/actix-ioframe/tests/test_server.rs +++ b/actix-ioframe/tests/test_server.rs @@ -4,11 +4,11 @@ use std::time::Duration; use actix_codec::BytesCodec; use actix_server_config::Io; -use actix_service::{new_apply_fn, Service}; +use actix_service::{apply_fn_factory, service_fn, Service}; use actix_testing::{self as test, TestServer}; -use futures::Future; -use tokio_tcp::TcpStream; -use tokio_timer::sleep; +use futures::future::ok; +use tokio_net::tcp::TcpStream; +use tokio_timer::delay_for; use actix_ioframe::{Builder, Connect}; @@ -22,13 +22,15 @@ fn test_disconnect() -> std::io::Result<()> { let srv = TestServer::with(move || { let disconnect1 = disconnect1.clone(); - new_apply_fn( + apply_fn_factory( Builder::new() - .factory(|conn: Connect<_>| Ok(conn.codec(BytesCodec).state(State))) + .factory(service_fn(|conn: Connect<_>| { + ok(conn.codec(BytesCodec).state(State)) + })) .disconnect(move |_, _| { disconnect1.store(true, Ordering::Relaxed); }) - .finish(|_t| Ok(None)), + .finish(service_fn(|_t| ok(None))), |io: Io, srv| srv.call(io.into_parts().0), ) }); @@ -37,9 +39,9 @@ fn test_disconnect() -> std::io::Result<()> { .service(|conn: Connect<_>| { let conn = conn.codec(BytesCodec).state(State); conn.sink().close(); - Ok(conn) + ok(conn) }) - .finish(|_t| Ok(None)); + .finish(service_fn(|_t| ok(None))); let conn = test::block_on( actix_connect::default_connector() @@ -48,11 +50,7 @@ fn test_disconnect() -> std::io::Result<()> { .unwrap(); test::block_on(client.call(conn.into_parts().0)).unwrap(); - let _ = test::block_on( - sleep(Duration::from_millis(100)) - .map(|_| ()) - .map_err(|_| ()), - ); + let _ = test::block_on(delay_for(Duration::from_millis(100))); assert!(disconnect.load(Ordering::Relaxed)); Ok(()) diff --git a/actix-rt/Cargo.toml b/actix-rt/Cargo.toml index af57286e..f134bd84 100644 --- a/actix-rt/Cargo.toml +++ b/actix-rt/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-rt" -version = "0.2.6" +version = "1.0.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix runtime" keywords = ["network", "framework", "async", "futures"] @@ -18,10 +18,13 @@ name = "actix_rt" path = "src/lib.rs" [dependencies] -actix-threadpool = "0.1.1" -futures = "0.1.25" -tokio-current-thread = "0.1" -tokio-executor = "0.1.5" -tokio-reactor = "0.1.7" -tokio-timer = "0.2.8" +actix-threadpool = "0.2" +futures = "0.3.1" + +# TODO: Replace this with dependency on tokio-runtime once it is ready +tokio = { version = "0.2.0-alpha.6" } +tokio-timer = "=0.3.0-alpha.6" +tokio-executor = "=0.2.0-alpha.6" +tokio-net = "=0.2.0-alpha.6" + copyless = "0.1.4" diff --git a/actix-rt/src/arbiter.rs b/actix-rt/src/arbiter.rs index c3357a8a..0a0456da 100644 --- a/actix-rt/src/arbiter.rs +++ b/actix-rt/src/arbiter.rs @@ -1,13 +1,15 @@ use std::any::{Any, TypeId}; use std::cell::{Cell, RefCell}; use std::collections::HashMap; +use std::pin::Pin; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::task::{Context, Poll}; use std::{fmt, thread}; -use futures::sync::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; -use futures::sync::oneshot::{channel, Canceled, Sender}; -use futures::{future, Async, Future, IntoFuture, Poll, Stream}; -use tokio_current_thread::spawn; +use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; +use futures::channel::oneshot::{channel, Canceled, Sender}; +use futures::{future, Future, FutureExt, Stream}; +use tokio::runtime::current_thread::spawn; use crate::builder::Builder; use crate::system::System; @@ -17,7 +19,7 @@ use copyless::BoxHelper; thread_local!( static ADDR: RefCell> = RefCell::new(None); static RUNNING: Cell = Cell::new(false); - static Q: RefCell>>> = RefCell::new(Vec::new()); + static Q: RefCell>>> = RefCell::new(Vec::new()); static STORAGE: RefCell>> = RefCell::new(HashMap::new()); ); @@ -25,7 +27,7 @@ pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0); pub(crate) enum ArbiterCommand { Stop, - Execute(Box + Send>), + Execute(Box + Unpin + Send>), ExecuteFn(Box), } @@ -96,41 +98,49 @@ impl Arbiter { let (arb_tx, arb_rx) = unbounded(); let arb_tx2 = arb_tx.clone(); - let handle = thread::Builder::new().name(name.clone()).spawn(move || { - let mut rt = Builder::new().build_rt().expect("Can not create Runtime"); - let arb = Arbiter::with_sender(arb_tx); + let handle = thread::Builder::new() + .name(name.clone()) + .spawn(move || { + let mut rt = Builder::new().build_rt().expect("Can not create Runtime"); + let arb = Arbiter::with_sender(arb_tx); - let (stop, stop_rx) = channel(); - RUNNING.with(|cell| cell.set(true)); - STORAGE.with(|cell| cell.borrow_mut().clear()); + let (stop, stop_rx) = channel(); + RUNNING.with(|cell| cell.set(true)); + STORAGE.with(|cell| cell.borrow_mut().clear()); - System::set_current(sys); + System::set_current(sys); - // start arbiter controller - rt.spawn(ArbiterController { - stop: Some(stop), - rx: arb_rx, + // start arbiter controller + rt.spawn(ArbiterController { + stop: Some(stop), + rx: arb_rx, + }); + ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); + + // register arbiter + let _ = System::current() + .sys() + .unbounded_send(SystemCommand::RegisterArbiter(id, arb.clone())); + + // run loop + let _ = match rt.block_on(stop_rx) { + Ok(code) => code, + Err(_) => 1, + }; + + // unregister arbiter + let _ = System::current() + .sys() + .unbounded_send(SystemCommand::UnregisterArbiter(id)); + }) + .unwrap_or_else(|err| { + panic!("Cannot spawn an arbiter's thread {:?}: {:?}", &name, err) }); - ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone())); - // register arbiter - let _ = System::current() - .sys() - .unbounded_send(SystemCommand::RegisterArbiter(id, arb.clone())); - - // run loop - let _ = match rt.block_on(stop_rx) { - Ok(code) => code, - Err(_) => 1, - }; - - // unregister arbiter - let _ = System::current() - .sys() - .unbounded_send(SystemCommand::UnregisterArbiter(id)); - }).unwrap_or_else(|err| panic!("Cannot spawn an arbiter's thread {:?}: {:?}", &name, err)); - - Arbiter{sender: arb_tx2, thread_handle: Some(handle)} + Arbiter { + sender: arb_tx2, + thread_handle: Some(handle), + } } pub(crate) fn run_system() { @@ -138,7 +148,9 @@ impl Arbiter { Q.with(|cell| { let mut v = cell.borrow_mut(); for fut in v.drain(..) { - spawn(fut); + // We pin the boxed future, so it can never again be moved. + let fut = unsafe { Pin::new_unchecked(fut) }; + tokio_executor::current_thread::spawn(fut); } }); } @@ -152,12 +164,15 @@ impl Arbiter { /// thread. pub fn spawn(future: F) where - F: Future + 'static, + F: Future + 'static, { RUNNING.with(move |cell| { if cell.get() { - spawn(Box::alloc().init(future)); + // Spawn the future on running executor + spawn(future); } else { + // Box the future and push it to the queue, this results in double boxing + // because the executor boxes the future again, but works for now Q.with(move |cell| cell.borrow_mut().push(Box::alloc().init(future))); } }); @@ -169,15 +184,15 @@ impl Arbiter { pub fn spawn_fn(f: F) where F: FnOnce() -> R + 'static, - R: IntoFuture + 'static, + R: Future + 'static, { - Arbiter::spawn(future::lazy(f)) + Arbiter::spawn(future::lazy(|_| f()).flatten()) } /// Send a future to the Arbiter's thread, and spawn it. pub fn send(&self, future: F) where - F: Future + Send + 'static, + F: Future + Send + Unpin + 'static, { let _ = self .sender @@ -200,7 +215,7 @@ impl Arbiter { /// Send a function to the Arbiter's thread. This function will be executed asynchronously. /// A future is created, and when resolved will contain the result of the function sent /// to the Arbiters thread. - pub fn exec(&self, f: F) -> impl Future + pub fn exec(&self, f: F) -> impl Future> where F: FnOnce() -> R + Send + 'static, R: Send + 'static, @@ -261,15 +276,17 @@ impl Arbiter { } fn with_sender(sender: UnboundedSender) -> Self { - Self{sender, thread_handle: None} + Self { + sender, + thread_handle: None, + } } /// Wait for the event loop to stop by joining the underlying thread (if have Some). - pub fn join(&mut self) -> thread::Result<()>{ + pub fn join(&mut self) -> thread::Result<()> { if let Some(thread_handle) = self.thread_handle.take() { thread_handle.join() - } - else { + } else { Ok(()) } } @@ -294,19 +311,18 @@ impl Drop for ArbiterController { } impl Future for ArbiterController { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { - match self.rx.poll() { - Ok(Async::Ready(None)) | Err(_) => return Ok(Async::Ready(())), - Ok(Async::Ready(Some(item))) => match item { + match Pin::new(&mut self.rx).poll_next(cx) { + Poll::Ready(None) => return Poll::Ready(()), + Poll::Ready(Some(item)) => match item { ArbiterCommand::Stop => { if let Some(stop) = self.stop.take() { let _ = stop.send(0); }; - return Ok(Async::Ready(())); + return Poll::Ready(()); } ArbiterCommand::Execute(fut) => { spawn(fut); @@ -315,7 +331,7 @@ impl Future for ArbiterController { f.call_box(); } }, - Ok(Async::NotReady) => return Ok(Async::NotReady), + Poll::Pending => return Poll::Pending, } } } @@ -346,14 +362,13 @@ impl SystemArbiter { } impl Future for SystemArbiter { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { - match self.commands.poll() { - Ok(Async::Ready(None)) | Err(_) => return Ok(Async::Ready(())), - Ok(Async::Ready(Some(cmd))) => match cmd { + match Pin::new(&mut self.commands).poll_next(cx) { + Poll::Ready(None) => return Poll::Ready(()), + Poll::Ready(Some(cmd)) => match cmd { SystemCommand::Exit(code) => { // stop arbiters for arb in self.arbiters.values() { @@ -371,7 +386,7 @@ impl Future for SystemArbiter { self.arbiters.remove(&name); } }, - Ok(Async::NotReady) => return Ok(Async::NotReady), + Poll::Pending => return Poll::Pending, } } } diff --git a/actix-rt/src/builder.rs b/actix-rt/src/builder.rs index 71509f14..1083b53a 100644 --- a/actix-rt/src/builder.rs +++ b/actix-rt/src/builder.rs @@ -1,19 +1,19 @@ use std::borrow::Cow; use std::io; -use futures::future; +use futures::channel::mpsc::unbounded; +use futures::channel::oneshot::{channel, Receiver}; use futures::future::{lazy, Future}; -use futures::sync::mpsc::unbounded; -use futures::sync::oneshot::{channel, Receiver}; +use futures::{future, FutureExt}; -use tokio_current_thread::{CurrentThread, Handle}; -use tokio_reactor::Reactor; -use tokio_timer::clock::Clock; -use tokio_timer::timer::Timer; +use tokio::runtime::current_thread::Handle; +use tokio_net::driver::Reactor; +use tokio_timer::{clock::Clock, timer::Timer}; use crate::arbiter::{Arbiter, SystemArbiter}; use crate::runtime::Runtime; use crate::system::System; +use tokio_executor::current_thread::CurrentThread; /// Builder struct for a actix runtime. /// @@ -118,7 +118,7 @@ impl Builder { rt.spawn(arb); // init system arbiter and run configuration method - let _ = rt.block_on(lazy(move || { + let _ = rt.block_on(lazy(move |_| { f(); Ok::<_, ()>(()) })); @@ -159,30 +159,31 @@ pub(crate) struct AsyncSystemRunner { impl AsyncSystemRunner { /// This function will start event loop and returns a future that /// resolves once the `System::stop()` function is called. - pub(crate) fn run_nonblocking(self) -> impl Future + Send { + pub(crate) fn run_nonblocking(self) -> impl Future> + Send { let AsyncSystemRunner { stop, .. } = self; // run loop - future::lazy(|| { + future::lazy(|_| { Arbiter::run_system(); - stop.then(|res| match res { - Ok(code) => { - if code != 0 { - Err(io::Error::new( - io::ErrorKind::Other, - format!("Non-zero exit code: {}", code), - )) - } else { - Ok(()) + async { + let res = match stop.await { + Ok(code) => { + if code != 0 { + Err(io::Error::new( + io::ErrorKind::Other, + format!("Non-zero exit code: {}", code), + )) + } else { + Ok(()) + } } - } - Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), - }) - .then(|result| { + Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)), + }; Arbiter::stop_system(); - result - }) + return res; + } }) + .flatten() } } @@ -202,10 +203,10 @@ impl SystemRunner { let SystemRunner { mut rt, stop, .. } = self; // run loop - let _ = rt.block_on(lazy(move || { + let _ = rt.block_on(async { Arbiter::run_system(); Ok::<_, ()>(()) - })); + }); let result = match rt.block_on(stop) { Ok(code) => { if code != 0 { @@ -224,19 +225,19 @@ impl SystemRunner { } /// Execute a future and wait for result. - pub fn block_on(&mut self, fut: F) -> Result + pub fn block_on(&mut self, fut: F) -> O where - F: Future, + F: Future, { - let _ = self.rt.block_on(lazy(move || { + let _ = self.rt.block_on(async { Arbiter::run_system(); - Ok::<_, ()>(()) - })); + }); + let res = self.rt.block_on(fut); - let _ = self.rt.block_on(lazy(move || { + let _ = self.rt.block_on(async { Arbiter::stop_system(); - Ok::<_, ()>(()) - })); + }); + res } } diff --git a/actix-rt/src/lib.rs b/actix-rt/src/lib.rs index 9b16b959..eeb768b2 100644 --- a/actix-rt/src/lib.rs +++ b/actix-rt/src/lib.rs @@ -20,7 +20,7 @@ pub use actix_threadpool as blocking; /// This function panics if actix system is not running. pub fn spawn(f: F) where - F: futures::Future + 'static, + F: futures::Future + 'static, { if !System::is_set() { panic!("System is not running"); diff --git a/actix-rt/src/runtime.rs b/actix-rt/src/runtime.rs index c6b2a9fc..ac1a6f6e 100644 --- a/actix-rt/src/runtime.rs +++ b/actix-rt/src/runtime.rs @@ -2,11 +2,12 @@ use std::error::Error; use std::{fmt, io}; use futures::Future; -use tokio_current_thread::{self as current_thread, CurrentThread}; -use tokio_executor; -use tokio_reactor::{self, Reactor}; -use tokio_timer::clock::{self, Clock}; -use tokio_timer::timer::{self, Timer}; +use tokio_executor::current_thread::{self, CurrentThread}; +use tokio_net::driver::{Handle as ReactorHandle, Reactor}; +use tokio_timer::{ + clock::Clock, + timer::{self, Timer}, +}; use crate::builder::Builder; @@ -18,7 +19,7 @@ use crate::builder::Builder; /// [mod]: index.html #[derive(Debug)] pub struct Runtime { - reactor_handle: tokio_reactor::Handle, + reactor_handle: ReactorHandle, timer_handle: timer::Handle, clock: Clock, executor: CurrentThread>, @@ -53,7 +54,7 @@ impl Runtime { } pub(super) fn new2( - reactor_handle: tokio_reactor::Handle, + reactor_handle: ReactorHandle, timer_handle: timer::Handle, clock: Clock, executor: CurrentThread>, @@ -83,9 +84,8 @@ impl Runtime { /// let mut rt = Runtime::new().unwrap(); /// /// // Spawn a future onto the runtime - /// rt.spawn(future::lazy(|| { + /// rt.spawn(future::lazy(|_| { /// println!("running on the runtime"); - /// Ok(()) /// })); /// # } /// # pub fn main() {} @@ -97,7 +97,7 @@ impl Runtime { /// is currently at capacity and is unable to spawn a new future. pub fn spawn(&mut self, future: F) -> &mut Self where - F: Future + 'static, + F: Future + 'static, { self.executor.spawn(future); self @@ -119,14 +119,14 @@ impl Runtime { /// /// The caller is responsible for ensuring that other spawned futures /// complete execution by calling `block_on` or `run`. - pub fn block_on(&mut self, f: F) -> Result + pub fn block_on(&mut self, f: F) -> F::Output where F: Future, { self.enter(|executor| { // Run the provided future let ret = executor.block_on(f); - ret.map_err(|e| e.into_inner().expect("unexpected execution error")) + ret }) } @@ -139,7 +139,7 @@ impl Runtime { fn enter(&mut self, f: F) -> R where - F: FnOnce(&mut current_thread::Entered>) -> R, + F: FnOnce(&mut CurrentThread>) -> R, { let Runtime { ref reactor_handle, @@ -149,25 +149,13 @@ impl Runtime { .. } = *self; - // Binds an executor to this thread - let mut enter = tokio_executor::enter().expect("Multiple executors at once"); - - // This will set the default handle and timer to use inside the closure - // and run the future. - tokio_reactor::with_default(&reactor_handle, &mut enter, |enter| { - clock::with_default(clock, enter, |enter| { - timer::with_default(&timer_handle, enter, |enter| { - // The TaskExecutor is a fake executor that looks into the - // current single-threaded executor when used. This is a trick, - // because we need two mutable references to the executor (one - // to run the provided future, another to install as the default - // one). We use the fake one here as the default one. - let mut default_executor = current_thread::TaskExecutor::current(); - tokio_executor::with_default(&mut default_executor, enter, |enter| { - let mut executor = executor.enter(enter); - f(&mut executor) - }) - }) + // WARN: We do not enter the executor here, since in tokio 0.2 the executor is entered + // automatically inside its `block_on` and `run` methods + tokio_executor::with_default(&mut current_thread::TaskExecutor::current(), || { + tokio_timer::clock::with_default(clock, || { + let _reactor_guard = tokio_net::driver::set_default(reactor_handle); + let _timer_guard = tokio_timer::set_default(timer_handle); + f(executor) }) }) } diff --git a/actix-rt/src/system.rs b/actix-rt/src/system.rs index 745de00f..c2eb9f37 100644 --- a/actix-rt/src/system.rs +++ b/actix-rt/src/system.rs @@ -2,9 +2,9 @@ use std::cell::RefCell; use std::io; use std::sync::atomic::{AtomicUsize, Ordering}; -use futures::sync::mpsc::UnboundedSender; +use futures::channel::mpsc::UnboundedSender; use futures::Future; -use tokio_current_thread::Handle; +use tokio::runtime::current_thread::Handle; use crate::arbiter::{Arbiter, SystemCommand}; use crate::builder::{Builder, SystemRunner}; @@ -64,7 +64,7 @@ impl System { pub fn run_in_executor>( name: T, executor: Handle, - ) -> impl Future + Send { + ) -> impl Future> + Send { Self::builder() .name(name) .build_async(executor) diff --git a/actix-server-config/Cargo.toml b/actix-server-config/Cargo.toml index 5c7053be..5a53fbb9 100644 --- a/actix-server-config/Cargo.toml +++ b/actix-server-config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-server-config" -version = "0.2.0" +version = "0.3.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix server config utils" homepage = "https://actix.rs" @@ -14,25 +14,14 @@ name = "actix_server_config" path = "src/lib.rs" [package.metadata.docs.rs] -features = ["ssl", "rust-tls", "uds"] +features = ["openssl", "rustls"] [features] default = [] - -# openssl -ssl = ["tokio-openssl"] - -# rustls -rust-tls = ["rustls", "tokio-rustls"] - -# unix domain sockets -uds = ["tokio-uds"] +openssl = ["tokio-openssl"] +rustls = ["tokio-rustls"] [dependencies] -futures = "0.1.25" -tokio-io = "0.1.12" -tokio-tcp = "0.1" -tokio-openssl = { version="0.3.0", optional = true } -rustls = { version = "0.16.0", optional = true } -tokio-rustls = { version = "0.10.0", optional = true } -tokio-uds = { version="0.2.5", optional = true } +tokio = "0.2.0-alpha.6" +tokio-openssl = { version = "0.4.0-alpha.6", optional = true } +tokio-rustls = { version = "0.12.0-alpha.8", optional = true } diff --git a/actix-server-config/src/lib.rs b/actix-server-config/src/lib.rs index fc5dac04..2ffb735f 100644 --- a/actix-server-config/src/lib.rs +++ b/actix-server-config/src/lib.rs @@ -1,10 +1,12 @@ +//! Actix server config utils. + use std::cell::Cell; use std::net::SocketAddr; use std::rc::Rc; -use std::{fmt, io, net, time}; +use std::{fmt, io, net, ops, time}; -use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_tcp::TcpStream; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::net::TcpStream; #[derive(Debug, Clone)] pub struct ServerConfig { @@ -13,6 +15,7 @@ pub struct ServerConfig { } impl ServerConfig { + #[inline] pub fn new(addr: SocketAddr) -> Self { ServerConfig { addr, @@ -21,16 +24,19 @@ impl ServerConfig { } /// Returns the address of the local half of this TCP server socket + #[inline] pub fn local_addr(&self) -> SocketAddr { self.addr } /// Returns true if connection is secure (tls enabled) + #[inline] pub fn secure(&self) -> bool { self.secure.as_ref().get() } /// Set secure flag + #[inline] pub fn set_secure(&self) { self.secure.as_ref().set(true) } @@ -114,7 +120,7 @@ impl Io { } } -impl std::ops::Deref for Io { +impl ops::Deref for Io { type Target = T; fn deref(&self) -> &T { @@ -122,14 +128,14 @@ impl std::ops::Deref for Io { } } -impl std::ops::DerefMut for Io { +impl ops::DerefMut for Io { fn deref_mut(&mut self) -> &mut T { &mut self.io } } impl fmt::Debug for Io { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Io {{{:?}}}", self.io) } } @@ -171,31 +177,31 @@ impl IoStream for TcpStream { } } -#[cfg(any(feature = "ssl"))] -impl IoStream for tokio_openssl::SslStream { +#[cfg(feature = "openssl")] +impl IoStream for tokio_openssl::SslStream { #[inline] fn peer_addr(&self) -> Option { - self.get_ref().get_ref().peer_addr() + self.get_ref().peer_addr() } #[inline] fn set_nodelay(&mut self, nodelay: bool) -> io::Result<()> { - self.get_mut().get_mut().set_nodelay(nodelay) + self.get_mut().set_nodelay(nodelay) } #[inline] fn set_linger(&mut self, dur: Option) -> io::Result<()> { - self.get_mut().get_mut().set_linger(dur) + self.get_mut().set_linger(dur) } #[inline] fn set_keepalive(&mut self, dur: Option) -> io::Result<()> { - self.get_mut().get_mut().set_keepalive(dur) + self.get_mut().set_keepalive(dur) } } -#[cfg(any(feature = "rust-tls"))] -impl IoStream for tokio_rustls::server::TlsStream { +#[cfg(feature = "rustls")] +impl IoStream for tokio_rustls::server::TlsStream { #[inline] fn peer_addr(&self) -> Option { self.get_ref().0.peer_addr() @@ -217,8 +223,8 @@ impl IoStream for tokio_rustls::server::TlsStream { } } -#[cfg(all(unix, feature = "uds"))] -impl IoStream for tokio_uds::UnixStream { +#[cfg(unix)] +impl IoStream for tokio::net::UnixStream { #[inline] fn peer_addr(&self) -> Option { None diff --git a/actix-server/CHANGES.md b/actix-server/CHANGES.md index 1b3aa478..8680373f 100644 --- a/actix-server/CHANGES.md +++ b/actix-server/CHANGES.md @@ -7,6 +7,7 @@ * Update `rustls` to 0.16 * Minimum required Rust version upped to 1.37.0 + ## [0.6.1] - 2019-09-25 ### Added diff --git a/actix-server/Cargo.toml b/actix-server/Cargo.toml index 3720b887..126ee37b 100644 --- a/actix-server/Cargo.toml +++ b/actix-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-server" -version = "0.7.0" +version = "0.8.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix server - General purpose tcp server" keywords = ["network", "framework", "async", "futures"] @@ -14,7 +14,7 @@ edition = "2018" workspace = ".." [package.metadata.docs.rs] -features = ["ssl", "tls", "rust-tls", "uds"] +features = ["nativetls", "openssl", "rustls", "uds"] [lib] name = "actix_server" @@ -22,55 +22,47 @@ path = "src/lib.rs" [features] default = [] - -# tls -tls = ["native-tls"] - -# openssl -ssl = ["openssl", "tokio-openssl", "actix-server-config/ssl"] - -# rustls -rust-tls = ["rustls", "tokio-rustls", "webpki", "webpki-roots", "actix-server-config/rust-tls"] - -# uds -uds = ["mio-uds", "tokio-uds", "actix-server-config/uds"] +nativetls = ["native-tls", "tokio-tls"] +openssl = ["open-ssl", "tokio-openssl", "actix-server-config/openssl"] +rustls = ["rust-tls", "tokio-rustls", "webpki", "webpki-roots", "actix-server-config/rustls"] [dependencies] -actix-rt = "0.2.2" -actix-service = "0.4.1" -actix-server-config = "0.2.0" +actix-rt = "1.0.0-alpha.1" +actix-service = "1.0.0-alpha.1" +actix-server-config = "0.3.0-alpha.1" log = "0.4" num_cpus = "1.0" - +pin-project = "0.4.5" mio = "0.6.19" net2 = "0.2" -futures = "0.1" +futures = "0.3.1" slab = "0.4" -tokio-io = "0.1" -tokio-tcp = "0.1" -tokio-timer = "0.2.8" -tokio-reactor = "0.1" -tokio-signal = "0.2" + +tokio = "0.2.0-alpha.6" +tokio-io = "0.2.0-alpha.6" +tokio-net = { version = "0.2.0-alpha.6", features = ["signal"] } +tokio-timer = "0.3.0-alpha.6" # unix domain sockets -mio-uds = { version="0.6.7", optional = true } -tokio-uds = { version="0.2.5", optional = true } +mio-uds = { version = "0.6.7", optional = true } -# native-tls -native-tls = { version="0.2", optional = true } +# nativetls +native-tls = { version = "0.2", optional = true } +tokio-tls = { version = "0.3.0-alpha.6", optional = true } # openssl -openssl = { version="0.10", optional = true } -tokio-openssl = { version="0.3", optional = true } +open-ssl = { version = "0.10", package = "openssl", optional = true } +tokio-openssl = { version = "0.4.0-alpha.6", optional = true } # rustls -rustls = { version = "0.16.0", optional = true } -tokio-rustls = { version = "0.10.0", optional = true } +rust-tls = { version = "0.16.0", package = "rustls", optional = true } +# tokio-rustls = { version = "0.12.0-alpha.2", optional = true } +tokio-rustls = { git = "https://github.com/quininer/tokio-rustls.git", branch = "tokio-0.2", optional = true } webpki = { version = "0.21", optional = true } webpki-roots = { version = "0.17", optional = true } [dev-dependencies] bytes = "0.4" -actix-codec = "0.1.2" +actix-codec = "0.2.0-alpha.1" env_logger = "0.6" diff --git a/actix-server/src/accept.rs b/actix-server/src/accept.rs index ce7c9325..421aa0ab 100644 --- a/actix-server/src/accept.rs +++ b/actix-server/src/accept.rs @@ -3,10 +3,10 @@ use std::time::{Duration, Instant}; use std::{io, thread}; use actix_rt::System; -use futures::future::{lazy, Future}; +use futures::FutureExt; use log::{error, info}; use slab::Slab; -use tokio_timer::Delay; +use tokio_timer::delay; use crate::server::Server; use crate::socket::{SocketAddr, SocketListener, StdListener}; @@ -370,7 +370,7 @@ impl Accept { match self.workers[self.next].send(msg) { Ok(_) => (), Err(tmp) => { - self.srv.worker_died(self.workers[self.next].idx); + self.srv.worker_faulted(self.workers[self.next].idx); msg = tmp; self.workers.swap_remove(self.next); if self.workers.is_empty() { @@ -396,7 +396,7 @@ impl Accept { return; } Err(tmp) => { - self.srv.worker_died(self.workers[self.next].idx); + self.srv.worker_faulted(self.workers[self.next].idx); msg = tmp; self.workers.swap_remove(self.next); if self.workers.is_empty() { @@ -440,14 +440,13 @@ impl Accept { info.timeout = Some(Instant::now() + Duration::from_millis(500)); let r = self.timer.1.clone(); - System::current().arbiter().send(lazy(move || { - Delay::new(Instant::now() + Duration::from_millis(510)) - .map_err(|_| ()) - .and_then(move |_| { - let _ = r.set_readiness(mio::Ready::readable()); - Ok(()) - }) - })); + System::current().arbiter().send( + async move { + delay(Instant::now() + Duration::from_millis(510)).await; + let _ = r.set_readiness(mio::Ready::readable()); + } + .boxed(), + ); return; } } diff --git a/actix-server/src/builder.rs b/actix-server/src/builder.rs index a0f0c3aa..1a798c38 100644 --- a/actix-server/src/builder.rs +++ b/actix-server/src/builder.rs @@ -1,22 +1,24 @@ -use std::time::Duration; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::time::{Duration, Instant}; use std::{io, mem, net}; use actix_rt::{spawn, Arbiter, System}; -use futures::future::{lazy, ok}; -use futures::stream::futures_unordered; -use futures::sync::mpsc::{unbounded, UnboundedReceiver}; -use futures::{Async, Future, Poll, Stream}; +use futures::channel::mpsc::{unbounded, UnboundedReceiver}; +use futures::future::ready; +use futures::stream::FuturesUnordered; +use futures::{ready, Future, FutureExt, Stream, StreamExt}; use log::{error, info}; use net2::TcpBuilder; use num_cpus; -use tokio_tcp::TcpStream; -use tokio_timer::sleep; +use tokio_net::tcp::TcpStream; +use tokio_timer::delay; use crate::accept::{AcceptLoop, AcceptNotify, Command}; use crate::config::{ConfiguredService, ServiceConfig}; use crate::server::{Server, ServerCommand}; -use crate::services::{InternalServiceFactory, ServiceFactory, StreamNewService}; -use crate::signals::{Signal, Signals}; +use crate::service::{InternalServiceFactory, ServiceFactory, StreamNewService}; +// use crate::signals::{Signal, Signals}; use crate::socket::StdListener; use crate::worker::{self, Worker, WorkerAvailability, WorkerClient}; use crate::{ssl, Token}; @@ -301,7 +303,7 @@ impl ServerBuilder { // handle signals if !self.no_signals { - Signals::start(self.server.clone()); + // Signals::start(self.server.clone()); } // start http server actor @@ -320,10 +322,12 @@ impl ServerBuilder { let services: Vec> = self.services.iter().map(|v| v.clone_factory()).collect(); - Arbiter::new().send(lazy(move || { - Worker::start(rx1, rx2, services, avail, timeout); - Ok::<_, ()>(()) - })); + Arbiter::new().send( + async move { + Worker::start(rx1, rx2, services, avail, timeout); + } + .boxed(), + ); worker } @@ -338,37 +342,37 @@ impl ServerBuilder { self.accept.send(Command::Resume); let _ = tx.send(()); } - ServerCommand::Signal(sig) => { - // Signals support - // Handle `SIGINT`, `SIGTERM`, `SIGQUIT` signals and stop actix system - match sig { - Signal::Int => { - info!("SIGINT received, exiting"); - self.exit = true; - self.handle_cmd(ServerCommand::Stop { - graceful: false, - completion: None, - }) - } - Signal::Term => { - info!("SIGTERM received, stopping"); - self.exit = true; - self.handle_cmd(ServerCommand::Stop { - graceful: true, - completion: None, - }) - } - Signal::Quit => { - info!("SIGQUIT received, exiting"); - self.exit = true; - self.handle_cmd(ServerCommand::Stop { - graceful: false, - completion: None, - }) - } - _ => (), - } - } + // ServerCommand::Signal(sig) => { + // Signals support + // Handle `SIGINT`, `SIGTERM`, `SIGQUIT` signals and stop actix system + // match sig { + // Signal::Int => { + // info!("SIGINT received, exiting"); + // self.exit = true; + // self.handle_cmd(ServerCommand::Stop { + // graceful: false, + // completion: None, + // }) + // } + // Signal::Term => { + // info!("SIGTERM received, stopping"); + // self.exit = true; + // self.handle_cmd(ServerCommand::Stop { + // graceful: true, + // completion: None, + // }) + // } + // Signal::Quit => { + // info!("SIGQUIT received, exiting"); + // self.exit = true; + // self.handle_cmd(ServerCommand::Stop { + // graceful: false, + // completion: None, + // }) + // } + // _ => (), + // } + // } ServerCommand::Stop { graceful, completion, @@ -381,39 +385,44 @@ impl ServerBuilder { // stop workers if !self.workers.is_empty() && graceful { spawn( - futures_unordered( - self.workers - .iter() - .map(move |worker| worker.1.stop(graceful)), - ) - .collect() - .then(move |_| { - if let Some(tx) = completion { - let _ = tx.send(()); - } - if exit { - spawn(sleep(Duration::from_millis(300)).then(|_| { - System::current().stop(); - ok(()) - })); - } - ok(()) - }), + self.workers + .iter() + .map(move |worker| worker.1.stop(graceful)) + .collect::>() + .collect::>() + .then(move |_| { + if let Some(tx) = completion { + let _ = tx.send(()); + } + if exit { + spawn( + async { + delay(Instant::now() + Duration::from_millis(300)) + .await; + System::current().stop(); + } + .boxed(), + ); + } + ready(()) + }), ) } else { // we need to stop system if server was spawned if self.exit { - spawn(sleep(Duration::from_millis(300)).then(|_| { - System::current().stop(); - ok(()) - })); + spawn( + delay(Instant::now() + Duration::from_millis(300)).then(|_| { + System::current().stop(); + ready(()) + }), + ); } if let Some(tx) = completion { let _ = tx.send(()); } } } - ServerCommand::WorkerDied(idx) => { + ServerCommand::WorkerFaulted(idx) => { let mut found = false; for i in 0..self.workers.len() { if self.workers[i].0 == idx { @@ -447,15 +456,15 @@ impl ServerBuilder { } impl Future for ServerBuilder { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { loop { - match self.cmd.poll() { - Ok(Async::Ready(None)) | Err(_) => return Ok(Async::Ready(())), - Ok(Async::NotReady) => return Ok(Async::NotReady), - Ok(Async::Ready(Some(item))) => self.handle_cmd(item), + match ready!(Pin::new(&mut self.cmd).poll_next(cx)) { + Some(it) => self.as_mut().get_mut().handle_cmd(it), + None => { + return Poll::Pending; + } } } } diff --git a/actix-server/src/config.rs b/actix-server/src/config.rs index e224d6a4..36e94a37 100644 --- a/actix-server/src/config.rs +++ b/actix-server/src/config.rs @@ -2,18 +2,17 @@ use std::collections::HashMap; use std::{fmt, io, net}; use actix_server_config::{Io, ServerConfig}; -use actix_service::{IntoNewService, NewService}; -use futures::future::{join_all, Future}; +use actix_service as actix; +use futures::future::{Future, FutureExt, LocalBoxFuture}; use log::error; -use tokio_tcp::TcpStream; - -use crate::counter::CounterGuard; +use tokio_net::tcp::TcpStream; use super::builder::bind_addr; -use super::services::{ +use super::service::{ BoxedServerService, InternalServiceFactory, ServerMessage, StreamService, }; use super::Token; +use crate::counter::CounterGuard; pub struct ServiceConfig { pub(crate) services: Vec<(String, net::TcpListener)>, @@ -108,50 +107,39 @@ impl InternalServiceFactory for ConfiguredService { }) } - fn create(&self) -> Box, Error = ()>> { + fn create(&self) -> LocalBoxFuture<'static, Result, ()>> { // configure services let mut rt = ServiceRuntime::new(self.services.clone()); self.rt.configure(&mut rt); rt.validate(); - let services = rt.services; + let names = self.names.clone(); - // on start futures - if rt.onstart.is_empty() { - // construct services - let mut fut = Vec::new(); - for (token, ns) in services { - let config = ServerConfig::new(self.names[&token].1); - fut.push(ns.new_service(&config).map(move |service| (token, service))); + // construct services + async move { + let services = rt.services; + // TODO: Proper error handling here + for f in rt.onstart.into_iter() { + f.await; } + let mut res = vec![]; + for (token, ns) in services.into_iter() { + let config = ServerConfig::new(names[&token].1); - Box::new(join_all(fut).map_err(|e| { - error!("Can not construct service: {:?}", e); - })) - } else { - let names = self.names.clone(); - - // run onstart future and then construct services - Box::new( - join_all(rt.onstart) - .map_err(|e| { - error!("Can not construct service: {:?}", e); - }) - .and_then(move |_| { - // construct services - let mut fut = Vec::new(); - for (token, ns) in services { - let config = ServerConfig::new(names[&token].1); - fut.push( - ns.new_service(&config).map(move |service| (token, service)), - ); - } - join_all(fut).map_err(|e| { - error!("Can not construct service: {:?}", e); - }) - }), - ) + let newserv = ns.new_service(&config); + match newserv.await { + Ok(serv) => { + res.push((token, serv)); + } + Err(e) => { + error!("Can not construct service {:?}", e); + return Err(e); + } + }; + } + return Ok(res); } + .boxed_local() } } @@ -181,7 +169,7 @@ fn not_configured(_: &mut ServiceRuntime) { pub struct ServiceRuntime { names: HashMap, services: HashMap, - onstart: Vec>>, + onstart: Vec>, } impl ServiceRuntime { @@ -207,8 +195,8 @@ impl ServiceRuntime { /// *ServiceConfig::bind()* or *ServiceConfig::listen()* methods. pub fn service(&mut self, name: &str, service: F) where - F: IntoNewService, - T: NewService> + 'static, + F: actix::IntoServiceFactory, + T: actix::ServiceFactory> + 'static, T::Future: 'static, T::Service: 'static, T::InitError: fmt::Debug, @@ -218,7 +206,7 @@ impl ServiceRuntime { self.services.insert( token.clone(), Box::new(ServiceFactory { - inner: service.into_new_service(), + inner: service.into_factory(), }), ); } else { @@ -229,21 +217,21 @@ impl ServiceRuntime { /// Execute future before services initialization. pub fn on_start(&mut self, fut: F) where - F: Future + 'static, + F: Future + 'static, { - self.onstart.push(Box::new(fut)) + self.onstart.push(fut.boxed_local()) } } type BoxedNewService = Box< - dyn NewService< + dyn actix::ServiceFactory< Request = (Option, ServerMessage), Response = (), Error = (), InitError = (), Config = ServerConfig, Service = BoxedServerService, - Future = Box>, + Future = LocalBoxFuture<'static, Result>, >, >; @@ -251,9 +239,9 @@ struct ServiceFactory { inner: T, } -impl NewService for ServiceFactory +impl actix::ServiceFactory for ServiceFactory where - T: NewService>, + T: actix::ServiceFactory>, T::Future: 'static, T::Service: 'static, T::Error: 'static, @@ -265,12 +253,19 @@ where type InitError = (); type Config = ServerConfig; type Service = BoxedServerService; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, cfg: &ServerConfig) -> Self::Future { - Box::new(self.inner.new_service(cfg).map_err(|_| ()).map(|s| { - let service: BoxedServerService = Box::new(StreamService::new(s)); - service - })) + let fut = self.inner.new_service(cfg); + async move { + return match fut.await { + Ok(s) => Ok(Box::new(StreamService::new(s)) as BoxedServerService), + Err(e) => { + error!("Can not construct service: {:?}", e); + Err(()) + } + }; + } + .boxed_local() } } diff --git a/actix-server/src/counter.rs b/actix-server/src/counter.rs index 539ce497..74f716da 100644 --- a/actix-server/src/counter.rs +++ b/actix-server/src/counter.rs @@ -1,7 +1,8 @@ use std::cell::Cell; use std::rc::Rc; -use futures::task::AtomicTask; +use futures::task::AtomicWaker; +use std::task; #[derive(Clone)] /// Simple counter with ability to notify task on reaching specific number @@ -13,7 +14,7 @@ pub struct Counter(Rc); struct CounterInner { count: Cell, capacity: usize, - task: AtomicTask, + task: AtomicWaker, } impl Counter { @@ -22,7 +23,7 @@ impl Counter { Counter(Rc::new(CounterInner { capacity, count: Cell::new(0), - task: AtomicTask::new(), + task: AtomicWaker::new(), })) } @@ -31,8 +32,8 @@ impl Counter { } /// Check if counter is not at capacity - pub fn available(&self) -> bool { - self.0.available() + pub fn available(&self, cx: &mut task::Context) -> bool { + self.0.available(cx) } /// Get total number of acquired counts @@ -66,14 +67,14 @@ impl CounterInner { let num = self.count.get(); self.count.set(num - 1); if num == self.capacity { - self.task.notify(); + self.task.wake(); } } - fn available(&self) -> bool { + fn available(&self, cx: &mut task::Context) -> bool { let avail = self.count.get() < self.capacity; if !avail { - self.task.register(); + self.task.register(cx.waker()); } avail } diff --git a/actix-server/src/lib.rs b/actix-server/src/lib.rs index 3e8cc8fc..6aefc010 100644 --- a/actix-server/src/lib.rs +++ b/actix-server/src/lib.rs @@ -5,8 +5,8 @@ mod builder; mod config; mod counter; mod server; -mod services; -mod signals; +mod service; +// mod signals; mod socket; pub mod ssl; mod worker; @@ -16,14 +16,11 @@ pub use actix_server_config::{Io, IoStream, Protocol, ServerConfig}; pub use self::builder::ServerBuilder; pub use self::config::{ServiceConfig, ServiceRuntime}; pub use self::server::Server; -pub use self::services::ServiceFactory; +pub use self::service::ServiceFactory; #[doc(hidden)] pub use self::socket::FromStream; -#[doc(hidden)] -pub use self::services::ServiceFactory as StreamServiceFactory; - /// Socket id token #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub(crate) struct Token(usize); diff --git a/actix-server/src/server.rs b/actix-server/src/server.rs index f1d862c9..67f9ca5d 100644 --- a/actix-server/src/server.rs +++ b/actix-server/src/server.rs @@ -1,16 +1,16 @@ -use futures::sync::mpsc::UnboundedSender; -use futures::sync::oneshot; -use futures::Future; +use futures::channel::mpsc::UnboundedSender; +use futures::channel::oneshot; +use futures::{Future, TryFutureExt}; use crate::builder::ServerBuilder; -use crate::signals::Signal; +// use crate::signals::Signal; #[derive(Debug)] pub(crate) enum ServerCommand { - WorkerDied(usize), + WorkerFaulted(usize), Pause(oneshot::Sender<()>), Resume(oneshot::Sender<()>), - Signal(Signal), + // Signal(Signal), /// Whether to try and shut down gracefully Stop { graceful: bool, @@ -31,26 +31,26 @@ impl Server { ServerBuilder::default() } - pub(crate) fn signal(&self, sig: Signal) { - let _ = self.0.unbounded_send(ServerCommand::Signal(sig)); - } + // pub(crate) fn signal(&self, sig: Signal) { + // let _ = self.0.unbounded_send(ServerCommand::Signal(sig)); + // } - pub(crate) fn worker_died(&self, idx: usize) { - let _ = self.0.unbounded_send(ServerCommand::WorkerDied(idx)); + pub(crate) fn worker_faulted(&self, idx: usize) { + let _ = self.0.unbounded_send(ServerCommand::WorkerFaulted(idx)); } /// Pause accepting incoming connections /// /// If socket contains some pending connection, they might be dropped. /// All opened connection remains active. - pub fn pause(&self) -> impl Future { + pub fn pause(&self) -> impl Future> { let (tx, rx) = oneshot::channel(); let _ = self.0.unbounded_send(ServerCommand::Pause(tx)); rx.map_err(|_| ()) } /// Resume accepting incoming connections - pub fn resume(&self) -> impl Future { + pub fn resume(&self) -> impl Future> { let (tx, rx) = oneshot::channel(); let _ = self.0.unbounded_send(ServerCommand::Resume(tx)); rx.map_err(|_| ()) @@ -59,7 +59,7 @@ impl Server { /// Stop incoming connection processing, stop all workers and exit. /// /// If server starts with `spawn()` method, then spawned thread get terminated. - pub fn stop(&self, graceful: bool) -> impl Future { + pub fn stop(&self, graceful: bool) -> impl Future> { let (tx, rx) = oneshot::channel(); let _ = self.0.unbounded_send(ServerCommand::Stop { graceful, diff --git a/actix-server/src/services.rs b/actix-server/src/service.rs similarity index 70% rename from actix-server/src/services.rs rename to actix-server/src/service.rs index 6dd90838..2a77c810 100644 --- a/actix-server/src/services.rs +++ b/actix-server/src/service.rs @@ -1,12 +1,13 @@ use std::marker::PhantomData; use std::net::SocketAddr; +use std::task::{Context, Poll}; use std::time::Duration; use actix_rt::spawn; use actix_server_config::{Io, ServerConfig}; -use actix_service::{NewService, Service}; -use futures::future::{err, ok, FutureResult}; -use futures::{Future, Poll}; +use actix_service::{self as actix, Service, ServiceFactory as ActixServiceFactory}; +use futures::future::{err, ok, LocalBoxFuture, Ready}; +use futures::{FutureExt, TryFutureExt}; use log::error; use super::Token; @@ -24,7 +25,7 @@ pub(crate) enum ServerMessage { } pub trait ServiceFactory: Send + Clone + 'static { - type NewService: NewService>; + type NewService: actix::ServiceFactory>; fn create(&self) -> Self::NewService; } @@ -34,7 +35,7 @@ pub(crate) trait InternalServiceFactory: Send { fn clone_factory(&self) -> Box; - fn create(&self) -> Box, Error = ()>>; + fn create(&self) -> LocalBoxFuture<'static, Result, ()>>; } pub(crate) type BoxedServerService = Box< @@ -42,7 +43,7 @@ pub(crate) type BoxedServerService = Box< Request = (Option, ServerMessage), Response = (), Error = (), - Future = FutureResult<(), ()>, + Future = Ready>, >, >; @@ -66,10 +67,10 @@ where type Request = (Option, ServerMessage); type Response = (); type Error = (); - type Future = FutureResult<(), ()>; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready().map_err(|_| ()) + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(ctx).map_err(|_| ()) } fn call(&mut self, (guard, req): (Option, ServerMessage)) -> Self::Future { @@ -80,10 +81,14 @@ where }); if let Ok(stream) = stream { - spawn(self.service.call(Io::new(stream)).then(move |res| { - drop(guard); - res.map_err(|_| ()).map(|_| ()) - })); + let f = self.service.call(Io::new(stream)); + spawn( + async move { + let _ = f.await; + drop(guard); + } + .boxed_local(), + ); ok(()) } else { err(()) @@ -142,19 +147,19 @@ where }) } - fn create(&self) -> Box, Error = ()>> { + fn create(&self) -> LocalBoxFuture<'static, Result, ()>> { let token = self.token; let config = ServerConfig::new(self.addr); - Box::new( - self.inner - .create() - .new_service(&config) - .map_err(|_| ()) - .map(move |inner| { - let service: BoxedServerService = Box::new(StreamService::new(inner)); - vec![(token, service)] - }), - ) + + self.inner + .create() + .new_service(&config) + .map_err(|_| ()) + .map_ok(move |inner| { + let service: BoxedServerService = Box::new(StreamService::new(inner)); + vec![(token, service)] + }) + .boxed_local() } } @@ -167,7 +172,7 @@ impl InternalServiceFactory for Box { self.as_ref().clone_factory() } - fn create(&self) -> Box, Error = ()>> { + fn create(&self) -> LocalBoxFuture<'static, Result, ()>> { self.as_ref().create() } } @@ -175,7 +180,7 @@ impl InternalServiceFactory for Box { impl ServiceFactory for F where F: Fn() -> T + Send + Clone + 'static, - T: NewService>, + T: actix::ServiceFactory>, I: FromStream, { type NewService = T; diff --git a/actix-server/src/signals.rs b/actix-server/src/signals.rs index 4d9e085f..f87cf18b 100644 --- a/actix-server/src/signals.rs +++ b/actix-server/src/signals.rs @@ -1,8 +1,13 @@ +use std::future::Future; use std::io; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix_rt::spawn; -use futures::stream::futures_unordered; -use futures::{Async, Future, Poll, Stream}; +use futures::future::LocalBoxFuture; +use futures::stream::{futures_unordered, FuturesUnordered, LocalBoxStream}; +use futures::{FutureExt, Stream, StreamExt, TryFutureExt, TryStream, TryStreamExt}; +use tokio_net::signal::unix::signal; use crate::server::Server; @@ -27,14 +32,14 @@ pub(crate) struct Signals { streams: Vec, } -type SigStream = Box>; +type SigStream = LocalBoxStream<'static, Result>; impl Signals { pub(crate) fn start(srv: Server) { let fut = { #[cfg(not(unix))] { - tokio_signal::ctrl_c() + tokio_net::signal::ctrl_c() .map_err(|_| ()) .and_then(move |stream| Signals { srv, @@ -44,51 +49,79 @@ impl Signals { #[cfg(unix)] { - use tokio_signal::unix; + use tokio_net::signal::unix; - let mut sigs: Vec>> = - Vec::new(); - sigs.push(Box::new( - tokio_signal::unix::Signal::new(tokio_signal::unix::SIGINT).map(|stream| { - let s: SigStream = Box::new(stream.map(|_| Signal::Int)); - s - }), - )); - sigs.push(Box::new( - tokio_signal::unix::Signal::new(tokio_signal::unix::SIGHUP).map( - |stream: unix::Signal| { + let mut sigs: Vec<_> = Vec::new(); + + let mut SIG_MAP = [ + ( + tokio_net::signal::unix::SignalKind::interrupt(), + Signal::Int, + ), + (tokio_net::signal::unix::SignalKind::hangup(), Signal::Hup), + ( + tokio_net::signal::unix::SignalKind::terminate(), + Signal::Term, + ), + (tokio_net::signal::unix::SignalKind::quit(), Signal::Quit), + ]; + + for (kind, sig) in SIG_MAP.into_iter() { + let sig = sig.clone(); + let fut = signal(*kind).unwrap(); + sigs.push(fut.map(move |_| Ok(sig)).boxed_local()); + } + /* TODO: Finish rewriting this + sigs.push( + tokio_net::signal::unix::signal(tokio_net::signal::si).unwrap() + .map(|stream| { + let s: SigStream = Box::new(stream.map(|_| Signal::Int)); + s + }).boxed() + ); + sigs.push( + + tokio_net::signal::unix::signal(tokio_net::signal::unix::SignalKind::hangup()).unwrap() + .map(|stream: unix::Signal| { let s: SigStream = Box::new(stream.map(|_| Signal::Hup)); s - }, - ), - )); - sigs.push(Box::new( - tokio_signal::unix::Signal::new(tokio_signal::unix::SIGTERM).map( - |stream| { + }).boxed() + ); + sigs.push( + tokio_net::signal::unix::signal( + tokio_net::signal::unix::SignalKind::terminate() + ).unwrap() + .map(|stream| { let s: SigStream = Box::new(stream.map(|_| Signal::Term)); s - }, - ), - )); - sigs.push(Box::new( - tokio_signal::unix::Signal::new(tokio_signal::unix::SIGQUIT).map( - |stream| { + }).boxed(), + ); + sigs.push( + tokio_net::signal::unix::signal( + tokio_net::signal::unix::SignalKind::quit() + ).unwrap() + .map(|stream| { let s: SigStream = Box::new(stream.map(|_| Signal::Quit)); s - }, - ), - )); - futures_unordered(sigs) - .collect() - .map_err(|_| ()) - .and_then(move |streams| Signals { srv, streams }) + }).boxed() + ); + */ + + Signals { srv, streams: sigs } } }; - spawn(fut); + spawn(async {}); } } impl Future for Signals { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + unimplemented!() + } + + /* type Item = (); type Error = (); @@ -115,4 +148,5 @@ impl Future for Signals { Ok(Async::NotReady) } } + */ } diff --git a/actix-server/src/socket.rs b/actix-server/src/socket.rs index 4cbefb80..574e6a1e 100644 --- a/actix-server/src/socket.rs +++ b/actix-server/src/socket.rs @@ -1,8 +1,8 @@ use std::{fmt, io, net}; use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_reactor::Handle; -use tokio_tcp::TcpStream; +use tokio_net::driver::Handle; +use tokio_net::tcp::TcpStream; pub(crate) enum StdListener { Tcp(net::TcpListener), diff --git a/actix-server/src/ssl/mod.rs b/actix-server/src/ssl/mod.rs index 6ce1222f..e9a8da87 100644 --- a/actix-server/src/ssl/mod.rs +++ b/actix-server/src/ssl/mod.rs @@ -3,19 +3,19 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use crate::counter::Counter; -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] mod openssl; -#[cfg(feature = "ssl")] +#[cfg(feature = "openssl")] pub use self::openssl::OpensslAcceptor; -#[cfg(feature = "tls")] +#[cfg(feature = "nativetls")] mod nativetls; -#[cfg(feature = "tls")] -pub use self::nativetls::{NativeTlsAcceptor, TlsStream}; +#[cfg(feature = "nativetls")] +pub use self::nativetls::NativeTlsAcceptor; -#[cfg(feature = "rust-tls")] +#[cfg(feature = "rustls")] mod rustls; -#[cfg(feature = "rust-tls")] +#[cfg(feature = "rustls")] pub use self::rustls::RustlsAcceptor; /// Sets the maximum per-worker concurrent ssl connection establish process. diff --git a/actix-server/src/ssl/nativetls.rs b/actix-server/src/ssl/nativetls.rs index 3a6859fe..5319f127 100644 --- a/actix-server/src/ssl/nativetls.rs +++ b/actix-server/src/ssl/nativetls.rs @@ -1,14 +1,16 @@ -use std::io; +use std::convert::Infallible; use std::marker::PhantomData; +use std::task::{Context, Poll}; -use actix_service::{NewService, Service}; -use futures::{future::ok, future::FutureResult, Async, Future, Poll}; -use native_tls::{self, Error, HandshakeError, TlsAcceptor}; -use tokio_io::{AsyncRead, AsyncWrite}; +use actix_service::{Service, ServiceFactory}; +use futures::future::{self, FutureExt as _, LocalBoxFuture, TryFutureExt as _}; +use native_tls::Error; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio_tls::{TlsAcceptor, TlsStream}; -use crate::counter::{Counter, CounterGuard}; +use crate::counter::Counter; use crate::ssl::MAX_CONN_COUNTER; -use crate::{Io, Protocol, ServerConfig}; +use crate::{Io, ServerConfig}; /// Support `SSL` connections via native-tls package /// @@ -18,8 +20,12 @@ pub struct NativeTlsAcceptor { io: PhantomData<(T, P)>, } -impl NativeTlsAcceptor { +impl NativeTlsAcceptor +where + T: AsyncRead + AsyncWrite + Unpin, +{ /// Create `NativeTlsAcceptor` instance + #[inline] pub fn new(acceptor: TlsAcceptor) -> Self { NativeTlsAcceptor { acceptor, @@ -28,7 +34,8 @@ impl NativeTlsAcceptor { } } -impl Clone for NativeTlsAcceptor { +impl Clone for NativeTlsAcceptor { + #[inline] fn clone(&self) -> Self { Self { acceptor: self.acceptor.clone(), @@ -37,21 +44,25 @@ impl Clone for NativeTlsAcceptor { } } -impl NewService for NativeTlsAcceptor { +impl ServiceFactory for NativeTlsAcceptor +where + T: AsyncRead + AsyncWrite + Unpin + 'static, + P: 'static, +{ type Request = Io; type Response = Io, P>; type Error = Error; type Config = ServerConfig; type Service = NativeTlsAcceptorService; - type InitError = (); - type Future = FutureResult; + type InitError = Infallible; + type Future = future::Ready>; fn new_service(&self, cfg: &ServerConfig) -> Self::Future { cfg.set_secure(); MAX_CONN_COUNTER.with(|conns| { - ok(NativeTlsAcceptorService { + future::ok(NativeTlsAcceptorService { acceptor: self.acceptor.clone(), conns: conns.clone(), io: PhantomData, @@ -66,117 +77,46 @@ pub struct NativeTlsAcceptorService { conns: Counter, } -impl Service for NativeTlsAcceptorService { +impl Clone for NativeTlsAcceptorService { + fn clone(&self) -> Self { + Self { + acceptor: self.acceptor.clone(), + io: PhantomData, + conns: self.conns.clone(), + } + } +} + +impl Service for NativeTlsAcceptorService +where + T: AsyncRead + AsyncWrite + Unpin + 'static, + P: 'static, +{ type Request = Io; type Response = Io, P>; type Error = Error; - type Future = Accept; + type Future = LocalBoxFuture<'static, Result, P>, Error>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - if self.conns.available() { - Ok(Async::Ready(())) + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + if self.conns.available(cx) { + Poll::Ready(Ok(())) } else { - Ok(Async::NotReady) + Poll::Pending } } fn call(&mut self, req: Self::Request) -> Self::Future { - let (io, params, _) = req.into_parts(); - Accept { - _guard: self.conns.get(), - inner: Some(self.acceptor.accept(io)), - params: Some(params), - } - } -} - -/// A wrapper around an underlying raw stream which implements the TLS or SSL -/// protocol. -/// -/// A `TlsStream` represents a handshake that has been completed successfully -/// and both the server and the client are ready for receiving and sending -/// data. Bytes read from a `TlsStream` are decrypted from `S` and bytes written -/// to a `TlsStream` are encrypted when passing through to `S`. -#[derive(Debug)] -pub struct TlsStream { - inner: native_tls::TlsStream, -} - -/// Future returned from `NativeTlsAcceptor::accept` which will resolve -/// once the accept handshake has finished. -pub struct Accept { - inner: Option, HandshakeError>>, - params: Option

, - _guard: CounterGuard, -} - -impl Future for Accept { - type Item = Io, P>; - type Error = Error; - - fn poll(&mut self) -> Poll { - match self.inner.take().expect("cannot poll MidHandshake twice") { - Ok(stream) => Ok(Async::Ready(Io::from_parts( - TlsStream { inner: stream }, - self.params.take().unwrap(), - Protocol::Unknown, - ))), - Err(HandshakeError::Failure(e)) => Err(e), - Err(HandshakeError::WouldBlock(s)) => match s.handshake() { - Ok(stream) => Ok(Async::Ready(Io::from_parts( - TlsStream { inner: stream }, - self.params.take().unwrap(), - Protocol::Unknown, - ))), - Err(HandshakeError::Failure(e)) => Err(e), - Err(HandshakeError::WouldBlock(s)) => { - self.inner = Some(Err(HandshakeError::WouldBlock(s))); - Ok(Async::NotReady) - } - }, - } - } -} - -impl TlsStream { - /// Get access to the internal `native_tls::TlsStream` stream which also - /// transitively allows access to `S`. - pub fn get_ref(&self) -> &native_tls::TlsStream { - &self.inner - } - - /// Get mutable access to the internal `native_tls::TlsStream` stream which - /// also transitively allows mutable access to `S`. - pub fn get_mut(&mut self) -> &mut native_tls::TlsStream { - &mut self.inner - } -} - -impl io::Read for TlsStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) - } -} - -impl io::Write for TlsStream { - fn write(&mut self, buf: &[u8]) -> io::Result { - self.inner.write(buf) - } - - fn flush(&mut self) -> io::Result<()> { - self.inner.flush() - } -} - -impl AsyncRead for TlsStream {} - -impl AsyncWrite for TlsStream { - fn shutdown(&mut self) -> Poll<(), io::Error> { - match self.inner.shutdown() { - Ok(_) => (), - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => (), - Err(e) => return Err(e), - } - self.inner.get_mut().shutdown() + let guard = self.conns.get(); + let this = self.clone(); + let (io, params, proto) = req.into_parts(); + async move { this.acceptor.accept(io).await } + .map_ok(move |stream| Io::from_parts(stream, params, proto)) + .map_ok(move |io| { + // Required to preserve `CounterGuard` until `Self::Future` + // is completely resolved. + let _ = guard; + io + }) + .boxed_local() } } diff --git a/actix-server/src/ssl/openssl.rs b/actix-server/src/ssl/openssl.rs index 9a9b2120..804e9fc9 100644 --- a/actix-server/src/ssl/openssl.rs +++ b/actix-server/src/ssl/openssl.rs @@ -1,10 +1,14 @@ +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; -use actix_service::{NewService, Service}; -use futures::{future::ok, future::FutureResult, Async, Future, Poll}; -use openssl::ssl::{HandshakeError, SslAcceptor}; +use actix_service::{Service, ServiceFactory}; +use futures::future::{ok, FutureExt, LocalBoxFuture, Ready}; +use open_ssl::ssl::SslAcceptor; +use pin_project::pin_project; use tokio_io::{AsyncRead, AsyncWrite}; -use tokio_openssl::{AcceptAsync, SslAcceptorExt, SslStream}; +use tokio_openssl::{HandshakeError, SslStream}; use crate::counter::{Counter, CounterGuard}; use crate::ssl::MAX_CONN_COUNTER; @@ -37,14 +41,14 @@ impl Clone for OpensslAcceptor { } } -impl NewService for OpensslAcceptor { +impl ServiceFactory for OpensslAcceptor { type Request = Io; type Response = Io, P>; type Error = HandshakeError; type Config = ServerConfig; type Service = OpensslAcceptorService; type InitError = (); - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, cfg: &ServerConfig) -> Self::Future { cfg.set_secure(); @@ -65,46 +69,54 @@ pub struct OpensslAcceptorService { io: PhantomData<(T, P)>, } -impl Service for OpensslAcceptorService { +impl Service for OpensslAcceptorService { type Request = Io; type Response = Io, P>; type Error = HandshakeError; type Future = OpensslAcceptorServiceFut; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - if self.conns.available() { - Ok(Async::Ready(())) + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + if self.conns.available(ctx) { + Poll::Ready(Ok(())) } else { - Ok(Async::NotReady) + Poll::Pending } } fn call(&mut self, req: Self::Request) -> Self::Future { let (io, params, _) = req.into_parts(); + let acc = self.acceptor.clone(); OpensslAcceptorServiceFut { _guard: self.conns.get(), - fut: SslAcceptorExt::accept_async(&self.acceptor, io), + fut: async move { + let acc = acc; + tokio_openssl::accept(&acc, io).await + } + .boxed_local::<'static>(), params: Some(params), } } } +#[pin_project] pub struct OpensslAcceptorServiceFut where T: AsyncRead + AsyncWrite, { - fut: AcceptAsync, + #[pin] + fut: LocalBoxFuture<'static, Result, HandshakeError>>, params: Option

, _guard: CounterGuard, } impl Future for OpensslAcceptorServiceFut { - type Item = Io, P>; - type Error = HandshakeError; + type Output = Result, P>, HandshakeError>; - fn poll(&mut self) -> Poll { - let io = futures::try_ready!(self.fut.poll()); - let proto = if let Some(protos) = io.get_ref().ssl().selected_alpn_protocol() { + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + + let io = futures::ready!(this.fut.poll(cx))?; + let proto = if let Some(protos) = io.ssl().selected_alpn_protocol() { const H2: &[u8] = b"\x02h2"; const HTTP10: &[u8] = b"\x08http/1.0"; const HTTP11: &[u8] = b"\x08http/1.1"; @@ -121,10 +133,7 @@ impl Future for OpensslAcceptorServiceFut { } else { Protocol::Unknown }; - Ok(Async::Ready(Io::from_parts( - io, - self.params.take().unwrap(), - proto, - ))) + + Poll::Ready(Ok(Io::from_parts(io, this.params.take().unwrap(), proto))) } } diff --git a/actix-server/src/ssl/rustls.rs b/actix-server/src/ssl/rustls.rs index 06edc0f5..f2c30f60 100644 --- a/actix-server/src/ssl/rustls.rs +++ b/actix-server/src/ssl/rustls.rs @@ -1,10 +1,14 @@ +use std::future::Future; use std::io; use std::marker::PhantomData; +use std::pin::Pin; use std::sync::Arc; +use std::task::{Context, Poll}; -use actix_service::{NewService, Service}; -use futures::{future::ok, future::FutureResult, Async, Future, Poll}; -use rustls::ServerConfig; +use actix_service::{Service, ServiceFactory}; +use futures::future::{ok, Ready}; +use pin_project::pin_project; +use rust_tls::ServerConfig; use tokio_io::{AsyncRead, AsyncWrite}; use tokio_rustls::{server::TlsStream, Accept, TlsAcceptor}; @@ -39,7 +43,7 @@ impl Clone for RustlsAcceptor { } } -impl NewService for RustlsAcceptor { +impl ServiceFactory for RustlsAcceptor { type Request = Io; type Response = Io, P>; type Error = io::Error; @@ -47,7 +51,7 @@ impl NewService for RustlsAcceptor { type Config = SrvConfig; type Service = RustlsAcceptorService; type InitError = (); - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, cfg: &SrvConfig) -> Self::Future { cfg.set_secure(); @@ -68,17 +72,17 @@ pub struct RustlsAcceptorService { conns: Counter, } -impl Service for RustlsAcceptorService { +impl Service for RustlsAcceptorService { type Request = Io; type Response = Io, P>; type Error = io::Error; type Future = RustlsAcceptorServiceFut; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - if self.conns.available() { - Ok(Async::Ready(())) + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + if self.conns.available(cx) { + Poll::Ready(Ok(())) } else { - Ok(Async::NotReady) + Poll::Pending } } @@ -92,25 +96,26 @@ impl Service for RustlsAcceptorService { } } +#[pin_project] pub struct RustlsAcceptorServiceFut where - T: AsyncRead + AsyncWrite, + T: AsyncRead + AsyncWrite + Unpin, { + #[pin] fut: Accept, params: Option

, _guard: CounterGuard, } -impl Future for RustlsAcceptorServiceFut { - type Item = Io, P>; - type Error = io::Error; +impl Future for RustlsAcceptorServiceFut { + type Output = Result, P>, io::Error>; - fn poll(&mut self) -> Poll { - let io = futures::try_ready!(self.fut.poll()); - Ok(Async::Ready(Io::from_parts( - io, - self.params.take().unwrap(), - Protocol::Unknown, - ))) + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); + let params = this.params.take().unwrap(); + Poll::Ready( + futures::ready!(this.fut.poll(cx)) + .map(move |io| Io::from_parts(io, params, Protocol::Unknown)), + ) } } diff --git a/actix-server/src/worker.rs b/actix-server/src/worker.rs index e03688a8..a1ba6c78 100644 --- a/actix-server/src/worker.rs +++ b/actix-server/src/worker.rs @@ -1,17 +1,20 @@ +use std::pin::Pin; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; +use std::task::{Context, Poll}; use std::{mem, time}; use actix_rt::{spawn, Arbiter}; -use futures::sync::mpsc::{UnboundedReceiver, UnboundedSender}; -use futures::sync::oneshot; -use futures::{future, Async, Future, Poll, Stream}; +use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; +use futures::channel::oneshot; +use futures::future::{join_all, LocalBoxFuture, MapOk}; +use futures::{Future, FutureExt, Stream, TryFutureExt}; use log::{error, info, trace}; -use tokio_timer::{sleep, Delay}; +use tokio_timer::{delay, Delay}; use crate::accept::AcceptNotify; use crate::counter::Counter; -use crate::services::{BoxedServerService, InternalServiceFactory, ServerMessage}; +use crate::service::{BoxedServerService, InternalServiceFactory, ServerMessage}; use crate::socket::{SocketAddr, StdStream}; use crate::Token; @@ -153,31 +156,38 @@ impl Worker { state: WorkerState::Unavailable(Vec::new()), }); - let mut fut = Vec::new(); + let mut fut: Vec, _>> = Vec::new(); for (idx, factory) in wrk.factories.iter().enumerate() { - fut.push(factory.create().map(move |res| { - res.into_iter() - .map(|(t, s)| (idx, t, s)) + fut.push(factory.create().map_ok(move |r| { + r.into_iter() + .map(|(t, s): (Token, _)| (idx, t, s)) .collect::>() })); } + spawn( - future::join_all(fut) - .map_err(|e| { - error!("Can not start worker: {:?}", e); - Arbiter::current().stop(); - }) - .and_then(move |services| { - for item in services { - for (idx, token, service) in item { - while token.0 >= wrk.services.len() { - wrk.services.push(None); + async move { + let res = join_all(fut).await; + let res: Result, _> = res.into_iter().collect(); + match res { + Ok(services) => { + for item in services { + for (idx, token, service) in item { + while token.0 >= wrk.services.len() { + wrk.services.push(None); + } + wrk.services[token.0] = Some((idx, service)); } - wrk.services[token.0] = Some((idx, service)); } } - wrk - }), + Err(e) => { + error!("Can not start worker: {:?}", e); + Arbiter::current().stop(); + } + } + wrk.await + } + .boxed_local(), ); } @@ -198,13 +208,17 @@ impl Worker { } } - fn check_readiness(&mut self, trace: bool) -> Result { - let mut ready = self.conns.available(); + fn check_readiness( + &mut self, + trace: bool, + cx: &mut Context<'_>, + ) -> Result { + let mut ready = self.conns.available(cx); let mut failed = None; for (token, service) in &mut self.services.iter_mut().enumerate() { if let Some(service) = service { - match service.1.poll_ready() { - Ok(Async::Ready(_)) => { + match service.1.poll_ready(cx) { + Poll::Ready(Ok(_)) => { if trace { trace!( "Service {:?} is available", @@ -212,8 +226,8 @@ impl Worker { ); } } - Ok(Async::NotReady) => ready = false, - Err(_) => { + Poll::Pending => ready = false, + Poll::Ready(Err(_)) => { error!( "Service {:?} readiness check returned error, restarting", self.factories[service.0].name(Token(token)) @@ -238,43 +252,44 @@ enum WorkerState { Restarting( usize, Token, - Box, Error = ()>>, + Pin, ()>>>>, ), Shutdown(Delay, Delay, oneshot::Sender), } impl Future for Worker { - type Item = (); - type Error = (); + type Output = (); - fn poll(&mut self) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { // `StopWorker` message handler - if let Ok(Async::Ready(Some(StopCommand { graceful, result }))) = self.rx2.poll() { + if let Poll::Ready(Some(StopCommand { graceful, result })) = + Pin::new(&mut self.rx2).poll_next(cx) + { self.availability.set(false); let num = num_connections(); if num == 0 { info!("Shutting down worker, 0 connections"); let _ = result.send(true); - return Ok(Async::Ready(())); + return Poll::Ready(()); } else if graceful { self.shutdown(false); let num = num_connections(); if num != 0 { info!("Graceful worker shutdown, {} connections", num); self.state = WorkerState::Shutdown( - sleep(time::Duration::from_secs(1)), - sleep(self.shutdown_timeout), + delay(time::Instant::now() + time::Duration::from_secs(1)), + delay(time::Instant::now() + self.shutdown_timeout), result, ); } else { let _ = result.send(true); - return Ok(Async::Ready(())); + return Poll::Ready(()); } } else { info!("Force shutdown worker, {} connections", num); self.shutdown(true); let _ = result.send(false); - return Ok(Async::Ready(())); + return Poll::Ready(()); } } @@ -282,13 +297,13 @@ impl Future for Worker { match state { WorkerState::Unavailable(mut conns) => { - match self.check_readiness(true) { + match self.check_readiness(true, cx) { Ok(true) => { self.state = WorkerState::Available; // process requests from wait queue while let Some(msg) = conns.pop() { - match self.check_readiness(false) { + match self.check_readiness(false, cx) { Ok(true) => { let guard = self.conns.get(); let _ = self.services[msg.token.0] @@ -300,7 +315,7 @@ impl Future for Worker { Ok(false) => { trace!("Worker is unavailable"); self.state = WorkerState::Unavailable(conns); - return self.poll(); + return self.poll(cx); } Err((token, idx)) => { trace!( @@ -312,16 +327,16 @@ impl Future for Worker { token, self.factories[idx].create(), ); - return self.poll(); + return self.poll(cx); } } } self.availability.set(true); - return self.poll(); + return self.poll(cx); } Ok(false) => { self.state = WorkerState::Unavailable(conns); - return Ok(Async::NotReady); + return Poll::Pending; } Err((token, idx)) => { trace!( @@ -330,13 +345,13 @@ impl Future for Worker { ); self.state = WorkerState::Restarting(idx, token, self.factories[idx].create()); - return self.poll(); + return self.poll(cx); } } } WorkerState::Restarting(idx, token, mut fut) => { - match fut.poll() { - Ok(Async::Ready(item)) => { + match Pin::new(&mut fut).poll(cx) { + Poll::Ready(Ok(item)) => { for (token, service) in item { trace!( "Service {:?} has been restarted", @@ -346,55 +361,55 @@ impl Future for Worker { self.state = WorkerState::Unavailable(Vec::new()); } } - Ok(Async::NotReady) => { - self.state = WorkerState::Restarting(idx, token, fut); - return Ok(Async::NotReady); - } - Err(_) => { + Poll::Ready(Err(_)) => { panic!( "Can not restart {:?} service", self.factories[idx].name(token) ); } + Poll::Pending => { + self.state = WorkerState::Restarting(idx, token, fut); + return Poll::Pending; + } } - return self.poll(); + return self.poll(cx); } WorkerState::Shutdown(mut t1, mut t2, tx) => { let num = num_connections(); if num == 0 { let _ = tx.send(true); Arbiter::current().stop(); - return Ok(Async::Ready(())); + return Poll::Ready(()); } // check graceful timeout - match t2.poll().unwrap() { - Async::NotReady => (), - Async::Ready(_) => { + match Pin::new(&mut t2).poll(cx) { + Poll::Pending => (), + Poll::Ready(_) => { self.shutdown(true); let _ = tx.send(false); Arbiter::current().stop(); - return Ok(Async::Ready(())); + return Poll::Ready(()); } } // sleep for 1 second and then check again - match t1.poll().unwrap() { - Async::NotReady => (), - Async::Ready(_) => { - t1 = sleep(time::Duration::from_secs(1)); - let _ = t1.poll(); + match Pin::new(&mut t1).poll(cx) { + Poll::Pending => (), + Poll::Ready(_) => { + t1 = delay(time::Instant::now() + time::Duration::from_secs(1)); + let _ = Pin::new(&mut t1).poll(cx); } } self.state = WorkerState::Shutdown(t1, t2, tx); - return Ok(Async::NotReady); + return Poll::Pending; } WorkerState::Available => { loop { - match self.rx.poll() { + match Pin::new(&mut self.rx).poll_next(cx) { // handle incoming tcp stream - Ok(Async::Ready(Some(WorkerCommand(msg)))) => { - match self.check_readiness(false) { + Poll::Ready(Some(WorkerCommand(msg))) => { + match self.check_readiness(false, cx) { Ok(true) => { let guard = self.conns.get(); let _ = self.services[msg.token.0] @@ -422,13 +437,13 @@ impl Future for Worker { ); } } - return self.poll(); + return self.poll(cx); } - Ok(Async::NotReady) => { + Poll::Pending => { self.state = WorkerState::Available; - return Ok(Async::NotReady); + return Poll::Pending; } - Ok(Async::Ready(None)) | Err(_) => return Ok(Async::Ready(())), + Poll::Ready(None) => return Poll::Ready(()), } } } diff --git a/actix-server/tests/test_server.rs b/actix-server/tests/test_server.rs index de7f7b09..22f4a4ab 100644 --- a/actix-server/tests/test_server.rs +++ b/actix-server/tests/test_server.rs @@ -4,11 +4,11 @@ use std::{net, thread, time}; use actix_codec::{BytesCodec, Framed}; use actix_server::{Io, Server, ServerConfig}; -use actix_service::{new_service_cfg, service_fn, IntoService}; +use actix_service::{factory_fn_cfg, service_fn, service_fn2}; use bytes::Bytes; -use futures::{Future, Sink}; +use futures::{future::ok, SinkExt}; use net2::TcpBuilder; -use tokio_tcp::TcpStream; +use tokio_net::tcp::TcpStream; fn unused_addr() -> net::SocketAddr { let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); @@ -28,9 +28,9 @@ fn test_bind() { let sys = actix_rt::System::new("test"); let srv = Server::build() .bind("test", addr, move || { - new_service_cfg(move |cfg: &ServerConfig| { + factory_fn_cfg(move |cfg: &ServerConfig| { assert_eq!(cfg.local_addr(), addr); - Ok::<_, ()>((|_| Ok::<_, ()>(())).into_service()) + ok::<_, ()>(service_fn2(|_| ok::<_, ()>(()))) }) }) .unwrap() @@ -54,7 +54,7 @@ fn test_bind_no_config() { let h = thread::spawn(move || { let sys = actix_rt::System::new("test"); let srv = Server::build() - .bind("test", addr, move || service_fn(|_| Ok::<_, ()>(()))) + .bind("test", addr, move || service_fn(|_| ok::<_, ()>(()))) .unwrap() .start(); let _ = tx.send((srv, actix_rt::System::current())); @@ -76,9 +76,9 @@ fn test_listen() { let lst = net::TcpListener::bind(addr).unwrap(); let srv = Server::build() .listen("test", lst, move || { - new_service_cfg(move |cfg: &ServerConfig| { + factory_fn_cfg(move |cfg: &ServerConfig| { assert_eq!(cfg.local_addr(), addr); - Ok::<_, ()>((|_| Ok::<_, ()>(())).into_service()) + ok::<_, ()>(service_fn2(|_| ok::<_, ()>(()))) }) }) .unwrap() @@ -102,19 +102,21 @@ fn test_start() { let h = thread::spawn(move || { let sys = actix_rt::System::new("test"); - let srv = Server::build() + let srv: Server = Server::build() .backlog(100) .bind("test", addr, move || { - new_service_cfg(move |cfg: &ServerConfig| { + factory_fn_cfg(move |cfg: &ServerConfig| { assert_eq!(cfg.local_addr(), addr); - Ok::<_, ()>( - (|io: Io| { - Framed::new(io.into_parts().0, BytesCodec) - .send(Bytes::from_static(b"test")) - .then(|_| Ok::<_, ()>(())) - }) - .into_service(), - ) + + let srv = service_fn2(|io: Io| { + async { + let mut f = Framed::new(io.into_parts().0, BytesCodec); + f.send(Bytes::from_static(b"test")).await.unwrap(); + Ok::<_, ()>(()) + } + }); + + ok::<_, ()>(srv) }) }) .unwrap() @@ -125,7 +127,7 @@ fn test_start() { }); let (srv, sys) = rx.recv().unwrap(); - let mut buf = [0u8; 4]; + let mut buf = [1u8; 4]; let mut conn = net::TcpStream::connect(addr).unwrap(); let _ = conn.read_exact(&mut buf); assert_eq!(buf, b"test"[..]); diff --git a/actix-service/Cargo.toml b/actix-service/Cargo.toml index 8bdf6bcf..9741bbfc 100644 --- a/actix-service/Cargo.toml +++ b/actix-service/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-service" -version = "0.4.2" +version = "1.0.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix Service" keywords = ["network", "framework", "async", "futures"] @@ -23,7 +23,9 @@ name = "actix_service" path = "src/lib.rs" [dependencies] -futures = "0.1.25" +futures = "0.3.1" +pin-project = "0.4.5" [dev-dependencies] -actix-rt = "0.2" \ No newline at end of file +tokio = "0.2.0-alpha.5" +actix-rt = "0.2" diff --git a/actix-service/src/and_then.rs b/actix-service/src/and_then.rs index 208c7b09..091fd141 100644 --- a/actix-service/src/and_then.rs +++ b/actix-service/src/and_then.rs @@ -1,6 +1,10 @@ -use futures::{Async, Future, Poll}; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; -use super::{IntoNewService, NewService, Service}; +use pin_project::pin_project; + +use super::{Service, ServiceFactory}; use crate::cell::Cell; /// Service for the `and_then` combinator, chaining a computation onto the end @@ -45,12 +49,12 @@ where type Error = A::Error; type Future = AndThenFuture; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - let not_ready = self.a.poll_ready()?.is_not_ready(); - if self.b.get_mut().poll_ready()?.is_not_ready() || not_ready { - Ok(Async::NotReady) + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + let not_ready = !self.a.poll_ready(cx)?.is_ready(); + if !self.b.get_mut().poll_ready(cx)?.is_ready() || not_ready { + Poll::Pending } else { - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } } @@ -59,13 +63,16 @@ where } } +#[pin_project] pub struct AndThenFuture where A: Service, B: Service, { b: Cell, + #[pin] fut_b: Option, + #[pin] fut_a: Option, } @@ -88,22 +95,33 @@ where A: Service, B: Service, { - type Item = B::Response; - type Error = A::Error; + type Output = Result; - fn poll(&mut self) -> Poll { - if let Some(ref mut fut) = self.fut_b { - return fut.poll(); - } + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); - match self.fut_a.as_mut().expect("Bug in actix-service").poll() { - Ok(Async::Ready(resp)) => { - let _ = self.fut_a.take(); - self.fut_b = Some(self.b.get_mut().call(resp)); - self.poll() + loop { + let mut fut_a = this.fut_a.as_mut(); + let mut fut_b = this.fut_b.as_mut(); + + if let Some(fut) = fut_b.as_mut().as_pin_mut() { + return fut.poll(cx); + } + + match fut_a + .as_mut() + .as_pin_mut() + .expect("Bug in actix-service") + .poll(cx) + { + Poll::Ready(Ok(resp)) => { + fut_a.set(None); + let new_fut = this.b.get_mut().call(resp); + fut_b.set(Some(new_fut)); + } + Poll::Ready(Err(e)) => return Poll::Ready(Err(e)), + Poll::Pending => return Poll::Pending, } - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(err) => Err(err), } } } @@ -111,8 +129,8 @@ where /// `AndThenNewService` new service combinator pub struct AndThenNewService where - A: NewService, - B: NewService, + A: ServiceFactory, + B: ServiceFactory, { a: A, b: B, @@ -120,8 +138,8 @@ where impl AndThenNewService where - A: NewService, - B: NewService< + A: ServiceFactory, + B: ServiceFactory< Config = A::Config, Request = A::Response, Error = A::Error, @@ -129,18 +147,15 @@ where >, { /// Create new `AndThen` combinator - pub fn new>(a: A, f: F) -> Self { - Self { - a, - b: f.into_new_service(), - } + pub fn new(a: A, b: B) -> Self { + Self { a, b } } } -impl NewService for AndThenNewService +impl ServiceFactory for AndThenNewService where - A: NewService, - B: NewService< + A: ServiceFactory, + B: ServiceFactory< Config = A::Config, Request = A::Response, Error = A::Error, @@ -163,8 +178,8 @@ where impl Clone for AndThenNewService where - A: NewService + Clone, - B: NewService + Clone, + A: ServiceFactory + Clone, + B: ServiceFactory + Clone, { fn clone(&self) -> Self { Self { @@ -174,21 +189,25 @@ where } } +#[pin_project] pub struct AndThenNewServiceFuture where - A: NewService, - B: NewService, + A: ServiceFactory, + B: ServiceFactory, { + #[pin] fut_b: B::Future, + #[pin] fut_a: A::Future, + a: Option, b: Option, } impl AndThenNewServiceFuture where - A: NewService, - B: NewService, + A: ServiceFactory, + B: ServiceFactory, { fn new(fut_a: A::Future, fut_b: B::Future) -> Self { AndThenNewServiceFuture { @@ -202,56 +221,55 @@ where impl Future for AndThenNewServiceFuture where - A: NewService, - B: NewService, + A: ServiceFactory, + B: ServiceFactory, { - type Item = AndThen; - type Error = A::InitError; + type Output = Result, A::InitError>; - fn poll(&mut self) -> Poll { - if self.a.is_none() { - if let Async::Ready(service) = self.fut_a.poll()? { - self.a = Some(service); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + if this.a.is_none() { + if let Poll::Ready(service) = this.fut_a.poll(cx)? { + *this.a = Some(service); } } - - if self.b.is_none() { - if let Async::Ready(service) = self.fut_b.poll()? { - self.b = Some(service); + if this.b.is_none() { + if let Poll::Ready(service) = this.fut_b.poll(cx)? { + *this.b = Some(service); } } - - if self.a.is_some() && self.b.is_some() { - Ok(Async::Ready(AndThen::new( - self.a.take().unwrap(), - self.b.take().unwrap(), + if this.a.is_some() && this.b.is_some() { + Poll::Ready(Ok(AndThen::new( + this.a.take().unwrap(), + this.b.take().unwrap(), ))) } else { - Ok(Async::NotReady) + Poll::Pending } } } #[cfg(test)] mod tests { - use futures::future::{ok, FutureResult}; - use futures::{Async, Poll}; use std::cell::Cell; use std::rc::Rc; + use std::task::{Context, Poll}; - use super::*; - use crate::{NewService, Service, ServiceExt}; + use futures::future::{lazy, ok, ready, Ready}; + + use crate::{factory_fn, pipeline, pipeline_factory, Service, ServiceFactory}; struct Srv1(Rc>); + impl Service for Srv1 { type Request = &'static str; type Response = &'static str; type Error = (); - type Future = FutureResult; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { self.0.set(self.0.get() + 1); - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } fn call(&mut self, req: &'static str) -> Self::Future { @@ -266,11 +284,11 @@ mod tests { type Request = &'static str; type Response = (&'static str, &'static str); type Error = (); - type Future = FutureResult; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { + fn poll_ready(&mut self, _: &mut Context) -> Poll> { self.0.set(self.0.get() + 1); - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } fn call(&mut self, req: &'static str) -> Self::Future { @@ -278,39 +296,35 @@ mod tests { } } - #[test] - fn test_poll_ready() { + #[tokio::test] + async fn test_poll_ready() { let cnt = Rc::new(Cell::new(0)); - let mut srv = Srv1(cnt.clone()).and_then(Srv2(cnt.clone())); - let res = srv.poll_ready(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(())); + let mut srv = pipeline(Srv1(cnt.clone())).and_then(Srv2(cnt.clone())); + let res = lazy(|cx| srv.poll_ready(cx)).await; + assert_eq!(res, Poll::Ready(Ok(()))); assert_eq!(cnt.get(), 2); } - #[test] - fn test_call() { + #[tokio::test] + async fn test_call() { let cnt = Rc::new(Cell::new(0)); - let mut srv = Srv1(cnt.clone()).and_then(Srv2(cnt)); - let res = srv.call("srv1").poll(); + let mut srv = pipeline(Srv1(cnt.clone())).and_then(Srv2(cnt)); + let res = srv.call("srv1").await; assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv1", "srv2"))); + assert_eq!(res.unwrap(), (("srv1", "srv2"))); } - #[test] - fn test_new_service() { + #[tokio::test] + async fn test_new_service() { let cnt = Rc::new(Cell::new(0)); let cnt2 = cnt.clone(); - let blank = move || Ok::<_, ()>(Srv1(cnt2.clone())); - let new_srv = blank - .into_new_service() - .and_then(move || Ok(Srv2(cnt.clone()))); - if let Async::Ready(mut srv) = new_srv.new_service(&()).poll().unwrap() { - let res = srv.call("srv1").poll(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv1", "srv2"))); - } else { - panic!() - } + let new_srv = + pipeline_factory(factory_fn(move || ready(Ok::<_, ()>(Srv1(cnt2.clone()))))) + .and_then(move || ready(Ok(Srv2(cnt.clone())))); + + let mut srv = new_srv.new_service(&()).await.unwrap(); + let res = srv.call("srv1").await; + assert!(res.is_ok()); + assert_eq!(res.unwrap(), ("srv1", "srv2")); } } diff --git a/actix-service/src/and_then_apply.rs b/actix-service/src/and_then_apply.rs deleted file mode 100644 index a8ce6991..00000000 --- a/actix-service/src/and_then_apply.rs +++ /dev/null @@ -1,186 +0,0 @@ -use std::rc::Rc; - -use futures::{Async, Future, Poll}; - -use crate::and_then::AndThen; -use crate::from_err::FromErr; -use crate::{NewService, Transform}; - -/// `Apply` new service combinator -pub struct AndThenTransform { - a: A, - b: B, - t: Rc, -} - -impl AndThenTransform -where - A: NewService, - B: NewService, - T: Transform, - T::Error: From, -{ - /// Create new `ApplyNewService` new service instance - pub fn new(t: T, a: A, b: B) -> Self { - Self { - a, - b, - t: Rc::new(t), - } - } -} - -impl Clone for AndThenTransform -where - A: Clone, - B: Clone, -{ - fn clone(&self) -> Self { - Self { - a: self.a.clone(), - b: self.b.clone(), - t: self.t.clone(), - } - } -} - -impl NewService for AndThenTransform -where - A: NewService, - B: NewService, - T: Transform, - T::Error: From, -{ - type Request = A::Request; - type Response = T::Response; - type Error = T::Error; - - type Config = A::Config; - type InitError = T::InitError; - type Service = AndThen, T::Transform>; - type Future = AndThenTransformFuture; - - fn new_service(&self, cfg: &A::Config) -> Self::Future { - AndThenTransformFuture { - a: None, - t: None, - t_cell: self.t.clone(), - fut_a: self.a.new_service(cfg), - fut_b: self.b.new_service(cfg), - fut_t: None, - } - } -} - -pub struct AndThenTransformFuture -where - A: NewService, - B: NewService, - T: Transform, - T::Error: From, -{ - fut_a: A::Future, - fut_b: B::Future, - fut_t: Option, - a: Option, - t: Option, - t_cell: Rc, -} - -impl Future for AndThenTransformFuture -where - A: NewService, - B: NewService, - T: Transform, - T::Error: From, -{ - type Item = AndThen, T::Transform>; - type Error = T::InitError; - - fn poll(&mut self) -> Poll { - if self.fut_t.is_none() { - if let Async::Ready(service) = self.fut_b.poll()? { - self.fut_t = Some(self.t_cell.new_transform(service)); - } - } - - if self.a.is_none() { - if let Async::Ready(service) = self.fut_a.poll()? { - self.a = Some(service); - } - } - - if let Some(ref mut fut) = self.fut_t { - if let Async::Ready(transform) = fut.poll()? { - self.t = Some(transform); - } - } - - if self.a.is_some() && self.t.is_some() { - Ok(Async::Ready(AndThen::new( - FromErr::new(self.a.take().unwrap()), - self.t.take().unwrap(), - ))) - } else { - Ok(Async::NotReady) - } - } -} - -#[cfg(test)] -mod tests { - use futures::future::{ok, FutureResult}; - use futures::{Async, Future, Poll}; - - use crate::{IntoNewService, IntoService, NewService, Service, ServiceExt}; - - #[derive(Clone)] - struct Srv; - impl Service for Srv { - type Request = (); - type Response = (); - type Error = (); - type Future = FutureResult<(), ()>; - - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) - } - - fn call(&mut self, _: ()) -> Self::Future { - ok(()) - } - } - - #[test] - fn test_apply() { - let blank = |req| Ok(req); - - let mut srv = blank - .into_service() - .apply_fn(Srv, |req: &'static str, srv: &mut Srv| { - srv.call(()).map(move |res| (req, res)) - }); - assert!(srv.poll_ready().is_ok()); - let res = srv.call("srv").poll(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv", ()))); - } - - #[test] - fn test_new_service() { - let blank = || Ok::<_, ()>((|req| Ok(req)).into_service()); - - let new_srv = blank.into_new_service().apply( - |req: &'static str, srv: &mut Srv| srv.call(()).map(move |res| (req, res)), - || Ok(Srv), - ); - if let Async::Ready(mut srv) = new_srv.new_service(&()).poll().unwrap() { - assert!(srv.poll_ready().is_ok()); - let res = srv.call("srv").poll(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv", ()))); - } else { - panic!() - } - } -} diff --git a/actix-service/src/and_then_apply_fn.rs b/actix-service/src/and_then_apply_fn.rs deleted file mode 100644 index 5fd846b7..00000000 --- a/actix-service/src/and_then_apply_fn.rs +++ /dev/null @@ -1,307 +0,0 @@ -use std::marker::PhantomData; - -use futures::{Async, Future, IntoFuture, Poll}; - -use super::{IntoNewService, IntoService, NewService, Service}; -use crate::cell::Cell; - -/// `Apply` service combinator -pub struct AndThenApply -where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Out, - Out: IntoFuture, - Out::Error: Into, -{ - a: A, - b: Cell, - f: Cell, - r: PhantomData<(Out,)>, -} - -impl AndThenApply -where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Out, - Out: IntoFuture, - Out::Error: Into, -{ - /// Create new `Apply` combinator - pub fn new, B1: IntoService>(a: A1, b: B1, f: F) -> Self { - Self { - f: Cell::new(f), - a: a.into_service(), - b: Cell::new(b.into_service()), - r: PhantomData, - } - } -} - -impl Clone for AndThenApply -where - A: Service + Clone, - B: Service, - F: FnMut(A::Response, &mut B) -> Out, - Out: IntoFuture, - Out::Error: Into, -{ - fn clone(&self) -> Self { - AndThenApply { - a: self.a.clone(), - b: self.b.clone(), - f: self.f.clone(), - r: PhantomData, - } - } -} - -impl Service for AndThenApply -where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Out, - Out: IntoFuture, - Out::Error: Into, -{ - type Request = A::Request; - type Response = Out::Item; - type Error = A::Error; - type Future = AndThenApplyFuture; - - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - let not_ready = self.a.poll_ready()?.is_not_ready(); - if self.b.get_mut().poll_ready()?.is_not_ready() || not_ready { - Ok(Async::NotReady) - } else { - Ok(Async::Ready(())) - } - } - - fn call(&mut self, req: A::Request) -> Self::Future { - AndThenApplyFuture { - b: self.b.clone(), - f: self.f.clone(), - fut_b: None, - fut_a: Some(self.a.call(req)), - } - } -} - -pub struct AndThenApplyFuture -where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Out, - Out: IntoFuture, - Out::Error: Into, -{ - b: Cell, - f: Cell, - fut_a: Option, - fut_b: Option, -} - -impl Future for AndThenApplyFuture -where - A: Service, - B: Service, - F: FnMut(A::Response, &mut B) -> Out, - Out: IntoFuture, - Out::Error: Into, -{ - type Item = Out::Item; - type Error = A::Error; - - fn poll(&mut self) -> Poll { - if let Some(ref mut fut) = self.fut_b { - return fut.poll().map_err(|e| e.into()); - } - - match self.fut_a.as_mut().expect("Bug in actix-service").poll() { - Ok(Async::Ready(resp)) => { - let _ = self.fut_a.take(); - self.fut_b = - Some((&mut *self.f.get_mut())(resp, self.b.get_mut()).into_future()); - self.poll() - } - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(err) => Err(err), - } - } -} - -/// `ApplyNewService` new service combinator -pub struct AndThenApplyNewService { - a: A, - b: B, - f: Cell, - r: PhantomData, -} - -impl AndThenApplyNewService -where - A: NewService, - B: NewService, - F: FnMut(A::Response, &mut B::Service) -> Out, - Out: IntoFuture, - Out::Error: Into, -{ - /// Create new `ApplyNewService` new service instance - pub fn new, B1: IntoNewService>(a: A1, b: B1, f: F) -> Self { - Self { - f: Cell::new(f), - a: a.into_new_service(), - b: b.into_new_service(), - r: PhantomData, - } - } -} - -impl Clone for AndThenApplyNewService -where - A: Clone, - B: Clone, -{ - fn clone(&self) -> Self { - Self { - a: self.a.clone(), - b: self.b.clone(), - f: self.f.clone(), - r: PhantomData, - } - } -} - -impl NewService for AndThenApplyNewService -where - A: NewService, - B: NewService, - F: FnMut(A::Response, &mut B::Service) -> Out, - Out: IntoFuture, - Out::Error: Into, -{ - type Request = A::Request; - type Response = Out::Item; - type Error = A::Error; - type Service = AndThenApply; - type Config = A::Config; - type InitError = A::InitError; - type Future = AndThenApplyNewServiceFuture; - - fn new_service(&self, cfg: &A::Config) -> Self::Future { - AndThenApplyNewServiceFuture { - a: None, - b: None, - f: self.f.clone(), - fut_a: self.a.new_service(cfg).into_future(), - fut_b: self.b.new_service(cfg).into_future(), - } - } -} - -pub struct AndThenApplyNewServiceFuture -where - A: NewService, - B: NewService, - F: FnMut(A::Response, &mut B::Service) -> Out, - Out: IntoFuture, - Out::Error: Into, -{ - fut_b: B::Future, - fut_a: A::Future, - f: Cell, - a: Option, - b: Option, -} - -impl Future for AndThenApplyNewServiceFuture -where - A: NewService, - B: NewService, - F: FnMut(A::Response, &mut B::Service) -> Out, - Out: IntoFuture, - Out::Error: Into, -{ - type Item = AndThenApply; - type Error = A::InitError; - - fn poll(&mut self) -> Poll { - if self.a.is_none() { - if let Async::Ready(service) = self.fut_a.poll()? { - self.a = Some(service); - } - } - - if self.b.is_none() { - if let Async::Ready(service) = self.fut_b.poll()? { - self.b = Some(service); - } - } - - if self.a.is_some() && self.b.is_some() { - Ok(Async::Ready(AndThenApply { - f: self.f.clone(), - a: self.a.take().unwrap(), - b: Cell::new(self.b.take().unwrap()), - r: PhantomData, - })) - } else { - Ok(Async::NotReady) - } - } -} - -#[cfg(test)] -mod tests { - use futures::future::{ok, FutureResult}; - use futures::{Async, Future, Poll}; - - use crate::blank::{Blank, BlankNewService}; - use crate::{NewService, Service, ServiceExt}; - - #[derive(Clone)] - struct Srv; - impl Service for Srv { - type Request = (); - type Response = (); - type Error = (); - type Future = FutureResult<(), ()>; - - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) - } - - fn call(&mut self, _: ()) -> Self::Future { - ok(()) - } - } - - #[test] - fn test_call() { - let mut srv = Blank::new().apply_fn(Srv, |req: &'static str, srv| { - srv.call(()).map(move |res| (req, res)) - }); - assert!(srv.poll_ready().is_ok()); - let res = srv.call("srv").poll(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv", ()))); - } - - #[test] - fn test_new_service() { - let new_srv = BlankNewService::new_unit().apply_fn( - || Ok(Srv), - |req: &'static str, srv| srv.call(()).map(move |res| (req, res)), - ); - if let Async::Ready(mut srv) = new_srv.new_service(&()).poll().unwrap() { - assert!(srv.poll_ready().is_ok()); - let res = srv.call("srv").poll(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv", ()))); - } else { - panic!() - } - } -} diff --git a/actix-service/src/apply.rs b/actix-service/src/apply.rs index d5f84463..e4a42f4d 100644 --- a/actix-service/src/apply.rs +++ b/actix-service/src/apply.rs @@ -1,175 +1,158 @@ +use pin_project::pin_project; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; -use futures::{Async, Future, IntoFuture, Poll}; - -use super::{IntoNewService, IntoService, NewService, Service}; +use super::{IntoService, IntoServiceFactory, Service, ServiceFactory}; /// Apply tranform function to a service -pub fn apply_fn(service: U, f: F) -> Apply +pub fn apply_fn( + service: U, + f: F, +) -> impl Service where - T: Service, - F: FnMut(In, &mut T) -> Out, - Out: IntoFuture, - Out::Error: From, + T: Service, + F: FnMut(In, &mut T) -> R, + R: Future>, U: IntoService, { Apply::new(service.into_service(), f) } /// Create factory for `apply` service. -pub fn new_apply_fn(service: U, f: F) -> ApplyNewService +pub fn apply_fn_factory( + service: U, + f: F, +) -> impl ServiceFactory< + Config = T::Config, + Request = In, + Response = Out, + Error = Err, + InitError = T::InitError, +> where - T: NewService, - F: FnMut(In, &mut T::Service) -> Out + Clone, - Out: IntoFuture, - Out::Error: From, - U: IntoNewService, + T: ServiceFactory, + F: FnMut(In, &mut T::Service) -> R + Clone, + R: Future>, + U: IntoServiceFactory, { - ApplyNewService::new(service.into_new_service(), f) + ApplyNewService::new(service.into_factory(), f) } #[doc(hidden)] /// `Apply` service combinator -pub struct Apply +#[pin_project] +struct Apply where - T: Service, + T: Service, { + #[pin] service: T, f: F, - r: PhantomData<(In, Out)>, + r: PhantomData<(In, Out, R)>, } -impl Apply +impl Apply where - T: Service, - F: FnMut(In, &mut T) -> Out, - Out: IntoFuture, - Out::Error: From, + T: Service, + F: FnMut(In, &mut T) -> R, + R: Future>, { /// Create new `Apply` combinator - pub(crate) fn new>(service: I, f: F) -> Self { + fn new(service: T, f: F) -> Self { Self { - service: service.into_service(), + service, f, r: PhantomData, } } } -impl Clone for Apply +impl Service for Apply where - T: Service + Clone, - F: Clone, -{ - fn clone(&self) -> Self { - Apply { - service: self.service.clone(), - f: self.f.clone(), - r: PhantomData, - } - } -} - -impl Service for Apply -where - T: Service, - F: FnMut(In, &mut T) -> Out, - Out: IntoFuture, - Out::Error: From, + T: Service, + F: FnMut(In, &mut T) -> R, + R: Future>, { type Request = In; - type Response = Out::Item; - type Error = Out::Error; - type Future = Out::Future; + type Response = Out; + type Error = Err; + type Future = R; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready().map_err(|e| e.into()) + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + Poll::Ready(futures::ready!(self.service.poll_ready(ctx))) } fn call(&mut self, req: In) -> Self::Future { - (self.f)(req, &mut self.service).into_future() + (self.f)(req, &mut self.service) } } /// `ApplyNewService` new service combinator -pub struct ApplyNewService +struct ApplyNewService where - T: NewService, + T: ServiceFactory, { service: T, f: F, - r: PhantomData<(In, Out)>, + r: PhantomData<(R, In, Out)>, } -impl ApplyNewService +impl ApplyNewService where - T: NewService, - F: FnMut(In, &mut T::Service) -> Out + Clone, - Out: IntoFuture, - Out::Error: From, + T: ServiceFactory, + F: FnMut(In, &mut T::Service) -> R + Clone, + R: Future>, { /// Create new `ApplyNewService` new service instance - pub(crate) fn new>(service: F1, f: F) -> Self { + fn new(service: T, f: F) -> Self { Self { f, - service: service.into_new_service(), + service, r: PhantomData, } } } -impl Clone for ApplyNewService +impl ServiceFactory for ApplyNewService where - T: NewService + Clone, - F: FnMut(In, &mut T::Service) -> Out + Clone, - Out: IntoFuture, -{ - fn clone(&self) -> Self { - Self { - service: self.service.clone(), - f: self.f.clone(), - r: PhantomData, - } - } -} - -impl NewService for ApplyNewService -where - T: NewService, - F: FnMut(In, &mut T::Service) -> Out + Clone, - Out: IntoFuture, - Out::Error: From, + T: ServiceFactory, + F: FnMut(In, &mut T::Service) -> R + Clone, + R: Future>, { type Request = In; - type Response = Out::Item; - type Error = Out::Error; + type Response = Out; + type Error = Err; type Config = T::Config; - type Service = Apply; + type Service = Apply; type InitError = T::InitError; - type Future = ApplyNewServiceFuture; + type Future = ApplyNewServiceFuture; fn new_service(&self, cfg: &T::Config) -> Self::Future { ApplyNewServiceFuture::new(self.service.new_service(cfg), self.f.clone()) } } -pub struct ApplyNewServiceFuture +#[pin_project] +struct ApplyNewServiceFuture where - T: NewService, - F: FnMut(In, &mut T::Service) -> Out + Clone, - Out: IntoFuture, + T: ServiceFactory, + F: FnMut(In, &mut T::Service) -> R + Clone, + R: Future>, { + #[pin] fut: T::Future, f: Option, r: PhantomData<(In, Out)>, } -impl ApplyNewServiceFuture +impl ApplyNewServiceFuture where - T: NewService, - F: FnMut(In, &mut T::Service) -> Out + Clone, - Out: IntoFuture, + T: ServiceFactory, + F: FnMut(In, &mut T::Service) -> R + Clone, + R: Future>, { fn new(fut: T::Future, f: F) -> Self { ApplyNewServiceFuture { @@ -180,43 +163,44 @@ where } } -impl Future for ApplyNewServiceFuture +impl Future for ApplyNewServiceFuture where - T: NewService, - F: FnMut(In, &mut T::Service) -> Out + Clone, - Out: IntoFuture, - Out::Error: From, + T: ServiceFactory, + F: FnMut(In, &mut T::Service) -> R + Clone, + R: Future>, { - type Item = Apply; - type Error = T::InitError; + type Output = Result, T::InitError>; - fn poll(&mut self) -> Poll { - if let Async::Ready(service) = self.fut.poll()? { - Ok(Async::Ready(Apply::new(service, self.f.take().unwrap()))) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + if let Poll::Ready(svc) = this.fut.poll(cx)? { + Poll::Ready(Ok(Apply::new(svc, this.f.take().unwrap()))) } else { - Ok(Async::NotReady) + Poll::Pending } } } #[cfg(test)] mod tests { - use futures::future::{ok, FutureResult}; - use futures::{Async, Future, Poll}; + use std::task::{Context, Poll}; + + use futures::future::{lazy, ok, Ready}; use super::*; - use crate::{IntoService, NewService, Service, ServiceExt}; + use crate::{pipeline, pipeline_factory, Service, ServiceFactory}; #[derive(Clone)] struct Srv; + impl Service for Srv { type Request = (); type Response = (); type Error = (); - type Future = FutureResult<(), ()>; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, _: ()) -> Self::Future { @@ -224,34 +208,42 @@ mod tests { } } - #[test] - fn test_call() { - let blank = |req| Ok(req); + #[tokio::test] + async fn test_call() { + let mut srv = pipeline(apply_fn(Srv, |req: &'static str, srv| { + let fut = srv.call(()); + async move { + let res = fut.await.unwrap(); + Ok((req, res)) + } + })); - let mut srv = blank - .into_service() - .apply_fn(Srv, |req: &'static str, srv| { - srv.call(()).map(move |res| (req, res)) - }); - assert!(srv.poll_ready().is_ok()); - let res = srv.call("srv").poll(); + assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); + + let res = srv.call("srv").await; assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv", ()))); + assert_eq!(res.unwrap(), (("srv", ()))); } - #[test] - fn test_new_service() { - let new_srv = ApplyNewService::new( - || Ok::<_, ()>(Srv), - |req: &'static str, srv| srv.call(()).map(move |res| (req, res)), - ); - if let Async::Ready(mut srv) = new_srv.new_service(&()).poll().unwrap() { - assert!(srv.poll_ready().is_ok()); - let res = srv.call("srv").poll(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv", ()))); - } else { - panic!() - } + #[tokio::test] + async fn test_new_service() { + let new_srv = pipeline_factory(apply_fn_factory( + || ok::<_, ()>(Srv), + |req: &'static str, srv| { + let fut = srv.call(()); + async move { + let res = fut.await.unwrap(); + Ok((req, res)) + } + }, + )); + + let mut srv = new_srv.new_service(&()).await.unwrap(); + + assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); + + let res = srv.call("srv").await; + assert!(res.is_ok()); + assert_eq!(res.unwrap(), (("srv", ()))); } } diff --git a/actix-service/src/apply_cfg.rs b/actix-service/src/apply_cfg.rs index a63de936..304ae1ee 100644 --- a/actix-service/src/apply_cfg.rs +++ b/actix-service/src/apply_cfg.rs @@ -1,43 +1,45 @@ +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; -use futures::future::Future; -use futures::{try_ready, Async, IntoFuture, Poll}; +use futures::ready; +use pin_project::pin_project; use crate::cell::Cell; -use crate::{IntoService, NewService, Service}; +use crate::{IntoService, Service, ServiceFactory}; /// Convert `Fn(&Config, &mut Service) -> Future` fn to a NewService -pub fn apply_cfg( +pub fn apply_cfg( srv: T, f: F, -) -> impl NewService< +) -> impl ServiceFactory< Config = C, Request = S::Request, Response = S::Response, Error = S::Error, Service = S, - InitError = R::Error, + InitError = E, > + Clone where F: FnMut(&C, &mut T) -> R, T: Service, - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { ApplyConfigService { f: Cell::new(f), - srv: Cell::new(srv.into_service()), + srv: Cell::new(srv), _t: PhantomData, } } /// Convert `Fn(&Config, &mut Service) -> Future` fn to a NewService /// Service get constructor from NewService. -pub fn new_apply_cfg( +pub fn apply_cfg_factory( srv: T, f: F, -) -> impl NewService< +) -> impl ServiceFactory< Config = C, Request = S::Request, Response = S::Response, @@ -48,10 +50,9 @@ pub fn new_apply_cfg( where C: Clone, F: FnMut(&C, &mut T::Service) -> R, - T: NewService, + T: ServiceFactory, T::InitError: From, - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { ApplyConfigNewService { @@ -61,26 +62,26 @@ where } } -/// Convert `Fn(&Config) -> Future` fn to NewService -struct ApplyConfigService +/// Convert `Fn(&Config) -> Future` fn to NewService\ +#[pin_project] +struct ApplyConfigService where F: FnMut(&C, &mut T) -> R, T: Service, - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { f: Cell, + #[pin] srv: Cell, _t: PhantomData<(C, R, S)>, } -impl Clone for ApplyConfigService +impl Clone for ApplyConfigService where F: FnMut(&C, &mut T) -> R, T: Service, - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { fn clone(&self) -> Self { @@ -92,12 +93,11 @@ where } } -impl NewService for ApplyConfigService +impl ServiceFactory for ApplyConfigService where F: FnMut(&C, &mut T) -> R, T: Service, - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { type Config = C; @@ -106,39 +106,37 @@ where type Error = S::Error; type Service = S; - type InitError = R::Error; - type Future = FnNewServiceConfigFut; + type InitError = E; + type Future = FnNewServiceConfigFut; fn new_service(&self, cfg: &C) -> Self::Future { FnNewServiceConfigFut { - fut: unsafe { (self.f.get_mut_unsafe())(cfg, self.srv.get_mut_unsafe()) } - .into_future(), + fut: unsafe { (self.f.get_mut_unsafe())(cfg, self.srv.get_mut_unsafe()) }, _t: PhantomData, } } } -struct FnNewServiceConfigFut +#[pin_project] +struct FnNewServiceConfigFut where - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { - fut: R::Future, + #[pin] + fut: R, _t: PhantomData<(S,)>, } -impl Future for FnNewServiceConfigFut +impl Future for FnNewServiceConfigFut where - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { - type Item = S; - type Error = R::Error; + type Output = Result; - fn poll(&mut self) -> Poll { - Ok(Async::Ready(try_ready!(self.fut.poll()).into_service())) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(Ok(ready!(self.project().fut.poll(cx))?.into_service())) } } @@ -147,9 +145,8 @@ struct ApplyConfigNewService where C: Clone, F: FnMut(&C, &mut T::Service) -> R, - T: NewService, - R: IntoFuture, - R::Item: IntoService, + T: ServiceFactory, + R: Future>, S: Service, { f: Cell, @@ -161,9 +158,8 @@ impl Clone for ApplyConfigNewService where C: Clone, F: FnMut(&C, &mut T::Service) -> R, - T: NewService, - R: IntoFuture, - R::Item: IntoService, + T: ServiceFactory, + R: Future>, S: Service, { fn clone(&self) -> Self { @@ -175,14 +171,13 @@ where } } -impl NewService for ApplyConfigNewService +impl ServiceFactory for ApplyConfigNewService where C: Clone, F: FnMut(&C, &mut T::Service) -> R, - T: NewService, + T: ServiceFactory, T::InitError: From, - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { type Config = C; @@ -191,7 +186,7 @@ where type Error = S::Error; type Service = S; - type InitError = R::Error; + type InitError = T::InitError; type Future = ApplyConfigNewServiceFut; fn new_service(&self, cfg: &C) -> Self::Future { @@ -206,21 +201,23 @@ where } } +#[pin_project] struct ApplyConfigNewServiceFut where C: Clone, F: FnMut(&C, &mut T::Service) -> R, - T: NewService, + T: ServiceFactory, T::InitError: From, - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { cfg: C, f: Cell, srv: Option, + #[pin] srv_fut: Option, - fut: Option, + #[pin] + fut: Option, _t: PhantomData<(S,)>, } @@ -228,39 +225,40 @@ impl Future for ApplyConfigNewServiceFut where C: Clone, F: FnMut(&C, &mut T::Service) -> R, - T: NewService, + T: ServiceFactory, T::InitError: From, - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { - type Item = S; - type Error = R::Error; + type Output = Result; - fn poll(&mut self) -> Poll { - if let Some(ref mut fut) = self.srv_fut { - match fut.poll()? { - Async::NotReady => return Ok(Async::NotReady), - Async::Ready(srv) => { - let _ = self.srv_fut.take(); - self.srv = Some(srv); - return self.poll(); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + 'poll: loop { + if let Some(fut) = this.srv_fut.as_mut().as_pin_mut() { + match fut.poll(cx)? { + Poll::Pending => return Poll::Pending, + Poll::Ready(srv) => { + this.srv_fut.set(None); + *this.srv = Some(srv); + continue 'poll; + } } } - } - if let Some(ref mut fut) = self.fut { - Ok(Async::Ready(try_ready!(fut.poll()).into_service())) - } else if let Some(ref mut srv) = self.srv { - match srv.poll_ready()? { - Async::NotReady => Ok(Async::NotReady), - Async::Ready(_) => { - self.fut = Some(self.f.get_mut()(&self.cfg, srv).into_future()); - return self.poll(); + if let Some(fut) = this.fut.as_mut().as_pin_mut() { + return Poll::Ready(Ok(ready!(fut.poll(cx))?.into_service())); + } else if let Some(ref mut srv) = this.srv { + match srv.poll_ready(cx)? { + Poll::Ready(_) => { + this.fut.set(Some(this.f.get_mut()(&this.cfg, srv))); + continue 'poll; + } + Poll::Pending => return Poll::Pending, } + } else { + return Poll::Pending; } - } else { - Ok(Async::NotReady) } } } diff --git a/actix-service/src/blank.rs b/actix-service/src/blank.rs deleted file mode 100644 index d02f75f8..00000000 --- a/actix-service/src/blank.rs +++ /dev/null @@ -1,84 +0,0 @@ -use std::marker::PhantomData; - -use futures::future::{ok, FutureResult}; -use futures::{Async, Poll}; - -use super::{NewService, Service}; - -/// Empty service -#[derive(Clone)] -pub struct Blank { - _t: PhantomData<(R, E)>, -} - -impl Blank { - pub fn err(self) -> Blank { - Blank { _t: PhantomData } - } -} - -impl Blank { - #[allow(clippy::new_ret_no_self)] - pub fn new() -> Blank { - Blank { _t: PhantomData } - } -} - -impl Default for Blank { - fn default() -> Blank { - Blank { _t: PhantomData } - } -} - -impl Service for Blank { - type Request = R; - type Response = R; - type Error = E; - type Future = FutureResult; - - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) - } - - fn call(&mut self, req: R) -> Self::Future { - ok(req) - } -} - -/// Empty service factory -pub struct BlankNewService { - _t: PhantomData<(R, E1, E2)>, -} - -impl BlankNewService { - pub fn new() -> BlankNewService { - BlankNewService { _t: PhantomData } - } -} - -impl BlankNewService { - pub fn new_unit() -> BlankNewService { - BlankNewService { _t: PhantomData } - } -} - -impl Default for BlankNewService { - fn default() -> BlankNewService { - Self::new() - } -} - -impl NewService for BlankNewService { - type Request = R; - type Response = R; - type Error = E1; - - type Config = (); - type Service = Blank; - type InitError = E2; - type Future = FutureResult; - - fn new_service(&self, _: &()) -> Self::Future { - ok(Blank::default()) - } -} diff --git a/actix-service/src/boxed.rs b/actix-service/src/boxed.rs index 16e37fa2..78f1d79a 100644 --- a/actix-service/src/boxed.rs +++ b/actix-service/src/boxed.rs @@ -1,7 +1,8 @@ -use futures::future::{err, ok, Either, FutureResult}; -use futures::{Async, Future, IntoFuture, Poll}; +use std::task::{Context, Poll}; -use crate::{NewService, Service}; +use futures::future::{FutureExt, LocalBoxFuture}; + +use crate::{Service, ServiceFactory}; pub type BoxedService = Box< dyn Service< @@ -12,17 +13,16 @@ pub type BoxedService = Box< >, >; -pub type BoxedServiceResponse = - Either, Box>>; +pub type BoxedServiceResponse = LocalBoxFuture<'static, Result>; pub struct BoxedNewService(Inner); /// Create boxed new service -pub fn new_service( - service: T, +pub fn factory( + factory: T, ) -> BoxedNewService where - T: NewService + 'static, + T: ServiceFactory + 'static, T::Request: 'static, T::Response: 'static, T::Service: 'static, @@ -30,8 +30,8 @@ where T::Error: 'static, T::InitError: 'static, { - BoxedNewService(Box::new(NewServiceWrapper { - service, + BoxedNewService(Box::new(FactoryWrapper { + factory, _t: std::marker::PhantomData, })) } @@ -46,18 +46,18 @@ where } type Inner = Box< - dyn NewService< + dyn ServiceFactory< Config = C, Request = Req, Response = Res, Error = Err, InitError = InitErr, Service = BoxedService, - Future = Box, Error = InitErr>>, + Future = LocalBoxFuture<'static, Result, InitErr>>, >, >; -impl NewService for BoxedNewService +impl ServiceFactory for BoxedNewService where Req: 'static, Res: 'static, @@ -70,25 +70,32 @@ where type InitError = InitErr; type Config = C; type Service = BoxedService; - type Future = Box>; + + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, cfg: &C) -> Self::Future { self.0.new_service(cfg) } } -struct NewServiceWrapper { - service: T, +struct FactoryWrapper { + factory: T, _t: std::marker::PhantomData, } -impl NewService for NewServiceWrapper +impl ServiceFactory for FactoryWrapper where Req: 'static, Res: 'static, Err: 'static, InitErr: 'static, - T: NewService, + T: ServiceFactory< + Config = C, + Request = Req, + Response = Res, + Error = Err, + InitError = InitErr, + >, T::Future: 'static, T::Service: 'static, ::Future: 'static, @@ -99,15 +106,13 @@ where type InitError = InitErr; type Config = C; type Service = BoxedService; - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; fn new_service(&self, cfg: &C) -> Self::Future { - Box::new( - self.service - .new_service(cfg) - .into_future() - .map(ServiceWrapper::boxed), - ) + self.factory + .new_service(cfg) + .map(|res| res.map(ServiceWrapper::boxed)) + .boxed_local() } } @@ -131,21 +136,13 @@ where type Request = Req; type Response = Res; type Error = Err; - type Future = Either< - FutureResult, - Box>, - >; + type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.0.poll_ready() + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + self.0.poll_ready(ctx) } fn call(&mut self, req: Self::Request) -> Self::Future { - let mut fut = self.0.call(req); - match fut.poll() { - Ok(Async::Ready(res)) => Either::A(ok(res)), - Err(e) => Either::A(err(e)), - Ok(Async::NotReady) => Either::B(Box::new(fut)), - } + self.0.call(req).boxed_local() } } diff --git a/actix-service/src/cell.rs b/actix-service/src/cell.rs index 2dc6d6a6..20be519f 100644 --- a/actix-service/src/cell.rs +++ b/actix-service/src/cell.rs @@ -1,4 +1,4 @@ -//! Custom cell impl +//! Custom cell impl, internal use only use std::{cell::UnsafeCell, fmt, rc::Rc}; pub(crate) struct Cell { diff --git a/actix-service/src/fn_service.rs b/actix-service/src/fn_service.rs index 8adcfd55..42c9a5b5 100644 --- a/actix-service/src/fn_service.rs +++ b/actix-service/src/fn_service.rs @@ -1,248 +1,245 @@ +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; -use futures::future::{ok, Future, FutureResult}; -use futures::{try_ready, Async, IntoFuture, Poll}; +use futures::future::{ok, Ready}; +use pin_project::pin_project; -use crate::{IntoNewService, IntoService, NewService, Service}; +use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory}; -/// Create `NewService` for function that can act as a Service -pub fn service_fn(f: F) -> NewServiceFn +/// Create `ServiceFactory` for function that can act as a `Service` +pub fn service_fn( + f: F, +) -> impl ServiceFactory + + Clone where - F: FnMut(Req) -> Out + Clone, - Out: IntoFuture, + F: FnMut(Req) -> Fut + Clone, + Fut: Future>, { NewServiceFn::new(f) } -/// Create `NewService` for function that can produce services -pub fn new_service_fn(f: F) -> FnNewServiceNoConfig +pub fn service_fn2( + f: F, +) -> impl Service +where + F: FnMut(Req) -> Fut, + Fut: Future>, +{ + ServiceFn::new(f) +} + +/// Create `ServiceFactory` for function that can produce services +pub fn factory_fn( + f: F, +) -> impl ServiceFactory< + Config = Cfg, + Service = S, + Request = S::Request, + Response = S::Response, + Error = S::Error, + InitError = Err, + Future = Fut, +> where - F: Fn() -> R, - R: IntoFuture, - R::Item: IntoService, S: Service, + F: Fn() -> Fut, + Fut: Future>, { FnNewServiceNoConfig::new(f) } -/// Create `NewService` for function that can produce services with configuration -pub fn new_service_cfg(f: F) -> FnNewServiceConfig +/// Create `ServiceFactory` for function that can produce services with configuration +pub fn factory_fn_cfg( + f: F, +) -> impl ServiceFactory< + Config = Cfg, + Service = Srv, + Request = Srv::Request, + Response = Srv::Response, + Error = Srv::Error, + InitError = Err, +> where - F: Fn(&C) -> R, - R: IntoFuture, - R::Item: IntoService, - S: Service, + F: Fn(&Cfg) -> Fut, + Fut: Future>, + Srv: Service, { FnNewServiceConfig::new(f) } -pub struct ServiceFn +pub struct ServiceFn where - F: FnMut(Req) -> Out, - Out: IntoFuture, + F: FnMut(Req) -> Fut, + Fut: Future>, { f: F, _t: PhantomData, } -impl ServiceFn +impl ServiceFn where - F: FnMut(Req) -> Out, - Out: IntoFuture, + F: FnMut(Req) -> Fut, + Fut: Future>, { pub(crate) fn new(f: F) -> Self { ServiceFn { f, _t: PhantomData } } } -impl Clone for ServiceFn +impl Clone for ServiceFn where - F: FnMut(Req) -> Out + Clone, - Out: IntoFuture, + F: FnMut(Req) -> Fut + Clone, + Fut: Future>, { fn clone(&self) -> Self { ServiceFn::new(self.f.clone()) } } -impl Service for ServiceFn +impl Service for ServiceFn where - F: FnMut(Req) -> Out, - Out: IntoFuture, + F: FnMut(Req) -> Fut, + Fut: Future>, { type Request = Req; - type Response = Out::Item; - type Error = Out::Error; - type Future = Out::Future; + type Response = Res; + type Error = Err; + type Future = Fut; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: Req) -> Self::Future { - (self.f)(req).into_future() + (self.f)(req) } } -impl IntoService> for F +impl IntoService> for F where - F: FnMut(Req) -> Out, - Out: IntoFuture, + F: FnMut(Req) -> Fut, + Fut: Future>, { - fn into_service(self) -> ServiceFn { + fn into_service(self) -> ServiceFn { ServiceFn::new(self) } } -pub struct NewServiceFn +struct NewServiceFn where - F: FnMut(Req) -> Out, - Out: IntoFuture, + F: FnMut(Req) -> Fut, + Fut: Future>, { f: F, _t: PhantomData<(Req, Cfg)>, } -impl NewServiceFn +impl NewServiceFn where - F: FnMut(Req) -> Out + Clone, - Out: IntoFuture, + F: FnMut(Req) -> Fut + Clone, + Fut: Future>, { - pub(crate) fn new(f: F) -> Self { + fn new(f: F) -> Self { NewServiceFn { f, _t: PhantomData } } } -impl Clone for NewServiceFn +impl Clone for NewServiceFn where - F: FnMut(Req) -> Out + Clone, - Out: IntoFuture, + F: FnMut(Req) -> Fut + Clone, + Fut: Future>, { fn clone(&self) -> Self { NewServiceFn::new(self.f.clone()) } } -impl NewService for NewServiceFn +impl ServiceFactory for NewServiceFn where - F: FnMut(Req) -> Out + Clone, - Out: IntoFuture, + F: FnMut(Req) -> Fut + Clone, + Fut: Future>, { type Request = Req; - type Response = Out::Item; - type Error = Out::Error; + type Response = Res; + type Error = Err; type Config = Cfg; - type Service = ServiceFn; + type Service = ServiceFn; type InitError = (); - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &Cfg) -> Self::Future { ok(ServiceFn::new(self.f.clone())) } } -impl IntoService> for NewServiceFn -where - F: FnMut(Req) -> Out + Clone, - Out: IntoFuture, -{ - fn into_service(self) -> ServiceFn { - ServiceFn::new(self.f.clone()) - } -} - -impl IntoNewService> for F -where - F: Fn(Req) -> Out + Clone, - Out: IntoFuture, -{ - fn into_new_service(self) -> NewServiceFn { - NewServiceFn::new(self) - } -} - /// Convert `Fn(&Config) -> Future` fn to NewService -pub struct FnNewServiceConfig +struct FnNewServiceConfig where - F: Fn(&C) -> R, - R: IntoFuture, - R::Item: IntoService, - S: Service, + F: Fn(&Cfg) -> Fut, + Fut: Future>, + Srv: Service, { f: F, - _t: PhantomData<(C, R, S, E)>, + _t: PhantomData<(Fut, Cfg, Srv, Err)>, } -impl FnNewServiceConfig +impl FnNewServiceConfig where - F: Fn(&C) -> R, - R: IntoFuture, - R::Item: IntoService, - S: Service, + F: Fn(&Cfg) -> Fut, + Fut: Future>, + Srv: Service, { pub fn new(f: F) -> Self { FnNewServiceConfig { f, _t: PhantomData } } } -impl NewService for FnNewServiceConfig +impl ServiceFactory for FnNewServiceConfig where - F: Fn(&C) -> R, - R: IntoFuture, - R::Item: IntoService, - S: Service, + F: Fn(&Cfg) -> Fut, + Fut: Future>, + Srv: Service, { - type Request = S::Request; - type Response = S::Response; - type Error = S::Error; + type Request = Srv::Request; + type Response = Srv::Response; + type Error = Srv::Error; - type Config = C; - type Service = S; - type InitError = E; - type Future = FnNewServiceConfigFut; + type Config = Cfg; + type Service = Srv; + type InitError = Err; + type Future = FnNewServiceConfigFut; - fn new_service(&self, cfg: &C) -> Self::Future { + fn new_service(&self, cfg: &Cfg) -> Self::Future { FnNewServiceConfigFut { - fut: (self.f)(cfg).into_future(), + fut: (self.f)(cfg), _t: PhantomData, } } } -pub struct FnNewServiceConfigFut +#[pin_project] +struct FnNewServiceConfigFut where - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { - fut: R::Future, + #[pin] + fut: R, _t: PhantomData<(S,)>, } impl Future for FnNewServiceConfigFut where - R: IntoFuture, - R::Item: IntoService, + R: Future>, S: Service, { - type Item = S; - type Error = R::Error; + type Output = Result; - fn poll(&mut self) -> Poll { - Ok(Async::Ready(try_ready!(self.fut.poll()).into_service())) - } -} - -impl Clone for FnNewServiceConfig -where - F: Fn(&C) -> R + Clone, - R: IntoFuture, - R::Item: IntoService, - S: Service, -{ - fn clone(&self) -> Self { - Self::new(self.f.clone()) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Poll::Ready(Ok(futures::ready!(self.project().fut.poll(cx))?)) } } @@ -250,7 +247,7 @@ where pub struct FnNewServiceNoConfig where F: Fn() -> R, - R: IntoFuture, + R: Future>, S: Service, { f: F, @@ -260,18 +257,18 @@ where impl FnNewServiceNoConfig where F: Fn() -> R, - R: IntoFuture, + R: Future>, S: Service, { - pub fn new(f: F) -> Self { + fn new(f: F) -> Self { FnNewServiceNoConfig { f, _t: PhantomData } } } -impl NewService for FnNewServiceNoConfig +impl ServiceFactory for FnNewServiceNoConfig where F: Fn() -> R, - R: IntoFuture, + R: Future>, S: Service, { type Request = S::Request; @@ -280,17 +277,17 @@ where type Service = S; type Config = C; type InitError = E; - type Future = R::Future; + type Future = R; fn new_service(&self, _: &C) -> Self::Future { - (self.f)().into_future() + (self.f)() } } impl Clone for FnNewServiceNoConfig where F: Fn() -> R + Clone, - R: IntoFuture, + R: Future>, S: Service, { fn clone(&self) -> Self { @@ -298,13 +295,13 @@ where } } -impl IntoNewService> for F +impl IntoServiceFactory> for F where F: Fn() -> R, - R: IntoFuture, + R: Future>, S: Service, { - fn into_new_service(self) -> FnNewServiceNoConfig { + fn into_factory(self) -> FnNewServiceNoConfig { FnNewServiceNoConfig::new(self) } } diff --git a/actix-service/src/fn_transform.rs b/actix-service/src/fn_transform.rs deleted file mode 100644 index 2e4be2d5..00000000 --- a/actix-service/src/fn_transform.rs +++ /dev/null @@ -1,80 +0,0 @@ -use std::marker::PhantomData; - -use futures::future::{ok, FutureResult}; -use futures::IntoFuture; - -use crate::apply::Apply; -use crate::{IntoTransform, Service, Transform}; - -/// Use function as transform service -pub fn transform_fn( - f: F, -) -> impl Transform -where - S: Service, - F: FnMut(In, &mut S) -> Out + Clone, - Out: IntoFuture, - Out::Error: From, -{ - FnTransform::new(f) -} - -pub struct FnTransform -where - F: FnMut(In, &mut S) -> Out + Clone, - Out: IntoFuture, -{ - f: F, - _t: PhantomData<(S, In, Out, Err)>, -} - -impl FnTransform -where - F: FnMut(In, &mut S) -> Out + Clone, - Out: IntoFuture, -{ - pub fn new(f: F) -> Self { - FnTransform { f, _t: PhantomData } - } -} - -impl Transform for FnTransform -where - S: Service, - F: FnMut(In, &mut S) -> Out + Clone, - Out: IntoFuture, - Out::Error: From, -{ - type Request = In; - type Response = Out::Item; - type Error = Out::Error; - type Transform = Apply; - type InitError = Err; - type Future = FutureResult; - - fn new_transform(&self, service: S) -> Self::Future { - ok(Apply::new(service, self.f.clone())) - } -} - -impl IntoTransform, S> for F -where - S: Service, - F: FnMut(In, &mut S) -> Out + Clone, - Out: IntoFuture, - Out::Error: From, -{ - fn into_transform(self) -> FnTransform { - FnTransform::new(self) - } -} - -impl Clone for FnTransform -where - F: FnMut(In, &mut S) -> Out + Clone, - Out: IntoFuture, -{ - fn clone(&self) -> Self { - Self::new(self.f.clone()) - } -} diff --git a/actix-service/src/from_err.rs b/actix-service/src/from_err.rs deleted file mode 100644 index 5d37d725..00000000 --- a/actix-service/src/from_err.rs +++ /dev/null @@ -1,220 +0,0 @@ -use std::marker::PhantomData; - -use futures::{Async, Future, Poll}; - -use super::{NewService, Service}; - -/// Service for the `from_err` combinator, changing the error type of a service. -/// -/// This is created by the `ServiceExt::from_err` method. -pub struct FromErr { - service: A, - f: PhantomData, -} - -impl FromErr { - pub(crate) fn new(service: A) -> Self - where - A: Service, - E: From, - { - FromErr { - service, - f: PhantomData, - } - } -} - -impl Clone for FromErr -where - A: Clone, -{ - fn clone(&self) -> Self { - FromErr { - service: self.service.clone(), - f: PhantomData, - } - } -} - -impl Service for FromErr -where - A: Service, - E: From, -{ - type Request = A::Request; - type Response = A::Response; - type Error = E; - type Future = FromErrFuture; - - fn poll_ready(&mut self) -> Poll<(), E> { - self.service.poll_ready().map_err(E::from) - } - - fn call(&mut self, req: A::Request) -> Self::Future { - FromErrFuture { - fut: self.service.call(req), - f: PhantomData, - } - } -} - -pub struct FromErrFuture { - fut: A::Future, - f: PhantomData, -} - -impl Future for FromErrFuture -where - A: Service, - E: From, -{ - type Item = A::Response; - type Error = E; - - fn poll(&mut self) -> Poll { - self.fut.poll().map_err(E::from) - } -} - -/// NewService for the `from_err` combinator, changing the type of a new -/// service's error. -/// -/// This is created by the `NewServiceExt::from_err` method. -pub struct FromErrNewService { - a: A, - e: PhantomData, -} - -impl FromErrNewService { - /// Create new `FromErr` new service instance - pub fn new(a: A) -> Self - where - A: NewService, - E: From, - { - Self { a, e: PhantomData } - } -} - -impl Clone for FromErrNewService -where - A: Clone, -{ - fn clone(&self) -> Self { - Self { - a: self.a.clone(), - e: PhantomData, - } - } -} - -impl NewService for FromErrNewService -where - A: NewService, - E: From, -{ - type Request = A::Request; - type Response = A::Response; - type Error = E; - - type Config = A::Config; - type Service = FromErr; - type InitError = A::InitError; - type Future = FromErrNewServiceFuture; - - fn new_service(&self, cfg: &A::Config) -> Self::Future { - FromErrNewServiceFuture { - fut: self.a.new_service(cfg), - e: PhantomData, - } - } -} - -pub struct FromErrNewServiceFuture -where - A: NewService, - E: From, -{ - fut: A::Future, - e: PhantomData, -} - -impl Future for FromErrNewServiceFuture -where - A: NewService, - E: From, -{ - type Item = FromErr; - type Error = A::InitError; - - fn poll(&mut self) -> Poll { - if let Async::Ready(service) = self.fut.poll()? { - Ok(Async::Ready(FromErr::new(service))) - } else { - Ok(Async::NotReady) - } - } -} - -#[cfg(test)] -mod tests { - use futures::future::{err, FutureResult}; - - use super::*; - use crate::{IntoNewService, NewService, Service, ServiceExt}; - - struct Srv; - impl Service for Srv { - type Request = (); - type Response = (); - type Error = (); - type Future = FutureResult<(), ()>; - - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Err(()) - } - - fn call(&mut self, _: ()) -> Self::Future { - err(()) - } - } - - #[derive(Debug, PartialEq)] - struct Error; - - impl From<()> for Error { - fn from(_: ()) -> Self { - Error - } - } - - #[test] - fn test_poll_ready() { - let mut srv = Srv.from_err::(); - let res = srv.poll_ready(); - assert!(res.is_err()); - assert_eq!(res.err().unwrap(), Error); - } - - #[test] - fn test_call() { - let mut srv = Srv.from_err::(); - let res = srv.call(()).poll(); - assert!(res.is_err()); - assert_eq!(res.err().unwrap(), Error); - } - - #[test] - fn test_new_service() { - let blank = || Ok::<_, ()>(Srv); - let new_srv = blank.into_new_service().from_err::(); - if let Async::Ready(mut srv) = new_srv.new_service(&()).poll().unwrap() { - let res = srv.call(()).poll(); - assert!(res.is_err()); - assert_eq!(res.err().unwrap(), Error); - } else { - panic!() - } - } -} diff --git a/actix-service/src/into.rs b/actix-service/src/into.rs new file mode 100644 index 00000000..b6b3f612 --- /dev/null +++ b/actix-service/src/into.rs @@ -0,0 +1,204 @@ +use std::task::{Context, Poll}; + +use crate::map::{Map, MapNewService}; +use crate::map_err::{MapErr, MapErrNewService}; +use crate::map_init_err::MapInitErr; +use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory}; + +#[inline] +/// Convert object of type `U` to a service `T` +pub fn into_service(service: U) -> ServiceMapper +where + U: IntoService, + T: Service, +{ + ServiceMapper { + service: service.into_service(), + } +} + +pub fn into_factory(factory: F) -> ServiceFactoryMapper +where + T: ServiceFactory, + F: IntoServiceFactory, +{ + ServiceFactoryMapper { + factory: factory.into_factory(), + } +} + +pub struct ServiceMapper { + service: T, +} + +pub struct ServiceFactoryMapper { + factory: T, +} + +impl ServiceMapper { + /// 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. + pub fn map( + self, + f: F, + ) -> ServiceMapper> + where + Self: Sized, + F: FnMut(T::Response) -> R + Clone, + { + ServiceMapper { + service: Map::new(self.service, 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. + pub fn map_err( + self, + f: F, + ) -> ServiceMapper> + where + Self: Sized, + F: Fn(T::Error) -> E + Clone, + { + ServiceMapper { + service: MapErr::new(self, f), + } + } +} + +impl Clone for ServiceMapper +where + T: Clone, +{ + fn clone(&self) -> Self { + ServiceMapper { + service: self.service.clone(), + } + } +} + +impl Service for ServiceMapper { + type Request = T::Request; + type Response = T::Response; + type Error = T::Error; + type Future = T::Future; + + #[inline] + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(ctx) + } + + #[inline] + fn call(&mut self, req: T::Request) -> Self::Future { + self.service.call(req) + } +} + +impl ServiceFactoryMapper { + /// Map this service's output to a different type, returning a new service + /// of the resulting type. + pub fn map( + self, + f: F, + ) -> ServiceFactoryMapper< + impl ServiceFactory< + Config = T::Config, + Request = T::Request, + Response = R, + Error = T::Error, + InitError = T::InitError, + >, + > + where + Self: Sized, + F: FnMut(T::Response) -> R + Clone, + { + ServiceFactoryMapper { + factory: MapNewService::new(self.factory, f), + } + } + + /// Map this service's error to a different error, returning a new service. + pub fn map_err( + self, + f: F, + ) -> ServiceFactoryMapper< + impl ServiceFactory< + Config = T::Config, + Request = T::Request, + Response = T::Response, + Error = E, + InitError = T::InitError, + >, + > + where + Self: Sized, + F: Fn(T::Error) -> E + Clone, + { + ServiceFactoryMapper { + factory: MapErrNewService::new(self.factory, f), + } + } + + /// Map this factory's init error to a different error, returning a new service. + pub fn map_init_err( + self, + f: F, + ) -> ServiceFactoryMapper< + impl ServiceFactory< + Config = T::Config, + Request = T::Request, + Response = T::Response, + Error = T::Error, + InitError = E, + >, + > + where + Self: Sized, + F: Fn(T::InitError) -> E + Clone, + { + ServiceFactoryMapper { + factory: MapInitErr::new(self.factory, f), + } + } +} + +impl Clone for ServiceFactoryMapper +where + T: Clone, +{ + fn clone(&self) -> Self { + ServiceFactoryMapper { + factory: self.factory.clone(), + } + } +} + +impl ServiceFactory for ServiceFactoryMapper { + type Config = T::Config; + type Request = T::Request; + type Response = T::Response; + type Error = T::Error; + type Service = T::Service; + type InitError = T::InitError; + type Future = T::Future; + + #[inline] + fn new_service(&self, cfg: &T::Config) -> Self::Future { + self.factory.new_service(cfg) + } +} diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index ee327da0..16162563 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -1,43 +1,32 @@ use std::cell::RefCell; +use std::future::Future; use std::rc::Rc; use std::sync::Arc; - -use futures::{Future, IntoFuture, Poll}; +use std::task::{self, Context, Poll}; mod and_then; -mod and_then_apply; -mod and_then_apply_fn; mod apply; mod apply_cfg; -pub mod blank; pub mod boxed; mod cell; mod fn_service; -mod fn_transform; -mod from_err; +mod into; mod map; mod map_config; mod map_err; mod map_init_err; +mod pipeline; mod then; mod transform; mod transform_err; -pub use self::and_then::{AndThen, AndThenNewService}; -pub use self::apply::{apply_fn, new_apply_fn, Apply, ApplyNewService}; -pub use self::apply_cfg::{apply_cfg, new_apply_cfg}; -pub use self::fn_service::{new_service_cfg, new_service_fn, service_fn, ServiceFn}; -pub use self::fn_transform::transform_fn; -pub use self::from_err::{FromErr, FromErrNewService}; -pub use self::map::{Map, MapNewService}; -pub use self::map_config::{MapConfig, MappedConfig, UnitConfig}; -pub use self::map_err::{MapErr, MapErrNewService}; -pub use self::map_init_err::MapInitErr; -pub use self::then::{Then, ThenNewService}; -pub use self::transform::{apply_transform, IntoTransform, Transform}; - -use self::and_then_apply::AndThenTransform; -use self::and_then_apply_fn::{AndThenApply, AndThenApplyNewService}; +pub use self::apply::{apply_fn, apply_fn_factory}; +pub use self::apply_cfg::{apply_cfg, apply_cfg_factory}; +pub use self::fn_service::{factory_fn, factory_fn_cfg, service_fn, service_fn2}; +pub use self::into::{into_factory, into_service, ServiceFactoryMapper, ServiceMapper}; +pub use self::map_config::{map_config, unit_config, MappedConfig}; +pub use self::pipeline::{pipeline, pipeline_factory, Pipeline, PipelineFactory}; +pub use self::transform::{apply, Transform}; /// An asynchronous function from `Request` to a `Response`. pub trait Service { @@ -51,7 +40,7 @@ pub trait Service { type Error; /// The future response value. - type Future: Future; + type Future: Future>; /// Returns `Ready` when the service is able to process requests. /// @@ -62,7 +51,7 @@ pub trait Service { /// 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>; + fn poll_ready(&mut self, ctx: &mut task::Context<'_>) -> Poll>; /// Process the request and return the response asynchronously. /// @@ -76,113 +65,16 @@ pub trait Service { fn call(&mut self, req: Self::Request) -> Self::Future; } -/// An extension trait for `Service`s that provides a variety of convenient -/// adapters -pub trait ServiceExt: Service { - /// Apply function to specified service and use it as a next service in - /// chain. - fn apply_fn(self, service: B1, f: F) -> AndThenApply - where - Self: Sized, - F: FnMut(Self::Response, &mut B) -> Out, - Out: IntoFuture, - Out::Error: Into, - B: Service, - B1: IntoService, - { - AndThenApply::new(self, service, f) - } - - /// Call another service after call to this one has resolved successfully. - /// - /// This function can be used to chain two services together and ensure that - /// the second service isn't called until call to the fist service have - /// finished. Result of the call to the first service is used as an - /// input parameter for the second service's call. - /// - /// Note that this function consumes the receiving service and returns a - /// wrapped version of it. - fn and_then(self, service: F) -> AndThen - where - Self: Sized, - F: IntoService, - B: 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. - fn from_err(self) -> FromErr - where - Self: Sized, - E: From, - { - 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 service and returns a - /// wrapped version of it. - fn then(self, service: B) -> Then - where - Self: Sized, - B: Service, Error = Self::Error>, - { - 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. - fn map(self, f: F) -> Map - where - Self: Sized, - F: FnMut(Self::Response) -> R, - { - 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. - fn map_err(self, f: F) -> MapErr - where - Self: Sized, - F: Fn(Self::Error) -> E, - { - MapErr::new(self, f) - } -} - -impl ServiceExt for T where T: Service {} - /// Creates new `Service` values. /// /// Acts as a service factory. This is useful for cases where new `Service` /// values must be produced. One case is a TCP server listener. The listener /// accepts new TCP streams, obtains a new `Service` value using the -/// `NewService` trait, and uses that new `Service` value to process inbound +/// `ServiceFactory` trait, and uses that new `Service` value to process inbound /// requests on that new TCP stream. /// /// `Config` is a service factory configuration type. -pub trait NewService { +pub trait ServiceFactory { /// Requests handled by the service. type Request; @@ -206,132 +98,10 @@ pub trait NewService { type InitError; /// The future of the `Service` instance. - type Future: Future; + type Future: Future>; /// Create and return a new service value asynchronously. fn new_service(&self, cfg: &Self::Config) -> Self::Future; - - /// Apply transform service to specified service and use it as a next service in - /// chain. - fn apply(self, transform: T1, service: B1) -> AndThenTransform - where - Self: Sized, - T: Transform, - T::Error: From, - T1: IntoTransform, - B: NewService, - B1: IntoNewService, - { - AndThenTransform::new(transform.into_transform(), self, service.into_new_service()) - } - - /// Apply function to specified service and use it as a next service in - /// chain. - fn apply_fn(self, service: I, f: F) -> AndThenApplyNewService - where - Self: Sized, - B: NewService, - I: IntoNewService, - F: FnMut(Self::Response, &mut B::Service) -> Out, - Out: IntoFuture, - Out::Error: Into, - { - AndThenApplyNewService::new(self, service, f) - } - - /// Call another service after call to this one has resolved successfully. - fn and_then(self, new_service: F) -> AndThenNewService - where - Self: Sized, - F: IntoNewService, - B: NewService< - Config = Self::Config, - Request = Self::Response, - Error = Self::Error, - InitError = Self::InitError, - >, - { - 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. - fn from_err(self) -> FromErrNewService - where - Self: Sized, - E: From, - { - 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. - fn then(self, new_service: F) -> ThenNewService - where - Self: Sized, - F: IntoNewService, - B: NewService< - Config = Self::Config, - Request = Result, - Error = Self::Error, - InitError = Self::InitError, - >, - { - 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, - F: FnMut(Self::Response) -> R, - { - 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, - F: Fn(Self::Error) -> E + Clone, - { - MapErrNewService::new(self, f) - } - - /// Map this factory's init error to a different error, returning a new service. - fn map_init_err(self, f: F) -> MapInitErr - where - Self: Sized, - F: Fn(Self::InitError) -> E, - { - MapInitErr::new(self, f) - } - - /// Map config to a different error, returning a new service. - fn map_config(self, f: F) -> MapConfig - where - Self: Sized, - F: Fn(&C) -> MappedConfig, - { - MapConfig::new(self, f) - } - - /// Replace config with unit - fn unit_config(self) -> UnitConfig - where - Self: NewService + Sized, - { - UnitConfig::new(self) - } } impl<'a, S> Service for &'a mut S @@ -343,8 +113,8 @@ where type Error = S::Error; type Future = S::Future; - fn poll_ready(&mut self) -> Poll<(), S::Error> { - (**self).poll_ready() + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + (**self).poll_ready(ctx) } fn call(&mut self, request: Self::Request) -> S::Future { @@ -361,8 +131,8 @@ where type Error = S::Error; type Future = S::Future; - fn poll_ready(&mut self) -> Poll<(), S::Error> { - (**self).poll_ready() + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + (**self).poll_ready(ctx) } fn call(&mut self, request: Self::Request) -> S::Future { @@ -379,18 +149,18 @@ where type Error = S::Error; type Future = S::Future; - fn poll_ready(&mut self) -> Poll<(), S::Error> { - self.borrow_mut().poll_ready() + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + self.borrow_mut().poll_ready(ctx) } fn call(&mut self, request: Self::Request) -> S::Future { - self.borrow_mut().call(request) + (&mut (**self).borrow_mut()).call(request) } } -impl NewService for Rc +impl ServiceFactory for Rc where - S: NewService, + S: ServiceFactory, { type Request = S::Request; type Response = S::Response; @@ -405,9 +175,9 @@ where } } -impl NewService for Arc +impl ServiceFactory for Arc where - S: NewService, + S: ServiceFactory, { type Request = S::Request; type Response = S::Response; @@ -431,13 +201,13 @@ where fn into_service(self) -> T; } -/// Trait for types that can be converted to a `NewService` -pub trait IntoNewService +/// Trait for types that can be converted to a `ServiceFactory` +pub trait IntoServiceFactory where - T: NewService, + T: ServiceFactory, { - /// Convert to an `NewService` - fn into_new_service(self) -> T; + /// Convert `Self` an `ServiceFactory` + fn into_factory(self) -> T; } impl IntoService for T @@ -449,11 +219,11 @@ where } } -impl IntoNewService for T +impl IntoServiceFactory for T where - T: NewService, + T: ServiceFactory, { - fn into_new_service(self) -> T { + fn into_factory(self) -> T { self } } diff --git a/actix-service/src/map.rs b/actix-service/src/map.rs index ac3654b0..f877293a 100644 --- a/actix-service/src/map.rs +++ b/actix-service/src/map.rs @@ -1,13 +1,16 @@ +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; -use futures::{Async, Future, Poll}; +use pin_project::pin_project; -use super::{NewService, Service}; +use super::{Service, ServiceFactory}; /// Service for the `map` combinator, changing the type of a service's response. /// /// This is created by the `ServiceExt::map` method. -pub struct Map { +pub(crate) struct Map { service: A, f: F, _t: PhantomData, @@ -52,8 +55,8 @@ where type Error = A::Error; type Future = MapFuture; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(ctx) } fn call(&mut self, req: A::Request) -> Self::Future { @@ -61,12 +64,14 @@ where } } -pub struct MapFuture +#[pin_project] +pub(crate) struct MapFuture where A: Service, F: FnMut(A::Response) -> Response, { f: F, + #[pin] fut: A::Future, } @@ -85,19 +90,20 @@ where A: Service, F: FnMut(A::Response) -> Response, { - type Item = Response; - type Error = A::Error; + type Output = Result; - fn poll(&mut self) -> Poll { - match self.fut.poll()? { - Async::Ready(resp) => Ok(Async::Ready((self.f)(resp))), - Async::NotReady => Ok(Async::NotReady), + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + match this.fut.poll(cx) { + Poll::Ready(Ok(resp)) => Poll::Ready(Ok((this.f)(resp))), + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), + Poll::Pending => Poll::Pending, } } } /// `MapNewService` new service combinator -pub struct MapNewService { +pub(crate) struct MapNewService { a: A, f: F, r: PhantomData, @@ -107,7 +113,7 @@ impl MapNewService { /// Create new `Map` new service instance pub fn new(a: A, f: F) -> Self where - A: NewService, + A: ServiceFactory, F: FnMut(A::Response) -> Res, { Self { @@ -132,9 +138,9 @@ where } } -impl NewService for MapNewService +impl ServiceFactory for MapNewService where - A: NewService, + A: ServiceFactory, F: FnMut(A::Response) -> Res + Clone, { type Request = A::Request; @@ -151,18 +157,20 @@ where } } -pub struct MapNewServiceFuture +#[pin_project] +pub(crate) struct MapNewServiceFuture where - A: NewService, + A: ServiceFactory, F: FnMut(A::Response) -> Res, { + #[pin] fut: A::Future, f: Option, } impl MapNewServiceFuture where - A: NewService, + A: ServiceFactory, F: FnMut(A::Response) -> Res, { fn new(fut: A::Future, f: F) -> Self { @@ -172,37 +180,38 @@ where impl Future for MapNewServiceFuture where - A: NewService, + A: ServiceFactory, F: FnMut(A::Response) -> Res, { - type Item = Map; - type Error = A::InitError; + type Output = Result, A::InitError>; - fn poll(&mut self) -> Poll { - if let Async::Ready(service) = self.fut.poll()? { - Ok(Async::Ready(Map::new(service, self.f.take().unwrap()))) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + if let Poll::Ready(svc) = this.fut.poll(cx)? { + Poll::Ready(Ok(Map::new(svc, this.f.take().unwrap()))) } else { - Ok(Async::NotReady) + Poll::Pending } } } #[cfg(test)] mod tests { - use futures::future::{ok, FutureResult}; + use futures::future::{lazy, ok, Ready}; use super::*; - use crate::{IntoNewService, Service, ServiceExt}; + use crate::{into_factory, into_service, Service}; struct Srv; + impl Service for Srv { type Request = (); type Response = (); type Error = (); - type Future = FutureResult<(), ()>; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, _: ()) -> Self::Future { @@ -210,32 +219,27 @@ mod tests { } } - #[test] - fn test_poll_ready() { - let mut srv = Srv.map(|_| "ok"); - let res = srv.poll_ready(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(())); + #[tokio::test] + async fn test_poll_ready() { + let mut srv = into_service(Srv).map(|_| "ok"); + let res = lazy(|cx| srv.poll_ready(cx)).await; + assert_eq!(res, Poll::Ready(Ok(()))); } - #[test] - fn test_call() { - let mut srv = Srv.map(|_| "ok"); - let res = srv.call(()).poll(); + #[tokio::test] + async fn test_call() { + let mut srv = into_service(Srv).map(|_| "ok"); + let res = srv.call(()).await; assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready("ok")); + assert_eq!(res.unwrap(), "ok"); } - #[test] - fn test_new_service() { - let blank = || Ok::<_, ()>(Srv); - let new_srv = blank.into_new_service().map(|_| "ok"); - if let Async::Ready(mut srv) = new_srv.new_service(&()).poll().unwrap() { - let res = srv.call(()).poll(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready("ok")); - } else { - panic!() - } + #[tokio::test] + async fn test_new_service() { + let new_srv = into_factory(|| ok::<_, ()>(Srv)).map(|_| "ok"); + let mut srv = new_srv.new_service(&()).await.unwrap(); + let res = srv.call(()).await; + assert!(res.is_ok()); + assert_eq!(res.unwrap(), ("ok")); } } diff --git a/actix-service/src/map_config.rs b/actix-service/src/map_config.rs index 16830fc0..ace723e4 100644 --- a/actix-service/src/map_config.rs +++ b/actix-service/src/map_config.rs @@ -1,14 +1,48 @@ use std::marker::PhantomData; -use super::NewService; +use super::ServiceFactory; pub enum MappedConfig<'a, T> { Ref(&'a T), Owned(T), } +/// Adapt external config to a config for provided new service +pub fn map_config( + factory: T, + f: F, +) -> impl ServiceFactory< + Config = C, + Request = T::Request, + Response = T::Response, + Error = T::Error, + InitError = T::InitError, +> +where + T: ServiceFactory, + F: Fn(&C) -> MappedConfig, +{ + MapConfig::new(factory, f) +} + +/// Replace config with unit +pub fn unit_config( + new_service: T, +) -> impl ServiceFactory< + Config = C, + Request = T::Request, + Response = T::Response, + Error = T::Error, + InitError = T::InitError, +> +where + T: ServiceFactory, +{ + UnitConfig::new(new_service) +} + /// `MapInitErr` service combinator -pub struct MapConfig { +pub(crate) struct MapConfig { a: A, f: F, e: PhantomData, @@ -18,7 +52,7 @@ impl MapConfig { /// Create new `MapConfig` combinator pub fn new(a: A, f: F) -> Self where - A: NewService, + A: ServiceFactory, F: Fn(&C) -> MappedConfig, { Self { @@ -43,9 +77,9 @@ where } } -impl NewService for MapConfig +impl ServiceFactory for MapConfig where - A: NewService, + A: ServiceFactory, F: Fn(&C) -> MappedConfig, { type Request = A::Request; @@ -66,17 +100,17 @@ where } /// `MapInitErr` service combinator -pub struct UnitConfig { +pub(crate) struct UnitConfig { a: A, e: PhantomData, } -impl UnitConfig { +impl UnitConfig +where + A: ServiceFactory, +{ /// Create new `UnitConfig` combinator - pub fn new(a: A) -> Self - where - A: NewService, - { + pub(crate) fn new(a: A) -> Self { Self { a, e: PhantomData } } } @@ -93,9 +127,9 @@ where } } -impl NewService for UnitConfig +impl ServiceFactory for UnitConfig where - A: NewService, + A: ServiceFactory, { type Request = A::Request; type Response = A::Response; diff --git a/actix-service/src/map_err.rs b/actix-service/src/map_err.rs index 47ce11fc..b76ad2af 100644 --- a/actix-service/src/map_err.rs +++ b/actix-service/src/map_err.rs @@ -1,14 +1,17 @@ +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; -use futures::{Async, Future, Poll}; +use pin_project::pin_project; -use super::{NewService, Service}; +use super::{Service, ServiceFactory}; /// Service for the `map_err` combinator, changing the type of a service's /// error. /// /// This is created by the `ServiceExt::map_err` method. -pub struct MapErr { +pub(crate) struct MapErr { service: A, f: F, _t: PhantomData, @@ -53,8 +56,8 @@ where type Error = E; type Future = MapErrFuture; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready().map_err(&self.f) + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(ctx).map_err(&self.f) } fn call(&mut self, req: A::Request) -> Self::Future { @@ -62,12 +65,14 @@ where } } -pub struct MapErrFuture +#[pin_project] +pub(crate) struct MapErrFuture where A: Service, F: Fn(A::Error) -> E, { f: F, + #[pin] fut: A::Future, } @@ -86,21 +91,21 @@ where A: Service, F: Fn(A::Error) -> E, { - type Item = A::Response; - type Error = E; + type Output = Result; - fn poll(&mut self) -> Poll { - self.fut.poll().map_err(&self.f) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + this.fut.poll(cx).map_err(this.f) } } -/// NewService for the `map_err` combinator, changing the type of a new +/// Factory for the `map_err` combinator, changing the type of a new /// service's error. /// /// This is created by the `NewServiceExt::map_err` method. -pub struct MapErrNewService +pub(crate) struct MapErrNewService where - A: NewService, + A: ServiceFactory, F: Fn(A::Error) -> E + Clone, { a: A, @@ -110,11 +115,11 @@ where impl MapErrNewService where - A: NewService, + A: ServiceFactory, F: Fn(A::Error) -> E + Clone, { /// Create new `MapErr` new service instance - pub fn new(a: A, f: F) -> Self { + pub(crate) fn new(a: A, f: F) -> Self { Self { a, f, @@ -125,7 +130,7 @@ where impl Clone for MapErrNewService where - A: NewService + Clone, + A: ServiceFactory + Clone, F: Fn(A::Error) -> E + Clone, { fn clone(&self) -> Self { @@ -137,9 +142,9 @@ where } } -impl NewService for MapErrNewService +impl ServiceFactory for MapErrNewService where - A: NewService, + A: ServiceFactory, F: Fn(A::Error) -> E + Clone, { type Request = A::Request; @@ -156,18 +161,20 @@ where } } -pub struct MapErrNewServiceFuture +#[pin_project] +pub(crate) struct MapErrNewServiceFuture where - A: NewService, + A: ServiceFactory, F: Fn(A::Error) -> E, { + #[pin] fut: A::Future, f: F, } impl MapErrNewServiceFuture where - A: NewService, + A: ServiceFactory, F: Fn(A::Error) -> E, { fn new(fut: A::Future, f: F) -> Self { @@ -177,27 +184,27 @@ where impl Future for MapErrNewServiceFuture where - A: NewService, + A: ServiceFactory, F: Fn(A::Error) -> E + Clone, { - type Item = MapErr; - type Error = A::InitError; + type Output = Result, A::InitError>; - fn poll(&mut self) -> Poll { - if let Async::Ready(service) = self.fut.poll()? { - Ok(Async::Ready(MapErr::new(service, self.f.clone()))) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + if let Poll::Ready(svc) = this.fut.poll(cx)? { + Poll::Ready(Ok(MapErr::new(svc, this.f.clone()))) } else { - Ok(Async::NotReady) + Poll::Pending } } } #[cfg(test)] mod tests { - use futures::future::{err, FutureResult}; + use futures::future::{err, lazy, ok, Ready}; use super::*; - use crate::{IntoNewService, NewService, Service, ServiceExt}; + use crate::{into_factory, into_service, Service}; struct Srv; @@ -205,10 +212,10 @@ mod tests { type Request = (); type Response = (); type Error = (); - type Future = FutureResult<(), ()>; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Err(()) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Err(())) } fn call(&mut self, _: ()) -> Self::Future { @@ -216,32 +223,27 @@ mod tests { } } - #[test] - fn test_poll_ready() { - let mut srv = Srv.map_err(|_| "error"); - let res = srv.poll_ready(); + #[tokio::test] + async fn test_poll_ready() { + let mut srv = into_service(Srv).map_err(|_| "error"); + let res = lazy(|cx| srv.poll_ready(cx)).await; + assert_eq!(res, Poll::Ready(Err("error"))); + } + + #[tokio::test] + async fn test_call() { + let mut srv = into_service(Srv).map_err(|_| "error"); + let res = srv.call(()).await; assert!(res.is_err()); assert_eq!(res.err().unwrap(), "error"); } - #[test] - fn test_call() { - let mut srv = Srv.map_err(|_| "error"); - let res = srv.call(()).poll(); + #[tokio::test] + async fn test_new_service() { + let new_srv = into_factory(|| ok::<_, ()>(Srv)).map_err(|_| "error"); + let mut srv = new_srv.new_service(&()).await.unwrap(); + let res = srv.call(()).await; assert!(res.is_err()); assert_eq!(res.err().unwrap(), "error"); } - - #[test] - fn test_new_service() { - let blank = || Ok::<_, ()>(Srv); - let new_srv = blank.into_new_service().map_err(|_| "error"); - if let Async::Ready(mut srv) = new_srv.new_service(&()).poll().unwrap() { - let res = srv.call(()).poll(); - assert!(res.is_err()); - assert_eq!(res.err().unwrap(), "error"); - } else { - panic!() - } - } } diff --git a/actix-service/src/map_init_err.rs b/actix-service/src/map_init_err.rs index 4866370a..36881692 100644 --- a/actix-service/src/map_init_err.rs +++ b/actix-service/src/map_init_err.rs @@ -1,23 +1,26 @@ +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; -use futures::{Future, Poll}; +use pin_project::pin_project; -use super::NewService; +use super::ServiceFactory; /// `MapInitErr` service combinator -pub struct MapInitErr { +pub(crate) struct MapInitErr { a: A, f: F, e: PhantomData, } -impl MapInitErr { +impl MapInitErr +where + A: ServiceFactory, + F: Fn(A::InitError) -> E, +{ /// Create new `MapInitErr` combinator - pub fn new(a: A, f: F) -> Self - where - A: NewService, - F: Fn(A::InitError) -> E, - { + pub(crate) fn new(a: A, f: F) -> Self { Self { a, f, @@ -40,9 +43,9 @@ where } } -impl NewService for MapInitErr +impl ServiceFactory for MapInitErr where - A: NewService, + A: ServiceFactory, F: Fn(A::InitError) -> E + Clone, { type Request = A::Request; @@ -58,19 +61,20 @@ where MapInitErrFuture::new(self.a.new_service(cfg), self.f.clone()) } } - -pub struct MapInitErrFuture +#[pin_project] +pub(crate) struct MapInitErrFuture where - A: NewService, + A: ServiceFactory, F: Fn(A::InitError) -> E, { f: F, + #[pin] fut: A::Future, } impl MapInitErrFuture where - A: NewService, + A: ServiceFactory, F: Fn(A::InitError) -> E, { fn new(fut: A::Future, f: F) -> Self { @@ -80,13 +84,13 @@ where impl Future for MapInitErrFuture where - A: NewService, + A: ServiceFactory, F: Fn(A::InitError) -> E, { - type Item = A::Service; - type Error = E; + type Output = Result; - fn poll(&mut self) -> Poll { - self.fut.poll().map_err(&self.f) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + this.fut.poll(cx).map_err(this.f) } } diff --git a/actix-service/src/pipeline.rs b/actix-service/src/pipeline.rs new file mode 100644 index 00000000..8c771aba --- /dev/null +++ b/actix-service/src/pipeline.rs @@ -0,0 +1,196 @@ +use std::task::{Context, Poll}; + +use crate::and_then::{AndThen, AndThenNewService}; +use crate::then::{Then, ThenNewService}; +use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory}; + +pub fn pipeline(service: F) -> Pipeline +where + F: IntoService, + T: Service, +{ + Pipeline { + service: service.into_service(), + } +} + +pub fn pipeline_factory(factory: F) -> PipelineFactory +where + T: ServiceFactory, + F: IntoServiceFactory, +{ + PipelineFactory { + factory: factory.into_factory(), + } +} + +/// Pipeline service +pub struct Pipeline { + service: T, +} + +impl Pipeline { + /// Call another service after call to this one has resolved successfully. + /// + /// This function can be used to chain two services together and ensure that + /// the second service isn't called until call to the fist service have + /// finished. Result of the call to the first service is used as an + /// input parameter for the second service's call. + /// + /// Note that this function consumes the receiving service and returns a + /// wrapped version of it. + pub fn and_then( + self, + service: F, + ) -> Pipeline> + where + Self: Sized, + F: IntoService, + U: Service, + { + Pipeline { + service: AndThen::new(self.service, service.into_service()), + } + } + + /// Chain on a computation for when a call to the service finished, + /// passing the result of the call to the next service `U`. + /// + /// Note that this function consumes the receiving pipeline and returns a + /// wrapped version of it. + pub fn then( + self, + service: F, + ) -> Pipeline> + where + Self: Sized, + F: IntoService, + U: Service, Error = T::Error>, + { + Pipeline { + service: Then::new(self.service, service.into_service()), + } + } +} + +impl Clone for Pipeline +where + T: Clone, +{ + fn clone(&self) -> Self { + Pipeline { + service: self.service.clone(), + } + } +} + +impl Service for Pipeline { + type Request = T::Request; + type Response = T::Response; + type Error = T::Error; + type Future = T::Future; + + #[inline] + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(ctx) + } + + #[inline] + fn call(&mut self, req: T::Request) -> Self::Future { + self.service.call(req) + } +} + +/// Pipeline constructor +pub struct PipelineFactory { + factory: T, +} + +impl PipelineFactory { + /// Call another service after call to this one has resolved successfully. + pub fn and_then( + self, + factory: F, + ) -> PipelineFactory< + impl ServiceFactory< + Config = T::Config, + Request = T::Request, + Response = U::Response, + Error = T::Error, + InitError = T::InitError, + >, + > + where + Self: Sized, + F: IntoServiceFactory, + U: ServiceFactory< + Config = T::Config, + Request = T::Response, + Error = T::Error, + InitError = T::InitError, + >, + { + PipelineFactory { + factory: AndThenNewService::new(self.factory, factory.into_factory()), + } + } + + /// 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 `U`. + /// + /// Note that this function consumes the receiving pipeline and returns a + /// wrapped version of it. + pub fn then( + self, + factory: F, + ) -> PipelineFactory< + impl ServiceFactory< + Config = T::Config, + Request = T::Request, + Response = U::Response, + Error = T::Error, + InitError = T::InitError, + >, + > + where + Self: Sized, + F: IntoServiceFactory, + U: ServiceFactory< + Config = T::Config, + Request = Result, + Error = T::Error, + InitError = T::InitError, + >, + { + PipelineFactory { + factory: ThenNewService::new(self.factory, factory.into_factory()), + } + } +} + +impl Clone for PipelineFactory +where + T: Clone, +{ + fn clone(&self) -> Self { + PipelineFactory { + factory: self.factory.clone(), + } + } +} + +impl ServiceFactory for PipelineFactory { + type Config = T::Config; + type Request = T::Request; + type Response = T::Response; + type Error = T::Error; + type Service = T::Service; + type InitError = T::InitError; + type Future = T::Future; + + #[inline] + fn new_service(&self, cfg: &T::Config) -> Self::Future { + self.factory.new_service(cfg) + } +} diff --git a/actix-service/src/then.rs b/actix-service/src/then.rs index 56fae3a1..8fbbe1e5 100644 --- a/actix-service/src/then.rs +++ b/actix-service/src/then.rs @@ -1,13 +1,17 @@ -use futures::{Async, Future, Poll}; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; -use super::{IntoNewService, NewService, Service}; +use pin_project::pin_project; + +use super::{Service, ServiceFactory}; use crate::cell::Cell; /// Service for the `then` combinator, chaining a computation onto the end of /// another service. /// /// This is created by the `ServiceExt::then` method. -pub struct Then { +pub(crate) struct Then { a: A, b: Cell, } @@ -45,12 +49,12 @@ where type Error = B::Error; type Future = ThenFuture; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - let not_ready = self.a.poll_ready()?.is_not_ready(); - if self.b.get_mut().poll_ready()?.is_not_ready() || not_ready { - Ok(Async::NotReady) + fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { + let not_ready = !self.a.poll_ready(ctx)?.is_ready(); + if !self.b.get_mut().poll_ready(ctx)?.is_ready() || not_ready { + Poll::Pending } else { - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } } @@ -59,13 +63,16 @@ where } } -pub struct ThenFuture +#[pin_project] +pub(crate) struct ThenFuture where A: Service, B: Service>, { b: Cell, + #[pin] fut_b: Option, + #[pin] fut_a: Option, } @@ -88,60 +95,63 @@ where A: Service, B: Service>, { - type Item = B::Response; - type Error = B::Error; + type Output = Result; - fn poll(&mut self) -> Poll { - if let Some(ref mut fut) = self.fut_b { - return fut.poll(); - } + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); - match self.fut_a.as_mut().expect("bug in actix-service").poll() { - Ok(Async::Ready(resp)) => { - let _ = self.fut_a.take(); - self.fut_b = Some(self.b.get_mut().call(Ok(resp))); - self.poll() + loop { + let mut fut_a = this.fut_a.as_mut(); + let mut fut_b = this.fut_b.as_mut(); + + if let Some(fut) = fut_b.as_mut().as_pin_mut() { + return fut.poll(cx); } - Err(err) => { - let _ = self.fut_a.take(); - self.fut_b = Some(self.b.get_mut().call(Err(err))); - self.poll() + + match fut_a + .as_mut() + .as_pin_mut() + .expect("Bug in actix-service") + .poll(cx) + { + Poll::Ready(r) => { + fut_a.set(None); + let new_fut = this.b.get_mut().call(r); + fut_b.set(Some(new_fut)); + } + + Poll::Pending => return Poll::Pending, } - Ok(Async::NotReady) => Ok(Async::NotReady), } } } -/// `ThenNewService` new service combinator -pub struct ThenNewService { +/// `.then()` service factory combinator +pub(crate) struct ThenNewService { a: A, b: B, } -impl ThenNewService { +impl ThenNewService +where + A: ServiceFactory, + B: ServiceFactory< + Config = A::Config, + Request = Result, + Error = A::Error, + InitError = A::InitError, + >, +{ /// Create new `AndThen` combinator - pub fn new(a: A, f: F) -> Self - where - A: NewService, - B: NewService< - Config = A::Config, - Request = Result, - Error = A::Error, - InitError = A::InitError, - >, - F: IntoNewService, - { - Self { - a, - b: f.into_new_service(), - } + pub fn new(a: A, b: B) -> Self { + Self { a, b } } } -impl NewService for ThenNewService +impl ServiceFactory for ThenNewService where - A: NewService, - B: NewService< + A: ServiceFactory, + B: ServiceFactory< Config = A::Config, Request = Result, Error = A::Error, @@ -175,17 +185,20 @@ where } } -pub struct ThenNewServiceFuture +#[pin_project] +pub(crate) struct ThenNewServiceFuture where - A: NewService, - B: NewService< + A: ServiceFactory, + B: ServiceFactory< Config = A::Config, Request = Result, Error = A::Error, InitError = A::InitError, >, { + #[pin] fut_b: B::Future, + #[pin] fut_a: A::Future, a: Option, b: Option, @@ -193,8 +206,8 @@ where impl ThenNewServiceFuture where - A: NewService, - B: NewService< + A: ServiceFactory, + B: ServiceFactory< Config = A::Config, Request = Result, Error = A::Error, @@ -213,61 +226,61 @@ where impl Future for ThenNewServiceFuture where - A: NewService, - B: NewService< + A: ServiceFactory, + B: ServiceFactory< Config = A::Config, Request = Result, Error = A::Error, InitError = A::InitError, >, { - type Item = Then; - type Error = A::InitError; + type Output = Result, A::InitError>; - fn poll(&mut self) -> Poll { - if self.a.is_none() { - if let Async::Ready(service) = self.fut_a.poll()? { - self.a = Some(service); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + if this.a.is_none() { + if let Poll::Ready(service) = this.fut_a.poll(cx)? { + *this.a = Some(service); } } - - if self.b.is_none() { - if let Async::Ready(service) = self.fut_b.poll()? { - self.b = Some(service); + if this.b.is_none() { + if let Poll::Ready(service) = this.fut_b.poll(cx)? { + *this.b = Some(service); } } - - if self.a.is_some() && self.b.is_some() { - Ok(Async::Ready(Then::new( - self.a.take().unwrap(), - self.b.take().unwrap(), + if this.a.is_some() && this.b.is_some() { + Poll::Ready(Ok(Then::new( + this.a.take().unwrap(), + this.b.take().unwrap(), ))) } else { - Ok(Async::NotReady) + Poll::Pending } } } #[cfg(test)] mod tests { - use futures::future::{err, ok, FutureResult}; - use futures::{Async, Future, Poll}; use std::cell::Cell; use std::rc::Rc; + use std::task::{Context, Poll}; - use crate::{IntoNewService, NewService, Service, ServiceExt}; + use futures::future::{err, lazy, ok, ready, Ready}; + + use crate::{pipeline, pipeline_factory, Service, ServiceFactory}; #[derive(Clone)] struct Srv1(Rc>); + impl Service for Srv1 { type Request = Result<&'static str, &'static str>; type Response = &'static str; type Error = (); - type Future = FutureResult; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { self.0.set(self.0.get() + 1); - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } fn call(&mut self, req: Result<&'static str, &'static str>) -> Self::Future { @@ -284,11 +297,11 @@ mod tests { type Request = Result<&'static str, ()>; type Response = (&'static str, &'static str); type Error = (); - type Future = FutureResult; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { self.0.set(self.0.get() + 1); - Ok(Async::Ready(())) + Poll::Ready(Err(())) } fn call(&mut self, req: Result<&'static str, ()>) -> Self::Future { @@ -299,46 +312,42 @@ mod tests { } } - #[test] - fn test_poll_ready() { + #[tokio::test] + async fn test_poll_ready() { let cnt = Rc::new(Cell::new(0)); - let mut srv = Srv1(cnt.clone()).then(Srv2(cnt.clone())); - let res = srv.poll_ready(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(())); + let mut srv = pipeline(Srv1(cnt.clone())).then(Srv2(cnt.clone())); + let res = lazy(|cx| srv.poll_ready(cx)).await; + assert_eq!(res, Poll::Ready(Err(()))); assert_eq!(cnt.get(), 2); } - #[test] - fn test_call() { + #[tokio::test] + async fn test_call() { let cnt = Rc::new(Cell::new(0)); - let mut srv = Srv1(cnt.clone()).then(Srv2(cnt)).clone(); + let mut srv = pipeline(Srv1(cnt.clone())).then(Srv2(cnt)); - let res = srv.call(Ok("srv1")).poll(); + let res = srv.call(Ok("srv1")).await; assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv1", "ok"))); + assert_eq!(res.unwrap(), (("srv1", "ok"))); - let res = srv.call(Err("srv")).poll(); + let res = srv.call(Err("srv")).await; assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv2", "err"))); + assert_eq!(res.unwrap(), (("srv2", "err"))); } - #[test] - fn test_new_service() { + #[tokio::test] + async fn test_factory() { let cnt = Rc::new(Cell::new(0)); let cnt2 = cnt.clone(); - let blank = move || Ok::<_, ()>(Srv1(cnt2.clone())); - let new_srv = blank.into_new_service().then(move || Ok(Srv2(cnt.clone()))); - if let Async::Ready(mut srv) = new_srv.clone().new_service(&()).poll().unwrap() { - let res = srv.call(Ok("srv1")).poll(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv1", "ok"))); + let blank = move || ready(Ok::<_, ()>(Srv1(cnt2.clone()))); + let factory = pipeline_factory(blank).then(move || ready(Ok(Srv2(cnt.clone())))); + let mut srv = factory.new_service(&()).await.unwrap(); + let res = srv.call(Ok("srv1")).await; + assert!(res.is_ok()); + assert_eq!(res.unwrap(), (("srv1", "ok"))); - let res = srv.call(Err("srv")).poll(); - assert!(res.is_ok()); - assert_eq!(res.unwrap(), Async::Ready(("srv2", "err"))); - } else { - panic!() - } + let res = srv.call(Err("srv")).await; + assert!(res.is_ok()); + assert_eq!(res.unwrap(), (("srv2", "err"))); } } diff --git a/actix-service/src/transform.rs b/actix-service/src/transform.rs index 0e93bf72..5421ba78 100644 --- a/actix-service/src/transform.rs +++ b/actix-service/src/transform.rs @@ -1,10 +1,13 @@ +use std::future::Future; +use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; +use std::task::{Context, Poll}; -use futures::{Async, Future, IntoFuture, Poll}; +use crate::transform_err::TransformMapInitErr; +use crate::{IntoServiceFactory, Service, ServiceFactory}; -use crate::transform_err::{TransformFromErr, TransformMapInitErr}; -use crate::{IntoNewService, NewService, Service}; +use pin_project::pin_project; /// The `Transform` trait defines the interface of a Service factory. `Transform` /// is often implemented for middleware, defining how to construct a @@ -32,7 +35,7 @@ pub trait Transform { type InitError; /// The future response value. - type Future: Future; + type Future: Future>; /// Creates and returns a new Service component, asynchronously fn new_transform(&self, service: S) -> Self::Future; @@ -46,32 +49,6 @@ pub trait Transform { { TransformMapInitErr::new(self, f) } - - /// Map this service's init error to any error implementing `From` for - /// this service`s `Error`. - /// - /// Note that this function consumes the receiving transform and returns a - /// wrapped version of it. - fn from_err(self) -> TransformFromErr - where - Self: Sized, - E: From, - { - TransformFromErr::new(self) - } - - // /// Map this service's init error to service's init error - // /// if it is implementing `Into` to this service`s `InitError`. - // /// - // /// Note that this function consumes the receiving transform and returns a - // /// wrapped version of it. - // fn into_err(self) -> TransformIntoErr - // where - // Self: Sized, - // Self::InitError: From, - // { - // TransformFromErr::new(self) - // } } impl Transform for Rc @@ -106,31 +83,13 @@ where } } -/// Trait for types that can be converted to a *transform service* -pub trait IntoTransform -where - T: Transform, -{ - /// Convert to a `TransformService` - fn into_transform(self) -> T; -} - -impl IntoTransform for T -where - T: Transform, -{ - fn into_transform(self) -> T { - self - } -} - -/// Apply transform to service factory. Function returns +/// Apply transform to a service. Function returns /// services factory that in initialization creates /// service and applies transform to this service. -pub fn apply_transform( - t: F, +pub fn apply( + t: T, service: U, -) -> impl NewService< +) -> impl ServiceFactory< Config = S::Config, Request = T::Request, Response = T::Response, @@ -139,30 +98,29 @@ pub fn apply_transform( InitError = S::InitError, > + Clone where - S: NewService, + S: ServiceFactory, T: Transform, - F: IntoTransform, - U: IntoNewService, + U: IntoServiceFactory, { - ApplyTransform::new(t.into_transform(), service.into_new_service()) + ApplyTransform::new(t, service.into_factory()) } /// `Apply` transform to new service -pub struct ApplyTransform { +struct ApplyTransform { s: Rc, t: Rc, } impl ApplyTransform where - S: NewService, + S: ServiceFactory, T: Transform, { /// Create new `ApplyTransform` new service instance - pub fn new>(t: F, service: S) -> Self { + fn new(t: T, service: S) -> Self { Self { s: Rc::new(service), - t: Rc::new(t.into_transform()), + t: Rc::new(t), } } } @@ -176,9 +134,9 @@ impl Clone for ApplyTransform { } } -impl NewService for ApplyTransform +impl ServiceFactory for ApplyTransform where - S: NewService, + S: ServiceFactory, T: Transform, { type Request = T::Request; @@ -193,40 +151,45 @@ where fn new_service(&self, cfg: &S::Config) -> Self::Future { ApplyTransformFuture { t_cell: self.t.clone(), - fut_a: self.s.new_service(cfg).into_future(), + fut_a: self.s.new_service(cfg), fut_t: None, } } } -pub struct ApplyTransformFuture +#[pin_project] +struct ApplyTransformFuture where - S: NewService, + S: ServiceFactory, T: Transform, { + #[pin] fut_a: S::Future, - fut_t: Option<::Future>, + #[pin] + fut_t: Option, t_cell: Rc, } impl Future for ApplyTransformFuture where - S: NewService, + S: ServiceFactory, T: Transform, { - type Item = T::Transform; - type Error = T::InitError; + type Output = Result; - fn poll(&mut self) -> Poll { - if self.fut_t.is_none() { - if let Async::Ready(service) = self.fut_a.poll()? { - self.fut_t = Some(self.t_cell.new_transform(service).into_future()); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); + + if this.fut_t.as_mut().as_pin_mut().is_none() { + if let Poll::Ready(service) = this.fut_a.poll(cx)? { + this.fut_t.set(Some(this.t_cell.new_transform(service))); } } - if let Some(ref mut fut) = self.fut_t { - fut.poll() + + if let Some(fut) = this.fut_t.as_mut().as_pin_mut() { + fut.poll(cx) } else { - Ok(Async::NotReady) + Poll::Pending } } } diff --git a/actix-service/src/transform_err.rs b/actix-service/src/transform_err.rs index dcc3c245..d41de357 100644 --- a/actix-service/src/transform_err.rs +++ b/actix-service/src/transform_err.rs @@ -1,6 +1,9 @@ +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; -use futures::{Future, Poll}; +use pin_project::pin_project; use super::Transform; @@ -63,12 +66,13 @@ where } } } - +#[pin_project] pub struct TransformMapInitErrFuture where T: Transform, F: Fn(T::InitError) -> E, { + #[pin] fut: T::Future, f: F, } @@ -78,85 +82,10 @@ where T: Transform, F: Fn(T::InitError) -> E + Clone, { - type Item = T::Transform; - type Error = E; + type Output = Result; - fn poll(&mut self) -> Poll { - self.fut.poll().map_err(&self.f) - } -} - -/// Transform for the `from_err` combinator, changing the type of a new -/// transform's init error. -/// -/// This is created by the `Transform::from_err` method. -pub struct TransformFromErr { - t: T, - e: PhantomData<(S, E)>, -} - -impl TransformFromErr -where - T: Transform, - E: From, -{ - /// Create new `TransformFromErr` new transform instance - pub fn new(t: T) -> Self { - Self { t, e: PhantomData } - } -} - -impl Clone for TransformFromErr -where - T: Clone, -{ - fn clone(&self) -> Self { - Self { - t: self.t.clone(), - e: PhantomData, - } - } -} - -impl Transform for TransformFromErr -where - T: Transform, - E: From, -{ - type Request = T::Request; - type Response = T::Response; - type Error = T::Error; - type Transform = T::Transform; - - type InitError = E; - type Future = TransformFromErrFuture; - - fn new_transform(&self, service: S) -> Self::Future { - TransformFromErrFuture { - fut: self.t.new_transform(service), - _t: PhantomData, - } - } -} - -pub struct TransformFromErrFuture -where - T: Transform, - E: From, -{ - fut: T::Future, - _t: PhantomData, -} - -impl Future for TransformFromErrFuture -where - T: Transform, - E: From, -{ - type Item = T::Transform; - type Error = E; - - fn poll(&mut self) -> Poll { - self.fut.poll().map_err(E::from) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.project(); + this.fut.poll(cx).map_err(this.f) } } diff --git a/actix-test-server/Cargo.toml b/actix-test-server/Cargo.toml index 2ce973a3..e0f07bb9 100644 --- a/actix-test-server/Cargo.toml +++ b/actix-test-server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-test-server" -version = "0.2.3" +version = "0.2.2" authors = ["Nikolay Kim "] description = "Actix test server" keywords = ["network", "framework", "async", "futures"] @@ -11,11 +11,27 @@ categories = ["network-programming", "asynchronous"] license = "MIT/Apache-2.0" exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"] edition = "2018" +workspace = ".." + +[package.metadata.docs.rs] +features = ["ssl", "tls", "rust-tls"] [lib] name = "actix_test_server" path = "src/lib.rs" +[features] +default = [] + +# tls +tls = ["native-tls", "actix-server/tls"] + +# openssl +ssl = ["openssl", "actix-server/ssl"] + +# rustls +rust-tls = ["rustls", "tokio-rustls", "webpki", "webpki-roots"] + [dependencies] actix-rt = "0.2.1" actix-server = "0.5.0" @@ -24,9 +40,21 @@ actix-testing = "0.1.0" log = "0.4" net2 = "0.2" -futures = "0.1" -tokio-tcp = "0.1" -tokio-reactor = "0.1" +futures = { package = "futures-preview", version = "0.3.0-alpha.18"} +tokio-io = "0.2.0-alpha.4" +tokio-net = "0.2.0-alpha.4" + +# native-tls +native-tls = { version="0.2", optional = true } + +# openssl +openssl = { version="0.10", optional = true } + +#rustls +rustls = { version = "^0.16", optional = true } +tokio-rustls = { version = "^0.12.0-alpha.2", optional = true } +webpki = { version = "0.21", optional = true } +webpki-roots = { version = "0.17", optional = true } [dev-dependencies] actix-service = "0.4.0" diff --git a/actix-test-server/src/lib.rs b/actix-test-server/src/lib.rs index 4681ce9b..7a3e0623 100644 --- a/actix-test-server/src/lib.rs +++ b/actix-test-server/src/lib.rs @@ -1,2 +1,149 @@ //! Various helpers for Actix applications to use during testing. -pub use actix_testing::*; +use std::sync::mpsc; +use std::{net, thread}; + +use actix_rt::{Runtime, System}; +use actix_server::{Server, StreamServiceFactory}; +pub use actix_server_config::{Io, ServerConfig}; + +use futures::future::{lazy, Future, IntoFuture}; +use net2::TcpBuilder; +use tokio_reactor::Handle; +use tokio_tcp::TcpStream; + +/// The `TestServer` type. +/// +/// `TestServer` is very simple test server that simplify process of writing +/// integration tests for actix-net applications. +/// +/// # Examples +/// +/// ```rust +/// use actix_service::{service_fn, IntoNewService}; +/// use actix_test_server::TestServer; +/// +/// fn main() { +/// let srv = TestServer::with(|| service_fn( +/// |sock| { +/// println!("New connection: {:?}", sock); +/// Ok::<_, ()>(()) +/// } +/// )); +/// +/// println!("SOCKET: {:?}", srv.connect()); +/// } +/// ``` +pub struct TestServer; + +/// Test server runstime +pub struct TestServerRuntime { + addr: net::SocketAddr, + host: String, + port: u16, + rt: Runtime, +} + +impl TestServer { + /// Start new test server with application factory + pub fn with(factory: F) -> TestServerRuntime { + let (tx, rx) = mpsc::channel(); + + // run server in separate thread + thread::spawn(move || { + let sys = System::new("actix-test-server"); + let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); + let local_addr = tcp.local_addr().unwrap(); + + Server::build() + .listen("test", tcp, factory)? + .workers(1) + .disable_signals() + .start(); + + tx.send((System::current(), local_addr)).unwrap(); + sys.run() + }); + + let (system, addr) = rx.recv().unwrap(); + System::set_current(system); + + let rt = Runtime::new().unwrap(); + let host = format!("{}", addr.ip()); + let port = addr.port(); + + TestServerRuntime { + addr, + rt, + host, + port, + } + } + + /// Get firat available unused local address + pub fn unused_addr() -> net::SocketAddr { + let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); + let socket = TcpBuilder::new_v4().unwrap(); + socket.bind(&addr).unwrap(); + socket.reuse_address(true).unwrap(); + let tcp = socket.to_tcp_listener().unwrap(); + tcp.local_addr().unwrap() + } +} + +impl TestServerRuntime { + /// Execute future on current runtime + pub fn block_on(&mut self, fut: F) -> Result + where + F: Future, + { + self.rt.block_on(fut) + } + + /// Runs the provided function, with runtime enabled. + pub fn run_on(&mut self, f: F) -> Result + where + F: FnOnce() -> R, + R: IntoFuture, + { + self.rt.block_on(lazy(|| f().into_future())) + } + + /// Spawn future to the current runtime + pub fn spawn(&mut self, fut: F) + where + F: Future + 'static, + { + self.rt.spawn(fut); + } + + /// Test server host + pub fn host(&self) -> &str { + &self.host + } + + /// Test server port + pub fn port(&self) -> u16 { + self.port + } + + /// Get test server address + pub fn addr(&self) -> net::SocketAddr { + self.addr + } + + /// Stop http server + fn stop(&mut self) { + System::current().stop(); + } + + /// Connect to server, return tokio TcpStream + pub fn connect(&self) -> std::io::Result { + TcpStream::from_std(net::TcpStream::connect(self.addr)?, &Handle::default()) + } +} + +impl Drop for TestServerRuntime { + fn drop(&mut self) { + self.stop() + } +} diff --git a/actix-testing/Cargo.toml b/actix-testing/Cargo.toml index eb9ac455..a4257fbd 100644 --- a/actix-testing/Cargo.toml +++ b/actix-testing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-testing" -version = "0.2.0" +version = "0.3.0-alpha.1" authors = ["Nikolay Kim "] description = "Actix testing utils" keywords = ["network", "framework", "async", "futures"] @@ -17,13 +17,13 @@ name = "actix_testing" path = "src/lib.rs" [dependencies] -actix-rt = "0.2.5" -actix-server = "0.7.0" -actix-server-config = "0.2.0" -actix-service = "0.4.2" +actix-rt = "1.0.0-alpha.1" +actix-server = "0.8.0-alpha.1" +actix-server-config = "0.3.0-alpha.1" +actix-service = "1.0.0-alpha.1" log = "0.4" net2 = "0.2" -futures = "0.1" -tokio-tcp = "0.1" -tokio-reactor = "0.1" +futures = "0.3.1" +tokio = "0.2.0-alpha.6" +tokio-net = { version = "0.2.0-alpha.6" } diff --git a/actix-testing/src/lib.rs b/actix-testing/src/lib.rs index f3607f92..a4631eda 100644 --- a/actix-testing/src/lib.rs +++ b/actix-testing/src/lib.rs @@ -3,12 +3,12 @@ use std::sync::mpsc; use std::{net, thread}; use actix_rt::System; -use actix_server::{Server, ServerBuilder, StreamServiceFactory}; +use actix_server::{Server, ServerBuilder, ServiceFactory}; pub use actix_server_config::{Io, ServerConfig}; use net2::TcpBuilder; -use tokio_reactor::Handle; -use tokio_tcp::TcpStream; +use tokio_net::driver::Handle; +use tokio_net::tcp::TcpStream; mod rt; pub use self::rt::*; @@ -21,12 +21,12 @@ pub use self::rt::*; /// # Examples /// /// ```rust -/// use actix_service::{service_fn, IntoNewService}; +/// use actix_service::{service_fn}; /// use actix_testing::TestServer; /// /// fn main() { /// let srv = TestServer::with(|| service_fn( -/// |sock| { +/// |sock| async move { /// println!("New connection: {:?}", sock); /// Ok::<_, ()>(()) /// } @@ -75,7 +75,7 @@ impl TestServer { } /// Start new test server with application factory - pub fn with>(factory: F) -> TestServerRuntime { + pub fn with>(factory: F) -> TestServerRuntime { let (tx, rx) = mpsc::channel(); // run server in separate thread diff --git a/actix-testing/src/rt.rs b/actix-testing/src/rt.rs index 488f3d65..8a135a66 100644 --- a/actix-testing/src/rt.rs +++ b/actix-testing/src/rt.rs @@ -1,9 +1,11 @@ //! Various helpers for Actix applications to use during testing. use std::cell::RefCell; +use std::future::Future; use actix_rt::{System, SystemRunner}; use actix_service::Service; -use futures::future::{lazy, Future, IntoFuture}; +use futures::future::{lazy, FutureExt}; +// use futures_util::future::FutureExt; thread_local! { static RT: RefCell = { @@ -35,11 +37,11 @@ impl Drop for Inner { /// /// Note that this function is intended to be used only for testing purpose. /// This function panics on nested call. -pub fn block_on(f: F) -> Result +pub fn block_on(f: F) -> F::Output where - F: IntoFuture, + F: Future, { - RT.with(move |rt| rt.borrow_mut().get_mut().block_on(f.into_future())) + RT.with(move |rt| rt.borrow_mut().get_mut().block_on(f)) } /// Runs the provided function, blocking the current thread until the result @@ -52,21 +54,21 @@ where /// /// Note that this function is intended to be used only for testing purpose. /// This function panics on nested call. -pub fn block_fn(f: F) -> Result +pub fn block_fn(f: F) -> F::Output where F: FnOnce() -> R, - R: IntoFuture, + R: Future, { - RT.with(move |rt| rt.borrow_mut().get_mut().block_on(lazy(f))) + RT.with(move |rt| rt.borrow_mut().get_mut().block_on(lazy(|_| f()))) } /// Spawn future to the current test runtime. pub fn spawn(fut: F) where - F: Future + 'static, + F: Future + 'static, { run_on(move || { - actix_rt::spawn(fut); + actix_rt::spawn(fut.map(|_| ())); }); } @@ -78,12 +80,7 @@ pub fn run_on(f: F) -> R where F: FnOnce() -> R, { - RT.with(move |rt| { - rt.borrow_mut() - .get_mut() - .block_on(lazy(|| Ok::<_, ()>(f()))) - }) - .unwrap() + RT.with(move |rt| rt.borrow_mut().get_mut().block_on(lazy(|_| f()))) } /// Calls service and waits for response future completion. diff --git a/actix-threadpool/CHANGES.md b/actix-threadpool/CHANGES.md index 1050c503..aadf9ac9 100644 --- a/actix-threadpool/CHANGES.md +++ b/actix-threadpool/CHANGES.md @@ -1,5 +1,11 @@ # Changes +## [0.2.0] - 2019-??-?? + +### Changed + +* Migrate to `std::future` + ## [0.1.2] - 2019-08-05 ### Changed diff --git a/actix-threadpool/Cargo.toml b/actix-threadpool/Cargo.toml index 5e9d855d..da6a9133 100644 --- a/actix-threadpool/Cargo.toml +++ b/actix-threadpool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-threadpool" -version = "0.1.2" +version = "0.2.0" authors = ["Nikolay Kim "] description = "Actix thread pool for sync code" keywords = ["actix", "network", "framework", "async", "futures"] @@ -19,7 +19,7 @@ path = "src/lib.rs" [dependencies] derive_more = "0.15" -futures = "0.1.25" +futures = "0.3.1" parking_lot = "0.9" lazy_static = "1.2" log = "0.4" diff --git a/actix-threadpool/src/lib.rs b/actix-threadpool/src/lib.rs index b9952e62..5ed6b7ca 100644 --- a/actix-threadpool/src/lib.rs +++ b/actix-threadpool/src/lib.rs @@ -1,33 +1,34 @@ //! Thread pool for blocking operations -use std::fmt; +use std::{ + future::Future, + pin::Pin, + task::{Context, Poll}, +}; use derive_more::Display; -use futures::sync::oneshot; -use futures::{Async, Future, Poll}; +use futures::channel::oneshot; use parking_lot::Mutex; use threadpool::ThreadPool; -/// Env variable for default cpu pool size +/// Env variable for default cpu pool size. const ENV_CPU_POOL_VAR: &str = "ACTIX_THREADPOOL"; lazy_static::lazy_static! { pub(crate) static ref DEFAULT_POOL: Mutex = { - let default = match std::env::var(ENV_CPU_POOL_VAR) { - Ok(val) => { - if let Ok(val) = val.parse() { - val - } else { - log::error!("Can not parse ACTIX_THREADPOOL value"); - num_cpus::get() * 5 - } - } - Err(_) => num_cpus::get() * 5, - }; + let num = std::env::var(ENV_CPU_POOL_VAR) + .map_err(|_| ()) + .and_then(|val| { + val.parse().map_err(|_| log::warn!( + "Can not parse {} value, using default", + ENV_CPU_POOL_VAR, + )) + }) + .unwrap_or_else(|_| num_cpus::get() * 5); Mutex::new( threadpool::Builder::new() .thread_name("actix-web".to_owned()) - .num_threads(default) + .num_threads(num) .build(), ) }; @@ -39,22 +40,17 @@ thread_local! { }; } -/// Blocking operation execution error -#[derive(Debug, Display)] -pub enum BlockingError { - #[display(fmt = "{:?}", _0)] - Error(E), - #[display(fmt = "Thread pool is gone")] - Canceled, -} +/// Error of blocking operation execution being cancelled. +#[derive(Clone, Copy, Debug, Display)] +#[display(fmt = "Thread pool is gone")] +pub struct Cancelled; /// Execute blocking function on a thread pool, returns future that resolves /// to result of the function execution. -pub fn run(f: F) -> CpuFuture +pub fn run(f: F) -> CpuFuture where - F: FnOnce() -> Result + Send + 'static, + F: FnOnce() -> I + Send + 'static, I: Send + 'static, - E: Send + fmt::Debug + 'static, { let (tx, rx) = oneshot::channel(); POOL.with(|pool| { @@ -70,19 +66,16 @@ where /// Blocking operation completion future. It resolves with results /// of blocking function execution. -pub struct CpuFuture { - rx: oneshot::Receiver>, +pub struct CpuFuture { + rx: oneshot::Receiver, } -impl Future for CpuFuture { - type Item = I; - type Error = BlockingError; +impl Future for CpuFuture { + type Output = Result; - fn poll(&mut self) -> Poll { - let res = futures::try_ready!(self.rx.poll().map_err(|_| BlockingError::Canceled)); - match res { - Ok(val) => Ok(Async::Ready(val)), - Err(err) => Err(BlockingError::Error(err)), - } + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let rx = Pin::new(&mut Pin::get_mut(self).rx); + let res = futures::ready!(rx.poll(cx)); + Poll::Ready(res.map_err(|_| Cancelled)) } } diff --git a/actix-utils/Cargo.toml b/actix-utils/Cargo.toml index ee3c1dba..85f4212e 100644 --- a/actix-utils/Cargo.toml +++ b/actix-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "actix-utils" -version = "0.4.7" +version = "0.5.0-alpha1" authors = ["Nikolay Kim "] description = "Actix utils - various actix net related services" keywords = ["network", "framework", "async", "futures"] @@ -18,14 +18,15 @@ name = "actix_utils" path = "src/lib.rs" [dependencies] -actix-service = "0.4.1" -actix-codec = "0.1.2" +actix-service = "1.0.0-alpha.1" +actix-codec = "0.2.0-alpha.1" bytes = "0.4" either = "1.5.2" -futures = "0.1.25" -tokio-timer = "0.2.8" -tokio-current-thread = "0.1.4" +futures = "0.3.1" +pin-project = "0.4.5" +tokio-timer = "0.3.0-alpha.6" +tokio-executor = { version="=0.2.0-alpha.6", features=["current-thread"] } log = "0.4" [dev-dependencies] -actix-rt = "0.2.2" +actix-rt = "1.0.0-alpha.1" diff --git a/actix-utils/src/counter.rs b/actix-utils/src/counter.rs index 2f355094..07d62454 100644 --- a/actix-utils/src/counter.rs +++ b/actix-utils/src/counter.rs @@ -1,7 +1,8 @@ use std::cell::Cell; use std::rc::Rc; +use std::task; -use futures::task::AtomicTask; +use crate::task::LocalWaker; #[derive(Clone)] /// Simple counter with ability to notify task on reaching specific number @@ -12,7 +13,7 @@ pub struct Counter(Rc); struct CounterInner { count: Cell, capacity: usize, - task: AtomicTask, + task: LocalWaker, } impl Counter { @@ -21,7 +22,7 @@ impl Counter { Counter(Rc::new(CounterInner { capacity, count: Cell::new(0), - task: AtomicTask::new(), + task: LocalWaker::new(), })) } @@ -32,8 +33,8 @@ impl Counter { /// Check if counter is not at capacity. If counter at capacity /// it registers notification for current task. - pub fn available(&self) -> bool { - self.0.available() + pub fn available(&self, cx: &mut task::Context) -> bool { + self.0.available(cx) } /// Get total number of acquired counts @@ -66,15 +67,15 @@ impl CounterInner { let num = self.count.get(); self.count.set(num - 1); if num == self.capacity { - self.task.notify(); + self.task.wake(); } } - fn available(&self) -> bool { + fn available(&self, cx: &mut task::Context) -> bool { if self.count.get() < self.capacity { true } else { - self.task.register(); + self.task.register(cx.waker()); false } } diff --git a/actix-utils/src/either.rs b/actix-utils/src/either.rs index 6bd4e59e..2bc07611 100644 --- a/actix-utils/src/either.rs +++ b/actix-utils/src/either.rs @@ -1,6 +1,10 @@ //! Contains `Either` service and related types and functions. -use actix_service::{IntoNewService, NewService, Service}; -use futures::{future, try_ready, Async, Future, IntoFuture, Poll}; +use std::pin::Pin; +use std::task::{Context, Poll}; + +use actix_service::{Service, ServiceFactory}; +use futures::{future, ready, Future}; +use pin_project::pin_project; /// Combine two different service types into a single type. /// @@ -31,21 +35,21 @@ where type Error = A::Error; type Future = future::Either; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - let left = self.left.poll_ready()?; - let right = self.right.poll_ready()?; + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + let left = self.left.poll_ready(cx)?; + let right = self.right.poll_ready(cx)?; if left.is_ready() && right.is_ready() { - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } else { - Ok(Async::NotReady) + Poll::Pending } } fn call(&mut self, req: either::Either) -> Self::Future { match req { - either::Either::Left(req) => future::Either::A(self.left.call(req)), - either::Either::Right(req) => future::Either::B(self.right.call(req)), + either::Either::Left(req) => future::Either::Left(self.left.call(req)), + either::Either::Right(req) => future::Either::Right(self.right.call(req)), } } } @@ -57,29 +61,24 @@ pub struct Either { } impl Either { - pub fn new(srv_a: F1, srv_b: F2) -> Either + pub fn new(left: A, right: B) -> Either where - A: NewService, - B: NewService< + A: ServiceFactory, + B: ServiceFactory< Config = A::Config, Response = A::Response, Error = A::Error, InitError = A::InitError, >, - F1: IntoNewService, - F2: IntoNewService, { - Either { - left: srv_a.into_new_service(), - right: srv_b.into_new_service(), - } + Either { left, right } } } -impl NewService for Either +impl ServiceFactory for Either where - A: NewService, - B: NewService< + A: ServiceFactory, + B: ServiceFactory< Config = A::Config, Response = A::Response, Error = A::Error, @@ -113,37 +112,41 @@ impl Clone for Either { } } +#[pin_project] #[doc(hidden)] -pub struct EitherNewService { +pub struct EitherNewService { left: Option, right: Option, - left_fut: ::Future, - right_fut: ::Future, + #[pin] + left_fut: A::Future, + #[pin] + right_fut: B::Future, } impl Future for EitherNewService where - A: NewService, - B: NewService, + A: ServiceFactory, + B: ServiceFactory, { - type Item = EitherService; - type Error = A::InitError; + type Output = Result, A::InitError>; - fn poll(&mut self) -> Poll { - if self.left.is_none() { - self.left = Some(try_ready!(self.left_fut.poll())); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let this = self.project(); + + if this.left.is_none() { + *this.left = Some(ready!(this.left_fut.poll(cx))?); } - if self.right.is_none() { - self.right = Some(try_ready!(self.right_fut.poll())); + if this.right.is_none() { + *this.right = Some(ready!(this.right_fut.poll(cx))?); } - if self.left.is_some() && self.right.is_some() { - Ok(Async::Ready(EitherService { - left: self.left.take().unwrap(), - right: self.right.take().unwrap(), + if this.left.is_some() && this.right.is_some() { + Poll::Ready(Ok(EitherService { + left: this.left.take().unwrap(), + right: this.right.take().unwrap(), })) } else { - Ok(Async::NotReady) + Poll::Pending } } } diff --git a/actix-utils/src/framed.rs b/actix-utils/src/framed.rs index 2fa2d8e9..0951c3db 100644 --- a/actix-utils/src/framed.rs +++ b/actix-utils/src/framed.rs @@ -1,15 +1,20 @@ //! Framed dispatcher service and related utilities +#![allow(type_alias_bounds)] use std::collections::VecDeque; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::{fmt, mem}; use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed}; use actix_service::{IntoService, Service}; -use futures::task::AtomicTask; -use futures::unsync::mpsc; -use futures::{Async, Future, Poll, Sink, Stream}; +use futures::future::{ready, FutureExt}; +use futures::{Future, Sink, Stream}; use log::debug; +use pin_project::pin_project; use crate::cell::Cell; +use crate::mpsc; +use crate::task::LocalWaker; type Request = ::Item; type Response = ::Item; @@ -68,22 +73,26 @@ pub enum FramedMessage { Close, } +type Rx = Option::Item>>>; +type Inner = Cell::Item, S::Error>>; + /// FramedTransport - is a future that reads frames from Framed object /// and pass then to the service. +#[pin_project] pub struct FramedTransport where S: Service, Response = Response>, S::Error: 'static, S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Encoder + Decoder, + T: AsyncRead + AsyncWrite + Unpin, + U: Encoder + Decoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { service: S, state: TransportState, framed: Framed, - rx: Option::Item>>>, + rx: Option::Item>>>, inner: Cell::Item, S::Error>>, } @@ -97,7 +106,7 @@ enum TransportState { struct FramedTransportInner { buf: VecDeque>, - task: AtomicTask, + task: LocalWaker, } impl FramedTransport @@ -105,130 +114,8 @@ where S: Service, Response = Response>, S::Error: 'static, S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Decoder + Encoder, - ::Item: 'static, - ::Error: std::fmt::Debug, -{ - fn poll_read(&mut self) -> bool { - loop { - match self.service.poll_ready() { - Ok(Async::Ready(_)) => { - let item = match self.framed.poll() { - Ok(Async::Ready(Some(el))) => el, - Err(err) => { - self.state = - TransportState::FramedError(FramedTransportError::Decoder(err)); - return true; - } - Ok(Async::NotReady) => return false, - Ok(Async::Ready(None)) => { - self.state = TransportState::Stopping; - return true; - } - }; - - let mut cell = self.inner.clone(); - tokio_current_thread::spawn(self.service.call(item).then(move |item| { - let inner = cell.get_mut(); - inner.buf.push_back(item); - inner.task.notify(); - Ok(()) - })); - } - Ok(Async::NotReady) => return false, - Err(err) => { - self.state = TransportState::Error(FramedTransportError::Service(err)); - return true; - } - } - } - } - - /// write to framed object - fn poll_write(&mut self) -> bool { - let inner = self.inner.get_mut(); - let mut rx_done = self.rx.is_none(); - let mut buf_empty = inner.buf.is_empty(); - loop { - while !self.framed.is_write_buf_full() { - if !buf_empty { - match inner.buf.pop_front().unwrap() { - Ok(msg) => { - if let Err(err) = self.framed.force_send(msg) { - self.state = TransportState::FramedError( - FramedTransportError::Encoder(err), - ); - return true; - } - buf_empty = inner.buf.is_empty(); - } - Err(err) => { - self.state = - TransportState::Error(FramedTransportError::Service(err)); - return true; - } - } - } - - if !rx_done && self.rx.is_some() { - match self.rx.as_mut().unwrap().poll() { - Ok(Async::Ready(Some(FramedMessage::Message(msg)))) => { - if let Err(err) = self.framed.force_send(msg) { - self.state = TransportState::FramedError( - FramedTransportError::Encoder(err), - ); - return true; - } - } - Ok(Async::Ready(Some(FramedMessage::Close))) => { - self.state = TransportState::FlushAndStop; - return true; - } - Ok(Async::Ready(None)) => { - rx_done = true; - let _ = self.rx.take(); - } - Ok(Async::NotReady) => rx_done = true, - Err(_e) => { - rx_done = true; - let _ = self.rx.take(); - } - } - } - - if rx_done && buf_empty { - break; - } - } - - if !self.framed.is_write_buf_empty() { - match self.framed.poll_complete() { - Ok(Async::NotReady) => break, - Err(err) => { - debug!("Error sending data: {:?}", err); - self.state = - TransportState::FramedError(FramedTransportError::Encoder(err)); - return true; - } - Ok(Async::Ready(_)) => (), - } - } else { - break; - } - } - - false - } -} - -impl FramedTransport -where - S: Service, Response = Response>, - S::Error: 'static, - S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Decoder + Encoder, + T: AsyncRead + AsyncWrite + Unpin, + U: Decoder + Encoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { @@ -240,7 +127,7 @@ where state: TransportState::Processing, inner: Cell::new(FramedTransportInner { buf: VecDeque::new(), - task: AtomicTask::new(), + task: LocalWaker::new(), }), } } @@ -248,7 +135,7 @@ where /// Get Sender pub fn set_receiver( mut self, - rx: mpsc::UnboundedReceiver::Item>>, + rx: mpsc::Receiver::Item>>, ) -> Self { self.rx = Some(rx); self @@ -283,51 +170,215 @@ where S: Service, Response = Response>, S::Error: 'static, S::Future: 'static, - T: AsyncRead + AsyncWrite, - U: Decoder + Encoder, + T: AsyncRead + AsyncWrite + Unpin, + U: Decoder + Encoder + Unpin, ::Item: 'static, ::Error: std::fmt::Debug, { - type Item = (); - type Error = FramedTransportError; + type Output = Result<(), FramedTransportError>; - fn poll(&mut self) -> Poll { - self.inner.get_ref().task.register(); + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + self.inner.get_ref().task.register(cx.waker()); - match mem::replace(&mut self.state, TransportState::Processing) { - TransportState::Processing => { - if self.poll_read() || self.poll_write() { - self.poll() - } else { - Ok(Async::NotReady) - } + let this = self.project(); + poll( + cx, + this.service, + this.state, + this.framed, + this.rx, + this.inner, + ) + } +} + +fn poll( + cx: &mut Context, + srv: &mut S, + state: &mut TransportState, + framed: &mut Framed, + rx: &mut Rx, + inner: &mut Inner, +) -> Poll>> +where + S: Service, Response = Response>, + S::Error: 'static, + S::Future: 'static, + T: AsyncRead + AsyncWrite + Unpin, + U: Decoder + Encoder + Unpin, + ::Item: 'static, + ::Error: std::fmt::Debug, +{ + match mem::replace(state, TransportState::Processing) { + TransportState::Processing => { + if poll_read(cx, srv, state, framed, inner) + || poll_write(cx, state, framed, rx, inner) + { + poll(cx, srv, state, framed, rx, inner) + } else { + Poll::Pending } - TransportState::Error(err) => { - if self.framed.is_write_buf_empty() - || (self.poll_write() || self.framed.is_write_buf_empty()) - { - Err(err) - } else { - self.state = TransportState::Error(err); - Ok(Async::NotReady) - } + } + TransportState::Error(err) => { + let is_empty = framed.is_write_buf_empty(); + if is_empty || (poll_write(cx, state, framed, rx, inner) || is_empty) { + Poll::Ready(Err(err)) + } else { + *state = TransportState::Error(err); + Poll::Pending } - TransportState::FlushAndStop => { - if !self.framed.is_write_buf_empty() { - match self.framed.poll_complete() { - Err(err) => { - debug!("Error sending data: {:?}", err); - Ok(Async::Ready(())) - } - Ok(Async::NotReady) => Ok(Async::NotReady), - Ok(Async::Ready(_)) => Ok(Async::Ready(())), + } + TransportState::FlushAndStop => { + if !framed.is_write_buf_empty() { + match Pin::new(framed).poll_flush(cx) { + Poll::Ready(Err(err)) => { + debug!("Error sending data: {:?}", err); + Poll::Ready(Ok(())) } - } else { - Ok(Async::Ready(())) + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(_)) => Poll::Ready(Ok(())), } + } else { + Poll::Ready(Ok(())) + } + } + TransportState::FramedError(err) => Poll::Ready(Err(err)), + TransportState::Stopping => Poll::Ready(Ok(())), + } +} + +fn poll_read( + cx: &mut Context, + srv: &mut S, + state: &mut TransportState, + framed: &mut Framed, + inner: &mut Inner, +) -> bool +where + S: Service, Response = Response>, + S::Error: 'static, + S::Future: 'static, + T: AsyncRead + AsyncWrite + Unpin, + U: Decoder + Encoder + Unpin, + ::Item: 'static, + ::Error: std::fmt::Debug, +{ + loop { + match srv.poll_ready(cx) { + Poll::Ready(Ok(_)) => { + let item = match framed.next_item(cx) { + Poll::Ready(Some(Ok(el))) => el, + Poll::Ready(Some(Err(err))) => { + *state = + TransportState::FramedError(FramedTransportError::Decoder(err)); + return true; + } + Poll::Pending => return false, + Poll::Ready(None) => { + *state = TransportState::Stopping; + return true; + } + }; + + let mut cell = inner.clone(); + let fut = srv.call(item).then(move |item| { + let inner = cell.get_mut(); + inner.buf.push_back(item); + inner.task.wake(); + ready(()) + }); + tokio_executor::current_thread::spawn(fut); + } + Poll::Pending => return false, + Poll::Ready(Err(err)) => { + *state = TransportState::Error(FramedTransportError::Service(err)); + return true; } - TransportState::FramedError(err) => Err(err), - TransportState::Stopping => Ok(Async::Ready(())), } } } + +/// write to framed object +fn poll_write( + cx: &mut Context, + state: &mut TransportState, + framed: &mut Framed, + rx: &mut Rx, + inner: &mut Inner, +) -> bool +where + S: Service, Response = Response>, + S::Error: 'static, + S::Future: 'static, + T: AsyncRead + AsyncWrite + Unpin, + U: Decoder + Encoder + Unpin, + ::Item: 'static, + ::Error: std::fmt::Debug, +{ + // let this = self.project(); + + let inner = inner.get_mut(); + let mut rx_done = rx.is_none(); + let mut buf_empty = inner.buf.is_empty(); + loop { + while !framed.is_write_buf_full() { + if !buf_empty { + match inner.buf.pop_front().unwrap() { + Ok(msg) => { + if let Err(err) = framed.force_send(msg) { + *state = + TransportState::FramedError(FramedTransportError::Encoder(err)); + return true; + } + buf_empty = inner.buf.is_empty(); + } + Err(err) => { + *state = TransportState::Error(FramedTransportError::Service(err)); + return true; + } + } + } + + if !rx_done && rx.is_some() { + match Pin::new(rx.as_mut().unwrap()).poll_next(cx) { + Poll::Ready(Some(FramedMessage::Message(msg))) => { + if let Err(err) = framed.force_send(msg) { + *state = + TransportState::FramedError(FramedTransportError::Encoder(err)); + return true; + } + } + Poll::Ready(Some(FramedMessage::Close)) => { + *state = TransportState::FlushAndStop; + return true; + } + Poll::Ready(None) => { + rx_done = true; + let _ = rx.take(); + } + Poll::Pending => rx_done = true, + } + } + + if rx_done && buf_empty { + break; + } + } + + if !framed.is_write_buf_empty() { + match framed.flush(cx) { + Poll::Pending => break, + Poll::Ready(Err(err)) => { + debug!("Error sending data: {:?}", err); + *state = TransportState::FramedError(FramedTransportError::Encoder(err)); + return true; + } + Poll::Ready(Ok(_)) => (), + } + } else { + break; + } + } + + false +} diff --git a/actix-utils/src/inflight.rs b/actix-utils/src/inflight.rs index 03d90873..35cd904d 100644 --- a/actix-utils/src/inflight.rs +++ b/actix-utils/src/inflight.rs @@ -1,8 +1,11 @@ use std::convert::Infallible; +use std::future::Future; +use std::pin::Pin; +use std::task::{Context, Poll}; use actix_service::{IntoService, Service, Transform}; -use futures::future::{ok, FutureResult}; -use futures::{Async, Future, Poll}; +use futures::future::{ok, Ready}; +use pin_project::pin_project; use super::counter::{Counter, CounterGuard}; @@ -32,7 +35,7 @@ impl Transform for InFlight { type Error = S::Error; type InitError = Infallible; type Transform = InFlightService; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(InFlightService::new(self.max_inflight, service)) @@ -68,14 +71,14 @@ where type Error = T::Error; type Future = InFlightServiceResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - if let Async::NotReady = self.service.poll_ready()? { - Ok(Async::NotReady) - } else if !self.count.available() { + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + if let Poll::Pending = self.service.poll_ready(cx)? { + Poll::Pending + } else if !self.count.available(cx) { log::trace!("InFlight limit exceeded"); - Ok(Async::NotReady) + Poll::Pending } else { - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } } @@ -87,31 +90,31 @@ where } } +#[pin_project] #[doc(hidden)] pub struct InFlightServiceResponse { + #[pin] fut: T::Future, _guard: CounterGuard, } impl Future for InFlightServiceResponse { - type Item = T::Response; - type Error = T::Error; + type Output = Result; - fn poll(&mut self) -> Poll { - self.fut.poll() + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + self.project().fut.poll(cx) } } #[cfg(test)] mod tests { - use futures::future::lazy; - use futures::{Async, Poll}; + use std::task::{Context, Poll}; use std::time::Duration; use super::*; - use actix_service::blank::{Blank, BlankNewService}; - use actix_service::{NewService, Service, ServiceExt}; + use actix_service::{apply, factory_fn, Service, ServiceFactory}; + use futures::future::{lazy, ok, FutureExt, LocalBoxFuture}; struct SleepService(Duration); @@ -119,57 +122,49 @@ mod tests { type Request = (); type Response = (); type Error = (); - type Future = Box>; + type Future = LocalBoxFuture<'static, Result<(), ()>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, _: ()) -> Self::Future { - Box::new(tokio_timer::sleep(self.0).map_err(|_| ())) + tokio_timer::delay_for(self.0) + .then(|_| ok::<_, ()>(())) + .boxed_local() } } #[test] fn test_transform() { let wait_time = Duration::from_millis(50); - let _ = actix_rt::System::new("test").block_on(lazy(|| { - let mut srv = - Blank::new().and_then(InFlightService::new(1, SleepService(wait_time))); - assert_eq!(srv.poll_ready(), Ok(Async::Ready(()))); + let _ = actix_rt::System::new("test").block_on(async { + let mut srv = InFlightService::new(1, SleepService(wait_time)); + assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); - let mut res = srv.call(()); - let _ = res.poll(); - assert_eq!(srv.poll_ready(), Ok(Async::NotReady)); + let res = srv.call(()); + assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending); - drop(res); - assert_eq!(srv.poll_ready(), Ok(Async::Ready(()))); - - Ok::<_, ()>(()) - })); + let _ = res.await; + assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); + }); } #[test] fn test_newtransform() { let wait_time = Duration::from_millis(50); - let _ = actix_rt::System::new("test").block_on(lazy(|| { - let srv = - BlankNewService::new().apply(InFlight::new(1), || Ok(SleepService(wait_time))); - if let Async::Ready(mut srv) = srv.new_service(&()).poll().unwrap() { - assert_eq!(srv.poll_ready(), Ok(Async::Ready(()))); + actix_rt::System::new("test").block_on(async { + let srv = apply(InFlight::new(1), factory_fn(|| ok(SleepService(wait_time)))); - let mut res = srv.call(()); - let _ = res.poll(); - assert_eq!(srv.poll_ready(), Ok(Async::NotReady)); + let mut srv = srv.new_service(&()).await.unwrap(); + assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); - drop(res); - assert_eq!(srv.poll_ready(), Ok(Async::Ready(()))); - } else { - panic!() - } + let res = srv.call(()); + assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending); - Ok::<_, ()>(()) - })); + let _ = res.await; + assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); + }); } } diff --git a/actix-utils/src/keepalive.rs b/actix-utils/src/keepalive.rs index 05436d7d..6f24dbf6 100644 --- a/actix-utils/src/keepalive.rs +++ b/actix-utils/src/keepalive.rs @@ -1,11 +1,13 @@ use std::convert::Infallible; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::time::{Duration, Instant}; -use actix_service::{NewService, Service}; -use futures::future::{ok, FutureResult}; -use futures::{Async, Future, Poll}; -use tokio_timer::Delay; +use actix_service::{Service, ServiceFactory}; +use futures::future::{ok, Ready}; +use tokio_timer::{delay, Delay}; use super::time::{LowResTime, LowResTimeService}; @@ -44,7 +46,7 @@ where } } -impl NewService for KeepAlive +impl ServiceFactory for KeepAlive where F: Fn() -> E + Clone, { @@ -54,7 +56,7 @@ where type InitError = Infallible; type Config = (); type Service = KeepAliveService; - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &()) -> Self::Future { ok(KeepAliveService::new( @@ -85,7 +87,7 @@ where ka, time, expire, - delay: Delay::new(expire), + delay: delay(expire), _t: PhantomData, } } @@ -98,22 +100,21 @@ where type Request = R; type Response = R; type Error = E; - type Future = FutureResult; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - match self.delay.poll() { - Ok(Async::Ready(_)) => { + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + match Pin::new(&mut self.delay).poll(cx) { + Poll::Ready(_) => { let now = self.time.now(); if self.expire <= now { - Err((self.f)()) + Poll::Ready(Err((self.f)())) } else { self.delay.reset(self.expire); - let _ = self.delay.poll(); - Ok(Async::Ready(())) + let _ = Pin::new(&mut self.delay).poll(cx); + Poll::Ready(Ok(())) } } - Ok(Async::NotReady) => Ok(Async::Ready(())), - Err(_e) => panic!(), + Poll::Pending => Poll::Ready(Ok(())), } } diff --git a/actix-utils/src/lib.rs b/actix-utils/src/lib.rs index 9d1a6b48..727362bf 100644 --- a/actix-utils/src/lib.rs +++ b/actix-utils/src/lib.rs @@ -6,7 +6,9 @@ pub mod either; pub mod framed; pub mod inflight; pub mod keepalive; +pub mod mpsc; +pub mod oneshot; pub mod order; -pub mod stream; +pub mod task; pub mod time; pub mod timeout; diff --git a/actix-utils/src/mpsc.rs b/actix-utils/src/mpsc.rs new file mode 100644 index 00000000..fe88ebc8 --- /dev/null +++ b/actix-utils/src/mpsc.rs @@ -0,0 +1,203 @@ +//! A multi-producer, single-consumer, futures-aware, FIFO queue with back +//! pressure, for use communicating between tasks on the same thread. +//! +//! These queues are the same as those in `futures::sync`, except they're not +//! intended to be sent across threads. + +use std::any::Any; +use std::cell::RefCell; +use std::collections::VecDeque; +use std::error::Error; +use std::pin::Pin; +use std::rc::{Rc, Weak}; +use std::task::{Context, Poll}; +use std::{fmt, mem}; + +use futures::{Sink, Stream}; + +use crate::task::LocalWaker; + +/// Creates a unbounded in-memory channel with buffered storage. +pub fn channel() -> (Sender, Receiver) { + let shared = Rc::new(RefCell::new(Shared { + buffer: VecDeque::new(), + blocked_recv: LocalWaker::new(), + })); + let sender = Sender { + shared: Rc::downgrade(&shared), + }; + let receiver = Receiver { + state: State::Open(shared), + }; + (sender, receiver) +} + +#[derive(Debug)] +struct Shared { + buffer: VecDeque, + blocked_recv: LocalWaker, +} + +/// The transmission end of a channel. +/// +/// This is created by the `channel` function. +#[derive(Debug)] +pub struct Sender { + shared: Weak>>, +} + +impl Sender { + /// Sends the provided message along this channel. + pub fn send(&self, item: T) -> Result<(), SendError> { + let shared = match self.shared.upgrade() { + Some(shared) => shared, + None => return Err(SendError(item)), // receiver was dropped + }; + let mut shared = shared.borrow_mut(); + + shared.buffer.push_back(item); + shared.blocked_recv.wake(); + Ok(()) + } +} + +impl Clone for Sender { + fn clone(&self) -> Self { + Sender { + shared: self.shared.clone(), + } + } +} + +impl Sink for Sender { + type Error = SendError; + + fn poll_ready(self: Pin<&mut Self>, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } + + fn start_send(self: Pin<&mut Self>, item: T) -> Result<(), SendError> { + self.send(item) + } + + fn poll_flush(self: Pin<&mut Self>, _: &mut Context) -> Poll>> { + Poll::Ready(Ok(())) + } + + fn poll_close(self: Pin<&mut Self>, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) + } +} + +impl Drop for Sender { + fn drop(&mut self) { + let shared = match self.shared.upgrade() { + Some(shared) => shared, + None => return, + }; + // The number of existing `Weak` indicates if we are possibly the last + // `Sender`. If we are the last, we possibly must notify a blocked + // `Receiver`. `self.shared` is always one of the `Weak` to this shared + // data. Therefore the smallest possible Rc::weak_count(&shared) is 1. + if Rc::weak_count(&shared) == 1 { + // Wake up receiver as its stream has ended + shared.borrow_mut().blocked_recv.wake(); + } + } +} + +/// The receiving end of a channel which implements the `Stream` trait. +/// +/// This is created by the `channel` function. +#[derive(Debug)] +pub struct Receiver { + state: State, +} + +impl Unpin for Receiver {} + +/// Possible states of a receiver. We're either Open (can receive more messages) +/// or we're closed with a list of messages we have left to receive. +#[derive(Debug)] +enum State { + Open(Rc>>), + Closed(VecDeque), +} + +impl Receiver { + /// Closes the receiving half + /// + /// This prevents any further messages from being sent on the channel while + /// still enabling the receiver to drain messages that are buffered. + pub fn close(&mut self) { + let items = match self.state { + State::Open(ref state) => { + let mut state = state.borrow_mut(); + let items = mem::replace(&mut state.buffer, VecDeque::new()); + items + } + State::Closed(_) => return, + }; + self.state = State::Closed(items); + } +} + +impl Stream for Receiver { + type Item = T; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let me = match self.state { + State::Open(ref mut me) => me, + State::Closed(ref mut items) => return Poll::Ready(items.pop_front()), + }; + + if let Some(shared) = Rc::get_mut(me) { + // All senders have been dropped, so drain the buffer and end the + // stream. + return Poll::Ready(shared.borrow_mut().buffer.pop_front()); + } + + let mut shared = me.borrow_mut(); + if let Some(msg) = shared.buffer.pop_front() { + Poll::Ready(Some(msg)) + } else { + shared.blocked_recv.register(cx.waker()); + Poll::Pending + } + } +} + +impl Drop for Receiver { + fn drop(&mut self) { + self.close(); + } +} + +/// Error type for sending, used when the receiving end of a channel is +/// dropped +pub struct SendError(T); + +impl fmt::Debug for SendError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_tuple("SendError").field(&"...").finish() + } +} + +impl fmt::Display for SendError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "send failed because receiver is gone") + } +} + +impl Error for SendError { + fn description(&self) -> &str { + "send failed because receiver is gone" + } +} + +impl SendError { + /// Returns the message that was attempted to be sent but failed. + pub fn into_inner(self) -> T { + self.0 + } +} diff --git a/actix-utils/src/oneshot.rs b/actix-utils/src/oneshot.rs new file mode 100644 index 00000000..08e4841b --- /dev/null +++ b/actix-utils/src/oneshot.rs @@ -0,0 +1,209 @@ +//! A one-shot, futures-aware channel +//! +//! This channel is similar to that in `sync::oneshot` but cannot be sent across +//! threads. + +use std::cell::RefCell; +use std::future::Future; +use std::pin::Pin; +use std::rc::{Rc, Weak}; +use std::task::{Context, Poll}; + +pub use futures::channel::oneshot::Canceled; + +use crate::task::LocalWaker; + +/// Creates a new futures-aware, one-shot channel. +/// +/// This function is the same as `sync::oneshot::channel` except that the +/// returned values cannot be sent across threads. +pub fn channel() -> (Sender, Receiver) { + let inner = Rc::new(RefCell::new(Inner { + value: None, + tx_task: LocalWaker::new(), + rx_task: LocalWaker::new(), + })); + let tx = Sender { + inner: Rc::downgrade(&inner), + }; + let rx = Receiver { + state: State::Open(inner), + }; + (tx, rx) +} + +/// Represents the completion half of a oneshot through which the result of a +/// computation is signaled. +/// +/// This is created by the `unsync::oneshot::channel` function and is equivalent +/// in functionality to `sync::oneshot::Sender` except that it cannot be sent +/// across threads. +#[derive(Debug)] +pub struct Sender { + inner: Weak>>, +} + +/// A future representing the completion of a computation happening elsewhere in +/// memory. +/// +/// This is created by the `unsync::oneshot::channel` function and is equivalent +/// in functionality to `sync::oneshot::Receiver` except that it cannot be sent +/// across threads. +#[derive(Debug)] +#[must_use = "futures do nothing unless polled"] +pub struct Receiver { + state: State, +} + +// The channels do not ever project Pin to the inner T +impl Unpin for Receiver {} +impl Unpin for Sender {} + +#[derive(Debug)] +enum State { + Open(Rc>>), + Closed(Option), +} + +#[derive(Debug)] +struct Inner { + value: Option, + tx_task: LocalWaker, + rx_task: LocalWaker, +} + +impl Sender { + /// Completes this oneshot with a successful result. + /// + /// This function will consume `self` and indicate to the other end, the + /// `Receiver`, that the error provided is the result of the computation this + /// represents. + /// + /// If the value is successfully enqueued for the remote end to receive, + /// then `Ok(())` is returned. If the receiving end was deallocated before + /// this function was called, however, then `Err` is returned with the value + /// provided. + pub fn send(self, val: T) -> Result<(), T> { + if let Some(inner) = self.inner.upgrade() { + inner.borrow_mut().value = Some(val); + Ok(()) + } else { + Err(val) + } + } + + /// Polls this `Sender` half to detect whether the `Receiver` this has + /// paired with has gone away. + /// + /// This function can be used to learn about when the `Receiver` (consumer) + /// half has gone away and nothing will be able to receive a message sent + /// from `complete`. + /// + /// Like `Future::poll`, this function will panic if it's not called from + /// within the context of a task. In other words, this should only ever be + /// called from inside another future. + /// + /// If `Ready` is returned then it means that the `Receiver` has disappeared + /// and the result this `Sender` would otherwise produce should no longer + /// be produced. + /// + /// If `NotReady` is returned then the `Receiver` is still alive and may be + /// able to receive a message if sent. The current task, however, is + /// scheduled to receive a notification if the corresponding `Receiver` goes + /// away. + pub fn poll_canceled(&mut self, cx: &mut Context) -> Poll<()> { + match self.inner.upgrade() { + Some(inner) => { + inner.borrow_mut().tx_task.register(cx.waker()); + Poll::Pending + } + None => Poll::Ready(()), + } + } + + /// Tests to see whether this `Sender`'s corresponding `Receiver` + /// has gone away. + /// + /// This function can be used to learn about when the `Receiver` (consumer) + /// half has gone away and nothing will be able to receive a message sent + /// from `send`. + /// + /// Note that this function is intended to *not* be used in the context of a + /// future. If you're implementing a future you probably want to call the + /// `poll_cancel` function which will block the current task if the + /// cancellation hasn't happened yet. This can be useful when working on a + /// non-futures related thread, though, which would otherwise panic if + /// `poll_cancel` were called. + pub fn is_canceled(&self) -> bool { + !self.inner.upgrade().is_some() + } +} + +impl Drop for Sender { + fn drop(&mut self) { + let inner = match self.inner.upgrade() { + Some(inner) => inner, + None => return, + }; + inner.borrow().rx_task.wake(); + } +} + +impl Receiver { + /// Gracefully close this receiver, preventing sending any future messages. + /// + /// Any `send` operation which happens after this method returns is + /// guaranteed to fail. Once this method is called the normal `poll` method + /// can be used to determine whether a message was actually sent or not. If + /// `Canceled` is returned from `poll` then no message was sent. + pub fn close(&mut self) { + match self.state { + State::Open(ref inner) => { + let mut inner = inner.borrow_mut(); + inner.tx_task.wake(); + let value = inner.value.take(); + drop(inner); + + self.state = State::Closed(value); + } + State::Closed(_) => return, + }; + } +} + +impl Future for Receiver { + type Output = Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.get_mut(); + + let inner = match this.state { + State::Open(ref mut inner) => inner, + State::Closed(ref mut item) => match item.take() { + Some(item) => return Poll::Ready(Ok(item.into())), + None => return Poll::Ready(Err(Canceled)), + }, + }; + + // If we've got a value, then skip the logic below as we're done. + if let Some(val) = inner.borrow_mut().value.take() { + return Poll::Ready(Ok(val)); + } + + // If we can get mutable access, then the sender has gone away. We + // didn't see a value above, so we're canceled. Otherwise we park + // our task and wait for a value to come in. + if Rc::get_mut(inner).is_some() { + Poll::Ready(Err(Canceled)) + } else { + inner.borrow().rx_task.register(cx.waker()); + Poll::Pending + } + } +} + +impl Drop for Receiver { + fn drop(&mut self) { + self.close(); + } +} diff --git a/actix-utils/src/order.rs b/actix-utils/src/order.rs index fa7bb750..61e00fd9 100644 --- a/actix-utils/src/order.rs +++ b/actix-utils/src/order.rs @@ -1,14 +1,17 @@ use std::collections::VecDeque; use std::convert::Infallible; use std::fmt; +use std::future::Future; use std::marker::PhantomData; +use std::pin::Pin; use std::rc::Rc; +use std::task::{Context, Poll}; use actix_service::{IntoService, Service, Transform}; -use futures::future::{ok, FutureResult}; -use futures::task::AtomicTask; -use futures::unsync::oneshot; -use futures::{Async, Future, Poll}; +use futures::future::{ok, ready, FutureExt, Ready}; + +use crate::oneshot; +use crate::task::LocalWaker; struct Record { rx: oneshot::Receiver>, @@ -93,7 +96,7 @@ where type Error = InOrderError; type InitError = Infallible; type Transform = InOrderService; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(InOrderService::new(service)) @@ -102,7 +105,7 @@ where pub struct InOrderService { service: S, - task: Rc, + task: Rc, acks: VecDeque>, } @@ -120,7 +123,7 @@ where Self { service: service.into_service(), acks: VecDeque::new(), - task: Rc::new(AtomicTask::new()), + task: Rc::new(LocalWaker::new()), } } } @@ -137,28 +140,30 @@ where type Error = InOrderError; type Future = InOrderServiceResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { // poll_ready could be called from different task - self.task.register(); + self.task.register(cx.waker()); // check acks while !self.acks.is_empty() { let rec = self.acks.front_mut().unwrap(); - match rec.rx.poll() { - Ok(Async::Ready(res)) => { + match Pin::new(&mut rec.rx).poll(cx) { + Poll::Ready(Ok(res)) => { let rec = self.acks.pop_front().unwrap(); let _ = rec.tx.send(res); } - Ok(Async::NotReady) => break, - Err(oneshot::Canceled) => return Err(InOrderError::Disconnected), + Poll::Pending => break, + Poll::Ready(Err(oneshot::Canceled)) => { + return Poll::Ready(Err(InOrderError::Disconnected)) + } } } // check nested service - if let Async::NotReady = self.service.poll_ready().map_err(InOrderError::Service)? { - Ok(Async::NotReady) + if let Poll::Pending = self.service.poll_ready(cx).map_err(InOrderError::Service)? { + Poll::Pending } else { - Ok(Async::Ready(())) + Poll::Ready(Ok(())) } } @@ -168,10 +173,10 @@ where self.acks.push_back(Record { rx: rx1, tx: tx2 }); let task = self.task.clone(); - tokio_current_thread::spawn(self.service.call(request).then(move |res| { - task.notify(); + tokio_executor::current_thread::spawn(self.service.call(request).then(move |res| { + task.wake(); let _ = tx1.send(res); - Ok(()) + ready(()) })); InOrderServiceResponse { rx: rx2 } @@ -184,29 +189,28 @@ pub struct InOrderServiceResponse { } impl Future for InOrderServiceResponse { - type Item = S::Response; - type Error = InOrderError; + type Output = Result>; - fn poll(&mut self) -> Poll { - match self.rx.poll() { - Ok(Async::NotReady) => Ok(Async::NotReady), - Ok(Async::Ready(Ok(res))) => Ok(Async::Ready(res)), - Ok(Async::Ready(Err(e))) => Err(e.into()), - Err(oneshot::Canceled) => Err(InOrderError::Disconnected), + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + match Pin::new(&mut self.rx).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(Ok(res))) => Poll::Ready(Ok(res)), + Poll::Ready(Ok(Err(e))) => Poll::Ready(Err(e.into())), + Poll::Ready(Err(_)) => Poll::Ready(Err(InOrderError::Disconnected)), } } } #[cfg(test)] mod tests { - use futures::future::{lazy, Future}; - use futures::{stream::futures_unordered, sync::oneshot, Async, Poll, Stream}; + use std::task::{Context, Poll}; use std::time::Duration; use super::*; - use actix_service::blank::Blank; - use actix_service::{Service, ServiceExt}; + use actix_service::Service; + use futures::channel::oneshot; + use futures::future::{lazy, LocalBoxFuture}; struct Srv; @@ -214,28 +218,14 @@ mod tests { type Request = oneshot::Receiver; type Response = usize; type Error = (); - type Future = Box>; + type Future = LocalBoxFuture<'static, Result>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, req: oneshot::Receiver) -> Self::Future { - Box::new(req.map_err(|_| ())) - } - } - - struct SrvPoll { - s: S, - } - - impl Future for SrvPoll { - type Item = (); - type Error = (); - - fn poll(&mut self) -> Poll<(), ()> { - let _ = self.s.poll_ready(); - Ok(Async::NotReady) + req.map(|res| res.map_err(|_| ())).boxed_local() } } @@ -251,23 +241,26 @@ mod tests { let rx2 = rx2; let rx3 = rx3; let tx_stop = tx_stop; - let _ = actix_rt::System::new("test").block_on(lazy(move || { - let mut srv = Blank::new().and_then(InOrderService::new(Srv)); + let _ = actix_rt::System::new("test").block_on(async { + let mut srv = InOrderService::new(Srv); let res1 = srv.call(rx1); let res2 = srv.call(rx2); let res3 = srv.call(rx3); - tokio_current_thread::spawn(SrvPoll { s: srv }); - futures_unordered(vec![res1, res2, res3]) - .collect() - .and_then(move |res: Vec<_>| { - assert_eq!(res, vec![1, 2, 3]); - let _ = tx_stop.send(()); - actix_rt::System::current().stop(); - Ok(()) - }) - })); + let _ = lazy(|cx| srv.poll_ready(cx)).await; + + // dispatcher do this + tokio_timer::delay_for(Duration::from_millis(100)).await; + let _ = lazy(|cx| srv.poll_ready(cx)).await; + + assert_eq!(res1.await.unwrap(), 1); + assert_eq!(res2.await.unwrap(), 2); + assert_eq!(res3.await.unwrap(), 3); + + let _ = tx_stop.send(()); + actix_rt::System::current().stop(); + }); }); let _ = tx3.send(3); @@ -275,7 +268,7 @@ mod tests { let _ = tx2.send(2); let _ = tx1.send(1); - let _ = rx_stop.wait(); + let _ = actix_rt::System::new("test").block_on(rx_stop); let _ = h.join(); } } diff --git a/actix-utils/src/stream.rs b/actix-utils/src/stream.rs deleted file mode 100644 index e8ac8783..00000000 --- a/actix-utils/src/stream.rs +++ /dev/null @@ -1,151 +0,0 @@ -use std::marker::PhantomData; -use std::rc::Rc; - -use actix_service::{IntoService, NewService, Service}; -use futures::unsync::mpsc; -use futures::{Async, Future, Poll, Stream}; - -type Request = Result<::Item, ::Error>; - -pub trait IntoStream { - type Item; - type Error; - type Stream: Stream; - - fn into_stream(self) -> Self::Stream; -} - -impl IntoStream for T -where - T: Stream, -{ - type Item = T::Item; - type Error = T::Error; - type Stream = T; - - fn into_stream(self) -> Self::Stream { - self - } -} - -pub struct StreamService { - factory: Rc, - config: T::Config, - _t: PhantomData<(S, E)>, -} - -impl Service for StreamService -where - S: IntoStream + 'static, - T: NewService, Response = (), Error = E, InitError = E>, - T::Future: 'static, - T::Service: 'static, - ::Future: 'static, -{ - type Request = S; - type Response = (); - type Error = E; - type Future = Box>; - - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) - } - - fn call(&mut self, req: S) -> Self::Future { - Box::new( - self.factory - .new_service(&self.config) - .and_then(move |srv| StreamDispatcher::new(req, srv)), - ) - } -} - -pub struct StreamDispatcher -where - S: IntoStream + 'static, - T: Service, Response = ()> + 'static, - T::Future: 'static, -{ - stream: S, - service: T, - err_rx: mpsc::UnboundedReceiver, - err_tx: mpsc::UnboundedSender, -} - -impl StreamDispatcher -where - S: Stream, - T: Service, Response = ()>, - T::Future: 'static, -{ - pub fn new(stream: F1, service: F2) -> Self - where - F1: IntoStream, - F2: IntoService, - { - let (err_tx, err_rx) = mpsc::unbounded(); - StreamDispatcher { - err_rx, - err_tx, - stream: stream.into_stream(), - service: service.into_service(), - } - } -} - -impl Future for StreamDispatcher -where - S: Stream, - T: Service, Response = ()>, - T::Future: 'static, -{ - type Item = (); - type Error = T::Error; - - fn poll(&mut self) -> Poll { - if let Ok(Async::Ready(Some(e))) = self.err_rx.poll() { - return Err(e); - } - - loop { - match self.service.poll_ready()? { - Async::Ready(_) => match self.stream.poll() { - Ok(Async::Ready(Some(item))) => { - tokio_current_thread::spawn(StreamDispatcherService { - fut: self.service.call(Ok(item)), - stop: self.err_tx.clone(), - }) - } - Err(err) => tokio_current_thread::spawn(StreamDispatcherService { - fut: self.service.call(Err(err)), - stop: self.err_tx.clone(), - }), - Ok(Async::NotReady) => return Ok(Async::NotReady), - Ok(Async::Ready(None)) => return Ok(Async::Ready(())), - }, - Async::NotReady => return Ok(Async::NotReady), - } - } - } -} - -struct StreamDispatcherService { - fut: F, - stop: mpsc::UnboundedSender, -} - -impl Future for StreamDispatcherService { - type Item = (); - type Error = (); - - fn poll(&mut self) -> Poll { - match self.fut.poll() { - Ok(Async::Ready(_)) => Ok(Async::Ready(())), - Ok(Async::NotReady) => Ok(Async::NotReady), - Err(e) => { - let _ = self.stop.unbounded_send(e); - Ok(Async::Ready(())) - } - } - } -} diff --git a/actix-utils/src/task.rs b/actix-utils/src/task.rs new file mode 100644 index 00000000..1370a362 --- /dev/null +++ b/actix-utils/src/task.rs @@ -0,0 +1,69 @@ +use std::cell::UnsafeCell; +use std::marker::PhantomData; +use std::task::Waker; +use std::{fmt, rc}; + +/// A synchronization primitive for task wakeup. +/// +/// Sometimes the task interested in a given event will change over time. +/// An `LocalWaker` can coordinate concurrent notifications with the consumer +/// potentially "updating" the underlying task to wake up. This is useful in +/// scenarios where a computation completes in another task and wants to +/// notify the consumer, but the consumer is in the process of being migrated to +/// a new logical task. +/// +/// Consumers should call `register` before checking the result of a computation +/// and producers should call `wake` after producing the computation (this +/// differs from the usual `thread::park` pattern). It is also permitted for +/// `wake` to be called **before** `register`. This results in a no-op. +/// +/// A single `AtomicWaker` may be reused for any number of calls to `register` or +/// `wake`. +pub struct LocalWaker { + waker: UnsafeCell>, + _t: PhantomData>, +} + +impl LocalWaker { + /// Create an `LocalWaker`. + pub fn new() -> Self { + LocalWaker { + waker: UnsafeCell::new(None), + _t: PhantomData, + } + } + + #[inline] + /// Registers the waker to be notified on calls to `wake`. + pub fn register(&self, waker: &Waker) { + unsafe { + let w = self.waker.get(); + if (*w).is_none() { + *w = Some(waker.clone()) + } + } + } + + #[inline] + /// Calls `wake` on the last `Waker` passed to `register`. + /// + /// If `register` has not been called yet, then this does nothing. + pub fn wake(&self) { + if let Some(waker) = self.take() { + waker.wake(); + } + } + + /// Returns the last `Waker` passed to `register`, so that the user can wake it. + /// + /// If a waker has not been registered, this returns `None`. + pub fn take(&self) -> Option { + unsafe { (*self.waker.get()).take() } + } +} + +impl fmt::Debug for LocalWaker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "LocalWaker") + } +} diff --git a/actix-utils/src/time.rs b/actix-utils/src/time.rs index 0138ec3e..2c50d9dc 100644 --- a/actix-utils/src/time.rs +++ b/actix-utils/src/time.rs @@ -1,10 +1,10 @@ use std::convert::Infallible; +use std::task::{Context, Poll}; use std::time::{self, Duration, Instant}; -use actix_service::{NewService, Service}; -use futures::future::{ok, FutureResult}; -use futures::{Async, Future, Poll}; -use tokio_timer::sleep; +use actix_service::{Service, ServiceFactory}; +use futures::future::{ok, ready, FutureExt, Ready}; +use tokio_timer::delay_for; use super::cell::Cell; @@ -42,14 +42,14 @@ impl Default for LowResTime { } } -impl NewService for LowResTime { +impl ServiceFactory for LowResTime { type Request = (); type Response = Instant; type Error = Infallible; type InitError = Infallible; type Config = (); type Service = LowResTimeService; - type Future = FutureResult; + type Future = Ready>; fn new_service(&self, _: &()) -> Self::Future { ok(self.timer()) @@ -79,12 +79,10 @@ impl LowResTimeService { b.resolution }; - tokio_current_thread::spawn(sleep(interval).map_err(|_| panic!()).and_then( - move |_| { - inner.get_mut().current.take(); - Ok(()) - }, - )); + tokio_executor::current_thread::spawn(delay_for(interval).then(move |_| { + inner.get_mut().current.take(); + ready(()) + })); now } } @@ -94,10 +92,10 @@ impl Service for LowResTimeService { type Request = (); type Response = Instant; type Error = Infallible; - type Future = FutureResult; + type Future = Ready>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, _: ()) -> Self::Future { @@ -146,12 +144,10 @@ impl SystemTimeService { b.resolution }; - tokio_current_thread::spawn(sleep(interval).map_err(|_| panic!()).and_then( - move |_| { - inner.get_mut().current.take(); - Ok(()) - }, - )); + tokio_executor::current_thread::spawn(delay_for(interval).then(move |_| { + inner.get_mut().current.take(); + ready(()) + })); now } } @@ -160,7 +156,6 @@ impl SystemTimeService { #[cfg(test)] mod tests { use super::*; - use futures::future; use std::time::{Duration, SystemTime}; /// State Under Test: Two calls of `SystemTimeService::now()` return the same value if they are done within resolution interval of `SystemTimeService`. @@ -170,13 +165,11 @@ mod tests { fn system_time_service_time_does_not_immediately_change() { let resolution = Duration::from_millis(50); - let _ = actix_rt::System::new("test").block_on(future::lazy(|| { + let _ = actix_rt::System::new("test").block_on(async { let time_service = SystemTimeService::with(resolution); assert_eq!(time_service.now(), time_service.now()); - - Ok::<(), ()>(()) - })); + }); } /// State Under Test: Two calls of `LowResTimeService::now()` return the same value if they are done within resolution interval of `SystemTimeService`. @@ -186,13 +179,11 @@ mod tests { fn lowres_time_service_time_does_not_immediately_change() { let resolution = Duration::from_millis(50); - let _ = actix_rt::System::new("test").block_on(future::lazy(|| { + let _ = actix_rt::System::new("test").block_on(async { let time_service = LowResTimeService::with(resolution); assert_eq!(time_service.now(), time_service.now()); - - Ok::<(), ()>(()) - })); + }); } /// State Under Test: `SystemTimeService::now()` updates returned value every resolution period. @@ -204,7 +195,7 @@ mod tests { let resolution = Duration::from_millis(100); let wait_time = Duration::from_millis(150); - let _ = actix_rt::System::new("test").block_on(future::lazy(|| { + actix_rt::System::new("test").block_on(async { let time_service = SystemTimeService::with(resolution); let first_time = time_service @@ -212,17 +203,15 @@ mod tests { .duration_since(SystemTime::UNIX_EPOCH) .unwrap(); - sleep(wait_time).then(move |_| { - let second_time = time_service - .now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap(); + delay_for(wait_time).await; - assert!(second_time - first_time >= wait_time); + let second_time = time_service + .now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap(); - Ok::<(), ()>(()) - }) - })); + assert!(second_time - first_time >= wait_time); + }); } /// State Under Test: `LowResTimeService::now()` updates returned value every resolution period. @@ -234,18 +223,15 @@ mod tests { let resolution = Duration::from_millis(100); let wait_time = Duration::from_millis(150); - let _ = actix_rt::System::new("test").block_on(future::lazy(|| { + let _ = actix_rt::System::new("test").block_on(async { let time_service = LowResTimeService::with(resolution); let first_time = time_service.now(); - sleep(wait_time).then(move |_| { - let second_time = time_service.now(); + delay_for(wait_time).await; - assert!(second_time - first_time >= wait_time); - - Ok::<(), ()>(()) - }) - })); + let second_time = time_service.now(); + assert!(second_time - first_time >= wait_time); + }); } } diff --git a/actix-utils/src/timeout.rs b/actix-utils/src/timeout.rs index e05788f7..2e8939cb 100644 --- a/actix-utils/src/timeout.rs +++ b/actix-utils/src/timeout.rs @@ -2,19 +2,21 @@ //! //! If the response does not complete within the specified timeout, the response //! will be aborted. -use std::fmt; +use std::future::Future; use std::marker::PhantomData; -use std::time::Duration; +use std::pin::Pin; +use std::task::{Context, Poll}; +use std::{fmt, time}; use actix_service::{IntoService, Service, Transform}; -use futures::future::{ok, FutureResult}; -use futures::{Async, Future, Poll}; -use tokio_timer::{clock, Delay}; +use futures::future::{ok, Ready}; +use pin_project::pin_project; +use tokio_timer::{clock, delay, Delay}; /// Applies a timeout to requests. #[derive(Debug)] pub struct Timeout { - timeout: Duration, + timeout: time::Duration, _t: PhantomData, } @@ -66,7 +68,7 @@ impl PartialEq for TimeoutError { } impl Timeout { - pub fn new(timeout: Duration) -> Self { + pub fn new(timeout: time::Duration) -> Self { Timeout { timeout, _t: PhantomData, @@ -89,7 +91,7 @@ where type Error = TimeoutError; type InitError = E; type Transform = TimeoutService; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(TimeoutService { @@ -103,14 +105,14 @@ where #[derive(Debug, Clone)] pub struct TimeoutService { service: S, - timeout: Duration, + timeout: time::Duration, } impl TimeoutService where S: Service, { - pub fn new(timeout: Duration, service: U) -> Self + pub fn new(timeout: time::Duration, service: U) -> Self where U: IntoService, { @@ -130,21 +132,23 @@ where type Error = TimeoutError; type Future = TimeoutServiceResponse; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready().map_err(TimeoutError::Service) + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(cx).map_err(TimeoutError::Service) } fn call(&mut self, request: S::Request) -> Self::Future { TimeoutServiceResponse { fut: self.service.call(request), - sleep: Delay::new(clock::now() + self.timeout), + sleep: delay(clock::now() + self.timeout), } } } /// `TimeoutService` response future +#[pin_project] #[derive(Debug)] pub struct TimeoutServiceResponse { + #[pin] fut: T::Future, sleep: Delay, } @@ -153,36 +157,34 @@ impl Future for TimeoutServiceResponse where T: Service, { - type Item = T::Response; - type Error = TimeoutError; + type Output = Result>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let mut this = self.project(); - fn poll(&mut self) -> Poll { // First, try polling the future - match self.fut.poll() { - Ok(Async::Ready(v)) => return Ok(Async::Ready(v)), - Ok(Async::NotReady) => {} - Err(e) => return Err(TimeoutError::Service(e)), + match this.fut.poll(cx) { + Poll::Ready(Ok(v)) => return Poll::Ready(Ok(v)), + Poll::Ready(Err(e)) => return Poll::Ready(Err(TimeoutError::Service(e))), + Poll::Pending => {} } // Now check the sleep - match self.sleep.poll() { - Ok(Async::NotReady) => Ok(Async::NotReady), - Ok(Async::Ready(_)) => Err(TimeoutError::Timeout), - Err(_) => Err(TimeoutError::Timeout), + match Pin::new(&mut this.sleep).poll(cx) { + Poll::Pending => Poll::Pending, + Poll::Ready(_) => Poll::Ready(Err(TimeoutError::Timeout)), } } } #[cfg(test)] mod tests { - use futures::future::lazy; - use futures::{Async, Poll}; - + use std::task::{Context, Poll}; use std::time::Duration; use super::*; - use actix_service::blank::{Blank, BlankNewService}; - use actix_service::{NewService, Service, ServiceExt}; + use actix_service::{apply, factory_fn, Service, ServiceFactory}; + use futures::future::{ok, FutureExt, LocalBoxFuture}; struct SleepService(Duration); @@ -190,14 +192,16 @@ mod tests { type Request = (); type Response = (); type Error = (); - type Future = Box>; + type Future = LocalBoxFuture<'static, Result<(), ()>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - Ok(Async::Ready(())) + fn poll_ready(&mut self, _: &mut Context) -> Poll> { + Poll::Ready(Ok(())) } fn call(&mut self, _: ()) -> Self::Future { - Box::new(tokio_timer::sleep(self.0).map_err(|_| ())) + tokio_timer::delay_for(self.0) + .then(|_| ok::<_, ()>(())) + .boxed_local() } } @@ -206,11 +210,10 @@ mod tests { let resolution = Duration::from_millis(100); let wait_time = Duration::from_millis(50); - let res = actix_rt::System::new("test").block_on(lazy(|| { - let mut timeout = Blank::default() - .and_then(TimeoutService::new(resolution, SleepService(wait_time))); - timeout.call(()) - })); + let res = actix_rt::System::new("test").block_on(async { + let mut timeout = TimeoutService::new(resolution, SleepService(wait_time)); + timeout.call(()).await + }); assert_eq!(res, Ok(())); } @@ -219,11 +222,10 @@ mod tests { let resolution = Duration::from_millis(100); let wait_time = Duration::from_millis(150); - let res = actix_rt::System::new("test").block_on(lazy(|| { - let mut timeout = Blank::default() - .and_then(TimeoutService::new(resolution, SleepService(wait_time))); - timeout.call(()) - })); + let res = actix_rt::System::new("test").block_on(async { + let mut timeout = TimeoutService::new(resolution, SleepService(wait_time)); + timeout.call(()).await + }); assert_eq!(res, Err(TimeoutError::Timeout)); } @@ -232,15 +234,15 @@ mod tests { let resolution = Duration::from_millis(100); let wait_time = Duration::from_millis(150); - let res = actix_rt::System::new("test").block_on(lazy(|| { - let timeout = BlankNewService::<(), (), ()>::default() - .apply(Timeout::new(resolution), || Ok(SleepService(wait_time))); - if let Async::Ready(mut to) = timeout.new_service(&()).poll().unwrap() { - to.call(()) - } else { - panic!() - } - })); + let res = actix_rt::System::new("test").block_on(async { + let timeout = apply( + Timeout::new(resolution), + factory_fn(|| ok::<_, ()>(SleepService(wait_time))), + ); + let mut srv = timeout.new_service(&()).await.unwrap(); + + srv.call(()).await + }); assert_eq!(res, Err(TimeoutError::Timeout)); } }