1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-08-12 06:27:09 +02:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Rob Ede
8666c4063f temp 2020-12-23 19:44:54 +00:00
115 changed files with 6013 additions and 3555 deletions

29
.github/workflows/bench.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Benchmark (Linux)
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- master
- '1.0'
jobs:
check_benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
profile: minimal
override: true
- name: Check benchmark
uses: actions-rs/cargo@v1
with:
command: bench
args: --package=actix-service

View File

@@ -14,7 +14,7 @@ jobs:
fail-fast: false
matrix:
version:
- 1.46.0
- 1.42.0
- stable
- nightly

View File

@@ -1,27 +1,31 @@
[workspace]
members = [
"actix-codec",
"actix-macros",
"actix-router",
"actix-connect",
"actix-rt",
"actix-server",
"actix-macros",
"actix-service",
"actix-server",
"actix-testing",
"actix-threadpool",
"actix-tls",
"actix-tracing",
"actix-utils",
"bytestring",
"router",
"string",
]
[patch.crates-io]
actix-codec = { path = "actix-codec" }
actix-macros = { path = "actix-macros" }
actix-router = { path = "actix-router" }
actix-connect = { path = "actix-connect" }
actix-rt = { path = "actix-rt" }
actix-macros = { path = "actix-macros" }
actix-server = { path = "actix-server" }
actix-service = { path = "actix-service" }
actix-testing = { path = "actix-testing" }
actix-threadpool = { path = "actix-threadpool" }
actix-tls = { path = "actix-tls" }
actix-tracing = { path = "actix-tracing" }
actix-utils = { path = "actix-utils" }
bytestring = { path = "bytestring" }
actix-router = { path = "router" }
bytestring = { path = "string" }

View File

@@ -1,12 +1,9 @@
# Actix Net
# Actix net [![codecov](https://codecov.io/gh/actix/actix-net/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-net) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
> A collection of lower-level libraries for composable network services.
![Apache 2.0 or MIT licensed](https://img.shields.io/crates/l/actix-server)
[![codecov](https://codecov.io/gh/actix/actix-net/branch/master/graph/badge.svg)](https://codecov.io/gh/actix/actix-net)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
Actix net - framework for composable network services
## Build statuses
| Platform | Build Status |
| ---------------- | ------------ |
| Linux | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Linux%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Linux)") |
@@ -14,13 +11,59 @@
| Windows | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows)") |
| Windows (MinGW) | [![build status](https://github.com/actix/actix-net/workflows/CI%20%28Windows-mingw%29/badge.svg?branch=master&event=push)](https://github.com/actix/actix-net/actions?query=workflow%3A"CI+(Windows-mingw)") |
## Example
See `actix-server/examples` and `actix-tls/examples` for some basic examples.
## Documentation & community resources
### MSRV
This repo's Minimum Supported Rust Version (MSRV) is 1.46.0.
* [Chat on Gitter](https://gitter.im/actix/actix)
* Minimum supported Rust version: 1.42 or later
## Example
```rust
fn main() -> io::Result<()> {
// load ssl keys
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder.set_private_key_file("./examples/key.pem", SslFiletype::PEM).unwrap();
builder.set_certificate_chain_file("./examples/cert.pem").unwrap();
let acceptor = builder.build();
let num = Arc::new(AtomicUsize::new(0));
// bind socket address and start workers. By default server uses number of
// available logical cpu as threads count. actix net start separate
// instances of service pipeline in each worker.
Server::build()
.bind(
// configure service pipeline
"basic", "0.0.0.0:8443",
move || {
let num = num.clone();
let acceptor = acceptor.clone();
// construct transformation pipeline
pipeline(
// service for converting incoming TcpStream to a SslStream<TcpStream>
fn_service(move |stream: actix_rt::net::TcpStream| async move {
SslAcceptorExt::accept_async(&acceptor, stream.into_parts().0).await
.map_err(|e| println!("Openssl error: {}", e))
}))
// .and_then() combinator chains result of previos service call to argument
/// for next service calll. in this case, on success we chain
/// ssl stream to the `logger` service.
.and_then(fn_service(logger))
// Next service counts number of connections
.and_then(move |_| {
let num = num.fetch_add(1, Ordering::Relaxed);
println!("got ssl connection {:?}", num);
future::ok(())
})
},
)?
.run()
}
```
## License
This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0))
@@ -30,5 +73,6 @@ at your option.
## Code of Conduct
Contribution to the actix-net repo is organized under the terms of the Contributor Covenant.
The Actix team promises to intervene to uphold that code of conduct.
Contribution to the actix-net crate is organized under the terms of the
Contributor Covenant, the maintainer of actix-net, @fafhrd91, promises to
intervene to uphold that code of conduct.

View File

@@ -1,25 +1,14 @@
# Changes
## Unreleased - 2021-xx-xx
## 0.4.0-beta.1 - 2020-12-28
* Replace `pin-project` with `pin-project-lite`. [#237]
* Upgrade `tokio` dependency to `1`. [#237]
* Upgrade `tokio-util` dependency to `0.6`. [#237]
* Upgrade `bytes` dependency to `1`. [#237]
[#237]: https://github.com/actix/actix-net/pull/237
## Unreleased - 2020-xx-xx
* Upgrade `pin-project` to `1.0`.
## 0.3.0 - 2020-08-23
* No changes from beta 2.
## 0.3.0-beta.2 - 2020-08-19
* Remove unused type parameter from `Framed::replace_codec`.
## 0.3.0-beta.1 - 2020-08-19
* Use `.advance()` instead of `.split_to()`.
* Upgrade `tokio-util` to `0.3`.
@@ -29,31 +18,32 @@
* Add method on `Framed` to get a pinned reference to the underlying I/O.
* Add method on `Framed` check emptiness of read buffer.
## [0.2.0] - 2019-12-10
## 0.2.0 - 2019-12-10
* Use specific futures dependencies
## [0.2.0-alpha.4]
## 0.2.0-alpha.4
* Fix buffer remaining capacity calculation
## [0.2.0-alpha.3]
## 0.2.0-alpha.3
* Use tokio 0.2
* Fix low/high watermark for write/read buffers
## [0.2.0-alpha.2]
## 0.2.0-alpha.2
* Migrated to `std::future`
## [0.1.2] - 2019-03-27
## 0.1.2 - 2019-03-27
* Added `Framed::map_io()` method.
## [0.1.1] - 2019-03-06
## 0.1.1 - 2019-03-06
* Added `FramedParts::with_read_buffer()` method.
## [0.1.0] - 2018-12-09
## 0.1.0 - 2018-12-09
* Move codec to separate crate

View File

@@ -1,8 +1,8 @@
[package]
name = "actix-codec"
version = "0.4.0-beta.1"
version = "0.3.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Codec utilities for working with framed protocols"
description = "Codec utilities for working with framed protocols."
keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
@@ -17,10 +17,10 @@ path = "src/lib.rs"
[dependencies]
bitflags = "1.2.1"
bytes = "1"
futures-core = { version = "0.3.7", default-features = false }
futures-sink = { version = "0.3.7", default-features = false }
bytes = "0.5.2"
futures-core = { version = "0.3.4", default-features = false }
futures-sink = { version = "0.3.4", default-features = false }
log = "0.4"
pin-project-lite = "0.2"
tokio = "1"
tokio-util = { version = "0.6", features = ["codec", "io"] }
pin-project = "1.0.0"
tokio = { version = "0.2.5", default-features = false }
tokio-util = { version = "0.3.1", default-features = false, features = ["codec"] }

View File

@@ -14,7 +14,7 @@ impl Encoder<Bytes> for BytesCodec {
#[inline]
fn encode(&mut self, item: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> {
dst.extend_from_slice(item.chunk());
dst.extend_from_slice(item.bytes());
Ok(())
}
}

View File

@@ -5,6 +5,7 @@ use std::{fmt, io};
use bytes::{Buf, BytesMut};
use futures_core::{ready, Stream};
use futures_sink::Sink;
use pin_project::pin_project;
use crate::{AsyncRead, AsyncWrite, Decoder, Encoder};
@@ -20,23 +21,22 @@ bitflags::bitflags! {
}
}
pin_project_lite::pin_project! {
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using
/// the `Encoder` and `Decoder` traits to encode and decode frames.
///
/// Raw I/O objects work with byte sequences, but higher-level code usually
/// wants to batch these into meaningful chunks, called "frames". This
/// method layers framing on top of an I/O object, by using the `Encoder`/`Decoder`
/// traits to handle encoding and decoding of message frames. Note that
/// the incoming and outgoing frame types may be distinct.
pub struct Framed<T, U> {
#[pin]
io: T,
codec: U,
flags: Flags,
read_buf: BytesMut,
write_buf: BytesMut,
}
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using
/// the `Encoder` and `Decoder` traits to encode and decode frames.
///
/// Raw I/O objects work with byte sequences, but higher-level code usually
/// wants to batch these into meaningful chunks, called "frames". This
/// method layers framing on top of an I/O object, by using the `Encoder`/`Decoder`
/// traits to handle encoding and decoding of message frames. Note that
/// the incoming and outgoing frame types may be distinct.
#[pin_project]
pub struct Framed<T, U> {
#[pin]
io: T,
codec: U,
flags: Flags,
read_buf: BytesMut,
write_buf: BytesMut,
}
impl<T, U> Framed<T, U>
@@ -220,8 +220,7 @@ impl<T, U> Framed<T, U> {
if remaining < LW {
this.read_buf.reserve(HW - remaining)
}
let cnt = match tokio_util::io::poll_read_buf(this.io, cx, this.read_buf) {
let cnt = match this.io.poll_read_buf(cx, &mut this.read_buf) {
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(e)) => return Poll::Ready(Some(Err(e.into()))),
Poll::Ready(Ok(cnt)) => cnt,

View File

@@ -1,4 +1,4 @@
//! Codec utilities for working with framed protocols.
//! Utilities for encoding and decoding frames.
//!
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
@@ -18,6 +18,5 @@ mod framed;
pub use self::bcodec::BytesCodec;
pub use self::framed::{Framed, FramedParts};
pub use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
pub use tokio::io::{AsyncRead, AsyncWrite};
pub use tokio_util::codec::{Decoder, Encoder};
pub use tokio_util::io::poll_read_buf;

154
actix-connect/CHANGES.md Normal file
View File

@@ -0,0 +1,154 @@
# Changes
## Unreleased - 2020-xx-xx
## 2.0.0 - 2020-09-02
- No significant changes from `2.0.0-alpha.4`.
## 2.0.0-alpha.4 - 2020-08-17
### Changed
* Update `rustls` dependency to 0.18
* Update `tokio-rustls` dependency to 0.14
## [2.0.0-alpha.3] - 2020-05-08
### Fixed
* Corrected spelling of `ConnectError::Unresolverd` to `ConnectError::Unresolved`
## [2.0.0-alpha.2] - 2020-03-08
### Changed
* Update `trust-dns-proto` dependency to 0.19. [#116]
* Update `trust-dns-resolver` dependency to 0.19. [#116]
* `Address` trait is now required to have static lifetime. [#116]
* `start_resolver` and `start_default_resolver` are now `async` and may return a `ConnectError`. [#116]
[#116]: https://github.com/actix/actix-net/pull/116
## [2.0.0-alpha.1] - 2020-03-03
### Changed
* Update `rustls` dependency to 0.17
* Update `tokio-rustls` dependency to 0.13
## [1.0.2] - 2020-01-15
* Fix actix-service 1.0.3 compatibility
## [1.0.1] - 2019-12-15
* Fix trust-dns-resolver compilation
## [1.0.0] - 2019-12-11
* Release
## [1.0.0-alpha.3] - 2019-12-07
### Changed
* Migrate to tokio 0.2
## [1.0.0-alpha.2] - 2019-12-02
### Changed
* Migrated to `std::future`
## [0.3.0] - 2019-10-03
### Changed
* Update `rustls` to 0.16
* Minimum required Rust version upped to 1.37.0
## [0.2.5] - 2019-09-05
* Add `TcpConnectService`
## [0.2.4] - 2019-09-02
* Use arbiter's storage for default async resolver
## [0.2.3] - 2019-08-05
* Add `ConnectService` and `OpensslConnectService`
## [0.2.2] - 2019-07-24
* Add `rustls` support
## [0.2.1] - 2019-07-17
### Added
* Expose Connect addrs #30
### Changed
* Update `derive_more` to 0.15
## [0.2.0] - 2019-05-12
### Changed
* Upgrade to actix-service 0.4
## [0.1.5] - 2019-04-19
### Added
* `Connect::set_addr()`
### Changed
* Use trust-dns-resolver 0.11.0
## [0.1.4] - 2019-04-12
### Changed
* Do not start default resolver immediately for default connector.
## [0.1.3] - 2019-04-11
### Changed
* Start trust-dns default resolver on first use
## [0.1.2] - 2019-04-04
### Added
* Log error if dns system config could not be loaded.
### Changed
* Rename connect Connector to TcpConnector #10
## [0.1.1] - 2019-03-15
### Fixed
* Fix error handling for single address
## [0.1.0] - 2019-03-14
* Refactor resolver and connector services
* Rename crate

58
actix-connect/Cargo.toml Normal file
View File

@@ -0,0 +1,58 @@
[package]
name = "actix-connect"
version = "2.0.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "TCP connector service for Actix ecosystem."
keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-connect/"
categories = ["network-programming", "asynchronous"]
license = "MIT OR Apache-2.0"
edition = "2018"
[package.metadata.docs.rs]
features = ["openssl", "rustls", "uri"]
[lib]
name = "actix_connect"
path = "src/lib.rs"
[features]
default = ["uri"]
# openssl
openssl = ["open-ssl", "tokio-openssl"]
# rustls
rustls = ["rust-tls", "tokio-rustls", "webpki"]
# support http::Uri as connect address
uri = ["http"]
[dependencies]
actix-service = "1.0.6"
actix-codec = "0.3.0"
actix-utils = "2.0.0"
actix-rt = "1.1.1"
derive_more = "0.99.2"
either = "1.5.3"
futures-util = { version = "0.3.4", default-features = false }
http = { version = "0.2.0", optional = true }
log = "0.4"
trust-dns-proto = { version = "0.19", default-features = false, features = ["tokio-runtime"] }
trust-dns-resolver = { version = "0.19", default-features = false, features = ["tokio-runtime", "system-config"] }
# openssl
open-ssl = { package = "openssl", version = "0.10", optional = true }
tokio-openssl = { version = "0.4.0", optional = true }
# rustls
rust-tls = { package = "rustls", version = "0.18.0", optional = true }
tokio-rustls = { version = "0.14.0", optional = true }
webpki = { version = "0.21", optional = true }
[dev-dependencies]
bytes = "0.5.3"
actix-testing = "1.0.0"

View File

@@ -8,8 +8,7 @@ use std::task::{Context, Poll};
use actix_rt::net::TcpStream;
use actix_service::{Service, ServiceFactory};
use futures_util::future::{ready, Ready};
use log::{error, trace};
use futures_util::future::{err, ok, BoxFuture, Either, FutureExt, Ready};
use super::connect::{Address, Connect, Connection};
use super::error::ConnectError;
@@ -41,7 +40,8 @@ impl<T> Clone for TcpConnectorFactory<T> {
}
}
impl<T: Address> ServiceFactory<Connect<T>> for TcpConnectorFactory<T> {
impl<T: Address> ServiceFactory for TcpConnectorFactory<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>;
type Error = ConnectError;
type Config = ();
@@ -50,7 +50,7 @@ impl<T: Address> ServiceFactory<Connect<T>> for TcpConnectorFactory<T> {
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
ready(Ok(self.service()))
ok(self.service())
}
}
@@ -70,38 +70,37 @@ impl<T> Clone for TcpConnector<T> {
}
}
impl<T: Address> Service<Connect<T>> for TcpConnector<T> {
impl<T: Address> Service for TcpConnector<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>;
type Error = ConnectError;
type Future = TcpConnectorResponse<T>;
#[allow(clippy::type_complexity)]
type Future = Either<TcpConnectorResponse<T>, Ready<Result<Self::Response, Self::Error>>>;
actix_service::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future {
let port = req.port();
let Connect { req, addr, .. } = req;
if let Some(addr) = addr {
TcpConnectorResponse::new(req, port, addr)
Either::Left(TcpConnectorResponse::new(req, port, addr))
} else {
error!("TCP connector: got unresolved address");
TcpConnectorResponse::Error(Some(ConnectError::Unresolved))
Either::Right(err(ConnectError::Unresolved))
}
}
}
type LocalBoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>;
#[doc(hidden)]
/// TCP stream connector response future
pub enum TcpConnectorResponse<T> {
Response {
req: Option<T>,
port: u16,
addrs: Option<VecDeque<SocketAddr>>,
stream: Option<LocalBoxFuture<'static, Result<TcpStream, io::Error>>>,
},
Error(Option<ConnectError>),
pub struct TcpConnectorResponse<T> {
req: Option<T>,
port: u16,
addrs: Option<VecDeque<SocketAddr>>,
stream: Option<BoxFuture<'static, Result<TcpStream, io::Error>>>,
}
impl<T: Address> TcpConnectorResponse<T> {
@@ -117,13 +116,13 @@ impl<T: Address> TcpConnectorResponse<T> {
);
match addr {
either::Either::Left(addr) => TcpConnectorResponse::Response {
either::Either::Left(addr) => TcpConnectorResponse {
req: Some(req),
port,
addrs: None,
stream: Some(Box::pin(TcpStream::connect(addr))),
stream: Some(TcpStream::connect(addr).boxed()),
},
either::Either::Right(addrs) => TcpConnectorResponse::Response {
either::Either::Right(addrs) => TcpConnectorResponse {
req: Some(req),
port,
addrs: Some(addrs),
@@ -138,43 +137,36 @@ impl<T: Address> Future for TcpConnectorResponse<T> {
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
match this {
TcpConnectorResponse::Error(e) => Poll::Ready(Err(e.take().unwrap())),
// connect
TcpConnectorResponse::Response {
req,
port,
addrs,
stream,
} => loop {
if let Some(new) = stream.as_mut() {
match new.as_mut().poll(cx) {
Poll::Ready(Ok(sock)) => {
let req = req.take().unwrap();
trace!(
"TCP connector - successfully connected to connecting to {:?} - {:?}",
req.host(), sock.peer_addr()
);
return Poll::Ready(Ok(Connection::new(sock, req)));
}
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(err)) => {
trace!(
"TCP connector - failed to connect to connecting to {:?} port: {}",
req.as_ref().unwrap().host(),
port,
);
if addrs.is_none() || addrs.as_ref().unwrap().is_empty() {
return Poll::Ready(Err(err.into()));
}
// connect
loop {
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 Poll::Ready(Ok(Connection::new(sock, req)));
}
Poll::Pending => return Poll::Pending,
Poll::Ready(Err(err)) => {
trace!(
"TCP connector - failed to connect to connecting to {:?} port: {}",
this.req.as_ref().unwrap().host(),
this.port,
);
if this.addrs.is_none() || this.addrs.as_ref().unwrap().is_empty() {
return Poll::Ready(Err(err.into()));
}
}
}
}
// try to connect
let addr = addrs.as_mut().unwrap().pop_front().unwrap();
*stream = Some(Box::pin(TcpStream::connect(addr)));
},
// try to connect
let addr = this.addrs.as_mut().unwrap().pop_front().unwrap();
this.stream = Some(TcpStream::connect(addr).boxed());
}
}
}

View File

@@ -5,12 +5,21 @@
//! * `openssl` - enables TLS support via `openssl` crate
//! * `rustls` - enables TLS support via `rustls` crate
#![deny(rust_2018_idioms, nonstandard_style)]
#![recursion_limit = "128"]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
#[macro_use]
extern crate log;
mod connect;
mod connector;
mod error;
mod resolve;
mod service;
pub mod ssl;
#[cfg(feature = "uri")]
mod uri;
@@ -36,7 +45,7 @@ pub async fn start_resolver(
cfg: ResolverConfig,
opts: ResolverOpts,
) -> Result<AsyncResolver, ConnectError> {
Ok(AsyncResolver::tokio(cfg, opts)?)
Ok(AsyncResolver::tokio(cfg, opts).await?)
}
struct DefaultResolver(AsyncResolver);
@@ -53,7 +62,7 @@ pub(crate) async fn get_default_resolver() -> Result<AsyncResolver, ConnectError
}
};
let resolver = AsyncResolver::tokio(cfg, opts)?;
let resolver = AsyncResolver::tokio(cfg, opts).await?;
Arbiter::set_item(DefaultResolver(resolver.clone()));
Ok(resolver)
@@ -67,8 +76,8 @@ pub async fn start_default_resolver() -> Result<AsyncResolver, ConnectError> {
/// Create TCP connector service.
pub fn new_connector<T: Address + 'static>(
resolver: AsyncResolver,
) -> impl Service<Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError> + Clone
{
) -> impl Service<Request = Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError>
+ Clone {
pipeline(Resolver::new(resolver)).and_then(TcpConnector::new())
}
@@ -76,8 +85,8 @@ pub fn new_connector<T: Address + 'static>(
pub fn new_connector_factory<T: Address + 'static>(
resolver: AsyncResolver,
) -> impl ServiceFactory<
Connect<T>,
Config = (),
Request = Connect<T>,
Response = Connection<T, TcpStream>,
Error = ConnectError,
InitError = (),
@@ -87,15 +96,15 @@ pub fn new_connector_factory<T: Address + 'static>(
/// Create connector service with default parameters.
pub fn default_connector<T: Address + 'static>(
) -> impl Service<Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError> + Clone
{
) -> impl Service<Request = Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError>
+ Clone {
pipeline(Resolver::default()).and_then(TcpConnector::new())
}
/// Create connector service factory with default parameters.
pub fn default_connector_factory<T: Address + 'static>() -> impl ServiceFactory<
Connect<T>,
Config = (),
Request = Connect<T>,
Response = Connection<T, TcpStream>,
Error = ConnectError,
InitError = (),

View File

@@ -6,13 +6,12 @@ use std::task::{Context, Poll};
use actix_service::{Service, ServiceFactory};
use futures_util::future::{ok, Either, Ready};
use log::trace;
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
use trust_dns_resolver::{error::ResolveError, lookup_ip::LookupIp};
use super::connect::{Address, Connect};
use super::error::ConnectError;
use super::get_default_resolver;
use crate::connect::{Address, Connect};
use crate::error::ConnectError;
use crate::get_default_resolver;
/// DNS Resolver Service factory
pub struct ResolverFactory<T> {
@@ -55,7 +54,8 @@ impl<T> Clone for ResolverFactory<T> {
}
}
impl<T: Address> ServiceFactory<Connect<T>> for ResolverFactory<T> {
impl<T: Address> ServiceFactory for ResolverFactory<T> {
type Request = Connect<T>;
type Response = Connect<T>;
type Error = ConnectError;
type Config = ();
@@ -102,7 +102,8 @@ impl<T> Clone for Resolver<T> {
}
}
impl<T: Address> Service<Connect<T>> for Resolver<T> {
impl<T: Address> Service for Resolver<T> {
type Request = Connect<T>;
type Response = Connect<T>;
type Error = ConnectError;
#[allow(clippy::type_complexity)]
@@ -111,7 +112,9 @@ impl<T: Address> Service<Connect<T>> for Resolver<T> {
Ready<Result<Connect<T>, Self::Error>>,
>;
actix_service::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, mut req: Connect<T>) -> Self::Future {
if req.addr.is_some() {

View File

@@ -8,10 +8,10 @@ use either::Either;
use futures_util::future::{ok, Ready};
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
use super::connect::{Address, Connect, Connection};
use super::connector::{TcpConnector, TcpConnectorFactory};
use super::error::ConnectError;
use super::resolve::{Resolver, ResolverFactory};
use crate::connect::{Address, Connect, Connection};
use crate::connector::{TcpConnector, TcpConnectorFactory};
use crate::error::ConnectError;
use crate::resolve::{Resolver, ResolverFactory};
pub struct ConnectServiceFactory<T> {
tcp: TcpConnectorFactory<T>,
@@ -70,7 +70,8 @@ impl<T> Clone for ConnectServiceFactory<T> {
}
}
impl<T: Address> ServiceFactory<Connect<T>> for ConnectServiceFactory<T> {
impl<T: Address> ServiceFactory for ConnectServiceFactory<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>;
type Error = ConnectError;
type Config = ();
@@ -89,12 +90,15 @@ pub struct ConnectService<T> {
resolver: Resolver<T>,
}
impl<T: Address> Service<Connect<T>> for ConnectService<T> {
impl<T: Address> Service for ConnectService<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>;
type Error = ConnectError;
type Future = ConnectServiceResponse<T>;
actix_service::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future {
ConnectServiceResponse {
@@ -105,8 +109,8 @@ impl<T: Address> Service<Connect<T>> for ConnectService<T> {
}
enum ConnectState<T: Address> {
Resolve(<Resolver<T> as Service<Connect<T>>>::Future),
Connect(<TcpConnector<T> as Service<Connect<T>>>::Future),
Resolve(<Resolver<T> as Service>::Future),
Connect(<TcpConnector<T> as Service>::Future),
}
impl<T: Address> ConnectState<T> {
@@ -156,12 +160,15 @@ pub struct TcpConnectService<T> {
resolver: Resolver<T>,
}
impl<T: Address + 'static> Service<Connect<T>> for TcpConnectService<T> {
impl<T: Address + 'static> Service for TcpConnectService<T> {
type Request = Connect<T>;
type Response = TcpStream;
type Error = ConnectError;
type Future = TcpConnectServiceResponse<T>;
actix_service::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future {
TcpConnectServiceResponse {
@@ -172,8 +179,8 @@ impl<T: Address + 'static> Service<Connect<T>> for TcpConnectService<T> {
}
enum TcpConnectState<T: Address> {
Resolve(<Resolver<T> as Service<Connect<T>>>::Future),
Connect(<TcpConnector<T> as Service<Connect<T>>>::Future),
Resolve(<Resolver<T> as Service>::Future),
Connect(<TcpConnector<T> as Service>::Future),
}
impl<T: Address> TcpConnectState<T> {

View File

@@ -4,89 +4,105 @@ use std::pin::Pin;
use std::task::{Context, Poll};
use std::{fmt, io};
pub use open_ssl::ssl::{Error as SslError, SslConnector, SslMethod};
pub use tokio_openssl::{HandshakeError, SslStream};
use actix_codec::{AsyncRead, AsyncWrite};
use actix_rt::net::TcpStream;
use actix_service::{Service, ServiceFactory};
use futures_util::{
future::{ready, Either, Ready},
ready,
};
use log::trace;
pub use openssl::ssl::{Error as SslError, HandshakeError, SslConnector, SslMethod};
pub use tokio_openssl::SslStream;
use futures_util::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready};
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
use crate::connect::{
use crate::{
Address, Connect, ConnectError, ConnectService, ConnectServiceFactory, Connection,
};
/// OpenSSL connector factory
pub struct OpensslConnector {
pub struct OpensslConnector<T, U> {
connector: SslConnector,
_t: PhantomData<(T, U)>,
}
impl OpensslConnector {
impl<T, U> OpensslConnector<T, U> {
pub fn new(connector: SslConnector) -> Self {
OpensslConnector { connector }
}
}
impl OpensslConnector {
pub fn service(connector: SslConnector) -> OpensslConnectorService {
OpensslConnectorService { connector }
}
}
impl Clone for OpensslConnector {
fn clone(&self) -> Self {
Self {
connector: self.connector.clone(),
OpensslConnector {
connector,
_t: PhantomData,
}
}
}
impl<T, U> ServiceFactory<Connection<T, U>> for OpensslConnector
impl<T, U> OpensslConnector<T, U>
where
T: Address + 'static,
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static,
{
pub fn service(connector: SslConnector) -> OpensslConnectorService<T, U> {
OpensslConnectorService {
connector,
_t: PhantomData,
}
}
}
impl<T, U> Clone for OpensslConnector<T, U> {
fn clone(&self) -> Self {
Self {
connector: self.connector.clone(),
_t: PhantomData,
}
}
}
impl<T, U> ServiceFactory for OpensslConnector<T, U>
where
T: Address + 'static,
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static,
{
type Request = Connection<T, U>;
type Response = Connection<T, SslStream<U>>;
type Error = io::Error;
type Config = ();
type Service = OpensslConnectorService;
type Service = OpensslConnectorService<T, U>;
type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
ready(Ok(OpensslConnectorService {
ok(OpensslConnectorService {
connector: self.connector.clone(),
}))
_t: PhantomData,
})
}
}
pub struct OpensslConnectorService {
pub struct OpensslConnectorService<T, U> {
connector: SslConnector,
_t: PhantomData<(T, U)>,
}
impl Clone for OpensslConnectorService {
impl<T, U> Clone for OpensslConnectorService<T, U> {
fn clone(&self) -> Self {
Self {
connector: self.connector.clone(),
_t: PhantomData,
}
}
}
impl<T, U> Service<Connection<T, U>> for OpensslConnectorService
impl<T, U> Service for OpensslConnectorService<T, U>
where
T: Address + 'static,
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug + 'static,
{
type Request = Connection<T, U>;
type Response = Connection<T, SslStream<U>>;
type Error = io::Error;
#[allow(clippy::type_complexity)]
type Future = Either<ConnectAsyncExt<T, U>, Ready<Result<Self::Response, Self::Error>>>;
actix_service::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, stream: Connection<T, U>) -> Self::Future {
trace!("SSL Handshake start for: {:?}", stream.host());
@@ -94,24 +110,19 @@ where
let host = stream.host().to_string();
match self.connector.configure() {
Err(e) => Either::Right(ready(Err(io::Error::new(io::ErrorKind::Other, e)))),
Ok(config) => {
let ssl = config
.into_ssl(&host)
.expect("SSL connect configuration was invalid.");
Either::Left(ConnectAsyncExt {
io: Some(SslStream::new(ssl, io).unwrap()),
stream: Some(stream),
_t: PhantomData,
})
}
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<T, U> {
io: Option<SslStream<U>>,
fut: LocalBoxFuture<'static, Result<SslStream<U>, HandshakeError<U>>>,
stream: Option<Connection<T, ()>>,
_t: PhantomData<U>,
}
@@ -125,23 +136,24 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
match ready!(Pin::new(this.io.as_mut().unwrap()).poll_connect(cx)) {
Ok(_) => {
let stream = this.stream.take().unwrap();
trace!("SSL Handshake success: {:?}", stream.host());
Poll::Ready(Ok(stream.replace(this.io.take().unwrap()).1))
match Pin::new(&mut this.fut).poll(cx) {
Poll::Ready(Ok(stream)) => {
let s = this.stream.take().unwrap();
trace!("SSL Handshake success: {:?}", s.host());
Poll::Ready(Ok(s.replace(stream).1))
}
Err(e) => {
Poll::Ready(Err(e)) => {
trace!("SSL Handshake error: {:?}", e);
Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, format!("{}", e))))
}
Poll::Pending => Poll::Pending,
}
}
}
pub struct OpensslConnectServiceFactory<T> {
tcp: ConnectServiceFactory<T>,
openssl: OpensslConnector,
openssl: OpensslConnector<T, TcpStream>,
}
impl<T> OpensslConnectServiceFactory<T> {
@@ -167,6 +179,7 @@ impl<T> OpensslConnectServiceFactory<T> {
tcp: self.tcp.service(),
openssl: OpensslConnectorService {
connector: self.openssl.connector.clone(),
_t: PhantomData,
},
}
}
@@ -181,7 +194,8 @@ impl<T> Clone for OpensslConnectServiceFactory<T> {
}
}
impl<T: Address + 'static> ServiceFactory<Connect<T>> for OpensslConnectServiceFactory<T> {
impl<T: Address + 'static> ServiceFactory for OpensslConnectServiceFactory<T> {
type Request = Connect<T>;
type Response = SslStream<TcpStream>;
type Error = ConnectError;
type Config = ();
@@ -190,22 +204,25 @@ impl<T: Address + 'static> ServiceFactory<Connect<T>> for OpensslConnectServiceF
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
ready(Ok(self.service()))
ok(self.service())
}
}
#[derive(Clone)]
pub struct OpensslConnectService<T> {
tcp: ConnectService<T>,
openssl: OpensslConnectorService,
openssl: OpensslConnectorService<T, TcpStream>,
}
impl<T: Address + 'static> Service<Connect<T>> for OpensslConnectService<T> {
impl<T: Address + 'static> Service for OpensslConnectService<T> {
type Request = Connect<T>;
type Response = SslStream<TcpStream>;
type Error = ConnectError;
type Future = OpensslConnectServiceResponse<T>;
actix_service::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future {
OpensslConnectServiceResponse {
@@ -217,9 +234,9 @@ impl<T: Address + 'static> Service<Connect<T>> for OpensslConnectService<T> {
}
pub struct OpensslConnectServiceResponse<T: Address + 'static> {
fut1: Option<<ConnectService<T> as Service<Connect<T>>>::Future>,
fut2: Option<<OpensslConnectorService as Service<Connection<T, TcpStream>>>::Future>,
openssl: OpensslConnectorService,
fut1: Option<<ConnectService<T> as Service>::Future>,
fut2: Option<<OpensslConnectorService<T, TcpStream> as Service>::Future>,
openssl: OpensslConnectorService<T, TcpStream>,
}
impl<T: Address> Future for OpensslConnectServiceResponse<T> {
@@ -227,7 +244,7 @@ impl<T: Address> Future for OpensslConnectServiceResponse<T> {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(ref mut fut) = self.fut1 {
match ready!(Pin::new(fut).poll(cx)) {
match futures_util::ready!(Pin::new(fut).poll(cx)) {
Ok(res) => {
let _ = self.fut1.take();
self.fut2 = Some(self.openssl.call(res));
@@ -237,7 +254,7 @@ impl<T: Address> Future for OpensslConnectServiceResponse<T> {
}
if let Some(ref mut fut) = self.fut2 {
match ready!(Pin::new(fut).poll(cx)) {
match futures_util::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,

View File

@@ -1,89 +1,104 @@
use std::fmt;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
pub use rustls::Session;
pub use rust_tls::Session;
pub use tokio_rustls::{client::TlsStream, rustls::ClientConfig};
use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory};
use futures_util::{
future::{ready, Ready},
ready,
};
use log::trace;
use futures_util::future::{ok, Ready};
use tokio_rustls::{Connect, TlsConnector};
use webpki::DNSNameRef;
use crate::connect::{Address, Connection};
use crate::{Address, Connection};
/// Rustls connector factory
pub struct RustlsConnector {
pub struct RustlsConnector<T, U> {
connector: Arc<ClientConfig>,
_t: PhantomData<(T, U)>,
}
impl RustlsConnector {
impl<T, U> RustlsConnector<T, U> {
pub fn new(connector: Arc<ClientConfig>) -> Self {
RustlsConnector { connector }
}
}
impl RustlsConnector {
pub fn service(connector: Arc<ClientConfig>) -> RustlsConnectorService {
RustlsConnectorService { connector }
}
}
impl Clone for RustlsConnector {
fn clone(&self) -> Self {
Self {
connector: self.connector.clone(),
RustlsConnector {
connector,
_t: PhantomData,
}
}
}
impl<T: Address, U> ServiceFactory<Connection<T, U>> for RustlsConnector
where
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug,
{
type Response = Connection<T, TlsStream<U>>;
type Error = std::io::Error;
type Config = ();
type Service = RustlsConnectorService;
type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
ready(Ok(RustlsConnectorService {
connector: self.connector.clone(),
}))
}
}
pub struct RustlsConnectorService {
connector: Arc<ClientConfig>,
}
impl Clone for RustlsConnectorService {
fn clone(&self) -> Self {
Self {
connector: self.connector.clone(),
}
}
}
impl<T, U> Service<Connection<T, U>> for RustlsConnectorService
impl<T, U> RustlsConnector<T, U>
where
T: Address,
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug,
{
pub fn service(connector: Arc<ClientConfig>) -> RustlsConnectorService<T, U> {
RustlsConnectorService {
connector,
_t: PhantomData,
}
}
}
impl<T, U> Clone for RustlsConnector<T, U> {
fn clone(&self) -> Self {
Self {
connector: self.connector.clone(),
_t: PhantomData,
}
}
}
impl<T: Address, U> ServiceFactory for RustlsConnector<T, U>
where
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug,
{
type Request = Connection<T, U>;
type Response = Connection<T, TlsStream<U>>;
type Error = std::io::Error;
type Config = ();
type Service = RustlsConnectorService<T, U>;
type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
ok(RustlsConnectorService {
connector: self.connector.clone(),
_t: PhantomData,
})
}
}
pub struct RustlsConnectorService<T, U> {
connector: Arc<ClientConfig>,
_t: PhantomData<(T, U)>,
}
impl<T, U> Clone for RustlsConnectorService<T, U> {
fn clone(&self) -> Self {
Self {
connector: self.connector.clone(),
_t: PhantomData,
}
}
}
impl<T: Address, U> Service for RustlsConnectorService<T, U>
where
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug,
{
type Request = Connection<T, U>;
type Response = Connection<T, TlsStream<U>>;
type Error = std::io::Error;
type Future = ConnectAsyncExt<T, U>;
actix_service::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, stream: Connection<T, U>) -> Self::Future {
trace!("SSL Handshake start for: {:?}", stream.host());
@@ -102,18 +117,20 @@ pub struct ConnectAsyncExt<T, U> {
stream: Option<Connection<T, ()>>,
}
impl<T, U> Future for ConnectAsyncExt<T, U>
impl<T: Address, U> Future for ConnectAsyncExt<T, U>
where
T: Address,
U: AsyncRead + AsyncWrite + Unpin + fmt::Debug,
{
type Output = Result<Connection<T, TlsStream<U>>, std::io::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let stream = ready!(Pin::new(&mut this.fut).poll(cx))?;
let s = this.stream.take().unwrap();
trace!("SSL Handshake success: {:?}", s.host());
Poll::Ready(Ok(s.replace(stream).1))
Poll::Ready(
futures_util::ready!(Pin::new(&mut this.fut).poll(cx)).map(|stream| {
let s = this.stream.take().unwrap();
trace!("SSL Handshake success: {:?}", s.host());
s.replace(stream).1
}),
)
}
}

View File

@@ -1,6 +1,6 @@
use http::Uri;
use super::Address;
use crate::Address;
impl Address for Uri {
fn host(&self) -> &str {

View File

@@ -2,18 +2,15 @@ use std::io;
use actix_codec::{BytesCodec, Framed};
use actix_rt::net::TcpStream;
use actix_server::TestServer;
use actix_service::{fn_service, Service, ServiceFactory};
use actix_testing::TestServer;
use bytes::Bytes;
use futures_util::sink::SinkExt;
use actix_tls::connect::{
self as actix_connect,
resolver::{ResolverConfig, ResolverOpts},
Connect,
};
use actix_connect::resolver::{ResolverConfig, ResolverOpts};
use actix_connect::Connect;
#[cfg(all(feature = "connect", feature = "openssl"))]
#[cfg(feature = "openssl")]
#[actix_rt::test]
async fn test_string() {
let srv = TestServer::with(|| {

View File

@@ -1,27 +1,11 @@
# Changes
## Unreleased - 2021-xx-xx
## Unreleased - 2020-xx-xx
## 2.0.0-beta.1 - 2020-12-28
### Added
* Add `System::attach_to_tokio` method. [#173]
### Changed
* Update `tokio` dependency to `1.0`. [#236]
* Rename `time` module `delay_for` to `sleep`, `delay_until` to `sleep_until`, `Delay` to `Sleep`
to stay aligned with Tokio's naming. [#236]
* Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`.
* These methods now accept `&self` when calling. [#236]
* Remove `'static` lifetime requirement for `System::run` and `Builder::run`. [#236]
* `Arbiter::spawn` now panics when `System` is not in scope. [#207]
### Fixed
* Fix work load issue by removing `PENDING` thread local. [#207]
[#207]: https://github.com/actix/actix-net/pull/207
[#236]: https://github.com/actix/actix-net/pull/236
## [1.1.1] - 2020-04-30
### Fixed

View File

@@ -1,8 +1,8 @@
[package]
name = "actix-rt"
version = "2.0.0-beta.1"
version = "1.1.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Tokio-based single-thread async runtime for the Actix ecosystem"
description = "Actix runtime"
keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
@@ -17,5 +17,11 @@ path = "src/lib.rs"
[dependencies]
actix-macros = "0.1.0"
copyless = "0.1.4"
futures-channel = "0.3.4"
futures-util = { version = "0.3.4", default-features = false, features = ["alloc"] }
smallvec = "1"
tokio = { version = "0.2.6", default-features = false, features = ["rt-core", "rt-util", "io-driver", "tcp", "uds", "udp", "time", "signal", "stream"] }
tokio = { version = "1", features = ["rt", "net", "signal", "sync", "time"] }
[dev-dependencies]
tokio = { version = "0.2.6", features = ["full"] }

View File

@@ -1,30 +1,31 @@
use std::any::{Any, TypeId};
use std::cell::RefCell;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::task::{Context, Poll};
use std::{fmt, thread};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use tokio::sync::oneshot::{channel, error::RecvError as Canceled, Sender};
// use futures_util::stream::FuturesUnordered;
// use tokio::task::JoinHandle;
// use tokio::stream::StreamExt;
use tokio::task::LocalSet;
use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures_channel::oneshot::{channel, Canceled, Sender};
use futures_util::{
future::{self, Future, FutureExt},
stream::Stream,
};
use crate::runtime::Runtime;
use crate::system::System;
use copyless::BoxHelper;
use smallvec::SmallVec;
pub use tokio::task::JoinHandle;
thread_local!(
static ADDR: RefCell<Option<Arbiter>> = RefCell::new(None);
// TODO: Commented out code are for Arbiter::local_join function.
// It can be safely removed if this function is not used in actix-*.
//
// /// stores join handle for spawned async tasks.
// static HANDLE: RefCell<FuturesUnordered<JoinHandle<()>>> =
// RefCell::new(FuturesUnordered::new());
static RUNNING: Cell<bool> = Cell::new(false);
static Q: RefCell<Vec<Pin<Box<dyn Future<Output = ()>>>>> = RefCell::new(Vec::new());
static PENDING: RefCell<SmallVec<[JoinHandle<()>; 8]>> = RefCell::new(SmallVec::new());
static STORAGE: RefCell<HashMap<TypeId, Box<dyn Any>>> = RefCell::new(HashMap::new());
);
@@ -68,14 +69,14 @@ impl Default for Arbiter {
}
impl Arbiter {
pub(crate) fn new_system(local: &LocalSet) -> Self {
let (tx, rx) = unbounded_channel();
pub(crate) fn new_system() -> Self {
let (tx, rx) = unbounded();
let arb = Arbiter::with_sender(tx);
ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone()));
RUNNING.with(|cell| cell.set(false));
STORAGE.with(|cell| cell.borrow_mut().clear());
local.spawn_local(ArbiterController { rx });
Arbiter::spawn(ArbiterController { stop: None, rx });
arb
}
@@ -90,14 +91,13 @@ impl Arbiter {
}
/// Check if current arbiter is running.
#[deprecated(note = "Thread local variables for running state of Arbiter is removed")]
pub fn is_running() -> bool {
false
RUNNING.with(|cell| cell.get())
}
/// Stop arbiter from continuing it's event loop.
pub fn stop(&self) {
let _ = self.sender.send(ArbiterCommand::Stop);
let _ = self.sender.unbounded_send(ArbiterCommand::Stop);
}
/// Spawn new thread and run event loop in spawned thread.
@@ -106,47 +106,69 @@ impl Arbiter {
let id = COUNT.fetch_add(1, Ordering::Relaxed);
let name = format!("actix-rt:worker:{}", id);
let sys = System::current();
let (tx, rx) = unbounded_channel();
let (arb_tx, arb_rx) = unbounded();
let arb_tx2 = arb_tx.clone();
let handle = thread::Builder::new()
.name(name.clone())
.spawn({
let tx = tx.clone();
move || {
let rt = Runtime::new().expect("Can not create Runtime");
let arb = Arbiter::with_sender(tx);
.spawn(move || {
let mut rt = Runtime::new().expect("Can not create Runtime");
let arb = Arbiter::with_sender(arb_tx);
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);
ADDR.with(|cell| *cell.borrow_mut() = Some(arb.clone()));
// 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()
.send(SystemCommand::RegisterArbiter(id, arb));
// register arbiter
let _ = System::current()
.sys()
.unbounded_send(SystemCommand::RegisterArbiter(id, arb));
// start arbiter controller
// run loop
rt.block_on(ArbiterController { rx });
// run loop
let _ = rt.block_on(stop_rx).unwrap_or(1);
// unregister arbiter
let _ = System::current()
.sys()
.send(SystemCommand::UnregisterArbiter(id));
}
// 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: tx,
sender: arb_tx2,
thread_handle: Some(handle),
}
}
pub(crate) fn run_system(rt: Option<&Runtime>) {
RUNNING.with(|cell| cell.set(true));
Q.with(|cell| {
let mut v = cell.borrow_mut();
for fut in v.drain(..) {
if let Some(rt) = rt {
rt.spawn(fut);
} else {
tokio::task::spawn_local(fut);
}
}
});
}
pub(crate) fn stop_system() {
RUNNING.with(|cell| cell.set(false));
}
/// Spawn a future on the current thread. This does not create a new Arbiter
/// or Arbiter address, it is simply a helper for spawning futures on the current
/// thread.
@@ -154,12 +176,26 @@ impl Arbiter {
where
F: Future<Output = ()> + 'static,
{
// HANDLE.with(|handle| {
// let handle = handle.borrow();
// handle.push(tokio::task::spawn_local(future));
// });
// let _ = tokio::task::spawn_local(CleanupPending);
let _ = tokio::task::spawn_local(future);
RUNNING.with(move |cell| {
if cell.get() {
// Spawn the future on running executor
let len = PENDING.with(move |cell| {
let mut p = cell.borrow_mut();
p.push(tokio::task::spawn_local(future));
p.len()
});
if len > 7 {
// Before reaching the inline size
tokio::task::spawn_local(CleanupPending);
}
} 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(Pin::from(Box::alloc().init(future)))
});
}
});
}
/// Executes a future on the current thread. This does not create a new Arbiter
@@ -170,9 +206,7 @@ impl Arbiter {
F: FnOnce() -> R + 'static,
R: Future<Output = ()> + 'static,
{
Arbiter::spawn(async {
f();
})
Arbiter::spawn(future::lazy(|_| f()).flatten())
}
/// Send a future to the Arbiter's thread, and spawn it.
@@ -180,7 +214,9 @@ impl Arbiter {
where
F: Future<Output = ()> + Send + Unpin + 'static,
{
let _ = self.sender.send(ArbiterCommand::Execute(Box::new(future)));
let _ = self
.sender
.unbounded_send(ArbiterCommand::Execute(Box::new(future)));
}
/// Send a function to the Arbiter's thread, and execute it. Any result from the function
@@ -191,7 +227,7 @@ impl Arbiter {
{
let _ = self
.sender
.send(ArbiterCommand::ExecuteFn(Box::new(move || {
.unbounded_send(ArbiterCommand::ExecuteFn(Box::new(move || {
f();
})));
}
@@ -207,8 +243,8 @@ impl Arbiter {
let (tx, rx) = channel();
let _ = self
.sender
.send(ArbiterCommand::ExecuteFn(Box::new(move || {
if !tx.is_closed() {
.unbounded_send(ArbiterCommand::ExecuteFn(Box::new(move || {
if !tx.is_canceled() {
let _ = tx.send(f());
}
})));
@@ -277,33 +313,40 @@ impl Arbiter {
/// Returns a future that will be completed once all currently spawned futures
/// have completed.
#[deprecated(since = "1.2.0", note = "Arbiter::local_join function is removed.")]
pub async fn local_join() {
// let handle = HANDLE.with(|fut| std::mem::take(&mut *fut.borrow_mut()));
// async move {
// handle.collect::<Vec<_>>().await;
// }
unimplemented!("Arbiter::local_join function is removed.")
pub fn local_join() -> impl Future<Output = ()> {
PENDING.with(move |cell| {
let current = cell.replace(SmallVec::new());
future::join_all(current).map(|_| ())
})
}
}
// /// Future used for cleaning-up already finished `JoinHandle`s
// /// from the `PENDING` list so the vector doesn't grow indefinitely
// struct CleanupPending;
//
// impl Future for CleanupPending {
// type Output = ();
//
// fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// HANDLE.with(move |handle| {
// recycle_join_handle(&mut *handle.borrow_mut(), cx);
// });
//
// Poll::Ready(())
// }
// }
/// Future used for cleaning-up already finished `JoinHandle`s
/// from the `PENDING` list so the vector doesn't grow indefinitely
struct CleanupPending;
impl Future for CleanupPending {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
PENDING.with(move |cell| {
let mut pending = cell.borrow_mut();
let mut i = 0;
while i != pending.len() {
if Pin::new(&mut pending[i]).poll(cx).is_ready() {
pending.remove(i);
} else {
i += 1;
}
}
});
Poll::Ready(())
}
}
struct ArbiterController {
stop: Option<Sender<i32>>,
rx: UnboundedReceiver<ArbiterCommand>,
}
@@ -325,17 +368,25 @@ impl Future for ArbiterController {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
match Pin::new(&mut self.rx).poll_recv(cx) {
match Pin::new(&mut self.rx).poll_next(cx) {
Poll::Ready(None) => return Poll::Ready(()),
Poll::Ready(Some(item)) => match item {
ArbiterCommand::Stop => return Poll::Ready(()),
ArbiterCommand::Stop => {
if let Some(stop) = self.stop.take() {
let _ = stop.send(0);
};
return Poll::Ready(());
}
ArbiterCommand::Execute(fut) => {
// HANDLE.with(|handle| {
// let mut handle = handle.borrow_mut();
// handle.push(tokio::task::spawn_local(fut));
// recycle_join_handle(&mut *handle, cx);
// });
tokio::task::spawn_local(fut);
let len = PENDING.with(move |cell| {
let mut p = cell.borrow_mut();
p.push(tokio::task::spawn_local(fut));
p.len()
});
if len > 7 {
// Before reaching the inline size
tokio::task::spawn_local(CleanupPending);
}
}
ArbiterCommand::ExecuteFn(f) => {
f.call_box();
@@ -347,20 +398,6 @@ impl Future for ArbiterController {
}
}
// fn recycle_join_handle(handle: &mut FuturesUnordered<JoinHandle<()>>, cx: &mut Context<'_>) {
// let _ = Pin::new(&mut *handle).poll_next(cx);
//
// // Try to recycle more join handles and free up memory.
// //
// // this is a guess. The yield limit for FuturesUnordered is 32.
// // So poll an extra 3 times would make the total poll below 128.
// if handle.len() > 64 {
// (0..3).for_each(|_| {
// let _ = Pin::new(&mut *handle).poll_next(cx);
// })
// }
// }
#[derive(Debug)]
pub(crate) enum SystemCommand {
Exit(i32),
@@ -390,7 +427,7 @@ impl Future for SystemArbiter {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
match Pin::new(&mut self.commands).poll_recv(cx) {
match Pin::new(&mut self.commands).poll_next(cx) {
Poll::Ready(None) => return Poll::Ready(()),
Poll::Ready(Some(cmd)) => match cmd {
SystemCommand::Exit(code) => {

View File

@@ -1,9 +1,9 @@
use std::borrow::Cow;
use std::future::Future;
use std::io;
use tokio::sync::mpsc::unbounded_channel;
use tokio::sync::oneshot::{channel, Receiver};
use futures_channel::mpsc::unbounded;
use futures_channel::oneshot::{channel, Receiver};
use futures_util::future::{lazy, Future, FutureExt};
use tokio::task::LocalSet;
use crate::arbiter::{Arbiter, SystemArbiter};
@@ -65,17 +65,16 @@ impl Builder {
/// Function `f` get called within tokio runtime context.
pub fn run<F>(self, f: F) -> io::Result<()>
where
F: FnOnce(),
F: FnOnce() + 'static,
{
self.create_runtime(f).run()
}
fn create_async_runtime(self, local: &LocalSet) -> AsyncSystemRunner {
let (stop_tx, stop) = channel();
let (sys_sender, sys_receiver) = unbounded_channel();
let (sys_sender, sys_receiver) = unbounded();
let system =
System::construct(sys_sender, Arbiter::new_system(local), self.stop_on_panic);
let system = System::construct(sys_sender, Arbiter::new_system(), self.stop_on_panic);
// system arbiter
let arb = SystemArbiter::new(stop_tx, sys_receiver);
@@ -88,26 +87,21 @@ impl Builder {
fn create_runtime<F>(self, f: F) -> SystemRunner
where
F: FnOnce(),
F: FnOnce() + 'static,
{
let (stop_tx, stop) = channel();
let (sys_sender, sys_receiver) = unbounded_channel();
let (sys_sender, sys_receiver) = unbounded();
let rt = Runtime::new().unwrap();
let system = System::construct(
sys_sender,
Arbiter::new_system(rt.local()),
self.stop_on_panic,
);
let system = System::construct(sys_sender, Arbiter::new_system(), self.stop_on_panic);
// system arbiter
let arb = SystemArbiter::new(stop_tx, sys_receiver);
let mut rt = Runtime::new().unwrap();
rt.spawn(arb);
// init system arbiter and run configuration method
rt.block_on(async { f() });
rt.block_on(lazy(move |_| f()));
SystemRunner { rt, stop, system }
}
@@ -126,21 +120,27 @@ impl AsyncSystemRunner {
let AsyncSystemRunner { stop, .. } = self;
// run loop
async {
match stop.await {
Ok(code) => {
if code != 0 {
Err(io::Error::new(
io::ErrorKind::Other,
format!("Non-zero exit code: {}", code),
))
} else {
Ok(())
lazy(|_| {
Arbiter::run_system(None);
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)),
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
};
Arbiter::stop_system();
res
}
}
})
.flatten()
}
}
@@ -157,10 +157,11 @@ impl SystemRunner {
/// This function will start event loop and will finish once the
/// `System::stop()` function is called.
pub fn run(self) -> io::Result<()> {
let SystemRunner { rt, stop, .. } = self;
let SystemRunner { mut rt, stop, .. } = self;
// run loop
match rt.block_on(stop) {
Arbiter::run_system(Some(&rt));
let result = match rt.block_on(stop) {
Ok(code) => {
if code != 0 {
Err(io::Error::new(
@@ -172,12 +173,19 @@ impl SystemRunner {
}
}
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
}
};
Arbiter::stop_system();
result
}
/// Execute a future and wait for result.
#[inline]
pub fn block_on<F: Future>(&self, fut: F) -> F::Output {
self.rt.block_on(fut)
pub fn block_on<F, O>(&mut self, fut: F) -> O
where
F: Future<Output = O> + 'static,
{
Arbiter::run_system(Some(&self.rt));
let res = self.rt.block_on(fut);
Arbiter::stop_system();
res
}
}

View File

@@ -1,12 +1,9 @@
//! Tokio-based single-thread async runtime for the Actix ecosystem.
//! A runtime implementation that runs everything on the current thread.
#![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use std::future::Future;
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub use actix_macros::{main, test};
@@ -25,12 +22,15 @@ pub use self::system::System;
/// # Panics
///
/// This function panics if actix system is not running.
#[inline]
pub fn spawn<F>(f: F)
where
F: Future<Output = ()> + 'static,
F: futures_util::future::Future<Output = ()> + 'static,
{
Arbiter::spawn(f)
if !System::is_set() {
panic!("System is not running");
}
Arbiter::spawn(f);
}
/// Asynchronous signal handling
@@ -59,7 +59,7 @@ pub mod net {
/// Utilities for tracking time.
pub mod time {
pub use tokio::time::Instant;
pub use tokio::time::{delay_for, delay_until, Delay};
pub use tokio::time::{interval, interval_at, Interval};
pub use tokio::time::{sleep, sleep_until, Sleep};
pub use tokio::time::{timeout, Timeout};
}

View File

@@ -18,9 +18,10 @@ impl Runtime {
#[allow(clippy::new_ret_no_self)]
/// Returns a new runtime initialized with default configuration values.
pub fn new() -> io::Result<Runtime> {
let rt = runtime::Builder::new_current_thread()
let rt = runtime::Builder::new()
.enable_io()
.enable_time()
.basic_scheduler()
.build()?;
Ok(Runtime {
@@ -29,10 +30,6 @@ impl Runtime {
})
}
pub(super) fn local(&self) -> &LocalSet {
&self.local
}
/// Spawn a future onto the single-threaded runtime.
///
/// See [module level][mod] documentation for more details.
@@ -47,7 +44,7 @@ impl Runtime {
///
/// # fn dox() {
/// // Create the runtime
/// let rt = Runtime::new().unwrap();
/// let mut rt = Runtime::new().unwrap();
///
/// // Spawn a future onto the runtime
/// rt.spawn(future::lazy(|_| {
@@ -85,10 +82,10 @@ impl Runtime {
///
/// The caller is responsible for ensuring that other spawned futures
/// complete execution by calling `block_on` or `run`.
pub fn block_on<F>(&self, f: F) -> F::Output
pub fn block_on<F>(&mut self, f: F) -> F::Output
where
F: Future,
F: Future + 'static,
{
self.local.block_on(&self.rt, f)
self.local.block_on(&mut self.rt, f)
}
}

View File

@@ -3,7 +3,7 @@ use std::future::Future;
use std::io;
use std::sync::atomic::{AtomicUsize, Ordering};
use tokio::sync::mpsc::UnboundedSender;
use futures_channel::mpsc::UnboundedSender;
use tokio::task::LocalSet;
use crate::arbiter::{Arbiter, SystemCommand};
@@ -70,7 +70,7 @@ impl System {
///
/// # Examples
///
/// ```rust,ignore
/// ```
/// use tokio::{runtime::Runtime, task::LocalSet};
/// use actix_rt::System;
/// use futures_util::future::try_join_all;
@@ -94,9 +94,10 @@ impl System {
/// }
///
///
/// let runtime = tokio::runtime::Builder::new_multi_thread()
/// .worker_threads(2)
/// let mut runtime = tokio::runtime::Builder::new()
/// .core_threads(2)
/// .enable_all()
/// .threaded_scheduler()
/// .build()
/// .unwrap();
///
@@ -139,7 +140,7 @@ impl System {
///
/// # Examples
///
/// ```rust,ignore
/// ```
/// use tokio::runtime::Runtime;
/// use actix_rt::System;
/// use futures_util::future::try_join_all;
@@ -163,9 +164,10 @@ impl System {
/// }
///
///
/// let runtime = tokio::runtime::Builder::new_multi_thread()
/// .worker_threads(2)
/// let runtime = tokio::runtime::Builder::new()
/// .core_threads(2)
/// .enable_all()
/// .threaded_scheduler()
/// .build()
/// .unwrap();
///
@@ -174,7 +176,7 @@ impl System {
/// ```
pub fn attach_to_tokio<Fut, R>(
name: impl Into<String>,
runtime: tokio::runtime::Runtime,
mut runtime: tokio::runtime::Runtime,
rest_operations: Fut,
) -> R
where
@@ -231,7 +233,7 @@ impl System {
/// Stop the system with a particular exit code.
pub fn stop_with_code(&self, code: i32) {
let _ = self.sys.send(SystemCommand::Exit(code));
let _ = self.sys.unbounded_send(SystemCommand::Exit(code));
}
pub(crate) fn sys(&self) -> &UnboundedSender<SystemCommand> {
@@ -254,7 +256,7 @@ impl System {
/// Function `f` get called within tokio runtime context.
pub fn run<F>(f: F) -> io::Result<()>
where
F: FnOnce(),
F: FnOnce() + 'static,
{
Self::builder().run(f)
}

View File

@@ -1,11 +1,25 @@
use std::time::{Duration, Instant};
#[test]
fn start_and_stop() {
actix_rt::System::new("start_and_stop").block_on(async move {
assert!(
actix_rt::Arbiter::is_running(),
"System doesn't seem to have started"
);
});
assert!(
!actix_rt::Arbiter::is_running(),
"System doesn't seem to have stopped"
);
}
#[test]
fn await_for_timer() {
let time = Duration::from_secs(2);
let instant = Instant::now();
actix_rt::System::new("test_wait_timer").block_on(async move {
tokio::time::sleep(time).await;
tokio::time::delay_for(time).await;
});
assert!(
instant.elapsed() >= time,
@@ -20,7 +34,7 @@ fn join_another_arbiter() {
actix_rt::System::new("test_join_another_arbiter").block_on(async move {
let mut arbiter = actix_rt::Arbiter::new();
arbiter.send(Box::pin(async move {
tokio::time::sleep(time).await;
tokio::time::delay_for(time).await;
actix_rt::Arbiter::current().stop();
}));
arbiter.join().unwrap();
@@ -35,7 +49,7 @@ fn join_another_arbiter() {
let mut arbiter = actix_rt::Arbiter::new();
arbiter.exec_fn(move || {
actix_rt::spawn(async move {
tokio::time::sleep(time).await;
tokio::time::delay_for(time).await;
actix_rt::Arbiter::current().stop();
});
});
@@ -50,7 +64,7 @@ fn join_another_arbiter() {
actix_rt::System::new("test_join_another_arbiter").block_on(async move {
let mut arbiter = actix_rt::Arbiter::new();
arbiter.send(Box::pin(async move {
tokio::time::sleep(time).await;
tokio::time::delay_for(time).await;
actix_rt::Arbiter::current().stop();
}));
arbiter.stop();
@@ -62,65 +76,39 @@ fn join_another_arbiter() {
);
}
// #[test]
// fn join_current_arbiter() {
// let time = Duration::from_secs(2);
//
// let instant = Instant::now();
// actix_rt::System::new("test_join_current_arbiter").block_on(async move {
// actix_rt::spawn(async move {
// tokio::time::delay_for(time).await;
// actix_rt::Arbiter::current().stop();
// });
// actix_rt::Arbiter::local_join().await;
// });
// assert!(
// instant.elapsed() >= time,
// "Join on current arbiter should wait for all spawned futures"
// );
//
// let large_timer = Duration::from_secs(20);
// let instant = Instant::now();
// actix_rt::System::new("test_join_current_arbiter").block_on(async move {
// actix_rt::spawn(async move {
// tokio::time::delay_for(time).await;
// actix_rt::Arbiter::current().stop();
// });
// let f = actix_rt::Arbiter::local_join();
// actix_rt::spawn(async move {
// tokio::time::delay_for(large_timer).await;
// actix_rt::Arbiter::current().stop();
// });
// f.await;
// });
// assert!(
// instant.elapsed() < large_timer,
// "local_join should await only for the already spawned futures"
// );
// }
#[test]
fn non_static_block_on() {
let string = String::from("test_str");
let str = string.as_str();
fn join_current_arbiter() {
let time = Duration::from_secs(2);
let sys = actix_rt::System::new("borrow some");
sys.block_on(async {
actix_rt::time::sleep(Duration::from_millis(1)).await;
assert_eq!("test_str", str);
let instant = Instant::now();
actix_rt::System::new("test_join_current_arbiter").block_on(async move {
actix_rt::spawn(async move {
tokio::time::delay_for(time).await;
actix_rt::Arbiter::current().stop();
});
actix_rt::Arbiter::local_join().await;
});
assert!(
instant.elapsed() >= time,
"Join on current arbiter should wait for all spawned futures"
);
let rt = actix_rt::Runtime::new().unwrap();
rt.block_on(async {
actix_rt::time::sleep(Duration::from_millis(1)).await;
assert_eq!("test_str", str);
let large_timer = Duration::from_secs(20);
let instant = Instant::now();
actix_rt::System::new("test_join_current_arbiter").block_on(async move {
actix_rt::spawn(async move {
tokio::time::delay_for(time).await;
actix_rt::Arbiter::current().stop();
});
let f = actix_rt::Arbiter::local_join();
actix_rt::spawn(async move {
tokio::time::delay_for(large_timer).await;
actix_rt::Arbiter::current().stop();
});
f.await;
});
actix_rt::System::run(|| {
assert_eq!("test_str", str);
actix_rt::System::current().stop();
})
.unwrap();
assert!(
instant.elapsed() < large_timer,
"local_join should await only for the already spawned futures"
);
}

View File

@@ -1,28 +1,11 @@
# Changes
## Unreleased - 2021-xx-xx
## 2.0.0-beta.2 - 2021-01-03
* Merge `actix-testing` to `actix-server` as `test_server` mod. [#242]
[#242]: https://github.com/actix/actix-net/pull/242
## 2.0.0-beta.1 - 2020-12-28
## Unreleased - 2020-xx-xx
* Added explicit info log message on accept queue pause. [#215]
* Prevent double registration of sockets when back-pressure is resolved. [#223]
* Update `mio` dependency to `0.7.3`. [#239]
* Remove `socket2` dependency. [#239]
* `ServerBuilder::backlog` now accepts `u32` instead of `i32`. [#239]
* Remove `AcceptNotify` type and pass `WakerQueue` to `Worker` to wake up `Accept`'s `Poll`. [#239]
* Convert `mio::net::TcpStream` to `actix_rt::net::TcpStream`(`UnixStream` for uds) using
`FromRawFd` and `IntoRawFd`(`FromRawSocket` and `IntoRawSocket` on windows). [#239]
* Remove `AsyncRead` and `AsyncWrite` trait bound for `socket::FromStream` trait. [#239]
[#215]: https://github.com/actix/actix-net/pull/215
[#223]: https://github.com/actix/actix-net/pull/223
[#239]: https://github.com/actix/actix-net/pull/239
## 1.0.4 - 2020-09-12

View File

@@ -1,10 +1,7 @@
[package]
name = "actix-server"
version = "2.0.0-beta.2"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"fakeshadow <24548779@qq.com>",
]
version = "1.0.4"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "General purpose TCP server built for the Actix ecosystem"
keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs"
@@ -23,20 +20,25 @@ path = "src/lib.rs"
default = []
[dependencies]
actix-codec = "0.4.0-beta.1"
actix-rt = "2.0.0-beta.1"
actix-service = "2.0.0-beta.2"
actix-utils = "3.0.0-beta.1"
actix-service = "1.0.6"
actix-rt = "1.1.1"
actix-codec = "0.3.0"
actix-utils = "2.0.0"
futures-core = { version = "0.3.7", default-features = false }
log = "0.4"
mio = { version = "0.7.6", features = ["os-poll", "net"] }
num_cpus = "1.13"
mio = "0.6.19"
socket2 = "0.3"
futures-channel = { version = "0.3.4", default-features = false }
futures-util = { version = "0.3.4", default-features = false, features = ["sink"] }
slab = "0.4"
tokio = { version = "1", features = ["sync"] }
# unix domain sockets
# FIXME: Remove it and use mio own uds feature once mio 0.7 is released
mio-uds = { version = "0.6.7" }
[dev-dependencies]
bytes = "1"
env_logger = "0.8"
futures-util = { version = "0.3.7", default-features = false, features = ["sink"] }
tokio = { version = "1", features = ["io-util"] }
bytes = "0.5"
env_logger = "0.7"
actix-testing = "1.0.0"
tokio = { version = "0.2", features = ["io-util"] }

View File

@@ -1,86 +1,120 @@
use std::sync::mpsc as sync_mpsc;
use std::time::Duration;
use std::{io, thread};
use actix_rt::time::{sleep_until, Instant};
use actix_rt::time::{delay_until, Instant};
use actix_rt::System;
use log::{error, info};
use mio::{Interest, Poll, Token as MioToken};
use slab::Slab;
use crate::server::Server;
use crate::socket::{MioListener, SocketAddr};
use crate::waker_queue::{WakerInterest, WakerQueue, WAKER_TOKEN};
use crate::worker::{Conn, WorkerHandle};
use crate::socket::{SocketAddr, SocketListener, StdListener};
use crate::worker::{Conn, WorkerClient};
use crate::Token;
pub(crate) enum Command {
Pause,
Resume,
Stop,
Worker(WorkerClient),
}
struct ServerSocketInfo {
// addr for socket. mainly used for logging.
addr: SocketAddr,
// be ware this is the crate token for identify socket and should not be confused with
// mio::Token
token: Token,
lst: MioListener,
// timeout is used to mark the deadline when this socket's listener should be registered again
// after an error.
sock: SocketListener,
timeout: Option<Instant>,
}
/// Accept loop would live with `ServerBuilder`.
///
/// It's tasked with construct `Poll` instance and `WakerQueue` which would be distributed to
/// `Accept` and `Worker`.
///
/// It would also listen to `ServerCommand` and push interests to `WakerQueue`.
#[derive(Clone)]
pub(crate) struct AcceptNotify(mio::SetReadiness);
impl AcceptNotify {
pub(crate) fn new(ready: mio::SetReadiness) -> Self {
AcceptNotify(ready)
}
pub(crate) fn notify(&self) {
let _ = self.0.set_readiness(mio::Ready::readable());
}
}
impl Default for AcceptNotify {
fn default() -> Self {
AcceptNotify::new(mio::Registration::new2().1)
}
}
pub(crate) struct AcceptLoop {
cmd_reg: Option<mio::Registration>,
cmd_ready: mio::SetReadiness,
notify_reg: Option<mio::Registration>,
notify_ready: mio::SetReadiness,
tx: sync_mpsc::Sender<Command>,
rx: Option<sync_mpsc::Receiver<Command>>,
srv: Option<Server>,
poll: Option<Poll>,
waker: WakerQueue,
}
impl AcceptLoop {
pub fn new(srv: Server) -> Self {
let poll = Poll::new().unwrap_or_else(|e| panic!("Can not create `mio::Poll`: {}", e));
let waker = WakerQueue::new(poll.registry())
.unwrap_or_else(|e| panic!("Can not create `mio::Waker`: {}", e));
pub fn new(srv: Server) -> AcceptLoop {
let (tx, rx) = sync_mpsc::channel();
let (cmd_reg, cmd_ready) = mio::Registration::new2();
let (notify_reg, notify_ready) = mio::Registration::new2();
Self {
AcceptLoop {
tx,
cmd_ready,
cmd_reg: Some(cmd_reg),
notify_ready,
notify_reg: Some(notify_reg),
rx: Some(rx),
srv: Some(srv),
poll: Some(poll),
waker,
}
}
pub(crate) fn waker_owned(&self) -> WakerQueue {
self.waker.clone()
pub fn send(&self, msg: Command) {
let _ = self.tx.send(msg);
let _ = self.cmd_ready.set_readiness(mio::Ready::readable());
}
pub fn wake(&self, i: WakerInterest) {
self.waker.wake(i);
pub fn get_notify(&self) -> AcceptNotify {
AcceptNotify::new(self.notify_ready.clone())
}
pub(crate) fn start(
&mut self,
socks: Vec<(Token, MioListener)>,
handles: Vec<WorkerHandle>,
socks: Vec<(Token, StdListener)>,
workers: Vec<WorkerClient>,
) {
let srv = self.srv.take().expect("Can not re-use AcceptInfo");
let poll = self.poll.take().unwrap();
let waker = self.waker.clone();
Accept::start(poll, waker, socks, srv, handles);
Accept::start(
self.rx.take().expect("Can not re-use AcceptInfo"),
self.cmd_reg.take().expect("Can not re-use AcceptInfo"),
self.notify_reg.take().expect("Can not re-use AcceptInfo"),
socks,
srv,
workers,
);
}
}
/// poll instance of the server.
struct Accept {
poll: Poll,
waker: WakerQueue,
handles: Vec<WorkerHandle>,
poll: mio::Poll,
rx: sync_mpsc::Receiver<Command>,
sockets: Slab<ServerSocketInfo>,
workers: Vec<WorkerClient>,
srv: Server,
timer: (mio::Registration, mio::SetReadiness),
next: usize,
backpressure: bool,
}
const DELTA: usize = 100;
const CMD: mio::Token = mio::Token(0);
const TIMER: mio::Token = mio::Token(1);
const NOTIFY: mio::Token = mio::Token(2);
/// This function defines errors that are per-connection. Which basically
/// means that if we get this error from `accept()` system call it means
/// next connection might be ready to be accepted.
@@ -95,290 +129,326 @@ fn connection_error(e: &io::Error) -> bool {
}
impl Accept {
#![allow(clippy::too_many_arguments)]
pub(crate) fn start(
poll: Poll,
waker: WakerQueue,
socks: Vec<(Token, MioListener)>,
rx: sync_mpsc::Receiver<Command>,
cmd_reg: mio::Registration,
notify_reg: mio::Registration,
socks: Vec<(Token, StdListener)>,
srv: Server,
handles: Vec<WorkerHandle>,
workers: Vec<WorkerClient>,
) {
// Accept runs in its own thread and would want to spawn additional futures to current
// actix system.
let sys = System::current();
thread::Builder::new()
// start accept thread
let _ = thread::Builder::new()
.name("actix-server accept loop".to_owned())
.spawn(move || {
System::set_current(sys);
let (mut accept, sockets) =
Accept::new_with_sockets(poll, waker, socks, handles, srv);
accept.poll_with(sockets);
})
.unwrap();
let mut accept = Accept::new(rx, socks, workers, srv);
// Start listening for incoming commands
if let Err(err) = accept.poll.register(
&cmd_reg,
CMD,
mio::Ready::readable(),
mio::PollOpt::edge(),
) {
panic!("Can not register Registration: {}", err);
}
// Start listening for notify updates
if let Err(err) = accept.poll.register(
&notify_reg,
NOTIFY,
mio::Ready::readable(),
mio::PollOpt::edge(),
) {
panic!("Can not register Registration: {}", err);
}
accept.poll();
});
}
fn new_with_sockets(
poll: Poll,
waker: WakerQueue,
socks: Vec<(Token, MioListener)>,
handles: Vec<WorkerHandle>,
fn new(
rx: sync_mpsc::Receiver<Command>,
socks: Vec<(Token, StdListener)>,
workers: Vec<WorkerClient>,
srv: Server,
) -> (Accept, Slab<ServerSocketInfo>) {
) -> Accept {
// Create a poll instance
let poll = match mio::Poll::new() {
Ok(poll) => poll,
Err(err) => panic!("Can not create mio::Poll: {}", err),
};
// Start accept
let mut sockets = Slab::new();
for (hnd_token, mut lst) in socks.into_iter() {
for (hnd_token, lst) in socks.into_iter() {
let addr = lst.local_addr();
let server = lst.into_listener();
let entry = sockets.vacant_entry();
let token = entry.key();
// Start listening for incoming connections
poll.registry()
.register(&mut lst, MioToken(token), Interest::READABLE)
.unwrap_or_else(|e| panic!("Can not register io: {}", e));
if let Err(err) = poll.register(
&server,
mio::Token(token + DELTA),
mio::Ready::readable(),
mio::PollOpt::edge(),
) {
panic!("Can not register io: {}", err);
}
entry.insert(ServerSocketInfo {
addr,
token: hnd_token,
lst,
sock: server,
timeout: None,
});
}
let accept = Accept {
// Timer
let (tm, tmr) = mio::Registration::new2();
if let Err(err) =
poll.register(&tm, TIMER, mio::Ready::readable(), mio::PollOpt::edge())
{
panic!("Can not register Registration: {}", err);
}
Accept {
poll,
waker,
handles,
rx,
sockets,
workers,
srv,
next: 0,
timer: (tm, tmr),
backpressure: false,
};
(accept, sockets)
}
}
fn poll_with(&mut self, mut sockets: Slab<ServerSocketInfo>) {
fn poll(&mut self) {
// Create storage for events
let mut events = mio::Events::with_capacity(128);
loop {
self.poll
.poll(&mut events, None)
.unwrap_or_else(|e| panic!("Poll error: {}", e));
if let Err(err) = self.poll.poll(&mut events, None) {
panic!("Poll error: {}", err);
}
for event in events.iter() {
let token = event.token();
match token {
// This is a loop because interests for command from previous version was
// a loop that would try to drain the command channel. It's yet unknown
// if it's necessary/good practice to actively drain the waker queue.
WAKER_TOKEN => 'waker: loop {
// take guard with every iteration so no new interest can be added
// until the current task is done.
let mut guard = self.waker.guard();
match guard.pop_front() {
// worker notify it becomes available. we may want to recover
// from backpressure.
Some(WakerInterest::WorkerAvailable) => {
drop(guard);
self.maybe_backpressure(&mut sockets, false);
}
// a new worker thread is made and it's handle would be added
// to Accept
Some(WakerInterest::Worker(handle)) => {
drop(guard);
// maybe we want to recover from a backpressure.
self.maybe_backpressure(&mut sockets, false);
self.handles.push(handle);
}
// got timer interest and it's time to try register socket(s)
// again.
Some(WakerInterest::Timer) => {
drop(guard);
self.process_timer(&mut sockets)
}
Some(WakerInterest::Pause) => {
drop(guard);
sockets.iter_mut().for_each(|(_, info)| {
match self.deregister(info) {
Ok(_) => info!(
"Paused accepting connections on {}",
info.addr
),
Err(e) => {
error!("Can not deregister server socket {}", e)
}
}
});
}
Some(WakerInterest::Resume) => {
drop(guard);
sockets.iter_mut().for_each(|(token, info)| {
self.register_logged(token, info);
});
}
Some(WakerInterest::Stop) => {
return self.deregister_all(&mut sockets);
}
// waker queue is drained.
None => {
// Reset the WakerQueue before break so it does not grow
// infinitely.
WakerQueue::reset(&mut guard);
break 'waker;
}
CMD => {
if !self.process_cmd() {
return;
}
},
}
TIMER => self.process_timer(),
NOTIFY => self.backpressure(false),
_ => {
let token = usize::from(token);
self.accept(&mut sockets, token);
if token < DELTA {
continue;
}
self.accept(token - DELTA);
}
}
}
}
}
fn process_timer(&self, sockets: &mut Slab<ServerSocketInfo>) {
fn process_timer(&mut self) {
let now = Instant::now();
sockets.iter_mut().for_each(|(token, info)| {
// only the ServerSocketInfo have an associate timeout value was de registered.
for (token, info) in self.sockets.iter_mut() {
if let Some(inst) = info.timeout.take() {
if now > inst {
self.register_logged(token, info);
if let Err(err) = self.poll.register(
&info.sock,
mio::Token(token + DELTA),
mio::Ready::readable(),
mio::PollOpt::edge(),
) {
error!("Can not register server socket {}", err);
} else {
info!("Resume accepting connections on {}", info.addr);
}
} else {
info.timeout = Some(inst);
}
}
});
}
}
fn process_cmd(&mut self) -> bool {
loop {
match self.rx.try_recv() {
Ok(cmd) => match cmd {
Command::Pause => {
for (_, info) in self.sockets.iter_mut() {
if let Err(err) = self.poll.deregister(&info.sock) {
error!("Can not deregister server socket {}", err);
} else {
info!("Paused accepting connections on {}", info.addr);
}
}
}
Command::Resume => {
for (token, info) in self.sockets.iter() {
if let Err(err) = self.register(token, info) {
error!("Can not resume socket accept process: {}", err);
} else {
info!(
"Accepting connections on {} has been resumed",
info.addr
);
}
}
}
Command::Stop => {
for (_, info) in self.sockets.iter() {
let _ = self.poll.deregister(&info.sock);
}
return false;
}
Command::Worker(worker) => {
self.backpressure(false);
self.workers.push(worker);
}
},
Err(err) => match err {
sync_mpsc::TryRecvError::Empty => break,
sync_mpsc::TryRecvError::Disconnected => {
for (_, info) in self.sockets.iter() {
let _ = self.poll.deregister(&info.sock);
}
return false;
}
},
}
}
true
}
#[cfg(not(target_os = "windows"))]
fn register(&self, token: usize, info: &mut ServerSocketInfo) -> io::Result<()> {
self.poll
.registry()
.register(&mut info.lst, MioToken(token), Interest::READABLE)
fn register(&self, token: usize, info: &ServerSocketInfo) -> io::Result<()> {
self.poll.register(
&info.sock,
mio::Token(token + DELTA),
mio::Ready::readable(),
mio::PollOpt::edge(),
)
}
#[cfg(target_os = "windows")]
fn register(&self, token: usize, info: &mut ServerSocketInfo) -> io::Result<()> {
fn register(&self, token: usize, info: &ServerSocketInfo) -> io::Result<()> {
// On windows, calling register without deregister cause an error.
// See https://github.com/actix/actix-web/issues/905
// Calling reregister seems to fix the issue.
self.poll
.registry()
.register(&mut info.lst, mio::Token(token), Interest::READABLE)
.register(
&info.sock,
mio::Token(token + DELTA),
mio::Ready::readable(),
mio::PollOpt::edge(),
)
.or_else(|_| {
self.poll.registry().reregister(
&mut info.lst,
mio::Token(token),
Interest::READABLE,
self.poll.reregister(
&info.sock,
mio::Token(token + DELTA),
mio::Ready::readable(),
mio::PollOpt::edge(),
)
})
}
fn register_logged(&self, token: usize, info: &mut ServerSocketInfo) {
match self.register(token, info) {
Ok(_) => info!("Resume accepting connections on {}", info.addr),
Err(e) => error!("Can not register server socket {}", e),
}
}
fn deregister(&self, info: &mut ServerSocketInfo) -> io::Result<()> {
self.poll.registry().deregister(&mut info.lst)
}
fn deregister_all(&self, sockets: &mut Slab<ServerSocketInfo>) {
sockets.iter_mut().for_each(|(_, info)| {
info!("Accepting connections on {} has been paused", info.addr);
let _ = self.deregister(info);
});
}
fn maybe_backpressure(&mut self, sockets: &mut Slab<ServerSocketInfo>, on: bool) {
fn backpressure(&mut self, on: bool) {
if self.backpressure {
if !on {
self.backpressure = false;
for (token, info) in sockets.iter_mut() {
for (token, info) in self.sockets.iter() {
if info.timeout.is_some() {
// socket will attempt to re-register itself when its timeout completes
continue;
}
self.register_logged(token, info);
if let Err(err) = self.register(token, info) {
error!("Can not resume socket accept process: {}", err);
} else {
info!("Accepting connections on {} has been resumed", info.addr);
}
}
}
} else if on {
self.backpressure = true;
self.deregister_all(sockets);
for (_, info) in self.sockets.iter() {
let _ = self.poll.deregister(&info.sock);
info!("Accepting connections on {} has been paused", info.addr);
}
}
}
fn accept_one(&mut self, sockets: &mut Slab<ServerSocketInfo>, mut msg: Conn) {
fn accept_one(&mut self, mut msg: Conn) {
if self.backpressure {
while !self.handles.is_empty() {
match self.handles[self.next].send(msg) {
Ok(_) => {
self.set_next();
break;
}
while !self.workers.is_empty() {
match self.workers[self.next].send(msg) {
Ok(_) => (),
Err(tmp) => {
// worker lost contact and could be gone. a message is sent to
// `ServerBuilder` future to notify it a new worker should be made.
// after that remove the fault worker.
self.srv.worker_faulted(self.handles[self.next].idx);
self.srv.worker_faulted(self.workers[self.next].idx);
msg = tmp;
self.handles.swap_remove(self.next);
if self.handles.is_empty() {
self.workers.swap_remove(self.next);
if self.workers.is_empty() {
error!("No workers");
return;
} else if self.handles.len() <= self.next {
} else if self.workers.len() <= self.next {
self.next = 0;
}
continue;
}
}
self.next = (self.next + 1) % self.workers.len();
break;
}
} else {
let mut idx = 0;
while idx < self.handles.len() {
while idx < self.workers.len() {
idx += 1;
if self.handles[self.next].available() {
match self.handles[self.next].send(msg) {
if self.workers[self.next].available() {
match self.workers[self.next].send(msg) {
Ok(_) => {
self.set_next();
self.next = (self.next + 1) % self.workers.len();
return;
}
// worker lost contact and could be gone. a message is sent to
// `ServerBuilder` future to notify it a new worker should be made.
// after that remove the fault worker and enter backpressure if necessary.
Err(tmp) => {
self.srv.worker_faulted(self.handles[self.next].idx);
self.srv.worker_faulted(self.workers[self.next].idx);
msg = tmp;
self.handles.swap_remove(self.next);
if self.handles.is_empty() {
self.workers.swap_remove(self.next);
if self.workers.is_empty() {
error!("No workers");
self.maybe_backpressure(sockets, true);
self.backpressure(true);
return;
} else if self.handles.len() <= self.next {
} else if self.workers.len() <= self.next {
self.next = 0;
}
continue;
}
}
}
self.set_next();
self.next = (self.next + 1) % self.workers.len();
}
// enable backpressure
self.maybe_backpressure(sockets, true);
self.accept_one(sockets, msg);
self.backpressure(true);
self.accept_one(msg);
}
}
// set next worker handle that would accept work.
fn set_next(&mut self) {
self.next = (self.next + 1) % self.handles.len();
}
fn accept(&mut self, sockets: &mut Slab<ServerSocketInfo>, token: usize) {
fn accept(&mut self, token: usize) {
loop {
let msg = if let Some(info) = sockets.get_mut(token) {
match info.lst.accept() {
let msg = if let Some(info) = self.sockets.get_mut(token) {
match info.sock.accept() {
Ok(Some((io, addr))) => Conn {
io,
token: info.token,
@@ -388,22 +458,18 @@ impl Accept {
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return,
Err(ref e) if connection_error(e) => continue,
Err(e) => {
// deregister listener temporary
error!("Error accepting connection: {}", e);
if let Err(err) = self.deregister(info) {
if let Err(err) = self.poll.deregister(&info.sock) {
error!("Can not deregister server socket {}", err);
}
// sleep after error. write the timeout to socket info as later the poll
// would need it mark which socket and when it's listener should be
// registered.
// sleep after error
info.timeout = Some(Instant::now() + Duration::from_millis(500));
// after the sleep a Timer interest is sent to Accept Poll
let waker = self.waker.clone();
let r = self.timer.1.clone();
System::current().arbiter().send(Box::pin(async move {
sleep_until(Instant::now() + Duration::from_millis(510)).await;
waker.wake(WakerInterest::Timer);
delay_until(Instant::now() + Duration::from_millis(510)).await;
let _ = r.set_readiness(mio::Ready::readable());
}));
return;
}
@@ -412,7 +478,7 @@ impl Accept {
return;
};
self.accept_one(sockets, msg);
self.accept_one(msg);
}
}
}

View File

@@ -1,35 +1,36 @@
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use std::{io, mem};
use std::{io, mem, net};
use actix_rt::net::TcpStream;
use actix_rt::time::{sleep_until, Instant};
use actix_rt::time::{delay_until, Instant};
use actix_rt::{spawn, System};
use futures_channel::mpsc::{unbounded, UnboundedReceiver};
use futures_channel::oneshot;
use futures_util::future::ready;
use futures_util::stream::FuturesUnordered;
use futures_util::{future::Future, ready, stream::Stream, FutureExt, StreamExt};
use log::{error, info};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver};
use tokio::sync::oneshot;
use socket2::{Domain, Protocol, Socket, Type};
use crate::accept::AcceptLoop;
use crate::accept::{AcceptLoop, AcceptNotify, Command};
use crate::config::{ConfiguredService, ServiceConfig};
use crate::server::{Server, ServerCommand};
use crate::service::{InternalServiceFactory, ServiceFactory, StreamNewService};
use crate::signals::{Signal, Signals};
use crate::socket::{MioListener, StdSocketAddr, StdTcpListener, ToSocketAddrs};
use crate::socket::{MioTcpListener, MioTcpSocket};
use crate::waker_queue::{WakerInterest, WakerQueue};
use crate::worker::{self, Worker, WorkerAvailability, WorkerHandle};
use crate::{join_all, Token};
use crate::socket::StdListener;
use crate::worker::{self, Worker, WorkerAvailability, WorkerClient};
use crate::Token;
/// Server builder
pub struct ServerBuilder {
threads: usize,
token: Token,
backlog: u32,
handles: Vec<(usize, WorkerHandle)>,
backlog: i32,
workers: Vec<(usize, WorkerClient)>,
services: Vec<Box<dyn InternalServiceFactory>>,
sockets: Vec<(Token, String, MioListener)>,
sockets: Vec<(Token, String, StdListener)>,
accept: AcceptLoop,
exit: bool,
shutdown_timeout: Duration,
@@ -48,13 +49,13 @@ impl Default for ServerBuilder {
impl ServerBuilder {
/// Create new Server builder instance
pub fn new() -> ServerBuilder {
let (tx, rx) = unbounded_channel();
let (tx, rx) = unbounded();
let server = Server::new(tx);
ServerBuilder {
threads: num_cpus::get(),
token: Token::default(),
handles: Vec::new(),
token: Token(0),
workers: Vec::new(),
services: Vec::new(),
sockets: Vec::new(),
accept: AcceptLoop::new(server.clone()),
@@ -88,7 +89,7 @@ impl ServerBuilder {
/// Generally set in the 64-2048 range. Default value is 2048.
///
/// This method should be called before `bind()` method call.
pub fn backlog(mut self, num: u32) -> Self {
pub fn backlog(mut self, num: i32) -> Self {
self.backlog = num;
self
}
@@ -146,7 +147,7 @@ impl ServerBuilder {
for (name, lst) in cfg.services {
let token = self.token.next();
srv.stream(token, name.clone(), lst.local_addr()?);
self.sockets.push((token, name, MioListener::Tcp(lst)));
self.sockets.push((token, name, StdListener::Tcp(lst)));
}
self.services.push(Box::new(srv));
}
@@ -159,7 +160,7 @@ impl ServerBuilder {
pub fn bind<F, U, N: AsRef<str>>(mut self, name: N, addr: U, factory: F) -> io::Result<Self>
where
F: ServiceFactory<TcpStream>,
U: ToSocketAddrs,
U: net::ToSocketAddrs,
{
let sockets = bind_addr(addr, self.backlog)?;
@@ -172,12 +173,12 @@ impl ServerBuilder {
lst.local_addr()?,
));
self.sockets
.push((token, name.as_ref().to_string(), MioListener::Tcp(lst)));
.push((token, name.as_ref().to_string(), StdListener::Tcp(lst)));
}
Ok(self)
}
#[cfg(unix)]
#[cfg(all(unix))]
/// Add new unix domain service to the server.
pub fn bind_uds<F, U, N>(self, name: N, addr: U, factory: F) -> io::Result<Self>
where
@@ -185,6 +186,8 @@ impl ServerBuilder {
N: AsRef<str>,
U: AsRef<std::path::Path>,
{
use std::os::unix::net::UnixListener;
// The path must not exist when we try to bind.
// Try to remove it to avoid bind error.
if let Err(e) = std::fs::remove_file(addr.as_ref()) {
@@ -194,27 +197,26 @@ impl ServerBuilder {
}
}
let lst = crate::socket::StdUnixListener::bind(addr)?;
let lst = UnixListener::bind(addr)?;
self.listen_uds(name, lst, factory)
}
#[cfg(unix)]
#[cfg(all(unix))]
/// Add new unix domain service to the server.
/// Useful when running as a systemd service and
/// a socket FD can be acquired using the systemd crate.
pub fn listen_uds<F, N: AsRef<str>>(
mut self,
name: N,
lst: crate::socket::StdUnixListener,
lst: std::os::unix::net::UnixListener,
factory: F,
) -> io::Result<Self>
where
F: ServiceFactory<actix_rt::net::UnixStream>,
{
use std::net::{IpAddr, Ipv4Addr};
lst.set_nonblocking(true)?;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
let token = self.token.next();
let addr = StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
self.services.push(StreamNewService::create(
name.as_ref().to_string(),
token,
@@ -222,7 +224,7 @@ impl ServerBuilder {
addr,
));
self.sockets
.push((token, name.as_ref().to_string(), MioListener::from(lst)));
.push((token, name.as_ref().to_string(), StdListener::Uds(lst)));
Ok(self)
}
@@ -230,25 +232,21 @@ impl ServerBuilder {
pub fn listen<F, N: AsRef<str>>(
mut self,
name: N,
lst: StdTcpListener,
lst: net::TcpListener,
factory: F,
) -> io::Result<Self>
where
F: ServiceFactory<TcpStream>,
{
lst.set_nonblocking(true)?;
let addr = lst.local_addr()?;
let token = self.token.next();
self.services.push(StreamNewService::create(
name.as_ref().to_string(),
token,
factory,
addr,
lst.local_addr()?,
));
self.sockets
.push((token, name.as_ref().to_string(), MioListener::from(lst)));
.push((token, name.as_ref().to_string(), StdListener::Tcp(lst)));
Ok(self)
}
@@ -265,12 +263,12 @@ impl ServerBuilder {
info!("Starting {} workers", self.threads);
// start workers
let handles = (0..self.threads)
let workers = (0..self.threads)
.map(|idx| {
let handle = self.start_worker(idx, self.accept.waker_owned());
self.handles.push((idx, handle.clone()));
let worker = self.start_worker(idx, self.accept.get_notify());
self.workers.push((idx, worker.clone()));
handle
worker
})
.collect();
@@ -283,7 +281,7 @@ impl ServerBuilder {
.into_iter()
.map(|t| (t.0, t.2))
.collect(),
handles,
workers,
);
// handle signals
@@ -298,9 +296,10 @@ impl ServerBuilder {
}
}
fn start_worker(&self, idx: usize, waker: WakerQueue) -> WorkerHandle {
let avail = WorkerAvailability::new(waker);
let services = self.services.iter().map(|v| v.clone_factory()).collect();
fn start_worker(&self, idx: usize, notify: AcceptNotify) -> WorkerClient {
let avail = WorkerAvailability::new(notify);
let services: Vec<Box<dyn InternalServiceFactory>> =
self.services.iter().map(|v| v.clone_factory()).collect();
Worker::start(idx, services, avail, self.shutdown_timeout)
}
@@ -308,11 +307,11 @@ impl ServerBuilder {
fn handle_cmd(&mut self, item: ServerCommand) {
match item {
ServerCommand::Pause(tx) => {
self.accept.wake(WakerInterest::Pause);
self.accept.send(Command::Pause);
let _ = tx.send(());
}
ServerCommand::Resume(tx) => {
self.accept.wake(WakerInterest::Resume);
self.accept.send(Command::Resume);
let _ = tx.send(());
}
ServerCommand::Signal(sig) => {
@@ -356,41 +355,50 @@ impl ServerBuilder {
let exit = self.exit;
// stop accept thread
self.accept.wake(WakerInterest::Stop);
self.accept.send(Command::Stop);
let notify = std::mem::take(&mut self.notify);
// stop workers
if !self.handles.is_empty() && graceful {
let iter = self
.handles
.iter()
.map(move |worker| worker.1.stop(graceful))
.collect();
let fut = join_all(iter);
spawn(async move {
let _ = fut.await;
if let Some(tx) = completion {
let _ = tx.send(());
}
for tx in notify {
let _ = tx.send(());
}
if exit {
spawn(async {
sleep_until(Instant::now() + Duration::from_millis(300)).await;
System::current().stop();
});
}
})
if !self.workers.is_empty() && graceful {
spawn(
self.workers
.iter()
.map(move |worker| worker.1.stop(graceful))
.collect::<FuturesUnordered<_>>()
.collect::<Vec<_>>()
.then(move |_| {
if let Some(tx) = completion {
let _ = tx.send(());
}
for tx in notify {
let _ = tx.send(());
}
if exit {
spawn(
async {
delay_until(
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(async {
sleep_until(Instant::now() + Duration::from_millis(300)).await;
System::current().stop();
});
spawn(
delay_until(Instant::now() + Duration::from_millis(300)).then(
|_| {
System::current().stop();
ready(())
},
),
);
}
if let Some(tx) = completion {
let _ = tx.send(());
@@ -402,9 +410,9 @@ impl ServerBuilder {
}
ServerCommand::WorkerFaulted(idx) => {
let mut found = false;
for i in 0..self.handles.len() {
if self.handles[i].0 == idx {
self.handles.swap_remove(i);
for i in 0..self.workers.len() {
if self.workers[i].0 == idx {
self.workers.swap_remove(i);
found = true;
break;
}
@@ -413,10 +421,10 @@ impl ServerBuilder {
if found {
error!("Worker has died {:?}, restarting", idx);
let mut new_idx = self.handles.len();
let mut new_idx = self.workers.len();
'found: loop {
for i in 0..self.handles.len() {
if self.handles[i].0 == new_idx {
for i in 0..self.workers.len() {
if self.workers[i].0 == new_idx {
new_idx += 1;
continue 'found;
}
@@ -424,9 +432,9 @@ impl ServerBuilder {
break;
}
let handle = self.start_worker(new_idx, self.accept.waker_owned());
self.handles.push((new_idx, handle.clone()));
self.accept.wake(WakerInterest::Worker(handle));
let worker = self.start_worker(new_idx, self.accept.get_notify());
self.workers.push((new_idx, worker.clone()));
self.accept.send(Command::Worker(worker));
}
}
}
@@ -438,18 +446,20 @@ impl Future for ServerBuilder {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
match Pin::new(&mut self.cmd).poll_recv(cx) {
Poll::Ready(Some(it)) => self.as_mut().get_mut().handle_cmd(it),
_ => return Poll::Pending,
match ready!(Pin::new(&mut self.cmd).poll_next(cx)) {
Some(it) => self.as_mut().get_mut().handle_cmd(it),
None => {
return Poll::Pending;
}
}
}
}
}
pub(super) fn bind_addr<S: ToSocketAddrs>(
pub(super) fn bind_addr<S: net::ToSocketAddrs>(
addr: S,
backlog: u32,
) -> io::Result<Vec<MioTcpListener>> {
backlog: i32,
) -> io::Result<Vec<net::TcpListener>> {
let mut err = None;
let mut succ = false;
let mut sockets = Vec::new();
@@ -477,13 +487,14 @@ pub(super) fn bind_addr<S: ToSocketAddrs>(
}
}
fn create_tcp_listener(addr: StdSocketAddr, backlog: u32) -> io::Result<MioTcpListener> {
let socket = match addr {
StdSocketAddr::V4(_) => MioTcpSocket::new_v4()?,
StdSocketAddr::V6(_) => MioTcpSocket::new_v6()?,
fn create_tcp_listener(addr: net::SocketAddr, backlog: i32) -> io::Result<net::TcpListener> {
let domain = match addr {
net::SocketAddr::V4(_) => Domain::ipv4(),
net::SocketAddr::V6(_) => Domain::ipv6(),
};
socket.set_reuseaddr(true)?;
socket.bind(addr)?;
socket.listen(backlog)
let socket = Socket::new(domain, Type::stream(), Some(Protocol::tcp()))?;
socket.set_reuse_address(true)?;
socket.bind(&addr.into())?;
socket.listen(backlog)?;
Ok(socket.into_tcp_listener())
}

View File

@@ -1,30 +1,26 @@
use std::collections::HashMap;
use std::future::Future;
use std::{fmt, io};
use std::{fmt, io, net};
use actix_rt::net::TcpStream;
use actix_service::{
fn_service, IntoServiceFactory as IntoBaseServiceFactory,
ServiceFactory as BaseServiceFactory,
};
use actix_service as actix;
use actix_utils::counter::CounterGuard;
use futures_core::future::LocalBoxFuture;
use futures_util::future::{ok, Future, FutureExt, LocalBoxFuture};
use log::error;
use crate::builder::bind_addr;
use crate::service::{BoxedServerService, InternalServiceFactory, StreamService};
use crate::socket::{MioStream, MioTcpListener, StdSocketAddr, StdTcpListener, ToSocketAddrs};
use crate::{ready, Token};
use super::builder::bind_addr;
use super::service::{BoxedServerService, InternalServiceFactory, StreamService};
use super::Token;
use crate::socket::StdStream;
pub struct ServiceConfig {
pub(crate) services: Vec<(String, MioTcpListener)>,
pub(crate) services: Vec<(String, net::TcpListener)>,
pub(crate) apply: Option<Box<dyn ServiceRuntimeConfiguration>>,
pub(crate) threads: usize,
pub(crate) backlog: u32,
pub(crate) backlog: i32,
}
impl ServiceConfig {
pub(super) fn new(threads: usize, backlog: u32) -> ServiceConfig {
pub(super) fn new(threads: usize, backlog: i32) -> ServiceConfig {
ServiceConfig {
threads,
backlog,
@@ -44,20 +40,24 @@ impl ServiceConfig {
/// Add new service to server
pub fn bind<U, N: AsRef<str>>(&mut self, name: N, addr: U) -> io::Result<&mut Self>
where
U: ToSocketAddrs,
U: net::ToSocketAddrs,
{
let sockets = bind_addr(addr, self.backlog)?;
for lst in sockets {
self._listen(name.as_ref(), lst);
self.listen(name.as_ref(), lst);
}
Ok(self)
}
/// Add new service to server
pub fn listen<N: AsRef<str>>(&mut self, name: N, lst: StdTcpListener) -> &mut Self {
self._listen(name, MioTcpListener::from_std(lst))
pub fn listen<N: AsRef<str>>(&mut self, name: N, lst: net::TcpListener) -> &mut Self {
if self.apply.is_none() {
self.apply = Some(Box::new(not_configured));
}
self.services.push((name.as_ref().to_string(), lst));
self
}
/// Register service configuration function. This function get called
@@ -69,19 +69,11 @@ impl ServiceConfig {
self.apply = Some(Box::new(f));
Ok(())
}
fn _listen<N: AsRef<str>>(&mut self, name: N, lst: MioTcpListener) -> &mut Self {
if self.apply.is_none() {
self.apply = Some(Box::new(not_configured));
}
self.services.push((name.as_ref().to_string(), lst));
self
}
}
pub(super) struct ConfiguredService {
rt: Box<dyn ServiceRuntimeConfiguration>,
names: HashMap<Token, (String, StdSocketAddr)>,
names: HashMap<Token, (String, net::SocketAddr)>,
topics: HashMap<String, Token>,
services: Vec<Token>,
}
@@ -96,7 +88,7 @@ impl ConfiguredService {
}
}
pub(super) fn stream(&mut self, token: Token, name: String, addr: StdSocketAddr) {
pub(super) fn stream(&mut self, token: Token, name: String, addr: net::SocketAddr) {
self.names.insert(token, (name.clone(), addr));
self.topics.insert(name, token);
self.services.push(token);
@@ -126,7 +118,7 @@ impl InternalServiceFactory for ConfiguredService {
let tokens = self.services.clone();
// construct services
Box::pin(async move {
async move {
let mut services = rt.services;
// TODO: Proper error handling here
for f in rt.onstart.into_iter() {
@@ -149,15 +141,18 @@ impl InternalServiceFactory for ConfiguredService {
let name = names.remove(&token).unwrap().0;
res.push((
token,
Box::new(StreamService::new(fn_service(move |_: TcpStream| {
error!("Service {:?} is not configured", name);
ready::<Result<_, ()>>(Ok(()))
}))),
Box::new(StreamService::new(actix::fn_service(
move |_: TcpStream| {
error!("Service {:?} is not configured", name);
ok::<_, ()>(())
},
))),
));
};
}
Ok(res)
})
}
.boxed_local()
}
}
@@ -213,8 +208,8 @@ impl ServiceRuntime {
/// *ServiceConfig::bind()* or *ServiceConfig::listen()* methods.
pub fn service<T, F>(&mut self, name: &str, service: F)
where
F: IntoBaseServiceFactory<T, TcpStream>,
T: BaseServiceFactory<TcpStream, Config = ()> + 'static,
F: actix::IntoServiceFactory<T>,
T: actix::ServiceFactory<Config = (), Request = TcpStream> + 'static,
T::Future: 'static,
T::Service: 'static,
T::InitError: fmt::Debug,
@@ -237,13 +232,13 @@ impl ServiceRuntime {
where
F: Future<Output = ()> + 'static,
{
self.onstart.push(Box::pin(fut))
self.onstart.push(fut.boxed_local())
}
}
type BoxedNewService = Box<
dyn BaseServiceFactory<
(Option<CounterGuard>, MioStream),
dyn actix::ServiceFactory<
Request = (Option<CounterGuard>, StdStream),
Response = (),
Error = (),
InitError = (),
@@ -257,14 +252,15 @@ struct ServiceFactory<T> {
inner: T,
}
impl<T> BaseServiceFactory<(Option<CounterGuard>, MioStream)> for ServiceFactory<T>
impl<T> actix::ServiceFactory for ServiceFactory<T>
where
T: BaseServiceFactory<TcpStream, Config = ()>,
T: actix::ServiceFactory<Config = (), Request = TcpStream>,
T::Future: 'static,
T::Service: 'static,
T::Error: 'static,
T::InitError: fmt::Debug + 'static,
{
type Request = (Option<CounterGuard>, StdStream);
type Response = ();
type Error = ();
type Config = ();
@@ -274,7 +270,7 @@ where
fn new_service(&self, _: ()) -> Self::Future {
let fut = self.inner.new_service(());
Box::pin(async move {
async move {
match fut.await {
Ok(s) => Ok(Box::new(StreamService::new(s)) as BoxedServerService),
Err(e) => {
@@ -282,6 +278,7 @@ where
Err(())
}
}
})
}
.boxed_local()
}
}

View File

@@ -11,38 +11,21 @@ mod server;
mod service;
mod signals;
mod socket;
mod test_server;
mod waker_queue;
mod worker;
pub use self::builder::ServerBuilder;
pub use self::config::{ServiceConfig, ServiceRuntime};
pub use self::server::Server;
pub use self::service::ServiceFactory;
pub use self::test_server::TestServer;
#[doc(hidden)]
pub use self::socket::FromStream;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
/// Socket ID token
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct Token(usize);
impl Default for Token {
fn default() -> Self {
Self::new()
}
}
impl Token {
fn new() -> Self {
Self(0)
}
pub(crate) fn next(&mut self) -> Token {
let token = Token(self.0);
self.0 += 1;
@@ -54,90 +37,3 @@ impl Token {
pub fn new() -> ServerBuilder {
ServerBuilder::default()
}
// temporary Ready type for std::future::{ready, Ready}; Can be removed when MSRV surpass 1.48
#[doc(hidden)]
pub struct Ready<T>(Option<T>);
pub(crate) fn ready<T>(t: T) -> Ready<T> {
Ready(Some(t))
}
impl<T> Unpin for Ready<T> {}
impl<T> Future for Ready<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(self.get_mut().0.take().unwrap())
}
}
// a poor man's join future. joined future is only used when starting/stopping the server.
// pin_project and pinned futures are overkill for this task.
pub(crate) struct JoinAll<T> {
fut: Vec<JoinFuture<T>>,
}
pub(crate) fn join_all<T>(fut: Vec<impl Future<Output = T> + 'static>) -> JoinAll<T> {
let fut = fut
.into_iter()
.map(|f| JoinFuture::Future(Box::pin(f)))
.collect();
JoinAll { fut }
}
enum JoinFuture<T> {
Future(Pin<Box<dyn Future<Output = T>>>),
Result(Option<T>),
}
impl<T> Unpin for JoinAll<T> {}
impl<T> Future for JoinAll<T> {
type Output = Vec<T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut ready = true;
let this = self.get_mut();
for fut in this.fut.iter_mut() {
if let JoinFuture::Future(f) = fut {
match f.as_mut().poll(cx) {
Poll::Ready(t) => {
*fut = JoinFuture::Result(Some(t));
}
Poll::Pending => ready = false,
}
}
}
if ready {
let mut res = Vec::new();
for fut in this.fut.iter_mut() {
if let JoinFuture::Result(f) = fut {
res.push(f.take().unwrap());
}
}
Poll::Ready(res)
} else {
Poll::Pending
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[actix_rt::test]
async fn test_join_all() {
let futs = vec![ready(Ok(1)), ready(Err(3)), ready(Ok(9))];
let mut res = join_all(futs).await.into_iter();
assert_eq!(Ok(1), res.next().unwrap());
assert_eq!(Err(3), res.next().unwrap());
assert_eq!(Ok(9), res.next().unwrap());
}
}

View File

@@ -3,8 +3,9 @@ use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::sync::mpsc::UnboundedSender;
use tokio::sync::oneshot;
use futures_channel::mpsc::UnboundedSender;
use futures_channel::oneshot;
use futures_util::FutureExt;
use crate::builder::ServerBuilder;
use crate::signals::Signal;
@@ -41,11 +42,11 @@ impl Server {
}
pub(crate) fn signal(&self, sig: Signal) {
let _ = self.0.send(ServerCommand::Signal(sig));
let _ = self.0.unbounded_send(ServerCommand::Signal(sig));
}
pub(crate) fn worker_faulted(&self, idx: usize) {
let _ = self.0.send(ServerCommand::WorkerFaulted(idx));
let _ = self.0.unbounded_send(ServerCommand::WorkerFaulted(idx));
}
/// Pause accepting incoming connections
@@ -54,19 +55,15 @@ impl Server {
/// All opened connection remains active.
pub fn pause(&self) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel();
let _ = self.0.send(ServerCommand::Pause(tx));
async {
let _ = rx.await;
}
let _ = self.0.unbounded_send(ServerCommand::Pause(tx));
rx.map(|_| ())
}
/// Resume accepting incoming connections
pub fn resume(&self) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel();
let _ = self.0.send(ServerCommand::Resume(tx));
async {
let _ = rx.await;
}
let _ = self.0.unbounded_send(ServerCommand::Resume(tx));
rx.map(|_| ())
}
/// Stop incoming connection processing, stop all workers and exit.
@@ -74,13 +71,11 @@ impl Server {
/// If server starts with `spawn()` method, then spawned thread get terminated.
pub fn stop(&self, graceful: bool) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel();
let _ = self.0.send(ServerCommand::Stop {
let _ = self.0.unbounded_send(ServerCommand::Stop {
graceful,
completion: Some(tx),
});
async {
let _ = rx.await;
}
rx.map(|_| ())
}
}
@@ -98,7 +93,7 @@ impl Future for Server {
if this.1.is_none() {
let (tx, rx) = oneshot::channel();
if this.0.send(ServerCommand::Notify(tx)).is_err() {
if this.0.unbounded_send(ServerCommand::Notify(tx)).is_err() {
return Poll::Ready(Ok(()));
}
this.1 = Some(rx);
@@ -106,7 +101,8 @@ impl Future for Server {
match Pin::new(this.1.as_mut().unwrap()).poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(_) => Poll::Ready(Ok(())),
Poll::Ready(Ok(_)) => Poll::Ready(Ok(())),
Poll::Ready(Err(_)) => Poll::Ready(Ok(())),
}
}
}

View File

@@ -2,16 +2,18 @@ use std::marker::PhantomData;
use std::net::SocketAddr;
use std::task::{Context, Poll};
use actix_service::{Service, ServiceFactory as BaseServiceFactory};
use actix_rt::spawn;
use actix_service::{self as actix, Service, ServiceFactory as ActixServiceFactory};
use actix_utils::counter::CounterGuard;
use futures_core::future::LocalBoxFuture;
use futures_util::future::{err, ok, LocalBoxFuture, Ready};
use futures_util::{FutureExt, TryFutureExt};
use log::error;
use crate::socket::{FromStream, MioStream};
use crate::{ready, Ready, Token};
use super::Token;
use crate::socket::{FromStream, StdStream};
pub trait ServiceFactory<Stream: FromStream>: Send + Clone + 'static {
type Factory: BaseServiceFactory<Stream, Config = ()>;
type Factory: actix::ServiceFactory<Config = (), Request = Stream>;
fn create(&self) -> Self::Factory;
}
@@ -26,34 +28,31 @@ pub(crate) trait InternalServiceFactory: Send {
pub(crate) type BoxedServerService = Box<
dyn Service<
(Option<CounterGuard>, MioStream),
Request = (Option<CounterGuard>, StdStream),
Response = (),
Error = (),
Future = Ready<Result<(), ()>>,
>,
>;
pub(crate) struct StreamService<S, I> {
service: S,
_phantom: PhantomData<I>,
pub(crate) struct StreamService<T> {
service: T,
}
impl<S, I> StreamService<S, I> {
pub(crate) fn new(service: S) -> Self {
StreamService {
service,
_phantom: PhantomData,
}
impl<T> StreamService<T> {
pub(crate) fn new(service: T) -> Self {
StreamService { service }
}
}
impl<S, I> Service<(Option<CounterGuard>, MioStream)> for StreamService<S, I>
impl<T, I> Service for StreamService<T>
where
S: Service<I>,
S::Future: 'static,
S::Error: 'static,
T: Service<Request = I>,
T::Future: 'static,
T::Error: 'static,
I: FromStream,
{
type Request = (Option<CounterGuard>, StdStream);
type Response = ();
type Error = ();
type Future = Ready<Result<(), ()>>;
@@ -62,21 +61,21 @@ where
self.service.poll_ready(ctx).map_err(|_| ())
}
fn call(&mut self, (guard, req): (Option<CounterGuard>, MioStream)) -> Self::Future {
ready(match FromStream::from_mio(req) {
fn call(&mut self, (guard, req): (Option<CounterGuard>, StdStream)) -> Self::Future {
match FromStream::from_stdstream(req) {
Ok(stream) => {
let f = self.service.call(stream);
actix_rt::spawn(async move {
spawn(async move {
let _ = f.await;
drop(guard);
});
Ok(())
ok(())
}
Err(e) => {
error!("Can not convert to an async tcp stream: {}", e);
Err(())
err(())
}
})
}
}
}
@@ -130,23 +129,22 @@ where
fn create(&self) -> LocalBoxFuture<'static, Result<Vec<(Token, BoxedServerService)>, ()>> {
let token = self.token;
let fut = self.inner.create().new_service(());
Box::pin(async move {
match fut.await {
Ok(inner) => {
let service = Box::new(StreamService::new(inner)) as _;
Ok(vec![(token, service)])
}
Err(_) => Err(()),
}
})
self.inner
.create()
.new_service(())
.map_err(|_| ())
.map_ok(move |inner| {
let service: BoxedServerService = Box::new(StreamService::new(inner));
vec![(token, service)]
})
.boxed_local()
}
}
impl<F, T, I> ServiceFactory<I> for F
where
F: Fn() -> T + Send + Clone + 'static,
T: BaseServiceFactory<I, Config = ()>,
T: actix::ServiceFactory<Config = (), Request = I>,
I: FromStream,
{
type Factory = T;

View File

@@ -2,7 +2,7 @@ use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_core::future::LocalBoxFuture;
use futures_util::future::lazy;
use crate::server::Server;
@@ -23,51 +23,48 @@ pub(crate) enum Signal {
pub(crate) struct Signals {
srv: Server,
#[cfg(not(unix))]
signals: LocalBoxFuture<'static, std::io::Result<()>>,
stream: Pin<Box<dyn Future<Output = std::io::Result<()>>>>,
#[cfg(unix)]
signals: Vec<(Signal, LocalBoxFuture<'static, ()>)>,
streams: Vec<(Signal, actix_rt::signal::unix::Signal)>,
}
impl Signals {
pub(crate) fn start(srv: Server) {
#[cfg(not(unix))]
{
actix_rt::spawn(Signals {
srv,
signals: Box::pin(actix_rt::signal::ctrl_c()),
});
}
#[cfg(unix)]
{
use actix_rt::signal::unix;
let sig_map = [
(unix::SignalKind::interrupt(), Signal::Int),
(unix::SignalKind::hangup(), Signal::Hup),
(unix::SignalKind::terminate(), Signal::Term),
(unix::SignalKind::quit(), Signal::Quit),
];
let mut signals = Vec::new();
for (kind, sig) in sig_map.iter() {
match unix::signal(*kind) {
Ok(mut stream) => {
let fut = Box::pin(async move {
let _ = stream.recv().await;
}) as _;
signals.push((*sig, fut));
}
Err(e) => log::error!(
"Can not initialize stream handler for {:?} err: {}",
sig,
e
),
}
actix_rt::spawn(lazy(|_| {
#[cfg(not(unix))]
{
actix_rt::spawn(Signals {
srv,
stream: Box::pin(actix_rt::signal::ctrl_c()),
});
}
#[cfg(unix)]
{
use actix_rt::signal::unix;
actix_rt::spawn(Signals { srv, signals });
}
let mut streams = Vec::new();
let sig_map = [
(unix::SignalKind::interrupt(), Signal::Int),
(unix::SignalKind::hangup(), Signal::Hup),
(unix::SignalKind::terminate(), Signal::Term),
(unix::SignalKind::quit(), Signal::Quit),
];
for (kind, sig) in sig_map.iter() {
match unix::signal(*kind) {
Ok(stream) => streams.push((*sig, stream)),
Err(e) => log::error!(
"Can not initialize stream handler for {:?} err: {}",
sig,
e
),
}
}
actix_rt::spawn(Signals { srv, streams })
}
}));
}
}
@@ -76,20 +73,25 @@ impl Future for Signals {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
#[cfg(not(unix))]
match self.signals.as_mut().poll(cx) {
match Pin::new(&mut self.stream).poll(cx) {
Poll::Ready(_) => {
self.srv.signal(Signal::Int);
Poll::Ready(())
}
Poll::Pending => Poll::Pending,
Poll::Pending => return Poll::Pending,
}
#[cfg(unix)]
{
for (sig, fut) in self.signals.iter_mut() {
if fut.as_mut().poll(cx).is_ready() {
let sig = *sig;
self.srv.signal(sig);
return Poll::Ready(());
for idx in 0..self.streams.len() {
loop {
match self.streams[idx].1.poll_recv(cx) {
Poll::Ready(None) => return Poll::Ready(()),
Poll::Pending => break,
Poll::Ready(Some(_)) => {
let sig = self.streams[idx].0;
self.srv.signal(sig);
}
}
}
}
Poll::Pending

View File

@@ -1,91 +1,135 @@
pub(crate) use std::net::{
SocketAddr as StdSocketAddr, TcpListener as StdTcpListener, ToSocketAddrs,
};
pub(crate) use mio::net::{TcpListener as MioTcpListener, TcpSocket as MioTcpSocket};
#[cfg(unix)]
pub(crate) use {
mio::net::UnixListener as MioUnixListener,
std::os::unix::net::UnixListener as StdUnixListener,
};
use std::{fmt, io};
use std::{fmt, io, net};
use actix_codec::{AsyncRead, AsyncWrite};
use actix_rt::net::TcpStream;
use mio::event::Source;
use mio::net::TcpStream as MioTcpStream;
use mio::{Interest, Registry, Token};
#[cfg(windows)]
use std::os::windows::io::{FromRawSocket, IntoRawSocket};
#[cfg(unix)]
use {
actix_rt::net::UnixStream,
mio::net::{SocketAddr as MioSocketAddr, UnixStream as MioUnixStream},
std::os::unix::io::{FromRawFd, IntoRawFd},
};
pub(crate) enum MioListener {
Tcp(MioTcpListener),
#[cfg(unix)]
Uds(MioUnixListener),
pub(crate) enum StdListener {
Tcp(net::TcpListener),
#[cfg(all(unix))]
Uds(std::os::unix::net::UnixListener),
}
impl MioListener {
pub(crate) enum SocketAddr {
Tcp(net::SocketAddr),
#[cfg(all(unix))]
Uds(std::os::unix::net::SocketAddr),
}
impl fmt::Display for SocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SocketAddr::Tcp(ref addr) => write!(f, "{}", addr),
#[cfg(all(unix))]
SocketAddr::Uds(ref addr) => write!(f, "{:?}", addr),
}
}
}
impl fmt::Debug for SocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SocketAddr::Tcp(ref addr) => write!(f, "{:?}", addr),
#[cfg(all(unix))]
SocketAddr::Uds(ref addr) => write!(f, "{:?}", addr),
}
}
}
impl fmt::Display for StdListener {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
StdListener::Tcp(ref lst) => write!(f, "{}", lst.local_addr().ok().unwrap()),
#[cfg(all(unix))]
StdListener::Uds(ref lst) => write!(f, "{:?}", lst.local_addr().ok().unwrap()),
}
}
}
impl StdListener {
pub(crate) fn local_addr(&self) -> SocketAddr {
match *self {
MioListener::Tcp(ref lst) => SocketAddr::Tcp(lst.local_addr().unwrap()),
#[cfg(unix)]
MioListener::Uds(ref lst) => SocketAddr::Uds(lst.local_addr().unwrap()),
match self {
StdListener::Tcp(lst) => SocketAddr::Tcp(lst.local_addr().unwrap()),
#[cfg(all(unix))]
StdListener::Uds(lst) => SocketAddr::Uds(lst.local_addr().unwrap()),
}
}
pub(crate) fn accept(&self) -> io::Result<Option<(MioStream, SocketAddr)>> {
match *self {
MioListener::Tcp(ref lst) => lst
.accept()
.map(|(stream, addr)| Some((MioStream::Tcp(stream), SocketAddr::Tcp(addr)))),
#[cfg(unix)]
MioListener::Uds(ref lst) => lst
.accept()
.map(|(stream, addr)| Some((MioStream::Uds(stream), SocketAddr::Uds(addr)))),
pub(crate) fn into_listener(self) -> SocketListener {
match self {
StdListener::Tcp(lst) => SocketListener::Tcp(
mio::net::TcpListener::from_std(lst)
.expect("Can not create mio::net::TcpListener"),
),
#[cfg(all(unix))]
StdListener::Uds(lst) => SocketListener::Uds(
mio_uds::UnixListener::from_listener(lst)
.expect("Can not create mio_uds::UnixListener"),
),
}
}
}
impl Source for MioListener {
#[derive(Debug)]
pub enum StdStream {
Tcp(std::net::TcpStream),
#[cfg(all(unix))]
Uds(std::os::unix::net::UnixStream),
}
pub(crate) enum SocketListener {
Tcp(mio::net::TcpListener),
#[cfg(all(unix))]
Uds(mio_uds::UnixListener),
}
impl SocketListener {
pub(crate) fn accept(&self) -> io::Result<Option<(StdStream, SocketAddr)>> {
match *self {
SocketListener::Tcp(ref lst) => lst
.accept_std()
.map(|(stream, addr)| Some((StdStream::Tcp(stream), SocketAddr::Tcp(addr)))),
#[cfg(all(unix))]
SocketListener::Uds(ref lst) => lst.accept_std().map(|res| {
res.map(|(stream, addr)| (StdStream::Uds(stream), SocketAddr::Uds(addr)))
}),
}
}
}
impl mio::Evented for SocketListener {
fn register(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
&self,
poll: &mio::Poll,
token: mio::Token,
interest: mio::Ready,
opts: mio::PollOpt,
) -> io::Result<()> {
match *self {
MioListener::Tcp(ref mut lst) => lst.register(registry, token, interests),
#[cfg(unix)]
MioListener::Uds(ref mut lst) => lst.register(registry, token, interests),
SocketListener::Tcp(ref lst) => lst.register(poll, token, interest, opts),
#[cfg(all(unix))]
SocketListener::Uds(ref lst) => lst.register(poll, token, interest, opts),
}
}
fn reregister(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
&self,
poll: &mio::Poll,
token: mio::Token,
interest: mio::Ready,
opts: mio::PollOpt,
) -> io::Result<()> {
match *self {
MioListener::Tcp(ref mut lst) => lst.reregister(registry, token, interests),
#[cfg(unix)]
MioListener::Uds(ref mut lst) => lst.reregister(registry, token, interests),
SocketListener::Tcp(ref lst) => lst.reregister(poll, token, interest, opts),
#[cfg(all(unix))]
SocketListener::Uds(ref lst) => lst.reregister(poll, token, interest, opts),
}
}
fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
match *self {
MioListener::Tcp(ref mut lst) => lst.deregister(registry),
#[cfg(unix)]
MioListener::Uds(ref mut lst) => {
let res = lst.deregister(registry);
SocketListener::Tcp(ref lst) => lst.deregister(poll),
#[cfg(all(unix))]
SocketListener::Uds(ref lst) => {
let res = lst.deregister(poll);
// cleanup file path
if let Ok(addr) = lst.local_addr() {
@@ -99,156 +143,28 @@ impl Source for MioListener {
}
}
impl From<StdTcpListener> for MioListener {
fn from(lst: StdTcpListener) -> Self {
MioListener::Tcp(MioTcpListener::from_std(lst))
}
pub trait FromStream: AsyncRead + AsyncWrite + Sized {
fn from_stdstream(sock: StdStream) -> io::Result<Self>;
}
#[cfg(unix)]
impl From<StdUnixListener> for MioListener {
fn from(lst: StdUnixListener) -> Self {
MioListener::Uds(MioUnixListener::from_std(lst))
}
}
impl fmt::Debug for MioListener {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
MioListener::Tcp(ref lst) => write!(f, "{:?}", lst),
#[cfg(all(unix))]
MioListener::Uds(ref lst) => write!(f, "{:?}", lst),
}
}
}
impl fmt::Display for MioListener {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
MioListener::Tcp(ref lst) => write!(f, "{}", lst.local_addr().ok().unwrap()),
#[cfg(unix)]
MioListener::Uds(ref lst) => write!(f, "{:?}", lst.local_addr().ok().unwrap()),
}
}
}
pub(crate) enum SocketAddr {
Tcp(StdSocketAddr),
#[cfg(unix)]
Uds(MioSocketAddr),
}
impl fmt::Display for SocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SocketAddr::Tcp(ref addr) => write!(f, "{}", addr),
#[cfg(unix)]
SocketAddr::Uds(ref addr) => write!(f, "{:?}", addr),
}
}
}
impl fmt::Debug for SocketAddr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SocketAddr::Tcp(ref addr) => write!(f, "{:?}", addr),
#[cfg(unix)]
SocketAddr::Uds(ref addr) => write!(f, "{:?}", addr),
}
}
}
#[derive(Debug)]
pub enum MioStream {
Tcp(MioTcpStream),
#[cfg(unix)]
Uds(MioUnixStream),
}
/// helper trait for converting mio stream to tokio stream.
pub trait FromStream: Sized {
fn from_mio(sock: MioStream) -> io::Result<Self>;
}
// FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream
#[cfg(unix)]
impl FromStream for TcpStream {
fn from_mio(sock: MioStream) -> io::Result<Self> {
fn from_stdstream(sock: StdStream) -> io::Result<Self> {
match sock {
MioStream::Tcp(mio) => {
let raw = IntoRawFd::into_raw_fd(mio);
// SAFETY: This is a in place conversion from mio stream to tokio stream.
TcpStream::from_std(unsafe { FromRawFd::from_raw_fd(raw) })
}
MioStream::Uds(_) => {
StdStream::Tcp(stream) => TcpStream::from_std(stream),
#[cfg(all(unix))]
StdStream::Uds(_) => {
panic!("Should not happen, bug in server impl");
}
}
}
}
// FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream
#[cfg(windows)]
impl FromStream for TcpStream {
fn from_mio(sock: MioStream) -> io::Result<Self> {
#[cfg(all(unix))]
impl FromStream for actix_rt::net::UnixStream {
fn from_stdstream(sock: StdStream) -> io::Result<Self> {
match sock {
MioStream::Tcp(mio) => {
let raw = IntoRawSocket::into_raw_socket(mio);
// SAFETY: This is a in place conversion from mio stream to tokio stream.
TcpStream::from_std(unsafe { FromRawSocket::from_raw_socket(raw) })
}
}
}
}
// FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream
#[cfg(unix)]
impl FromStream for UnixStream {
fn from_mio(sock: MioStream) -> io::Result<Self> {
match sock {
MioStream::Tcp(_) => panic!("Should not happen, bug in server impl"),
MioStream::Uds(mio) => {
let raw = IntoRawFd::into_raw_fd(mio);
// SAFETY: This is a in place conversion from mio stream to tokio stream.
UnixStream::from_std(unsafe { FromRawFd::from_raw_fd(raw) })
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn socket_addr() {
let addr = SocketAddr::Tcp("127.0.0.1:8080".parse().unwrap());
assert!(format!("{:?}", addr).contains("127.0.0.1:8080"));
assert_eq!(format!("{}", addr), "127.0.0.1:8080");
let addr: StdSocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = MioTcpSocket::new_v4().unwrap();
socket.set_reuseaddr(true).unwrap();
socket.bind(addr).unwrap();
let tcp = socket.listen(128).unwrap();
let lst = MioListener::Tcp(tcp);
assert!(format!("{:?}", lst).contains("TcpListener"));
assert!(format!("{}", lst).contains("127.0.0.1"));
}
#[test]
#[cfg(unix)]
fn uds() {
let _ = std::fs::remove_file("/tmp/sock.xxxxx");
if let Ok(socket) = MioUnixListener::bind("/tmp/sock.xxxxx") {
let addr = socket.local_addr().expect("Couldn't get local address");
let a = SocketAddr::Uds(addr);
assert!(format!("{:?}", a).contains("/tmp/sock.xxxxx"));
assert!(format!("{}", a).contains("/tmp/sock.xxxxx"));
let lst = MioListener::Uds(socket);
assert!(format!("{:?}", lst).contains("/tmp/sock.xxxxx"));
assert!(format!("{}", lst).contains("/tmp/sock.xxxxx"));
StdStream::Tcp(_) => panic!("Should not happen, bug in server impl"),
StdStream::Uds(stream) => actix_rt::net::UnixStream::from_std(stream),
}
}
}

View File

@@ -1,89 +0,0 @@
use std::{
collections::VecDeque,
ops::Deref,
sync::{Arc, Mutex, MutexGuard},
};
use mio::{Registry, Token as MioToken, Waker};
use crate::worker::WorkerHandle;
/// waker token for `mio::Poll` instance
pub(crate) const WAKER_TOKEN: MioToken = MioToken(usize::MAX);
/// `mio::Waker` with a queue for waking up the `Accept`'s `Poll` and contains the `WakerInterest`
/// the `Poll` would want to look into.
pub(crate) struct WakerQueue(Arc<(Waker, Mutex<VecDeque<WakerInterest>>)>);
impl Clone for WakerQueue {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl Deref for WakerQueue {
type Target = (Waker, Mutex<VecDeque<WakerInterest>>);
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl WakerQueue {
/// construct a waker queue with given `Poll`'s `Registry` and capacity.
///
/// A fixed `WAKER_TOKEN` is used to identify the wake interest and the `Poll` needs to match
/// event's token for it to properly handle `WakerInterest`.
pub(crate) fn new(registry: &Registry) -> std::io::Result<Self> {
let waker = Waker::new(registry, WAKER_TOKEN)?;
let queue = Mutex::new(VecDeque::with_capacity(16));
Ok(Self(Arc::new((waker, queue))))
}
/// push a new interest to the queue and wake up the accept poll afterwards.
pub(crate) fn wake(&self, interest: WakerInterest) {
let (waker, queue) = self.deref();
queue
.lock()
.expect("Failed to lock WakerQueue")
.push_back(interest);
waker
.wake()
.unwrap_or_else(|e| panic!("can not wake up Accept Poll: {}", e));
}
/// get a MutexGuard of the waker queue.
pub(crate) fn guard(&self) -> MutexGuard<'_, VecDeque<WakerInterest>> {
self.deref().1.lock().expect("Failed to lock WakerQueue")
}
/// reset the waker queue so it does not grow infinitely.
pub(crate) fn reset(queue: &mut VecDeque<WakerInterest>) {
std::mem::swap(&mut VecDeque::<WakerInterest>::with_capacity(16), queue);
}
}
/// types of interests we would look into when `Accept`'s `Poll` is waked up by waker.
///
/// *. These interests should not be confused with `mio::Interest` and mostly not I/O related
pub(crate) enum WakerInterest {
/// `WorkerAvailable` is an interest from `Worker` notifying `Accept` there is a worker
/// available and can accept new tasks.
WorkerAvailable,
/// `Pause`, `Resume`, `Stop` Interest are from `ServerBuilder` future. It listens to
/// `ServerCommand` and notify `Accept` to do exactly these tasks.
Pause,
Resume,
Stop,
/// `Timer` is an interest sent as a delayed future. When an error happens on accepting
/// connection `Accept` would deregister socket listener temporary and wake up the poll and
/// register them again after the delayed future resolve.
Timer,
/// `Worker` is an interest happen after a worker runs into faulted state(This is determined
/// by if work can be sent to it successfully).`Accept` would be waked up and add the new
/// `WorkerHandle`.
Worker(WorkerHandle),
}

View File

@@ -1,22 +1,22 @@
use std::future::Future;
use std::pin::Pin;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use std::task::{Context, Poll};
use std::time::Duration;
use std::time;
use actix_rt::time::{sleep_until, Instant, Sleep};
use actix_rt::time::{delay_until, Delay, Instant};
use actix_rt::{spawn, Arbiter};
use actix_utils::counter::Counter;
use futures_core::future::LocalBoxFuture;
use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures_channel::oneshot;
use futures_util::future::{join_all, LocalBoxFuture, MapOk};
use futures_util::{future::Future, stream::Stream, FutureExt, TryFutureExt};
use log::{error, info, trace};
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use tokio::sync::oneshot;
use crate::accept::AcceptNotify;
use crate::service::{BoxedServerService, InternalServiceFactory};
use crate::socket::{MioStream, SocketAddr};
use crate::waker_queue::{WakerInterest, WakerQueue};
use crate::{join_all, Token};
use crate::socket::{SocketAddr, StdStream};
use crate::Token;
pub(crate) struct WorkerCommand(Conn);
@@ -29,7 +29,7 @@ pub(crate) struct StopCommand {
#[derive(Debug)]
pub(crate) struct Conn {
pub io: MioStream,
pub io: StdStream,
pub token: Token,
pub peer: Option<SocketAddr>,
}
@@ -46,33 +46,31 @@ pub fn max_concurrent_connections(num: usize) {
MAX_CONNS.store(num, Ordering::Relaxed);
}
pub(crate) fn num_connections() -> usize {
MAX_CONNS_COUNTER.with(|conns| conns.total())
}
thread_local! {
static MAX_CONNS_COUNTER: Counter =
Counter::new(MAX_CONNS.load(Ordering::Relaxed));
}
pub(crate) fn num_connections() -> usize {
MAX_CONNS_COUNTER.with(|conns| conns.total())
}
// a handle to worker that can send message to worker and share the availability of worker to other
// thread.
#[derive(Clone)]
pub(crate) struct WorkerHandle {
pub(crate) struct WorkerClient {
pub idx: usize,
tx1: UnboundedSender<WorkerCommand>,
tx2: UnboundedSender<StopCommand>,
avail: WorkerAvailability,
}
impl WorkerHandle {
impl WorkerClient {
pub fn new(
idx: usize,
tx1: UnboundedSender<WorkerCommand>,
tx2: UnboundedSender<StopCommand>,
avail: WorkerAvailability,
) -> Self {
WorkerHandle {
WorkerClient {
idx,
tx1,
tx2,
@@ -81,7 +79,9 @@ impl WorkerHandle {
}
pub fn send(&self, msg: Conn) -> Result<(), Conn> {
self.tx1.send(WorkerCommand(msg)).map_err(|msg| msg.0 .0)
self.tx1
.unbounded_send(WorkerCommand(msg))
.map_err(|msg| msg.into_inner().0)
}
pub fn available(&self) -> bool {
@@ -90,21 +90,21 @@ impl WorkerHandle {
pub fn stop(&self, graceful: bool) -> oneshot::Receiver<bool> {
let (result, rx) = oneshot::channel();
let _ = self.tx2.send(StopCommand { graceful, result });
let _ = self.tx2.unbounded_send(StopCommand { graceful, result });
rx
}
}
#[derive(Clone)]
pub(crate) struct WorkerAvailability {
waker: WakerQueue,
notify: AcceptNotify,
available: Arc<AtomicBool>,
}
impl WorkerAvailability {
pub fn new(waker: WakerQueue) -> Self {
pub fn new(notify: AcceptNotify) -> Self {
WorkerAvailability {
waker,
notify,
available: Arc::new(AtomicBool::new(false)),
}
}
@@ -115,9 +115,8 @@ impl WorkerAvailability {
pub fn set(&self, val: bool) {
let old = self.available.swap(val, Ordering::Release);
// notify the accept on switched to available.
if !old && val {
self.waker.wake(WakerInterest::WorkerAvailable);
self.notify.notify()
}
}
}
@@ -134,7 +133,7 @@ pub(crate) struct Worker {
conns: Counter,
factories: Vec<Box<dyn InternalServiceFactory>>,
state: WorkerState,
shutdown_timeout: Duration,
shutdown_timeout: time::Duration,
}
struct WorkerService {
@@ -165,65 +164,63 @@ impl Worker {
idx: usize,
factories: Vec<Box<dyn InternalServiceFactory>>,
availability: WorkerAvailability,
shutdown_timeout: Duration,
) -> WorkerHandle {
let (tx1, rx) = unbounded_channel();
let (tx2, rx2) = unbounded_channel();
shutdown_timeout: time::Duration,
) -> WorkerClient {
let (tx1, rx) = unbounded();
let (tx2, rx2) = unbounded();
let avail = availability.clone();
// every worker runs in it's own arbiter.
Arbiter::new().send(Box::pin(async move {
availability.set(false);
let mut wrk = MAX_CONNS_COUNTER.with(move |conns| Worker {
rx,
rx2,
availability,
factories,
shutdown_timeout,
services: Vec::new(),
conns: conns.clone(),
state: WorkerState::Unavailable,
});
Arbiter::new().send(
async move {
availability.set(false);
let mut wrk = MAX_CONNS_COUNTER.with(move |conns| Worker {
rx,
rx2,
availability,
factories,
shutdown_timeout,
services: Vec::new(),
conns: conns.clone(),
state: WorkerState::Unavailable(Vec::new()),
});
let fut = wrk
.factories
.iter()
.enumerate()
.map(|(idx, factory)| {
let fut = factory.create();
async move {
fut.await.map(|r| {
r.into_iter().map(|(t, s)| (idx, t, s)).collect::<Vec<_>>()
})
}
})
.collect::<Vec<_>>();
let mut fut: Vec<MapOk<LocalBoxFuture<'static, _>, _>> = Vec::new();
for (idx, factory) in wrk.factories.iter().enumerate() {
fut.push(factory.create().map_ok(move |r| {
r.into_iter()
.map(|(t, s): (Token, _)| (idx, t, s))
.collect::<Vec<_>>()
}));
}
spawn(async move {
let res: Result<Vec<_>, _> = join_all(fut).await.into_iter().collect();
match res {
Ok(services) => {
for item in services {
for (factory, token, service) in item {
assert_eq!(token.0, wrk.services.len());
wrk.services.push(WorkerService {
factory,
service,
status: WorkerServiceStatus::Unavailable,
});
spawn(async move {
let res = join_all(fut).await;
let res: Result<Vec<_>, _> = res.into_iter().collect();
match res {
Ok(services) => {
for item in services {
for (factory, token, service) in item {
assert_eq!(token.0, wrk.services.len());
wrk.services.push(WorkerService {
factory,
service,
status: WorkerServiceStatus::Unavailable,
});
}
}
}
Err(e) => {
error!("Can not start worker: {:?}", e);
Arbiter::current().stop();
}
}
Err(e) => {
error!("Can not start worker: {:?}", e);
Arbiter::current().stop();
}
}
wrk.await
});
}));
wrk.await
});
}
.boxed(),
);
WorkerHandle::new(idx, tx1, tx2, avail)
WorkerClient::new(idx, tx1, tx2, avail)
}
fn shutdown(&mut self, force: bool) {
@@ -245,7 +242,7 @@ impl Worker {
fn check_readiness(&mut self, cx: &mut Context<'_>) -> Result<bool, (Token, usize)> {
let mut ready = self.conns.available(cx);
let mut failed = None;
for (idx, srv) in self.services.iter_mut().enumerate() {
for (idx, srv) in &mut self.services.iter_mut().enumerate() {
if srv.status == WorkerServiceStatus::Available
|| srv.status == WorkerServiceStatus::Unavailable
{
@@ -291,15 +288,16 @@ impl Worker {
enum WorkerState {
Available,
Unavailable,
Unavailable(Vec<Conn>),
Restarting(
usize,
Token,
LocalBoxFuture<'static, Result<Vec<(Token, BoxedServerService)>, ()>>,
#[allow(clippy::type_complexity)]
Pin<Box<dyn Future<Output = Result<Vec<(Token, BoxedServerService)>, ()>>>>,
),
Shutdown(
Pin<Box<Sleep>>,
Pin<Box<Sleep>>,
Pin<Box<Delay>>,
Pin<Box<Delay>>,
Option<oneshot::Sender<bool>>,
),
}
@@ -307,10 +305,12 @@ enum WorkerState {
impl Future for Worker {
type Output = ();
// FIXME: remove this attribute
#[allow(clippy::never_loop)]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// `StopWorker` message handler
if let Poll::Ready(Some(StopCommand { graceful, result })) =
Pin::new(&mut self.rx2).poll_recv(cx)
Pin::new(&mut self.rx2).poll_next(cx)
{
self.availability.set(false);
let num = num_connections();
@@ -324,8 +324,8 @@ impl Future for Worker {
if num != 0 {
info!("Graceful worker shutdown, {} connections", num);
self.state = WorkerState::Shutdown(
Box::pin(sleep_until(Instant::now() + Duration::from_secs(1))),
Box::pin(sleep_until(Instant::now() + self.shutdown_timeout)),
Box::pin(delay_until(Instant::now() + time::Duration::from_secs(1))),
Box::pin(delay_until(Instant::now() + self.shutdown_timeout)),
Some(result),
);
} else {
@@ -341,35 +341,53 @@ impl Future for Worker {
}
match self.state {
WorkerState::Unavailable => match self.check_readiness(cx) {
Ok(true) => {
self.state = WorkerState::Available;
self.availability.set(true);
self.poll(cx)
WorkerState::Unavailable(ref mut conns) => {
let conn = conns.pop();
match self.check_readiness(cx) {
Ok(true) => {
// process requests from wait queue
if let Some(conn) = conn {
let guard = self.conns.get();
let _ = self.services[conn.token.0]
.service
.call((Some(guard), conn.io));
} else {
self.state = WorkerState::Available;
self.availability.set(true);
}
self.poll(cx)
}
Ok(false) => {
// push connection back to queue
if let Some(conn) = conn {
if let WorkerState::Unavailable(ref mut conns) = self.state {
conns.push(conn);
}
}
Poll::Pending
}
Err((token, idx)) => {
trace!(
"Service {:?} failed, restarting",
self.factories[idx].name(token)
);
self.services[token.0].status = WorkerServiceStatus::Restarting;
self.state =
WorkerState::Restarting(idx, token, self.factories[idx].create());
self.poll(cx)
}
}
Ok(false) => Poll::Pending,
Err((token, idx)) => {
trace!(
"Service {:?} failed, restarting",
self.factories[idx].name(token)
);
self.services[token.0].status = WorkerServiceStatus::Restarting;
self.state =
WorkerState::Restarting(idx, token, self.factories[idx].create());
self.poll(cx)
}
},
}
WorkerState::Restarting(idx, token, ref mut fut) => {
match fut.as_mut().poll(cx) {
match Pin::new(fut).poll(cx) {
Poll::Ready(Ok(item)) => {
// only interest in the first item?
if let Some((token, service)) = item.into_iter().next() {
for (token, service) in item {
trace!(
"Service {:?} has been restarted",
self.factories[idx].name(token)
);
self.services[token.0].created(service);
self.state = WorkerState::Unavailable;
self.state = WorkerState::Unavailable(Vec::new());
return self.poll(cx);
}
}
@@ -379,7 +397,9 @@ impl Future for Worker {
self.factories[idx].name(token)
);
}
Poll::Pending => return Poll::Pending,
Poll::Pending => {
return Poll::Pending;
}
}
self.poll(cx)
}
@@ -392,56 +412,71 @@ impl Future for Worker {
}
// check graceful timeout
if Pin::new(t2).poll(cx).is_ready() {
let _ = tx.take().unwrap().send(false);
self.shutdown(true);
Arbiter::current().stop();
return Poll::Ready(());
match t2.as_mut().poll(cx) {
Poll::Pending => (),
Poll::Ready(_) => {
let _ = tx.take().unwrap().send(false);
self.shutdown(true);
Arbiter::current().stop();
return Poll::Ready(());
}
}
// sleep for 1 second and then check again
if t1.as_mut().poll(cx).is_ready() {
*t1 = Box::pin(sleep_until(Instant::now() + Duration::from_secs(1)));
let _ = t1.as_mut().poll(cx);
match t1.as_mut().poll(cx) {
Poll::Pending => (),
Poll::Ready(_) => {
*t1 = Box::pin(delay_until(
Instant::now() + time::Duration::from_secs(1),
));
let _ = t1.as_mut().poll(cx);
}
}
Poll::Pending
}
// actively poll stream and handle worker command
WorkerState::Available => loop {
match self.check_readiness(cx) {
Ok(true) => (),
Ok(false) => {
trace!("Worker is unavailable");
self.availability.set(false);
self.state = WorkerState::Unavailable;
return self.poll(cx);
}
Err((token, idx)) => {
trace!(
"Service {:?} failed, restarting",
self.factories[idx].name(token)
);
self.availability.set(false);
self.services[token.0].status = WorkerServiceStatus::Restarting;
self.state =
WorkerState::Restarting(idx, token, self.factories[idx].create());
return self.poll(cx);
WorkerState::Available => {
loop {
match Pin::new(&mut self.rx).poll_next(cx) {
// handle incoming io stream
Poll::Ready(Some(WorkerCommand(msg))) => {
match self.check_readiness(cx) {
Ok(true) => {
let guard = self.conns.get();
let _ = self.services[msg.token.0]
.service
.call((Some(guard), msg.io));
continue;
}
Ok(false) => {
trace!("Worker is unavailable");
self.availability.set(false);
self.state = WorkerState::Unavailable(vec![msg]);
}
Err((token, idx)) => {
trace!(
"Service {:?} failed, restarting",
self.factories[idx].name(token)
);
self.availability.set(false);
self.services[token.0].status =
WorkerServiceStatus::Restarting;
self.state = WorkerState::Restarting(
idx,
token,
self.factories[idx].create(),
);
}
}
return self.poll(cx);
}
Poll::Pending => {
self.state = WorkerState::Available;
return Poll::Pending;
}
Poll::Ready(None) => return Poll::Ready(()),
}
}
match Pin::new(&mut self.rx).poll_recv(cx) {
// handle incoming io stream
Poll::Ready(Some(WorkerCommand(msg))) => {
let guard = self.conns.get();
let _ = self.services[msg.token.0]
.service
.call((Some(guard), msg.io));
}
Poll::Pending => return Poll::Pending,
Poll::Ready(None) => return Poll::Ready(()),
};
},
}
}
}
}

View File

@@ -5,13 +5,14 @@ use std::{net, thread, time};
use actix_server::Server;
use actix_service::fn_service;
use futures_util::future::{lazy, ok};
use socket2::{Domain, Protocol, Socket, Type};
fn unused_addr() -> net::SocketAddr {
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = mio::net::TcpSocket::new_v4().unwrap();
socket.bind(addr).unwrap();
socket.set_reuseaddr(true).unwrap();
let tcp = socket.listen(32).unwrap();
let socket = Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap();
socket.bind(&addr.into()).unwrap();
socket.set_reuse_address(true).unwrap();
let tcp = socket.into_tcp_listener();
tcp.local_addr().unwrap()
}
@@ -22,14 +23,12 @@ fn test_bind() {
let h = thread::spawn(move || {
let sys = actix_rt::System::new("test");
let srv = sys.block_on(lazy(|_| {
Server::build()
.workers(1)
.disable_signals()
.bind("test", addr, move || fn_service(|_| ok::<_, ()>(())))
.unwrap()
.start()
}));
let srv = Server::build()
.workers(1)
.disable_signals()
.bind("test", addr, move || fn_service(|_| ok::<_, ()>(())))
.unwrap()
.start();
let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run();
});
@@ -49,15 +48,13 @@ fn test_listen() {
let h = thread::spawn(move || {
let sys = actix_rt::System::new("test");
let lst = net::TcpListener::bind(addr).unwrap();
sys.block_on(async {
Server::build()
.disable_signals()
.workers(1)
.listen("test", lst, move || fn_service(|_| ok::<_, ()>(())))
.unwrap()
.start();
let _ = tx.send(actix_rt::System::current());
});
Server::build()
.disable_signals()
.workers(1)
.listen("test", lst, move || fn_service(|_| ok::<_, ()>(())))
.unwrap()
.start();
let _ = tx.send(actix_rt::System::current());
let _ = sys.run();
});
let sys = rx.recv().unwrap();
@@ -82,25 +79,22 @@ fn test_start() {
let h = thread::spawn(move || {
let sys = actix_rt::System::new("test");
let srv = sys.block_on(lazy(|_| {
Server::build()
.backlog(100)
.disable_signals()
.bind("test", addr, move || {
fn_service(|io: TcpStream| async move {
let mut f = Framed::new(io, BytesCodec);
f.send(Bytes::from_static(b"test")).await.unwrap();
Ok::<_, ()>(())
})
let srv: Server = Server::build()
.backlog(100)
.disable_signals()
.bind("test", addr, move || {
fn_service(|io: TcpStream| async move {
let mut f = Framed::new(io, BytesCodec);
f.send(Bytes::from_static(b"test")).await.unwrap();
Ok::<_, ()>(())
})
.unwrap()
.start()
}));
})
.unwrap()
.start();
let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run();
});
let (srv, sys) = rx.recv().unwrap();
let mut buf = [1u8; 4];
@@ -151,30 +145,28 @@ fn test_configure() {
let h = thread::spawn(move || {
let num = num2.clone();
let sys = actix_rt::System::new("test");
let srv = sys.block_on(lazy(|_| {
Server::build()
.disable_signals()
.configure(move |cfg| {
let num = num.clone();
let lst = net::TcpListener::bind(addr3).unwrap();
cfg.bind("addr1", addr1)
.unwrap()
.bind("addr2", addr2)
.unwrap()
.listen("addr3", lst)
.apply(move |rt| {
let num = num.clone();
rt.service("addr1", fn_service(|_| ok::<_, ()>(())));
rt.service("addr3", fn_service(|_| ok::<_, ()>(())));
rt.on_start(lazy(move |_| {
let _ = num.fetch_add(1, Relaxed);
}))
})
})
.unwrap()
.workers(1)
.start()
}));
let srv = Server::build()
.disable_signals()
.configure(move |cfg| {
let num = num.clone();
let lst = net::TcpListener::bind(addr3).unwrap();
cfg.bind("addr1", addr1)
.unwrap()
.bind("addr2", addr2)
.unwrap()
.listen("addr3", lst)
.apply(move |rt| {
let num = num.clone();
rt.service("addr1", fn_service(|_| ok::<_, ()>(())));
rt.service("addr3", fn_service(|_| ok::<_, ()>(())));
rt.on_start(lazy(move |_| {
let _ = num.fetch_add(1, Relaxed);
}))
})
})
.unwrap()
.workers(1)
.start();
let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run();
});

View File

@@ -1,35 +1,13 @@
# Changes
## Unreleased - 2021-xx-xx
## 2.0.0-beta.2 - 2021-01-03
* Remove redundant type parameter from `map_config`.
## 2.0.0-beta.1 - 2020-12-28
* `Service`, other traits, and many type signatures now take the the request type as a type
parameter instead of an associated type. [#232]
* Add `always_ready!` and `forward_ready!` macros. [#233]
* Crate is now `no_std`. [#233]
* Migrate pin projections to `pin-project-lite`. [#233]
* Remove `AndThenApplyFn` and Pipeline `and_then_apply_fn`. Use the
`.and_then(apply_fn(...))` construction. [#233]
* Move non-vital methods to `ServiceExt` and `ServiceFactoryExt` extension traits. [#235]
[#232]: https://github.com/actix/actix-net/pull/232
[#233]: https://github.com/actix/actix-net/pull/233
[#235]: https://github.com/actix/actix-net/pull/235
## Unreleased - 2020-xx-xx
* Upgrade `pin-project` to `1.0`.
## 1.0.6 - 2020-08-09
### Fixed
* Removed unsound custom Cell implementation that allowed obtaining several mutable references to
the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through
service combinators. Attempts to acquire several mutable references to the same data will instead
result in a panic.
* Removed unsound custom Cell implementation that allowed obtaining several mutable references to the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through service combinators. Attempts to acquire several mutable references to the same data will instead result in a panic.
## [1.0.5] - 2020-01-16

View File

@@ -1,10 +1,7 @@
[package]
name = "actix-service"
version = "2.0.0-beta.2"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
]
version = "1.0.6"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Service trait and combinators for representing asynchronous request/response operations."
keywords = ["network", "framework", "async", "futures", "service"]
homepage = "https://actix.rs"
@@ -20,9 +17,17 @@ name = "actix_service"
path = "src/lib.rs"
[dependencies]
futures-core = { version = "0.3.7", default-features = false }
pin-project-lite = "0.2"
futures-util = "0.3.1"
pin-project = "1.0.0"
[dev-dependencies]
actix-rt = "1.0.0"
futures-util = { version = "0.3.7", default-features = false }
criterion = "0.3"
[[bench]]
name = "unsafecell_vs_refcell"
harness = false
[[bench]]
name = "and_then"
harness = false

View File

@@ -0,0 +1,332 @@
use actix_service::boxed::BoxFuture;
use actix_service::IntoService;
use actix_service::Service;
/// Benchmark various implementations of and_then
use criterion::{criterion_main, Criterion};
use futures_util::future::join_all;
use futures_util::future::TryFutureExt;
use std::cell::{RefCell, UnsafeCell};
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
/*
* Test services A,B for AndThen service implementations
*/
async fn svc1(_: ()) -> Result<usize, ()> {
Ok(1)
}
async fn svc2(req: usize) -> Result<usize, ()> {
Ok(req + 1)
}
/*
* AndThenUC - original AndThen service based on UnsafeCell
* Cut down version of actix_service::AndThenService based on actix-service::Cell
*/
struct AndThenUC<A, B>(Rc<UnsafeCell<(A, B)>>);
impl<A, B> AndThenUC<A, B> {
fn new(a: A, b: B) -> Self
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
Self(Rc::new(UnsafeCell::new((a, b))))
}
}
impl<A, B> Clone for AndThenUC<A, B> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<A, B> Service for AndThenUC<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
type Request = A::Request;
type Response = B::Response;
type Error = A::Error;
type Future = AndThenServiceResponse<A, B>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: A::Request) -> Self::Future {
let fut = unsafe { &mut *(*self.0).get() }.0.call(req);
AndThenServiceResponse {
state: State::A(fut, Some(self.0.clone())),
}
}
}
#[pin_project::pin_project]
pub(crate) struct AndThenServiceResponse<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
#[pin]
state: State<A, B>,
}
#[pin_project::pin_project(project = StateProj)]
enum State<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
A(#[pin] A::Future, Option<Rc<UnsafeCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
impl<A, B> Future for AndThenServiceResponse<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
type Output = Result<B::Response, A::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
match this.state.as_mut().project() {
StateProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let b = b.take().unwrap();
this.state.set(State::Empty); // drop fut A
let fut = unsafe { &mut (*b.get()).1 }.call(res);
this.state.set(State::B(fut));
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty);
r
}),
StateProj::Empty => {
panic!("future must not be polled after it returned `Poll::Ready`")
}
}
}
}
/*
* AndThenRC - AndThen service based on RefCell
*/
struct AndThenRC<A, B>(Rc<RefCell<(A, B)>>);
impl<A, B> AndThenRC<A, B> {
fn new(a: A, b: B) -> Self
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
Self(Rc::new(RefCell::new((a, b))))
}
}
impl<A, B> Clone for AndThenRC<A, B> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<A, B> Service for AndThenRC<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
type Request = A::Request;
type Response = B::Response;
type Error = A::Error;
type Future = AndThenServiceResponseRC<A, B>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: A::Request) -> Self::Future {
let fut = self.0.borrow_mut().0.call(req);
AndThenServiceResponseRC {
state: StateRC::A(fut, Some(self.0.clone())),
}
}
}
#[pin_project::pin_project]
pub(crate) struct AndThenServiceResponseRC<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
#[pin]
state: StateRC<A, B>,
}
#[pin_project::pin_project(project = StateRCProj)]
enum StateRC<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
impl<A, B> Future for AndThenServiceResponseRC<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
type Output = Result<B::Response, A::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
match this.state.as_mut().project() {
StateRCProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let b = b.take().unwrap();
this.state.set(StateRC::Empty); // drop fut A
let fut = b.borrow_mut().1.call(res);
this.state.set(StateRC::B(fut));
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
StateRCProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(StateRC::Empty);
r
}),
StateRCProj::Empty => {
panic!("future must not be polled after it returned `Poll::Ready`")
}
}
}
}
/*
* AndThenRCFuture - AndThen service based on RefCell
* and standard futures::future::and_then combinator in a Box
*/
struct AndThenRCFuture<A, B>(Rc<RefCell<(A, B)>>);
impl<A, B> AndThenRCFuture<A, B> {
fn new(a: A, b: B) -> Self
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
Self(Rc::new(RefCell::new((a, b))))
}
}
impl<A, B> Clone for AndThenRCFuture<A, B> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<A, B> Service for AndThenRCFuture<A, B>
where
A: Service + 'static,
A::Future: 'static,
B: Service<Request = A::Response, Error = A::Error> + 'static,
B::Future: 'static,
{
type Request = A::Request;
type Response = B::Response;
type Error = A::Error;
type Future = BoxFuture<Self::Response, Self::Error>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: A::Request) -> Self::Future {
let fut = self.0.borrow_mut().0.call(req);
let core = self.0.clone();
let fut2 = move |res| (*core).borrow_mut().1.call(res);
Box::pin(fut.and_then(fut2))
}
}
/// Criterion Benchmark for async Service
/// Should be used from within criterion group:
/// ```rust,ignore
/// let mut criterion: ::criterion::Criterion<_> =
/// ::criterion::Criterion::default().configure_from_args();
/// bench_async_service(&mut criterion, ok_service(), "async_service_direct");
/// ```
///
/// Usable for benching Service wrappers:
/// Using minimum service code implementation we first measure
/// time to run minimum service, then measure time with wrapper.
///
/// Sample output
/// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us]
pub fn bench_async_service<S>(c: &mut Criterion, srv: S, name: &str)
where
S: Service<Request = (), Response = usize, Error = ()> + Clone + 'static,
{
let mut rt = actix_rt::System::new("test");
// start benchmark loops
c.bench_function(name, move |b| {
b.iter_custom(|iters| {
let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect();
// exclude request generation, it appears it takes significant time vs call (3us vs 1us)
let start = std::time::Instant::now();
// benchmark body
rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await });
// check that at least first request succeeded
start.elapsed()
})
});
}
pub fn service_benches() {
let mut criterion: ::criterion::Criterion<_> =
::criterion::Criterion::default().configure_from_args();
bench_async_service(
&mut criterion,
AndThenUC::new(svc1.into_service(), svc2.into_service()),
"AndThen with UnsafeCell",
);
bench_async_service(
&mut criterion,
AndThenRC::new(svc1.into_service(), svc2.into_service()),
"AndThen with RefCell",
);
bench_async_service(
&mut criterion,
AndThenUC::new(svc1.into_service(), svc2.into_service()),
"AndThen with UnsafeCell",
);
bench_async_service(
&mut criterion,
AndThenRC::new(svc1.into_service(), svc2.into_service()),
"AndThen with RefCell",
);
bench_async_service(
&mut criterion,
AndThenRCFuture::new(svc1.into_service(), svc2.into_service()),
"AndThen with RefCell via future::and_then",
);
}
criterion_main!(service_benches);

View File

@@ -0,0 +1,112 @@
use actix_service::Service;
use criterion::{criterion_main, Criterion};
use futures_util::future::join_all;
use futures_util::future::{ok, Ready};
use std::cell::{RefCell, UnsafeCell};
use std::rc::Rc;
use std::task::{Context, Poll};
struct SrvUC(Rc<UnsafeCell<usize>>);
impl Default for SrvUC {
fn default() -> Self {
Self(Rc::new(UnsafeCell::new(0)))
}
}
impl Clone for SrvUC {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl Service for SrvUC {
type Request = ();
type Response = usize;
type Error = ();
type Future = Ready<Result<Self::Response, ()>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
unsafe { *(*self.0).get() = *(*self.0).get() + 1 };
ok(unsafe { *self.0.get() })
}
}
struct SrvRC(Rc<RefCell<usize>>);
impl Default for SrvRC {
fn default() -> Self {
Self(Rc::new(RefCell::new(0)))
}
}
impl Clone for SrvRC {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl Service for SrvRC {
type Request = ();
type Response = usize;
type Error = ();
type Future = Ready<Result<Self::Response, ()>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
let prev = *self.0.borrow();
*(*self.0).borrow_mut() = prev + 1;
ok(*self.0.borrow())
}
}
/// Criterion Benchmark for async Service
/// Should be used from within criterion group:
/// ```rust,ignore
/// let mut criterion: ::criterion::Criterion<_> =
/// ::criterion::Criterion::default().configure_from_args();
/// bench_async_service(&mut criterion, ok_service(), "async_service_direct");
/// ```
///
/// Usable for benching Service wrappers:
/// Using minimum service code implementation we first measure
/// time to run minimum service, then measure time with wrapper.
///
/// Sample output
/// async_service_direct time: [1.0908 us 1.1656 us 1.2613 us]
pub fn bench_async_service<S>(c: &mut Criterion, srv: S, name: &str)
where
S: Service<Request = (), Response = usize, Error = ()> + Clone + 'static,
{
let mut rt = actix_rt::System::new("test");
// start benchmark loops
c.bench_function(name, move |b| {
b.iter_custom(|iters| {
let mut srvs: Vec<_> = (1..iters).map(|_| srv.clone()).collect();
// exclude request generation, it appears it takes significant time vs call (3us vs 1us)
let start = std::time::Instant::now();
// benchmark body
rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await });
// check that at least first request succeeded
start.elapsed()
})
});
}
pub fn service_benches() {
let mut criterion: ::criterion::Criterion<_> =
::criterion::Criterion::default().configure_from_args();
bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell");
bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell");
bench_async_service(&mut criterion, SrvUC::default(), "Service with UnsafeCell");
bench_async_service(&mut criterion, SrvRC::default(), "Service with RefCell");
}
criterion_main!(service_benches);

View File

@@ -1,13 +1,8 @@
use alloc::rc::Rc;
use core::{
cell::RefCell,
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use super::{Service, ServiceFactory};
@@ -15,33 +10,34 @@ use super::{Service, ServiceFactory};
/// of another service which completes successfully.
///
/// This is created by the `Pipeline::and_then` method.
pub(crate) struct AndThenService<A, B, Req>(Rc<RefCell<(A, B)>>, PhantomData<Req>);
pub(crate) struct AndThenService<A, B>(Rc<RefCell<(A, B)>>);
impl<A, B, Req> AndThenService<A, B, Req> {
impl<A, B> AndThenService<A, B> {
/// Create new `AndThen` combinator
pub(crate) fn new(a: A, b: B) -> Self
where
A: Service<Req>,
B: Service<A::Response, Error = A::Error>,
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
Self(Rc::new(RefCell::new((a, b))), PhantomData)
Self(Rc::new(RefCell::new((a, b))))
}
}
impl<A, B, Req> Clone for AndThenService<A, B, Req> {
impl<A, B> Clone for AndThenService<A, B> {
fn clone(&self) -> Self {
AndThenService(self.0.clone(), PhantomData)
AndThenService(self.0.clone())
}
}
impl<A, B, Req> Service<Req> for AndThenService<A, B, Req>
impl<A, B> Service for AndThenService<A, B>
where
A: Service<Req>,
B: Service<A::Response, Error = A::Error>,
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
type Request = A::Request;
type Response = B::Response;
type Error = A::Error;
type Future = AndThenServiceResponse<A, B, Req>;
type Future = AndThenServiceResponse<A, B>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let mut srv = self.0.borrow_mut();
@@ -53,51 +49,38 @@ where
}
}
fn call(&mut self, req: Req) -> Self::Future {
fn call(&mut self, req: A::Request) -> Self::Future {
AndThenServiceResponse {
state: State::A {
fut: self.0.borrow_mut().0.call(req),
b: Some(self.0.clone()),
},
state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())),
}
}
}
pin_project! {
pub(crate) struct AndThenServiceResponse<A, B, Req>
where
A: Service<Req>,
B: Service<A::Response, Error = A::Error>,
{
#[pin]
state: State<A, B, Req>,
}
}
pin_project! {
#[project = StateProj]
enum State<A, B, Req>
where
A: Service<Req>,
B: Service<A::Response, Error = A::Error>,
{
A {
#[pin]
fut: A::Future,
b: Option<Rc<RefCell<(A, B)>>>,
},
B {
#[pin]
fut: B::Future,
},
Empty,
}
}
impl<A, B, Req> Future for AndThenServiceResponse<A, B, Req>
#[pin_project::pin_project]
pub(crate) struct AndThenServiceResponse<A, B>
where
A: Service<Req>,
B: Service<A::Response, Error = A::Error>,
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
#[pin]
state: State<A, B>,
}
#[pin_project::pin_project(project = StateProj)]
enum State<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
impl<A, B> Future for AndThenServiceResponse<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
type Output = Result<B::Response, A::Error>;
@@ -105,17 +88,17 @@ where
let mut this = self.as_mut().project();
match this.state.as_mut().project() {
StateProj::A { fut, b } => match fut.poll(cx)? {
StateProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let b = b.take().unwrap();
this.state.set(State::Empty); // drop fut A
let fut = b.borrow_mut().1.call(res);
this.state.set(State::B { fut });
this.state.set(State::B(fut));
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
StateProj::B { fut } => fut.poll(cx).map(|r| {
StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty);
r
}),
@@ -127,28 +110,27 @@ where
}
/// `.and_then()` service factory combinator
pub(crate) struct AndThenServiceFactory<A, B, Req>
pub(crate) struct AndThenServiceFactory<A, B>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<
A::Response,
Config = A::Config,
Request = A::Response,
Error = A::Error,
InitError = A::InitError,
>,
{
inner: Rc<(A, B)>,
_phantom: PhantomData<Req>,
}
impl<A, B, Req> AndThenServiceFactory<A, B, Req>
impl<A, B> AndThenServiceFactory<A, B>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<
A::Response,
Config = A::Config,
Request = A::Response,
Error = A::Error,
InitError = A::InitError,
>,
@@ -157,29 +139,29 @@ where
pub(crate) fn new(a: A, b: B) -> Self {
Self {
inner: Rc::new((a, b)),
_phantom: PhantomData,
}
}
}
impl<A, B, Req> ServiceFactory<Req> for AndThenServiceFactory<A, B, Req>
impl<A, B> ServiceFactory for AndThenServiceFactory<A, B>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<
A::Response,
Config = A::Config,
Request = A::Response,
Error = A::Error,
InitError = A::InitError,
>,
{
type Request = A::Request;
type Response = B::Response;
type Error = A::Error;
type Config = A::Config;
type Service = AndThenService<A::Service, B::Service, Req>;
type Service = AndThenService<A::Service, B::Service>;
type InitError = A::InitError;
type Future = AndThenServiceFactoryResponse<A, B, Req>;
type Future = AndThenServiceFactoryResponse<A, B>;
fn new_service(&self, cfg: A::Config) -> Self::Future {
let inner = &*self.inner;
@@ -190,13 +172,13 @@ where
}
}
impl<A, B, Req> Clone for AndThenServiceFactory<A, B, Req>
impl<A, B> Clone for AndThenServiceFactory<A, B>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<
A::Response,
Config = A::Config,
Request = A::Response,
Error = A::Error,
InitError = A::InitError,
>,
@@ -204,31 +186,29 @@ where
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_phantom: PhantomData,
}
}
}
pin_project! {
pub(crate) struct AndThenServiceFactoryResponse<A, B, Req>
where
A: ServiceFactory<Req>,
B: ServiceFactory<A::Response>,
{
#[pin]
fut_a: A::Future,
#[pin]
fut_b: B::Future,
#[pin_project::pin_project]
pub(crate) struct AndThenServiceFactoryResponse<A, B>
where
A: ServiceFactory,
B: ServiceFactory<Request = A::Response>,
{
#[pin]
fut_a: A::Future,
#[pin]
fut_b: B::Future,
a: Option<A::Service>,
b: Option<B::Service>,
}
a: Option<A::Service>,
b: Option<B::Service>,
}
impl<A, B, Req> AndThenServiceFactoryResponse<A, B, Req>
impl<A, B> AndThenServiceFactoryResponse<A, B>
where
A: ServiceFactory<Req>,
B: ServiceFactory<A::Response>,
A: ServiceFactory,
B: ServiceFactory<Request = A::Response>,
{
fn new(fut_a: A::Future, fut_b: B::Future) -> Self {
AndThenServiceFactoryResponse {
@@ -240,12 +220,12 @@ where
}
}
impl<A, B, Req> Future for AndThenServiceFactoryResponse<A, B, Req>
impl<A, B> Future for AndThenServiceFactoryResponse<A, B>
where
A: ServiceFactory<Req>,
B: ServiceFactory<A::Response, Error = A::Error, InitError = A::InitError>,
A: ServiceFactory,
B: ServiceFactory<Request = A::Response, Error = A::Error, InitError = A::InitError>,
{
type Output = Result<AndThenService<A::Service, B::Service, Req>, A::InitError>;
type Output = Result<AndThenService<A::Service, B::Service>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
@@ -273,21 +253,18 @@ where
#[cfg(test)]
mod tests {
use alloc::rc::Rc;
use core::{
cell::Cell,
task::{Context, Poll},
};
use std::cell::Cell;
use std::rc::Rc;
use std::task::{Context, Poll};
use futures_util::future::lazy;
use futures_util::future::{lazy, ok, ready, Ready};
use crate::{
fn_factory, ok, pipeline, pipeline_factory, ready, Ready, Service, ServiceFactory,
};
use crate::{fn_factory, pipeline, pipeline_factory, Service, ServiceFactory};
struct Srv1(Rc<Cell<usize>>);
impl Service<&'static str> for Srv1 {
impl Service for Srv1 {
type Request = &'static str;
type Response = &'static str;
type Error = ();
type Future = Ready<Result<Self::Response, ()>>;
@@ -305,7 +282,8 @@ mod tests {
#[derive(Clone)]
struct Srv2(Rc<Cell<usize>>);
impl Service<&'static str> for Srv2 {
impl Service for Srv2 {
type Request = &'static str;
type Response = (&'static str, &'static str);
type Error = ();
type Future = Ready<Result<Self::Response, ()>>;

View File

@@ -0,0 +1,326 @@
use std::cell::RefCell;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use crate::{Service, ServiceFactory};
/// `Apply` service combinator
pub(crate) struct AndThenApplyFn<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
srv: Rc<RefCell<(A, B, F)>>,
r: PhantomData<(Fut, Res, Err)>,
}
impl<A, B, F, Fut, Res, Err> AndThenApplyFn<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
/// Create new `Apply` combinator
pub(crate) fn new(a: A, b: B, f: F) -> Self {
Self {
srv: Rc::new(RefCell::new((a, b, f))),
r: PhantomData,
}
}
}
impl<A, B, F, Fut, Res, Err> Clone for AndThenApplyFn<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
fn clone(&self) -> Self {
AndThenApplyFn {
srv: self.srv.clone(),
r: PhantomData,
}
}
}
impl<A, B, F, Fut, Res, Err> Service for AndThenApplyFn<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
type Request = A::Request;
type Response = Res;
type Error = Err;
type Future = AndThenApplyFnFuture<A, B, F, Fut, Res, Err>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let mut inner = self.srv.borrow_mut();
let not_ready = inner.0.poll_ready(cx)?.is_pending();
if inner.1.poll_ready(cx)?.is_pending() || not_ready {
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
fn call(&mut self, req: A::Request) -> Self::Future {
let fut = self.srv.borrow_mut().0.call(req);
AndThenApplyFnFuture {
state: State::A(fut, Some(self.srv.clone())),
}
}
}
#[pin_project::pin_project]
pub(crate) struct AndThenApplyFnFuture<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error>,
Err: From<B::Error>,
{
#[pin]
state: State<A, B, F, Fut, Res, Err>,
}
#[pin_project::pin_project(project = StateProj)]
enum State<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error>,
Err: From<B::Error>,
{
A(#[pin] A::Future, Option<Rc<RefCell<(A, B, F)>>>),
B(#[pin] Fut),
Empty,
}
impl<A, B, F, Fut, Res, Err> Future for AndThenApplyFnFuture<A, B, F, Fut, Res, Err>
where
A: Service,
B: Service,
F: FnMut(A::Response, &mut B) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
type Output = Result<Res, Err>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
match this.state.as_mut().project() {
StateProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let b = b.take().unwrap();
this.state.set(State::Empty);
let (_, b, f) = &mut *b.borrow_mut();
let fut = f(res, b);
this.state.set(State::B(fut));
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty);
r
}),
StateProj::Empty => {
panic!("future must not be polled after it returned `Poll::Ready`")
}
}
}
}
/// `AndThenApplyFn` service factory
pub(crate) struct AndThenApplyFnFactory<A, B, F, Fut, Res, Err> {
srv: Rc<(A, B, F)>,
r: PhantomData<(Fut, Res, Err)>,
}
impl<A, B, F, Fut, Res, Err> AndThenApplyFnFactory<A, B, F, Fut, Res, Err>
where
A: ServiceFactory,
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
/// Create new `ApplyNewService` new service instance
pub(crate) fn new(a: A, b: B, f: F) -> Self {
Self {
srv: Rc::new((a, b, f)),
r: PhantomData,
}
}
}
impl<A, B, F, Fut, Res, Err> Clone for AndThenApplyFnFactory<A, B, F, Fut, Res, Err> {
fn clone(&self) -> Self {
Self {
srv: self.srv.clone(),
r: PhantomData,
}
}
}
impl<A, B, F, Fut, Res, Err> ServiceFactory for AndThenApplyFnFactory<A, B, F, Fut, Res, Err>
where
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
type Request = A::Request;
type Response = Res;
type Error = Err;
type Service = AndThenApplyFn<A::Service, B::Service, F, Fut, Res, Err>;
type Config = A::Config;
type InitError = A::InitError;
type Future = AndThenApplyFnFactoryResponse<A, B, F, Fut, Res, Err>;
fn new_service(&self, cfg: A::Config) -> Self::Future {
let srv = &*self.srv;
AndThenApplyFnFactoryResponse {
a: None,
b: None,
f: srv.2.clone(),
fut_a: srv.0.new_service(cfg.clone()),
fut_b: srv.1.new_service(cfg),
}
}
}
#[pin_project::pin_project]
pub(crate) struct AndThenApplyFnFactoryResponse<A, B, F, Fut, Res, Err>
where
A: ServiceFactory,
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error>,
Err: From<B::Error>,
{
#[pin]
fut_b: B::Future,
#[pin]
fut_a: A::Future,
f: F,
a: Option<A::Service>,
b: Option<B::Service>,
}
impl<A, B, F, Fut, Res, Err> Future for AndThenApplyFnFactoryResponse<A, B, F, Fut, Res, Err>
where
A: ServiceFactory,
B: ServiceFactory<Config = A::Config, InitError = A::InitError>,
F: FnMut(A::Response, &mut B::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
type Output =
Result<AndThenApplyFn<A::Service, B::Service, F, Fut, Res, Err>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
if this.a.is_none() {
if let Poll::Ready(service) = this.fut_a.poll(cx)? {
*this.a = Some(service);
}
}
if this.b.is_none() {
if let Poll::Ready(service) = this.fut_b.poll(cx)? {
*this.b = Some(service);
}
}
if this.a.is_some() && this.b.is_some() {
Poll::Ready(Ok(AndThenApplyFn {
srv: Rc::new(RefCell::new((
this.a.take().unwrap(),
this.b.take().unwrap(),
this.f.clone(),
))),
r: PhantomData,
}))
} else {
Poll::Pending
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures_util::future::{lazy, ok, Ready, TryFutureExt};
use crate::{fn_service, pipeline, pipeline_factory, Service, ServiceFactory};
#[derive(Clone)]
struct Srv;
impl Service for Srv {
type Request = ();
type Response = ();
type Error = ();
type Future = Ready<Result<(), ()>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
#[allow(clippy::unit_arg)]
fn call(&mut self, req: Self::Request) -> Self::Future {
ok(req)
}
}
#[actix_rt::test]
async fn test_service() {
let mut srv = pipeline(ok).and_then_apply_fn(Srv, |req: &'static str, s| {
s.call(()).map_ok(move |res| (req, res))
});
let res = lazy(|cx| srv.poll_ready(cx)).await;
assert_eq!(res, Poll::Ready(Ok(())));
let res = srv.call("srv").await;
assert!(res.is_ok());
assert_eq!(res.unwrap(), ("srv", ()));
}
#[actix_rt::test]
async fn test_service_factory() {
let new_srv = pipeline_factory(|| ok::<_, ()>(fn_service(ok))).and_then_apply_fn(
|| ok(Srv),
|req: &'static str, s| s.call(()).map_ok(move |res| (req, res)),
);
let mut srv = new_srv.new_service(()).await.unwrap();
let res = lazy(|cx| srv.poll_ready(cx)).await;
assert_eq!(res, Poll::Ready(Ok(())));
let res = srv.call("srv").await;
assert!(res.is_ok());
assert_eq!(res.unwrap(), ("srv", ()));
}
}

View File

@@ -1,230 +1,227 @@
use core::{
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use futures_core::ready;
use pin_project_lite::pin_project;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use super::{IntoService, IntoServiceFactory, Service, ServiceFactory};
/// Apply transform function to a service.
///
/// The In and Out type params refer to the request and response types for the wrapped service.
pub fn apply_fn<I, S, F, Fut, Req, In, Res, Err>(
service: I,
wrap_fn: F,
) -> Apply<S, F, Req, In, Res, Err>
pub fn apply_fn<T, F, R, In, Out, Err, U>(service: U, f: F) -> Apply<T, F, R, In, Out, Err>
where
I: IntoService<S, In>,
S: Service<In, Error = Err>,
F: FnMut(Req, &mut S) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
T: Service<Error = Err>,
F: FnMut(In, &mut T) -> R,
R: Future<Output = Result<Out, Err>>,
U: IntoService<T>,
{
Apply::new(service.into_service(), wrap_fn)
Apply::new(service.into_service(), f)
}
/// Service factory that produces `apply_fn` service.
///
/// The In and Out type params refer to the request and response types for the wrapped service.
pub fn apply_fn_factory<I, SF, F, Fut, Req, In, Res, Err>(
service: I,
pub fn apply_fn_factory<T, F, R, In, Out, Err, U>(
service: U,
f: F,
) -> ApplyFactory<SF, F, Req, In, Res, Err>
) -> ApplyServiceFactory<T, F, R, In, Out, Err>
where
I: IntoServiceFactory<SF, In>,
SF: ServiceFactory<In, Error = Err>,
F: FnMut(Req, &mut SF::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
T: ServiceFactory<Error = Err>,
F: FnMut(In, &mut T::Service) -> R + Clone,
R: Future<Output = Result<Out, Err>>,
U: IntoServiceFactory<T>,
{
ApplyFactory::new(service.into_factory(), f)
ApplyServiceFactory::new(service.into_factory(), f)
}
/// `Apply` service combinator.
///
/// The In and Out type params refer to the request and response types for the wrapped service.
pub struct Apply<S, F, Req, In, Res, Err>
/// `Apply` service combinator
pub struct Apply<T, F, R, In, Out, Err>
where
S: Service<In, Error = Err>,
T: Service<Error = Err>,
{
service: S,
wrap_fn: F,
_phantom: PhantomData<(Req, In, Res, Err)>,
service: T,
f: F,
r: PhantomData<(In, Out, R)>,
}
impl<S, F, Fut, Req, In, Res, Err> Apply<S, F, Req, In, Res, Err>
impl<T, F, R, In, Out, Err> Apply<T, F, R, In, Out, Err>
where
S: Service<In, Error = Err>,
F: FnMut(Req, &mut S) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
T: Service<Error = Err>,
F: FnMut(In, &mut T) -> R,
R: Future<Output = Result<Out, Err>>,
{
/// Create new `Apply` combinator
fn new(service: S, wrap_fn: F) -> Self {
fn new(service: T, f: F) -> Self {
Self {
service,
wrap_fn,
_phantom: PhantomData,
f,
r: PhantomData,
}
}
}
impl<S, F, Fut, Req, In, Res, Err> Clone for Apply<S, F, Req, In, Res, Err>
impl<T, F, R, In, Out, Err> Clone for Apply<T, F, R, In, Out, Err>
where
S: Service<In, Error = Err> + Clone,
F: FnMut(Req, &mut S) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
T: Service<Error = Err> + Clone,
F: FnMut(In, &mut T) -> R + Clone,
R: Future<Output = Result<Out, Err>>,
{
fn clone(&self) -> Self {
Apply {
service: self.service.clone(),
wrap_fn: self.wrap_fn.clone(),
_phantom: PhantomData,
f: self.f.clone(),
r: PhantomData,
}
}
}
impl<S, F, Fut, Req, In, Res, Err> Service<Req> for Apply<S, F, Req, In, Res, Err>
impl<T, F, R, In, Out, Err> Service for Apply<T, F, R, In, Out, Err>
where
S: Service<In, Error = Err>,
F: FnMut(Req, &mut S) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
T: Service<Error = Err>,
F: FnMut(In, &mut T) -> R,
R: Future<Output = Result<Out, Err>>,
{
type Response = Res;
type Request = In;
type Response = Out;
type Error = Err;
type Future = Fut;
type Future = R;
crate::forward_ready!(service);
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(futures_util::ready!(self.service.poll_ready(cx)))
}
fn call(&mut self, req: Req) -> Self::Future {
(self.wrap_fn)(req, &mut self.service)
fn call(&mut self, req: In) -> Self::Future {
(self.f)(req, &mut self.service)
}
}
/// `ApplyFactory` service factory combinator.
pub struct ApplyFactory<SF, F, Req, In, Res, Err> {
factory: SF,
wrap_fn: F,
_phantom: PhantomData<(Req, In, Res, Err)>,
/// `apply()` service factory
pub struct ApplyServiceFactory<T, F, R, In, Out, Err>
where
T: ServiceFactory<Error = Err>,
F: FnMut(In, &mut T::Service) -> R + Clone,
R: Future<Output = Result<Out, Err>>,
{
service: T,
f: F,
r: PhantomData<(R, In, Out)>,
}
impl<SF, F, Fut, Req, In, Res, Err> ApplyFactory<SF, F, Req, In, Res, Err>
impl<T, F, R, In, Out, Err> ApplyServiceFactory<T, F, R, In, Out, Err>
where
SF: ServiceFactory<In, Error = Err>,
F: FnMut(Req, &mut SF::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
T: ServiceFactory<Error = Err>,
F: FnMut(In, &mut T::Service) -> R + Clone,
R: Future<Output = Result<Out, Err>>,
{
/// Create new `ApplyFactory` new service instance
fn new(factory: SF, wrap_fn: F) -> Self {
/// Create new `ApplyNewService` new service instance
fn new(service: T, f: F) -> Self {
Self {
factory,
wrap_fn,
_phantom: PhantomData,
f,
service,
r: PhantomData,
}
}
}
impl<SF, F, Fut, Req, In, Res, Err> Clone for ApplyFactory<SF, F, Req, In, Res, Err>
impl<T, F, R, In, Out, Err> Clone for ApplyServiceFactory<T, F, R, In, Out, Err>
where
SF: ServiceFactory<In, Error = Err> + Clone,
F: FnMut(Req, &mut SF::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
T: ServiceFactory<Error = Err> + Clone,
F: FnMut(In, &mut T::Service) -> R + Clone,
R: Future<Output = Result<Out, Err>>,
{
fn clone(&self) -> Self {
Self {
factory: self.factory.clone(),
wrap_fn: self.wrap_fn.clone(),
_phantom: PhantomData,
service: self.service.clone(),
f: self.f.clone(),
r: PhantomData,
}
}
}
impl<SF, F, Fut, Req, In, Res, Err> ServiceFactory<Req>
for ApplyFactory<SF, F, Req, In, Res, Err>
impl<T, F, R, In, Out, Err> ServiceFactory for ApplyServiceFactory<T, F, R, In, Out, Err>
where
SF: ServiceFactory<In, Error = Err>,
F: FnMut(Req, &mut SF::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
T: ServiceFactory<Error = Err>,
F: FnMut(In, &mut T::Service) -> R + Clone,
R: Future<Output = Result<Out, Err>>,
{
type Response = Res;
type Request = In;
type Response = Out;
type Error = Err;
type Config = SF::Config;
type Service = Apply<SF::Service, F, Req, In, Res, Err>;
type InitError = SF::InitError;
type Future = ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>;
type Config = T::Config;
type Service = Apply<T::Service, F, R, In, Out, Err>;
type InitError = T::InitError;
type Future = ApplyServiceFactoryResponse<T, F, R, In, Out, Err>;
fn new_service(&self, cfg: SF::Config) -> Self::Future {
let svc = self.factory.new_service(cfg);
ApplyServiceFactoryResponse::new(svc, self.wrap_fn.clone())
fn new_service(&self, cfg: T::Config) -> Self::Future {
ApplyServiceFactoryResponse::new(self.service.new_service(cfg), self.f.clone())
}
}
pin_project! {
pub struct ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
where
SF: ServiceFactory<In, Error = Err>,
F: FnMut(Req, &mut SF::Service) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
{
#[pin]
fut: SF::Future,
wrap_fn: Option<F>,
_phantom: PhantomData<(Req, Res)>,
}
}
impl<SF, F, Fut, Req, In, Res, Err> ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
#[pin_project::pin_project]
pub struct ApplyServiceFactoryResponse<T, F, R, In, Out, Err>
where
SF: ServiceFactory<In, Error = Err>,
F: FnMut(Req, &mut SF::Service) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
T: ServiceFactory<Error = Err>,
F: FnMut(In, &mut T::Service) -> R,
R: Future<Output = Result<Out, Err>>,
{
fn new(fut: SF::Future, wrap_fn: F) -> Self {
#[pin]
fut: T::Future,
f: Option<F>,
r: PhantomData<(In, Out)>,
}
impl<T, F, R, In, Out, Err> ApplyServiceFactoryResponse<T, F, R, In, Out, Err>
where
T: ServiceFactory<Error = Err>,
F: FnMut(In, &mut T::Service) -> R,
R: Future<Output = Result<Out, Err>>,
{
fn new(fut: T::Future, f: F) -> Self {
Self {
f: Some(f),
fut,
wrap_fn: Some(wrap_fn),
_phantom: PhantomData,
r: PhantomData,
}
}
}
impl<SF, F, Fut, Req, In, Res, Err> Future
for ApplyServiceFactoryResponse<SF, F, Fut, Req, In, Res, Err>
impl<T, F, R, In, Out, Err> Future for ApplyServiceFactoryResponse<T, F, R, In, Out, Err>
where
SF: ServiceFactory<In, Error = Err>,
F: FnMut(Req, &mut SF::Service) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
T: ServiceFactory<Error = Err>,
F: FnMut(In, &mut T::Service) -> R,
R: Future<Output = Result<Out, Err>>,
{
type Output = Result<Apply<SF::Service, F, Req, In, Res, Err>, SF::InitError>;
type Output = Result<Apply<T::Service, F, R, In, Out, Err>, T::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let svc = ready!(this.fut.poll(cx))?;
Poll::Ready(Ok(Apply::new(svc, this.wrap_fn.take().unwrap())))
if let Poll::Ready(svc) = this.fut.poll(cx)? {
Poll::Ready(Ok(Apply::new(svc, this.f.take().unwrap())))
} else {
Poll::Pending
}
}
}
#[cfg(test)]
mod tests {
use core::task::Poll;
use std::task::{Context, Poll};
use futures_util::future::lazy;
use futures_util::future::{lazy, ok, Ready};
use super::*;
use crate::{ok, pipeline, pipeline_factory, Ready, Service, ServiceFactory};
use crate::{pipeline, pipeline_factory, Service, ServiceFactory};
#[derive(Clone)]
struct Srv;
impl Service<()> for Srv {
impl Service for Srv {
type Request = ();
type Response = ();
type Error = ();
type Future = Ready<Result<(), ()>>;
crate::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
ok(())

View File

@@ -1,237 +1,227 @@
use alloc::rc::Rc;
use core::{
cell::RefCell,
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use std::cell::RefCell;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use crate::{Service, ServiceFactory};
/// Convert `Fn(Config, &mut Service1) -> Future<Service2>` fn to a service factory.
pub fn apply_cfg<S1, Req, F, Cfg, Fut, S2, Err>(
srv: S1,
/// Convert `Fn(Config, &mut Service1) -> Future<Service2>` fn to a service factory
pub fn apply_cfg<F, C, T, R, S, E>(
srv: T,
f: F,
) -> impl ServiceFactory<
Req,
Config = Cfg,
Response = S2::Response,
Error = S2::Error,
Service = S2,
InitError = Err,
Future = Fut,
> + Clone
where
S1: Service<Req>,
F: FnMut(Cfg, &mut S1) -> Fut,
Fut: Future<Output = Result<S2, Err>>,
S2: Service<Req>,
{
ApplyConfigService {
srv: Rc::new(RefCell::new((srv, f))),
_phantom: PhantomData,
}
}
/// Convert `Fn(Config, &mut ServiceFactory1) -> Future<ServiceFactory2>` fn to a service factory.
///
/// Service1 get constructed from `T` factory.
pub fn apply_cfg_factory<SF, Req, F, Cfg, Fut, S>(
factory: SF,
f: F,
) -> impl ServiceFactory<
Req,
Config = Cfg,
Config = C,
Request = S::Request,
Response = S::Response,
Error = S::Error,
Service = S,
InitError = SF::InitError,
InitError = E,
Future = R,
> + Clone
where
SF: ServiceFactory<Req, Config = ()>,
F: FnMut(Cfg, &mut SF::Service) -> Fut,
SF::InitError: From<SF::Error>,
Fut: Future<Output = Result<S, SF::InitError>>,
S: Service<Req>,
F: FnMut(C, &mut T) -> R,
T: Service,
R: Future<Output = Result<S, E>>,
S: Service,
{
ApplyConfigService {
srv: Rc::new(RefCell::new((srv, f))),
_t: PhantomData,
}
}
/// Convert `Fn(Config, &mut Service1) -> Future<Service2>` fn to a service factory
///
/// Service1 get constructed from `T` factory.
pub fn apply_cfg_factory<F, C, T, R, S>(
factory: T,
f: F,
) -> impl ServiceFactory<
Config = C,
Request = S::Request,
Response = S::Response,
Error = S::Error,
Service = S,
InitError = T::InitError,
> + Clone
where
F: FnMut(C, &mut T::Service) -> R,
T: ServiceFactory<Config = ()>,
T::InitError: From<T::Error>,
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{
ApplyConfigServiceFactory {
srv: Rc::new(RefCell::new((factory, f))),
_phantom: PhantomData,
_t: PhantomData,
}
}
/// Convert `Fn(Config, &mut Server) -> Future<Service>` fn to NewService\
struct ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err>
struct ApplyConfigService<F, C, T, R, S, E>
where
S1: Service<Req>,
F: FnMut(Cfg, &mut S1) -> Fut,
Fut: Future<Output = Result<S2, Err>>,
S2: Service<Req>,
F: FnMut(C, &mut T) -> R,
T: Service,
R: Future<Output = Result<S, E>>,
S: Service,
{
srv: Rc<RefCell<(S1, F)>>,
_phantom: PhantomData<(Cfg, Req, Fut, S2)>,
srv: Rc<RefCell<(T, F)>>,
_t: PhantomData<(C, R, S)>,
}
impl<S1, Req, F, Cfg, Fut, S2, Err> Clone for ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err>
impl<F, C, T, R, S, E> Clone for ApplyConfigService<F, C, T, R, S, E>
where
S1: Service<Req>,
F: FnMut(Cfg, &mut S1) -> Fut,
Fut: Future<Output = Result<S2, Err>>,
S2: Service<Req>,
F: FnMut(C, &mut T) -> R,
T: Service,
R: Future<Output = Result<S, E>>,
S: Service,
{
fn clone(&self) -> Self {
ApplyConfigService {
srv: self.srv.clone(),
_phantom: PhantomData,
_t: PhantomData,
}
}
}
impl<S1, Req, F, Cfg, Fut, S2, Err> ServiceFactory<Req>
for ApplyConfigService<S1, Req, F, Cfg, Fut, S2, Err>
impl<F, C, T, R, S, E> ServiceFactory for ApplyConfigService<F, C, T, R, S, E>
where
S1: Service<Req>,
F: FnMut(Cfg, &mut S1) -> Fut,
Fut: Future<Output = Result<S2, Err>>,
S2: Service<Req>,
F: FnMut(C, &mut T) -> R,
T: Service,
R: Future<Output = Result<S, E>>,
S: Service,
{
type Config = Cfg;
type Response = S2::Response;
type Error = S2::Error;
type Service = S2;
type Config = C;
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Service = S;
type InitError = Err;
type Future = Fut;
type InitError = E;
type Future = R;
fn new_service(&self, cfg: Cfg) -> Self::Future {
fn new_service(&self, cfg: C) -> Self::Future {
let (t, f) = &mut *self.srv.borrow_mut();
f(cfg, t)
}
}
/// Convert `Fn(&Config) -> Future<Service>` fn to NewService
struct ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S>
struct ApplyConfigServiceFactory<F, C, T, R, S>
where
SF: ServiceFactory<Req, Config = ()>,
F: FnMut(Cfg, &mut SF::Service) -> Fut,
Fut: Future<Output = Result<S, SF::InitError>>,
S: Service<Req>,
F: FnMut(C, &mut T::Service) -> R,
T: ServiceFactory<Config = ()>,
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{
srv: Rc<RefCell<(SF, F)>>,
_phantom: PhantomData<(Cfg, Req, Fut, S)>,
srv: Rc<RefCell<(T, F)>>,
_t: PhantomData<(C, R, S)>,
}
impl<SF, Req, F, Cfg, Fut, S> Clone for ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S>
impl<F, C, T, R, S> Clone for ApplyConfigServiceFactory<F, C, T, R, S>
where
SF: ServiceFactory<Req, Config = ()>,
F: FnMut(Cfg, &mut SF::Service) -> Fut,
Fut: Future<Output = Result<S, SF::InitError>>,
S: Service<Req>,
F: FnMut(C, &mut T::Service) -> R,
T: ServiceFactory<Config = ()>,
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{
fn clone(&self) -> Self {
Self {
srv: self.srv.clone(),
_phantom: PhantomData,
_t: PhantomData,
}
}
}
impl<SF, Req, F, Cfg, Fut, S> ServiceFactory<Req>
for ApplyConfigServiceFactory<SF, Req, F, Cfg, Fut, S>
impl<F, C, T, R, S> ServiceFactory for ApplyConfigServiceFactory<F, C, T, R, S>
where
SF: ServiceFactory<Req, Config = ()>,
SF::InitError: From<SF::Error>,
F: FnMut(Cfg, &mut SF::Service) -> Fut,
Fut: Future<Output = Result<S, SF::InitError>>,
S: Service<Req>,
F: FnMut(C, &mut T::Service) -> R,
T: ServiceFactory<Config = ()>,
T::InitError: From<T::Error>,
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{
type Config = Cfg;
type Config = C;
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Service = S;
type InitError = SF::InitError;
type Future = ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>;
type InitError = T::InitError;
type Future = ApplyConfigServiceFactoryResponse<F, C, T, R, S>;
fn new_service(&self, cfg: Cfg) -> Self::Future {
fn new_service(&self, cfg: C) -> Self::Future {
ApplyConfigServiceFactoryResponse {
cfg: Some(cfg),
store: self.srv.clone(),
state: State::A {
fut: self.srv.borrow().0.new_service(()),
},
state: State::A(self.srv.borrow().0.new_service(())),
}
}
}
pin_project! {
struct ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>
where
SF: ServiceFactory<Req, Config = ()>,
SF::InitError: From<SF::Error>,
F: FnMut(Cfg, &mut SF::Service) -> Fut,
Fut: Future<Output = Result<S, SF::InitError>>,
S: Service<Req>,
{
cfg: Option<Cfg>,
store: Rc<RefCell<(SF, F)>>,
#[pin]
state: State<SF, Fut, S, Req>,
}
}
pin_project! {
#[project = StateProj]
enum State<SF, Fut, S, Req>
where
SF: ServiceFactory<Req, Config = ()>,
SF::InitError: From<SF::Error>,
Fut: Future<Output = Result<S, SF::InitError>>,
S: Service<Req>,
{
A { #[pin] fut: SF::Future },
B { svc: SF::Service },
C { #[pin] fut: Fut },
}
}
impl<SF, Req, F, Cfg, Fut, S> Future
for ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>
#[pin_project::pin_project]
struct ApplyConfigServiceFactoryResponse<F, C, T, R, S>
where
SF: ServiceFactory<Req, Config = ()>,
SF::InitError: From<SF::Error>,
F: FnMut(Cfg, &mut SF::Service) -> Fut,
Fut: Future<Output = Result<S, SF::InitError>>,
S: Service<Req>,
F: FnMut(C, &mut T::Service) -> R,
T: ServiceFactory<Config = ()>,
T::InitError: From<T::Error>,
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{
type Output = Result<S, SF::InitError>;
cfg: Option<C>,
store: Rc<RefCell<(T, F)>>,
#[pin]
state: State<T, R, S>,
}
#[pin_project::pin_project(project = StateProj)]
enum State<T, R, S>
where
T: ServiceFactory<Config = ()>,
T::InitError: From<T::Error>,
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{
A(#[pin] T::Future),
B(T::Service),
C(#[pin] R),
}
impl<F, C, T, R, S> Future for ApplyConfigServiceFactoryResponse<F, C, T, R, S>
where
F: FnMut(C, &mut T::Service) -> R,
T: ServiceFactory<Config = ()>,
T::InitError: From<T::Error>,
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{
type Output = Result<S, T::InitError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
match this.state.as_mut().project() {
StateProj::A { fut } => match fut.poll(cx)? {
StateProj::A(fut) => match fut.poll(cx)? {
Poll::Pending => Poll::Pending,
Poll::Ready(svc) => {
this.state.set(State::B { svc });
Poll::Ready(srv) => {
this.state.set(State::B(srv));
self.poll(cx)
}
},
StateProj::B { svc } => match svc.poll_ready(cx)? {
StateProj::B(srv) => match srv.poll_ready(cx)? {
Poll::Ready(_) => {
{
let (_, f) = &mut *this.store.borrow_mut();
let fut = f(this.cfg.take().unwrap(), svc);
this.state.set(State::C { fut });
let fut = f(this.cfg.take().unwrap(), srv);
this.state.set(State::C(fut));
}
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
StateProj::C { fut } => fut.poll(cx),
StateProj::C(fut) => fut.poll(cx),
}
}
}

View File

@@ -1,141 +1,145 @@
use alloc::boxed::Box;
use core::{
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::future::FutureExt;
use crate::{Service, ServiceFactory};
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
pub type BoxFuture<I, E> = Pin<Box<dyn Future<Output = Result<I, E>>>>;
pub type BoxService<Req, Res, Err> =
Box<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>;
Box<dyn Service<Request = Req, Response = Res, Error = Err, Future = BoxFuture<Res, Err>>>;
pub struct BoxServiceFactory<Cfg, Req, Res, Err, InitErr>(Inner<Cfg, Req, Res, Err, InitErr>);
pub struct BoxServiceFactory<C, Req, Res, Err, InitErr>(Inner<C, Req, Res, Err, InitErr>);
/// Create boxed service factory
pub fn factory<SF, Req>(
factory: SF,
) -> BoxServiceFactory<SF::Config, Req, SF::Response, SF::Error, SF::InitError>
pub fn factory<T>(
factory: T,
) -> BoxServiceFactory<T::Config, T::Request, T::Response, T::Error, T::InitError>
where
SF: ServiceFactory<Req> + 'static,
Req: 'static,
SF::Response: 'static,
SF::Service: 'static,
SF::Future: 'static,
SF::Error: 'static,
SF::InitError: 'static,
T: ServiceFactory + 'static,
T::Request: 'static,
T::Response: 'static,
T::Service: 'static,
T::Future: 'static,
T::Error: 'static,
T::InitError: 'static,
{
BoxServiceFactory(Box::new(FactoryWrapper {
factory,
_t: PhantomData,
_t: std::marker::PhantomData,
}))
}
/// Create boxed service
pub fn service<S, Req>(service: S) -> BoxService<Req, S::Response, S::Error>
pub fn service<T>(service: T) -> BoxService<T::Request, T::Response, T::Error>
where
S: Service<Req> + 'static,
Req: 'static,
S::Future: 'static,
T: Service + 'static,
T::Future: 'static,
{
Box::new(ServiceWrapper(service, PhantomData))
Box::new(ServiceWrapper(service))
}
type Inner<C, Req, Res, Err, InitErr> = Box<
dyn ServiceFactory<
Req,
Config = C,
Request = Req,
Response = Res,
Error = Err,
InitError = InitErr,
Service = BoxService<Req, Res, Err>,
Future = BoxFuture<Result<BoxService<Req, Res, Err>, InitErr>>,
Future = BoxFuture<BoxService<Req, Res, Err>, InitErr>,
>,
>;
impl<C, Req, Res, Err, InitErr> ServiceFactory<Req>
for BoxServiceFactory<C, Req, Res, Err, InitErr>
impl<C, Req, Res, Err, InitErr> ServiceFactory for BoxServiceFactory<C, Req, Res, Err, InitErr>
where
Req: 'static,
Res: 'static,
Err: 'static,
InitErr: 'static,
{
type Request = Req;
type Response = Res;
type Error = Err;
type InitError = InitErr;
type Config = C;
type Service = BoxService<Req, Res, Err>;
type Future = BoxFuture<Result<Self::Service, InitErr>>;
type Future = BoxFuture<Self::Service, InitErr>;
fn new_service(&self, cfg: C) -> Self::Future {
self.0.new_service(cfg)
}
}
struct FactoryWrapper<SF, Req, Cfg> {
factory: SF,
_t: PhantomData<(Req, Cfg)>,
struct FactoryWrapper<C, T: ServiceFactory> {
factory: T,
_t: std::marker::PhantomData<C>,
}
impl<SF, Req, Cfg, Res, Err, InitErr> ServiceFactory<Req> for FactoryWrapper<SF, Req, Cfg>
impl<C, T, Req, Res, Err, InitErr> ServiceFactory for FactoryWrapper<C, T>
where
Req: 'static,
Res: 'static,
Err: 'static,
InitErr: 'static,
SF: ServiceFactory<Req, Config = Cfg, Response = Res, Error = Err, InitError = InitErr>,
SF::Future: 'static,
SF::Service: 'static,
<SF::Service as Service<Req>>::Future: 'static,
T: ServiceFactory<
Config = C,
Request = Req,
Response = Res,
Error = Err,
InitError = InitErr,
>,
T::Future: 'static,
T::Service: 'static,
<T::Service as Service>::Future: 'static,
{
type Request = Req;
type Response = Res;
type Error = Err;
type InitError = InitErr;
type Config = Cfg;
type Config = C;
type Service = BoxService<Req, Res, Err>;
type Future = BoxFuture<Result<Self::Service, Self::InitError>>;
type Future = BoxFuture<Self::Service, Self::InitError>;
fn new_service(&self, cfg: Cfg) -> Self::Future {
let fut = self.factory.new_service(cfg);
Box::pin(async {
let res = fut.await;
res.map(ServiceWrapper::boxed)
})
fn new_service(&self, cfg: C) -> Self::Future {
Box::pin(
self.factory
.new_service(cfg)
.map(|res| res.map(ServiceWrapper::boxed)),
)
}
}
struct ServiceWrapper<S: Service<Req>, Req>(S, PhantomData<Req>);
struct ServiceWrapper<T: Service>(T);
impl<S, Req> ServiceWrapper<S, Req>
impl<T> ServiceWrapper<T>
where
S: Service<Req> + 'static,
Req: 'static,
S::Future: 'static,
T: Service + 'static,
T::Future: 'static,
{
fn boxed(service: S) -> BoxService<Req, S::Response, S::Error> {
Box::new(ServiceWrapper(service, PhantomData))
fn boxed(service: T) -> BoxService<T::Request, T::Response, T::Error> {
Box::new(ServiceWrapper(service))
}
}
impl<S, Req, Res, Err> Service<Req> for ServiceWrapper<S, Req>
impl<T, Req, Res, Err> Service for ServiceWrapper<T>
where
S: Service<Req, Response = Res, Error = Err>,
S::Future: 'static,
T: Service<Request = Req, Response = Res, Error = Err>,
T::Future: 'static,
{
type Request = Req;
type Response = Res;
type Error = Err;
type Future = BoxFuture<Result<Res, Err>>;
type Future = BoxFuture<Res, Err>;
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.0.poll_ready(ctx)
}
fn call(&mut self, req: Req) -> Self::Future {
fn call(&mut self, req: Self::Request) -> Self::Future {
Box::pin(self.0.call(req))
}
}

View File

@@ -1,70 +0,0 @@
use crate::{dev, Service, ServiceFactory};
pub trait ServiceExt<Req>: Service<Req> {
/// 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<F, R>(self, f: F) -> dev::Map<Self, F, Req, R>
where
Self: Sized,
F: FnMut(Self::Response) -> R,
{
dev::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. For example, this can be useful 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<F, E>(self, f: F) -> dev::MapErr<Self, Req, F, E>
where
Self: Sized,
F: Fn(Self::Error) -> E,
{
dev::MapErr::new(self, f)
}
}
impl<S, Req> ServiceExt<Req> for S where S: Service<Req> {}
pub trait ServiceFactoryExt<Req>: ServiceFactory<Req> {
/// Map this service's output to a different type, returning a new service
/// of the resulting type.
fn map<F, R>(self, f: F) -> crate::map::MapServiceFactory<Self, F, Req, R>
where
Self: Sized,
F: FnMut(Self::Response) -> R + Clone,
{
crate::map::MapServiceFactory::new(self, f)
}
/// Map this service's error to a different error, returning a new service.
fn map_err<F, E>(self, f: F) -> crate::map_err::MapErrServiceFactory<Self, Req, F, E>
where
Self: Sized,
F: Fn(Self::Error) -> E + Clone,
{
crate::map_err::MapErrServiceFactory::new(self, f)
}
/// Map this factory's init error to a different error, returning a new service.
fn map_init_err<F, E>(self, f: F) -> crate::map_init_err::MapInitErr<Self, F, Req, E>
where
Self: Sized,
F: Fn(Self::InitError) -> E + Clone,
{
crate::map_init_err::MapInitErr::new(self, f)
}
}
impl<S, Req> ServiceFactoryExt<Req> for S where S: ServiceFactory<Req> {}

View File

@@ -1,6 +1,10 @@
use core::{future::Future, marker::PhantomData, task::Poll};
use std::future::Future;
use std::marker::PhantomData;
use std::task::{Context, Poll};
use crate::{ok, IntoService, IntoServiceFactory, Ready, Service, ServiceFactory};
use futures_util::future::{ok, Ready};
use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory};
/// Create `ServiceFactory` for function that can act as a `Service`
pub fn fn_service<F, Fut, Req, Res, Err, Cfg>(
@@ -49,11 +53,9 @@ where
/// Ok(())
/// }
/// ```
pub fn fn_factory<F, Cfg, Srv, Req, Fut, Err>(
f: F,
) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
pub fn fn_factory<F, Cfg, Srv, Fut, Err>(f: F) -> FnServiceNoConfig<F, Cfg, Srv, Fut, Err>
where
Srv: Service<Req>,
Srv: Service,
F: Fn() -> Fut,
Fut: Future<Output = Result<Srv, Err>>,
{
@@ -90,13 +92,13 @@ where
/// Ok(())
/// }
/// ```
pub fn fn_factory_with_config<F, Fut, Cfg, Srv, Req, Err>(
pub fn fn_factory_with_config<F, Fut, Cfg, Srv, Err>(
f: F,
) -> FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
) -> FnServiceConfig<F, Fut, Cfg, Srv, Err>
where
F: Fn(Cfg) -> Fut,
Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>,
Srv: Service,
{
FnServiceConfig::new(f)
}
@@ -130,23 +132,26 @@ where
}
}
impl<F, Fut, Req, Res, Err> Service<Req> for FnService<F, Fut, Req, Res, Err>
impl<F, Fut, Req, Res, Err> Service for FnService<F, Fut, Req, Res, Err>
where
F: FnMut(Req) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
{
type Request = Req;
type Response = Res;
type Error = Err;
type Future = Fut;
crate::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Req) -> Self::Future {
(self.f)(req)
}
}
impl<F, Fut, Req, Res, Err> IntoService<FnService<F, Fut, Req, Res, Err>, Req> for F
impl<F, Fut, Req, Res, Err> IntoService<FnService<F, Fut, Req, Res, Err>> for F
where
F: FnMut(Req) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
@@ -185,28 +190,31 @@ where
}
}
impl<F, Fut, Req, Res, Err> Service<Req> for FnServiceFactory<F, Fut, Req, Res, Err, ()>
impl<F, Fut, Req, Res, Err> Service for FnServiceFactory<F, Fut, Req, Res, Err, ()>
where
F: FnMut(Req) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
{
type Request = Req;
type Response = Res;
type Error = Err;
type Future = Fut;
crate::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Req) -> Self::Future {
fn call(&mut self, req: Self::Request) -> Self::Future {
(self.f)(req)
}
}
impl<F, Fut, Req, Res, Err, Cfg> ServiceFactory<Req>
for FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
impl<F, Fut, Req, Res, Err, Cfg> ServiceFactory for FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
where
F: FnMut(Req) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
{
type Request = Req;
type Response = Res;
type Error = Err;
@@ -221,7 +229,7 @@ where
}
impl<F, Fut, Req, Res, Err, Cfg>
IntoServiceFactory<FnServiceFactory<F, Fut, Req, Res, Err, Cfg>, Req> for F
IntoServiceFactory<FnServiceFactory<F, Fut, Req, Res, Err, Cfg>> for F
where
F: Fn(Req) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
@@ -232,32 +240,32 @@ where
}
/// Convert `Fn(&Config) -> Future<Service>` fn to NewService
pub struct FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
pub struct FnServiceConfig<F, Fut, Cfg, Srv, Err>
where
F: Fn(Cfg) -> Fut,
Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>,
Srv: Service,
{
f: F,
_t: PhantomData<(Fut, Cfg, Req, Srv, Err)>,
_t: PhantomData<(Fut, Cfg, Srv, Err)>,
}
impl<F, Fut, Cfg, Srv, Req, Err> FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
impl<F, Fut, Cfg, Srv, Err> FnServiceConfig<F, Fut, Cfg, Srv, Err>
where
F: Fn(Cfg) -> Fut,
Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>,
Srv: Service,
{
fn new(f: F) -> Self {
FnServiceConfig { f, _t: PhantomData }
}
}
impl<F, Fut, Cfg, Srv, Req, Err> Clone for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
impl<F, Fut, Cfg, Srv, Err> Clone for FnServiceConfig<F, Fut, Cfg, Srv, Err>
where
F: Fn(Cfg) -> Fut + Clone,
Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>,
Srv: Service,
{
fn clone(&self) -> Self {
FnServiceConfig {
@@ -267,13 +275,13 @@ where
}
}
impl<F, Fut, Cfg, Srv, Req, Err> ServiceFactory<Req>
for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
impl<F, Fut, Cfg, Srv, Err> ServiceFactory for FnServiceConfig<F, Fut, Cfg, Srv, Err>
where
F: Fn(Cfg) -> Fut,
Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>,
Srv: Service,
{
type Request = Srv::Request;
type Response = Srv::Response;
type Error = Srv::Error;
@@ -288,77 +296,76 @@ where
}
/// Converter for `Fn() -> Future<Service>` fn
pub struct FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
pub struct FnServiceNoConfig<F, C, S, R, E>
where
F: Fn() -> Fut,
Srv: Service<Req>,
Fut: Future<Output = Result<Srv, Err>>,
F: Fn() -> R,
S: Service,
R: Future<Output = Result<S, E>>,
{
f: F,
_t: PhantomData<(Cfg, Req)>,
_t: PhantomData<C>,
}
impl<F, Cfg, Srv, Req, Fut, Err> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
impl<F, C, S, R, E> FnServiceNoConfig<F, C, S, R, E>
where
F: Fn() -> Fut,
Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>,
F: Fn() -> R,
R: Future<Output = Result<S, E>>,
S: Service,
{
fn new(f: F) -> Self {
Self { f, _t: PhantomData }
}
}
impl<F, Cfg, Srv, Req, Fut, Err> ServiceFactory<Req>
for FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
impl<F, C, S, R, E> ServiceFactory for FnServiceNoConfig<F, C, S, R, E>
where
F: Fn() -> Fut,
Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>,
F: Fn() -> R,
R: Future<Output = Result<S, E>>,
S: Service,
{
type Response = Srv::Response;
type Error = Srv::Error;
type Service = Srv;
type Config = Cfg;
type InitError = Err;
type Future = Fut;
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Service = S;
type Config = C;
type InitError = E;
type Future = R;
fn new_service(&self, _: Cfg) -> Self::Future {
fn new_service(&self, _: C) -> Self::Future {
(self.f)()
}
}
impl<F, Cfg, Srv, Req, Fut, Err> Clone for FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
impl<F, C, S, R, E> Clone for FnServiceNoConfig<F, C, S, R, E>
where
F: Fn() -> Fut + Clone,
Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>,
F: Fn() -> R + Clone,
R: Future<Output = Result<S, E>>,
S: Service,
{
fn clone(&self) -> Self {
Self::new(self.f.clone())
}
}
impl<F, Cfg, Srv, Req, Fut, Err>
IntoServiceFactory<FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>, Req> for F
impl<F, C, S, R, E> IntoServiceFactory<FnServiceNoConfig<F, C, S, R, E>> for F
where
F: Fn() -> Fut,
Fut: Future<Output = Result<Srv, Err>>,
Srv: Service<Req>,
F: Fn() -> R,
R: Future<Output = Result<S, E>>,
S: Service,
{
fn into_factory(self) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err> {
fn into_factory(self) -> FnServiceNoConfig<F, C, S, R, E> {
FnServiceNoConfig::new(self)
}
}
#[cfg(test)]
mod tests {
use core::task::Poll;
use std::task::Poll;
use futures_util::future::lazy;
use futures_util::future::{lazy, ok};
use super::*;
use crate::{ok, Service, ServiceFactory};
use crate::{Service, ServiceFactory};
#[actix_rt::test]
async fn test_fn_service() {

View File

@@ -1,47 +1,38 @@
//! See [`Service`] docs for information on this crate's foundational trait.
#![no_std]
#![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
extern crate alloc;
use alloc::{boxed::Box, rc::Rc, sync::Arc};
use core::{
cell::RefCell,
future::Future,
task::{self, Context, Poll},
};
use std::cell::RefCell;
use std::future::Future;
use std::rc::Rc;
use std::sync::Arc;
use std::task::{self, Context, Poll};
mod and_then;
mod and_then_apply_fn;
mod apply;
mod apply_cfg;
pub mod boxed;
mod ext;
mod fn_service;
mod map;
mod map_config;
mod map_err;
mod map_init_err;
mod pipeline;
mod ready;
mod then;
mod transform;
mod transform_err;
pub use self::apply::{apply_fn, apply_fn_factory};
pub use self::apply_cfg::{apply_cfg, apply_cfg_factory};
pub use self::ext::{ServiceExt, ServiceFactoryExt};
pub use self::fn_service::{fn_factory, fn_factory_with_config, fn_service};
pub use self::map_config::{map_config, unit_config};
pub use self::pipeline::{pipeline, pipeline_factory, Pipeline, PipelineFactory};
pub use self::transform::{apply, Transform};
#[allow(unused_imports)]
use self::ready::{err, ok, ready, Ready};
/// An asynchronous operation from `Request` to a `Response`.
///
/// The `Service` trait models a request/response interaction, receiving requests and returning
@@ -81,11 +72,14 @@ use self::ready::{err, ok, ready, Ready};
/// ```rust,ignore
/// async fn my_service(req: u8) -> Result<u64, MyError>;
/// ```
pub trait Service<Req> {
pub trait Service {
/// Requests handled by the service.
type Request;
/// Responses given by the service.
type Response;
/// Errors produced by the service when polling readiness or executing call.
/// Errors produced by the service.
type Error;
/// The future response value.
@@ -115,7 +109,40 @@ pub trait Service<Req> {
///
/// Calling `call` without calling `poll_ready` is permitted. The
/// implementation must be resilient to this fact.
fn call(&mut self, req: Req) -> Self::Future;
fn call(&mut self, req: Self::Request) -> Self::Future;
/// 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<F, R>(self, f: F) -> crate::dev::Map<Self, F, R>
where
Self: Sized,
F: FnMut(Self::Response) -> R,
{
crate::dev::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. For example, this can be useful 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<F, E>(self, f: F) -> crate::dev::MapErr<Self, F, E>
where
Self: Sized,
F: Fn(Self::Error) -> E,
{
crate::dev::MapErr::new(self, f)
}
}
/// Factory for creating `Service`s.
@@ -127,7 +154,10 @@ pub trait Service<Req> {
/// requests on that new TCP stream.
///
/// `Config` is a service factory configuration type.
pub trait ServiceFactory<Req> {
pub trait ServiceFactory {
/// Requests handled by the created services.
type Request;
/// Responses given by the created services.
type Response;
@@ -138,7 +168,11 @@ pub trait ServiceFactory<Req> {
type Config;
/// The kind of `Service` created by this factory.
type Service: Service<Req, Response = Self::Response, Error = Self::Error>;
type Service: Service<
Request = Self::Request,
Response = Self::Response,
Error = Self::Error,
>;
/// Errors potentially raised while building a service.
type InitError;
@@ -148,12 +182,41 @@ pub trait ServiceFactory<Req> {
/// Create and return a new service asynchronously.
fn new_service(&self, cfg: Self::Config) -> Self::Future;
/// Map this service's output to a different type, returning a new service
/// of the resulting type.
fn map<F, R>(self, f: F) -> crate::map::MapServiceFactory<Self, F, R>
where
Self: Sized,
F: FnMut(Self::Response) -> R + Clone,
{
crate::map::MapServiceFactory::new(self, f)
}
/// Map this service's error to a different error, returning a new service.
fn map_err<F, E>(self, f: F) -> crate::map_err::MapErrServiceFactory<Self, F, E>
where
Self: Sized,
F: Fn(Self::Error) -> E + Clone,
{
crate::map_err::MapErrServiceFactory::new(self, f)
}
/// Map this factory's init error to a different error, returning a new service.
fn map_init_err<F, E>(self, f: F) -> crate::map_init_err::MapInitErr<Self, F, E>
where
Self: Sized,
F: Fn(Self::InitError) -> E + Clone,
{
crate::map_init_err::MapInitErr::new(self, f)
}
}
impl<'a, S, Req> Service<Req> for &'a mut S
impl<'a, S> Service for &'a mut S
where
S: Service<Req> + 'a,
S: Service + 'a,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
@@ -162,15 +225,16 @@ where
(**self).poll_ready(ctx)
}
fn call(&mut self, request: Req) -> S::Future {
fn call(&mut self, request: Self::Request) -> S::Future {
(**self).call(request)
}
}
impl<S, Req> Service<Req> for Box<S>
impl<S> Service for Box<S>
where
S: Service<Req> + ?Sized,
S: Service + ?Sized,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
@@ -179,15 +243,16 @@ where
(**self).poll_ready(ctx)
}
fn call(&mut self, request: Req) -> S::Future {
fn call(&mut self, request: Self::Request) -> S::Future {
(**self).call(request)
}
}
impl<S, Req> Service<Req> for RefCell<S>
impl<S> Service for RefCell<S>
where
S: Service<Req>,
S: Service,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
@@ -196,15 +261,16 @@ where
self.borrow_mut().poll_ready(ctx)
}
fn call(&mut self, request: Req) -> S::Future {
fn call(&mut self, request: Self::Request) -> S::Future {
self.borrow_mut().call(request)
}
}
impl<S, Req> Service<Req> for Rc<RefCell<S>>
impl<S> Service for Rc<RefCell<S>>
where
S: Service<Req>,
S: Service,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
@@ -213,15 +279,16 @@ where
self.borrow_mut().poll_ready(ctx)
}
fn call(&mut self, request: Req) -> S::Future {
fn call(&mut self, request: Self::Request) -> S::Future {
(&mut (**self).borrow_mut()).call(request)
}
}
impl<S, Req> ServiceFactory<Req> for Rc<S>
impl<S> ServiceFactory for Rc<S>
where
S: ServiceFactory<Req>,
S: ServiceFactory,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Config = S::Config;
@@ -234,10 +301,11 @@ where
}
}
impl<S, Req> ServiceFactory<Req> for Arc<S>
impl<S> ServiceFactory for Arc<S>
where
S: ServiceFactory<Req>,
S: ServiceFactory,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Config = S::Config;
@@ -251,52 +319,52 @@ where
}
/// Trait for types that can be converted to a `Service`
pub trait IntoService<S, Req>
pub trait IntoService<T>
where
S: Service<Req>,
T: Service,
{
/// Convert to a `Service`
fn into_service(self) -> S;
fn into_service(self) -> T;
}
/// Trait for types that can be converted to a `ServiceFactory`
pub trait IntoServiceFactory<SF, Req>
pub trait IntoServiceFactory<T>
where
SF: ServiceFactory<Req>,
T: ServiceFactory,
{
/// Convert `Self` to a `ServiceFactory`
fn into_factory(self) -> SF;
fn into_factory(self) -> T;
}
impl<S, Req> IntoService<S, Req> for S
impl<T> IntoService<T> for T
where
S: Service<Req>,
T: Service,
{
fn into_service(self) -> S {
fn into_service(self) -> T {
self
}
}
impl<SF, Req> IntoServiceFactory<SF, Req> for SF
impl<T> IntoServiceFactory<T> for T
where
SF: ServiceFactory<Req>,
T: ServiceFactory,
{
fn into_factory(self) -> SF {
fn into_factory(self) -> T {
self
}
}
/// Convert object of type `U` to a service `S`
pub fn into_service<I, S, Req>(tp: I) -> S
/// Convert object of type `T` to a service `S`
pub fn into_service<T, S>(tp: T) -> S
where
I: IntoService<S, Req>,
S: Service<Req>,
S: Service,
T: IntoService<S>,
{
tp.into_service()
}
pub mod dev {
pub use crate::apply::{Apply, ApplyFactory};
pub use crate::apply::{Apply, ApplyServiceFactory};
pub use crate::fn_service::{
FnService, FnServiceConfig, FnServiceFactory, FnServiceNoConfig,
};
@@ -307,27 +375,3 @@ pub mod dev {
pub use crate::transform::ApplyTransform;
pub use crate::transform_err::TransformMapInitErr;
}
#[macro_export]
macro_rules! always_ready {
() => {
fn poll_ready(
&mut self,
_: &mut ::core::task::Context<'_>,
) -> ::core::task::Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
};
}
#[macro_export]
macro_rules! forward_ready {
($field:ident) => {
fn poll_ready(
&mut self,
cx: &mut ::core::task::Context<'_>,
) -> ::core::task::Poll<Result<(), Self::Error>> {
self.$field.poll_ready(cx)
}
};
}

View File

@@ -1,29 +1,25 @@
use core::{
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
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<A, F, Req, Res> {
pub struct Map<A, F, Response> {
service: A,
f: F,
_t: PhantomData<(Req, Res)>,
_t: PhantomData<Response>,
}
impl<A, F, Req, Res> Map<A, F, Req, Res> {
impl<A, F, Response> Map<A, F, Response> {
/// Create new `Map` combinator
pub(crate) fn new(service: A, f: F) -> Self
where
A: Service<Req>,
F: FnMut(A::Response) -> Res,
A: Service,
F: FnMut(A::Response) -> Response,
{
Self {
service,
@@ -33,7 +29,7 @@ impl<A, F, Req, Res> Map<A, F, Req, Res> {
}
}
impl<A, F, Req, Res> Clone for Map<A, F, Req, Res>
impl<A, F, Response> Clone for Map<A, F, Response>
where
A: Clone,
F: Clone,
@@ -47,50 +43,52 @@ where
}
}
impl<A, F, Req, Res> Service<Req> for Map<A, F, Req, Res>
impl<A, F, Response> Service for Map<A, F, Response>
where
A: Service<Req>,
F: FnMut(A::Response) -> Res + Clone,
A: Service,
F: FnMut(A::Response) -> Response + Clone,
{
type Response = Res;
type Request = A::Request;
type Response = Response;
type Error = A::Error;
type Future = MapFuture<A, F, Req, Res>;
type Future = MapFuture<A, F, Response>;
crate::forward_ready!(service);
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(ctx)
}
fn call(&mut self, req: Req) -> Self::Future {
fn call(&mut self, req: A::Request) -> Self::Future {
MapFuture::new(self.service.call(req), self.f.clone())
}
}
pin_project! {
pub struct MapFuture<A, F, Req, Res>
where
A: Service<Req>,
F: FnMut(A::Response) -> Res,
{
f: F,
#[pin]
fut: A::Future,
}
#[pin_project::pin_project]
pub struct MapFuture<A, F, Response>
where
A: Service,
F: FnMut(A::Response) -> Response,
{
f: F,
#[pin]
fut: A::Future,
}
impl<A, F, Req, Res> MapFuture<A, F, Req, Res>
impl<A, F, Response> MapFuture<A, F, Response>
where
A: Service<Req>,
F: FnMut(A::Response) -> Res,
A: Service,
F: FnMut(A::Response) -> Response,
{
fn new(fut: A::Future, f: F) -> Self {
MapFuture { f, fut }
}
}
impl<A, F, Req, Res> Future for MapFuture<A, F, Req, Res>
impl<A, F, Response> Future for MapFuture<A, F, Response>
where
A: Service<Req>,
F: FnMut(A::Response) -> Res,
A: Service,
F: FnMut(A::Response) -> Response,
{
type Output = Result<Res, A::Error>;
type Output = Result<Response, A::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
@@ -104,17 +102,17 @@ where
}
/// `MapNewService` new service combinator
pub struct MapServiceFactory<A, F, Req, Res> {
pub struct MapServiceFactory<A, F, Res> {
a: A,
f: F,
r: PhantomData<(Res, Req)>,
r: PhantomData<Res>,
}
impl<A, F, Req, Res> MapServiceFactory<A, F, Req, Res> {
impl<A, F, Res> MapServiceFactory<A, F, Res> {
/// Create new `Map` new service instance
pub(crate) fn new(a: A, f: F) -> Self
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: FnMut(A::Response) -> Res,
{
Self {
@@ -125,7 +123,7 @@ impl<A, F, Req, Res> MapServiceFactory<A, F, Req, Res> {
}
}
impl<A, F, Req, Res> Clone for MapServiceFactory<A, F, Req, Res>
impl<A, F, Res> Clone for MapServiceFactory<A, F, Res>
where
A: Clone,
F: Clone,
@@ -139,39 +137,39 @@ where
}
}
impl<A, F, Req, Res> ServiceFactory<Req> for MapServiceFactory<A, F, Req, Res>
impl<A, F, Res> ServiceFactory for MapServiceFactory<A, F, Res>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: FnMut(A::Response) -> Res + Clone,
{
type Request = A::Request;
type Response = Res;
type Error = A::Error;
type Config = A::Config;
type Service = Map<A::Service, F, Req, Res>;
type Service = Map<A::Service, F, Res>;
type InitError = A::InitError;
type Future = MapServiceFuture<A, F, Req, Res>;
type Future = MapServiceFuture<A, F, Res>;
fn new_service(&self, cfg: A::Config) -> Self::Future {
MapServiceFuture::new(self.a.new_service(cfg), self.f.clone())
}
}
pin_project! {
pub struct MapServiceFuture<A, F, Req, Res>
where
A: ServiceFactory<Req>,
F: FnMut(A::Response) -> Res,
{
#[pin]
fut: A::Future,
f: Option<F>,
}
#[pin_project::pin_project]
pub struct MapServiceFuture<A, F, Res>
where
A: ServiceFactory,
F: FnMut(A::Response) -> Res,
{
#[pin]
fut: A::Future,
f: Option<F>,
}
impl<A, F, Req, Res> MapServiceFuture<A, F, Req, Res>
impl<A, F, Res> MapServiceFuture<A, F, Res>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: FnMut(A::Response) -> Res,
{
fn new(fut: A::Future, f: F) -> Self {
@@ -179,12 +177,12 @@ where
}
}
impl<A, F, Req, Res> Future for MapServiceFuture<A, F, Req, Res>
impl<A, F, Res> Future for MapServiceFuture<A, F, Res>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: FnMut(A::Response) -> Res,
{
type Output = Result<Map<A::Service, F, Req, Res>, A::InitError>;
type Output = Result<Map<A::Service, F, Res>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
@@ -199,21 +197,22 @@ where
#[cfg(test)]
mod tests {
use futures_util::future::lazy;
use futures_util::future::{lazy, ok, Ready};
use super::*;
use crate::{
ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt,
};
use crate::{IntoServiceFactory, Service, ServiceFactory};
struct Srv;
impl Service<()> for Srv {
impl Service for Srv {
type Request = ();
type Response = ();
type Error = ();
type Future = Ready<Result<(), ()>>;
crate::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
ok(())

View File

@@ -1,4 +1,4 @@
use core::marker::PhantomData;
use std::marker::PhantomData;
use super::{IntoServiceFactory, ServiceFactory};
@@ -6,123 +6,121 @@ use super::{IntoServiceFactory, ServiceFactory};
///
/// Note that this function consumes the receiving service factory and returns
/// a wrapped version of it.
pub fn map_config<I, SF, Req, F, Cfg>(factory: I, f: F) -> MapConfig<SF, Req, F, Cfg>
pub fn map_config<T, U, F, C>(factory: U, f: F) -> MapConfig<T, F, C>
where
I: IntoServiceFactory<SF, Req>,
SF: ServiceFactory<Req>,
F: Fn(Cfg) -> SF::Config,
T: ServiceFactory,
U: IntoServiceFactory<T>,
F: Fn(C) -> T::Config,
{
MapConfig::new(factory.into_factory(), f)
}
/// Replace config with unit.
pub fn unit_config<I, SF, Cfg, Req>(factory: I) -> UnitConfig<SF, Cfg, Req>
/// Replace config with unit
pub fn unit_config<T, U, C>(factory: U) -> UnitConfig<T, C>
where
I: IntoServiceFactory<SF, Req>,
SF: ServiceFactory<Req, Config = ()>,
T: ServiceFactory<Config = ()>,
U: IntoServiceFactory<T>,
{
UnitConfig::new(factory.into_factory())
}
/// `map_config()` adapter service factory
pub struct MapConfig<SF, Req, F, Cfg> {
factory: SF,
cfg_mapper: F,
e: PhantomData<(Cfg, Req)>,
pub struct MapConfig<A, F, C> {
a: A,
f: F,
e: PhantomData<C>,
}
impl<SF, Req, F, Cfg> MapConfig<SF, Req, F, Cfg> {
impl<A, F, C> MapConfig<A, F, C> {
/// Create new `MapConfig` combinator
pub(crate) fn new(factory: SF, cfg_mapper: F) -> Self
pub(crate) fn new(a: A, f: F) -> Self
where
SF: ServiceFactory<Req>,
F: Fn(Cfg) -> SF::Config,
A: ServiceFactory,
F: Fn(C) -> A::Config,
{
Self {
factory,
cfg_mapper,
a,
f,
e: PhantomData,
}
}
}
impl<SF, Req, F, Cfg> Clone for MapConfig<SF, Req, F, Cfg>
impl<A, F, C> Clone for MapConfig<A, F, C>
where
SF: Clone,
A: Clone,
F: Clone,
{
fn clone(&self) -> Self {
Self {
factory: self.factory.clone(),
cfg_mapper: self.cfg_mapper.clone(),
a: self.a.clone(),
f: self.f.clone(),
e: PhantomData,
}
}
}
impl<SF, Req, F, Cfg> ServiceFactory<Req> for MapConfig<SF, Req, F, Cfg>
impl<A, F, C> ServiceFactory for MapConfig<A, F, C>
where
SF: ServiceFactory<Req>,
F: Fn(Cfg) -> SF::Config,
A: ServiceFactory,
F: Fn(C) -> A::Config,
{
type Response = SF::Response;
type Error = SF::Error;
type Request = A::Request;
type Response = A::Response;
type Error = A::Error;
type Config = Cfg;
type Service = SF::Service;
type InitError = SF::InitError;
type Future = SF::Future;
type Config = C;
type Service = A::Service;
type InitError = A::InitError;
type Future = A::Future;
fn new_service(&self, cfg: Self::Config) -> Self::Future {
let mapped_cfg = (self.cfg_mapper)(cfg);
self.factory.new_service(mapped_cfg)
fn new_service(&self, cfg: C) -> Self::Future {
self.a.new_service((self.f)(cfg))
}
}
/// `unit_config()` config combinator
pub struct UnitConfig<SF, Cfg, Req> {
factory: SF,
_phantom: PhantomData<(Cfg, Req)>,
pub struct UnitConfig<A, C> {
a: A,
e: PhantomData<C>,
}
impl<SF, Cfg, Req> UnitConfig<SF, Cfg, Req>
impl<A, C> UnitConfig<A, C>
where
SF: ServiceFactory<Req, Config = ()>,
A: ServiceFactory<Config = ()>,
{
/// Create new `UnitConfig` combinator
pub(crate) fn new(factory: SF) -> Self {
Self {
factory,
_phantom: PhantomData,
}
pub(crate) fn new(a: A) -> Self {
Self { a, e: PhantomData }
}
}
impl<SF, Cfg, Req> Clone for UnitConfig<SF, Cfg, Req>
impl<A, C> Clone for UnitConfig<A, C>
where
SF: Clone,
A: Clone,
{
fn clone(&self) -> Self {
Self {
factory: self.factory.clone(),
_phantom: PhantomData,
a: self.a.clone(),
e: PhantomData,
}
}
}
impl<SF, Cfg, Req> ServiceFactory<Req> for UnitConfig<SF, Cfg, Req>
impl<A, C> ServiceFactory for UnitConfig<A, C>
where
SF: ServiceFactory<Req, Config = ()>,
A: ServiceFactory<Config = ()>,
{
type Response = SF::Response;
type Error = SF::Error;
type Request = A::Request;
type Response = A::Response;
type Error = A::Error;
type Config = Cfg;
type Service = SF::Service;
type InitError = SF::InitError;
type Future = SF::Future;
type Config = C;
type Service = A::Service;
type InitError = A::InitError;
type Future = A::Future;
fn new_service(&self, _: Cfg) -> Self::Future {
self.factory.new_service(())
fn new_service(&self, _: C) -> Self::Future {
self.a.new_service(())
}
}

View File

@@ -1,11 +1,7 @@
use core::{
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use super::{Service, ServiceFactory};
@@ -13,18 +9,18 @@ use super::{Service, ServiceFactory};
/// error.
///
/// This is created by the `ServiceExt::map_err` method.
pub struct MapErr<S, Req, F, E> {
service: S,
pub struct MapErr<A, F, E> {
service: A,
f: F,
_t: PhantomData<(E, Req)>,
_t: PhantomData<E>,
}
impl<S, Req, F, E> MapErr<S, Req, F, E> {
impl<A, F, E> MapErr<A, F, E> {
/// Create new `MapErr` combinator
pub(crate) fn new(service: S, f: F) -> Self
pub(crate) fn new(service: A, f: F) -> Self
where
S: Service<Req>,
F: Fn(S::Error) -> E,
A: Service,
F: Fn(A::Error) -> E,
{
Self {
service,
@@ -34,9 +30,9 @@ impl<S, Req, F, E> MapErr<S, Req, F, E> {
}
}
impl<S, Req, F, E> Clone for MapErr<S, Req, F, E>
impl<A, F, E> Clone for MapErr<A, F, E>
where
S: Clone,
A: Clone,
F: Clone,
{
fn clone(&self) -> Self {
@@ -48,39 +44,39 @@ where
}
}
impl<A, Req, F, E> Service<Req> for MapErr<A, Req, F, E>
impl<A, F, E> Service for MapErr<A, F, E>
where
A: Service<Req>,
A: Service,
F: Fn(A::Error) -> E + Clone,
{
type Request = A::Request;
type Response = A::Response;
type Error = E;
type Future = MapErrFuture<A, Req, F, E>;
type Future = MapErrFuture<A, F, E>;
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(ctx).map_err(&self.f)
}
fn call(&mut self, req: Req) -> Self::Future {
fn call(&mut self, req: A::Request) -> Self::Future {
MapErrFuture::new(self.service.call(req), self.f.clone())
}
}
pin_project! {
pub struct MapErrFuture<A, Req, F, E>
where
A: Service<Req>,
F: Fn(A::Error) -> E,
{
f: F,
#[pin]
fut: A::Future,
}
#[pin_project::pin_project]
pub struct MapErrFuture<A, F, E>
where
A: Service,
F: Fn(A::Error) -> E,
{
f: F,
#[pin]
fut: A::Future,
}
impl<A, Req, F, E> MapErrFuture<A, Req, F, E>
impl<A, F, E> MapErrFuture<A, F, E>
where
A: Service<Req>,
A: Service,
F: Fn(A::Error) -> E,
{
fn new(fut: A::Future, f: F) -> Self {
@@ -88,9 +84,9 @@ where
}
}
impl<A, Req, F, E> Future for MapErrFuture<A, Req, F, E>
impl<A, F, E> Future for MapErrFuture<A, F, E>
where
A: Service<Req>,
A: Service,
F: Fn(A::Error) -> E,
{
type Output = Result<A::Response, E>;
@@ -105,19 +101,19 @@ where
/// service's error.
///
/// This is created by the `NewServiceExt::map_err` method.
pub struct MapErrServiceFactory<A, Req, F, E>
pub struct MapErrServiceFactory<A, F, E>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: Fn(A::Error) -> E + Clone,
{
a: A,
f: F,
e: PhantomData<(E, Req)>,
e: PhantomData<E>,
}
impl<A, Req, F, E> MapErrServiceFactory<A, Req, F, E>
impl<A, F, E> MapErrServiceFactory<A, F, E>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: Fn(A::Error) -> E + Clone,
{
/// Create new `MapErr` new service instance
@@ -130,9 +126,9 @@ where
}
}
impl<A, Req, F, E> Clone for MapErrServiceFactory<A, Req, F, E>
impl<A, F, E> Clone for MapErrServiceFactory<A, F, E>
where
A: ServiceFactory<Req> + Clone,
A: ServiceFactory + Clone,
F: Fn(A::Error) -> E + Clone,
{
fn clone(&self) -> Self {
@@ -144,39 +140,39 @@ where
}
}
impl<A, Req, F, E> ServiceFactory<Req> for MapErrServiceFactory<A, Req, F, E>
impl<A, F, E> ServiceFactory for MapErrServiceFactory<A, F, E>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: Fn(A::Error) -> E + Clone,
{
type Request = A::Request;
type Response = A::Response;
type Error = E;
type Config = A::Config;
type Service = MapErr<A::Service, Req, F, E>;
type Service = MapErr<A::Service, F, E>;
type InitError = A::InitError;
type Future = MapErrServiceFuture<A, Req, F, E>;
type Future = MapErrServiceFuture<A, F, E>;
fn new_service(&self, cfg: A::Config) -> Self::Future {
MapErrServiceFuture::new(self.a.new_service(cfg), self.f.clone())
}
}
pin_project! {
pub struct MapErrServiceFuture<A, Req, F, E>
where
A: ServiceFactory<Req>,
F: Fn(A::Error) -> E,
{
#[pin]
fut: A::Future,
f: F,
}
#[pin_project::pin_project]
pub struct MapErrServiceFuture<A, F, E>
where
A: ServiceFactory,
F: Fn(A::Error) -> E,
{
#[pin]
fut: A::Future,
f: F,
}
impl<A, Req, F, E> MapErrServiceFuture<A, Req, F, E>
impl<A, F, E> MapErrServiceFuture<A, F, E>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: Fn(A::Error) -> E,
{
fn new(fut: A::Future, f: F) -> Self {
@@ -184,12 +180,12 @@ where
}
}
impl<A, Req, F, E> Future for MapErrServiceFuture<A, Req, F, E>
impl<A, F, E> Future for MapErrServiceFuture<A, F, E>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: Fn(A::Error) -> E + Clone,
{
type Output = Result<MapErr<A::Service, Req, F, E>, A::InitError>;
type Output = Result<MapErr<A::Service, F, E>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
@@ -203,17 +199,15 @@ where
#[cfg(test)]
mod tests {
use futures_util::future::lazy;
use futures_util::future::{err, lazy, ok, Ready};
use super::*;
use crate::{
err, ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory,
ServiceFactoryExt,
};
use crate::{IntoServiceFactory, Service, ServiceFactory};
struct Srv;
impl Service<()> for Srv {
impl Service for Srv {
type Request = ();
type Response = ();
type Error = ();
type Future = Ready<Result<(), ()>>;

View File

@@ -1,25 +1,21 @@
use core::{
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use super::ServiceFactory;
/// `MapInitErr` service combinator
pub struct MapInitErr<A, F, Req, Err> {
pub struct MapInitErr<A, F, E> {
a: A,
f: F,
e: PhantomData<(Req, Err)>,
e: PhantomData<E>,
}
impl<A, F, Req, Err> MapInitErr<A, F, Req, Err>
impl<A, F, E> MapInitErr<A, F, E>
where
A: ServiceFactory<Req>,
F: Fn(A::InitError) -> Err,
A: ServiceFactory,
F: Fn(A::InitError) -> E,
{
/// Create new `MapInitErr` combinator
pub(crate) fn new(a: A, f: F) -> Self {
@@ -31,7 +27,7 @@ where
}
}
impl<A, F, Req, E> Clone for MapInitErr<A, F, Req, E>
impl<A, F, E> Clone for MapInitErr<A, F, E>
where
A: Clone,
F: Clone,
@@ -45,39 +41,39 @@ where
}
}
impl<A, F, Req, E> ServiceFactory<Req> for MapInitErr<A, F, Req, E>
impl<A, F, E> ServiceFactory for MapInitErr<A, F, E>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: Fn(A::InitError) -> E + Clone,
{
type Request = A::Request;
type Response = A::Response;
type Error = A::Error;
type Config = A::Config;
type Service = A::Service;
type InitError = E;
type Future = MapInitErrFuture<A, F, Req, E>;
type Future = MapInitErrFuture<A, F, E>;
fn new_service(&self, cfg: A::Config) -> Self::Future {
MapInitErrFuture::new(self.a.new_service(cfg), self.f.clone())
}
}
pin_project! {
pub struct MapInitErrFuture<A, F, Req, E>
where
A: ServiceFactory<Req>,
F: Fn(A::InitError) -> E,
{
f: F,
#[pin]
fut: A::Future,
}
#[pin_project::pin_project]
pub struct MapInitErrFuture<A, F, E>
where
A: ServiceFactory,
F: Fn(A::InitError) -> E,
{
f: F,
#[pin]
fut: A::Future,
}
impl<A, F, Req, E> MapInitErrFuture<A, F, Req, E>
impl<A, F, E> MapInitErrFuture<A, F, E>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: Fn(A::InitError) -> E,
{
fn new(fut: A::Future, f: F) -> Self {
@@ -85,9 +81,9 @@ where
}
}
impl<A, F, Req, E> Future for MapInitErrFuture<A, F, Req, E>
impl<A, F, E> Future for MapInitErrFuture<A, F, E>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
F: Fn(A::InitError) -> E,
{
type Output = Result<A::Service, E>;

View File

@@ -1,9 +1,8 @@
use core::{
marker::PhantomData,
task::{Context, Poll},
};
use std::future::Future;
use std::task::{Context, Poll};
use crate::and_then::{AndThenService, AndThenServiceFactory};
use crate::and_then_apply_fn::{AndThenApplyFn, AndThenApplyFnFactory};
use crate::map::{Map, MapServiceFactory};
use crate::map_err::{MapErr, MapErrServiceFactory};
use crate::map_init_err::MapInitErr;
@@ -11,39 +10,33 @@ use crate::then::{ThenService, ThenServiceFactory};
use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory};
/// Construct new pipeline with one service in pipeline chain.
pub fn pipeline<I, S, Req>(service: I) -> Pipeline<S, Req>
pub fn pipeline<F, T>(service: F) -> Pipeline<T>
where
I: IntoService<S, Req>,
S: Service<Req>,
F: IntoService<T>,
T: Service,
{
Pipeline {
service: service.into_service(),
_phantom: PhantomData,
}
}
/// Construct new pipeline factory with one service factory.
pub fn pipeline_factory<I, SF, Req>(factory: I) -> PipelineFactory<SF, Req>
pub fn pipeline_factory<T, F>(factory: F) -> PipelineFactory<T>
where
I: IntoServiceFactory<SF, Req>,
SF: ServiceFactory<Req>,
T: ServiceFactory,
F: IntoServiceFactory<T>,
{
PipelineFactory {
factory: factory.into_factory(),
_phantom: PhantomData,
}
}
/// Pipeline service - pipeline allows to compose multiple service into one service.
pub struct Pipeline<S, Req> {
service: S,
_phantom: PhantomData<Req>,
pub struct Pipeline<T> {
service: T,
}
impl<S, Req> Pipeline<S, Req>
where
S: Service<Req>,
{
impl<T: Service> Pipeline<T> {
/// Call another service after call to this one has resolved successfully.
///
/// This function can be used to chain two services together and ensure that
@@ -53,18 +46,41 @@ where
///
/// Note that this function consumes the receiving service and returns a
/// wrapped version of it.
pub fn and_then<I, S1>(
pub fn and_then<F, U>(
self,
service: I,
) -> Pipeline<impl Service<Req, Response = S1::Response, Error = S::Error> + Clone, Req>
service: F,
) -> Pipeline<
impl Service<Request = T::Request, Response = U::Response, Error = T::Error> + Clone,
>
where
Self: Sized,
I: IntoService<S1, S::Response>,
S1: Service<S::Response, Error = S::Error>,
F: IntoService<U>,
U: Service<Request = T::Response, Error = T::Error>,
{
Pipeline {
service: AndThenService::new(self.service, service.into_service()),
_phantom: PhantomData,
}
}
/// Apply function to specified service and use it as a next service in
/// chain.
///
/// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))`
pub fn and_then_apply_fn<U, I, F, Fut, Res, Err>(
self,
service: I,
f: F,
) -> Pipeline<impl Service<Request = T::Request, Response = Res, Error = Err> + Clone>
where
Self: Sized,
I: IntoService<U>,
U: Service,
F: FnMut(T::Response, &mut U) -> Fut,
Fut: Future<Output = Result<Res, Err>>,
Err: From<T::Error> + From<U::Error>,
{
Pipeline {
service: AndThenApplyFn::new(self.service, service.into_service(), f),
}
}
@@ -73,18 +89,19 @@ where
///
/// Note that this function consumes the receiving pipeline and returns a
/// wrapped version of it.
pub fn then<F, S1>(
pub fn then<F, U>(
self,
service: F,
) -> Pipeline<impl Service<Req, Response = S1::Response, Error = S::Error> + Clone, Req>
) -> Pipeline<
impl Service<Request = T::Request, Response = U::Response, Error = T::Error> + Clone,
>
where
Self: Sized,
F: IntoService<S1, Result<S::Response, S::Error>>,
S1: Service<Result<S::Response, S::Error>, Error = S::Error>,
F: IntoService<U>,
U: Service<Request = Result<T::Response, T::Error>, Error = T::Error>,
{
Pipeline {
service: ThenService::new(self.service, service.into_service()),
_phantom: PhantomData,
}
}
@@ -97,14 +114,13 @@ where
/// 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<F, R>(self, f: F) -> Pipeline<Map<S, F, Req, R>, Req>
pub fn map<F, R>(self, f: F) -> Pipeline<Map<T, F, R>>
where
Self: Sized,
F: FnMut(S::Response) -> R,
F: FnMut(T::Response) -> R,
{
Pipeline {
service: Map::new(self.service, f),
_phantom: PhantomData,
}
}
@@ -116,85 +132,114 @@ where
///
/// Note that this function consumes the receiving service and returns a
/// wrapped version of it.
pub fn map_err<F, E>(self, f: F) -> Pipeline<MapErr<S, Req, F, E>, Req>
pub fn map_err<F, E>(self, f: F) -> Pipeline<MapErr<T, F, E>>
where
Self: Sized,
F: Fn(S::Error) -> E,
F: Fn(T::Error) -> E,
{
Pipeline {
service: MapErr::new(self.service, f),
_phantom: PhantomData,
}
}
}
impl<T, Req> Clone for Pipeline<T, Req>
impl<T> Clone for Pipeline<T>
where
T: Clone,
{
fn clone(&self) -> Self {
Pipeline {
service: self.service.clone(),
_phantom: PhantomData,
}
}
}
impl<S: Service<Req>, Req> Service<Req> for Pipeline<S, Req> {
type Response = S::Response;
type Error = S::Error;
type Future = S::Future;
impl<T: Service> Service for Pipeline<T> {
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<Result<(), S::Error>> {
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), T::Error>> {
self.service.poll_ready(ctx)
}
#[inline]
fn call(&mut self, req: Req) -> Self::Future {
fn call(&mut self, req: T::Request) -> Self::Future {
self.service.call(req)
}
}
/// Pipeline factory
pub struct PipelineFactory<SF, Req> {
factory: SF,
_phantom: PhantomData<Req>,
pub struct PipelineFactory<T> {
factory: T,
}
impl<SF, Req> PipelineFactory<SF, Req>
where
SF: ServiceFactory<Req>,
{
impl<T: ServiceFactory> PipelineFactory<T> {
/// Call another service after call to this one has resolved successfully.
pub fn and_then<I, SF1>(
pub fn and_then<F, U>(
self,
factory: I,
factory: F,
) -> PipelineFactory<
impl ServiceFactory<
Req,
Response = SF1::Response,
Error = SF::Error,
Config = SF::Config,
InitError = SF::InitError,
Service = impl Service<Req, Response = SF1::Response, Error = SF::Error> + Clone,
Request = T::Request,
Response = U::Response,
Error = T::Error,
Config = T::Config,
InitError = T::InitError,
Service = impl Service<
Request = T::Request,
Response = U::Response,
Error = T::Error,
> + Clone,
> + Clone,
Req,
>
where
Self: Sized,
SF::Config: Clone,
I: IntoServiceFactory<SF1, SF::Response>,
SF1: ServiceFactory<
SF::Response,
Config = SF::Config,
Error = SF::Error,
InitError = SF::InitError,
T::Config: Clone,
F: IntoServiceFactory<U>,
U: ServiceFactory<
Config = T::Config,
Request = T::Response,
Error = T::Error,
InitError = T::InitError,
>,
{
PipelineFactory {
factory: AndThenServiceFactory::new(self.factory, factory.into_factory()),
_phantom: PhantomData,
}
}
/// Apply function to specified service and use it as a next service in
/// chain.
///
/// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))`
pub fn and_then_apply_fn<U, I, F, Fut, Res, Err>(
self,
factory: I,
f: F,
) -> PipelineFactory<
impl ServiceFactory<
Request = T::Request,
Response = Res,
Error = Err,
Config = T::Config,
InitError = T::InitError,
Service = impl Service<Request = T::Request, Response = Res, Error = Err> + Clone,
> + Clone,
>
where
Self: Sized,
T::Config: Clone,
I: IntoServiceFactory<U>,
U: ServiceFactory<Config = T::Config, InitError = T::InitError>,
F: FnMut(T::Response, &mut U::Service) -> Fut + Clone,
Fut: Future<Output = Result<Res, Err>>,
Err: From<T::Error> + From<U::Error>,
{
PipelineFactory {
factory: AndThenApplyFnFactory::new(self.factory, factory.into_factory(), f),
}
}
@@ -204,103 +249,96 @@ where
///
/// Note that this function consumes the receiving pipeline and returns a
/// wrapped version of it.
pub fn then<I, SF1>(
pub fn then<F, U>(
self,
factory: I,
factory: F,
) -> PipelineFactory<
impl ServiceFactory<
Req,
Response = SF1::Response,
Error = SF::Error,
Config = SF::Config,
InitError = SF::InitError,
Service = impl Service<Req, Response = SF1::Response, Error = SF::Error> + Clone,
Request = T::Request,
Response = U::Response,
Error = T::Error,
Config = T::Config,
InitError = T::InitError,
Service = impl Service<
Request = T::Request,
Response = U::Response,
Error = T::Error,
> + Clone,
> + Clone,
Req,
>
where
Self: Sized,
SF::Config: Clone,
I: IntoServiceFactory<SF1, Result<SF::Response, SF::Error>>,
SF1: ServiceFactory<
Result<SF::Response, SF::Error>,
Config = SF::Config,
Error = SF::Error,
InitError = SF::InitError,
T::Config: Clone,
F: IntoServiceFactory<U>,
U: ServiceFactory<
Config = T::Config,
Request = Result<T::Response, T::Error>,
Error = T::Error,
InitError = T::InitError,
>,
{
PipelineFactory {
factory: ThenServiceFactory::new(self.factory, factory.into_factory()),
_phantom: PhantomData,
}
}
/// Map this service's output to a different type, returning a new service
/// of the resulting type.
pub fn map<F, R>(self, f: F) -> PipelineFactory<MapServiceFactory<SF, F, Req, R>, Req>
pub fn map<F, R>(self, f: F) -> PipelineFactory<MapServiceFactory<T, F, R>>
where
Self: Sized,
F: FnMut(SF::Response) -> R + Clone,
F: FnMut(T::Response) -> R + Clone,
{
PipelineFactory {
factory: MapServiceFactory::new(self.factory, f),
_phantom: PhantomData,
}
}
/// Map this service's error to a different error, returning a new service.
pub fn map_err<F, E>(
self,
f: F,
) -> PipelineFactory<MapErrServiceFactory<SF, Req, F, E>, Req>
pub fn map_err<F, E>(self, f: F) -> PipelineFactory<MapErrServiceFactory<T, F, E>>
where
Self: Sized,
F: Fn(SF::Error) -> E + Clone,
F: Fn(T::Error) -> E + Clone,
{
PipelineFactory {
factory: MapErrServiceFactory::new(self.factory, f),
_phantom: PhantomData,
}
}
/// Map this factory's init error to a different error, returning a new service.
pub fn map_init_err<F, E>(self, f: F) -> PipelineFactory<MapInitErr<SF, F, Req, E>, Req>
pub fn map_init_err<F, E>(self, f: F) -> PipelineFactory<MapInitErr<T, F, E>>
where
Self: Sized,
F: Fn(SF::InitError) -> E + Clone,
F: Fn(T::InitError) -> E + Clone,
{
PipelineFactory {
factory: MapInitErr::new(self.factory, f),
_phantom: PhantomData,
}
}
}
impl<T, Req> Clone for PipelineFactory<T, Req>
impl<T> Clone for PipelineFactory<T>
where
T: Clone,
{
fn clone(&self) -> Self {
PipelineFactory {
factory: self.factory.clone(),
_phantom: PhantomData,
}
}
}
impl<SF, Req> ServiceFactory<Req> for PipelineFactory<SF, Req>
where
SF: ServiceFactory<Req>,
{
type Config = SF::Config;
type Response = SF::Response;
type Error = SF::Error;
type Service = SF::Service;
type InitError = SF::InitError;
type Future = SF::Future;
impl<T: ServiceFactory> ServiceFactory for PipelineFactory<T> {
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: SF::Config) -> Self::Future {
fn new_service(&self, cfg: T::Config) -> Self::Future {
self.factory.new_service(cfg)
}
}

View File

@@ -1,54 +0,0 @@
//! When MSRV is 1.48, replace with `core::future::Ready` and `core::future::ready()`.
use core::{
future::Future,
pin::Pin,
task::{Context, Poll},
};
/// Future for the [`ready`](ready()) function.
#[derive(Debug, Clone)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct Ready<T> {
val: Option<T>,
}
impl<T> Ready<T> {
/// Unwraps the value from this immediately ready future.
#[inline]
pub fn into_inner(mut self) -> T {
self.val.take().unwrap()
}
}
impl<T> Unpin for Ready<T> {}
impl<T> Future for Ready<T> {
type Output = T;
#[inline]
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
let val = self.val.take().expect("Ready can not be polled twice.");
Poll::Ready(val)
}
}
/// Creates a future that is immediately ready with a value.
#[allow(dead_code)]
pub(crate) fn ready<T>(val: T) -> Ready<T> {
Ready { val: Some(val) }
}
/// Create a future that is immediately ready with a success value.
#[allow(dead_code)]
pub(crate) fn ok<T, E>(val: T) -> Ready<Result<T, E>> {
Ready { val: Some(Ok(val)) }
}
/// Create a future that is immediately ready with an error value.
#[allow(dead_code)]
pub(crate) fn err<T, E>(err: E) -> Ready<Result<T, E>> {
Ready {
val: Some(Err(err)),
}
}

View File

@@ -1,13 +1,8 @@
use alloc::rc::Rc;
use core::{
cell::RefCell,
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use super::{Service, ServiceFactory};
@@ -15,33 +10,34 @@ use super::{Service, ServiceFactory};
/// another service.
///
/// This is created by the `Pipeline::then` method.
pub(crate) struct ThenService<A, B, Req>(Rc<RefCell<(A, B)>>, PhantomData<Req>);
pub(crate) struct ThenService<A, B>(Rc<RefCell<(A, B)>>);
impl<A, B, Req> ThenService<A, B, Req> {
impl<A, B> ThenService<A, B> {
/// Create new `.then()` combinator
pub(crate) fn new(a: A, b: B) -> ThenService<A, B, Req>
pub(crate) fn new(a: A, b: B) -> ThenService<A, B>
where
A: Service<Req>,
B: Service<Result<A::Response, A::Error>, Error = A::Error>,
A: Service,
B: Service<Request = Result<A::Response, A::Error>, Error = A::Error>,
{
Self(Rc::new(RefCell::new((a, b))), PhantomData)
Self(Rc::new(RefCell::new((a, b))))
}
}
impl<A, B, Req> Clone for ThenService<A, B, Req> {
impl<A, B> Clone for ThenService<A, B> {
fn clone(&self) -> Self {
ThenService(self.0.clone(), PhantomData)
ThenService(self.0.clone())
}
}
impl<A, B, Req> Service<Req> for ThenService<A, B, Req>
impl<A, B> Service for ThenService<A, B>
where
A: Service<Req>,
B: Service<Result<A::Response, A::Error>, Error = A::Error>,
A: Service,
B: Service<Request = Result<A::Response, A::Error>, Error = A::Error>,
{
type Request = A::Request;
type Response = B::Response;
type Error = B::Error;
type Future = ThenServiceResponse<A, B, Req>;
type Future = ThenServiceResponse<A, B>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let mut srv = self.0.borrow_mut();
@@ -53,44 +49,38 @@ where
}
}
fn call(&mut self, req: Req) -> Self::Future {
fn call(&mut self, req: A::Request) -> Self::Future {
ThenServiceResponse {
state: State::A {
fut: self.0.borrow_mut().0.call(req),
b: Some(self.0.clone()),
},
state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())),
}
}
}
pin_project! {
pub(crate) struct ThenServiceResponse<A, B, Req>
where
A: Service<Req>,
B: Service<Result<A::Response, A::Error>>,
{
#[pin]
state: State<A, B, Req>,
}
}
pin_project! {
#[project = StateProj]
enum State<A, B, Req>
where
A: Service<Req>,
B: Service<Result<A::Response, A::Error>>,
{
A { #[pin] fut: A::Future, b: Option<Rc<RefCell<(A, B)>>> },
B { #[pin] fut: B::Future },
Empty,
}
}
impl<A, B, Req> Future for ThenServiceResponse<A, B, Req>
#[pin_project::pin_project]
pub(crate) struct ThenServiceResponse<A, B>
where
A: Service<Req>,
B: Service<Result<A::Response, A::Error>>,
A: Service,
B: Service<Request = Result<A::Response, A::Error>>,
{
#[pin]
state: State<A, B>,
}
#[pin_project::pin_project(project = StateProj)]
enum State<A, B>
where
A: Service,
B: Service<Request = Result<A::Response, A::Error>>,
{
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
impl<A, B> Future for ThenServiceResponse<A, B>
where
A: Service,
B: Service<Request = Result<A::Response, A::Error>>,
{
type Output = Result<B::Response, B::Error>;
@@ -98,17 +88,17 @@ where
let mut this = self.as_mut().project();
match this.state.as_mut().project() {
StateProj::A { fut, b } => match fut.poll(cx) {
StateProj::A(fut, b) => match fut.poll(cx) {
Poll::Ready(res) => {
let b = b.take().unwrap();
this.state.set(State::Empty); // drop fut A
let fut = b.borrow_mut().1.call(res);
this.state.set(State::B { fut });
this.state.set(State::B(fut));
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
StateProj::B { fut } => fut.poll(cx).map(|r| {
StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty);
r
}),
@@ -120,43 +110,44 @@ where
}
/// `.then()` service factory combinator
pub(crate) struct ThenServiceFactory<A, B, Req>(Rc<(A, B)>, PhantomData<Req>);
pub(crate) struct ThenServiceFactory<A, B>(Rc<(A, B)>);
impl<A, B, Req> ThenServiceFactory<A, B, Req>
impl<A, B> ThenServiceFactory<A, B>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<
Result<A::Response, A::Error>,
Config = A::Config,
Request = Result<A::Response, A::Error>,
Error = A::Error,
InitError = A::InitError,
>,
{
/// Create new `AndThen` combinator
pub(crate) fn new(a: A, b: B) -> Self {
Self(Rc::new((a, b)), PhantomData)
Self(Rc::new((a, b)))
}
}
impl<A, B, Req> ServiceFactory<Req> for ThenServiceFactory<A, B, Req>
impl<A, B> ServiceFactory for ThenServiceFactory<A, B>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<
Result<A::Response, A::Error>,
Config = A::Config,
Request = Result<A::Response, A::Error>,
Error = A::Error,
InitError = A::InitError,
>,
{
type Request = A::Request;
type Response = B::Response;
type Error = A::Error;
type Config = A::Config;
type Service = ThenService<A::Service, B::Service, Req>;
type Service = ThenService<A::Service, B::Service>;
type InitError = A::InitError;
type Future = ThenServiceFactoryResponse<A, B, Req>;
type Future = ThenServiceFactoryResponse<A, B>;
fn new_service(&self, cfg: A::Config) -> Self::Future {
let srv = &*self.0;
@@ -164,38 +155,37 @@ where
}
}
impl<A, B, Req> Clone for ThenServiceFactory<A, B, Req> {
impl<A, B> Clone for ThenServiceFactory<A, B> {
fn clone(&self) -> Self {
Self(self.0.clone(), PhantomData)
Self(self.0.clone())
}
}
pin_project! {
pub(crate) struct ThenServiceFactoryResponse<A, B, Req>
where
A: ServiceFactory<Req>,
B: ServiceFactory<
Result<A::Response, A::Error>,
Config = A::Config,
Error = A::Error,
InitError = A::InitError,
>,
{
#[pin]
fut_b: B::Future,
#[pin]
fut_a: A::Future,
a: Option<A::Service>,
b: Option<B::Service>,
}
}
impl<A, B, Req> ThenServiceFactoryResponse<A, B, Req>
#[pin_project::pin_project]
pub(crate) struct ThenServiceFactoryResponse<A, B>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
B: ServiceFactory<
Result<A::Response, A::Error>,
Config = A::Config,
Request = Result<A::Response, A::Error>,
Error = A::Error,
InitError = A::InitError,
>,
{
#[pin]
fut_b: B::Future,
#[pin]
fut_a: A::Future,
a: Option<A::Service>,
b: Option<B::Service>,
}
impl<A, B> ThenServiceFactoryResponse<A, B>
where
A: ServiceFactory,
B: ServiceFactory<
Config = A::Config,
Request = Result<A::Response, A::Error>,
Error = A::Error,
InitError = A::InitError,
>,
@@ -210,17 +200,17 @@ where
}
}
impl<A, B, Req> Future for ThenServiceFactoryResponse<A, B, Req>
impl<A, B> Future for ThenServiceFactoryResponse<A, B>
where
A: ServiceFactory<Req>,
A: ServiceFactory,
B: ServiceFactory<
Result<A::Response, A::Error>,
Config = A::Config,
Request = Result<A::Response, A::Error>,
Error = A::Error,
InitError = A::InitError,
>,
{
type Output = Result<ThenService<A::Service, B::Service, Req>, A::InitError>;
type Output = Result<ThenService<A::Service, B::Service>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
@@ -248,20 +238,19 @@ where
#[cfg(test)]
mod tests {
use alloc::rc::Rc;
use core::{
cell::Cell,
task::{Context, Poll},
};
use std::cell::Cell;
use std::rc::Rc;
use std::task::{Context, Poll};
use futures_util::future::lazy;
use futures_util::future::{err, lazy, ok, ready, Ready};
use crate::{err, ok, pipeline, pipeline_factory, ready, Ready, Service, ServiceFactory};
use crate::{pipeline, pipeline_factory, Service, ServiceFactory};
#[derive(Clone)]
struct Srv1(Rc<Cell<usize>>);
impl Service<Result<&'static str, &'static str>> for Srv1 {
impl Service for Srv1 {
type Request = Result<&'static str, &'static str>;
type Response = &'static str;
type Error = ();
type Future = Ready<Result<Self::Response, Self::Error>>;
@@ -281,7 +270,8 @@ mod tests {
struct Srv2(Rc<Cell<usize>>);
impl Service<Result<&'static str, ()>> for Srv2 {
impl Service for Srv2 {
type Request = Result<&'static str, ()>;
type Response = (&'static str, &'static str);
type Error = ();
type Future = Ready<Result<Self::Response, ()>>;

View File

@@ -1,22 +1,18 @@
use alloc::{rc::Rc, sync::Arc};
use core::{
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc;
use std::task::{Context, Poll};
use crate::transform_err::TransformMapInitErr;
use crate::{IntoServiceFactory, Service, ServiceFactory};
/// Apply transform to a service.
pub fn apply<T, S, I, Req>(t: T, factory: I) -> ApplyTransform<T, S, Req>
pub fn apply<T, S, U>(t: T, factory: U) -> ApplyTransform<T, S>
where
I: IntoServiceFactory<S, Req>,
S: ServiceFactory<Req>,
T: Transform<S::Service, Req, InitError = S::InitError>,
S: ServiceFactory,
T: Transform<S::Service, InitError = S::InitError>,
U: IntoServiceFactory<S>,
{
ApplyTransform::new(t, factory.into_factory())
}
@@ -93,7 +89,10 @@ where
/// }
/// }
/// ```
pub trait Transform<S, Req> {
pub trait Transform<S> {
/// Requests handled by the service.
type Request;
/// Responses given by the service.
type Response;
@@ -101,7 +100,11 @@ pub trait Transform<S, Req> {
type Error;
/// The `TransformService` value created by this factory
type Transform: Service<Req, Response = Self::Response, Error = Self::Error>;
type Transform: Service<
Request = Self::Request,
Response = Self::Response,
Error = Self::Error,
>;
/// Errors produced while building a transform service.
type InitError;
@@ -114,7 +117,7 @@ pub trait Transform<S, Req> {
/// Map this transform's factory error to a different error,
/// returning a new transform service factory.
fn map_init_err<F, E>(self, f: F) -> TransformMapInitErr<Self, S, Req, F, E>
fn map_init_err<F, E>(self, f: F) -> TransformMapInitErr<Self, S, F, E>
where
Self: Sized,
F: Fn(Self::InitError) -> E + Clone,
@@ -123,10 +126,11 @@ pub trait Transform<S, Req> {
}
}
impl<T, S, Req> Transform<S, Req> for Rc<T>
impl<T, S> Transform<S> for Rc<T>
where
T: Transform<S, Req>,
T: Transform<S>,
{
type Request = T::Request;
type Response = T::Response;
type Error = T::Error;
type InitError = T::InitError;
@@ -138,10 +142,11 @@ where
}
}
impl<T, S, Req> Transform<S, Req> for Arc<T>
impl<T, S> Transform<S> for Arc<T>
where
T: Transform<S, Req>,
T: Transform<S>,
{
type Request = T::Request;
type Response = T::Response;
type Error = T::Error;
type InitError = T::InitError;
@@ -154,76 +159,72 @@ where
}
/// `Apply` transform to new service
pub struct ApplyTransform<T, S, Req>(Rc<(T, S)>, PhantomData<Req>);
pub struct ApplyTransform<T, S>(Rc<(T, S)>);
impl<T, S, Req> ApplyTransform<T, S, Req>
impl<T, S> ApplyTransform<T, S>
where
S: ServiceFactory<Req>,
T: Transform<S::Service, Req, InitError = S::InitError>,
S: ServiceFactory,
T: Transform<S::Service, InitError = S::InitError>,
{
/// Create new `ApplyTransform` new service instance
fn new(t: T, service: S) -> Self {
Self(Rc::new((t, service)), PhantomData)
Self(Rc::new((t, service)))
}
}
impl<T, S, Req> Clone for ApplyTransform<T, S, Req> {
impl<T, S> Clone for ApplyTransform<T, S> {
fn clone(&self) -> Self {
ApplyTransform(self.0.clone(), PhantomData)
ApplyTransform(self.0.clone())
}
}
impl<T, S, Req> ServiceFactory<Req> for ApplyTransform<T, S, Req>
impl<T, S> ServiceFactory for ApplyTransform<T, S>
where
S: ServiceFactory<Req>,
T: Transform<S::Service, Req, InitError = S::InitError>,
S: ServiceFactory,
T: Transform<S::Service, InitError = S::InitError>,
{
type Request = T::Request;
type Response = T::Response;
type Error = T::Error;
type Config = S::Config;
type Service = T::Transform;
type InitError = T::InitError;
type Future = ApplyTransformFuture<T, S, Req>;
type Future = ApplyTransformFuture<T, S>;
fn new_service(&self, cfg: S::Config) -> Self::Future {
ApplyTransformFuture {
store: self.0.clone(),
state: ApplyTransformFutureState::A {
fut: self.0.as_ref().1.new_service(cfg),
},
state: ApplyTransformFutureState::A(self.0.as_ref().1.new_service(cfg)),
}
}
}
pin_project! {
pub struct ApplyTransformFuture<T, S, Req>
where
S: ServiceFactory<Req>,
T: Transform<S::Service, Req, InitError = S::InitError>,
{
store: Rc<(T, S)>,
#[pin]
state: ApplyTransformFutureState<T, S, Req>,
}
}
pin_project! {
#[project = ApplyTransformFutureStateProj]
pub enum ApplyTransformFutureState<T, S, Req>
where
S: ServiceFactory<Req>,
T: Transform<S::Service, Req, InitError = S::InitError>,
{
A { #[pin] fut: S::Future },
B { #[pin] fut: T::Future },
}
}
impl<T, S, Req> Future for ApplyTransformFuture<T, S, Req>
#[pin_project::pin_project]
pub struct ApplyTransformFuture<T, S>
where
S: ServiceFactory<Req>,
T: Transform<S::Service, Req, InitError = S::InitError>,
S: ServiceFactory,
T: Transform<S::Service, InitError = S::InitError>,
{
store: Rc<(T, S)>,
#[pin]
state: ApplyTransformFutureState<T, S>,
}
#[pin_project::pin_project(project = ApplyTransformFutureStateProj)]
pub enum ApplyTransformFutureState<T, S>
where
S: ServiceFactory,
T: Transform<S::Service, InitError = S::InitError>,
{
A(#[pin] S::Future),
B(#[pin] T::Future),
}
impl<T, S> Future for ApplyTransformFuture<T, S>
where
S: ServiceFactory,
T: Transform<S::Service, InitError = S::InitError>,
{
type Output = Result<T::Transform, T::InitError>;
@@ -231,15 +232,15 @@ where
let mut this = self.as_mut().project();
match this.state.as_mut().project() {
ApplyTransformFutureStateProj::A { fut } => match fut.poll(cx)? {
ApplyTransformFutureStateProj::A(fut) => match fut.poll(cx)? {
Poll::Ready(srv) => {
let fut = this.store.0.new_transform(srv);
this.state.set(ApplyTransformFutureState::B { fut });
this.state.set(ApplyTransformFutureState::B(fut));
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
ApplyTransformFutureStateProj::B { fut } => fut.poll(cx),
ApplyTransformFutureStateProj::B(fut) => fut.poll(cx),
}
}
}

View File

@@ -1,11 +1,7 @@
use core::{
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use super::Transform;
@@ -13,75 +9,75 @@ use super::Transform;
/// transform's init error.
///
/// This is created by the `Transform::map_init_err` method.
pub struct TransformMapInitErr<T, S, Req, F, E> {
transform: T,
mapper: F,
_phantom: PhantomData<(S, Req, E)>,
pub struct TransformMapInitErr<T, S, F, E> {
t: T,
f: F,
e: PhantomData<(S, E)>,
}
impl<T, S, F, E, Req> TransformMapInitErr<T, S, Req, F, E> {
impl<T, S, F, E> TransformMapInitErr<T, S, F, E> {
pub(crate) fn new(t: T, f: F) -> Self
where
T: Transform<S, Req>,
T: Transform<S>,
F: Fn(T::InitError) -> E,
{
Self {
transform: t,
mapper: f,
_phantom: PhantomData,
t,
f,
e: PhantomData,
}
}
}
impl<T, S, Req, F, E> Clone for TransformMapInitErr<T, S, Req, F, E>
impl<T, S, F, E> Clone for TransformMapInitErr<T, S, F, E>
where
T: Clone,
F: Clone,
{
fn clone(&self) -> Self {
Self {
transform: self.transform.clone(),
mapper: self.mapper.clone(),
_phantom: PhantomData,
t: self.t.clone(),
f: self.f.clone(),
e: PhantomData,
}
}
}
impl<T, S, F, E, Req> Transform<S, Req> for TransformMapInitErr<T, S, Req, F, E>
impl<T, S, F, E> Transform<S> for TransformMapInitErr<T, S, F, E>
where
T: Transform<S, Req>,
T: Transform<S>,
F: Fn(T::InitError) -> E + Clone,
{
type Request = T::Request;
type Response = T::Response;
type Error = T::Error;
type Transform = T::Transform;
type InitError = E;
type Future = TransformMapInitErrFuture<T, S, F, E, Req>;
type Future = TransformMapInitErrFuture<T, S, F, E>;
fn new_transform(&self, service: S) -> Self::Future {
TransformMapInitErrFuture {
fut: self.transform.new_transform(service),
f: self.mapper.clone(),
fut: self.t.new_transform(service),
f: self.f.clone(),
}
}
}
pin_project! {
pub struct TransformMapInitErrFuture<T, S, F, E, Req>
where
T: Transform<S, Req>,
#[pin_project::pin_project]
pub struct TransformMapInitErrFuture<T, S, F, E>
where
T: Transform<S>,
F: Fn(T::InitError) -> E,
{
#[pin]
fut: T::Future,
f: F,
}
{
#[pin]
fut: T::Future,
f: F,
}
impl<T, S, F, E, Req> Future for TransformMapInitErrFuture<T, S, F, E, Req>
impl<T, S, F, E> Future for TransformMapInitErrFuture<T, S, F, E>
where
T: Transform<S, Req>,
T: Transform<S>,
F: Fn(T::InitError) -> E + Clone,
{
type Output = Result<T::Transform, E>;

33
actix-testing/CHANGES.md Normal file
View File

@@ -0,0 +1,33 @@
# Changes
## [1.0.1] - 2020-05-19
* Replace deprecated `net2` crate with `socket2`
* Remove unused `futures` dependency
## [1.0.0] - 2019-12-11
* Update actix-server to 1.0.0
## [1.0.0-alpha.3] - 2019-12-07
* Migrate to tokio 0.2
## [1.0.0-alpha.2] - 2019-12-02
* Re-export `test` attribute macros
## [0.3.0-alpha.1] - 2019-11-22
* Migrate to std::future
## [0.2.0] - 2019-10-14
* Upgrade actix-server and actix-server-config deps
## [0.1.0] - 2019-09-25
* Initial impl

27
actix-testing/Cargo.toml Normal file
View File

@@ -0,0 +1,27 @@
[package]
name = "actix-testing"
version = "1.0.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix testing utils"
keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-testing/"
categories = ["network-programming", "asynchronous"]
license = "MIT OR Apache-2.0"
edition = "2018"
workspace = ".."
readme = "README.md"
[lib]
name = "actix_testing"
path = "src/lib.rs"
[dependencies]
actix-rt = "1.0.0"
actix-macros = "0.1.0"
actix-server = "1.0.0"
actix-service = "1.0.0"
log = "0.4"
socket2 = "0.3"

9
actix-testing/README.md Normal file
View File

@@ -0,0 +1,9 @@
# Actix test utilities [![crates.io](https://meritbadge.herokuapp.com/actix-testing)](https://crates.io/crates/actix-testint) [![Join the chat at https://gitter.im/actix/actix](https://badges.gitter.im/actix/actix.svg)](https://gitter.im/actix/actix?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Documentation & community resources
* [User Guide](https://actix.rs/docs/)
* [API Documentation](https://docs.rs/actix-testing/)
* [Chat on gitter](https://gitter.im/actix/actix)
* Cargo package: [actix-http-test](https://crates.io/crates/actix-testing)
* Minimum supported Rust version: 1.37 or later

View File

@@ -1,9 +1,19 @@
//! Various helpers for Actix applications to use during testing.
#![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity, clippy::needless_doctest_main)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use std::sync::mpsc;
use std::{net, thread};
use actix_rt::{net::TcpStream, System};
use actix_server::{Server, ServerBuilder, ServiceFactory};
use socket2::{Domain, Protocol, Socket, Type};
use crate::{Server, ServerBuilder, ServiceFactory};
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub use actix_macros::test;
/// The `TestServer` type.
///
@@ -14,7 +24,7 @@ use crate::{Server, ServerBuilder, ServiceFactory};
///
/// ```rust
/// use actix_service::fn_service;
/// use actix_server::TestServer;
/// use actix_testing::TestServer;
///
/// #[actix_rt::main]
/// async fn main() {
@@ -77,15 +87,13 @@ impl TestServer {
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
let local_addr = tcp.local_addr().unwrap();
sys.block_on(async {
Server::build()
.listen("test", tcp, factory)
.unwrap()
.workers(1)
.disable_signals()
.start();
tx.send((System::current(), local_addr)).unwrap();
});
Server::build()
.listen("test", tcp, factory)?
.workers(1)
.disable_signals()
.start();
tx.send((System::current(), local_addr)).unwrap();
sys.run()
});
@@ -105,10 +113,11 @@ impl TestServer {
/// Get first available unused local address
pub fn unused_addr() -> net::SocketAddr {
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = mio::net::TcpSocket::new_v4().unwrap();
socket.bind(addr).unwrap();
socket.set_reuseaddr(true).unwrap();
let tcp = socket.listen(1024).unwrap();
let socket =
Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap();
socket.bind(&addr.into()).unwrap();
socket.set_reuse_address(true).unwrap();
let tcp = socket.into_tcp_listener();
tcp.local_addr().unwrap()
}
}

View File

@@ -19,7 +19,7 @@ path = "src/lib.rs"
[dependencies]
derive_more = "0.99.2"
futures-channel = "0.3.7"
futures-channel = "0.3.1"
parking_lot = "0.11"
lazy_static = "1.3"
log = "0.4"

View File

@@ -1,20 +1,6 @@
# Changes
## Unreleased - 2021-xx-xx
## 3.0.0-beta.2 - 2021-xx-xx
* Depend on stable trust-dns packages. [#204]
[#204]: https://github.com/actix/actix-net/pull/204
## 3.0.0-beta.1 - 2020-12-29
* Move acceptors under `accept` module. [#238]
* Merge `actix-connect` crate under `connect` module. [#238]
* Add feature flags to enable acceptors and/or connectors individually. [#238]
[#238]: https://github.com/actix/actix-net/pull/238
## Unreleased - 2020-xx-xx
## 2.0.0 - 2020-09-03

View File

@@ -1,9 +1,9 @@
[package]
name = "actix-tls"
version = "3.0.0-beta.2"
version = "2.0.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "TLS acceptor and connector services for Actix ecosystem"
keywords = ["network", "tls", "ssl", "async", "transport"]
description = "TLS acceptor services for Actix ecosystem."
keywords = ["network", "framework", "async", "tls", "ssl"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-tls/"
@@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0"
edition = "2018"
[package.metadata.docs.rs]
features = ["openssl", "rustls", "native-tls", "accept", "connect", "uri"]
features = ["openssl", "rustls", "nativetls"]
[lib]
name = "actix_tls"
@@ -20,64 +20,45 @@ path = "src/lib.rs"
[[example]]
name = "basic"
required-features = ["accept", "rustls"]
required-features = ["rustls"]
[features]
default = ["accept", "connect", "uri"]
# enable acceptor services
accept = []
# enable connector services
connect = ["trust-dns-proto/tokio-runtime", "trust-dns-resolver/tokio-runtime", "trust-dns-resolver/system-config"]
# use openssl impls
openssl = ["tls-openssl", "tokio-openssl"]
# use rustls impls
rustls = ["tls-rustls", "webpki", "webpki-roots", "tokio-rustls"]
# use native-tls impls
native-tls = ["tls-native-tls", "tokio-native-tls"]
# support http::Uri as connect address
uri = ["http"]
[dependencies]
actix-codec = "0.4.0-beta.1"
actix-rt = "2.0.0-beta.1"
actix-service = "2.0.0-beta.2"
actix-utils = "3.0.0-beta.1"
derive_more = "0.99.5"
either = "1.6"
futures-util = { version = "0.3.7", default-features = false }
http = { version = "0.2.2", optional = true }
log = "0.4"
# resolver
trust-dns-proto = { version = "0.20.0", default-features = false, optional = true }
trust-dns-resolver = { version = "0.20.0", default-features = false, optional = true }
default = []
# openssl
tls-openssl = { package = "openssl", version = "0.10", optional = true }
tokio-openssl = { version = "0.6", optional = true }
# TODO: Reduce dependencies where tokio wrappers re-export base crate.
openssl = ["open-ssl", "tokio-openssl"]
# rustls
tls-rustls = { package = "rustls", version = "0.19", optional = true }
tokio-rustls = { version = "0.22", optional = true }
rustls = ["rust-tls", "webpki", "webpki-roots", "tokio-rustls"]
# nativetls
nativetls = ["native-tls", "tokio-tls"]
[dependencies]
actix-service = "1.0.0"
actix-codec = "0.3.0"
actix-utils = "2.0.0"
futures-util = { version = "0.3.4", default-features = false }
# openssl
open-ssl = { package = "openssl", version = "0.10", optional = true }
tokio-openssl = { version = "0.4.0", optional = true }
# rustls
rust-tls = { package = "rustls", version = "0.18.0", optional = true }
webpki = { version = "0.21", optional = true }
webpki-roots = { version = "0.21", optional = true }
webpki-roots = { version = "0.20", optional = true }
tokio-rustls = { version = "0.14.0", optional = true }
# native-tls
tls-native-tls = { package = "native-tls", version = "0.2", optional = true }
tokio-native-tls = { version = "0.3", optional = true }
native-tls = { version = "0.2", optional = true }
tokio-tls = { version = "0.3", optional = true }
[dev-dependencies]
actix-server = "2.0.0-beta.2"
bytes = "1"
env_logger = "0.8"
futures-util = { version = "0.3.7", default-features = false, features = ["sink"] }
bytes = "0.5"
log = "0.4"
env_logger = "0.7"
actix-testing = "1.0.0"
actix-server = "1"
actix-rt = "1"

View File

@@ -15,10 +15,6 @@
//! http --verify=false https://127.0.0.1:8443
//! ```
// this rename only exists because of how we have organised the crate's feature flags
// it is not necessary for your actual code
extern crate tls_rustls as rustls;
use std::{
env,
fs::File,
@@ -31,10 +27,10 @@ use std::{
use actix_server::Server;
use actix_service::pipeline_factory;
use actix_tls::accept::rustls::Acceptor as RustlsAcceptor;
use actix_tls::rustls::Acceptor as RustlsAcceptor;
use futures_util::future::ok;
use log::info;
use rustls::{
use rust_tls::{
internal::pemfile::certs, internal::pemfile::rsa_private_keys, NoClientAuth, ServerConfig,
};

View File

@@ -1,42 +0,0 @@
//! TLS acceptor services for Actix ecosystem.
//!
//! ## Crate Features
//! * `openssl` - TLS acceptor using the `openssl` crate.
//! * `rustls` - TLS acceptor using the `rustls` crate.
//! * `native-tls` - TLS acceptor using the `native-tls` crate.
use std::sync::atomic::{AtomicUsize, Ordering};
use actix_utils::counter::Counter;
#[cfg(feature = "openssl")]
pub mod openssl;
#[cfg(feature = "rustls")]
pub mod rustls;
#[cfg(feature = "native-tls")]
pub mod nativetls;
pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
thread_local! {
static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed));
}
/// Sets the maximum per-worker concurrent TLS connection limit.
///
/// All listeners will stop accepting connections when this limit is reached.
/// It can be used to regulate the global TLS CPU usage.
///
/// By default, the connection limit is 256.
pub fn max_concurrent_tls_connect(num: usize) {
MAX_CONN.store(num, Ordering::Relaxed);
}
/// TLS error combined with service error.
#[derive(Debug)]
pub enum TlsError<E1, E2> {
Tls(E1),
Service(E2),
}

View File

@@ -1,17 +1,46 @@
//! TLS acceptor and connector services for Actix ecosystem
//! TLS acceptor services for Actix ecosystem.
//!
//! ## Crate Features
//! * `openssl` - TLS acceptor using the `openssl` crate.
//! * `rustls` - TLS acceptor using the `rustls` crate.
//! * `nativetls` - TLS acceptor using the `native-tls` crate.
#![deny(rust_2018_idioms, nonstandard_style)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
#[cfg(feature = "native-tls")]
extern crate tls_native_tls as native_tls;
#[cfg(feature = "openssl")]
extern crate tls_openssl as openssl;
#[cfg(feature = "rustls")]
extern crate tls_rustls as rustls;
use std::sync::atomic::{AtomicUsize, Ordering};
#[cfg(feature = "accept")]
pub mod accept;
#[cfg(feature = "connect")]
pub mod connect;
use actix_utils::counter::Counter;
#[cfg(feature = "openssl")]
pub mod openssl;
#[cfg(feature = "rustls")]
pub mod rustls;
#[cfg(feature = "nativetls")]
pub mod nativetls;
pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
thread_local! {
static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed));
}
/// Sets the maximum per-worker concurrent TLS connection limit.
///
/// All listeners will stop accepting connections when this limit is reached.
/// It can be used to regulate the global TLS CPU usage.
///
/// By default, the connection limit is 256.
pub fn max_concurrent_tls_connect(num: usize) {
MAX_CONN.store(num, Ordering::Relaxed);
}
/// TLS error combined with service error.
#[derive(Debug)]
pub enum TlsError<E1, E2> {
Tls(E1),
Service(E2),
}

View File

@@ -1,79 +1,93 @@
use std::marker::PhantomData;
use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory};
use actix_utils::counter::Counter;
use futures_util::future::{ready, LocalBoxFuture, Ready};
use futures_util::future::{self, FutureExt, LocalBoxFuture, TryFutureExt};
pub use native_tls::Error;
pub use tokio_native_tls::{TlsAcceptor, TlsStream};
pub use tokio_tls::{TlsAcceptor, TlsStream};
use super::MAX_CONN_COUNTER;
use crate::MAX_CONN_COUNTER;
/// Accept TLS connections via `native-tls` package.
///
/// `native-tls` feature enables this `Acceptor` type.
pub struct Acceptor {
/// `nativetls` feature enables this `Acceptor` type.
pub struct Acceptor<T> {
acceptor: TlsAcceptor,
io: PhantomData<T>,
}
impl Acceptor {
impl<T> Acceptor<T>
where
T: AsyncRead + AsyncWrite + Unpin,
{
/// Create `native-tls` based `Acceptor` service factory.
#[inline]
pub fn new(acceptor: TlsAcceptor) -> Self {
Acceptor { acceptor }
}
}
impl Clone for Acceptor {
#[inline]
fn clone(&self) -> Self {
Self {
acceptor: self.acceptor.clone(),
Acceptor {
acceptor,
io: PhantomData,
}
}
}
impl<T> ServiceFactory<T> for Acceptor
impl<T> Clone for Acceptor<T> {
#[inline]
fn clone(&self) -> Self {
Self {
acceptor: self.acceptor.clone(),
io: PhantomData,
}
}
}
impl<T> ServiceFactory for Acceptor<T>
where
T: AsyncRead + AsyncWrite + Unpin + 'static,
{
type Request = T;
type Response = TlsStream<T>;
type Error = Error;
type Config = ();
type Service = NativeTlsAcceptorService<T>;
type Service = NativeTlsAcceptorService;
type Config = ();
type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>;
type Future = future::Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
MAX_CONN_COUNTER.with(|conns| {
ready(Ok(NativeTlsAcceptorService {
future::ok(NativeTlsAcceptorService {
acceptor: self.acceptor.clone(),
conns: conns.clone(),
}))
io: PhantomData,
})
})
}
}
pub struct NativeTlsAcceptorService {
pub struct NativeTlsAcceptorService<T> {
acceptor: TlsAcceptor,
io: PhantomData<T>,
conns: Counter,
}
impl Clone for NativeTlsAcceptorService {
impl<T> Clone for NativeTlsAcceptorService<T> {
fn clone(&self) -> Self {
Self {
acceptor: self.acceptor.clone(),
io: PhantomData,
conns: self.conns.clone(),
}
}
}
impl<T> Service<T> for NativeTlsAcceptorService
impl<T> Service for NativeTlsAcceptorService<T>
where
T: AsyncRead + AsyncWrite + Unpin + 'static,
{
type Request = T;
type Response = TlsStream<T>;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<TlsStream<T>, Error>>;
@@ -86,13 +100,15 @@ where
}
}
fn call(&mut self, io: T) -> Self::Future {
fn call(&mut self, req: Self::Request) -> Self::Future {
let guard = self.conns.get();
let this = self.clone();
Box::pin(async move {
let io = this.acceptor.accept(io).await;
drop(guard);
io
})
async move { this.acceptor.accept(req).await }
.map_ok(move |io| {
// Required to preserve `CounterGuard` until `Self::Future` is completely resolved.
let _ = guard;
io
})
.boxed_local()
}
}

View File

@@ -1,78 +1,77 @@
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory};
use actix_utils::counter::{Counter, CounterGuard};
use futures_util::{
future::{ready, Ready},
ready,
};
use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready};
pub use openssl::ssl::{
AlpnError, Error as SslError, HandshakeError, Ssl, SslAcceptor, SslAcceptorBuilder,
};
pub use tokio_openssl::SslStream;
pub use open_ssl::ssl::{AlpnError, SslAcceptor, SslAcceptorBuilder};
pub use tokio_openssl::{HandshakeError, SslStream};
use super::MAX_CONN_COUNTER;
use crate::MAX_CONN_COUNTER;
/// Accept TLS connections via `openssl` package.
///
/// `openssl` feature enables this `Acceptor` type.
pub struct Acceptor {
pub struct Acceptor<T: AsyncRead + AsyncWrite> {
acceptor: SslAcceptor,
io: PhantomData<T>,
}
impl Acceptor {
impl<T: AsyncRead + AsyncWrite> Acceptor<T> {
/// Create OpenSSL based `Acceptor` service factory.
#[inline]
pub fn new(acceptor: SslAcceptor) -> Self {
Acceptor { acceptor }
}
}
impl Clone for Acceptor {
#[inline]
fn clone(&self) -> Self {
Self {
acceptor: self.acceptor.clone(),
Acceptor {
acceptor,
io: PhantomData,
}
}
}
impl<T> ServiceFactory<T> for Acceptor
where
T: AsyncRead + AsyncWrite + Unpin + 'static,
{
impl<T: AsyncRead + AsyncWrite> Clone for Acceptor<T> {
#[inline]
fn clone(&self) -> Self {
Self {
acceptor: self.acceptor.clone(),
io: PhantomData,
}
}
}
impl<T: AsyncRead + AsyncWrite + Unpin + 'static> ServiceFactory for Acceptor<T> {
type Request = T;
type Response = SslStream<T>;
type Error = SslError;
type Error = HandshakeError<T>;
type Config = ();
type Service = AcceptorService;
type Service = AcceptorService<T>;
type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
MAX_CONN_COUNTER.with(|conns| {
ready(Ok(AcceptorService {
ok(AcceptorService {
acceptor: self.acceptor.clone(),
conns: conns.clone(),
}))
io: PhantomData,
})
})
}
}
pub struct AcceptorService {
pub struct AcceptorService<T> {
acceptor: SslAcceptor,
conns: Counter,
io: PhantomData<T>,
}
impl<T> Service<T> for AcceptorService
where
T: AsyncRead + AsyncWrite + Unpin + 'static,
{
impl<T: AsyncRead + AsyncWrite + Unpin + 'static> Service for AcceptorService<T> {
type Request = T;
type Response = SslStream<T>;
type Error = SslError;
type Error = HandshakeError<T>;
type Future = AcceptorServiceResponse<T>;
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@@ -83,12 +82,15 @@ where
}
}
fn call(&mut self, io: T) -> Self::Future {
let ssl_ctx = self.acceptor.context();
let ssl = Ssl::new(ssl_ctx).expect("Provided SSL acceptor was invalid.");
fn call(&mut self, req: Self::Request) -> Self::Future {
let acc = self.acceptor.clone();
AcceptorServiceResponse {
_guard: self.conns.get(),
stream: Some(SslStream::new(ssl, io).unwrap()),
fut: async move {
let acc = acc;
tokio_openssl::accept(&acc, req).await
}
.boxed_local(),
}
}
}
@@ -97,15 +99,15 @@ pub struct AcceptorServiceResponse<T>
where
T: AsyncRead + AsyncWrite,
{
stream: Option<SslStream<T>>,
fut: LocalBoxFuture<'static, Result<SslStream<T>, HandshakeError<T>>>,
_guard: CounterGuard,
}
impl<T: AsyncRead + AsyncWrite + Unpin> Future for AcceptorServiceResponse<T> {
type Output = Result<SslStream<T>, SslError>;
type Output = Result<SslStream<T>, HandshakeError<T>>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
ready!(Pin::new(self.stream.as_mut().unwrap()).poll_accept(cx))?;
Poll::Ready(Ok(self.stream.take().expect("SSL connect has resolved.")))
let io = futures_util::ready!(Pin::new(&mut self.fut).poll(cx))?;
Poll::Ready(Ok(io))
}
}

View File

@@ -1,5 +1,6 @@
use std::future::Future;
use std::io;
use std::marker::PhantomData;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
@@ -7,73 +8,74 @@ use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory};
use actix_utils::counter::{Counter, CounterGuard};
use futures_util::future::{ready, Ready};
use futures_util::future::{ok, Ready};
use tokio_rustls::{Accept, TlsAcceptor};
pub use rustls::{ServerConfig, Session};
pub use rust_tls::{ServerConfig, Session};
pub use tokio_rustls::server::TlsStream;
pub use webpki_roots::TLS_SERVER_ROOTS;
use super::MAX_CONN_COUNTER;
use crate::MAX_CONN_COUNTER;
/// Accept TLS connections via `rustls` package.
///
/// `rustls` feature enables this `Acceptor` type.
pub struct Acceptor {
pub struct Acceptor<T> {
config: Arc<ServerConfig>,
io: PhantomData<T>,
}
impl Acceptor {
impl<T: AsyncRead + AsyncWrite> Acceptor<T> {
/// Create Rustls based `Acceptor` service factory.
#[inline]
pub fn new(config: ServerConfig) -> Self {
Acceptor {
config: Arc::new(config),
io: PhantomData,
}
}
}
impl Clone for Acceptor {
impl<T> Clone for Acceptor<T> {
#[inline]
fn clone(&self) -> Self {
Self {
config: self.config.clone(),
io: PhantomData,
}
}
}
impl<T> ServiceFactory<T> for Acceptor
where
T: AsyncRead + AsyncWrite + Unpin,
{
impl<T: AsyncRead + AsyncWrite + Unpin> ServiceFactory for Acceptor<T> {
type Request = T;
type Response = TlsStream<T>;
type Error = io::Error;
type Config = ();
type Service = AcceptorService<T>;
type Service = AcceptorService;
type Config = ();
type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
MAX_CONN_COUNTER.with(|conns| {
ready(Ok(AcceptorService {
ok(AcceptorService {
acceptor: self.config.clone().into(),
conns: conns.clone(),
}))
io: PhantomData,
})
})
}
}
/// Rustls based `Acceptor` service
pub struct AcceptorService {
pub struct AcceptorService<T> {
acceptor: TlsAcceptor,
io: PhantomData<T>,
conns: Counter,
}
impl<T> Service<T> for AcceptorService
where
T: AsyncRead + AsyncWrite + Unpin,
{
impl<T: AsyncRead + AsyncWrite + Unpin> Service for AcceptorService<T> {
type Request = T;
type Response = TlsStream<T>;
type Error = io::Error;
type Future = AcceptorServiceFut<T>;
@@ -86,7 +88,7 @@ where
}
}
fn call(&mut self, req: T) -> Self::Future {
fn call(&mut self, req: Self::Request) -> Self::Future {
AcceptorServiceFut {
_guard: self.conns.get(),
fut: self.acceptor.accept(req),
@@ -102,14 +104,16 @@ where
_guard: CounterGuard,
}
impl<T> Future for AcceptorServiceFut<T>
where
T: AsyncRead + AsyncWrite + Unpin,
{
impl<T: AsyncRead + AsyncWrite + Unpin> Future for AcceptorServiceFut<T> {
type Output = Result<TlsStream<T>, io::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
Pin::new(&mut this.fut).poll(cx)
let res = futures_util::ready!(Pin::new(&mut this.fut).poll(cx));
match res {
Ok(io) => Poll::Ready(Ok(io)),
Err(e) => Poll::Ready(Err(e)),
}
}
}

View File

@@ -16,12 +16,11 @@ name = "actix_tracing"
path = "src/lib.rs"
[dependencies]
actix-service = "2.0.0-beta.2"
actix-service = "1.0.4"
futures-util = { version = "0.3.4", default-features = false }
tracing = "0.1"
tracing-futures = "0.2"
[dev_dependencies]
actix-rt = "1.0"
slab = "0.4"
slab = "0.4"

View File

@@ -4,7 +4,8 @@
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use core::marker::PhantomData;
use std::marker::PhantomData;
use std::task::{Context, Poll};
use actix_service::{
apply, dev::ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform,
@@ -26,18 +27,21 @@ impl<S, F> TracingService<S, F> {
}
}
impl<S, Req, F> Service<Req> for TracingService<S, F>
impl<S, F> Service for TracingService<S, F>
where
S: Service<Req>,
F: Fn(&Req) -> Option<tracing::Span>,
S: Service,
F: Fn(&S::Request) -> Option<tracing::Span>,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Future = Either<S::Future, Instrumented<S::Future>>;
actix_service::forward_ready!(inner);
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(ctx)
}
fn call(&mut self, req: Req) -> Self::Future {
fn call(&mut self, req: Self::Request) -> Self::Future {
let span = (self.make_span)(&req);
let _enter = span.as_ref().map(|s| s.enter());
@@ -70,12 +74,18 @@ impl<S, U, F> TracingTransform<S, U, F> {
}
}
impl<S, Req, U, F> Transform<S, Req> for TracingTransform<S, U, F>
impl<S, U, F> Transform<S> for TracingTransform<S, U, F>
where
S: Service<Req>,
U: ServiceFactory<Req, Response = S::Response, Error = S::Error, Service = S>,
F: Fn(&Req) -> Option<tracing::Span> + Clone,
S: Service,
U: ServiceFactory<
Request = S::Request,
Response = S::Response,
Error = S::Error,
Service = S,
>,
F: Fn(&S::Request) -> Option<tracing::Span> + Clone,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Transform = TracingService<S, F>;
@@ -100,14 +110,14 @@ where
/// |req: &Request| Some(span!(Level::INFO, "request", req.id))
/// );
/// ```
pub fn trace<S, Req, I, F>(
service_factory: I,
pub fn trace<S, U, F>(
service_factory: U,
make_span: F,
) -> ApplyTransform<TracingTransform<S::Service, S, F>, S, Req>
) -> ApplyTransform<TracingTransform<S::Service, S, F>, S>
where
I: IntoServiceFactory<S, Req>,
S: ServiceFactory<Req>,
F: Fn(&Req) -> Option<tracing::Span> + Clone,
S: ServiceFactory,
F: Fn(&S::Request) -> Option<tracing::Span> + Clone,
U: IntoServiceFactory<S>,
{
apply(
TracingTransform::new(make_span),

View File

@@ -1,156 +1,223 @@
# Changes
## Unreleased - 2021-xx-xx
## 3.0.0-beta.1 - 2020-12-28
* Update `bytes` dependency to `1`. [#237]
* Use `pin-project-lite` to replace `pin-project`. [#229]
* Remove `condition`,`either`,`inflight`,`keepalive`,`oneshot`,`order`,`stream` and `time` mods. [#229]
[#229]: https://github.com/actix/actix-net/pull/229
[#237]: https://github.com/actix/actix-net/pull/237
## Unreleased - 2020-xx-xx
* Upgrade `pin-project` to `1.0`.
## 2.0.0 - 2020-08-23
* No changes from beta 1.
## 2.0.0-beta.1 - 2020-08-19
* Upgrade `tokio-util` to `0.3`.
* Remove unsound custom Cell and use `std::cell::RefCell` instead, as well as `actix-service`.
* Rename method to correctly spelled `LocalWaker::is_registered`.
## [1.0.6] - 2020-01-08
## 1.0.6 - 2020-01-08
* Add `Clone` impl for `condition::Waiter`.
* Add `Clone` impl for `condition::Waiter`
## [1.0.5] - 2020-01-08
## 1.0.5 - 2020-01-08
* Add `Condition` type.
* Add `Pool` of one-shot's.
## [1.0.4] - 2019-12-20
## 1.0.4 - 2019-12-20
* Add methods to check `LocalWaker` registration state.
## [1.0.3] - 2019-12-11
## 1.0.3 - 2019-12-11
* Revert InOrder service changes
## [1.0.2] - 2019-12-11
## 1.0.2 - 2019-12-11
* Allow to create `framed::Dispatcher` with custom `mpsc::Receiver`.
* Add `oneshot::Sender::is_canceled()` method.
* Allow to create `framed::Dispatcher` with custom `mpsc::Receiver`
* Add `oneshot::Sender::is_canceled()` method
## 1.0.1 - 2019-12-11
* Optimize InOrder service.
## [1.0.1] - 2019-12-11
* Optimize InOrder service
## 1.0.0 - 2019-12-11
* Simplify oneshot and mpsc implementations.
## [1.0.0] - 2019-12-11
* Simplify oneshot and mpsc implementations
## 1.0.0-alpha.3 - 2019-12-07
* Migrate to tokio 0.2.
* Fix oneshot.
## [1.0.0-alpha.3] - 2019-12-07
* Migrate to tokio 0.2
## 1.0.0-alpha.2 - 2019-12-02
* Migrate to `std::future`.
* Fix oneshot
## [1.0.0-alpha.2] - 2019-12-02
* Migrate to `std::future`
## [0.4.7] - 2019-10-14
## 0.4.7 - 2019-10-14
* Re-register task on every framed transport poll.
## 0.4.6 - 2019-10-08
## [0.4.6] - 2019-10-08
* Refactor `Counter` type. register current task in available method.
## 0.4.5 - 2019-07-19
* Deprecated `CloneableService` as it is not safe.
## [0.4.5] - 2019-07-19
### Removed
* Deprecated `CloneableService` as it is not safe
## 0.4.4 - 2019-07-17
* Undeprecate `FramedTransport` as it is actually useful.
## [0.4.4] - 2019-07-17
### Changed
* Undeprecate `FramedTransport` as it is actually useful
## 0.4.3 - 2019-07-17
* Deprecate `CloneableService` as it is not safe and in general not very useful.
* Deprecate `FramedTransport` in favor of `actix-ioframe`.
## [0.4.3] - 2019-07-17
### Deprecated
* Deprecate `CloneableService` as it is not safe and in general not very useful
* Deprecate `FramedTransport` in favor of `actix-ioframe`
## 0.4.2 - 2019-06-26
* Do not block on sink drop for FramedTransport.
## [0.4.2] - 2019-06-26
### Fixed
* Do not block on sink drop for FramedTransport
## 0.4.1 - 2019-05-15
* Change `Either` constructor.
## [0.4.1] - 2019-05-15
### Changed
* Change `Either` constructor
## 0.4.0 - 2019-05-11
* Change `Either` to handle two nexted services.
* Upgrade actix-service 0.4.
* Removed framed related services.
* Removed stream related services.
## [0.4.0] - 2019-05-11
### Changed
* Change `Either` to handle two nexted services
* Upgrade actix-service 0.4
### Deleted
* Framed related services
* Stream related services
## [0.3.5] - 2019-04-04
### Added
## 0.3.5 - 2019-04-04
* Allow to send messages to `FramedTransport` via mpsc channel.
* Remove `'static` constraint from Clonable service.
### Changed
* Remove 'static constraint from Clonable service
## 0.3.4 - 2019-03-12
## [0.3.4] - 2019-03-12
### Changed
* `TimeoutService`, `InOrderService`, `InFlightService` accepts generic IntoService services.
* Fix `InFlightService::poll_ready()` nested service readiness check.
* Fix `InOrderService::poll_ready()` nested service readiness check.
### Fixed
* Fix `InFlightService::poll_ready()` nested service readiness check
* Fix `InOrderService::poll_ready()` nested service readiness check
## 0.3.3 - 2019-03-09
* Revert IntoFuture change.
* Add generic config param for IntoFramed and TakeOne new services.
## [0.3.3] - 2019-03-09
### Changed
* Revert IntoFuture change
* Add generic config param for IntoFramed and TakeOne new services
## 0.3.2 - 2019-03-04
* Use IntoFuture for new services.
## [0.3.2] - 2019-03-04
## 0.3.1 - 2019-03-04
* Use new type of transform trait.
### Changed
* Use IntoFuture for new services
## 0.3.0 - 2019-03-02
## [0.3.1] - 2019-03-04
### Changed
* Use new type of transform trait
## [0.3.0] - 2019-03-02
### Changed
* Use new `NewService` trait
* BoxedNewService` and `BoxedService` types moved to actix-service crate.
## 0.2.4 - 2019-02-21
## [0.2.4] - 2019-02-21
### Changed
* Custom `BoxedNewService` implementation.
## 0.2.3 - 2019-02-21
* Add `BoxedNewService` and `BoxedService`.
## [0.2.3] - 2019-02-21
### Added
* Add `BoxedNewService` and `BoxedService`
## 0.2.2 - 2019-02-11
* Add `Display` impl for `TimeoutError`.
* Add `Display` impl for `InOrderError`.
## [0.2.2] - 2019-02-11
### Added
* Add `Display` impl for `TimeoutError`
* Add `Display` impl for `InOrderError`
## 0.2.1 - 2019-02-06
## [0.2.1] - 2019-02-06
### Added
* Add `InOrder` service. the service yields responses as they become available,
in the order that their originating requests were submitted to the service.
* Convert `Timeout` and `InFlight` services to a transforms.
### Changed
* Convert `Timeout` and `InFlight` services to a transforms
## 0.2.0 - 2019-02-01
* Fix framed transport error handling.
* Added Clone impl for Either service.
* Added Clone impl for Timeout service factory.
* Added Service and NewService for Stream dispatcher.
* Switch to actix-service 0.2.
## [0.2.0] - 2019-02-01
* Fix framed transport error handling
* Added Clone impl for Either service
* Added Clone impl for Timeout service factory
* Added Service and NewService for Stream dispatcher
* Switch to actix-service 0.2
## 0.1.0 - 2018-12-09
* Move utils services to separate crate.
## [0.1.0] - 2018-12-09
* Move utils services to separate crate

View File

@@ -1,8 +1,8 @@
[package]
name = "actix-utils"
version = "3.0.0-beta.1"
version = "2.0.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Various network related services and utilities for the Actix ecosystem"
description = "Various network related services and utilities for the Actix ecosystem."
keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
@@ -16,14 +16,15 @@ name = "actix_utils"
path = "src/lib.rs"
[dependencies]
actix-codec = "0.4.0-beta.1"
actix-rt = "2.0.0-beta.1"
actix-service = "2.0.0-beta.2"
futures-core = { version = "0.3.7", default-features = false }
futures-sink = { version = "0.3.7", default-features = false }
actix-codec = "0.3.0"
actix-rt = "1.1.1"
actix-service = "1.0.6"
bitflags = "1.2.1"
bytes = "0.5.3"
either = "1.5.3"
futures-channel = { version = "0.3.4", default-features = false }
futures-sink = { version = "0.3.4", default-features = false }
futures-util = { version = "0.3.4", default-features = false }
log = "0.4"
pin-project-lite = "0.2.0"
[dev-dependencies]
futures-util = { version = "0.3.7", default-features = false }
pin-project = "1.0.0"
slab = "0.4"

View File

@@ -0,0 +1,129 @@
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use slab::Slab;
use crate::task::LocalWaker;
/// Condition allows to notify multiple receivers at the same time
pub struct Condition(Rc<RefCell<Inner>>);
struct Inner {
data: Slab<Option<LocalWaker>>,
}
impl Default for Condition {
fn default() -> Self {
Self::new()
}
}
impl Condition {
pub fn new() -> Condition {
Condition(Rc::new(RefCell::new(Inner { data: Slab::new() })))
}
/// Get condition waiter
pub fn wait(&mut self) -> Waiter {
let token = self.0.borrow_mut().data.insert(None);
Waiter {
token,
inner: self.0.clone(),
}
}
/// Notify all waiters
pub fn notify(&self) {
let inner = self.0.borrow();
for item in inner.data.iter() {
if let Some(waker) = item.1 {
waker.wake();
}
}
}
}
impl Drop for Condition {
fn drop(&mut self) {
self.notify()
}
}
#[must_use = "Waiter do nothing unless polled"]
pub struct Waiter {
token: usize,
inner: Rc<RefCell<Inner>>,
}
impl Clone for Waiter {
fn clone(&self) -> Self {
let token = self.inner.borrow_mut().data.insert(None);
Waiter {
token,
inner: self.inner.clone(),
}
}
}
impl Future for Waiter {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let mut inner = this.inner.borrow_mut();
let inner = unsafe { inner.data.get_unchecked_mut(this.token) };
if inner.is_none() {
let waker = LocalWaker::default();
waker.register(cx.waker());
*inner = Some(waker);
Poll::Pending
} else if inner.as_mut().unwrap().register(cx.waker()) {
Poll::Pending
} else {
Poll::Ready(())
}
}
}
impl Drop for Waiter {
fn drop(&mut self) {
self.inner.borrow_mut().data.remove(self.token);
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures_util::future::lazy;
#[actix_rt::test]
async fn test_condition() {
let mut cond = Condition::new();
let mut waiter = cond.wait();
assert_eq!(
lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
Poll::Pending
);
cond.notify();
waiter.await;
let mut waiter = cond.wait();
assert_eq!(
lazy(|cx| Pin::new(&mut waiter).poll(cx)).await,
Poll::Pending
);
let mut waiter2 = waiter.clone();
assert_eq!(
lazy(|cx| Pin::new(&mut waiter2).poll(cx)).await,
Poll::Pending
);
drop(cond);
waiter.await;
waiter2.await;
}
}

View File

@@ -1,7 +1,6 @@
use core::cell::Cell;
use core::task;
use std::cell::Cell;
use std::rc::Rc;
use std::task;
use crate::task::LocalWaker;

View File

@@ -1,17 +1,15 @@
//! Framed dispatcher service and related utilities.
//! Framed dispatcher service and related utilities
#![allow(type_alias_bounds)]
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
use core::{fmt, mem};
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_core::stream::Stream;
use futures_util::{future::Future, stream::Stream, FutureExt};
use log::debug;
use pin_project_lite::pin_project;
use crate::mpsc;
@@ -63,35 +61,28 @@ pub enum Message<T> {
Close,
}
pin_project! {
/// Dispatcher is a future that reads frames from Framed object
/// and passes them to the service.
pub struct Dispatcher<S, T, U, I>
where
S: Service<<U as Decoder>::Item, Response = I>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead,
T: AsyncWrite,
U: Encoder<I>,
U: Decoder,
I: 'static,
<U as Encoder<I>>::Error: fmt::Debug,
{
service: S,
state: State<S, U, I>,
#[pin]
framed: Framed<T, U>,
rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
tx: mpsc::Sender<Result<Message<I>, S::Error>>,
}
/// Dispatcher is a future that reads frames from Framed object
/// and passes them to the service.
#[pin_project::pin_project]
pub struct Dispatcher<S, T, U, I>
where
S: Service<Request = <U as Decoder>::Item, Response = I>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Encoder<I> + Decoder,
I: 'static,
<U as Encoder<I>>::Error: std::fmt::Debug,
{
service: S,
state: State<S, U, I>,
#[pin]
framed: Framed<T, U>,
rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
tx: mpsc::Sender<Result<Message<I>, S::Error>>,
}
enum State<S, U, I>
where
S: Service<<U as Decoder>::Item>,
U: Encoder<I> + Decoder,
{
enum State<S: Service, U: Encoder<I> + Decoder, I> {
Processing,
Error(DispatcherError<S::Error, U, I>),
FramedError(DispatcherError<S::Error, U, I>),
@@ -99,11 +90,7 @@ where
Stopping,
}
impl<S, U, I> State<S, U, I>
where
S: Service<<U as Decoder>::Item>,
U: Encoder<I> + Decoder,
{
impl<S: Service, U: Encoder<I> + Decoder, I> State<S, U, I> {
fn take_error(&mut self) -> DispatcherError<S::Error, U, I> {
match mem::replace(self, State::Processing) {
State::Error(err) => err,
@@ -121,19 +108,16 @@ where
impl<S, T, U, I> Dispatcher<S, T, U, I>
where
S: Service<<U as Decoder>::Item, Response = I>,
S: Service<Request = <U as Decoder>::Item, Response = I>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>,
I: 'static,
<U as Decoder>::Error: fmt::Debug,
<U as Encoder<I>>::Error: fmt::Debug,
<U as Decoder>::Error: std::fmt::Debug,
<U as Encoder<I>>::Error: std::fmt::Debug,
{
pub fn new<F>(framed: Framed<T, U>, service: F) -> Self
where
F: IntoService<S, <U as Decoder>::Item>,
{
pub fn new<F: IntoService<S>>(framed: Framed<T, U>, service: F) -> Self {
let (tx, rx) = mpsc::channel();
Dispatcher {
framed,
@@ -145,14 +129,11 @@ where
}
/// Construct new `Dispatcher` instance with customer `mpsc::Receiver`
pub fn with_rx<F>(
pub fn with_rx<F: IntoService<S>>(
framed: Framed<T, U>,
service: F,
rx: mpsc::Receiver<Result<Message<I>, S::Error>>,
) -> Self
where
F: IntoService<S, <U as Decoder>::Item>,
{
) -> Self {
let tx = rx.sender();
Dispatcher {
framed,
@@ -191,13 +172,13 @@ where
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool
where
S: Service<<U as Decoder>::Item, Response = I>,
S: Service<Request = <U as Decoder>::Item, Response = I>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>,
I: 'static,
<U as Encoder<I>>::Error: fmt::Debug,
<U as Encoder<I>>::Error: std::fmt::Debug,
{
loop {
let this = self.as_mut().project();
@@ -217,11 +198,9 @@ where
};
let tx = this.tx.clone();
let fut = this.service.call(item);
actix_rt::spawn(async move {
let item = fut.await;
actix_rt::spawn(this.service.call(item).map(move |item| {
let _ = tx.send(item.map(Message::Item));
});
}));
}
Poll::Pending => return false,
Poll::Ready(Err(err)) => {
@@ -235,13 +214,13 @@ where
/// write to framed object
fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool
where
S: Service<<U as Decoder>::Item, Response = I>,
S: Service<Request = <U as Decoder>::Item, Response = I>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>,
I: 'static,
<U as Encoder<I>>::Error: fmt::Debug,
<U as Encoder<I>>::Error: std::fmt::Debug,
{
loop {
let mut this = self.as_mut().project();
@@ -286,14 +265,14 @@ where
impl<S, T, U, I> Future for Dispatcher<S, T, U, I>
where
S: Service<<U as Decoder>::Item, Response = I>,
S: Service<Request = <U as Decoder>::Item, Response = I>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder<I>,
I: 'static,
<U as Encoder<I>>::Error: fmt::Debug,
<U as Decoder>::Error: fmt::Debug,
<U as Encoder<I>>::Error: std::fmt::Debug,
<U as Decoder>::Error: std::fmt::Debug,
{
type Output = Result<(), DispatcherError<S::Error, U, I>>;

153
actix-utils/src/either.rs Normal file
View File

@@ -0,0 +1,153 @@
//! Contains `Either` service and related types and functions.
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_service::{Service, ServiceFactory};
use futures_util::{future, future::Future, ready};
/// Combine two different service types into a single type.
///
/// Both services must be of the same request, response, and error types.
/// `EitherService` is useful for handling conditional branching in service
/// middleware to different inner service types.
pub struct EitherService<A, B> {
left: A,
right: B,
}
impl<A: Clone, B: Clone> Clone for EitherService<A, B> {
fn clone(&self) -> Self {
EitherService {
left: self.left.clone(),
right: self.right.clone(),
}
}
}
impl<A, B> Service for EitherService<A, B>
where
A: Service,
B: Service<Response = A::Response, Error = A::Error>,
{
type Request = either::Either<A::Request, B::Request>;
type Response = A::Response;
type Error = A::Error;
type Future = future::Either<A::Future, B::Future>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let left = self.left.poll_ready(cx)?;
let right = self.right.poll_ready(cx)?;
if left.is_ready() && right.is_ready() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
fn call(&mut self, req: either::Either<A::Request, B::Request>) -> Self::Future {
match req {
either::Either::Left(req) => future::Either::Left(self.left.call(req)),
either::Either::Right(req) => future::Either::Right(self.right.call(req)),
}
}
}
/// Combine two different new service types into a single service.
pub struct Either<A, B> {
left: A,
right: B,
}
impl<A, B> Either<A, B> {
pub fn new(left: A, right: B) -> Either<A, B>
where
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<
Config = A::Config,
Response = A::Response,
Error = A::Error,
InitError = A::InitError,
>,
{
Either { left, right }
}
}
impl<A, B> ServiceFactory for Either<A, B>
where
A: ServiceFactory,
A::Config: Clone,
B: ServiceFactory<
Config = A::Config,
Response = A::Response,
Error = A::Error,
InitError = A::InitError,
>,
{
type Request = either::Either<A::Request, B::Request>;
type Response = A::Response;
type Error = A::Error;
type InitError = A::InitError;
type Config = A::Config;
type Service = EitherService<A::Service, B::Service>;
type Future = EitherNewService<A, B>;
fn new_service(&self, cfg: A::Config) -> Self::Future {
EitherNewService {
left: None,
right: None,
left_fut: self.left.new_service(cfg.clone()),
right_fut: self.right.new_service(cfg),
}
}
}
impl<A: Clone, B: Clone> Clone for Either<A, B> {
fn clone(&self) -> Self {
Self {
left: self.left.clone(),
right: self.right.clone(),
}
}
}
#[doc(hidden)]
#[pin_project::pin_project]
pub struct EitherNewService<A: ServiceFactory, B: ServiceFactory> {
left: Option<A::Service>,
right: Option<B::Service>,
#[pin]
left_fut: A::Future,
#[pin]
right_fut: B::Future,
}
impl<A, B> Future for EitherNewService<A, B>
where
A: ServiceFactory,
B: ServiceFactory<Response = A::Response, Error = A::Error, InitError = A::InitError>,
{
type Output = Result<EitherService<A::Service, B::Service>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
if this.left.is_none() {
*this.left = Some(ready!(this.left_fut.poll(cx))?);
}
if this.right.is_none() {
*this.right = Some(ready!(this.right_fut.poll(cx))?);
}
if this.left.is_some() && this.right.is_some() {
Poll::Ready(Ok(EitherService {
left: this.left.take().unwrap(),
right: this.right.take().unwrap(),
}))
} else {
Poll::Pending
}
}
}

169
actix-utils/src/inflight.rs Normal file
View File

@@ -0,0 +1,169 @@
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_util::future::{ok, Ready};
use super::counter::{Counter, CounterGuard};
/// InFlight - new service for service that can limit number of in-flight
/// async requests.
///
/// Default number of in-flight requests is 15
pub struct InFlight {
max_inflight: usize,
}
impl InFlight {
pub fn new(max: usize) -> Self {
Self { max_inflight: max }
}
}
impl Default for InFlight {
fn default() -> Self {
Self::new(15)
}
}
impl<S> Transform<S> for InFlight
where
S: Service,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type InitError = Infallible;
type Transform = InFlightService<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ok(InFlightService::new(self.max_inflight, service))
}
}
pub struct InFlightService<S> {
count: Counter,
service: S,
}
impl<S> InFlightService<S>
where
S: Service,
{
pub fn new<U>(max: usize, service: U) -> Self
where
U: IntoService<S>,
{
Self {
count: Counter::new(max),
service: service.into_service(),
}
}
}
impl<T> Service for InFlightService<T>
where
T: Service,
{
type Request = T::Request;
type Response = T::Response;
type Error = T::Error;
type Future = InFlightServiceResponse<T>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
if self.service.poll_ready(cx)?.is_pending() {
Poll::Pending
} else if !self.count.available(cx) {
log::trace!("InFlight limit exceeded");
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
fn call(&mut self, req: T::Request) -> Self::Future {
InFlightServiceResponse {
fut: self.service.call(req),
_guard: self.count.get(),
}
}
}
#[doc(hidden)]
#[pin_project::pin_project]
pub struct InFlightServiceResponse<T: Service> {
#[pin]
fut: T::Future,
_guard: CounterGuard,
}
impl<T: Service> Future for InFlightServiceResponse<T> {
type Output = Result<T::Response, T::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().fut.poll(cx)
}
}
#[cfg(test)]
mod tests {
use std::task::{Context, Poll};
use std::time::Duration;
use super::*;
use actix_service::{apply, fn_factory, Service, ServiceFactory};
use futures_util::future::{lazy, ok, FutureExt, LocalBoxFuture};
struct SleepService(Duration);
impl Service for SleepService {
type Request = ();
type Response = ();
type Error = ();
type Future = LocalBoxFuture<'static, Result<(), ()>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
actix_rt::time::delay_for(self.0)
.then(|_| ok::<_, ()>(()))
.boxed_local()
}
}
#[actix_rt::test]
async fn test_transform() {
let wait_time = Duration::from_millis(50);
let mut srv = InFlightService::new(1, SleepService(wait_time));
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
let res = srv.call(());
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
let _ = res.await;
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
}
#[actix_rt::test]
async fn test_new_transform() {
let wait_time = Duration::from_millis(50);
let srv = apply(InFlight::new(1), fn_factory(|| ok(SleepService(wait_time))));
let mut srv = srv.new_service(&()).await.unwrap();
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
let res = srv.call(());
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Pending);
let _ = res.await;
assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(())));
}
}

View File

@@ -0,0 +1,125 @@
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;
use actix_rt::time::{delay_until, Delay, Instant};
use actix_service::{Service, ServiceFactory};
use futures_util::future::{ok, Ready};
use super::time::{LowResTime, LowResTimeService};
pub struct KeepAlive<R, E, F> {
f: F,
ka: Duration,
time: LowResTime,
_t: PhantomData<(R, E)>,
}
impl<R, E, F> KeepAlive<R, E, F>
where
F: Fn() -> E + Clone,
{
pub fn new(ka: Duration, time: LowResTime, f: F) -> Self {
KeepAlive {
f,
ka,
time,
_t: PhantomData,
}
}
}
impl<R, E, F> Clone for KeepAlive<R, E, F>
where
F: Clone,
{
fn clone(&self) -> Self {
KeepAlive {
f: self.f.clone(),
ka: self.ka,
time: self.time.clone(),
_t: PhantomData,
}
}
}
impl<R, E, F> ServiceFactory for KeepAlive<R, E, F>
where
F: Fn() -> E + Clone,
{
type Request = R;
type Response = R;
type Error = E;
type InitError = Infallible;
type Config = ();
type Service = KeepAliveService<R, E, F>;
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
ok(KeepAliveService::new(
self.ka,
self.time.timer(),
self.f.clone(),
))
}
}
pub struct KeepAliveService<R, E, F> {
f: F,
ka: Duration,
time: LowResTimeService,
delay: Delay,
expire: Instant,
_t: PhantomData<(R, E)>,
}
impl<R, E, F> KeepAliveService<R, E, F>
where
F: Fn() -> E,
{
pub fn new(ka: Duration, time: LowResTimeService, f: F) -> Self {
let expire = Instant::from_std(time.now() + ka);
KeepAliveService {
f,
ka,
time,
expire,
delay: delay_until(expire),
_t: PhantomData,
}
}
}
impl<R, E, F> Service for KeepAliveService<R, E, F>
where
F: Fn() -> E,
{
type Request = R;
type Response = R;
type Error = E;
type Future = Ready<Result<R, E>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
match Pin::new(&mut self.delay).poll(cx) {
Poll::Ready(_) => {
let now = Instant::from_std(self.time.now());
if self.expire <= now {
Poll::Ready(Err((self.f)()))
} else {
self.delay.reset(self.expire);
let _ = Pin::new(&mut self.delay).poll(cx);
Poll::Ready(Ok(()))
}
}
Poll::Pending => Poll::Ready(Ok(())),
}
}
fn call(&mut self, req: R) -> Self::Future {
self.expire = Instant::from_std(self.time.now() + self.ka);
ok(req)
}
}

View File

@@ -1,12 +1,20 @@
//! Various network related services and utilities for the Actix ecosystem.
//! Actix utils - various helper services
#![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
pub mod condition;
pub mod counter;
pub mod dispatcher;
pub mod either;
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;

View File

@@ -1,17 +1,15 @@
//! A multi-producer, single-consumer, futures-aware, FIFO queue.
use core::any::Any;
use core::cell::RefCell;
use core::fmt;
use core::pin::Pin;
use core::task::{Context, Poll};
use std::any::Any;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::error::Error;
use std::fmt;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use futures_core::stream::Stream;
use futures_sink::Sink;
use futures_util::stream::Stream;
use crate::task::LocalWaker;

316
actix-utils/src/oneshot.rs Normal file
View File

@@ -0,0 +1,316 @@
//! A one-shot, futures-aware channel.
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
pub use futures_channel::oneshot::Canceled;
use slab::Slab;
use crate::task::LocalWaker;
/// Creates a new futures-aware, one-shot channel.
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
let inner = Rc::new(RefCell::new(Inner {
value: None,
rx_task: LocalWaker::new(),
}));
let tx = Sender {
inner: inner.clone(),
};
let rx = Receiver { inner };
(tx, rx)
}
/// Creates a new futures-aware, pool of one-shot's.
pub fn pool<T>() -> Pool<T> {
Pool(Rc::new(RefCell::new(Slab::new())))
}
/// Represents the completion half of a oneshot through which the result of a
/// computation is signaled.
#[derive(Debug)]
pub struct Sender<T> {
inner: Rc<RefCell<Inner<T>>>,
}
/// A future representing the completion of a computation happening elsewhere in
/// memory.
#[derive(Debug)]
#[must_use = "futures do nothing unless polled"]
pub struct Receiver<T> {
inner: Rc<RefCell<Inner<T>>>,
}
// The channels do not ever project Pin to the inner T
impl<T> Unpin for Receiver<T> {}
impl<T> Unpin for Sender<T> {}
#[derive(Debug)]
struct Inner<T> {
value: Option<T>,
rx_task: LocalWaker,
}
impl<T> Sender<T> {
/// 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 dropped before
/// this function was called, however, then `Err` is returned with the value
/// provided.
pub fn send(self, val: T) -> Result<(), T> {
if Rc::strong_count(&self.inner) == 2 {
let mut inner = self.inner.borrow_mut();
inner.value = Some(val);
inner.rx_task.wake();
Ok(())
} else {
Err(val)
}
}
/// Tests to see whether this `Sender`'s corresponding `Receiver`
/// has gone away.
pub fn is_canceled(&self) -> bool {
Rc::strong_count(&self.inner) == 1
}
}
impl<T> Drop for Sender<T> {
fn drop(&mut self) {
self.inner.borrow().rx_task.wake();
}
}
impl<T> Future for Receiver<T> {
type Output = Result<T, Canceled>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
// If we've got a value, then skip the logic below as we're done.
if let Some(val) = this.inner.borrow_mut().value.take() {
return Poll::Ready(Ok(val));
}
// Check if sender is dropped and return error if it is.
if Rc::strong_count(&this.inner) == 1 {
Poll::Ready(Err(Canceled))
} else {
this.inner.borrow().rx_task.register(cx.waker());
Poll::Pending
}
}
}
/// Futures-aware, pool of one-shot's.
pub struct Pool<T>(Rc<RefCell<Slab<PoolInner<T>>>>);
bitflags::bitflags! {
pub struct Flags: u8 {
const SENDER = 0b0000_0001;
const RECEIVER = 0b0000_0010;
}
}
#[derive(Debug)]
struct PoolInner<T> {
flags: Flags,
value: Option<T>,
waker: LocalWaker,
}
impl<T> Pool<T> {
pub fn channel(&mut self) -> (PSender<T>, PReceiver<T>) {
let token = self.0.borrow_mut().insert(PoolInner {
flags: Flags::all(),
value: None,
waker: LocalWaker::default(),
});
(
PSender {
token,
inner: self.0.clone(),
},
PReceiver {
token,
inner: self.0.clone(),
},
)
}
}
impl<T> Clone for Pool<T> {
fn clone(&self) -> Self {
Pool(self.0.clone())
}
}
/// Represents the completion half of a oneshot through which the result of a
/// computation is signaled.
#[derive(Debug)]
pub struct PSender<T> {
token: usize,
inner: Rc<RefCell<Slab<PoolInner<T>>>>,
}
/// A future representing the completion of a computation happening elsewhere in
/// memory.
#[derive(Debug)]
#[must_use = "futures do nothing unless polled"]
pub struct PReceiver<T> {
token: usize,
inner: Rc<RefCell<Slab<PoolInner<T>>>>,
}
// The one-shots do not ever project Pin to the inner T
impl<T> Unpin for PReceiver<T> {}
impl<T> Unpin for PSender<T> {}
impl<T> PSender<T> {
/// 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 dropped before
/// this function was called, however, then `Err` is returned with the value
/// provided.
pub fn send(self, val: T) -> Result<(), T> {
let mut inner = self.inner.borrow_mut();
let inner = unsafe { inner.get_unchecked_mut(self.token) };
if inner.flags.contains(Flags::RECEIVER) {
inner.value = Some(val);
inner.waker.wake();
Ok(())
} else {
Err(val)
}
}
/// Tests to see whether this `Sender`'s corresponding `Receiver`
/// has gone away.
pub fn is_canceled(&self) -> bool {
!unsafe { self.inner.borrow().get_unchecked(self.token) }
.flags
.contains(Flags::RECEIVER)
}
}
impl<T> Drop for PSender<T> {
fn drop(&mut self) {
let mut inner = self.inner.borrow_mut();
let inner_token = unsafe { inner.get_unchecked_mut(self.token) };
if inner_token.flags.contains(Flags::RECEIVER) {
inner_token.waker.wake();
inner_token.flags.remove(Flags::SENDER);
} else {
inner.remove(self.token);
}
}
}
impl<T> Drop for PReceiver<T> {
fn drop(&mut self) {
let mut inner = self.inner.borrow_mut();
let inner_token = unsafe { inner.get_unchecked_mut(self.token) };
if inner_token.flags.contains(Flags::SENDER) {
inner_token.flags.remove(Flags::RECEIVER);
} else {
inner.remove(self.token);
}
}
}
impl<T> Future for PReceiver<T> {
type Output = Result<T, Canceled>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let mut inner = this.inner.borrow_mut();
let inner = unsafe { inner.get_unchecked_mut(this.token) };
// If we've got a value, then skip the logic below as we're done.
if let Some(val) = inner.value.take() {
return Poll::Ready(Ok(val));
}
// Check if sender is dropped and return error if it is.
if !inner.flags.contains(Flags::SENDER) {
Poll::Ready(Err(Canceled))
} else {
inner.waker.register(cx.waker());
Poll::Pending
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures_util::future::lazy;
#[actix_rt::test]
async fn test_oneshot() {
let (tx, rx) = channel();
tx.send("test").unwrap();
assert_eq!(rx.await.unwrap(), "test");
let (tx, rx) = channel();
assert!(!tx.is_canceled());
drop(rx);
assert!(tx.is_canceled());
assert!(tx.send("test").is_err());
let (tx, rx) = channel::<&'static str>();
drop(tx);
assert!(rx.await.is_err());
let (tx, mut rx) = channel::<&'static str>();
assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending);
tx.send("test").unwrap();
assert_eq!(rx.await.unwrap(), "test");
let (tx, mut rx) = channel::<&'static str>();
assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending);
drop(tx);
assert!(rx.await.is_err());
}
#[actix_rt::test]
async fn test_pool() {
let (tx, rx) = pool().channel();
tx.send("test").unwrap();
assert_eq!(rx.await.unwrap(), "test");
let (tx, rx) = pool().channel();
assert!(!tx.is_canceled());
drop(rx);
assert!(tx.is_canceled());
assert!(tx.send("test").is_err());
let (tx, rx) = pool::<&'static str>().channel();
drop(tx);
assert!(rx.await.is_err());
let (tx, mut rx) = pool::<&'static str>().channel();
assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending);
tx.send("test").unwrap();
assert_eq!(rx.await.unwrap(), "test");
let (tx, mut rx) = pool::<&'static str>().channel();
assert_eq!(lazy(|cx| Pin::new(&mut rx).poll(cx)).await, Poll::Pending);
drop(tx);
assert!(rx.await.is_err());
}
}

283
actix-utils/src/order.rs Normal file
View File

@@ -0,0 +1,283 @@
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_util::future::{ok, Ready};
use crate::oneshot;
use crate::task::LocalWaker;
struct Record<I, E> {
rx: oneshot::Receiver<Result<I, E>>,
tx: oneshot::Sender<Result<I, E>>,
}
/// Timeout error
pub enum InOrderError<E> {
/// Service error
Service(E),
/// Service call dropped
Disconnected,
}
impl<E> From<E> for InOrderError<E> {
fn from(err: E) -> Self {
InOrderError::Service(err)
}
}
impl<E: fmt::Debug> fmt::Debug for InOrderError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InOrderError::Service(e) => write!(f, "InOrderError::Service({:?})", e),
InOrderError::Disconnected => write!(f, "InOrderError::Disconnected"),
}
}
}
impl<E: fmt::Display> fmt::Display for InOrderError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
InOrderError::Service(e) => e.fmt(f),
InOrderError::Disconnected => write!(f, "InOrder service disconnected"),
}
}
}
/// InOrder - The service will yield responses as they become available,
/// in the order that their originating requests were submitted to the service.
pub struct InOrder<S> {
_t: PhantomData<S>,
}
impl<S> InOrder<S>
where
S: Service,
S::Response: 'static,
S::Future: 'static,
S::Error: 'static,
{
pub fn new() -> Self {
Self { _t: PhantomData }
}
pub fn service(service: S) -> InOrderService<S> {
InOrderService::new(service)
}
}
impl<S> Default for InOrder<S>
where
S: Service,
S::Response: 'static,
S::Future: 'static,
S::Error: 'static,
{
fn default() -> Self {
Self::new()
}
}
impl<S> Transform<S> for InOrder<S>
where
S: Service,
S::Response: 'static,
S::Future: 'static,
S::Error: 'static,
{
type Request = S::Request;
type Response = S::Response;
type Error = InOrderError<S::Error>;
type InitError = Infallible;
type Transform = InOrderService<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
ok(InOrderService::new(service))
}
}
pub struct InOrderService<S: Service> {
service: S,
waker: Rc<LocalWaker>,
acks: VecDeque<Record<S::Response, S::Error>>,
}
impl<S> InOrderService<S>
where
S: Service,
S::Response: 'static,
S::Future: 'static,
S::Error: 'static,
{
pub fn new<U>(service: U) -> Self
where
U: IntoService<S>,
{
Self {
service: service.into_service(),
acks: VecDeque::new(),
waker: Rc::new(LocalWaker::new()),
}
}
}
impl<S> Service for InOrderService<S>
where
S: Service,
S::Response: 'static,
S::Future: 'static,
S::Error: 'static,
{
type Request = S::Request;
type Response = S::Response;
type Error = InOrderError<S::Error>;
type Future = InOrderServiceResponse<S>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
// poll_ready could be called from different task
self.waker.register(cx.waker());
// check acks
while !self.acks.is_empty() {
let rec = self.acks.front_mut().unwrap();
match Pin::new(&mut rec.rx).poll(cx) {
Poll::Ready(Ok(res)) => {
let rec = self.acks.pop_front().unwrap();
let _ = rec.tx.send(res);
}
Poll::Pending => break,
Poll::Ready(Err(oneshot::Canceled)) => {
return Poll::Ready(Err(InOrderError::Disconnected))
}
}
}
// check nested service
if self
.service
.poll_ready(cx)
.map_err(InOrderError::Service)?
.is_pending()
{
Poll::Pending
} else {
Poll::Ready(Ok(()))
}
}
fn call(&mut self, request: S::Request) -> Self::Future {
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
self.acks.push_back(Record { rx: rx1, tx: tx2 });
let waker = self.waker.clone();
let fut = self.service.call(request);
actix_rt::spawn(async move {
let res = fut.await;
waker.wake();
let _ = tx1.send(res);
});
InOrderServiceResponse { rx: rx2 }
}
}
#[doc(hidden)]
pub struct InOrderServiceResponse<S: Service> {
rx: oneshot::Receiver<Result<S::Response, S::Error>>,
}
impl<S: Service> Future for InOrderServiceResponse<S> {
type Output = Result<S::Response, InOrderError<S::Error>>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
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 std::task::{Context, Poll};
use std::time::Duration;
use super::*;
use actix_service::Service;
use futures_channel::oneshot;
use futures_util::future::{lazy, poll_fn, FutureExt, LocalBoxFuture};
struct Srv;
impl Service for Srv {
type Request = oneshot::Receiver<usize>;
type Response = usize;
type Error = ();
type Future = LocalBoxFuture<'static, Result<usize, ()>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, req: oneshot::Receiver<usize>) -> Self::Future {
req.map(|res| res.map_err(|_| ())).boxed_local()
}
}
#[actix_rt::test]
async fn test_in_order() {
let (tx1, rx1) = oneshot::channel();
let (tx2, rx2) = oneshot::channel();
let (tx3, rx3) = oneshot::channel();
let (tx_stop, rx_stop) = oneshot::channel();
let h = std::thread::spawn(move || {
let rx1 = rx1;
let rx2 = rx2;
let rx3 = rx3;
let tx_stop = tx_stop;
actix_rt::System::new("test").block_on(async {
let mut srv = InOrderService::new(Srv);
let _ = lazy(|cx| srv.poll_ready(cx)).await;
let res1 = srv.call(rx1);
let res2 = srv.call(rx2);
let res3 = srv.call(rx3);
actix_rt::spawn(async move {
poll_fn(|cx| {
let _ = srv.poll_ready(cx);
Poll::<()>::Pending
})
.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);
std::thread::sleep(Duration::from_millis(50));
let _ = tx2.send(2);
let _ = tx1.send(1);
let _ = rx_stop.await;
let _ = h.join();
}
}

76
actix-utils/src/stream.rs Normal file
View File

@@ -0,0 +1,76 @@
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_service::{IntoService, Service};
use futures_util::{stream::Stream, FutureExt};
use crate::mpsc;
#[pin_project::pin_project]
pub struct Dispatcher<S, T>
where
S: Stream,
T: Service<Request = S::Item, Response = ()> + 'static,
{
#[pin]
stream: S,
service: T,
err_rx: mpsc::Receiver<T::Error>,
err_tx: mpsc::Sender<T::Error>,
}
impl<S, T> Dispatcher<S, T>
where
S: Stream,
T: Service<Request = S::Item, Response = ()> + 'static,
{
pub fn new<F>(stream: S, service: F) -> Self
where
F: IntoService<T>,
{
let (err_tx, err_rx) = mpsc::channel();
Dispatcher {
err_rx,
err_tx,
stream,
service: service.into_service(),
}
}
}
impl<S, T> Future for Dispatcher<S, T>
where
S: Stream,
T: Service<Request = S::Item, Response = ()> + 'static,
{
type Output = Result<(), T::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
if let Poll::Ready(Some(e)) = Pin::new(&mut this.err_rx).poll_next(cx) {
return Poll::Ready(Err(e));
}
loop {
return match this.service.poll_ready(cx)? {
Poll::Ready(_) => match this.stream.poll_next(cx) {
Poll::Ready(Some(item)) => {
let stop = this.err_tx.clone();
actix_rt::spawn(this.service.call(item).map(move |res| {
if let Err(e) = res {
let _ = stop.send(e);
}
}));
this = self.as_mut().project();
continue;
}
Poll::Pending => Poll::Pending,
Poll::Ready(None) => Poll::Ready(Ok(())),
},
Poll::Pending => Poll::Pending,
};
}
}
}

View File

@@ -1,7 +1,7 @@
use core::cell::UnsafeCell;
use core::fmt;
use core::marker::PhantomData;
use core::task::Waker;
use std::cell::UnsafeCell;
use std::marker::PhantomData;
use std::task::Waker;
use std::{fmt, rc};
/// A synchronization primitive for task wakeup.
///
@@ -23,8 +23,7 @@ use core::task::Waker;
#[derive(Default)]
pub struct LocalWaker {
pub(crate) waker: UnsafeCell<Option<Waker>>,
// mark LocalWaker as a !Send type.
_t: PhantomData<*const ()>,
_t: PhantomData<rc::Rc<()>>,
}
impl LocalWaker {

225
actix-utils/src/time.rs Normal file
View File

@@ -0,0 +1,225 @@
use std::cell::RefCell;
use std::convert::Infallible;
use std::rc::Rc;
use std::task::{Context, Poll};
use std::time::{self, Duration, Instant};
use actix_rt::time::delay_for;
use actix_service::{Service, ServiceFactory};
use futures_util::future::{ok, ready, FutureExt, Ready};
#[derive(Clone, Debug)]
pub struct LowResTime(Rc<RefCell<Inner>>);
#[derive(Debug)]
struct Inner {
resolution: Duration,
current: Option<Instant>,
}
impl Inner {
fn new(resolution: Duration) -> Self {
Inner {
resolution,
current: None,
}
}
}
impl LowResTime {
pub fn with(resolution: Duration) -> LowResTime {
LowResTime(Rc::new(RefCell::new(Inner::new(resolution))))
}
pub fn timer(&self) -> LowResTimeService {
LowResTimeService(self.0.clone())
}
}
impl Default for LowResTime {
fn default() -> Self {
LowResTime(Rc::new(RefCell::new(Inner::new(Duration::from_secs(1)))))
}
}
impl ServiceFactory for LowResTime {
type Request = ();
type Response = Instant;
type Error = Infallible;
type InitError = Infallible;
type Config = ();
type Service = LowResTimeService;
type Future = Ready<Result<Self::Service, Self::InitError>>;
fn new_service(&self, _: ()) -> Self::Future {
ok(self.timer())
}
}
#[derive(Clone, Debug)]
pub struct LowResTimeService(Rc<RefCell<Inner>>);
impl LowResTimeService {
pub fn with(resolution: Duration) -> LowResTimeService {
LowResTimeService(Rc::new(RefCell::new(Inner::new(resolution))))
}
/// Get current time. This function has to be called from
/// future's poll method, otherwise it panics.
pub fn now(&self) -> Instant {
let cur = self.0.borrow().current;
if let Some(cur) = cur {
cur
} else {
let now = Instant::now();
let inner = self.0.clone();
let interval = {
let mut b = inner.borrow_mut();
b.current = Some(now);
b.resolution
};
actix_rt::spawn(delay_for(interval).then(move |_| {
inner.borrow_mut().current.take();
ready(())
}));
now
}
}
}
impl Service for LowResTimeService {
type Request = ();
type Response = Instant;
type Error = Infallible;
type Future = Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
ok(self.now())
}
}
#[derive(Clone, Debug)]
pub struct SystemTime(Rc<RefCell<SystemTimeInner>>);
#[derive(Debug)]
struct SystemTimeInner {
resolution: Duration,
current: Option<time::SystemTime>,
}
impl SystemTimeInner {
fn new(resolution: Duration) -> Self {
SystemTimeInner {
resolution,
current: None,
}
}
}
#[derive(Clone, Debug)]
pub struct SystemTimeService(Rc<RefCell<SystemTimeInner>>);
impl SystemTimeService {
pub fn with(resolution: Duration) -> SystemTimeService {
SystemTimeService(Rc::new(RefCell::new(SystemTimeInner::new(resolution))))
}
/// Get current time. This function has to be called from
/// future's poll method, otherwise it panics.
pub fn now(&self) -> time::SystemTime {
let cur = self.0.borrow().current;
if let Some(cur) = cur {
cur
} else {
let now = time::SystemTime::now();
let inner = self.0.clone();
let interval = {
let mut b = inner.borrow_mut();
b.current = Some(now);
b.resolution
};
actix_rt::spawn(delay_for(interval).then(move |_| {
inner.borrow_mut().current.take();
ready(())
}));
now
}
}
}
#[cfg(test)]
mod tests {
use super::*;
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`.
///
/// Expected Behavior: Two back-to-back calls of `SystemTimeService::now()` return the same value.
#[actix_rt::test]
async fn system_time_service_time_does_not_immediately_change() {
let resolution = Duration::from_millis(50);
let time_service = SystemTimeService::with(resolution);
assert_eq!(time_service.now(), time_service.now());
}
/// State Under Test: Two calls of `LowResTimeService::now()` return the same value if they are done within resolution interval of `SystemTimeService`.
///
/// Expected Behavior: Two back-to-back calls of `LowResTimeService::now()` return the same value.
#[actix_rt::test]
async fn low_res_time_service_time_does_not_immediately_change() {
let resolution = Duration::from_millis(50);
let time_service = LowResTimeService::with(resolution);
assert_eq!(time_service.now(), time_service.now());
}
/// State Under Test: `SystemTimeService::now()` updates returned value every resolution period.
///
/// Expected Behavior: Two calls of `LowResTimeService::now()` made in subsequent resolution interval return different values
/// and second value is greater than the first one at least by a resolution interval.
#[actix_rt::test]
async fn system_time_service_time_updates_after_resolution_interval() {
let resolution = Duration::from_millis(100);
let wait_time = Duration::from_millis(300);
let time_service = SystemTimeService::with(resolution);
let first_time = time_service
.now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
delay_for(wait_time).await;
let second_time = time_service
.now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
assert!(second_time - first_time >= wait_time);
}
/// State Under Test: `LowResTimeService::now()` updates returned value every resolution period.
///
/// Expected Behavior: Two calls of `LowResTimeService::now()` made in subsequent resolution interval return different values
/// and second value is greater than the first one at least by a resolution interval.
#[actix_rt::test]
async fn low_res_time_service_time_updates_after_resolution_interval() {
let resolution = Duration::from_millis(100);
let wait_time = Duration::from_millis(300);
let time_service = LowResTimeService::with(resolution);
let first_time = time_service.now();
delay_for(wait_time).await;
let second_time = time_service.now();
assert!(second_time - first_time >= wait_time);
}
}

View File

@@ -1,16 +1,16 @@
//! Service that applies a timeout to requests.
//!
//! If the response does not complete within the specified timeout, the response will be aborted.
//! If the response does not complete within the specified timeout, the response
//! will be aborted.
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::{fmt, time};
use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin;
use core::task::{Context, Poll};
use core::{fmt, time};
use actix_rt::time::{sleep, Sleep};
use actix_rt::time::{delay_for, Delay};
use actix_service::{IntoService, Service, Transform};
use pin_project_lite::pin_project;
use futures_util::future::{ok, Ready};
/// Applies a timeout to requests.
#[derive(Debug)]
@@ -78,149 +78,126 @@ impl<E> Clone for Timeout<E> {
}
}
impl<S, E, Req> Transform<S, Req> for Timeout<E>
impl<S, E> Transform<S> for Timeout<E>
where
S: Service<Req>,
S: Service,
{
type Request = S::Request;
type Response = S::Response;
type Error = TimeoutError<S::Error>;
type Transform = TimeoutService<S, Req>;
type InitError = E;
type Future = TimeoutFuture<Self::Transform, Self::InitError>;
type Transform = TimeoutService<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;
fn new_transform(&self, service: S) -> Self::Future {
let service = TimeoutService {
ok(TimeoutService {
service,
timeout: self.timeout,
_phantom: PhantomData,
};
TimeoutFuture {
service: Some(service),
_err: PhantomData,
}
}
}
pub struct TimeoutFuture<T, E> {
service: Option<T>,
_err: PhantomData<E>,
}
impl<T, E> Unpin for TimeoutFuture<T, E> {}
impl<T, E> Future for TimeoutFuture<T, E> {
type Output = Result<T, E>;
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(Ok(self.get_mut().service.take().unwrap()))
})
}
}
/// Applies a timeout to requests.
#[derive(Debug, Clone)]
pub struct TimeoutService<S, Req> {
pub struct TimeoutService<S> {
service: S,
timeout: time::Duration,
_phantom: PhantomData<Req>,
}
impl<S, Req> TimeoutService<S, Req>
impl<S> TimeoutService<S>
where
S: Service<Req>,
S: Service,
{
pub fn new<U>(timeout: time::Duration, service: U) -> Self
where
U: IntoService<S, Req>,
U: IntoService<S>,
{
TimeoutService {
timeout,
service: service.into_service(),
_phantom: PhantomData,
}
}
}
impl<S, Req> Service<Req> for TimeoutService<S, Req>
impl<S> Service for TimeoutService<S>
where
S: Service<Req>,
S: Service,
{
type Request = S::Request;
type Response = S::Response;
type Error = TimeoutError<S::Error>;
type Future = TimeoutServiceResponse<S, Req>;
type Future = TimeoutServiceResponse<S>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx).map_err(TimeoutError::Service)
}
fn call(&mut self, request: Req) -> Self::Future {
fn call(&mut self, request: S::Request) -> Self::Future {
TimeoutServiceResponse {
fut: self.service.call(request),
sleep: sleep(self.timeout),
sleep: delay_for(self.timeout),
}
}
}
pin_project! {
/// `TimeoutService` response future
#[derive(Debug)]
pub struct TimeoutServiceResponse<S, Req>
where
S: Service<Req>
{
#[pin]
fut: S::Future,
#[pin]
sleep: Sleep,
}
/// `TimeoutService` response future
#[pin_project::pin_project]
#[derive(Debug)]
pub struct TimeoutServiceResponse<T: Service> {
#[pin]
fut: T::Future,
sleep: Delay,
}
impl<S, Req> Future for TimeoutServiceResponse<S, Req>
impl<T> Future for TimeoutServiceResponse<T>
where
S: Service<Req>,
T: Service,
{
type Output = Result<S::Response, TimeoutError<S::Error>>;
type Output = Result<T::Response, TimeoutError<T::Error>>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let mut this = self.project();
// First, try polling the future
if let Poll::Ready(res) = this.fut.poll(cx) {
return match res {
Ok(v) => Poll::Ready(Ok(v)),
Err(e) => Poll::Ready(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
this.sleep.poll(cx).map(|_| 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 core::task::Poll;
use core::time::Duration;
use std::task::{Context, Poll};
use std::time::Duration;
use super::*;
use actix_service::{apply, fn_factory, Service, ServiceFactory};
use futures_core::future::LocalBoxFuture;
use futures_util::future::{ok, FutureExt, LocalBoxFuture};
struct SleepService(Duration);
impl Service<()> for SleepService {
impl Service for SleepService {
type Request = ();
type Response = ();
type Error = ();
type Future = LocalBoxFuture<'static, Result<(), ()>>;
actix_service::always_ready!();
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future {
let sleep = actix_rt::time::sleep(self.0);
Box::pin(async move {
sleep.await;
Ok(())
})
actix_rt::time::delay_for(self.0)
.then(|_| ok::<_, ()>(()))
.boxed_local()
}
}
@@ -249,7 +226,7 @@ mod tests {
let timeout = apply(
Timeout::new(resolution),
fn_factory(|| async { Ok::<_, ()>(SleepService(wait_time)) }),
fn_factory(|| ok::<_, ()>(SleepService(wait_time))),
);
let mut srv = timeout.new_service(&()).await.unwrap();

View File

@@ -1,36 +0,0 @@
# Changes
## Unreleased - 2021-xx-xx
## 1.0.0 - 2020-12-31
* Update `bytes` dependency to `1`.
* Add array and slice of `u8` impls of `TryFrom` up to 32 in length.
* Rename `get_ref` to `as_bytes` and rename `into_inner` to `into_bytes`.
* `ByteString::new` is now a `const fn`.
* Crate is now `#[no_std]` compatible.
## 0.1.5 - 2020-03-30
* Serde support
## 0.1.4 - 2020-01-14
* Fix `AsRef<str>` impl
## 0.1.3 - 2020-01-13
* Add `PartialEq<T: AsRef<str>>`, `AsRef<[u8]>` impls
## 0.1.2 - 2019-12-22
* Fix `new()` method
* Make `ByteString::from_static()` and `ByteString::from_bytes_unchecked()` methods const.
## 0.1.1 - 2019-12-07
* Fix hash impl
## 0.1.0 - 2019-12-07
* Initial release

Some files were not shown because too many files have changed in this diff Show More