1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-08-15 04:25:57 +02:00

Compare commits

..

44 Commits

Author SHA1 Message Date
Rob Ede
ef83647ac9 prepare testing beta release 2020-12-28 23:54:21 +00:00
Rob Ede
98a17081b8 prepare server beta release 2020-12-28 23:50:00 +00:00
fakeshadow
b7202db8fd update actix-server and actix-testing to tokio 1.0 (#239) 2020-12-28 23:44:53 +00:00
Rob Ede
a09f9abfcb prepare utils release 3.0.0-beta.1 2020-12-28 03:32:28 +00:00
Rob Ede
e4a44b77e6 prepare codec release 0.4.0-beta.1 2020-12-28 03:24:43 +00:00
fakeshadow
2ee8f45f5d update actix-codec and actix-utils to tokio 1.0 (#237) 2020-12-28 03:16:37 +00:00
Rob Ede
f48e3f4cb0 prepare release for rt and service 2020-12-28 01:58:31 +00:00
Rob Ede
3d3bd60368 fix rt override 2020-12-28 01:53:11 +00:00
Rob Ede
d684128831 fix rt override 2020-12-28 01:48:19 +00:00
fakeshadow
0c12930796 update to tokio 1.0 for actix-rt (#236) 2020-12-28 01:40:22 +00:00
Rob Ede
ba44ea7d0b remove futures-util from service deps (#235) 2020-12-27 18:24:57 +00:00
Rob Ede
8a58a341a4 service improvements (#233) 2020-12-27 14:15:42 +00:00
Rob Ede
33c9aa6988 bump msrv to 1.46 2020-12-27 04:36:08 +00:00
Rob Ede
3ab8c3eb69 service trait takes request type parameter (#232) 2020-12-27 04:28:00 +00:00
fakeshadow
518bf3f6a6 remove RUNNING Q PENDING thread locals from actix-rt (#207) 2020-12-26 23:26:02 +00:00
fakeshadow
43ce25cda1 Remove unused mods in actix-utils (#229) 2020-12-26 21:27:59 +00:00
Yuki Okushi
4e4122b702 Disable PR comment from codecov 2020-12-17 21:42:21 +09:00
Aravinth Manivannan
b296d0f254 Intradoc links conversion (#227)
* intra doc conversion

* rm trailing blank comment
2020-12-14 08:22:30 +00:00
Juan Aguilar
02a902068f Refactor LocalWaker (#224) 2020-12-13 19:26:57 +00:00
fakeshadow
049795662f remove ServerMessage type. remove one unused InternalServiceFactory impl (#225) 2020-12-13 00:46:32 +00:00
Rob Ede
4e43216b99 standardise compiler lints across all crates (#226) 2020-12-12 23:24:00 +00:00
Rob Ede
93889776c4 prevent double registration of sockets when backpressure is resolved (#223) 2020-12-12 17:19:20 +00:00
Yuki Okushi
ab496a71b5 Fix release date 2020-12-03 08:59:59 +09:00
Yuki Okushi
76d956e25c macros: Add actix-reexport feature (#218) 2020-12-03 08:59:13 +09:00
Ivan Babrou
89e56cf661 Notify about paused accept loop (#215) 2020-11-29 15:30:13 +00:00
Rob Ede
8aca8d4d07 fix clippy warnings (#214)
and make my spelling checker happy
2020-11-25 01:41:14 +00:00
fakeshadow
e0dd2a3d76 remove actix-threadpool re-export from actix-rt (#212) 2020-11-24 17:03:09 +00:00
Rob Ede
59e976aaca address clippy error (#213) 2020-11-24 16:35:47 +00:00
Zura Benashvili
4cc1c87724 docs(transform): remove extra generic parameter (#211) 2020-11-20 22:45:57 +00:00
Yuki Okushi
ca39917d2c Update CoC contact information 2020-10-31 12:08:06 +09:00
ghizzo01
704af672b9 Bump pin-project to 1.0 (#202) 2020-10-25 19:42:40 +09:00
Rob Ede
242bef269f delete ioframe removed package readme
closes #199
2020-09-22 12:29:07 +01:00
Rob Ede
6c65e2a79f prepare router 0.2.5 release (#198) 2020-09-21 22:46:59 +01:00
nujz
e5ca271764 actix-router: fix from_hex error (#196) 2020-09-20 18:04:18 +01:00
nujz
98a2197a09 fix doc error (#195) 2020-09-19 23:12:41 +09:00
Rob Ede
fb0aa02b3c move and update server+tls examples (#190) 2020-09-13 10:12:07 +01:00
Rob Ede
681eeb497d prepare server release 1.0.4 (#188) 2020-09-12 15:28:17 +01:00
Igor Aleksanov
3e04b87311 actix-service: Fix broken link in readme (#189) 2020-09-12 15:08:03 +01:00
Rob Ede
77b7826658 prepare tls v2 release (#186) 2020-09-08 18:00:07 +01:00
Igor Aleksanov
b7a9cb7bb4 actix-rt: Make the process of running System in existing Runtime more clear (#173) 2020-09-06 11:01:24 +01:00
Robert Gabriel Jakabosky
88d99ac89c Fix clippy errors. (#187) 2020-09-06 10:41:42 +01:00
Rob Ede
7632f51509 prepare connect v2 stable release (#185) 2020-09-02 22:14:07 +01:00
Rob Ede
d28687d0d7 promote codec/utils out of beta (#184) 2020-08-24 09:18:37 +01:00
Rob Ede
27c6be9881 remove unused type parameter from Framed::replace_codec (#183) 2020-08-20 00:30:26 +01:00
117 changed files with 3678 additions and 5599 deletions

View File

@@ -1,29 +0,0 @@
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

@@ -31,4 +31,4 @@ jobs:
uses: actions-rs/clippy-check@v1 uses: actions-rs/clippy-check@v1
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features --all --tests args: --workspace --tests

View File

@@ -14,7 +14,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
version: version:
- 1.42.0 - 1.46.0
- stable - stable
- nightly - nightly
@@ -53,20 +53,20 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --all --bins --examples --tests args: --workspace --bins --examples --tests
- name: tests - name: tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
timeout-minutes: 40 timeout-minutes: 40
with: with:
command: test command: test
args: --all --all-features --no-fail-fast -- --nocapture args: --workspace --exclude=actix-tls --no-fail-fast -- --nocapture
- name: Generate coverage file - name: Generate coverage file
if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request') if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request')
run: | run: |
cargo install cargo-tarpaulin cargo install cargo-tarpaulin
cargo tarpaulin --out Xml --workspace --all-features cargo tarpaulin --out Xml --workspace
- name: Upload to Codecov - name: Upload to Codecov
if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request') if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request')

View File

@@ -34,10 +34,10 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --all --bins --examples --tests args: --workspace --bins --examples --tests
- name: tests - name: tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --all --all-features --no-fail-fast -- --nocapture args: --workspace --exclude=actix-tls --no-fail-fast -- --nocapture

View File

@@ -42,4 +42,4 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --all --bins --examples --tests args: --workspace --bins --examples --tests

View File

@@ -60,10 +60,10 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --all --bins --examples --tests args: --workspace --bins --examples --tests
- name: tests - name: tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: test
args: --all --all-features --no-fail-fast -- --nocapture args: --workspace --exclude=actix-tls --no-fail-fast -- --nocapture

View File

@@ -34,10 +34,13 @@ This Code of Conduct applies both within project spaces and in public spaces whe
## Enforcement ## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at fafhrd91@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at robjtede@icloud.com ([@robjtede]) or huyuumi@neet.club ([@JohnTitor]). The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
[@robjtede]: https://github.com/robjtede
[@JohnTitor]: https://github.com/JohnTitor
## Attribution ## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

View File

@@ -16,16 +16,16 @@ members = [
] ]
[patch.crates-io] [patch.crates-io]
actix-codec = { path = "actix-codec" } actix-codec = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
actix-connect = { path = "actix-connect" } actix-connect = { path = "actix-connect" }
actix-rt = { path = "actix-rt" } actix-rt = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
actix-macros = { path = "actix-macros" } actix-macros = { path = "actix-macros" }
actix-server = { path = "actix-server" } actix-server = { path = "actix-server" }
actix-service = { path = "actix-service" } actix-service = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
actix-testing = { path = "actix-testing" } actix-testing = { path = "actix-testing" }
actix-threadpool = { path = "actix-threadpool" } actix-threadpool = { path = "actix-threadpool" }
actix-tls = { path = "actix-tls" } actix-tls = { path = "actix-tls" }
actix-tracing = { path = "actix-tracing" } actix-tracing = { path = "actix-tracing" }
actix-utils = { path = "actix-utils" } actix-utils = { git = "https://github.com/actix/actix-net.git", rev = "ba44ea7d0bafaf5fccb9a34003d503e1910943ee" }
actix-router = { path = "router" } actix-router = { path = "router" }
bytestring = { path = "string" } bytestring = { path = "string" }

View File

@@ -14,7 +14,7 @@ Actix net - framework for composable network services
## Documentation & community resources ## Documentation & community resources
* [Chat on Gitter](https://gitter.im/actix/actix) * [Chat on Gitter](https://gitter.im/actix/actix)
* Minimum supported Rust version: 1.42 or later * Minimum supported Rust version: 1.46 or later
## Example ## Example

View File

@@ -2,6 +2,24 @@
## Unreleased - 2020-xx-xx ## Unreleased - 2020-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
## 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 ## 0.3.0-beta.1 - 2020-08-19
* Use `.advance()` instead of `.split_to()`. * Use `.advance()` instead of `.split_to()`.
* Upgrade `tokio-util` to `0.3`. * Upgrade `tokio-util` to `0.3`.
@@ -11,32 +29,31 @@
* Add method on `Framed` to get a pinned reference to the underlying I/O. * Add method on `Framed` to get a pinned reference to the underlying I/O.
* Add method on `Framed` check emptiness of read buffer. * 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 * Use specific futures dependencies
## [0.2.0-alpha.4]
## 0.2.0-alpha.4
* Fix buffer remaining capacity calculation * Fix buffer remaining capacity calculation
## [0.2.0-alpha.3]
## 0.2.0-alpha.3
* Use tokio 0.2 * Use tokio 0.2
* Fix low/high watermark for write/read buffers * Fix low/high watermark for write/read buffers
## [0.2.0-alpha.2]
## 0.2.0-alpha.2
* Migrated to `std::future` * Migrated to `std::future`
## [0.1.2] - 2019-03-27
## 0.1.2 - 2019-03-27
* Added `Framed::map_io()` method. * Added `Framed::map_io()` method.
## [0.1.1] - 2019-03-06
## 0.1.1 - 2019-03-06
* Added `FramedParts::with_read_buffer()` method. * Added `FramedParts::with_read_buffer()` method.
## [0.1.0] - 2018-12-09
## 0.1.0 - 2018-12-09
* Move codec to separate crate * Move codec to separate crate

View File

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

View File

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

View File

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

View File

@@ -1,16 +1,16 @@
//! Utilities for encoding and decoding frames. //! Codec utilities for working with framed protocols.
//! //!
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and //! Contains adapters to go from streams of bytes, [`AsyncRead`] and
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`]. //! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
//! Framed streams are also known as `transports`. //! Framed streams are also known as `transports`.
//! //!
//! [`AsyncRead`]: AsyncRead
//! [`AsyncWrite`]: AsyncWrite
//! [`Sink`]: futures_sink::Sink //! [`Sink`]: futures_sink::Sink
//! [`Stream`]: futures_core::Stream //! [`Stream`]: futures_core::Stream
#![deny(rust_2018_idioms)] #![deny(rust_2018_idioms, nonstandard_style)]
#![warn(missing_docs)] #![warn(missing_docs)]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
mod bcodec; mod bcodec;
mod framed; mod framed;
@@ -18,5 +18,6 @@ mod framed;
pub use self::bcodec::BytesCodec; pub use self::bcodec::BytesCodec;
pub use self::framed::{Framed, FramedParts}; pub use self::framed::{Framed, FramedParts};
pub use tokio::io::{AsyncRead, AsyncWrite}; pub use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
pub use tokio_util::codec::{Decoder, Encoder}; pub use tokio_util::codec::{Decoder, Encoder};
pub use tokio_util::io::poll_read_buf;

View File

@@ -1,8 +1,11 @@
# Changes # Changes
## Unreleased ## 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 ## 2.0.0-alpha.4 - 2020-08-17
### Changed ### Changed

View File

@@ -1,8 +1,8 @@
[package] [package]
name = "actix-connect" name = "actix-connect"
version = "2.0.0-alpha.4" version = "2.0.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix connect - tcp connector service" description = "TCP connector service for Actix ecosystem."
keywords = ["network", "framework", "async", "futures"] keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs" homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git" repository = "https://github.com/actix/actix-net.git"
@@ -31,10 +31,11 @@ rustls = ["rust-tls", "tokio-rustls", "webpki"]
uri = ["http"] uri = ["http"]
[dependencies] [dependencies]
actix-service = "1.0.3" actix-service = "1.0.6"
actix-codec = "0.2.0" actix-codec = "0.3.0"
actix-utils = "1.0.6" actix-utils = "2.0.0"
actix-rt = "1.0.0" actix-rt = "1.1.1"
derive_more = "0.99.2" derive_more = "0.99.2"
either = "1.5.3" either = "1.5.3"
futures-util = { version = "0.3.4", default-features = false } futures-util = { version = "0.3.4", default-features = false }
@@ -44,14 +45,14 @@ trust-dns-proto = { version = "0.19", default-features = false, features = ["tok
trust-dns-resolver = { version = "0.19", default-features = false, features = ["tokio-runtime", "system-config"] } trust-dns-resolver = { version = "0.19", default-features = false, features = ["tokio-runtime", "system-config"] }
# openssl # openssl
open-ssl = { version="0.10", package = "openssl", optional = true } open-ssl = { package = "openssl", version = "0.10", optional = true }
tokio-openssl = { version = "0.4.0", optional = true } tokio-openssl = { version = "0.4.0", optional = true }
# rustls # rustls
rust-tls = { version = "0.18.0", package = "rustls", optional = true } rust-tls = { package = "rustls", version = "0.18.0", optional = true }
tokio-rustls = { version = "0.14.0", optional = true } tokio-rustls = { version = "0.14.0", optional = true }
webpki = { version = "0.21", optional = true } webpki = { version = "0.21", optional = true }
[dev-dependencies] [dev-dependencies]
bytes = "0.5.3" bytes = "0.5.3"
actix-testing = { version="1.0.0" } actix-testing = "1.0.0"

View File

@@ -43,7 +43,7 @@ pub struct Connect<T> {
} }
impl<T: Address> Connect<T> { impl<T: Address> Connect<T> {
/// Create `Connect` instance by spliting the string by ':' and convert the second part to u16 /// Create `Connect` instance by splitting the string by ':' and convert the second part to u16
pub fn new(req: T) -> Connect<T> { pub fn new(req: T) -> Connect<T> {
let (_, port) = parse(req.host()); let (_, port) = parse(req.host());
Connect { Connect {
@@ -53,7 +53,8 @@ impl<T: Address> Connect<T> {
} }
} }
/// Create new `Connect` instance from host and address. Connector skips name resolution stage for such connect messages. /// Create new `Connect` instance from host and address. Connector skips name resolution stage
/// for such connect messages.
pub fn with(req: T, addr: SocketAddr) -> Connect<T> { pub fn with(req: T, addr: SocketAddr) -> Connect<T> {
Connect { Connect {
req, req,
@@ -102,7 +103,7 @@ impl<T: Address> Connect<T> {
self.req.port().unwrap_or(self.port) self.req.port().unwrap_or(self.port)
} }
/// Preresolved addresses of the request. /// Pre-resolved addresses of the request.
pub fn addrs(&self) -> ConnectAddrsIter<'_> { pub fn addrs(&self) -> ConnectAddrsIter<'_> {
let inner = match self.addr { let inner = match self.addr {
None => Either::Left(None), None => Either::Left(None),
@@ -113,7 +114,7 @@ impl<T: Address> Connect<T> {
ConnectAddrsIter { inner } ConnectAddrsIter { inner }
} }
/// Takes preresolved addresses of the request. /// Takes pre-resolved addresses of the request.
pub fn take_addrs(&mut self) -> ConnectTakeAddrsIter { pub fn take_addrs(&mut self) -> ConnectTakeAddrsIter {
let inner = match self.addr.take() { let inner = match self.addr.take() {
None => Either::Left(None), None => Either::Left(None),
@@ -137,7 +138,7 @@ impl<T: Address> fmt::Display for Connect<T> {
} }
} }
/// Iterator over addresses in a [`Connect`](struct.Connect.html) request. /// Iterator over addresses in a [`Connect`] request.
#[derive(Clone)] #[derive(Clone)]
pub struct ConnectAddrsIter<'a> { pub struct ConnectAddrsIter<'a> {
inner: Either<Option<SocketAddr>, vec_deque::Iter<'a, SocketAddr>>, inner: Either<Option<SocketAddr>, vec_deque::Iter<'a, SocketAddr>>,
@@ -172,7 +173,7 @@ impl ExactSizeIterator for ConnectAddrsIter<'_> {}
impl FusedIterator for ConnectAddrsIter<'_> {} impl FusedIterator for ConnectAddrsIter<'_> {}
/// Owned iterator over addresses in a [`Connect`](struct.Connect.html) request. /// Owned iterator over addresses in a [`Connect`] request.
#[derive(Debug)] #[derive(Debug)]
pub struct ConnectTakeAddrsIter { pub struct ConnectTakeAddrsIter {
inner: Either<Option<SocketAddr>, vec_deque::IntoIter<SocketAddr>>, inner: Either<Option<SocketAddr>, vec_deque::IntoIter<SocketAddr>>,

View File

@@ -13,7 +13,7 @@ use futures_util::future::{err, ok, BoxFuture, Either, FutureExt, Ready};
use super::connect::{Address, Connect, Connection}; use super::connect::{Address, Connect, Connection};
use super::error::ConnectError; use super::error::ConnectError;
/// Tcp connector service factory /// TCP connector service factory
#[derive(Debug)] #[derive(Debug)]
pub struct TcpConnectorFactory<T>(PhantomData<T>); pub struct TcpConnectorFactory<T>(PhantomData<T>);
@@ -22,7 +22,7 @@ impl<T> TcpConnectorFactory<T> {
TcpConnectorFactory(PhantomData) TcpConnectorFactory(PhantomData)
} }
/// Create tcp connector service /// Create TCP connector service
pub fn service(&self) -> TcpConnector<T> { pub fn service(&self) -> TcpConnector<T> {
TcpConnector(PhantomData) TcpConnector(PhantomData)
} }
@@ -40,8 +40,7 @@ impl<T> Clone for TcpConnectorFactory<T> {
} }
} }
impl<T: Address> ServiceFactory for TcpConnectorFactory<T> { impl<T: Address> ServiceFactory<Connect<T>> for TcpConnectorFactory<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>; type Response = Connection<T, TcpStream>;
type Error = ConnectError; type Error = ConnectError;
type Config = (); type Config = ();
@@ -54,7 +53,7 @@ impl<T: Address> ServiceFactory for TcpConnectorFactory<T> {
} }
} }
/// Tcp connector service /// TCP connector service
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct TcpConnector<T>(PhantomData<T>); pub struct TcpConnector<T>(PhantomData<T>);
@@ -70,15 +69,13 @@ impl<T> Clone for TcpConnector<T> {
} }
} }
impl<T: Address> Service for TcpConnector<T> { impl<T: Address> Service<Connect<T>> for TcpConnector<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>; type Response = Connection<T, TcpStream>;
type Error = ConnectError; type Error = ConnectError;
#[allow(clippy::type_complexity)]
type Future = Either<TcpConnectorResponse<T>, Ready<Result<Self::Response, Self::Error>>>; type Future = Either<TcpConnectorResponse<T>, Ready<Result<Self::Response, Self::Error>>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future { fn call(&mut self, req: Connect<T>) -> Self::Future {
let port = req.port(); let port = req.port();
@@ -94,7 +91,7 @@ impl<T: Address> Service for TcpConnector<T> {
} }
#[doc(hidden)] #[doc(hidden)]
/// Tcp stream connector response future /// TCP stream connector response future
pub struct TcpConnectorResponse<T> { pub struct TcpConnectorResponse<T> {
req: Option<T>, req: Option<T>,
port: u16, port: u16,

View File

@@ -20,7 +20,7 @@ pub enum ConnectError {
#[display(fmt = "Connector received `Connect` method with unresolved host")] #[display(fmt = "Connector received `Connect` method with unresolved host")]
Unresolved, Unresolved,
/// Connection io error /// Connection IO error
#[display(fmt = "{}", _0)] #[display(fmt = "{}", _0)]
Io(io::Error), Io(io::Error),
} }

View File

@@ -1,12 +1,14 @@
//! Actix connect - tcp connector service //! TCP connector service for Actix ecosystem.
//! //!
//! ## Package feature //! ## Package feature
//! //!
//! * `openssl` - enables ssl support via `openssl` crate //! * `openssl` - enables TLS support via `openssl` crate
//! * `rustls` - enables ssl support via `rustls` crate //! * `rustls` - enables TLS support via `rustls` crate
#![deny(rust_2018_idioms, warnings)]
#![allow(clippy::type_complexity)] #![deny(rust_2018_idioms, nonstandard_style)]
#![recursion_limit = "128"] #![recursion_limit = "128"]
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
#[macro_use] #[macro_use]
extern crate log; extern crate log;
@@ -71,20 +73,20 @@ pub async fn start_default_resolver() -> Result<AsyncResolver, ConnectError> {
get_default_resolver().await get_default_resolver().await
} }
/// Create tcp connector service /// Create TCP connector service.
pub fn new_connector<T: Address + 'static>( pub fn new_connector<T: Address + 'static>(
resolver: AsyncResolver, resolver: AsyncResolver,
) -> impl Service<Request = Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError> ) -> impl Service<Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError> + Clone
+ Clone { {
pipeline(Resolver::new(resolver)).and_then(TcpConnector::new()) pipeline(Resolver::new(resolver)).and_then(TcpConnector::new())
} }
/// Create tcp connector service /// Create TCP connector service factory.
pub fn new_connector_factory<T: Address + 'static>( pub fn new_connector_factory<T: Address + 'static>(
resolver: AsyncResolver, resolver: AsyncResolver,
) -> impl ServiceFactory< ) -> impl ServiceFactory<
Connect<T>,
Config = (), Config = (),
Request = Connect<T>,
Response = Connection<T, TcpStream>, Response = Connection<T, TcpStream>,
Error = ConnectError, Error = ConnectError,
InitError = (), InitError = (),
@@ -92,17 +94,17 @@ pub fn new_connector_factory<T: Address + 'static>(
pipeline_factory(ResolverFactory::new(resolver)).and_then(TcpConnectorFactory::new()) pipeline_factory(ResolverFactory::new(resolver)).and_then(TcpConnectorFactory::new())
} }
/// Create connector service with default parameters /// Create connector service with default parameters.
pub fn default_connector<T: Address + 'static>( pub fn default_connector<T: Address + 'static>(
) -> impl Service<Request = Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError> ) -> impl Service<Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError> + Clone
+ Clone { {
pipeline(Resolver::default()).and_then(TcpConnector::new()) pipeline(Resolver::default()).and_then(TcpConnector::new())
} }
/// Create connector service factory with default parameters /// Create connector service factory with default parameters.
pub fn default_connector_factory<T: Address + 'static>() -> impl ServiceFactory< pub fn default_connector_factory<T: Address + 'static>() -> impl ServiceFactory<
Connect<T>,
Config = (), Config = (),
Request = Connect<T>,
Response = Connection<T, TcpStream>, Response = Connection<T, TcpStream>,
Error = ConnectError, Error = ConnectError,
InitError = (), InitError = (),

View File

@@ -54,8 +54,7 @@ impl<T> Clone for ResolverFactory<T> {
} }
} }
impl<T: Address> ServiceFactory for ResolverFactory<T> { impl<T: Address> ServiceFactory<Connect<T>> for ResolverFactory<T> {
type Request = Connect<T>;
type Response = Connect<T>; type Response = Connect<T>;
type Error = ConnectError; type Error = ConnectError;
type Config = (); type Config = ();
@@ -102,18 +101,16 @@ impl<T> Clone for Resolver<T> {
} }
} }
impl<T: Address> Service for Resolver<T> { impl<T: Address> Service<Connect<T>> for Resolver<T> {
type Request = Connect<T>;
type Response = Connect<T>; type Response = Connect<T>;
type Error = ConnectError; type Error = ConnectError;
#[allow(clippy::type_complexity)]
type Future = Either< type Future = Either<
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>, Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>,
Ready<Result<Connect<T>, Self::Error>>, Ready<Result<Connect<T>, Self::Error>>,
>; >;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, mut req: Connect<T>) -> Self::Future { fn call(&mut self, mut req: Connect<T>) -> Self::Future {
if req.addr.is_some() { if req.addr.is_some() {

View File

@@ -70,8 +70,7 @@ impl<T> Clone for ConnectServiceFactory<T> {
} }
} }
impl<T: Address> ServiceFactory for ConnectServiceFactory<T> { impl<T: Address> ServiceFactory<Connect<T>> for ConnectServiceFactory<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>; type Response = Connection<T, TcpStream>;
type Error = ConnectError; type Error = ConnectError;
type Config = (); type Config = ();
@@ -90,15 +89,12 @@ pub struct ConnectService<T> {
resolver: Resolver<T>, resolver: Resolver<T>,
} }
impl<T: Address> Service for ConnectService<T> { impl<T: Address> Service<Connect<T>> for ConnectService<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>; type Response = Connection<T, TcpStream>;
type Error = ConnectError; type Error = ConnectError;
type Future = ConnectServiceResponse<T>; type Future = ConnectServiceResponse<T>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future { fn call(&mut self, req: Connect<T>) -> Self::Future {
ConnectServiceResponse { ConnectServiceResponse {
@@ -109,11 +105,12 @@ impl<T: Address> Service for ConnectService<T> {
} }
enum ConnectState<T: Address> { enum ConnectState<T: Address> {
Resolve(<Resolver<T> as Service>::Future), Resolve(<Resolver<T> as Service<Connect<T>>>::Future),
Connect(<TcpConnector<T> as Service>::Future), Connect(<TcpConnector<T> as Service<Connect<T>>>::Future),
} }
impl<T: Address> ConnectState<T> { impl<T: Address> ConnectState<T> {
#[allow(clippy::type_complexity)]
fn poll( fn poll(
&mut self, &mut self,
cx: &mut Context<'_>, cx: &mut Context<'_>,
@@ -159,15 +156,12 @@ pub struct TcpConnectService<T> {
resolver: Resolver<T>, resolver: Resolver<T>,
} }
impl<T: Address + 'static> Service for TcpConnectService<T> { impl<T: Address + 'static> Service<Connect<T>> for TcpConnectService<T> {
type Request = Connect<T>;
type Response = TcpStream; type Response = TcpStream;
type Error = ConnectError; type Error = ConnectError;
type Future = TcpConnectServiceResponse<T>; type Future = TcpConnectServiceResponse<T>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future { fn call(&mut self, req: Connect<T>) -> Self::Future {
TcpConnectServiceResponse { TcpConnectServiceResponse {
@@ -178,8 +172,8 @@ impl<T: Address + 'static> Service for TcpConnectService<T> {
} }
enum TcpConnectState<T: Address> { enum TcpConnectState<T: Address> {
Resolve(<Resolver<T> as Service>::Future), Resolve(<Resolver<T> as Service<Connect<T>>>::Future),
Connect(<TcpConnector<T> as Service>::Future), Connect(<TcpConnector<T> as Service<Connect<T>>>::Future),
} }
impl<T: Address> TcpConnectState<T> { impl<T: Address> TcpConnectState<T> {

View File

@@ -17,7 +17,7 @@ use crate::{
Address, Connect, ConnectError, ConnectService, ConnectServiceFactory, Connection, Address, Connect, ConnectError, ConnectService, ConnectServiceFactory, Connection,
}; };
/// Openssl connector factory /// OpenSSL connector factory
pub struct OpensslConnector<T, U> { pub struct OpensslConnector<T, U> {
connector: SslConnector, connector: SslConnector,
_t: PhantomData<(T, U)>, _t: PhantomData<(T, U)>,
@@ -97,11 +97,10 @@ where
type Request = Connection<T, U>; type Request = Connection<T, U>;
type Response = Connection<T, SslStream<U>>; type Response = Connection<T, SslStream<U>>;
type Error = io::Error; type Error = io::Error;
#[allow(clippy::type_complexity)]
type Future = Either<ConnectAsyncExt<T, U>, Ready<Result<Self::Response, Self::Error>>>; type Future = Either<ConnectAsyncExt<T, U>, Ready<Result<Self::Response, Self::Error>>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, stream: Connection<T, U>) -> Self::Future { fn call(&mut self, stream: Connection<T, U>) -> Self::Future {
trace!("SSL Handshake start for: {:?}", stream.host()); trace!("SSL Handshake start for: {:?}", stream.host());
@@ -164,7 +163,7 @@ impl<T> OpensslConnectServiceFactory<T> {
} }
} }
/// Construct new connect service with custom dns resolver /// Construct new connect service with custom DNS resolver
pub fn with_resolver(connector: SslConnector, resolver: AsyncResolver) -> Self { pub fn with_resolver(connector: SslConnector, resolver: AsyncResolver) -> Self {
OpensslConnectServiceFactory { OpensslConnectServiceFactory {
tcp: ConnectServiceFactory::with_resolver(resolver), tcp: ConnectServiceFactory::with_resolver(resolver),
@@ -172,7 +171,7 @@ impl<T> OpensslConnectServiceFactory<T> {
} }
} }
/// Construct openssl connect service /// Construct OpenSSL connect service
pub fn service(&self) -> OpensslConnectService<T> { pub fn service(&self) -> OpensslConnectService<T> {
OpensslConnectService { OpensslConnectService {
tcp: self.tcp.service(), tcp: self.tcp.service(),
@@ -219,9 +218,7 @@ impl<T: Address + 'static> Service for OpensslConnectService<T> {
type Error = ConnectError; type Error = ConnectError;
type Future = OpensslConnectServiceResponse<T>; type Future = OpensslConnectServiceResponse<T>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, req: Connect<T>) -> Self::Future { fn call(&mut self, req: Connect<T>) -> Self::Future {
OpensslConnectServiceResponse { OpensslConnectServiceResponse {

View File

@@ -96,9 +96,7 @@ where
type Error = std::io::Error; type Error = std::io::Error;
type Future = ConnectAsyncExt<T, U>; type Future = ConnectAsyncExt<T, U>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, stream: Connection<T, U>) -> Self::Future { fn call(&mut self, stream: Connection<T, U>) -> Self::Future {
trace!("SSL Handshake start for: {:?}", stream.host()); trace!("SSL Handshake start for: {:?}", stream.host());

View File

@@ -88,9 +88,9 @@ async fn test_new_service() {
assert_eq!(con.peer_addr().unwrap(), srv.addr()); assert_eq!(con.peer_addr().unwrap(), srv.addr());
} }
#[cfg(feature = "openssl")] #[cfg(all(feature = "openssl", feature = "uri"))]
#[actix_rt::test] #[actix_rt::test]
async fn test_uri() { async fn test_openssl_uri() {
use std::convert::TryFrom; use std::convert::TryFrom;
let srv = TestServer::with(|| { let srv = TestServer::with(|| {
@@ -107,7 +107,7 @@ async fn test_uri() {
assert_eq!(con.peer_addr().unwrap(), srv.addr()); assert_eq!(con.peer_addr().unwrap(), srv.addr());
} }
#[cfg(feature = "rustls")] #[cfg(all(feature = "rustls", feature = "uri"))]
#[actix_rt::test] #[actix_rt::test]
async fn test_rustls_uri() { async fn test_rustls_uri() {
use std::convert::TryFrom; use std::convert::TryFrom;

View File

@@ -1,3 +0,0 @@
# actix-ioframe
**This crate has been deprecated and removed.**

View File

@@ -1,5 +1,9 @@
# CHANGES # CHANGES
## 0.1.3 - 2020-12-3
* Add `actix-reexport` feature
## 0.1.2 - 2020-05-18 ## 0.1.2 - 2020-05-18
### Changed ### Changed

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "actix-macros" name = "actix-macros"
version = "0.1.2" version = "0.1.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix runtime macros" description = "Actix runtime macros"
repository = "https://github.com/actix/actix-net" repository = "https://github.com/actix/actix-net"
@@ -16,6 +16,9 @@ proc-macro = true
quote = "1.0.3" quote = "1.0.3"
syn = { version = "^1", features = ["full"] } syn = { version = "^1", features = ["full"] }
[features]
actix-reexport = []
[dev-dependencies] [dev-dependencies]
actix-rt = "1.0" actix-rt = "1.0"

View File

@@ -1,5 +1,8 @@
//! Macros for use with Tokio //! Macros for use with Tokio
extern crate proc_macro;
#![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")]
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote; use quote::quote;
@@ -33,14 +36,25 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
sig.asyncness = None; sig.asyncness = None;
(quote! { if cfg!(feature = "actix-reexport") {
#(#attrs)* (quote! {
#vis #sig { #(#attrs)*
actix_rt::System::new(stringify!(#name)) #vis #sig {
.block_on(async move { #body }) actix::System::new(stringify!(#name))
} .block_on(async move { #body })
}) }
.into() })
.into()
} else {
(quote! {
#(#attrs)*
#vis #sig {
actix_rt::System::new(stringify!(#name))
.block_on(async move { #body })
}
})
.into()
}
} }
/// Marks async test function to be executed by actix runtime. /// Marks async test function to be executed by actix runtime.

View File

@@ -1,5 +1,27 @@
# Changes # Changes
## 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 ## [1.1.1] - 2020-04-30
### Fixed ### Fixed

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,11 @@
//! A runtime implementation that runs everything on the current thread. //! Tokio-based single-thread async runtime for the Actix ecosystem.
#![deny(rust_2018_idioms, warnings)]
#![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity)] #![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 #[cfg(not(test))] // Work around for rust-lang/rust#62127
pub use actix_macros::{main, test}; pub use actix_macros::{main, test};
@@ -15,23 +20,17 @@ pub use self::builder::{Builder, SystemRunner};
pub use self::runtime::Runtime; pub use self::runtime::Runtime;
pub use self::system::System; pub use self::system::System;
#[doc(hidden)]
pub use actix_threadpool as blocking;
/// Spawns a future on the current arbiter. /// Spawns a future on the current arbiter.
/// ///
/// # Panics /// # Panics
/// ///
/// This function panics if actix system is not running. /// This function panics if actix system is not running.
#[inline]
pub fn spawn<F>(f: F) pub fn spawn<F>(f: F)
where where
F: futures_util::future::Future<Output = ()> + 'static, F: Future<Output = ()> + 'static,
{ {
if !System::is_set() { Arbiter::spawn(f)
panic!("System is not running");
}
Arbiter::spawn(f);
} }
/// Asynchronous signal handling /// Asynchronous signal handling
@@ -60,7 +59,7 @@ pub mod net {
/// Utilities for tracking time. /// Utilities for tracking time.
pub mod time { pub mod time {
pub use tokio::time::Instant; 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::{interval, interval_at, Interval};
pub use tokio::time::{sleep, sleep_until, Sleep};
pub use tokio::time::{timeout, Timeout}; pub use tokio::time::{timeout, Timeout};
} }

View File

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

View File

@@ -3,7 +3,7 @@ use std::future::Future;
use std::io; use std::io;
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
use futures_channel::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use tokio::task::LocalSet; use tokio::task::LocalSet;
use crate::arbiter::{Arbiter, SystemCommand}; use crate::arbiter::{Arbiter, SystemCommand};
@@ -57,10 +57,57 @@ impl System {
Self::builder().name(name).build() Self::builder().name(name).build()
} }
#[allow(clippy::new_ret_no_self)] /// Create new system using provided tokio `LocalSet`.
/// Create new system using provided tokio Handle.
/// ///
/// This method panics if it can not spawn system arbiter /// This method panics if it can not spawn system arbiter
///
/// Note: This method uses provided `LocalSet` to create a `System` future only.
/// All the [`Arbiter`]s will be started in separate threads using their own tokio `Runtime`s.
/// It means that using this method currently it is impossible to make `actix-rt` work in the
/// alternative `tokio` `Runtime`s (e.g. provided by [`tokio_compat`]).
///
/// [`tokio_compat`]: https://crates.io/crates/tokio-compat
///
/// # Examples
///
/// ```rust,ignore
/// use tokio::{runtime::Runtime, task::LocalSet};
/// use actix_rt::System;
/// use futures_util::future::try_join_all;
///
/// async fn run_application() {
/// let first_task = tokio::spawn(async {
/// // ...
/// # println!("One task");
/// # Ok::<(),()>(())
/// });
///
/// let second_task = tokio::spawn(async {
/// // ...
/// # println!("Another task");
/// # Ok::<(),()>(())
/// });
///
/// try_join_all(vec![first_task, second_task])
/// .await
/// .expect("Some of the futures finished unexpectedly");
/// }
///
///
/// let runtime = tokio::runtime::Builder::new_multi_thread()
/// .worker_threads(2)
/// .enable_all()
/// .build()
/// .unwrap();
///
///
/// let actix_system_task = LocalSet::new();
/// let sys = System::run_in_tokio("actix-main-system", &actix_system_task);
/// actix_system_task.spawn_local(sys);
///
/// let rest_operations = run_application();
/// runtime.block_on(actix_system_task.run_until(rest_operations));
/// ```
pub fn run_in_tokio<T: Into<String>>( pub fn run_in_tokio<T: Into<String>>(
name: T, name: T,
local: &LocalSet, local: &LocalSet,
@@ -71,6 +118,75 @@ impl System {
.run_nonblocking() .run_nonblocking()
} }
/// Consume the provided tokio Runtime and start the `System` in it.
/// This method will create a `LocalSet` object and occupy the current thread
/// for the created `System` exclusively. All the other asynchronous tasks that
/// should be executed as well must be aggregated into one future, provided as the last
/// argument to this method.
///
/// Note: This method uses provided `Runtime` to create a `System` future only.
/// All the [`Arbiter`]s will be started in separate threads using their own tokio `Runtime`s.
/// It means that using this method currently it is impossible to make `actix-rt` work in the
/// alternative `tokio` `Runtime`s (e.g. provided by `tokio_compat`).
///
/// [`tokio_compat`]: https://crates.io/crates/tokio-compat
///
/// # Arguments
///
/// - `name`: Name of the System
/// - `runtime`: A tokio Runtime to run the system in.
/// - `rest_operations`: A future to be executed in the runtime along with the System.
///
/// # Examples
///
/// ```rust,ignore
/// use tokio::runtime::Runtime;
/// use actix_rt::System;
/// use futures_util::future::try_join_all;
///
/// async fn run_application() {
/// let first_task = tokio::spawn(async {
/// // ...
/// # println!("One task");
/// # Ok::<(),()>(())
/// });
///
/// let second_task = tokio::spawn(async {
/// // ...
/// # println!("Another task");
/// # Ok::<(),()>(())
/// });
///
/// try_join_all(vec![first_task, second_task])
/// .await
/// .expect("Some of the futures finished unexpectedly");
/// }
///
///
/// let runtime = tokio::runtime::Builder::new_multi_thread()
/// .worker_threads(2)
/// .enable_all()
/// .build()
/// .unwrap();
///
/// let rest_operations = run_application();
/// System::attach_to_tokio("actix-main-system", runtime, rest_operations);
/// ```
pub fn attach_to_tokio<Fut, R>(
name: impl Into<String>,
runtime: tokio::runtime::Runtime,
rest_operations: Fut,
) -> R
where
Fut: std::future::Future<Output = R>,
{
let actix_system_task = LocalSet::new();
let sys = System::run_in_tokio(name.into(), &actix_system_task);
actix_system_task.spawn_local(sys);
runtime.block_on(actix_system_task.run_until(rest_operations))
}
/// Get current running system. /// Get current running system.
pub fn current() -> System { pub fn current() -> System {
CURRENT.with(|cell| match *cell.borrow() { CURRENT.with(|cell| match *cell.borrow() {
@@ -115,7 +231,7 @@ impl System {
/// Stop the system with a particular exit code. /// Stop the system with a particular exit code.
pub fn stop_with_code(&self, code: i32) { pub fn stop_with_code(&self, code: i32) {
let _ = self.sys.unbounded_send(SystemCommand::Exit(code)); let _ = self.sys.send(SystemCommand::Exit(code));
} }
pub(crate) fn sys(&self) -> &UnboundedSender<SystemCommand> { pub(crate) fn sys(&self) -> &UnboundedSender<SystemCommand> {
@@ -138,7 +254,7 @@ impl System {
/// Function `f` get called within tokio runtime context. /// Function `f` get called within tokio runtime context.
pub fn run<F>(f: F) -> io::Result<()> pub fn run<F>(f: F) -> io::Result<()>
where where
F: FnOnce() + 'static, F: FnOnce(),
{ {
Self::builder().run(f) Self::builder().run(f)
} }

View File

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

View File

@@ -1,216 +1,144 @@
# Changes # Changes
## Unreleased ## Unreleased - 2020-xx-xx
### Changed
* workers must be greater than 0 ## 2.0.0-beta.1 - 2020-12-28
* 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]
## [1.0.3] - 2020-05-19 [#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
### Changed
## 1.0.4 - 2020-09-12
* Update actix-codec to 0.3.0.
* Workers must be greater than 0. [#167]
[#167]: https://github.com/actix/actix-net/pull/167
## 1.0.3 - 2020-05-19
* Replace deprecated `net2` crate with `socket2` [#140] * Replace deprecated `net2` crate with `socket2` [#140]
[#140]: https://github.com/actix/actix-net/pull/140 [#140]: https://github.com/actix/actix-net/pull/140
## [1.0.2] - 2020-02-26
### Fixed
## 1.0.2 - 2020-02-26
* Avoid error by calling `reregister()` on Windows [#103] * Avoid error by calling `reregister()` on Windows [#103]
[#103]: https://github.com/actix/actix-net/pull/103 [#103]: https://github.com/actix/actix-net/pull/103
## [1.0.1] - 2019-12-29
### Changed
## 1.0.1 - 2019-12-29
* Rename `.start()` method to `.run()` * Rename `.start()` method to `.run()`
## [1.0.0] - 2019-12-11
### Changed
## 1.0.0 - 2019-12-11
* Use actix-net releases * Use actix-net releases
## [1.0.0-alpha.4] - 2019-12-08 ## 1.0.0-alpha.4 - 2019-12-08
### Changed
* Use actix-service 1.0.0-alpha.4 * Use actix-service 1.0.0-alpha.4
## [1.0.0-alpha.3] - 2019-12-07
### Changed
## 1.0.0-alpha.3 - 2019-12-07
* Migrate to tokio 0.2 * Migrate to tokio 0.2
### Fixed
* Fix compilation on non-unix platforms * Fix compilation on non-unix platforms
* Better handling server configuration * Better handling server configuration
## [1.0.0-alpha.2] - 2019-12-02 ## 1.0.0-alpha.2 - 2019-12-02
### Changed
* Simplify server service (remove actix-server-config) * Simplify server service (remove actix-server-config)
* Allow to wait on `Server` until server stops * Allow to wait on `Server` until server stops
## [0.8.0-alpha.1] - 2019-11-22 ## 0.8.0-alpha.1 - 2019-11-22
### Changed
* Migrate to `std::future` * Migrate to `std::future`
## [0.7.0] - 2019-10-04 ## 0.7.0 - 2019-10-04
### Changed
* Update `rustls` to 0.16 * Update `rustls` to 0.16
* Minimum required Rust version upped to 1.37.0 * Minimum required Rust version upped to 1.37.0
## [0.6.1] - 2019-09-25 ## 0.6.1 - 2019-09-25
### Added
* Add UDS listening support to `ServerBuilder` * Add UDS listening support to `ServerBuilder`
## [0.6.0] - 2019-07-18 ## 0.6.0 - 2019-07-18
### Added
* Support Unix domain sockets #3 * Support Unix domain sockets #3
## [0.5.1] - 2019-05-18 ## 0.5.1 - 2019-05-18
### Changed
* ServerBuilder::shutdown_timeout() accepts u64 * ServerBuilder::shutdown_timeout() accepts u64
## [0.5.0] - 2019-05-12 ## 0.5.0 - 2019-05-12
### Added
* Add `Debug` impl for `SslError` * Add `Debug` impl for `SslError`
* Derive debug for `Server` and `ServerCommand` * Derive debug for `Server` and `ServerCommand`
### Changed
* Upgrade to actix-service 0.4 * Upgrade to actix-service 0.4
## [0.4.3] - 2019-04-16 ## 0.4.3 - 2019-04-16
### Added
* Re-export `IoStream` trait * Re-export `IoStream` trait
* Depend on `ssl` and `rust-tls` features from actix-server-config
### Changed
* Deppend on `ssl` and `rust-tls` features from actix-server-config
## [0.4.2] - 2019-03-30 ## 0.4.2 - 2019-03-30
### Fixed
* Fix SIGINT force shutdown * Fix SIGINT force shutdown
## [0.4.1] - 2019-03-14 ## 0.4.1 - 2019-03-14
### Added
* `SystemRuntime::on_start()` - allow to run future before server service initialization * `SystemRuntime::on_start()` - allow to run future before server service initialization
## [0.4.0] - 2019-03-12 ## 0.4.0 - 2019-03-12
### Changed
* Use `ServerConfig` for service factory * Use `ServerConfig` for service factory
* Wrap tcp socket to `Io` type * Wrap tcp socket to `Io` type
* Upgrade actix-service * Upgrade actix-service
## [0.3.1] - 2019-03-04 ## 0.3.1 - 2019-03-04
### Added
* Add `ServerBuilder::maxconnrate` sets the maximum per-worker number of concurrent connections * Add `ServerBuilder::maxconnrate` sets the maximum per-worker number of concurrent connections
* Add helper ssl error `SslError` * Add helper ssl error `SslError`
### Changed
* Rename `StreamServiceFactory` to `ServiceFactory` * Rename `StreamServiceFactory` to `ServiceFactory`
* Deprecate `StreamServiceFactory` * Deprecate `StreamServiceFactory`
## [0.3.0] - 2019-03-02 ## 0.3.0 - 2019-03-02
### Changed
* Use new `NewService` trait * Use new `NewService` trait
## [0.2.1] - 2019-02-09 ## 0.2.1 - 2019-02-09
### Changed
* Drop service response * Drop service response
## [0.2.0] - 2019-02-01 ## 0.2.0 - 2019-02-01
### Changed
* Migrate to actix-service 0.2 * Migrate to actix-service 0.2
* Updated rustls dependency * Updated rustls dependency
## [0.1.3] - 2018-12-21 ## 0.1.3 - 2018-12-21
### Fixed
* Fix max concurrent connections handling * Fix max concurrent connections handling
## [0.1.2] - 2018-12-12 ## 0.1.2 - 2018-12-12
### Changed
* rename ServiceConfig::rt() to ServiceConfig::apply() * rename ServiceConfig::rt() to ServiceConfig::apply()
### Fixed
* Fix back-pressure for concurrent ssl handshakes * Fix back-pressure for concurrent ssl handshakes
## [0.1.1] - 2018-12-11 ## 0.1.1 - 2018-12-11
* Fix signal handling on windows * Fix signal handling on windows
## [0.1.0] - 2018-12-09 ## 0.1.0 - 2018-12-09
* Move server to separate crate * Move server to separate crate

View File

@@ -1,8 +1,11 @@
[package] [package]
name = "actix-server" name = "actix-server"
version = "1.0.3" version = "2.0.0-beta.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = [
description = "Actix server - General purpose tcp server" "Nikolay Kim <fafhrd91@gmail.com>",
"fakeshadow <24548779@qq.com>",
]
description = "General purpose TCP server built for the Actix ecosystem"
keywords = ["network", "framework", "async", "futures"] keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs" homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git" repository = "https://github.com/actix/actix-net.git"
@@ -11,7 +14,6 @@ categories = ["network-programming", "asynchronous"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
exclude = [".gitignore", ".cargo/config"] exclude = [".gitignore", ".cargo/config"]
edition = "2018" edition = "2018"
workspace = ".."
[lib] [lib]
name = "actix_server" name = "actix_server"
@@ -21,24 +23,21 @@ path = "src/lib.rs"
default = [] default = []
[dependencies] [dependencies]
actix-service = "1.0.1" actix-codec = "0.4.0-beta.1"
actix-rt = "1.0.0" actix-rt = "2.0.0-beta.1"
actix-codec = "0.2.0" actix-service = "2.0.0-beta.1"
actix-utils = "1.0.4" actix-utils = "3.0.0-beta.1"
futures-core = { version = "0.3.7", default-features = false }
log = "0.4" log = "0.4"
num_cpus = "1.11" mio = { version = "0.7.6", features = ["os-poll", "net"] }
mio = "0.6.19" num_cpus = "1.13"
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" 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] [dev-dependencies]
bytes = "0.5" actix-testing = "2.0.0-beta.1"
env_logger = "0.7" bytes = "1"
actix-testing = "1.0.0" env_logger = "0.8"
futures-util = { version = "0.3.7", default-features = false, features = ["sink"] }
tokio = { version = "1", features = ["io-util"] }

View File

@@ -0,0 +1,88 @@
//! Simple composite-service TCP echo server.
//!
//! Using the following command:
//!
//! ```sh
//! nc 127.0.0.1 8080
//! ```
//!
//! Start typing. When you press enter the typed line will be echoed back. The server will log
//! the length of each line it echos and the total size of data sent when the connection is closed.
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
use std::{env, io};
use actix_rt::net::TcpStream;
use actix_server::Server;
use actix_service::pipeline_factory;
use bytes::BytesMut;
use futures_util::future::ok;
use log::{error, info};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[actix_rt::main]
async fn main() -> io::Result<()> {
env::set_var("RUST_LOG", "actix=trace,basic=trace");
env_logger::init();
let count = Arc::new(AtomicUsize::new(0));
let addr = ("127.0.0.1", 8080);
info!("starting server on port: {}", &addr.0);
// Bind socket address and start worker(s). By default, the server uses the number of available
// logical CPU cores as the worker count. For this reason, the closure passed to bind needs
// to return a service *factory*; so it can be created once per worker.
Server::build()
.bind("echo", addr, move || {
let count = Arc::clone(&count);
let num2 = Arc::clone(&count);
pipeline_factory(move |mut stream: TcpStream| {
let count = Arc::clone(&count);
async move {
let num = count.fetch_add(1, Ordering::SeqCst);
let num = num + 1;
let mut size = 0;
let mut buf = BytesMut::new();
loop {
match stream.read_buf(&mut buf).await {
// end of stream; bail from loop
Ok(0) => break,
// more bytes to process
Ok(bytes_read) => {
info!("[{}] read {} bytes", num, bytes_read);
stream.write_all(&buf[size..]).await.unwrap();
size += bytes_read;
}
// stream error; bail from loop with error
Err(err) => {
error!("Stream Error: {:?}", err);
return Err(());
}
}
}
// send data down service pipeline
Ok((buf.freeze(), size))
}
})
.map_err(|err| error!("Service Error: {:?}", err))
.and_then(move |(_, size)| {
let num = num2.load(Ordering::SeqCst);
info!("[{}] total bytes read: {}", num, size);
ok(size)
})
})?
.workers(1)
.run()
.await
}

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
//! General purpose tcp server //! General purpose TCP server.
#![deny(rust_2018_idioms, warnings)]
#![allow(clippy::type_complexity)] #![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")]
mod accept; mod accept;
mod builder; mod builder;
@@ -9,6 +11,7 @@ mod server;
mod service; mod service;
mod signals; mod signals;
mod socket; mod socket;
mod waker_queue;
mod worker; mod worker;
pub use self::builder::ServerBuilder; pub use self::builder::ServerBuilder;
@@ -19,11 +22,25 @@ pub use self::service::ServiceFactory;
#[doc(hidden)] #[doc(hidden)]
pub use self::socket::FromStream; pub use self::socket::FromStream;
/// Socket id token use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
/// Socket ID token
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct Token(usize); pub(crate) struct Token(usize);
impl Default for Token {
fn default() -> Self {
Self::new()
}
}
impl Token { impl Token {
fn new() -> Self {
Self(0)
}
pub(crate) fn next(&mut self) -> Token { pub(crate) fn next(&mut self) -> Token {
let token = Token(self.0); let token = Token(self.0);
self.0 += 1; self.0 += 1;
@@ -35,3 +52,90 @@ impl Token {
pub fn new() -> ServerBuilder { pub fn new() -> ServerBuilder {
ServerBuilder::default() 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,9 +3,8 @@ use std::io;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use futures_channel::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use futures_channel::oneshot; use tokio::sync::oneshot;
use futures_util::FutureExt;
use crate::builder::ServerBuilder; use crate::builder::ServerBuilder;
use crate::signals::Signal; use crate::signals::Signal;
@@ -42,11 +41,11 @@ impl Server {
} }
pub(crate) fn signal(&self, sig: Signal) { pub(crate) fn signal(&self, sig: Signal) {
let _ = self.0.unbounded_send(ServerCommand::Signal(sig)); let _ = self.0.send(ServerCommand::Signal(sig));
} }
pub(crate) fn worker_faulted(&self, idx: usize) { pub(crate) fn worker_faulted(&self, idx: usize) {
let _ = self.0.unbounded_send(ServerCommand::WorkerFaulted(idx)); let _ = self.0.send(ServerCommand::WorkerFaulted(idx));
} }
/// Pause accepting incoming connections /// Pause accepting incoming connections
@@ -55,15 +54,19 @@ impl Server {
/// All opened connection remains active. /// All opened connection remains active.
pub fn pause(&self) -> impl Future<Output = ()> { pub fn pause(&self) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let _ = self.0.unbounded_send(ServerCommand::Pause(tx)); let _ = self.0.send(ServerCommand::Pause(tx));
rx.map(|_| ()) async {
let _ = rx.await;
}
} }
/// Resume accepting incoming connections /// Resume accepting incoming connections
pub fn resume(&self) -> impl Future<Output = ()> { pub fn resume(&self) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let _ = self.0.unbounded_send(ServerCommand::Resume(tx)); let _ = self.0.send(ServerCommand::Resume(tx));
rx.map(|_| ()) async {
let _ = rx.await;
}
} }
/// Stop incoming connection processing, stop all workers and exit. /// Stop incoming connection processing, stop all workers and exit.
@@ -71,11 +74,13 @@ impl Server {
/// If server starts with `spawn()` method, then spawned thread get terminated. /// If server starts with `spawn()` method, then spawned thread get terminated.
pub fn stop(&self, graceful: bool) -> impl Future<Output = ()> { pub fn stop(&self, graceful: bool) -> impl Future<Output = ()> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let _ = self.0.unbounded_send(ServerCommand::Stop { let _ = self.0.send(ServerCommand::Stop {
graceful, graceful,
completion: Some(tx), completion: Some(tx),
}); });
rx.map(|_| ()) async {
let _ = rx.await;
}
} }
} }
@@ -93,7 +98,7 @@ impl Future for Server {
if this.1.is_none() { if this.1.is_none() {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
if this.0.unbounded_send(ServerCommand::Notify(tx)).is_err() { if this.0.send(ServerCommand::Notify(tx)).is_err() {
return Poll::Ready(Ok(())); return Poll::Ready(Ok(()));
} }
this.1 = Some(rx); this.1 = Some(rx);
@@ -101,8 +106,7 @@ impl Future for Server {
match Pin::new(this.1.as_mut().unwrap()).poll(cx) { match Pin::new(this.1.as_mut().unwrap()).poll(cx) {
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
Poll::Ready(Ok(_)) => Poll::Ready(Ok(())), Poll::Ready(_) => Poll::Ready(Ok(())),
Poll::Ready(Err(_)) => Poll::Ready(Ok(())),
} }
} }
} }

View File

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

View File

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

View File

@@ -1,135 +1,91 @@
use std::{fmt, io, net}; 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 actix_codec::{AsyncRead, AsyncWrite};
use actix_rt::net::TcpStream; use actix_rt::net::TcpStream;
use mio::event::Source;
use mio::net::TcpStream as MioTcpStream;
use mio::{Interest, Registry, Token};
pub(crate) enum StdListener { #[cfg(windows)]
Tcp(net::TcpListener), use std::os::windows::io::{FromRawSocket, IntoRawSocket};
#[cfg(all(unix))] #[cfg(unix)]
Uds(std::os::unix::net::UnixListener), 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 SocketAddr { impl MioListener {
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 { pub(crate) fn local_addr(&self) -> SocketAddr {
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 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"),
),
}
}
}
#[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 { match *self {
SocketListener::Tcp(ref lst) => lst MioListener::Tcp(ref lst) => SocketAddr::Tcp(lst.local_addr().unwrap()),
.accept_std() #[cfg(unix)]
.map(|(stream, addr)| Some((StdStream::Tcp(stream), SocketAddr::Tcp(addr)))), MioListener::Uds(ref lst) => SocketAddr::Uds(lst.local_addr().unwrap()),
#[cfg(all(unix))] }
SocketListener::Uds(ref lst) => lst.accept_std().map(|res| { }
res.map(|(stream, addr)| (StdStream::Uds(stream), SocketAddr::Uds(addr)))
}), 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)))),
} }
} }
} }
impl mio::Evented for SocketListener { impl Source for MioListener {
fn register( fn register(
&self, &mut self,
poll: &mio::Poll, registry: &Registry,
token: mio::Token, token: Token,
interest: mio::Ready, interests: Interest,
opts: mio::PollOpt,
) -> io::Result<()> { ) -> io::Result<()> {
match *self { match *self {
SocketListener::Tcp(ref lst) => lst.register(poll, token, interest, opts), MioListener::Tcp(ref mut lst) => lst.register(registry, token, interests),
#[cfg(all(unix))] #[cfg(unix)]
SocketListener::Uds(ref lst) => lst.register(poll, token, interest, opts), MioListener::Uds(ref mut lst) => lst.register(registry, token, interests),
} }
} }
fn reregister( fn reregister(
&self, &mut self,
poll: &mio::Poll, registry: &Registry,
token: mio::Token, token: Token,
interest: mio::Ready, interests: Interest,
opts: mio::PollOpt,
) -> io::Result<()> { ) -> io::Result<()> {
match *self { match *self {
SocketListener::Tcp(ref lst) => lst.reregister(poll, token, interest, opts), MioListener::Tcp(ref mut lst) => lst.reregister(registry, token, interests),
#[cfg(all(unix))] #[cfg(unix)]
SocketListener::Uds(ref lst) => lst.reregister(poll, token, interest, opts), MioListener::Uds(ref mut lst) => lst.reregister(registry, token, interests),
} }
} }
fn deregister(&self, poll: &mio::Poll) -> io::Result<()> {
fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
match *self { match *self {
SocketListener::Tcp(ref lst) => lst.deregister(poll), MioListener::Tcp(ref mut lst) => lst.deregister(registry),
#[cfg(all(unix))] #[cfg(unix)]
SocketListener::Uds(ref lst) => { MioListener::Uds(ref mut lst) => {
let res = lst.deregister(poll); let res = lst.deregister(registry);
// cleanup file path // cleanup file path
if let Ok(addr) = lst.local_addr() { if let Ok(addr) = lst.local_addr() {
@@ -143,28 +99,156 @@ impl mio::Evented for SocketListener {
} }
} }
pub trait FromStream: AsyncRead + AsyncWrite + Sized { impl From<StdTcpListener> for MioListener {
fn from_stdstream(sock: StdStream) -> io::Result<Self>; fn from(lst: StdTcpListener) -> Self {
MioListener::Tcp(MioTcpListener::from_std(lst))
}
} }
impl FromStream for TcpStream { #[cfg(unix)]
fn from_stdstream(sock: StdStream) -> io::Result<Self> { impl From<StdUnixListener> for MioListener {
match sock { fn from(lst: StdUnixListener) -> Self {
StdStream::Tcp(stream) => TcpStream::from_std(stream), 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))] #[cfg(all(unix))]
StdStream::Uds(_) => { 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> {
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(_) => {
panic!("Should not happen, bug in server impl"); panic!("Should not happen, bug in server impl");
} }
} }
} }
} }
#[cfg(all(unix))] // FIXME: This is a workaround and we need an efficient way to convert between mio and tokio stream
impl FromStream for actix_rt::net::UnixStream { #[cfg(windows)]
fn from_stdstream(sock: StdStream) -> io::Result<Self> { impl FromStream for TcpStream {
fn from_mio(sock: MioStream) -> io::Result<Self> {
match sock { match sock {
StdStream::Tcp(_) => panic!("Should not happen, bug in server impl"), MioStream::Tcp(mio) => {
StdStream::Uds(stream) => actix_rt::net::UnixStream::from_std(stream), 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"));
} }
} }
} }

View File

@@ -0,0 +1,89 @@
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::pin::Pin;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::time; use std::time::Duration;
use actix_rt::time::{delay_until, Delay, Instant}; use actix_rt::time::{sleep_until, Instant, Sleep};
use actix_rt::{spawn, Arbiter}; use actix_rt::{spawn, Arbiter};
use actix_utils::counter::Counter; use actix_utils::counter::Counter;
use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}; use futures_core::future::LocalBoxFuture;
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 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::service::{BoxedServerService, InternalServiceFactory, ServerMessage}; use crate::socket::{MioStream, SocketAddr};
use crate::socket::{SocketAddr, StdStream}; use crate::waker_queue::{WakerInterest, WakerQueue};
use crate::Token; use crate::{join_all, Token};
pub(crate) struct WorkerCommand(Conn); pub(crate) struct WorkerCommand(Conn);
@@ -29,7 +29,7 @@ pub(crate) struct StopCommand {
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct Conn { pub(crate) struct Conn {
pub io: StdStream, pub io: MioStream,
pub token: Token, pub token: Token,
pub peer: Option<SocketAddr>, pub peer: Option<SocketAddr>,
} }
@@ -46,31 +46,33 @@ pub fn max_concurrent_connections(num: usize) {
MAX_CONNS.store(num, Ordering::Relaxed); MAX_CONNS.store(num, Ordering::Relaxed);
} }
pub(crate) fn num_connections() -> usize {
MAX_CONNS_COUNTER.with(|conns| conns.total())
}
thread_local! { thread_local! {
static MAX_CONNS_COUNTER: Counter = static MAX_CONNS_COUNTER: Counter =
Counter::new(MAX_CONNS.load(Ordering::Relaxed)); 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)] #[derive(Clone)]
pub(crate) struct WorkerClient { pub(crate) struct WorkerHandle {
pub idx: usize, pub idx: usize,
tx1: UnboundedSender<WorkerCommand>, tx1: UnboundedSender<WorkerCommand>,
tx2: UnboundedSender<StopCommand>, tx2: UnboundedSender<StopCommand>,
avail: WorkerAvailability, avail: WorkerAvailability,
} }
impl WorkerClient { impl WorkerHandle {
pub fn new( pub fn new(
idx: usize, idx: usize,
tx1: UnboundedSender<WorkerCommand>, tx1: UnboundedSender<WorkerCommand>,
tx2: UnboundedSender<StopCommand>, tx2: UnboundedSender<StopCommand>,
avail: WorkerAvailability, avail: WorkerAvailability,
) -> Self { ) -> Self {
WorkerClient { WorkerHandle {
idx, idx,
tx1, tx1,
tx2, tx2,
@@ -79,9 +81,7 @@ impl WorkerClient {
} }
pub fn send(&self, msg: Conn) -> Result<(), Conn> { pub fn send(&self, msg: Conn) -> Result<(), Conn> {
self.tx1 self.tx1.send(WorkerCommand(msg)).map_err(|msg| msg.0 .0)
.unbounded_send(WorkerCommand(msg))
.map_err(|msg| msg.into_inner().0)
} }
pub fn available(&self) -> bool { pub fn available(&self) -> bool {
@@ -90,21 +90,21 @@ impl WorkerClient {
pub fn stop(&self, graceful: bool) -> oneshot::Receiver<bool> { pub fn stop(&self, graceful: bool) -> oneshot::Receiver<bool> {
let (result, rx) = oneshot::channel(); let (result, rx) = oneshot::channel();
let _ = self.tx2.unbounded_send(StopCommand { graceful, result }); let _ = self.tx2.send(StopCommand { graceful, result });
rx rx
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct WorkerAvailability { pub(crate) struct WorkerAvailability {
notify: AcceptNotify, waker: WakerQueue,
available: Arc<AtomicBool>, available: Arc<AtomicBool>,
} }
impl WorkerAvailability { impl WorkerAvailability {
pub fn new(notify: AcceptNotify) -> Self { pub fn new(waker: WakerQueue) -> Self {
WorkerAvailability { WorkerAvailability {
notify, waker,
available: Arc::new(AtomicBool::new(false)), available: Arc::new(AtomicBool::new(false)),
} }
} }
@@ -115,8 +115,9 @@ impl WorkerAvailability {
pub fn set(&self, val: bool) { pub fn set(&self, val: bool) {
let old = self.available.swap(val, Ordering::Release); let old = self.available.swap(val, Ordering::Release);
// notify the accept on switched to available.
if !old && val { if !old && val {
self.notify.notify() self.waker.wake(WakerInterest::WorkerAvailable);
} }
} }
} }
@@ -133,7 +134,7 @@ pub(crate) struct Worker {
conns: Counter, conns: Counter,
factories: Vec<Box<dyn InternalServiceFactory>>, factories: Vec<Box<dyn InternalServiceFactory>>,
state: WorkerState, state: WorkerState,
shutdown_timeout: time::Duration, shutdown_timeout: Duration,
} }
struct WorkerService { struct WorkerService {
@@ -164,63 +165,65 @@ impl Worker {
idx: usize, idx: usize,
factories: Vec<Box<dyn InternalServiceFactory>>, factories: Vec<Box<dyn InternalServiceFactory>>,
availability: WorkerAvailability, availability: WorkerAvailability,
shutdown_timeout: time::Duration, shutdown_timeout: Duration,
) -> WorkerClient { ) -> WorkerHandle {
let (tx1, rx) = unbounded(); let (tx1, rx) = unbounded_channel();
let (tx2, rx2) = unbounded(); let (tx2, rx2) = unbounded_channel();
let avail = availability.clone(); let avail = availability.clone();
Arbiter::new().send( // every worker runs in it's own arbiter.
async move { Arbiter::new().send(Box::pin(async move {
availability.set(false); availability.set(false);
let mut wrk = MAX_CONNS_COUNTER.with(move |conns| Worker { let mut wrk = MAX_CONNS_COUNTER.with(move |conns| Worker {
rx, rx,
rx2, rx2,
availability, availability,
factories, factories,
shutdown_timeout, shutdown_timeout,
services: Vec::new(), services: Vec::new(),
conns: conns.clone(), conns: conns.clone(),
state: WorkerState::Unavailable(Vec::new()), state: WorkerState::Unavailable,
}); });
let mut fut: Vec<MapOk<LocalBoxFuture<'static, _>, _>> = Vec::new(); let fut = wrk
for (idx, factory) in wrk.factories.iter().enumerate() { .factories
fut.push(factory.create().map_ok(move |r| { .iter()
r.into_iter() .enumerate()
.map(|(t, s): (Token, _)| (idx, t, s)) .map(|(idx, factory)| {
.collect::<Vec<_>>() let fut = factory.create();
})); async move {
} fut.await.map(|r| {
r.into_iter().map(|(t, s)| (idx, t, s)).collect::<Vec<_>>()
})
}
})
.collect::<Vec<_>>();
spawn(async move { spawn(async move {
let res = join_all(fut).await; let res: Result<Vec<_>, _> = join_all(fut).await.into_iter().collect();
let res: Result<Vec<_>, _> = res.into_iter().collect(); match res {
match res { Ok(services) => {
Ok(services) => { for item in services {
for item in services { for (factory, token, service) in item {
for (factory, token, service) in item { assert_eq!(token.0, wrk.services.len());
assert_eq!(token.0, wrk.services.len()); wrk.services.push(WorkerService {
wrk.services.push(WorkerService { factory,
factory, service,
service, status: WorkerServiceStatus::Unavailable,
status: WorkerServiceStatus::Unavailable, });
});
}
} }
} }
Err(e) => {
error!("Can not start worker: {:?}", e);
Arbiter::current().stop();
}
} }
wrk.await Err(e) => {
}); error!("Can not start worker: {:?}", e);
} Arbiter::current().stop();
.boxed(), }
); }
wrk.await
});
}));
WorkerClient::new(idx, tx1, tx2, avail) WorkerHandle::new(idx, tx1, tx2, avail)
} }
fn shutdown(&mut self, force: bool) { fn shutdown(&mut self, force: bool) {
@@ -228,23 +231,12 @@ impl Worker {
self.services.iter_mut().for_each(|srv| { self.services.iter_mut().for_each(|srv| {
if srv.status == WorkerServiceStatus::Available { if srv.status == WorkerServiceStatus::Available {
srv.status = WorkerServiceStatus::Stopped; srv.status = WorkerServiceStatus::Stopped;
actix_rt::spawn(
srv.service
.call((None, ServerMessage::ForceShutdown))
.map(|_| ()),
);
} }
}); });
} else { } else {
let timeout = self.shutdown_timeout;
self.services.iter_mut().for_each(move |srv| { self.services.iter_mut().for_each(move |srv| {
if srv.status == WorkerServiceStatus::Available { if srv.status == WorkerServiceStatus::Available {
srv.status = WorkerServiceStatus::Stopping; srv.status = WorkerServiceStatus::Stopping;
actix_rt::spawn(
srv.service
.call((None, ServerMessage::Shutdown(timeout)))
.map(|_| ()),
);
} }
}); });
} }
@@ -253,7 +245,7 @@ impl Worker {
fn check_readiness(&mut self, cx: &mut Context<'_>) -> Result<bool, (Token, usize)> { fn check_readiness(&mut self, cx: &mut Context<'_>) -> Result<bool, (Token, usize)> {
let mut ready = self.conns.available(cx); let mut ready = self.conns.available(cx);
let mut failed = None; let mut failed = None;
for (idx, srv) in &mut self.services.iter_mut().enumerate() { for (idx, srv) in self.services.iter_mut().enumerate() {
if srv.status == WorkerServiceStatus::Available if srv.status == WorkerServiceStatus::Available
|| srv.status == WorkerServiceStatus::Unavailable || srv.status == WorkerServiceStatus::Unavailable
{ {
@@ -299,15 +291,15 @@ impl Worker {
enum WorkerState { enum WorkerState {
Available, Available,
Unavailable(Vec<Conn>), Unavailable,
Restarting( Restarting(
usize, usize,
Token, Token,
Pin<Box<dyn Future<Output = Result<Vec<(Token, BoxedServerService)>, ()>>>>, LocalBoxFuture<'static, Result<Vec<(Token, BoxedServerService)>, ()>>,
), ),
Shutdown( Shutdown(
Pin<Box<Delay>>, Pin<Box<Sleep>>,
Pin<Box<Delay>>, Pin<Box<Sleep>>,
Option<oneshot::Sender<bool>>, Option<oneshot::Sender<bool>>,
), ),
} }
@@ -315,12 +307,10 @@ enum WorkerState {
impl Future for Worker { impl Future for Worker {
type Output = (); type Output = ();
// FIXME: remove this attribute
#[allow(clippy::never_loop)]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// `StopWorker` message handler // `StopWorker` message handler
if let Poll::Ready(Some(StopCommand { graceful, result })) = if let Poll::Ready(Some(StopCommand { graceful, result })) =
Pin::new(&mut self.rx2).poll_next(cx) Pin::new(&mut self.rx2).poll_recv(cx)
{ {
self.availability.set(false); self.availability.set(false);
let num = num_connections(); let num = num_connections();
@@ -334,8 +324,8 @@ impl Future for Worker {
if num != 0 { if num != 0 {
info!("Graceful worker shutdown, {} connections", num); info!("Graceful worker shutdown, {} connections", num);
self.state = WorkerState::Shutdown( self.state = WorkerState::Shutdown(
Box::pin(delay_until(Instant::now() + time::Duration::from_secs(1))), Box::pin(sleep_until(Instant::now() + Duration::from_secs(1))),
Box::pin(delay_until(Instant::now() + self.shutdown_timeout)), Box::pin(sleep_until(Instant::now() + self.shutdown_timeout)),
Some(result), Some(result),
); );
} else { } else {
@@ -351,53 +341,35 @@ impl Future for Worker {
} }
match self.state { match self.state {
WorkerState::Unavailable(ref mut conns) => { WorkerState::Unavailable => match self.check_readiness(cx) {
let conn = conns.pop(); Ok(true) => {
match self.check_readiness(cx) { self.state = WorkerState::Available;
Ok(true) => { self.availability.set(true);
// process requests from wait queue self.poll(cx)
if let Some(conn) = conn {
let guard = self.conns.get();
let _ = self.services[conn.token.0]
.service
.call((Some(guard), ServerMessage::Connect(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) => { WorkerState::Restarting(idx, token, ref mut fut) => {
match Pin::new(fut).poll(cx) { match fut.as_mut().poll(cx) {
Poll::Ready(Ok(item)) => { Poll::Ready(Ok(item)) => {
for (token, service) in item { // only interest in the first item?
if let Some((token, service)) = item.into_iter().next() {
trace!( trace!(
"Service {:?} has been restarted", "Service {:?} has been restarted",
self.factories[idx].name(token) self.factories[idx].name(token)
); );
self.services[token.0].created(service); self.services[token.0].created(service);
self.state = WorkerState::Unavailable(Vec::new()); self.state = WorkerState::Unavailable;
return self.poll(cx); return self.poll(cx);
} }
} }
@@ -407,9 +379,7 @@ impl Future for Worker {
self.factories[idx].name(token) self.factories[idx].name(token)
); );
} }
Poll::Pending => { Poll::Pending => return Poll::Pending,
return Poll::Pending;
}
} }
self.poll(cx) self.poll(cx)
} }
@@ -422,71 +392,56 @@ impl Future for Worker {
} }
// check graceful timeout // check graceful timeout
match t2.as_mut().poll(cx) { if Pin::new(t2).poll(cx).is_ready() {
Poll::Pending => (), let _ = tx.take().unwrap().send(false);
Poll::Ready(_) => { self.shutdown(true);
let _ = tx.take().unwrap().send(false); Arbiter::current().stop();
self.shutdown(true); return Poll::Ready(());
Arbiter::current().stop();
return Poll::Ready(());
}
} }
// sleep for 1 second and then check again // sleep for 1 second and then check again
match t1.as_mut().poll(cx) { if t1.as_mut().poll(cx).is_ready() {
Poll::Pending => (), *t1 = Box::pin(sleep_until(Instant::now() + Duration::from_secs(1)));
Poll::Ready(_) => { let _ = t1.as_mut().poll(cx);
*t1 = Box::pin(delay_until(
Instant::now() + time::Duration::from_secs(1),
));
let _ = t1.as_mut().poll(cx);
}
} }
Poll::Pending Poll::Pending
} }
WorkerState::Available => { // actively poll stream and handle worker command
loop { WorkerState::Available => loop {
match Pin::new(&mut self.rx).poll_next(cx) { match self.check_readiness(cx) {
// handle incoming io stream Ok(true) => (),
Poll::Ready(Some(WorkerCommand(msg))) => { Ok(false) => {
match self.check_readiness(cx) { trace!("Worker is unavailable");
Ok(true) => { self.availability.set(false);
let guard = self.conns.get(); self.state = WorkerState::Unavailable;
let _ = self.services[msg.token.0] return self.poll(cx);
.service }
.call((Some(guard), ServerMessage::Connect(msg.io))); Err((token, idx)) => {
continue; trace!(
} "Service {:?} failed, restarting",
Ok(false) => { self.factories[idx].name(token)
trace!("Worker is unavailable"); );
self.availability.set(false); self.availability.set(false);
self.state = WorkerState::Unavailable(vec![msg]); self.services[token.0].status = WorkerServiceStatus::Restarting;
} self.state =
Err((token, idx)) => { WorkerState::Restarting(idx, token, self.factories[idx].create());
trace!( return self.poll(cx);
"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,14 +5,13 @@ use std::{net, thread, time};
use actix_server::Server; use actix_server::Server;
use actix_service::fn_service; use actix_service::fn_service;
use futures_util::future::{lazy, ok}; use futures_util::future::{lazy, ok};
use socket2::{Domain, Protocol, Socket, Type};
fn unused_addr() -> net::SocketAddr { fn unused_addr() -> net::SocketAddr {
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap(); let socket = mio::net::TcpSocket::new_v4().unwrap();
socket.bind(&addr.into()).unwrap(); socket.bind(addr).unwrap();
socket.set_reuse_address(true).unwrap(); socket.set_reuseaddr(true).unwrap();
let tcp = socket.into_tcp_listener(); let tcp = socket.listen(32).unwrap();
tcp.local_addr().unwrap() tcp.local_addr().unwrap()
} }
@@ -23,12 +22,14 @@ fn test_bind() {
let h = thread::spawn(move || { let h = thread::spawn(move || {
let sys = actix_rt::System::new("test"); let sys = actix_rt::System::new("test");
let srv = Server::build() let srv = sys.block_on(lazy(|_| {
.workers(1) Server::build()
.disable_signals() .workers(1)
.bind("test", addr, move || fn_service(|_| ok::<_, ()>(()))) .disable_signals()
.unwrap() .bind("test", addr, move || fn_service(|_| ok::<_, ()>(())))
.start(); .unwrap()
.start()
}));
let _ = tx.send((srv, actix_rt::System::current())); let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run(); let _ = sys.run();
}); });
@@ -48,13 +49,15 @@ fn test_listen() {
let h = thread::spawn(move || { let h = thread::spawn(move || {
let sys = actix_rt::System::new("test"); let sys = actix_rt::System::new("test");
let lst = net::TcpListener::bind(addr).unwrap(); let lst = net::TcpListener::bind(addr).unwrap();
Server::build() sys.block_on(async {
.disable_signals() Server::build()
.workers(1) .disable_signals()
.listen("test", lst, move || fn_service(|_| ok::<_, ()>(()))) .workers(1)
.unwrap() .listen("test", lst, move || fn_service(|_| ok::<_, ()>(())))
.start(); .unwrap()
let _ = tx.send(actix_rt::System::current()); .start();
let _ = tx.send(actix_rt::System::current());
});
let _ = sys.run(); let _ = sys.run();
}); });
let sys = rx.recv().unwrap(); let sys = rx.recv().unwrap();
@@ -79,22 +82,25 @@ fn test_start() {
let h = thread::spawn(move || { let h = thread::spawn(move || {
let sys = actix_rt::System::new("test"); let sys = actix_rt::System::new("test");
let srv: Server = Server::build() let srv = sys.block_on(lazy(|_| {
.backlog(100) Server::build()
.disable_signals() .backlog(100)
.bind("test", addr, move || { .disable_signals()
fn_service(|io: TcpStream| async move { .bind("test", addr, move || {
let mut f = Framed::new(io, BytesCodec); fn_service(|io: TcpStream| async move {
f.send(Bytes::from_static(b"test")).await.unwrap(); let mut f = Framed::new(io, BytesCodec);
Ok::<_, ()>(()) f.send(Bytes::from_static(b"test")).await.unwrap();
Ok::<_, ()>(())
})
}) })
}) .unwrap()
.unwrap() .start()
.start(); }));
let _ = tx.send((srv, actix_rt::System::current())); let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run(); let _ = sys.run();
}); });
let (srv, sys) = rx.recv().unwrap(); let (srv, sys) = rx.recv().unwrap();
let mut buf = [1u8; 4]; let mut buf = [1u8; 4];
@@ -145,28 +151,30 @@ fn test_configure() {
let h = thread::spawn(move || { let h = thread::spawn(move || {
let num = num2.clone(); let num = num2.clone();
let sys = actix_rt::System::new("test"); let sys = actix_rt::System::new("test");
let srv = Server::build() let srv = sys.block_on(lazy(|_| {
.disable_signals() Server::build()
.configure(move |cfg| { .disable_signals()
let num = num.clone(); .configure(move |cfg| {
let lst = net::TcpListener::bind(addr3).unwrap(); let num = num.clone();
cfg.bind("addr1", addr1) let lst = net::TcpListener::bind(addr3).unwrap();
.unwrap() cfg.bind("addr1", addr1)
.bind("addr2", addr2) .unwrap()
.unwrap() .bind("addr2", addr2)
.listen("addr3", lst) .unwrap()
.apply(move |rt| { .listen("addr3", lst)
let num = num.clone(); .apply(move |rt| {
rt.service("addr1", fn_service(|_| ok::<_, ()>(()))); let num = num.clone();
rt.service("addr3", fn_service(|_| ok::<_, ()>(()))); rt.service("addr1", fn_service(|_| ok::<_, ()>(())));
rt.on_start(lazy(move |_| { rt.service("addr3", fn_service(|_| ok::<_, ()>(())));
let _ = num.fetch_add(1, Relaxed); rt.on_start(lazy(move |_| {
})) let _ = num.fetch_add(1, Relaxed);
}) }))
}) })
.unwrap() })
.workers(1) .unwrap()
.start(); .workers(1)
.start()
}));
let _ = tx.send((srv, actix_rt::System::current())); let _ = tx.send((srv, actix_rt::System::current()));
let _ = sys.run(); let _ = sys.run();
}); });

View File

@@ -3,11 +3,29 @@
## Unreleased - 2020-xx-xx ## Unreleased - 2020-xx-xx
## 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
## 1.0.6 - 2020-08-09 ## 1.0.6 - 2020-08-09
### Fixed ### 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 ## [1.0.5] - 2020-01-16

View File

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

View File

@@ -2,6 +2,6 @@
> Service trait and combinators for representing asynchronous request/response operations. > Service trait and combinators for representing asynchronous request/response operations.
See documentation for detailed explanations these components: [https://docs.rs/actix-service](docs). See documentation for detailed explanations these components: [https://docs.rs/actix-service][docs].
[docs]: https://docs.rs/actix-service [docs]: https://docs.rs/actix-service

View File

@@ -1,332 +0,0 @@
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

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

View File

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

View File

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

View File

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

70
actix-service/src/ext.rs Normal file
View File

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

View File

@@ -1,36 +1,47 @@
//! See [`Service`](trait.Service.html) docs for information on this crate's foundational trait. //! See [`Service`] docs for information on this crate's foundational trait.
#![deny(rust_2018_idioms, warnings)] #![no_std]
#![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity)] #![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::cell::RefCell; extern crate alloc;
use std::future::Future;
use std::rc::Rc; use alloc::{boxed::Box, rc::Rc, sync::Arc};
use std::sync::Arc; use core::{
use std::task::{self, Context, Poll}; cell::RefCell,
future::Future,
task::{self, Context, Poll},
};
mod and_then; mod and_then;
mod and_then_apply_fn;
mod apply; mod apply;
mod apply_cfg; mod apply_cfg;
pub mod boxed; pub mod boxed;
mod ext;
mod fn_service; mod fn_service;
mod map; mod map;
mod map_config; mod map_config;
mod map_err; mod map_err;
mod map_init_err; mod map_init_err;
mod pipeline; mod pipeline;
mod ready;
mod then; mod then;
mod transform; mod transform;
mod transform_err; mod transform_err;
pub use self::apply::{apply_fn, apply_fn_factory}; pub use self::apply::{apply_fn, apply_fn_factory};
pub use self::apply_cfg::{apply_cfg, apply_cfg_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::fn_service::{fn_factory, fn_factory_with_config, fn_service};
pub use self::map_config::{map_config, unit_config}; pub use self::map_config::{map_config, unit_config};
pub use self::pipeline::{pipeline, pipeline_factory, Pipeline, PipelineFactory}; pub use self::pipeline::{pipeline, pipeline_factory, Pipeline, PipelineFactory};
pub use self::transform::{apply, Transform}; pub use self::transform::{apply, Transform};
#[allow(unused_imports)]
use self::ready::{err, ok, ready, Ready};
/// An asynchronous operation from `Request` to a `Response`. /// An asynchronous operation from `Request` to a `Response`.
/// ///
/// The `Service` trait models a request/response interaction, receiving requests and returning /// The `Service` trait models a request/response interaction, receiving requests and returning
@@ -65,19 +76,16 @@ pub use self::transform::{apply, Transform};
/// ``` /// ```
/// ///
/// Sometimes it is not necessary to implement the Service trait. For example, the above service /// Sometimes it is not necessary to implement the Service trait. For example, the above service
/// could be rewritten as a simple function and passed to [fn_service](fn.fn_service.html). /// could be rewritten as a simple function and passed to [fn_service](fn_service()).
/// ///
/// ```rust,ignore /// ```rust,ignore
/// async fn my_service(req: u8) -> Result<u64, MyError>; /// async fn my_service(req: u8) -> Result<u64, MyError>;
/// ``` /// ```
pub trait Service { pub trait Service<Req> {
/// Requests handled by the service.
type Request;
/// Responses given by the service. /// Responses given by the service.
type Response; type Response;
/// Errors produced by the service. /// Errors produced by the service when polling readiness or executing call.
type Error; type Error;
/// The future response value. /// The future response value.
@@ -107,40 +115,7 @@ pub trait Service {
/// ///
/// Calling `call` without calling `poll_ready` is permitted. The /// Calling `call` without calling `poll_ready` is permitted. The
/// implementation must be resilient to this fact. /// implementation must be resilient to this fact.
fn call(&mut self, req: Self::Request) -> Self::Future; fn call(&mut self, req: Req) -> 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. /// Factory for creating `Service`s.
@@ -152,10 +127,7 @@ pub trait Service {
/// requests on that new TCP stream. /// requests on that new TCP stream.
/// ///
/// `Config` is a service factory configuration type. /// `Config` is a service factory configuration type.
pub trait ServiceFactory { pub trait ServiceFactory<Req> {
/// Requests handled by the created services.
type Request;
/// Responses given by the created services. /// Responses given by the created services.
type Response; type Response;
@@ -166,11 +138,7 @@ pub trait ServiceFactory {
type Config; type Config;
/// The kind of `Service` created by this factory. /// The kind of `Service` created by this factory.
type Service: Service< type Service: Service<Req, Response = Self::Response, Error = Self::Error>;
Request = Self::Request,
Response = Self::Response,
Error = Self::Error,
>;
/// Errors potentially raised while building a service. /// Errors potentially raised while building a service.
type InitError; type InitError;
@@ -180,41 +148,12 @@ pub trait ServiceFactory {
/// Create and return a new service asynchronously. /// Create and return a new service asynchronously.
fn new_service(&self, cfg: Self::Config) -> Self::Future; 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> Service for &'a mut S impl<'a, S, Req> Service<Req> for &'a mut S
where where
S: Service + 'a, S: Service<Req> + 'a,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Future = S::Future; type Future = S::Future;
@@ -223,16 +162,15 @@ where
(**self).poll_ready(ctx) (**self).poll_ready(ctx)
} }
fn call(&mut self, request: Self::Request) -> S::Future { fn call(&mut self, request: Req) -> S::Future {
(**self).call(request) (**self).call(request)
} }
} }
impl<S> Service for Box<S> impl<S, Req> Service<Req> for Box<S>
where where
S: Service + ?Sized, S: Service<Req> + ?Sized,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Future = S::Future; type Future = S::Future;
@@ -241,16 +179,15 @@ where
(**self).poll_ready(ctx) (**self).poll_ready(ctx)
} }
fn call(&mut self, request: Self::Request) -> S::Future { fn call(&mut self, request: Req) -> S::Future {
(**self).call(request) (**self).call(request)
} }
} }
impl<S> Service for RefCell<S> impl<S, Req> Service<Req> for RefCell<S>
where where
S: Service, S: Service<Req>,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Future = S::Future; type Future = S::Future;
@@ -259,16 +196,15 @@ where
self.borrow_mut().poll_ready(ctx) self.borrow_mut().poll_ready(ctx)
} }
fn call(&mut self, request: Self::Request) -> S::Future { fn call(&mut self, request: Req) -> S::Future {
self.borrow_mut().call(request) self.borrow_mut().call(request)
} }
} }
impl<S> Service for Rc<RefCell<S>> impl<S, Req> Service<Req> for Rc<RefCell<S>>
where where
S: Service, S: Service<Req>,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Future = S::Future; type Future = S::Future;
@@ -277,16 +213,15 @@ where
self.borrow_mut().poll_ready(ctx) self.borrow_mut().poll_ready(ctx)
} }
fn call(&mut self, request: Self::Request) -> S::Future { fn call(&mut self, request: Req) -> S::Future {
(&mut (**self).borrow_mut()).call(request) (&mut (**self).borrow_mut()).call(request)
} }
} }
impl<S> ServiceFactory for Rc<S> impl<S, Req> ServiceFactory<Req> for Rc<S>
where where
S: ServiceFactory, S: ServiceFactory<Req>,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Config = S::Config; type Config = S::Config;
@@ -299,11 +234,10 @@ where
} }
} }
impl<S> ServiceFactory for Arc<S> impl<S, Req> ServiceFactory<Req> for Arc<S>
where where
S: ServiceFactory, S: ServiceFactory<Req>,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Config = S::Config; type Config = S::Config;
@@ -317,52 +251,52 @@ where
} }
/// Trait for types that can be converted to a `Service` /// Trait for types that can be converted to a `Service`
pub trait IntoService<T> pub trait IntoService<S, Req>
where where
T: Service, S: Service<Req>,
{ {
/// Convert to a `Service` /// Convert to a `Service`
fn into_service(self) -> T; fn into_service(self) -> S;
} }
/// Trait for types that can be converted to a `ServiceFactory` /// Trait for types that can be converted to a `ServiceFactory`
pub trait IntoServiceFactory<T> pub trait IntoServiceFactory<SF, Req>
where where
T: ServiceFactory, SF: ServiceFactory<Req>,
{ {
/// Convert `Self` to a `ServiceFactory` /// Convert `Self` to a `ServiceFactory`
fn into_factory(self) -> T; fn into_factory(self) -> SF;
} }
impl<T> IntoService<T> for T impl<S, Req> IntoService<S, Req> for S
where where
T: Service, S: Service<Req>,
{ {
fn into_service(self) -> T { fn into_service(self) -> S {
self self
} }
} }
impl<T> IntoServiceFactory<T> for T impl<SF, Req> IntoServiceFactory<SF, Req> for SF
where where
T: ServiceFactory, SF: ServiceFactory<Req>,
{ {
fn into_factory(self) -> T { fn into_factory(self) -> SF {
self self
} }
} }
/// Convert object of type `T` to a service `S` /// Convert object of type `U` to a service `S`
pub fn into_service<T, S>(tp: T) -> S pub fn into_service<I, S, Req>(tp: I) -> S
where where
S: Service, I: IntoService<S, Req>,
T: IntoService<S>, S: Service<Req>,
{ {
tp.into_service() tp.into_service()
} }
pub mod dev { pub mod dev {
pub use crate::apply::{Apply, ApplyServiceFactory}; pub use crate::apply::{Apply, ApplyFactory};
pub use crate::fn_service::{ pub use crate::fn_service::{
FnService, FnServiceConfig, FnServiceFactory, FnServiceNoConfig, FnService, FnServiceConfig, FnServiceFactory, FnServiceNoConfig,
}; };
@@ -373,3 +307,27 @@ pub mod dev {
pub use crate::transform::ApplyTransform; pub use crate::transform::ApplyTransform;
pub use crate::transform_err::TransformMapInitErr; 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,25 +1,29 @@
use std::future::Future; use core::{
use std::marker::PhantomData; future::Future,
use std::pin::Pin; marker::PhantomData,
use std::task::{Context, Poll}; pin::Pin,
task::{Context, Poll},
};
use pin_project_lite::pin_project;
use super::{Service, ServiceFactory}; use super::{Service, ServiceFactory};
/// Service for the `map` combinator, changing the type of a service's response. /// Service for the `map` combinator, changing the type of a service's response.
/// ///
/// This is created by the `ServiceExt::map` method. /// This is created by the `ServiceExt::map` method.
pub struct Map<A, F, Response> { pub struct Map<A, F, Req, Res> {
service: A, service: A,
f: F, f: F,
_t: PhantomData<Response>, _t: PhantomData<(Req, Res)>,
} }
impl<A, F, Response> Map<A, F, Response> { impl<A, F, Req, Res> Map<A, F, Req, Res> {
/// Create new `Map` combinator /// Create new `Map` combinator
pub(crate) fn new(service: A, f: F) -> Self pub(crate) fn new(service: A, f: F) -> Self
where where
A: Service, A: Service<Req>,
F: FnMut(A::Response) -> Response, F: FnMut(A::Response) -> Res,
{ {
Self { Self {
service, service,
@@ -29,7 +33,7 @@ impl<A, F, Response> Map<A, F, Response> {
} }
} }
impl<A, F, Response> Clone for Map<A, F, Response> impl<A, F, Req, Res> Clone for Map<A, F, Req, Res>
where where
A: Clone, A: Clone,
F: Clone, F: Clone,
@@ -43,52 +47,50 @@ where
} }
} }
impl<A, F, Response> Service for Map<A, F, Response> impl<A, F, Req, Res> Service<Req> for Map<A, F, Req, Res>
where where
A: Service, A: Service<Req>,
F: FnMut(A::Response) -> Response + Clone, F: FnMut(A::Response) -> Res + Clone,
{ {
type Request = A::Request; type Response = Res;
type Response = Response;
type Error = A::Error; type Error = A::Error;
type Future = MapFuture<A, F, Response>; type Future = MapFuture<A, F, Req, Res>;
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { crate::forward_ready!(service);
self.service.poll_ready(ctx)
}
fn call(&mut self, req: A::Request) -> Self::Future { fn call(&mut self, req: Req) -> Self::Future {
MapFuture::new(self.service.call(req), self.f.clone()) MapFuture::new(self.service.call(req), self.f.clone())
} }
} }
#[pin_project::pin_project] pin_project! {
pub struct MapFuture<A, F, Response> pub struct MapFuture<A, F, Req, Res>
where where
A: Service, A: Service<Req>,
F: FnMut(A::Response) -> Response, F: FnMut(A::Response) -> Res,
{ {
f: F, f: F,
#[pin] #[pin]
fut: A::Future, fut: A::Future,
}
} }
impl<A, F, Response> MapFuture<A, F, Response> impl<A, F, Req, Res> MapFuture<A, F, Req, Res>
where where
A: Service, A: Service<Req>,
F: FnMut(A::Response) -> Response, F: FnMut(A::Response) -> Res,
{ {
fn new(fut: A::Future, f: F) -> Self { fn new(fut: A::Future, f: F) -> Self {
MapFuture { f, fut } MapFuture { f, fut }
} }
} }
impl<A, F, Response> Future for MapFuture<A, F, Response> impl<A, F, Req, Res> Future for MapFuture<A, F, Req, Res>
where where
A: Service, A: Service<Req>,
F: FnMut(A::Response) -> Response, F: FnMut(A::Response) -> Res,
{ {
type Output = Result<Response, A::Error>; type Output = Result<Res, A::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let this = self.project();
@@ -102,17 +104,17 @@ where
} }
/// `MapNewService` new service combinator /// `MapNewService` new service combinator
pub struct MapServiceFactory<A, F, Res> { pub struct MapServiceFactory<A, F, Req, Res> {
a: A, a: A,
f: F, f: F,
r: PhantomData<Res>, r: PhantomData<(Res, Req)>,
} }
impl<A, F, Res> MapServiceFactory<A, F, Res> { impl<A, F, Req, Res> MapServiceFactory<A, F, Req, Res> {
/// Create new `Map` new service instance /// Create new `Map` new service instance
pub(crate) fn new(a: A, f: F) -> Self pub(crate) fn new(a: A, f: F) -> Self
where where
A: ServiceFactory, A: ServiceFactory<Req>,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Res,
{ {
Self { Self {
@@ -123,7 +125,7 @@ impl<A, F, Res> MapServiceFactory<A, F, Res> {
} }
} }
impl<A, F, Res> Clone for MapServiceFactory<A, F, Res> impl<A, F, Req, Res> Clone for MapServiceFactory<A, F, Req, Res>
where where
A: Clone, A: Clone,
F: Clone, F: Clone,
@@ -137,39 +139,39 @@ where
} }
} }
impl<A, F, Res> ServiceFactory for MapServiceFactory<A, F, Res> impl<A, F, Req, Res> ServiceFactory<Req> for MapServiceFactory<A, F, Req, Res>
where where
A: ServiceFactory, A: ServiceFactory<Req>,
F: FnMut(A::Response) -> Res + Clone, F: FnMut(A::Response) -> Res + Clone,
{ {
type Request = A::Request;
type Response = Res; type Response = Res;
type Error = A::Error; type Error = A::Error;
type Config = A::Config; type Config = A::Config;
type Service = Map<A::Service, F, Res>; type Service = Map<A::Service, F, Req, Res>;
type InitError = A::InitError; type InitError = A::InitError;
type Future = MapServiceFuture<A, F, Res>; type Future = MapServiceFuture<A, F, Req, Res>;
fn new_service(&self, cfg: A::Config) -> Self::Future { fn new_service(&self, cfg: A::Config) -> Self::Future {
MapServiceFuture::new(self.a.new_service(cfg), self.f.clone()) MapServiceFuture::new(self.a.new_service(cfg), self.f.clone())
} }
} }
#[pin_project::pin_project] pin_project! {
pub struct MapServiceFuture<A, F, Res> pub struct MapServiceFuture<A, F, Req, Res>
where where
A: ServiceFactory, A: ServiceFactory<Req>,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Res,
{ {
#[pin] #[pin]
fut: A::Future, fut: A::Future,
f: Option<F>, f: Option<F>,
}
} }
impl<A, F, Res> MapServiceFuture<A, F, Res> impl<A, F, Req, Res> MapServiceFuture<A, F, Req, Res>
where where
A: ServiceFactory, A: ServiceFactory<Req>,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Res,
{ {
fn new(fut: A::Future, f: F) -> Self { fn new(fut: A::Future, f: F) -> Self {
@@ -177,12 +179,12 @@ where
} }
} }
impl<A, F, Res> Future for MapServiceFuture<A, F, Res> impl<A, F, Req, Res> Future for MapServiceFuture<A, F, Req, Res>
where where
A: ServiceFactory, A: ServiceFactory<Req>,
F: FnMut(A::Response) -> Res, F: FnMut(A::Response) -> Res,
{ {
type Output = Result<Map<A::Service, F, Res>, A::InitError>; type Output = Result<Map<A::Service, F, Req, Res>, A::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project(); let this = self.project();
@@ -197,22 +199,21 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use futures_util::future::{lazy, ok, Ready}; use futures_util::future::lazy;
use super::*; use super::*;
use crate::{IntoServiceFactory, Service, ServiceFactory}; use crate::{
ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt,
};
struct Srv; struct Srv;
impl Service for Srv { impl Service<()> for Srv {
type Request = ();
type Response = (); type Response = ();
type Error = (); type Error = ();
type Future = Ready<Result<(), ()>>; type Future = Ready<Result<(), ()>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { crate::always_ready!();
Poll::Ready(Ok(()))
}
fn call(&mut self, _: ()) -> Self::Future { fn call(&mut self, _: ()) -> Self::Future {
ok(()) ok(())

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,4 +6,4 @@
* [API Documentation](https://docs.rs/actix-testing/) * [API Documentation](https://docs.rs/actix-testing/)
* [Chat on gitter](https://gitter.im/actix/actix) * [Chat on gitter](https://gitter.im/actix/actix)
* Cargo package: [actix-http-test](https://crates.io/crates/actix-testing) * Cargo package: [actix-http-test](https://crates.io/crates/actix-testing)
* Minimum supported Rust version: 1.37 or later * Minimum supported Rust version: 1.46 or later

View File

@@ -1,6 +1,9 @@
//! Various helpers for Actix applications to use during testing. //! Various helpers for Actix applications to use during testing.
#![deny(rust_2018_idioms, warnings)]
#![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity, clippy::needless_doctest_main)] #![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::sync::mpsc;
use std::{net, thread}; use std::{net, thread};
@@ -37,7 +40,7 @@ pub use actix_macros::test;
/// ``` /// ```
pub struct TestServer; pub struct TestServer;
/// Test server runstime /// Test server runtime
pub struct TestServerRuntime { pub struct TestServerRuntime {
addr: net::SocketAddr, addr: net::SocketAddr,
host: String, host: String,
@@ -84,13 +87,15 @@ impl TestServer {
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap(); let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
let local_addr = tcp.local_addr().unwrap(); let local_addr = tcp.local_addr().unwrap();
Server::build() sys.block_on(async {
.listen("test", tcp, factory)? Server::build()
.workers(1) .listen("test", tcp, factory)
.disable_signals() .unwrap()
.start(); .workers(1)
.disable_signals()
tx.send((System::current(), local_addr)).unwrap(); .start();
tx.send((System::current(), local_addr)).unwrap();
});
sys.run() sys.run()
}); });
@@ -107,7 +112,7 @@ impl TestServer {
} }
} }
/// Get firat available unused local address /// Get first available unused local address
pub fn unused_addr() -> net::SocketAddr { pub fn unused_addr() -> net::SocketAddr {
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap(); let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = let socket =

View File

@@ -1,5 +1,9 @@
//! Thread pool for blocking operations //! Thread pool for blocking operations
#![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")]
use std::fmt; use std::fmt;
use std::future::Future; use std::future::Future;
use std::pin::Pin; use std::pin::Pin;

View File

@@ -1,39 +1,37 @@
# Changes # Changes
## Unreleased ## Unreleased - 2020-xx-xx
## 2.0.0 - 2020-09-03
* `nativetls::NativeTlsAcceptor` is renamed to `nativetls::Acceptor`.
* Where possible, "SSL" terminology is replaced with "TLS".
* `SslError` is renamed to `TlsError`.
* `TlsError::Ssl` enum variant is renamed to `TlsError::Tls`.
* `max_concurrent_ssl_connect` is renamed to `max_concurrent_tls_connect`.
## 2.0.0-alpha.2 - 2020-08-17 ## 2.0.0-alpha.2 - 2020-08-17
### Changed
* Update `rustls` dependency to 0.18 * Update `rustls` dependency to 0.18
* Update `tokio-rustls` dependency to 0.14 * Update `tokio-rustls` dependency to 0.14
* Update `webpki-roots` dependency to 0.20 * Update `webpki-roots` dependency to 0.20
## [2.0.0-alpha.1] - 2020-03-03 ## [2.0.0-alpha.1] - 2020-03-03
### Changed
* Update `rustls` dependency to 0.17 * Update `rustls` dependency to 0.17
* Update `tokio-rustls` dependency to 0.13 * Update `tokio-rustls` dependency to 0.13
* Update `webpki-roots` dependency to 0.19 * Update `webpki-roots` dependency to 0.19
## [1.0.0] - 2019-12-11
## [1.0.0] - 2019-12-11
* 1.0.0 release * 1.0.0 release
## [1.0.0-alpha.3] - 2019-12-07 ## [1.0.0-alpha.3] - 2019-12-07
### Changed
* Migrate to tokio 0.2 * Migrate to tokio 0.2
* Enable rustls acceptor service * Enable rustls acceptor service
* Enable native-tls acceptor service * Enable native-tls acceptor service
## [1.0.0-alpha.1] - 2019-12-02
* Split openssl accetor from actix-server package ## [1.0.0-alpha.1] - 2019-12-02
* Split openssl acceptor from actix-server package

View File

@@ -1,16 +1,15 @@
[package] [package]
name = "actix-tls" name = "actix-tls"
version = "2.0.0-alpha.2" version = "2.0.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"] authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix tls services" description = "TLS acceptor services for Actix ecosystem."
keywords = ["network", "framework", "async", "futures"] keywords = ["network", "framework", "async", "tls", "ssl"]
homepage = "https://actix.rs" homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git" repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-tls/" documentation = "https://docs.rs/actix-tls/"
categories = ["network-programming", "asynchronous"] categories = ["network-programming", "asynchronous"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2018" edition = "2018"
workspace = ".."
[package.metadata.docs.rs] [package.metadata.docs.rs]
features = ["openssl", "rustls", "nativetls"] features = ["openssl", "rustls", "nativetls"]
@@ -19,6 +18,10 @@ features = ["openssl", "rustls", "nativetls"]
name = "actix_tls" name = "actix_tls"
path = "src/lib.rs" path = "src/lib.rs"
[[example]]
name = "basic"
required-features = ["rustls"]
[features] [features]
default = [] default = []
@@ -33,28 +36,29 @@ nativetls = ["native-tls", "tokio-tls"]
[dependencies] [dependencies]
actix-service = "1.0.0" actix-service = "1.0.0"
actix-codec = "0.2.0" actix-codec = "0.3.0"
actix-utils = "1.0.0" actix-utils = "2.0.0"
actix-rt = "1.0.0"
derive_more = "0.99.2"
either = "1.5.2"
futures-util = { version = "0.3.4", default-features = false } futures-util = { version = "0.3.4", default-features = false }
log = "0.4"
# openssl # openssl
open-ssl = { version="0.10", package = "openssl", optional = true } open-ssl = { package = "openssl", version = "0.10", optional = true }
tokio-openssl = { version = "0.4.0", optional = true } tokio-openssl = { version = "0.4.0", optional = true }
# rustls # rustls
rust-tls = { version = "0.18.0", package = "rustls", optional = true } rust-tls = { package = "rustls", version = "0.18.0", optional = true }
webpki = { version = "0.21", optional = true } webpki = { version = "0.21", optional = true }
webpki-roots = { version = "0.20", optional = true } webpki-roots = { version = "0.20", optional = true }
tokio-rustls = { version = "0.14.0", optional = true } tokio-rustls = { version = "0.14.0", optional = true }
# native-tls # native-tls
native-tls = { version="0.2", optional = true } native-tls = { version = "0.2", optional = true }
tokio-tls = { version="0.3", optional = true } tokio-tls = { version = "0.3", optional = true }
[dev-dependencies] [dev-dependencies]
bytes = "0.5" bytes = "0.5"
actix-testing = { version="1.0.0" } log = "0.4"
env_logger = "0.7"
actix-testing = "2.0.0-beta.1"
actix-server = "2.0.0-beta.1"
actix-rt = "1"

View File

@@ -0,0 +1,82 @@
//! TLS Acceptor Server
//!
//! Using either HTTPie (`http`) or cURL:
//!
//! This commands will produce errors in the server log:
//! ```sh
//! curl 127.0.0.1:8443
//! http 127.0.0.1:8443
//! ```
//!
//! These commands will show "empty reply" on the client but will debug print the TLS stream info
//! in the server log, indicating a successful TLS handshake:
//! ```sh
//! curl -k https://127.0.0.1:8443
//! http --verify=false https://127.0.0.1:8443
//! ```
use std::{
env,
fs::File,
io::{self, BufReader},
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
use actix_server::Server;
use actix_service::pipeline_factory;
use actix_tls::rustls::Acceptor as RustlsAcceptor;
use futures_util::future::ok;
use log::info;
use rust_tls::{
internal::pemfile::certs, internal::pemfile::rsa_private_keys, NoClientAuth, ServerConfig,
};
#[derive(Debug)]
struct ServiceState {
num: Arc<AtomicUsize>,
}
#[actix_rt::main]
async fn main() -> io::Result<()> {
env::set_var("RUST_LOG", "actix=trace,basic=trace");
env_logger::init();
let mut tls_config = ServerConfig::new(NoClientAuth::new());
// Load TLS key and cert files
let cert_file = &mut BufReader::new(File::open("./examples/cert.pem").unwrap());
let key_file = &mut BufReader::new(File::open("./examples/key.pem").unwrap());
let cert_chain = certs(cert_file).unwrap();
let mut keys = rsa_private_keys(key_file).unwrap();
tls_config
.set_single_cert(cert_chain, keys.remove(0))
.unwrap();
let tls_acceptor = RustlsAcceptor::new(tls_config);
let count = Arc::new(AtomicUsize::new(0));
let addr = ("127.0.0.1", 8443);
info!("starting server on port: {}", &addr.0);
Server::build()
.bind("tls-example", addr, move || {
let count = Arc::clone(&count);
// Set up TLS service factory
pipeline_factory(tls_acceptor.clone())
.map_err(|err| println!("Rustls error: {:?}", err))
.and_then(move |stream| {
let num = count.fetch_add(1, Ordering::Relaxed);
info!("[{}] Got TLS connection: {:?}", num, stream);
ok(())
})
})?
.workers(1)
.run()
.await
}

View File

@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIENjCCAp6gAwIBAgIRANp+D9pBErdacw6KjrwJ+4swDQYJKoZIhvcNAQELBQAw
bTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSEwHwYDVQQLDBhyb2JA
c29tYnJhLng1Mi5kZXYgKFJvYikxKDAmBgNVBAMMH21rY2VydCByb2JAc29tYnJh
Lng1Mi5kZXYgKFJvYikwHhcNMTkwNjAxMDAwMDAwWhcNMzAwOTEzMDIzNDI0WjBM
MScwJQYDVQQKEx5ta2NlcnQgZGV2ZWxvcG1lbnQgY2VydGlmaWNhdGUxITAfBgNV
BAsMGHJvYkBzb21icmEueDUyLmRldiAoUm9iKTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBALYAn8dsQUDTp8SptAtkiAySvQYLpAOct3/OjBn+dSYfbQcp
Ph9w/Zo83Msl7Fb1DBvADHFtyBpESATZ2chS5fwCAwUFTlKrzMk3qauEoJ3cCQa8
ccqhTMLeT38jRlhXrMHWBfz0ipqy+yTLWeM32LX8s0jPbbsZ3gVJ/Ls4qm0CTaqb
zRdcQ7GTVKYet5DR7ZvwvAaLtWk/iiHKwnOveuF27HNlxj0Rwd/lhJ/t9x8xJwyR
MTdm852KQadI8xOSbWNK4j9419yzKjUEMKgn78wT/7DQfeKKCAreHa4MaEw4+koD
2Bqb+V4fI6T84VvXkNG3CjSpmIiYGlIE1LVgBL8CAwEAAaNyMHAwDgYDVR0PAQH/
BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0j
BBgwFoAUto/ox0MqZShmQpViV/gjfJKrMDkwGgYDVR0RBBMwEYIJbG9jYWxob3N0
hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBgQBUCMzqTY0sg+61gh8gKS5TCL6qs0R1
xys/EFFaD5JYUsfM/HyhHd0jq+x5Pd3mB2Jvhoq9xhjMwP11H8Uw5lLBHA8USdF9
EiLW1GvT3/gLfMqb0lPk0RMRBeX8c0QbDtqdiUCE7S6zJbZ5gjFeRuFNjdcGA1Ss
8CPPts2mns5cwah6H7T/BFzj5aR9Qe14vo1Rpr5gD5CpHvk1t16q7YsczQfVMvt3
Ydk6p0rwA8Z5okQK7y3qKPZI+//ygWL6ZBjVjl1/Al8vybG2UYjYgfMBwaVvMiDJ
j/vCdVmlvGb+MZlZID/p2veaNeEKgi1A1EOj3sNuQYXXFfSD9mdamX7JIfGi/U7v
ivvUjJUbzGrUngldt5iCKqcCQum7nlzu9sT1Tm2t/n4tz/btrI+Wimg8riSzM+Nk
dfuvv4NbWe6Th5460HH8mMvfPZSB8dCoxwm98tuqcMXLkR1RJX5Z8LYAaPTsUs/h
HxQCY4EaY7feZ/qFal9FGwvpzVr3/XjgSCU=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtgCfx2xBQNOnxKm0C2SIDJK9BgukA5y3f86MGf51Jh9tByk+
H3D9mjzcyyXsVvUMG8AMcW3IGkRIBNnZyFLl/AIDBQVOUqvMyTepq4SgndwJBrxx
yqFMwt5PfyNGWFeswdYF/PSKmrL7JMtZ4zfYtfyzSM9tuxneBUn8uziqbQJNqpvN
F1xDsZNUph63kNHtm/C8Bou1aT+KIcrCc6964Xbsc2XGPRHB3+WEn+33HzEnDJEx
N2bznYpBp0jzE5JtY0riP3jX3LMqNQQwqCfvzBP/sNB94ooICt4drgxoTDj6SgPY
Gpv5Xh8jpPzhW9eQ0bcKNKmYiJgaUgTUtWAEvwIDAQABAoIBADC0Zg21+Jhii6jj
SR0rYAUNV6xAfTnCPJDlMzTZlXwIOOMLtGYxlIwr8WIj2eVDWmQqtqm8GSp+T0+N
BOzI0mboGurDCryw4PKQBMWzjk/wTDITR9hT5fjYCSoaxH5rp/2PSrbwsg7ICtFD
4eAeV84Lu+amK9VADNwZepqXhXP6EDOY5yovkwzOQNDM/qVzHSe9EoFP74M/oWnY
ohIuWdZzwAZuTA5SUjPygiVzs/vhsrSE9crMIzr5VgKBi+C+ALkrL7Lc4GlRPI4r
6VsbIxZHa7who+FhjZ0cVfdXHH47QDdf10X5bEXsaFBvGGCLtkQ3XEpov6GOlaH+
aY7fzPECgYEA4LGloaMC9J27uyPxHkQwEehexmJdIu0vNUefv5yiO9PbvrjvYnh7
JxRVgv1fy2bRMOvg19TujCYRZdkrLDqSDsfFfEiThvlFBRZfKKIHmWdyfvIe9Jp9
rqdxhWAco7FoM+W6c8c4iR4xs8/GA60CVcAiTLqgPWWzn12fesiULi0CgYEAz1xD
OulJyfpHVGQ6ZM1wR0SZ9H9GS3BenpL2ue5uBfe3hM+JIAAM61Y48wJuCWT5EvfL
FgnH3oCo7SYGcgGkERS8H7k67DJCLlqDo/3FC7lX/irz+ya/FoZmKBagvjEUWhpe
Bb2dRIbqsG0lsCzU9MVrgtvodD0MBTyt0RM5fhsCgYEAhgYQiLhGBAituLN4mBgO
IDBdj7GOYk3dkcc2J0HTlyIIeduvlinNM4Myel6NrDKY5rhbtgGhhGEUkY6W7NvG
0SAh0L8tmB3JKH6upfr3023b4pKjGj2oZ+wij27DxnQEdqg5reOP+mHTPbDaKMki
kml3TBMpj1XBbXaXsNJBaMUCgYEAnnNzEC4563QrU2pvUJ3HgT4Dotgqv/Sy6NuG
W1e9jSPYgU0RDHndZWtygwdFTDpzNbJR5po8t2J7MxQOcsmcNE0y387sHpbdCYyy
8Po2uxm7CoaJ/02BUVYL8/Aujob0dVGWrS5SYY3zAjO1S+VGKXA+EjW2cDRB3jKa
45ucICcCgYBdMxB5Oj6GpdewWWaBss9dwHtDaD4oVGYIBbIc2qdyCYixWdW9NccV
fRJs0ulGrpg9OtyWbwZASu2jz55+s3hi4rnrcaXKiIh9Rs25v1irF6Dmduvo7CaN
Mf7zBg7LUttmqN6D3npIAxmBULl8KRfjnt6U2tJolF5X0qQ1uqnnTA==
-----END RSA PRIVATE KEY-----

View File

@@ -1,6 +1,13 @@
//! SSL Services //! TLS acceptor services for Actix ecosystem.
#![deny(rust_2018_idioms, warnings)] //!
#![allow(clippy::type_complexity)] //! ## 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")]
use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::atomic::{AtomicUsize, Ordering};
@@ -15,25 +22,25 @@ pub mod rustls;
#[cfg(feature = "nativetls")] #[cfg(feature = "nativetls")]
pub mod nativetls; pub mod nativetls;
/// Sets the maximum per-worker concurrent ssl connection establish process.
///
/// All listeners will stop accepting connections when this limit is
/// reached. It can be used to limit the global SSL CPU usage.
///
/// By default max connections is set to a 256.
pub fn max_concurrent_ssl_connect(num: usize) {
MAX_CONN.store(num, Ordering::Relaxed);
}
pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256); pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
thread_local! { thread_local! {
static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed)); static MAX_CONN_COUNTER: Counter = Counter::new(MAX_CONN.load(Ordering::Relaxed));
} }
/// Ssl error combinded with service error. /// 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)] #[derive(Debug)]
pub enum SslError<E1, E2> { pub enum TlsError<E1, E2> {
Ssl(E1), Tls(E1),
Service(E2), Service(E2),
} }

View File

@@ -5,34 +5,35 @@ use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use actix_utils::counter::Counter; use actix_utils::counter::Counter;
use futures_util::future::{self, FutureExt, LocalBoxFuture, TryFutureExt}; use futures_util::future::{self, FutureExt, LocalBoxFuture, TryFutureExt};
pub use native_tls::Error; pub use native_tls::Error;
pub use tokio_tls::{TlsAcceptor, TlsStream}; pub use tokio_tls::{TlsAcceptor, TlsStream};
use crate::MAX_CONN_COUNTER; use crate::MAX_CONN_COUNTER;
/// Support `SSL` connections via native-tls package /// Accept TLS connections via `native-tls` package.
/// ///
/// `tls` feature enables `NativeTlsAcceptor` type /// `nativetls` feature enables this `Acceptor` type.
pub struct NativeTlsAcceptor<T> { pub struct Acceptor<T> {
acceptor: TlsAcceptor, acceptor: TlsAcceptor,
io: PhantomData<T>, io: PhantomData<T>,
} }
impl<T> NativeTlsAcceptor<T> impl<T> Acceptor<T>
where where
T: AsyncRead + AsyncWrite + Unpin, T: AsyncRead + AsyncWrite + Unpin,
{ {
/// Create `NativeTlsAcceptor` instance /// Create `native-tls` based `Acceptor` service factory.
#[inline] #[inline]
pub fn new(acceptor: TlsAcceptor) -> Self { pub fn new(acceptor: TlsAcceptor) -> Self {
NativeTlsAcceptor { Acceptor {
acceptor, acceptor,
io: PhantomData, io: PhantomData,
} }
} }
} }
impl<T> Clone for NativeTlsAcceptor<T> { impl<T> Clone for Acceptor<T> {
#[inline] #[inline]
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
@@ -42,7 +43,7 @@ impl<T> Clone for NativeTlsAcceptor<T> {
} }
} }
impl<T> ServiceFactory for NativeTlsAcceptor<T> impl<T> ServiceFactory for Acceptor<T>
where where
T: AsyncRead + AsyncWrite + Unpin + 'static, T: AsyncRead + AsyncWrite + Unpin + 'static,
{ {
@@ -104,8 +105,7 @@ where
let this = self.clone(); let this = self.clone();
async move { this.acceptor.accept(req).await } async move { this.acceptor.accept(req).await }
.map_ok(move |io| { .map_ok(move |io| {
// Required to preserve `CounterGuard` until `Self::Future` // Required to preserve `CounterGuard` until `Self::Future` is completely resolved.
// is completely resolved.
let _ = guard; let _ = guard;
io io
}) })

View File

@@ -3,26 +3,27 @@ use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
pub use open_ssl::ssl::{AlpnError, SslAcceptor, SslAcceptorBuilder};
pub use tokio_openssl::{HandshakeError, SslStream};
use actix_codec::{AsyncRead, AsyncWrite}; use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use actix_utils::counter::{Counter, CounterGuard}; use actix_utils::counter::{Counter, CounterGuard};
use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready}; use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready};
pub use open_ssl::ssl::{AlpnError, SslAcceptor, SslAcceptorBuilder};
pub use tokio_openssl::{HandshakeError, SslStream};
use crate::MAX_CONN_COUNTER; use crate::MAX_CONN_COUNTER;
/// Support `TLS` server connections via openssl package /// Accept TLS connections via `openssl` package.
/// ///
/// `openssl` feature enables `Acceptor` type /// `openssl` feature enables this `Acceptor` type.
pub struct Acceptor<T: AsyncRead + AsyncWrite> { pub struct Acceptor<T: AsyncRead + AsyncWrite> {
acceptor: SslAcceptor, acceptor: SslAcceptor,
io: PhantomData<T>, io: PhantomData<T>,
} }
impl<T: AsyncRead + AsyncWrite> Acceptor<T> { impl<T: AsyncRead + AsyncWrite> Acceptor<T> {
/// Create default `OpensslAcceptor` /// Create OpenSSL based `Acceptor` service factory.
#[inline]
pub fn new(acceptor: SslAcceptor) -> Self { pub fn new(acceptor: SslAcceptor) -> Self {
Acceptor { Acceptor {
acceptor, acceptor,
@@ -32,6 +33,7 @@ impl<T: AsyncRead + AsyncWrite> Acceptor<T> {
} }
impl<T: AsyncRead + AsyncWrite> Clone for Acceptor<T> { impl<T: AsyncRead + AsyncWrite> Clone for Acceptor<T> {
#[inline]
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
acceptor: self.acceptor.clone(), acceptor: self.acceptor.clone(),

View File

@@ -17,16 +17,17 @@ pub use webpki_roots::TLS_SERVER_ROOTS;
use crate::MAX_CONN_COUNTER; use crate::MAX_CONN_COUNTER;
/// Support `SSL` connections via rustls package /// Accept TLS connections via `rustls` package.
/// ///
/// `rust-tls` feature enables `RustlsAcceptor` type /// `rustls` feature enables this `Acceptor` type.
pub struct Acceptor<T> { pub struct Acceptor<T> {
config: Arc<ServerConfig>, config: Arc<ServerConfig>,
io: PhantomData<T>, io: PhantomData<T>,
} }
impl<T: AsyncRead + AsyncWrite> Acceptor<T> { impl<T: AsyncRead + AsyncWrite> Acceptor<T> {
/// Create rustls based `Acceptor` service factory /// Create Rustls based `Acceptor` service factory.
#[inline]
pub fn new(config: ServerConfig) -> Self { pub fn new(config: ServerConfig) -> Self {
Acceptor { Acceptor {
config: Arc::new(config), config: Arc::new(config),
@@ -36,6 +37,7 @@ impl<T: AsyncRead + AsyncWrite> Acceptor<T> {
} }
impl<T> Clone for Acceptor<T> { impl<T> Clone for Acceptor<T> {
#[inline]
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
config: self.config.clone(), config: self.config.clone(),
@@ -65,7 +67,7 @@ impl<T: AsyncRead + AsyncWrite + Unpin> ServiceFactory for Acceptor<T> {
} }
} }
/// RusTLS based `Acceptor` service /// Rustls based `Acceptor` service
pub struct AcceptorService<T> { pub struct AcceptorService<T> {
acceptor: TlsAcceptor, acceptor: TlsAcceptor,
io: PhantomData<T>, io: PhantomData<T>,

View File

@@ -1,8 +1,10 @@
//! Actix tracing - support for tokio tracing with Actix services. //! Actix tracing - support for tokio tracing with Actix services.
#![deny(rust_2018_idioms, warnings)]
use std::marker::PhantomData; #![deny(rust_2018_idioms, nonstandard_style)]
use std::task::{Context, Poll}; #![doc(html_logo_url = "https://actix.rs/img/logo.png")]
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
use core::marker::PhantomData;
use actix_service::{ use actix_service::{
apply, dev::ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform, apply, dev::ApplyTransform, IntoServiceFactory, Service, ServiceFactory, Transform,
@@ -24,21 +26,18 @@ impl<S, F> TracingService<S, F> {
} }
} }
impl<S, F> Service for TracingService<S, F> impl<S, Req, F> Service<Req> for TracingService<S, F>
where where
S: Service, S: Service<Req>,
F: Fn(&S::Request) -> Option<tracing::Span>, F: Fn(&Req) -> Option<tracing::Span>,
{ {
type Request = S::Request;
type Response = S::Response; type Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Future = Either<S::Future, Instrumented<S::Future>>; type Future = Either<S::Future, Instrumented<S::Future>>;
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { actix_service::forward_ready!(inner);
self.inner.poll_ready(ctx)
}
fn call(&mut self, req: Self::Request) -> Self::Future { fn call(&mut self, req: Req) -> Self::Future {
let span = (self.make_span)(&req); let span = (self.make_span)(&req);
let _enter = span.as_ref().map(|s| s.enter()); let _enter = span.as_ref().map(|s| s.enter());
@@ -57,8 +56,6 @@ where
} }
/// A `Transform` implementation that wraps services with a [`TracingService`]. /// A `Transform` implementation that wraps services with a [`TracingService`].
///
/// [`TracingService`]: struct.TracingService.html
pub struct TracingTransform<S, U, F> { pub struct TracingTransform<S, U, F> {
make_span: F, make_span: F,
_p: PhantomData<fn(S, U)>, _p: PhantomData<fn(S, U)>,
@@ -73,18 +70,12 @@ impl<S, U, F> TracingTransform<S, U, F> {
} }
} }
impl<S, U, F> Transform<S> for TracingTransform<S, U, F> impl<S, Req, U, F> Transform<S, Req> for TracingTransform<S, U, F>
where where
S: Service, S: Service<Req>,
U: ServiceFactory< U: ServiceFactory<Req, Response = S::Response, Error = S::Error, Service = S>,
Request = S::Request, F: Fn(&Req) -> Option<tracing::Span> + Clone,
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 Response = S::Response;
type Error = S::Error; type Error = S::Error;
type Transform = TracingService<S, F>; type Transform = TracingService<S, F>;
@@ -109,14 +100,14 @@ where
/// |req: &Request| Some(span!(Level::INFO, "request", req.id)) /// |req: &Request| Some(span!(Level::INFO, "request", req.id))
/// ); /// );
/// ``` /// ```
pub fn trace<S, U, F>( pub fn trace<S, Req, I, F>(
service_factory: U, service_factory: I,
make_span: F, make_span: F,
) -> ApplyTransform<TracingTransform<S::Service, S, F>, S> ) -> ApplyTransform<TracingTransform<S::Service, S, F>, S, Req>
where where
S: ServiceFactory, I: IntoServiceFactory<S, Req>,
F: Fn(&S::Request) -> Option<tracing::Span> + Clone, S: ServiceFactory<Req>,
U: IntoServiceFactory<S>, F: Fn(&Req) -> Option<tracing::Span> + Clone,
{ {
apply( apply(
TracingTransform::new(make_span), TracingTransform::new(make_span),

View File

@@ -2,218 +2,155 @@
## Unreleased - 2020-xx-xx ## Unreleased - 2020-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
## 2.0.0 - 2020-08-23
* No changes from beta 1.
## 2.0.0-beta.1 - 2020-08-19 ## 2.0.0-beta.1 - 2020-08-19
* Upgrade `tokio-util` to `0.3`. * Upgrade `tokio-util` to `0.3`.
* Remove unsound custom Cell and use `std::cell::RefCell` instead, as well as `actix-service`. * Remove unsound custom Cell and use `std::cell::RefCell` instead, as well as `actix-service`.
* Rename method to correctly spelled `LocalWaker::is_registered`. * Rename method to correctly spelled `LocalWaker::is_registered`.
## [1.0.6] - 2020-01-08
* Add `Clone` impl for `condition::Waiter` ## 1.0.6 - 2020-01-08
* Add `Clone` impl for `condition::Waiter`.
## [1.0.5] - 2020-01-08
## 1.0.5 - 2020-01-08
* Add `Condition` type. * Add `Condition` type.
* Add `Pool` of one-shot's. * 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. * Add methods to check `LocalWaker` registration state.
## [1.0.3] - 2019-12-11
## 1.0.3 - 2019-12-11
* Revert InOrder service changes * Revert InOrder service changes
## [1.0.2] - 2019-12-11
* Allow to create `framed::Dispatcher` with custom `mpsc::Receiver` ## 1.0.2 - 2019-12-11
* Allow to create `framed::Dispatcher` with custom `mpsc::Receiver`.
* Add `oneshot::Sender::is_canceled()` method.
* Add `oneshot::Sender::is_canceled()` method
## [1.0.1] - 2019-12-11 ## 1.0.1 - 2019-12-11
* Optimize InOrder service.
* Optimize InOrder service
## [1.0.0] - 2019-12-11 ## 1.0.0 - 2019-12-11
* Simplify oneshot and mpsc implementations.
* Simplify oneshot and mpsc implementations
## [1.0.0-alpha.3] - 2019-12-07 ## 1.0.0-alpha.3 - 2019-12-07
* Migrate to tokio 0.2.
* Fix oneshot.
* Migrate to tokio 0.2
* Fix oneshot ## 1.0.0-alpha.2 - 2019-12-02
* Migrate to `std::future`.
## [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. * 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. * Refactor `Counter` type. register current task in available method.
## [0.4.5] - 2019-07-19 ## 0.4.5 - 2019-07-19
* Deprecated `CloneableService` as it is not safe.
### Removed
* Deprecated `CloneableService` as it is not safe
## [0.4.4] - 2019-07-17 ## 0.4.4 - 2019-07-17
* Undeprecate `FramedTransport` as it is actually useful.
### Changed
* Undeprecate `FramedTransport` as it is actually useful
## [0.4.3] - 2019-07-17 ## 0.4.3 - 2019-07-17
* Deprecate `CloneableService` as it is not safe and in general not very useful.
### Deprecated * Deprecate `FramedTransport` in favor of `actix-ioframe`.
* 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 ## 0.4.2 - 2019-06-26
* Do not block on sink drop for FramedTransport.
### Fixed
* Do not block on sink drop for FramedTransport
## [0.4.1] - 2019-05-15 ## 0.4.1 - 2019-05-15
* Change `Either` constructor.
### Changed
* Change `Either` constructor
## [0.4.0] - 2019-05-11 ## 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.
### 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. * 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. * `TimeoutService`, `InOrderService`, `InFlightService` accepts generic IntoService services.
* Fix `InFlightService::poll_ready()` nested service readiness check.
### Fixed * Fix `InOrderService::poll_ready()` nested service readiness check.
* Fix `InFlightService::poll_ready()` nested service readiness check
* Fix `InOrderService::poll_ready()` nested service readiness check
## [0.3.3] - 2019-03-09 ## 0.3.3 - 2019-03-09
* Revert IntoFuture change.
### Changed * Add generic config param for IntoFramed and TakeOne new services.
* Revert IntoFuture change
* Add generic config param for IntoFramed and TakeOne new services
## [0.3.2] - 2019-03-04 ## 0.3.2 - 2019-03-04
* Use IntoFuture for new services.
### Changed ## 0.3.1 - 2019-03-04
* Use new type of transform trait.
* Use IntoFuture for new services
## [0.3.1] - 2019-03-04 ## 0.3.0 - 2019-03-02
### Changed
* Use new type of transform trait
## [0.3.0] - 2019-03-02
### Changed
* Use new `NewService` trait * Use new `NewService` trait
* BoxedNewService` and `BoxedService` types moved to actix-service crate. * 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. * Custom `BoxedNewService` implementation.
## [0.2.3] - 2019-02-21 ## 0.2.3 - 2019-02-21
* Add `BoxedNewService` and `BoxedService`.
### Added
* Add `BoxedNewService` and `BoxedService`
## [0.2.2] - 2019-02-11 ## 0.2.2 - 2019-02-11
* Add `Display` impl for `TimeoutError`.
### Added * Add `Display` impl for `InOrderError`.
* 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, * Add `InOrder` service. the service yields responses as they become available,
in the order that their originating requests were submitted to the service. 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 ## 0.2.0 - 2019-02-01
* Fix framed transport error handling.
* Fix framed transport error handling * Added Clone impl for Either service.
* Added Clone impl for Timeout service factory.
* Added Clone impl for Either service * Added Service and NewService for Stream dispatcher.
* Switch to actix-service 0.2.
* 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 ## 0.1.0 - 2018-12-09
* Move utils services to separate crate.
* Move utils services to separate crate

View File

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

View File

@@ -1,129 +0,0 @@
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,6 +1,7 @@
use std::cell::Cell; use core::cell::Cell;
use core::task;
use std::rc::Rc; use std::rc::Rc;
use std::task;
use crate::task::LocalWaker; use crate::task::LocalWaker;

View File

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

View File

@@ -1,153 +0,0 @@
//! 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
}
}
}

View File

@@ -1,169 +0,0 @@
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 let Poll::Pending = self.service.poll_ready(cx)? {
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

@@ -1,125 +0,0 @@
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,18 +1,12 @@
//! Actix utils - various helper services //! Various network related services and utilities for the Actix ecosystem.
#![deny(rust_2018_idioms)] #![deny(rust_2018_idioms, nonstandard_style)]
#![allow(clippy::type_complexity)] #![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 counter;
pub mod dispatcher; pub mod dispatcher;
pub mod either;
pub mod inflight;
pub mod keepalive;
pub mod mpsc; pub mod mpsc;
pub mod oneshot;
pub mod order;
pub mod stream;
pub mod task; pub mod task;
pub mod time;
pub mod timeout; pub mod timeout;

View File

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

View File

@@ -1,316 +0,0 @@
//! 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());
}
}

View File

@@ -1,278 +0,0 @@
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 let Poll::Pending = self.service.poll_ready(cx).map_err(InOrderError::Service)? {
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();
}
}

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