1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-08-13 18:58:23 +02:00

Compare commits

...

108 Commits

Author SHA1 Message Date
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
Rob Ede
119dc39f5b prepare codec and utils betas (#182) 2020-08-19 11:00:12 +01:00
Rob Ede
b3010c13e0 solve framed integration with actix-http (#179) 2020-08-18 23:27:37 +01:00
Adrian Wechner
fecdfcd8d4 assert workers greater than zero (#167) 2020-08-18 16:44:22 +01:00
Yuki Okushi
578a560853 connect,tls: Bump up to next alpha versions (#181) 2020-08-17 15:39:17 +01:00
Rob Ede
fb098536ee bump MSRV to 1.42 (#180) 2020-08-17 15:37:57 +01:00
Rob Ede
5d28be9ad6 fix actix-service readme reference (#176) 2020-08-11 12:20:09 +01:00
Rob Ede
a5a6b6704c prepare actix-service 1.0.6 release (#175) 2020-08-09 16:10:58 +01:00
Igor Aleksanov
afb0a3c9fc actix-service: Fix clippy warning in benches (#174) 2020-08-07 17:16:45 +09:00
Miloas
02aaa75591 fix actix-service doc error (#172) 2020-08-06 11:21:51 +01:00
Yuki Okushi
ed4b708c66 Fix CI on MSRV check (#171) 2020-08-05 09:02:41 +09:00
Yuki Okushi
235a76dcd4 GHA: Switch action to the official setup-msys2 (#169) 2020-07-29 08:47:32 +09:00
Matt Kantor
0c5f1da625 Remove garbled doc comment for actix_router::IntoPattern::is_single (#168) 2020-07-29 05:46:53 +09:00
Yuki Okushi
8ace9264b7 Check code style with rustfmt on CI (#164) 2020-07-22 12:32:13 +09:00
Yuki Okushi
0dca1a705a actix-utils: Remove unsound custom Cell as well (#161) 2020-07-22 01:14:32 +01:00
Juan Aguilar
5d6d309e66 Simplify bcodec decode (#162) 2020-07-20 23:09:24 +09:00
Juan Aguilar
8d0bd7ce1c Improve bcodec encode performance (#157) 2020-07-19 22:36:51 +01:00
Sergey "Shnatsel" Davidoff
a67e38b4a0 Remove unsound custom Cell (#158) 2020-07-20 06:05:36 +09:00
Rob Ede
334c98575a Upgrade tokio utils to 0.3 (#138) 2020-07-20 05:44:26 +09:00
Rob Ede
a9b5a7b070 Create PULL_REQUEST_TEMPLATE.md (#159) 2020-07-20 03:01:09 +09:00
Yuki Okushi
61176f6410 Update rustls-related dependencies (#154) 2020-07-14 11:14:06 +01:00
Yuki Okushi
10b4c30a06 Use OR instead of deprecated / in license field (#155) 2020-07-14 11:11:30 +01:00
Yuki Okushi
7f550bcf0f threadpool: Bump up to 0.3.3 (#156) 2020-07-14 11:10:15 +01:00
Yuki Okushi
887f11f787 Merge pull request #153 from actix/tweak-actions
Tweak actions trigger events
2020-07-08 09:04:05 +09:00
Yuki Okushi
e2a6d352b0 Tweak actions trigger events 2020-07-08 08:38:24 +09:00
Yuki Okushi
f6c697a2dd Merge pull request #152 from paolobarbolini/pl-011
Update parking_lot to 0.11
2020-07-04 03:20:08 +09:00
Paolo Barbolini
5ecdfd684a Update parking_lot to 0.11 2020-07-03 17:37:10 +02:00
Yuki Okushi
7140c04c44 Merge pull request #149 from taiki-e/pin-project
Remove uses of pin_project::project attribute
2020-06-07 02:01:08 +09:00
Taiki Endo
9528df4486 Remove uses of pin_project::project attribute
pin-project will deprecate the project attribute due to some unfixable
limitations.

Refs: https://github.com/taiki-e/pin-project/issues/225
2020-06-06 06:42:45 +09:00
Pen Tree
755a8bb9d1 fix codec doc links (#148) 2020-06-02 18:05:39 +01:00
Yuki Okushi
f3cb6efc30 Merge pull request #146 from actix/cache-v2
Update `actions/cache` to v2
2020-05-28 04:59:34 +09:00
Yuki Okushi
87b857705c Update actions/cache to v2 2020-05-28 03:14:01 +09:00
Yuki Okushi
c897c5d3eb Merge pull request #145 from JohnTitor/new-threalpool
threadpool: Bump up to 0.3.2
2020-05-20 15:24:39 +09:00
Yuki Okushi
134e76b8b4 threadpool: Bump up to 0.3.2 2020-05-20 14:19:16 +09:00
Yuki Okushi
f3a401c23b Merge pull request #144 from JohnTitor/codecov-config
Add codecov config
2020-05-20 11:03:31 +09:00
Yuki Okushi
f7e8a912b3 Add codecov config 2020-05-19 14:45:39 +09:00
Yuki Okushi
11a1e11858 Merge pull request #143 from JohnTitor/new-testing
testing: Bump up to 1.0.1
2020-05-19 14:37:54 +09:00
Yuki Okushi
d0b27ee7e6 testing: Bump up to 1.0.1 2020-05-19 14:08:08 +09:00
Yuki Okushi
2d2b0591a2 Merge pull request #142 from JohnTitor/new-server
server: Bump up to 1.0.3
2020-05-19 13:58:39 +09:00
Yuki Okushi
abbc5f715f server: Bump up to 1.0.3 2020-05-19 10:23:17 +09:00
Yuki Okushi
140a6c76e3 Merge pull request #141 from actix/fix-ci
Only check compilation on mingw CI
2020-05-19 09:39:03 +09:00
Yuki Okushi
2395b28c5e Only check compilation on mingw CI
Disabled to run tests since somehow linking with OpenSSL is broken.
2020-05-19 09:11:27 +09:00
Yuki Okushi
aad4812ba6 Merge pull request #140 from JohnTitor/replace-net2
Replace deprecated `net2` crate with `socket2`
2020-05-19 08:58:40 +09:00
Yuki Okushi
ac6c78c476 testing: Replace net2 crate with socket2 2020-05-19 08:21:40 +09:00
Yuki Okushi
8218a098e8 server: Replace net2 crate with socket2 2020-05-19 08:17:44 +09:00
Yuki Okushi
49a6f525be Merge pull request #139 from JohnTitor/next-macros
macros: Bump up to 0.1.2
2020-05-19 07:50:46 +09:00
Yuki Okushi
f59ff82395 macros: Bump up to 0.1.2 2020-05-18 15:36:23 +09:00
Yuki Okushi
f7cc62564d Merge pull request #136 from JohnTitor/connect-alpha-3
actix-connect: Bump up to 2.0.0-alpha.3
2020-05-08 01:36:16 +09:00
Yuki Okushi
b125e2bdce actix-connect: Bump up to 2.0.0-alpha.3 2020-05-08 01:07:57 +09:00
Yuki Okushi
a5c185e80e Merge pull request #135 from actix/fix/unresolverd
correct spelling of ConnectError::Unresolved
2020-05-06 14:45:30 +09:00
Rob Ede
523cee0351 correct spelling of ConnectError::Unresolved 2020-05-03 23:14:22 +01:00
Yuki Okushi
343b3c09fc Merge pull request #134 from JohnTitor/new-rt
Bump up `actix-rt` to 1.1.1
2020-04-30 14:34:17 +09:00
Yuki Okushi
8a10580663 Bump up actix-rt to 1.1.1 2020-04-30 03:07:12 +09:00
Yuki Okushi
1b4a117063 Merge pull request #128 from Jonathas-Conceicao/topic/fix_memory_leak
actix-rt: Spawn future to cleanup pending JoinHandles
2020-04-30 02:58:13 +09:00
Yuki Okushi
700997fe48 Merge pull request #133 from actix/macro-compile-testing
add macro compile tests
2020-04-29 15:33:00 +09:00
Rob Ede
4c5568ed70 add trybuild compile tests 2020-04-26 20:11:16 +01:00
Yuki Okushi
7d0cfe1b4d Merge pull request #131 from danpintara/pull-1
actix-macros: Simplify test macros by using original signature
2020-04-23 02:33:52 +09:00
Daniel Pintara
e35c261c9f actix-macros: test: Simplify by using #sig instead of #name(#inputs) #ret 2020-04-22 00:13:32 +07:00
Yuki Okushi
115ef3fcb3 Merge pull request #130 from JohnTitor/dont-clone
Remove unnecessary clone usage
2020-04-20 08:37:10 +09:00
Yuki Okushi
c0482e2532 Remove unnecessary clone usage 2020-04-20 08:02:08 +09:00
Jonathas-Conceicao
6906f25e01 actix-rt: Set threshold size for arbiter's pending futures list
Signed-off-by: Jonathas-Conceicao <jadoliveira@inf.ufpel.edu.br>
2020-04-16 03:12:05 -03:00
Jonathas-Conceicao
06bca19524 actix-rt: Spawn future to cleanup pending JoinHandles
Signed-off-by: Jonathas-Conceicao <jadoliveira@inf.ufpel.edu.br>
2020-04-09 20:36:44 -03:00
Yuki Okushi
e9e2185296 Merge pull request #127 from rubdos/test-fixture-integration
Forward actix_rt::test arguments to test function.
2020-04-09 17:45:17 +09:00
Ruben De Smet
aae52a80ab Forward actix_rt::test arguments to test function.
Previously,

```rust
async fn foo(_a: u32) {}
```

would compile to

```rust
fn foo() {/* something */}
```

This patches changes this behaviour to

```rust
fn foo(_a: u32) {/* something */}
```

by simply forwarding the input arguments.

This allows any test fixture library (e.g. `rstest`, cfr.
https://github.com/la10736/rstest/issues/85) to integrate with
actix::test.
2020-04-08 16:48:10 +02:00
Yuki Okushi
65e2e8052e Release actix-rt 1.1.0 (#126)
* Release actix-rt 1.1.0

* Update actix-rt/CHANGES.md
2020-04-08 16:34:07 +09:00
Jonathas-Conceicao
783880bb0a actix-rt: Add Arbiter::is_running helper and fix System::is_set doc
`Arbiter::is_running` can be used to check if the current even-loop is currently
running; which should also work after the system has stopped. `System::is_set`
was updated to reflect what it actually does, it tells if the event loop has
started, which alone can't tell if it has stopped.

Signed-off-by: Jonathas-Conceicao <jadoliveira@inf.ufpel.edu.br>
2020-04-05 21:00:54 -03:00
Jonathas-Conceicao
69e8df9d62 actix-rt: Run rustfmt
Signed-off-by: Jonathas-Conceicao <jadoliveira@inf.ufpel.edu.br>
2020-04-05 21:00:54 -03:00
Yuki Okushi
9addf1a36b Merge pull request #125 from actix/fix/noisy-check
fix noisy check warning
2020-04-05 13:20:25 +09:00
Rob Ede
187a58472d fix noisy check warning 2020-04-04 23:57:52 +01:00
Nikolay Kim
30aa0b7bb6 add serde support to bytestring 2020-03-30 11:54:40 +06:00
Yuki Okushi
e775d08d76 Merge pull request #122 from actix/JohnTitor-patch-1
Upload coverage on PRs
2020-03-18 05:31:59 +09:00
Yuki Okushi
d5f95b54b7 Upload coverage on PRs 2020-03-18 05:03:37 +09:00
Yuki Okushi
904f90abc2 Merge pull request #121 from actix/revert-115-JohnTitor-patch-2
Revert "Disable windows-mingw builder temporarily"
2020-03-16 18:06:42 +09:00
Yuki Okushi
950c73077c Revert "Disable windows-mingw builder temporarily" 2020-03-16 17:31:10 +09:00
Yuki Okushi
732731a9c8 Merge pull request #120 from kornelski/err
std Error for BlockingError
2020-03-14 00:14:42 +09:00
Kornel Lesiński
0dd5a7ce1d std Error for BlockingError
#93
2020-03-13 12:35:20 +00:00
Yuki Okushi
7105091e51 Merge pull request #119 from JohnTitor/futures
Minimize `futures-*` dependencies
2020-03-13 05:12:37 +09:00
Yuki Okushi
08959dfc21 actix-tracing: Minimize futures-util dependencies 2020-03-12 07:13:32 +09:00
Yuki Okushi
2792433ad6 actix-codec: Minimize futures-* dependencies 2020-03-12 07:13:32 +09:00
Yuki Okushi
437a7b05c6 actix-rt: Fix build 2020-03-12 07:13:32 +09:00
Yuki Okushi
3d125c5381 actix-testing: Remove unused deps 2020-03-12 07:13:32 +09:00
Yuki Okushi
fbf7d6ef33 Update examples 2020-03-12 07:13:32 +09:00
Yuki Okushi
e6b6f08369 actix-utils: Minimize futures-* dependencies 2020-03-12 07:13:32 +09:00
Yuki Okushi
4e806b3e3f actix-tls: Minimize futures-* dependencies 2020-03-12 07:13:31 +09:00
Yuki Okushi
f5b07053fc actix-server: Minimize futures-* dependencies 2020-03-12 07:13:31 +09:00
Yuki Okushi
dd3bec83bf actix-ioframe: Minimize futures-* dependencies 2020-03-12 07:13:31 +09:00
Yuki Okushi
f955e49930 actix-connect: Minimize futures-* dependencies 2020-03-12 04:22:38 +09:00
128 changed files with 1896 additions and 2413 deletions

24
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,24 @@
## PR Type
<!-- What kind of change does this PR make? -->
<!-- Bug Fix / Feature / Refactor / Code Style / Other -->
INSERT_PR_TYPE
## PR Checklist
Check your PR fulfills the following:
<!-- For draft PRs check the boxes as you complete them. -->
- [ ] Tests for the changes have been added / updated.
- [ ] Documentation comments have been added / updated.
- [ ] A changelog entry has been made for the appropriate packages.
- [ ] Format code with the latest stable rustfmt
## Overview
<!-- Describe the current and new behavior. -->
<!-- Emphasize any breaking changes. -->
<!-- If this PR fixes or closes an issue, reference it here. -->
<!-- Closes #000 -->

View File

@@ -1,13 +1,19 @@
name: Benchmark (Linux)
on: [push, pull_request]
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- master
- '1.0'
jobs:
check_benchmark:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1

34
.github/workflows/clippy-fmt.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
on:
pull_request:
types: [opened, synchronize, reopened]
name: Clippy and rustfmt Check
jobs:
clippy_check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
profile: minimal
override: true
- name: Check with rustfmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: clippy
profile: minimal
override: true
- name: Check with Clippy
uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --workspace --tests

View File

@@ -1,18 +0,0 @@
on: pull_request
name: Clippy Check
jobs:
clippy_check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: clippy
profile: minimal
override: true
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-features --all --tests

View File

@@ -1,6 +1,12 @@
name: CI (Linux)
on: [push, pull_request]
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- master
- '1.0'
jobs:
build_and_test:
@@ -8,7 +14,7 @@ jobs:
fail-fast: false
matrix:
version:
- 1.39.0
- 1.42.0
- stable
- nightly
@@ -16,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: Install ${{ matrix.version }}
uses: actions-rs/toolchain@v1
@@ -29,18 +35,16 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: generate-lockfile
- name: Cache cargo registry
uses: actions/cache@v1
- name: Cache cargo dirs
uses: actions/cache@v2
with:
path: ~/.cargo/registry
key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo index
uses: actions/cache@v1
with:
path: ~/.cargo/git
key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }}
path:
~/.cargo/registry
~/.cargo/git
~/.cargo/bin
key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-trimmed-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v1
uses: actions/cache@v2
with:
path: target
key: ${{ matrix.version }}-x86_64-unknown-linux-gnu-cargo-build-trimmed-${{ hashFiles('**/Cargo.lock') }}
@@ -49,29 +53,30 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: check
args: --all --bins --examples --tests
args: --workspace --bins --examples --tests
- name: tests
uses: actions-rs/cargo@v1
timeout-minutes: 40
with:
command: test
args: --all --all-features --no-fail-fast -- --nocapture
args: --workspace --exclude=actix-tls --no-fail-fast -- --nocapture
- name: Generate coverage file
if: matrix.version == 'stable' && github.ref == 'refs/heads/master'
if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request')
run: |
cargo install cargo-tarpaulin
cargo tarpaulin --out Xml --workspace --all-features
cargo tarpaulin --out Xml --workspace
- name: Upload to Codecov
if: matrix.version == 'stable' && github.ref == 'refs/heads/master'
if: matrix.version == 'stable' && (github.ref == 'refs/heads/master' || github.event_name == 'pull_request')
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: cobertura.xml
- name: Clear the cargo caches
run: |
rustup update stable
rustup override set stable
cargo install cargo-cache --no-default-features --features ci-autoclean
cargo-cache

View File

@@ -1,6 +1,12 @@
name: CI (macOS)
on: [push, pull_request]
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- master
- '1.0'
jobs:
build_and_test:
@@ -15,7 +21,7 @@ jobs:
runs-on: macos-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: Install ${{ matrix.version }}
uses: actions-rs/toolchain@v1
@@ -28,10 +34,10 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: check
args: --all --bins --examples --tests
args: --workspace --bins --examples --tests
- name: tests
uses: actions-rs/cargo@v1
with:
command: test
args: --all --all-features --no-fail-fast -- --nocapture
args: --workspace --exclude=actix-tls --no-fail-fast -- --nocapture

45
.github/workflows/windows-mingw.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: CI (Windows-mingw)
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- master
- '1.0'
jobs:
build_and_test:
strategy:
fail-fast: false
matrix:
version:
- stable
- nightly
name: ${{ matrix.version }} - x86_64-pc-windows-gnu
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Install ${{ matrix.version }}
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.version }}-x86_64-pc-windows-gnu
profile: minimal
override: true
- name: Install MSYS2
uses: msys2/setup-msys2@v2
- name: Install packages
run: |
msys2 -c 'pacman -Sy --noconfirm pacman'
msys2 -c 'pacman --noconfirm -S base-devel pkg-config'
- name: check build
uses: actions-rs/cargo@v1
with:
command: check
args: --workspace --bins --examples --tests

View File

@@ -1,6 +1,12 @@
name: CI (Windows)
on: [push, pull_request]
on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- master
- '1.0'
env:
VCPKGRS_DYNAMIC: 1
@@ -21,7 +27,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: Install ${{ matrix.version }}
uses: actions-rs/toolchain@v1
@@ -54,10 +60,10 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: check
args: --all --bins --examples --tests
args: --workspace --bins --examples --tests
- name: tests
uses: actions-rs/cargo@v1
with:
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
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.
[@robjtede]: https://github.com/robjtede
[@JohnTitor]: https://github.com/JohnTitor
## 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]

View File

@@ -2,7 +2,6 @@
members = [
"actix-codec",
"actix-connect",
"actix-ioframe",
"actix-rt",
"actix-macros",
"actix-service",
@@ -19,7 +18,6 @@ members = [
[patch.crates-io]
actix-codec = { path = "actix-codec" }
actix-connect = { path = "actix-connect" }
actix-ioframe = { path = "actix-ioframe" }
actix-rt = { path = "actix-rt" }
actix-macros = { path = "actix-macros" }
actix-server = { path = "actix-server" }

View File

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

View File

@@ -1,6 +1,22 @@
# Changes
* Use `.advance()` intead of `.split_to()`
## Unreleased - 2020-xx-xx
* Upgrade `pin-project` to `1.0`.
## 0.3.0 - 2020-08-23
* No changes from beta 2.
## 0.3.0-beta.2 - 2020-08-19
* Remove unused type parameter from `Framed::replace_codec`.
## 0.3.0-beta.1 - 2020-08-19
* Use `.advance()` instead of `.split_to()`.
* Upgrade `tokio-util` to `0.3`.
* Improve `BytesCodec` `.encode()` performance
* Simplify `BytesCodec` `.decode()`
* Rename methods on `Framed` to better describe their use.
* Add method on `Framed` to get a pinned reference to the underlying I/O.
* Add method on `Framed` check emptiness of read buffer.
## [0.2.0] - 2019-12-10
@@ -8,7 +24,7 @@
## [0.2.0-alpha.4]
* Fix buffer remaining capacity calcualtion
* Fix buffer remaining capacity calculation
## [0.2.0-alpha.3]

View File

@@ -1,16 +1,15 @@
[package]
name = "actix-codec"
version = "0.2.0"
version = "0.3.0"
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"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-codec/"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
license = "MIT OR Apache-2.0"
edition = "2018"
workspace = ".."
[lib]
name = "actix_codec"
@@ -19,9 +18,9 @@ path = "src/lib.rs"
[dependencies]
bitflags = "1.2.1"
bytes = "0.5.2"
futures-core = "0.3.1"
futures-sink = "0.3.1"
tokio = { version = "0.2.4", default-features=false }
tokio-util = { version = "0.2.0", default-features=false, features=["codec"] }
futures-core = { version = "0.3.4", default-features = false }
futures-sink = { version = "0.3.4", default-features = false }
log = "0.4"
pin-project = "0.4.8"
pin-project = "1.0.0"
tokio = { version = "0.2.5", default-features = false }
tokio-util = { version = "0.3.1", default-features = false, features = ["codec"] }

View File

@@ -1,4 +1,4 @@
use bytes::{BufMut, Bytes, BytesMut};
use bytes::{Buf, Bytes, BytesMut};
use std::io;
use super::{Decoder, Encoder};
@@ -9,13 +9,12 @@ use super::{Decoder, Encoder};
#[derive(Debug, Copy, Clone)]
pub struct BytesCodec;
impl Encoder for BytesCodec {
type Item = Bytes;
impl Encoder<Bytes> for BytesCodec {
type Error = io::Error;
#[inline]
fn encode(&mut self, item: Bytes, dst: &mut BytesMut) -> Result<(), Self::Error> {
dst.reserve(item.len());
dst.put(item);
dst.extend_from_slice(item.bytes());
Ok(())
}
}
@@ -28,8 +27,7 @@ impl Decoder for BytesCodec {
if src.is_empty() {
Ok(None)
} else {
let len = src.len();
Ok(Some(src.split_to(len)))
Ok(Some(src.split()))
}
}
}

View File

@@ -9,7 +9,9 @@ use pin_project::pin_project;
use crate::{AsyncRead, AsyncWrite, Decoder, Encoder};
/// Low-water mark
const LW: usize = 1024;
/// High-water mark
const HW: usize = 8 * 1024;
bitflags::bitflags! {
@@ -21,6 +23,12 @@ bitflags::bitflags! {
/// A unified `Stream` and `Sink` interface to an underlying I/O object, using
/// the `Encoder` and `Decoder` traits to encode and decode frames.
///
/// Raw I/O objects work with byte sequences, but higher-level code usually
/// wants to batch these into meaningful chunks, called "frames". This
/// method layers framing on top of an I/O object, by using the `Encoder`/`Decoder`
/// traits to handle encoding and decoding of message frames. Note that
/// the incoming and outgoing frame types may be distinct.
#[pin_project]
pub struct Framed<T, U> {
#[pin]
@@ -34,17 +42,8 @@ pub struct Framed<T, U> {
impl<T, U> Framed<T, U>
where
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder,
U: Decoder,
{
/// Provides a `Stream` and `Sink` interface for reading and writing to this
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
///
/// Raw I/O objects work with byte sequences, but higher-level code usually
/// wants to batch these into meaningful chunks, called "frames". This
/// method layers framing on top of an I/O object, by using the `Codec`
/// traits to handle encoding and decoding of messages frames. Note that
/// the incoming and outgoing frame types may be distinct.
///
/// This function returns a *single* object that is both `Stream` and
/// `Sink`; grouping this into a single object is often useful for layering
/// things like gzip or TLS, which require both read and write access to the
@@ -61,40 +60,13 @@ where
}
impl<T, U> Framed<T, U> {
/// Provides a `Stream` and `Sink` interface for reading and writing to this
/// `Io` object, using `Decode` and `Encode` to read and write the raw data.
///
/// Raw I/O objects work with byte sequences, but higher-level code usually
/// wants to batch these into meaningful chunks, called "frames". This
/// method layers framing on top of an I/O object, by using the `Codec`
/// traits to handle encoding and decoding of messages frames. Note that
/// the incoming and outgoing frame types may be distinct.
///
/// This function returns a *single* object that is both `Stream` and
/// `Sink`; grouping this into a single object is often useful for layering
/// things like gzip or TLS, which require both read and write access to the
/// underlying object.
///
/// This objects takes a stream and a readbuffer and a writebuffer. These
/// field can be obtained from an existing `Framed` with the
/// `into_parts` method.
pub fn from_parts(parts: FramedParts<T, U>) -> Framed<T, U> {
Framed {
io: parts.io,
codec: parts.codec,
flags: parts.flags,
write_buf: parts.write_buf,
read_buf: parts.read_buf,
}
}
/// Returns a reference to the underlying codec.
pub fn get_codec(&self) -> &U {
pub fn codec_ref(&self) -> &U {
&self.codec
}
/// Returns a mutable reference to the underlying codec.
pub fn get_codec_mut(&mut self) -> &mut U {
pub fn codec_mut(&mut self) -> &mut U {
&mut self.codec
}
@@ -104,20 +76,29 @@ impl<T, U> Framed<T, U> {
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_ref(&self) -> &T {
pub fn io_ref(&self) -> &T {
&self.io
}
/// Returns a mutable reference to the underlying I/O stream wrapped by
/// `Frame`.
/// Returns a mutable reference to the underlying I/O stream.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn get_mut(&mut self) -> &mut T {
pub fn io_mut(&mut self) -> &mut T {
&mut self.io
}
/// Returns a `Pin` of a mutable reference to the underlying I/O stream.
pub fn io_pin(self: Pin<&mut Self>) -> Pin<&mut T> {
self.project().io
}
/// Check if read buffer is empty.
pub fn is_read_buf_empty(&self) -> bool {
self.read_buf.is_empty()
}
/// Check if write buffer is empty.
pub fn is_write_buf_empty(&self) -> bool {
self.write_buf.is_empty()
@@ -128,8 +109,15 @@ impl<T, U> Framed<T, U> {
self.write_buf.len() >= HW
}
/// Check if framed is able to write more data.
///
/// `Framed` object considers ready if there is free space in write buffer.
pub fn is_write_ready(&self) -> bool {
self.write_buf.len() < HW
}
/// Consume the `Frame`, returning `Frame` with different codec.
pub fn into_framed<U2>(self, codec: U2) -> Framed<T, U2> {
pub fn replace_codec<U2>(self, codec: U2) -> Framed<T, U2> {
Framed {
codec,
io: self.io,
@@ -140,7 +128,7 @@ impl<T, U> Framed<T, U> {
}
/// Consume the `Frame`, returning `Frame` with different io.
pub fn map_io<F, T2>(self, f: F) -> Framed<T2, U>
pub fn into_map_io<F, T2>(self, f: F) -> Framed<T2, U>
where
F: Fn(T) -> T2,
{
@@ -154,7 +142,7 @@ impl<T, U> Framed<T, U> {
}
/// Consume the `Frame`, returning `Frame` with different codec.
pub fn map_codec<F, U2>(self, f: F) -> Framed<T, U2>
pub fn into_map_codec<F, U2>(self, f: F) -> Framed<T, U2>
where
F: Fn(U) -> U2,
{
@@ -166,30 +154,14 @@ impl<T, U> Framed<T, U> {
write_buf: self.write_buf,
}
}
/// Consumes the `Frame`, returning its underlying I/O stream, the buffer
/// with unprocessed data, and the codec.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn into_parts(self) -> FramedParts<T, U> {
FramedParts {
io: self.io,
codec: self.codec,
flags: self.flags,
read_buf: self.read_buf,
write_buf: self.write_buf,
}
}
}
impl<T, U> Framed<T, U> {
/// Serialize item and Write to the inner buffer
pub fn write(mut self: Pin<&mut Self>, item: <U as Encoder>::Item) -> Result<(), <U as Encoder>::Error>
pub fn write<I>(mut self: Pin<&mut Self>, item: I) -> Result<(), <U as Encoder<I>>::Error>
where
T: AsyncWrite,
U: Encoder,
U: Encoder<I>,
{
let this = self.as_mut().project();
let remaining = this.write_buf.capacity() - this.write_buf.len();
@@ -201,15 +173,11 @@ impl<T, U> Framed<T, U> {
Ok(())
}
/// Check if framed is able to write more data.
///
/// `Framed` object considers ready if there is free space in write buffer.
pub fn is_write_ready(&self) -> bool {
self.write_buf.len() < HW
}
/// Try to read underlying I/O stream and decode item.
pub fn next_item(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Result<U::Item, U::Error>>>
pub fn next_item(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<<U as Decoder>::Item, U::Error>>>
where
T: AsyncRead,
U: Decoder,
@@ -266,10 +234,13 @@ impl<T, U> Framed<T, U> {
}
/// Flush write buffer to underlying I/O stream.
pub fn flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), U::Error>>
pub fn flush<I>(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), U::Error>>
where
T: AsyncWrite,
U: Encoder,
U: Encoder<I>,
{
let mut this = self.as_mut().project();
log::trace!("flushing framed transport");
@@ -277,9 +248,7 @@ impl<T, U> Framed<T, U> {
while !this.write_buf.is_empty() {
log::trace!("writing; remaining={}", this.write_buf.len());
let n = ready!(
this.io.as_mut().poll_write(cx, this.write_buf)
)?;
let n = ready!(this.io.as_mut().poll_write(cx, this.write_buf))?;
if n == 0 {
return Poll::Ready(Err(io::Error::new(
@@ -301,10 +270,13 @@ impl<T, U> Framed<T, U> {
}
/// Flush write buffer and shutdown underlying I/O stream.
pub fn close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), U::Error>>
pub fn close<I>(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), U::Error>>
where
T: AsyncWrite,
U: Encoder,
U: Encoder<I>,
{
let mut this = self.as_mut().project();
ready!(this.io.as_mut().poll_flush(cx))?;
@@ -325,10 +297,10 @@ where
}
}
impl<T, U> Sink<U::Item> for Framed<T, U>
impl<T, U, I> Sink<I> for Framed<T, U>
where
T: AsyncWrite,
U: Encoder,
U: Encoder<I>,
U::Error: From<io::Error>,
{
type Error = U::Error;
@@ -341,24 +313,15 @@ where
}
}
fn start_send(
self: Pin<&mut Self>,
item: <U as Encoder>::Item,
) -> Result<(), Self::Error> {
fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> {
self.write(item)
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.flush(cx)
}
fn poll_close(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.close(cx)
}
}
@@ -376,6 +339,41 @@ where
}
}
impl<T, U> Framed<T, U> {
/// This function returns a *single* object that is both `Stream` and
/// `Sink`; grouping this into a single object is often useful for layering
/// things like gzip or TLS, which require both read and write access to the
/// underlying object.
///
/// These objects take a stream, a read buffer and a write buffer. These
/// fields can be obtained from an existing `Framed` with the `into_parts` method.
pub fn from_parts(parts: FramedParts<T, U>) -> Framed<T, U> {
Framed {
io: parts.io,
codec: parts.codec,
flags: parts.flags,
write_buf: parts.write_buf,
read_buf: parts.read_buf,
}
}
/// Consumes the `Frame`, returning its underlying I/O stream, the buffer
/// with unprocessed data, and the codec.
///
/// Note that care should be taken to not tamper with the underlying stream
/// of data coming in as it may corrupt the stream of frames otherwise
/// being worked with.
pub fn into_parts(self) -> FramedParts<T, U> {
FramedParts {
io: self.io,
codec: self.codec,
flags: self.flags,
read_buf: self.read_buf,
write_buf: self.write_buf,
}
}
}
/// `FramedParts` contains an export of the data of a Framed transport.
/// It can be used to construct a new `Framed` with a different codec.
/// It contains all current buffers and the inner transport.

View File

@@ -2,11 +2,15 @@
//!
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
//! Framed streams are also known as [transports].
//! Framed streams are also known as `transports`.
//!
//! [`AsyncRead`]: #
//! [`AsyncWrite`]: #
#![deny(rust_2018_idioms, warnings)]
//! [`AsyncRead`]: AsyncRead
//! [`AsyncWrite`]: AsyncWrite
//! [`Sink`]: futures_sink::Sink
//! [`Stream`]: futures_core::Stream
#![deny(rust_2018_idioms)]
#![warn(missing_docs)]
mod bcodec;
mod framed;

View File

@@ -1,5 +1,25 @@
# Changes
## Unreleased - 2020-xx-xx
## 2.0.0 - 2020-09-02
- No significant changes from `2.0.0-alpha.4`.
## 2.0.0-alpha.4 - 2020-08-17
### Changed
* Update `rustls` dependency to 0.18
* Update `tokio-rustls` dependency to 0.14
## [2.0.0-alpha.3] - 2020-05-08
### Fixed
* Corrected spelling of `ConnectError::Unresolverd` to `ConnectError::Unresolved`
## [2.0.0-alpha.2] - 2020-03-08
### Changed

View File

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

View File

@@ -43,7 +43,7 @@ pub struct 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> {
let (_, port) = parse(req.host());
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> {
Connect {
req,
@@ -102,7 +103,7 @@ impl<T: Address> Connect<T> {
self.req.port().unwrap_or(self.port)
}
/// Preresolved addresses of the request.
/// Pre-resolved addresses of the request.
pub fn addrs(&self) -> ConnectAddrsIter<'_> {
let inner = match self.addr {
None => Either::Left(None),
@@ -113,7 +114,7 @@ impl<T: Address> Connect<T> {
ConnectAddrsIter { inner }
}
/// Takes preresolved addresses of the request.
/// Takes pre-resolved addresses of the request.
pub fn take_addrs(&mut self) -> ConnectTakeAddrsIter {
let inner = match self.addr.take() {
None => Either::Left(None),

View File

@@ -8,12 +8,12 @@ use std::task::{Context, Poll};
use actix_rt::net::TcpStream;
use actix_service::{Service, ServiceFactory};
use futures::future::{err, ok, BoxFuture, Either, FutureExt, Ready};
use futures_util::future::{err, ok, BoxFuture, Either, FutureExt, Ready};
use super::connect::{Address, Connect, Connection};
use super::error::ConnectError;
/// Tcp connector service factory
/// TCP connector service factory
#[derive(Debug)]
pub struct TcpConnectorFactory<T>(PhantomData<T>);
@@ -22,7 +22,7 @@ impl<T> TcpConnectorFactory<T> {
TcpConnectorFactory(PhantomData)
}
/// Create tcp connector service
/// Create TCP connector service
pub fn service(&self) -> TcpConnector<T> {
TcpConnector(PhantomData)
}
@@ -54,7 +54,7 @@ impl<T: Address> ServiceFactory for TcpConnectorFactory<T> {
}
}
/// Tcp connector service
/// TCP connector service
#[derive(Default, Debug)]
pub struct TcpConnector<T>(PhantomData<T>);
@@ -74,6 +74,7 @@ impl<T: Address> Service for TcpConnector<T> {
type Request = Connect<T>;
type Response = Connection<T, TcpStream>;
type Error = ConnectError;
#[allow(clippy::type_complexity)]
type Future = Either<TcpConnectorResponse<T>, Ready<Result<Self::Response, Self::Error>>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@@ -88,13 +89,13 @@ impl<T: Address> Service for TcpConnector<T> {
Either::Left(TcpConnectorResponse::new(req, port, addr))
} else {
error!("TCP connector: got unresolved address");
Either::Right(err(ConnectError::Unresolverd))
Either::Right(err(ConnectError::Unresolved))
}
}
}
#[doc(hidden)]
/// Tcp stream connector response future
/// TCP stream connector response future
pub struct TcpConnectorResponse<T> {
req: Option<T>,
port: u16,

View File

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

View File

@@ -1,11 +1,11 @@
//! Actix connect - tcp connector service
//! TCP connector service for Actix ecosystem.
//!
//! ## Package feature
//!
//! * `openssl` - enables ssl support via `openssl` crate
//! * `rustls` - enables ssl support via `rustls` crate
#![deny(rust_2018_idioms, warnings)]
#![allow(clippy::type_complexity)]
//! * `openssl` - enables TLS support via `openssl` crate
//! * `rustls` - enables TLS support via `rustls` crate
#![deny(rust_2018_idioms)]
#![recursion_limit = "128"]
#[macro_use]
@@ -71,7 +71,7 @@ pub async fn start_default_resolver() -> Result<AsyncResolver, ConnectError> {
get_default_resolver().await
}
/// Create tcp connector service
/// Create TCP connector service.
pub fn new_connector<T: Address + 'static>(
resolver: AsyncResolver,
) -> impl Service<Request = Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError>
@@ -79,7 +79,7 @@ pub fn new_connector<T: Address + 'static>(
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>(
resolver: AsyncResolver,
) -> impl ServiceFactory<
@@ -92,14 +92,14 @@ pub fn new_connector_factory<T: Address + 'static>(
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>(
) -> impl Service<Request = Connect<T>, Response = Connection<T, TcpStream>, Error = ConnectError>
+ Clone {
pipeline(Resolver::default()).and_then(TcpConnector::new())
}
/// Create connector service factory with default parameters
/// Create connector service factory with default parameters.
pub fn default_connector_factory<T: Address + 'static>() -> impl ServiceFactory<
Config = (),
Request = Connect<T>,

View File

@@ -5,7 +5,7 @@ use std::pin::Pin;
use std::task::{Context, Poll};
use actix_service::{Service, ServiceFactory};
use futures::future::{ok, Either, Ready};
use futures_util::future::{ok, Either, Ready};
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
use trust_dns_resolver::{error::ResolveError, lookup_ip::LookupIp};
@@ -106,6 +106,7 @@ impl<T: Address> Service for Resolver<T> {
type Request = Connect<T>;
type Response = Connect<T>;
type Error = ConnectError;
#[allow(clippy::type_complexity)]
type Future = Either<
Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>,
Ready<Result<Connect<T>, Self::Error>>,

View File

@@ -5,7 +5,7 @@ use std::task::{Context, Poll};
use actix_rt::net::TcpStream;
use actix_service::{Service, ServiceFactory};
use either::Either;
use futures::future::{ok, Ready};
use futures_util::future::{ok, Ready};
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
use crate::connect::{Address, Connect, Connection};
@@ -114,6 +114,7 @@ enum ConnectState<T: Address> {
}
impl<T: Address> ConnectState<T> {
#[allow(clippy::type_complexity)]
fn poll(
&mut self,
cx: &mut Context<'_>,

View File

@@ -10,14 +10,14 @@ pub use tokio_openssl::{HandshakeError, SslStream};
use actix_codec::{AsyncRead, AsyncWrite};
use actix_rt::net::TcpStream;
use actix_service::{Service, ServiceFactory};
use futures::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready};
use futures_util::future::{err, ok, Either, FutureExt, LocalBoxFuture, Ready};
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
use crate::{
Address, Connect, ConnectError, ConnectService, ConnectServiceFactory, Connection,
};
/// Openssl connector factory
/// OpenSSL connector factory
pub struct OpensslConnector<T, U> {
connector: SslConnector,
_t: PhantomData<(T, U)>,
@@ -97,6 +97,7 @@ where
type Request = Connection<T, U>;
type Response = Connection<T, SslStream<U>>;
type Error = io::Error;
#[allow(clippy::type_complexity)]
type Future = Either<ConnectAsyncExt<T, U>, Ready<Result<Self::Response, Self::Error>>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
@@ -164,7 +165,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 {
OpensslConnectServiceFactory {
tcp: ConnectServiceFactory::with_resolver(resolver),
@@ -172,7 +173,7 @@ impl<T> OpensslConnectServiceFactory<T> {
}
}
/// Construct openssl connect service
/// Construct OpenSSL connect service
pub fn service(&self) -> OpensslConnectService<T> {
OpensslConnectService {
tcp: self.tcp.service(),
@@ -243,7 +244,7 @@ impl<T: Address> Future for OpensslConnectServiceResponse<T> {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(ref mut fut) = self.fut1 {
match futures::ready!(Pin::new(fut).poll(cx)) {
match futures_util::ready!(Pin::new(fut).poll(cx)) {
Ok(res) => {
let _ = self.fut1.take();
self.fut2 = Some(self.openssl.call(res));
@@ -253,7 +254,7 @@ impl<T: Address> Future for OpensslConnectServiceResponse<T> {
}
if let Some(ref mut fut) = self.fut2 {
match futures::ready!(Pin::new(fut).poll(cx)) {
match futures_util::ready!(Pin::new(fut).poll(cx)) {
Ok(connect) => Poll::Ready(Ok(connect.into_parts().0)),
Err(e) => Poll::Ready(Err(ConnectError::Io(io::Error::new(
io::ErrorKind::Other,

View File

@@ -10,7 +10,7 @@ pub use tokio_rustls::{client::TlsStream, rustls::ClientConfig};
use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory};
use futures::future::{ok, Ready};
use futures_util::future::{ok, Ready};
use tokio_rustls::{Connect, TlsConnector};
use webpki::DNSNameRef;
@@ -126,7 +126,7 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
Poll::Ready(
futures::ready!(Pin::new(&mut this.fut).poll(cx)).map(|stream| {
futures_util::ready!(Pin::new(&mut this.fut).poll(cx)).map(|stream| {
let s = this.stream.take().unwrap();
trace!("SSL Handshake success: {:?}", s.host());
s.replace(stream).1

View File

@@ -5,7 +5,7 @@ use actix_rt::net::TcpStream;
use actix_service::{fn_service, Service, ServiceFactory};
use actix_testing::TestServer;
use bytes::Bytes;
use futures::SinkExt;
use futures_util::sink::SinkExt;
use actix_connect::resolver::{ResolverConfig, ResolverOpts};
use actix_connect::Connect;
@@ -88,9 +88,9 @@ async fn test_new_service() {
assert_eq!(con.peer_addr().unwrap(), srv.addr());
}
#[cfg(feature = "openssl")]
#[cfg(all(feature = "openssl", feature = "uri"))]
#[actix_rt::test]
async fn test_uri() {
async fn test_openssl_uri() {
use std::convert::TryFrom;
let srv = TestServer::with(|| {
@@ -107,7 +107,7 @@ async fn test_uri() {
assert_eq!(con.peer_addr().unwrap(), srv.addr());
}
#[cfg(feature = "rustls")]
#[cfg(all(feature = "rustls", feature = "uri"))]
#[actix_rt::test]
async fn test_rustls_uri() {
use std::convert::TryFrom;

View File

@@ -1,33 +0,0 @@
# Changes
## [0.5.0] - 2019-12-29
* Simplify state management
* Allow to set custom output stream
* Removed disconnect callback
## [0.4.1] - 2019-12-11
* Disconnect callback accepts owned state
## [0.4.0] - 2019-12-11
* Remove `E` param
## [0.3.0-alpha.3] - 2019-12-07
* Migrate to tokio 0.2
## [0.3.0-alpha.2] - 2019-12-02
* Migrate to `std::future`
## [0.1.1] - 2019-10-14
* Re-register task on every dispatcher poll.
## [0.1.0] - 2019-09-25
* Initial release

View File

@@ -1,31 +0,0 @@
[package]
name = "actix-ioframe"
version = "0.5.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix framed service"
keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-ioframe/"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
edition = "2018"
[lib]
name = "actix_ioframe"
path = "src/lib.rs"
[dependencies]
actix-service = "1.0.1"
actix-codec = "0.2.0"
actix-utils = "1.0.4"
actix-rt = "1.0.0"
bytes = "0.5.3"
either = "1.5.3"
futures = "0.3.1"
pin-project = "0.4.6"
log = "0.4"
[dev-dependencies]
actix-connect = "2.0.0-alpha.2"
actix-testing = "1.0.0"

View File

@@ -1 +0,0 @@
../LICENSE-APACHE

View File

@@ -1 +0,0 @@
../LICENSE-MIT

View File

@@ -1,123 +0,0 @@
use std::marker::PhantomData;
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed};
use actix_utils::mpsc::Receiver;
use futures::Stream;
pub struct Connect<Io, Codec>
where
Codec: Encoder + Decoder,
{
io: Io,
_t: PhantomData<Codec>,
}
impl<Io, Codec> Connect<Io, Codec>
where
Io: AsyncRead + AsyncWrite,
Codec: Encoder + Decoder,
{
pub(crate) fn new(io: Io) -> Self {
Self {
io,
_t: PhantomData,
}
}
pub fn codec(
self,
codec: Codec,
) -> ConnectResult<Io, (), Codec, Receiver<<Codec as Encoder>::Item>> {
ConnectResult {
state: (),
out: None,
framed: Framed::new(self.io, codec),
}
}
}
#[pin_project::pin_project]
pub struct ConnectResult<Io, St, Codec: Encoder + Decoder, Out> {
pub(crate) state: St,
pub(crate) out: Option<Out>,
#[pin]
pub(crate) framed: Framed<Io, Codec>,
}
impl<Io, St, Codec: Encoder + Decoder, Out: Unpin> ConnectResult<Io, St, Codec, Out> {
#[inline]
pub fn get_ref(&self) -> &Io {
self.framed.get_ref()
}
#[inline]
pub fn get_mut(&mut self) -> &mut Io {
self.framed.get_mut()
}
pub fn out<U>(self, out: U) -> ConnectResult<Io, St, Codec, U>
where
U: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
ConnectResult {
state: self.state,
framed: self.framed,
out: Some(out),
}
}
#[inline]
pub fn state<S>(self, state: S) -> ConnectResult<Io, S, Codec, Out> {
ConnectResult {
state,
framed: self.framed,
out: self.out,
}
}
}
impl<Io, St, Codec, Out> Stream for ConnectResult<Io, St, Codec, Out>
where
Io: AsyncRead + AsyncWrite,
Codec: Encoder + Decoder,
{
type Item = Result<<Codec as Decoder>::Item, <Codec as Decoder>::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
self.project().framed.next_item(cx)
}
}
impl<Io, St, Codec, Out> futures::Sink<<Codec as Encoder>::Item>
for ConnectResult<Io, St, Codec, Out>
where
Io: AsyncRead + AsyncWrite,
Codec: Encoder + Decoder,
{
type Error = <Codec as Encoder>::Error;
fn poll_ready(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
if self.as_mut().project().framed.is_write_ready() {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
}
fn start_send(
self: Pin<&mut Self>,
item: <Codec as Encoder>::Item,
) -> Result<(), Self::Error> {
self.project().framed.write(item)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.as_mut().project().framed.flush(cx)
}
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.as_mut().project().framed.close(cx)
}
}

View File

@@ -1,248 +0,0 @@
//! Framed dispatcher service and related utilities
use std::pin::Pin;
use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed};
use actix_service::Service;
use actix_utils::mpsc;
use futures::Stream;
use pin_project::pin_project;
use log::debug;
use crate::error::ServiceError;
type Request<U> = <U as Decoder>::Item;
type Response<U> = <U as Encoder>::Item;
/// FramedTransport - is a future that reads frames from Framed object
/// and pass then to the service.
#[pin_project]
pub(crate) struct Dispatcher<S, T, U, Out>
where
S: Service<Request = Request<U>, Response = Option<Response<U>>>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Encoder + Decoder,
<U as Encoder>::Item: 'static,
<U as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <U as Encoder>::Item> + Unpin,
{
service: S,
sink: Option<Out>,
state: FramedState<S, U>,
#[pin]
framed: Framed<T, U>,
rx: mpsc::Receiver<Result<<U as Encoder>::Item, S::Error>>,
}
impl<S, T, U, Out> Dispatcher<S, T, U, Out>
where
S: Service<Request = Request<U>, Response = Option<Response<U>>>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder,
<U as Encoder>::Item: 'static,
<U as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <U as Encoder>::Item> + Unpin,
{
pub(crate) fn new(framed: Framed<T, U>, service: S, sink: Option<Out>) -> Self {
Dispatcher {
sink,
service,
framed,
rx: mpsc::channel().1,
state: FramedState::Processing,
}
}
}
enum FramedState<S: Service, U: Encoder + Decoder> {
Processing,
Error(ServiceError<S::Error, U>),
FramedError(ServiceError<S::Error, U>),
FlushAndStop,
Stopping,
}
impl<S: Service, U: Encoder + Decoder> FramedState<S, U> {
fn take_error(&mut self) -> ServiceError<S::Error, U> {
match std::mem::replace(self, FramedState::Processing) {
FramedState::Error(err) => err,
_ => panic!(),
}
}
fn take_framed_error(&mut self) -> ServiceError<S::Error, U> {
match std::mem::replace(self, FramedState::Processing) {
FramedState::FramedError(err) => err,
_ => panic!(),
}
}
}
impl<S, T, U, Out> Dispatcher<S, T, U, Out>
where
S: Service<Request = Request<U>, Response = Option<Response<U>>>,
S::Error: 'static,
S::Future: 'static,
T: AsyncRead + AsyncWrite,
U: Decoder + Encoder,
<U as Encoder>::Item: 'static,
<U as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <U as Encoder>::Item> + Unpin,
{
fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool {
loop {
let this = self.as_mut().project();
match this.service.poll_ready(cx) {
Poll::Ready(Ok(_)) => {
let item = match this.framed.next_item(cx) {
Poll::Ready(Some(Ok(el))) => el,
Poll::Ready(Some(Err(err))) => {
*this.state = FramedState::FramedError(ServiceError::Decoder(err));
return true;
}
Poll::Pending => return false,
Poll::Ready(None) => {
log::trace!("Client disconnected");
*this.state = FramedState::Stopping;
return true;
}
};
let tx = this.rx.sender();
let fut = this.service.call(item);
actix_rt::spawn(async move {
let item = fut.await;
let item = match item {
Ok(Some(item)) => Ok(item),
Ok(None) => return,
Err(err) => Err(err),
};
let _ = tx.send(item);
});
}
Poll::Pending => return false,
Poll::Ready(Err(err)) => {
*this.state = FramedState::Error(ServiceError::Service(err));
return true;
}
}
}
}
/// write to framed object
fn poll_write(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool {
loop {
let mut this = self.as_mut().project();
while !this.framed.is_write_buf_full() {
match Pin::new(&mut this.rx).poll_next(cx) {
Poll::Ready(Some(Ok(msg))) => {
if let Err(err) = this.framed.as_mut().write(msg) {
*this.state = FramedState::FramedError(ServiceError::Encoder(err));
return true;
}
continue;
}
Poll::Ready(Some(Err(err))) => {
*this.state = FramedState::Error(ServiceError::Service(err));
return true;
}
Poll::Ready(None) | Poll::Pending => (),
}
if this.sink.is_some() {
match Pin::new(this.sink.as_mut().unwrap()).poll_next(cx) {
Poll::Ready(Some(msg)) => {
if let Err(err) = this.framed.as_mut().write(msg) {
*this.state =
FramedState::FramedError(ServiceError::Encoder(err));
return true;
}
continue;
}
Poll::Ready(None) => {
let _ = this.sink.take();
*this.state = FramedState::FlushAndStop;
return true;
}
Poll::Pending => (),
}
}
break;
}
if !this.framed.is_write_buf_empty() {
match this.framed.as_mut().flush(cx) {
Poll::Pending => break,
Poll::Ready(Ok(_)) => (),
Poll::Ready(Err(err)) => {
debug!("Error sending data: {:?}", err);
*this.state = FramedState::FramedError(ServiceError::Encoder(err));
return true;
}
}
} else {
break;
}
}
false
}
pub(crate) fn poll(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), ServiceError<S::Error, U>>> {
let mut this = self.as_mut().project();
match this.state {
FramedState::Processing => loop {
let read = self.as_mut().poll_read(cx);
let write = self.as_mut().poll_write(cx);
if read || write {
continue;
} else {
return Poll::Pending;
}
},
FramedState::Error(_) => {
// flush write buffer
if !this.framed.is_write_buf_empty() {
if let Poll::Pending = this.framed.flush(cx) {
return Poll::Pending;
}
}
Poll::Ready(Err(this.state.take_error()))
}
FramedState::FlushAndStop => {
// drain service responses
match Pin::new(this.rx).poll_next(cx) {
Poll::Ready(Some(Ok(msg))) => {
if this.framed.as_mut().write(msg).is_err() {
return Poll::Ready(Ok(()));
}
}
Poll::Ready(Some(Err(_))) => return Poll::Ready(Ok(())),
Poll::Ready(None) | Poll::Pending => (),
}
// flush io
if !this.framed.is_write_buf_empty() {
match this.framed.flush(cx) {
Poll::Ready(Err(err)) => {
debug!("Error sending data: {:?}", err);
}
Poll::Pending => {
return Poll::Pending;
}
Poll::Ready(_) => (),
}
};
Poll::Ready(Ok(()))
}
FramedState::FramedError(_) => Poll::Ready(Err(this.state.take_framed_error())),
FramedState::Stopping => Poll::Ready(Ok(())),
}
}
}

View File

@@ -1,49 +0,0 @@
use std::fmt;
use actix_codec::{Decoder, Encoder};
/// Framed service errors
pub enum ServiceError<E, U: Encoder + Decoder> {
/// Inner service error
Service(E),
/// Encoder parse error
Encoder(<U as Encoder>::Error),
/// Decoder parse error
Decoder(<U as Decoder>::Error),
}
impl<E, U: Encoder + Decoder> From<E> for ServiceError<E, U> {
fn from(err: E) -> Self {
ServiceError::Service(err)
}
}
impl<E, U: Encoder + Decoder> fmt::Debug for ServiceError<E, U>
where
E: fmt::Debug,
<U as Encoder>::Error: fmt::Debug,
<U as Decoder>::Error: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ServiceError::Service(ref e) => write!(fmt, "ServiceError::Service({:?})", e),
ServiceError::Encoder(ref e) => write!(fmt, "ServiceError::Encoder({:?})", e),
ServiceError::Decoder(ref e) => write!(fmt, "ServiceError::Encoder({:?})", e),
}
}
}
impl<E, U: Encoder + Decoder> fmt::Display for ServiceError<E, U>
where
E: fmt::Display,
<U as Encoder>::Error: fmt::Debug,
<U as Decoder>::Error: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ServiceError::Service(ref e) => write!(fmt, "{}", e),
ServiceError::Encoder(ref e) => write!(fmt, "{:?}", e),
ServiceError::Decoder(ref e) => write!(fmt, "{:?}", e),
}
}
}

View File

@@ -1,11 +0,0 @@
// #![deny(rust_2018_idioms, warnings)]
#![allow(clippy::type_complexity, clippy::too_many_arguments)]
mod connect;
mod dispatcher;
mod error;
mod service;
pub use self::connect::{Connect, ConnectResult};
pub use self::error::ServiceError;
pub use self::service::{Builder, FactoryBuilder};

View File

@@ -1,416 +0,0 @@
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite, Decoder, Encoder, Framed};
use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory};
use either::Either;
use futures::{ready, Stream};
use pin_project::project;
use crate::connect::{Connect, ConnectResult};
use crate::dispatcher::Dispatcher;
use crate::error::ServiceError;
type RequestItem<U> = <U as Decoder>::Item;
type ResponseItem<U> = Option<<U as Encoder>::Item>;
/// Service builder - structure that follows the builder pattern
/// for building instances for framed services.
pub struct Builder<St, C, Io, Codec, Out> {
connect: C,
_t: PhantomData<(St, Io, Codec, Out)>,
}
impl<St, C, Io, Codec, Out> Builder<St, C, Io, Codec, Out>
where
C: Service<Request = Connect<Io, Codec>, Response = ConnectResult<Io, St, Codec, Out>>,
Io: AsyncRead + AsyncWrite,
Codec: Decoder + Encoder,
<Codec as Encoder>::Item: 'static,
<Codec as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
/// Construct framed handler service with specified connect service
pub fn new<F>(connect: F) -> Builder<St, C, Io, Codec, Out>
where
F: IntoService<C>,
Io: AsyncRead + AsyncWrite,
C: Service<Request = Connect<Io, Codec>, Response = ConnectResult<Io, St, Codec, Out>>,
Codec: Decoder + Encoder,
Out: Stream<Item = <Codec as Encoder>::Item>,
{
Builder {
connect: connect.into_service(),
_t: PhantomData,
}
}
/// Provide stream items handler service and construct service factory.
pub fn build<F, T>(self, service: F) -> FramedServiceImpl<St, C, T, Io, Codec, Out>
where
F: IntoServiceFactory<T>,
T: ServiceFactory<
Config = St,
Request = RequestItem<Codec>,
Response = ResponseItem<Codec>,
Error = C::Error,
InitError = C::Error,
>,
{
FramedServiceImpl {
connect: self.connect,
handler: Rc::new(service.into_factory()),
_t: PhantomData,
}
}
}
/// Service builder - structure that follows the builder pattern
/// for building instances for framed services.
pub struct FactoryBuilder<St, C, Io, Codec, Out> {
connect: C,
_t: PhantomData<(St, Io, Codec, Out)>,
}
impl<St, C, Io, Codec, Out> FactoryBuilder<St, C, Io, Codec, Out>
where
Io: AsyncRead + AsyncWrite,
C: ServiceFactory<
Config = (),
Request = Connect<Io, Codec>,
Response = ConnectResult<Io, St, Codec, Out>,
>,
Codec: Decoder + Encoder,
<Codec as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
/// Construct framed handler new service with specified connect service
pub fn new<F>(connect: F) -> FactoryBuilder<St, C, Io, Codec, Out>
where
F: IntoServiceFactory<C>,
Io: AsyncRead + AsyncWrite,
C: ServiceFactory<
Config = (),
Request = Connect<Io, Codec>,
Response = ConnectResult<Io, St, Codec, Out>,
>,
Codec: Decoder + Encoder,
Out: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
FactoryBuilder {
connect: connect.into_factory(),
_t: PhantomData,
}
}
pub fn build<F, T, Cfg>(self, service: F) -> FramedService<St, C, T, Io, Codec, Out, Cfg>
where
F: IntoServiceFactory<T>,
T: ServiceFactory<
Config = St,
Request = RequestItem<Codec>,
Response = ResponseItem<Codec>,
Error = C::Error,
InitError = C::Error,
>,
{
FramedService {
connect: self.connect,
handler: Rc::new(service.into_factory()),
_t: PhantomData,
}
}
}
pub struct FramedService<St, C, T, Io, Codec, Out, Cfg> {
connect: C,
handler: Rc<T>,
_t: PhantomData<(St, Io, Codec, Out, Cfg)>,
}
impl<St, C, T, Io, Codec, Out, Cfg> ServiceFactory
for FramedService<St, C, T, Io, Codec, Out, Cfg>
where
Io: AsyncRead + AsyncWrite,
C: ServiceFactory<
Config = (),
Request = Connect<Io, Codec>,
Response = ConnectResult<Io, St, Codec, Out>,
>,
T: ServiceFactory<
Config = St,
Request = RequestItem<Codec>,
Response = ResponseItem<Codec>,
Error = C::Error,
InitError = C::Error,
>,
<T::Service as Service>::Error: 'static,
<T::Service as Service>::Future: 'static,
Codec: Decoder + Encoder,
<Codec as Encoder>::Item: 'static,
<Codec as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
type Config = Cfg;
type Request = Io;
type Response = ();
type Error = ServiceError<C::Error, Codec>;
type InitError = C::InitError;
type Service = FramedServiceImpl<St, C::Service, T, Io, Codec, Out>;
type Future = FramedServiceResponse<St, C, T, Io, Codec, Out>;
fn new_service(&self, _: Cfg) -> Self::Future {
// create connect service and then create service impl
FramedServiceResponse {
fut: self.connect.new_service(()),
handler: self.handler.clone(),
}
}
}
#[pin_project::pin_project]
pub struct FramedServiceResponse<St, C, T, Io, Codec, Out>
where
Io: AsyncRead + AsyncWrite,
C: ServiceFactory<
Config = (),
Request = Connect<Io, Codec>,
Response = ConnectResult<Io, St, Codec, Out>,
>,
T: ServiceFactory<
Config = St,
Request = RequestItem<Codec>,
Response = ResponseItem<Codec>,
Error = C::Error,
InitError = C::Error,
>,
<T::Service as Service>::Error: 'static,
<T::Service as Service>::Future: 'static,
Codec: Decoder + Encoder,
<Codec as Encoder>::Item: 'static,
<Codec as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
#[pin]
fut: C::Future,
handler: Rc<T>,
}
impl<St, C, T, Io, Codec, Out> Future for FramedServiceResponse<St, C, T, Io, Codec, Out>
where
Io: AsyncRead + AsyncWrite,
C: ServiceFactory<
Config = (),
Request = Connect<Io, Codec>,
Response = ConnectResult<Io, St, Codec, Out>,
>,
T: ServiceFactory<
Config = St,
Request = RequestItem<Codec>,
Response = ResponseItem<Codec>,
Error = C::Error,
InitError = C::Error,
>,
<T::Service as Service>::Error: 'static,
<T::Service as Service>::Future: 'static,
Codec: Decoder + Encoder,
<Codec as Encoder>::Item: 'static,
<Codec as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
type Output = Result<FramedServiceImpl<St, C::Service, T, Io, Codec, Out>, C::InitError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let connect = ready!(this.fut.poll(cx))?;
Poll::Ready(Ok(FramedServiceImpl {
connect,
handler: this.handler.clone(),
_t: PhantomData,
}))
}
}
pub struct FramedServiceImpl<St, C, T, Io, Codec, Out> {
connect: C,
handler: Rc<T>,
_t: PhantomData<(St, Io, Codec, Out)>,
}
impl<St, C, T, Io, Codec, Out> Service for FramedServiceImpl<St, C, T, Io, Codec, Out>
where
Io: AsyncRead + AsyncWrite,
C: Service<Request = Connect<Io, Codec>, Response = ConnectResult<Io, St, Codec, Out>>,
T: ServiceFactory<
Config = St,
Request = RequestItem<Codec>,
Response = ResponseItem<Codec>,
Error = C::Error,
InitError = C::Error,
>,
<T::Service as Service>::Error: 'static,
<T::Service as Service>::Future: 'static,
Codec: Decoder + Encoder,
<Codec as Encoder>::Item: 'static,
<Codec as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
type Request = Io;
type Response = ();
type Error = ServiceError<C::Error, Codec>;
type Future = FramedServiceImplResponse<St, Io, Codec, Out, C, T>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.connect.poll_ready(cx).map_err(|e| e.into())
}
fn call(&mut self, req: Io) -> Self::Future {
FramedServiceImplResponse {
inner: FramedServiceImplResponseInner::Connect(
self.connect.call(Connect::new(req)),
self.handler.clone(),
),
}
}
}
#[pin_project::pin_project]
pub struct FramedServiceImplResponse<St, Io, Codec, Out, C, T>
where
C: Service<Request = Connect<Io, Codec>, Response = ConnectResult<Io, St, Codec, Out>>,
T: ServiceFactory<
Config = St,
Request = RequestItem<Codec>,
Response = ResponseItem<Codec>,
Error = C::Error,
InitError = C::Error,
>,
<T::Service as Service>::Error: 'static,
<T::Service as Service>::Future: 'static,
Io: AsyncRead + AsyncWrite,
Codec: Encoder + Decoder,
<Codec as Encoder>::Item: 'static,
<Codec as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
#[pin]
inner: FramedServiceImplResponseInner<St, Io, Codec, Out, C, T>,
}
impl<St, Io, Codec, Out, C, T> Future for FramedServiceImplResponse<St, Io, Codec, Out, C, T>
where
C: Service<Request = Connect<Io, Codec>, Response = ConnectResult<Io, St, Codec, Out>>,
T: ServiceFactory<
Config = St,
Request = RequestItem<Codec>,
Response = ResponseItem<Codec>,
Error = C::Error,
InitError = C::Error,
>,
<T::Service as Service>::Error: 'static,
<T::Service as Service>::Future: 'static,
Io: AsyncRead + AsyncWrite,
Codec: Encoder + Decoder,
<Codec as Encoder>::Item: 'static,
<Codec as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
type Output = Result<(), ServiceError<C::Error, Codec>>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
loop {
match this.inner.poll(cx) {
Either::Left(new) => {
this = self.as_mut().project();
this.inner.set(new)
}
Either::Right(poll) => return poll,
};
}
}
}
#[pin_project::pin_project]
enum FramedServiceImplResponseInner<St, Io, Codec, Out, C, T>
where
C: Service<Request = Connect<Io, Codec>, Response = ConnectResult<Io, St, Codec, Out>>,
T: ServiceFactory<
Config = St,
Request = RequestItem<Codec>,
Response = ResponseItem<Codec>,
Error = C::Error,
InitError = C::Error,
>,
<T::Service as Service>::Error: 'static,
<T::Service as Service>::Future: 'static,
Io: AsyncRead + AsyncWrite,
Codec: Encoder + Decoder,
<Codec as Encoder>::Item: 'static,
<Codec as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
Connect(#[pin] C::Future, Rc<T>),
Handler(#[pin] T::Future, Option<Framed<Io, Codec>>, Option<Out>),
Dispatcher(#[pin] Dispatcher<T::Service, Io, Codec, Out>),
}
impl<St, Io, Codec, Out, C, T> FramedServiceImplResponseInner<St, Io, Codec, Out, C, T>
where
C: Service<Request = Connect<Io, Codec>, Response = ConnectResult<Io, St, Codec, Out>>,
T: ServiceFactory<
Config = St,
Request = RequestItem<Codec>,
Response = ResponseItem<Codec>,
Error = C::Error,
InitError = C::Error,
>,
<T::Service as Service>::Error: 'static,
<T::Service as Service>::Future: 'static,
Io: AsyncRead + AsyncWrite,
Codec: Encoder + Decoder,
<Codec as Encoder>::Item: 'static,
<Codec as Encoder>::Error: std::fmt::Debug,
Out: Stream<Item = <Codec as Encoder>::Item> + Unpin,
{
#[project]
fn poll(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Either<
FramedServiceImplResponseInner<St, Io, Codec, Out, C, T>,
Poll<Result<(), ServiceError<C::Error, Codec>>>,
> {
#[project]
match self.project() {
FramedServiceImplResponseInner::Connect(fut, handler) => match fut.poll(cx) {
Poll::Ready(Ok(res)) => Either::Left(FramedServiceImplResponseInner::Handler(
handler.new_service(res.state),
Some(res.framed),
res.out,
)),
Poll::Pending => Either::Right(Poll::Pending),
Poll::Ready(Err(e)) => Either::Right(Poll::Ready(Err(e.into()))),
},
FramedServiceImplResponseInner::Handler(fut, framed, out) => {
match fut.poll(cx) {
Poll::Ready(Ok(handler)) => {
Either::Left(FramedServiceImplResponseInner::Dispatcher(
Dispatcher::new(framed.take().unwrap(), handler, out.take()),
))
}
Poll::Pending => Either::Right(Poll::Pending),
Poll::Ready(Err(e)) => Either::Right(Poll::Ready(Err(e.into()))),
}
}
FramedServiceImplResponseInner::Dispatcher(fut) => {
Either::Right(fut.poll(cx))
}
}
}
}

View File

@@ -1,55 +0,0 @@
use std::cell::Cell;
use std::rc::Rc;
use actix_codec::BytesCodec;
use actix_service::{fn_factory_with_config, fn_service, IntoService, Service};
use actix_testing::TestServer;
use actix_utils::mpsc;
use bytes::{Bytes, BytesMut};
use futures::future::ok;
use actix_ioframe::{Builder, Connect, FactoryBuilder};
#[derive(Clone)]
struct State(Option<mpsc::Sender<Bytes>>);
#[actix_rt::test]
async fn test_basic() {
let client_item = Rc::new(Cell::new(false));
let srv = TestServer::with(move || {
FactoryBuilder::new(fn_service(|conn: Connect<_, _>| {
ok(conn.codec(BytesCodec).state(State(None)))
}))
// echo
.build(fn_service(|t: BytesMut| ok(Some(t.freeze()))))
});
let item = client_item.clone();
let mut client = Builder::new(fn_service(move |conn: Connect<_, _>| {
async move {
let (tx, rx) = mpsc::channel();
let _ = tx.send(Bytes::from_static(b"Hello"));
Ok(conn.codec(BytesCodec).out(rx).state(State(Some(tx))))
}
}))
.build(fn_factory_with_config(move |mut cfg: State| {
let item = item.clone();
ok((move |t: BytesMut| {
assert_eq!(t.freeze(), Bytes::from_static(b"Hello"));
item.set(true);
// drop Sender, which will close connection
cfg.0.take();
ok::<_, ()>(None)
})
.into_service())
}));
let conn = actix_connect::default_connector()
.call(actix_connect::Connect::with(String::new(), srv.addr()))
.await
.unwrap();
client.call(conn.into_parts().0).await.unwrap();
assert!(client_item.get());
}

1
actix-macros/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/wip

13
actix-macros/CHANGES.md Normal file
View File

@@ -0,0 +1,13 @@
# CHANGES
## 0.1.3 - 2020-12-3
* Add `actix-reexport` feature
## 0.1.2 - 2020-05-18
### Changed
* Forward actix_rt::test arguments to test function [#127]
[#127]: https://github.com/actix/actix-net/pull/127

View File

@@ -1,14 +1,13 @@
[package]
name = "actix-macros"
version = "0.1.1"
version = "0.1.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix runtime macros"
repository = "https://github.com/actix/actix-net"
documentation = "https://docs.rs/actix-macros/"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
license = "MIT OR Apache-2.0"
edition = "2018"
workspace = ".."
[lib]
proc-macro = true
@@ -17,5 +16,11 @@ proc-macro = true
quote = "1.0.3"
syn = { version = "^1", features = ["full"] }
[features]
actix-reexport = []
[dev-dependencies]
actix-rt = { version = "1.0.0" }
actix-rt = "1.0"
futures-util = { version = "0.3", default-features = false }
trybuild = "1"

View File

@@ -33,14 +33,25 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
sig.asyncness = None;
(quote! {
#(#attrs)*
#vis #sig {
actix_rt::System::new(stringify!(#name))
.block_on(async move { #body })
}
})
.into()
if cfg!(feature = "actix-reexport") {
(quote! {
#(#attrs)*
#vis #sig {
actix::System::new(stringify!(#name))
.block_on(async move { #body })
}
})
.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.
@@ -55,12 +66,11 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
/// ```
#[proc_macro_attribute]
pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(item as syn::ItemFn);
let ret = &input.sig.output;
let name = &input.sig.ident;
let body = &input.block;
let mut input = syn::parse_macro_input!(item as syn::ItemFn);
let attrs = &input.attrs;
let vis = &input.vis;
let sig = &mut input.sig;
let body = &input.block;
let mut has_test_attr = false;
for attr in attrs {
@@ -69,7 +79,7 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
}
}
if input.sig.asyncness.is_none() {
if sig.asyncness.is_none() {
return syn::Error::new_spanned(
input.sig.fn_token,
format!("only async fn is supported, {}", input.sig.ident),
@@ -78,10 +88,12 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
.into();
}
sig.asyncness = None;
let result = if has_test_attr {
quote! {
#(#attrs)*
fn #name() #ret {
#vis #sig {
actix_rt::System::new("test")
.block_on(async { #body })
}
@@ -90,7 +102,7 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
quote! {
#[test]
#(#attrs)*
fn #name() #ret {
#vis #sig {
actix_rt::System::new("test")
.block_on(async { #body })
}

View File

@@ -0,0 +1,9 @@
#[test]
fn compile_macros() {
let t = trybuild::TestCases::new();
t.pass("tests/trybuild/main-01-basic.rs");
t.compile_fail("tests/trybuild/main-02-only-async.rs");
t.pass("tests/trybuild/test-01-basic.rs");
t.pass("tests/trybuild/test-02-keep-attrs.rs");
}

View File

@@ -0,0 +1,4 @@
#[actix_rt::main]
async fn main() {
println!("Hello world");
}

View File

@@ -0,0 +1,4 @@
#[actix_rt::main]
fn main() {
futures_util::future::ready(()).await
}

View File

@@ -0,0 +1,14 @@
error: only async fn is supported
--> $DIR/main-02-only-async.rs:2:1
|
2 | fn main() {
| ^^
error[E0601]: `main` function not found in crate `$CRATE`
--> $DIR/main-02-only-async.rs:1:1
|
1 | / #[actix_rt::main]
2 | | fn main() {
3 | | futures_util::future::ready(()).await
4 | | }
| |_^ consider adding a `main` function to `$DIR/tests/trybuild/main-02-only-async.rs`

View File

@@ -0,0 +1,6 @@
#[actix_rt::test]
async fn my_test() {
assert!(true);
}
fn main() {}

View File

@@ -0,0 +1,7 @@
#[actix_rt::test]
#[should_panic]
async fn my_test() {
todo!()
}
fn main() {}

View File

@@ -1,10 +1,33 @@
# Changes
## [TBD] - [TBD]
## Unreleased - 2020-xx-xx
- Expose `System::is_set` to check if current system is running
### Added
- Add `Arbiter::local_join` associated function to get be able to `await` for spawned futures
* Add `System::attach_to_tokio` method. [#173]
## [1.1.1] - 2020-04-30
### Fixed
* Fix memory leak due to [#94] (see [#129] for more detail)
[#129]: https://github.com/actix/actix-net/issues/129
## [1.1.0] - 2020-04-08
**This version has been yanked.**
### Added
* Expose `System::is_set` to check if current system has ben started [#99]
* Add `Arbiter::is_running` to check if event loop is running [#124]
* Add `Arbiter::local_join` associated function
to get be able to `await` for spawned futures [#94]
[#94]: https://github.com/actix/actix-net/pull/94
[#99]: https://github.com/actix/actix-net/pull/99
[#124]: https://github.com/actix/actix-net/pull/124
## [1.0.0] - 2019-12-11

View File

@@ -1,6 +1,6 @@
[package]
name = "actix-rt"
version = "1.0.0"
version = "1.1.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix runtime"
keywords = ["network", "framework", "async", "futures"]
@@ -8,7 +8,7 @@ homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-rt/"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
@@ -17,8 +17,11 @@ path = "src/lib.rs"
[dependencies]
actix-macros = "0.1.0"
actix-threadpool = "0.3"
futures-channel = { version = "0.3.1", default-features = false }
futures-util = { version = "0.3.1", default-features = false }
copyless = "0.1.4"
futures-channel = "0.3.4"
futures-util = { version = "0.3.4", default-features = false, features = ["alloc"] }
smallvec = "1"
tokio = { version = "0.2.6", default-features = false, features = ["rt-core", "rt-util", "io-driver", "tcp", "uds", "udp", "time", "signal", "stream"] }
[dev-dependencies]
tokio = { version = "0.2.6", features = ["full"] }

View File

@@ -8,20 +8,24 @@ use std::{fmt, thread};
use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures_channel::oneshot::{channel, Canceled, Sender};
use futures_util::{future::{self, Future, FutureExt}, stream::Stream};
use futures_util::{
future::{self, Future, FutureExt},
stream::Stream,
};
use crate::runtime::Runtime;
use crate::system::System;
use copyless::BoxHelper;
use smallvec::SmallVec;
pub use tokio::task::JoinHandle;
thread_local!(
static ADDR: RefCell<Option<Arbiter>> = RefCell::new(None);
static RUNNING: Cell<bool> = Cell::new(false);
static Q: RefCell<Vec<Pin<Box<dyn Future<Output = ()>>>>> = RefCell::new(Vec::new());
static PENDING: RefCell<Vec<JoinHandle<()>>> = RefCell::new(Vec::new());
static PENDING: RefCell<SmallVec<[JoinHandle<()>; 8]>> = RefCell::new(SmallVec::new());
static STORAGE: RefCell<HashMap<TypeId, Box<dyn Any>>> = RefCell::new(HashMap::new());
);
@@ -86,6 +90,11 @@ impl Arbiter {
})
}
/// Check if current arbiter is running.
pub fn is_running() -> bool {
RUNNING.with(|cell| cell.get())
}
/// Stop arbiter from continuing it's event loop.
pub fn stop(&self) {
let _ = self.sender.unbounded_send(ArbiterCommand::Stop);
@@ -125,10 +134,7 @@ impl Arbiter {
.unbounded_send(SystemCommand::RegisterArbiter(id, arb));
// run loop
let _ = match rt.block_on(stop_rx) {
Ok(code) => code,
Err(_) => 1,
};
let _ = rt.block_on(stop_rx).unwrap_or(1);
// unregister arbiter
let _ = System::current()
@@ -173,15 +179,20 @@ impl Arbiter {
RUNNING.with(move |cell| {
if cell.get() {
// Spawn the future on running executor
PENDING.with(move |cell| {
cell.borrow_mut().push(tokio::task::spawn_local(future));
})
let len = PENDING.with(move |cell| {
let mut p = cell.borrow_mut();
p.push(tokio::task::spawn_local(future));
p.len()
});
if len > 7 {
// Before reaching the inline size
tokio::task::spawn_local(CleanupPending);
}
} else {
// Box the future and push it to the queue, this results in double boxing
// because the executor boxes the future again, but works for now
Q.with(move |cell| {
cell.borrow_mut()
.push(Pin::from(Box::alloc().init(future)))
cell.borrow_mut().push(Pin::from(Box::alloc().init(future)))
});
}
});
@@ -304,12 +315,36 @@ impl Arbiter {
/// have completed.
pub fn local_join() -> impl Future<Output = ()> {
PENDING.with(move |cell| {
let current = cell.replace(Vec::new());
let current = cell.replace(SmallVec::new());
future::join_all(current).map(|_| ())
})
}
}
/// Future used for cleaning-up already finished `JoinHandle`s
/// from the `PENDING` list so the vector doesn't grow indefinitely
struct CleanupPending;
impl Future for CleanupPending {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
PENDING.with(move |cell| {
let mut pending = cell.borrow_mut();
let mut i = 0;
while i != pending.len() {
if let Poll::Ready(_) = Pin::new(&mut pending[i]).poll(cx) {
pending.remove(i);
} else {
i += 1;
}
}
});
Poll::Ready(())
}
}
struct ArbiterController {
stop: Option<Sender<i32>>,
rx: UnboundedReceiver<ArbiterCommand>,
@@ -343,9 +378,15 @@ impl Future for ArbiterController {
return Poll::Ready(());
}
ArbiterCommand::Execute(fut) => {
PENDING.with(move |cell| {
cell.borrow_mut().push(tokio::task::spawn_local(fut));
let len = PENDING.with(move |cell| {
let mut p = cell.borrow_mut();
p.push(tokio::task::spawn_local(fut));
p.len()
});
if len > 7 {
// Before reaching the inline size
tokio::task::spawn_local(CleanupPending);
}
}
ArbiterCommand::ExecuteFn(f) => {
f.call_box();

View File

@@ -137,7 +137,7 @@ impl AsyncSystemRunner {
Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
};
Arbiter::stop_system();
return res;
res
}
})
.flatten()

View File

@@ -15,9 +15,6 @@ pub use self::builder::{Builder, SystemRunner};
pub use self::runtime::Runtime;
pub use self::system::System;
#[doc(hidden)]
pub use actix_threadpool as blocking;
/// Spawns a future on the current arbiter.
///
/// # Panics

View File

@@ -57,10 +57,59 @@ impl System {
Self::builder().name(name).build()
}
#[allow(clippy::new_ret_no_self)]
/// Create new system using provided tokio Handle.
/// Create new system using provided tokio `LocalSet`.
///
/// 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`]).
///
/// [`Arbiter`]: struct.Arbiter.html
/// [`tokio_compat`]: https://crates.io/crates/tokio-compat
///
/// # Examples
///
/// ```
/// 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 mut runtime = tokio::runtime::Builder::new()
/// .core_threads(2)
/// .enable_all()
/// .threaded_scheduler()
/// .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>>(
name: T,
local: &LocalSet,
@@ -71,6 +120,77 @@ impl System {
.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`).
///
/// [`Arbiter`]: struct.Arbiter.html
/// [`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
///
/// ```
/// 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()
/// .core_threads(2)
/// .enable_all()
/// .threaded_scheduler()
/// .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>,
mut 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.
pub fn current() -> System {
CURRENT.with(|cell| match *cell.borrow() {
@@ -79,7 +199,7 @@ impl System {
})
}
/// Check if current system is running.
/// Check if current system is set, i.e., as already been started.
pub fn is_set() -> bool {
CURRENT.with(|cell| cell.borrow().is_some())
}

View File

@@ -1,5 +1,19 @@
use std::time::{Duration, Instant};
#[test]
fn start_and_stop() {
actix_rt::System::new("start_and_stop").block_on(async move {
assert!(
actix_rt::Arbiter::is_running(),
"System doesn't seem to have started"
);
});
assert!(
!actix_rt::Arbiter::is_running(),
"System doesn't seem to have stopped"
);
}
#[test]
fn await_for_timer() {
let time = Duration::from_secs(2);

View File

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

View File

@@ -1,17 +1,16 @@
[package]
name = "actix-server"
version = "1.0.2"
version = "1.0.4"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix server - General purpose tcp server"
description = "General purpose TCP server built for the Actix ecosystem"
keywords = ["network", "framework", "async", "futures"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-server/"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"]
license = "MIT OR Apache-2.0"
exclude = [".gitignore", ".cargo/config"]
edition = "2018"
workspace = ".."
[lib]
name = "actix_server"
@@ -21,16 +20,17 @@ path = "src/lib.rs"
default = []
[dependencies]
actix-service = "1.0.1"
actix-rt = "1.0.0"
actix-codec = "0.2.0"
actix-utils = "1.0.4"
actix-service = "1.0.6"
actix-rt = "1.1.1"
actix-codec = "0.3.0"
actix-utils = "2.0.0"
log = "0.4"
num_cpus = "1.11"
num_cpus = "1.13"
mio = "0.6.19"
net2 = "0.2"
futures = "0.3.1"
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"
# unix domain sockets
@@ -40,4 +40,5 @@ mio-uds = { version = "0.6.7" }
[dev-dependencies]
bytes = "0.5"
env_logger = "0.7"
actix-testing = "1.0.0"
actix-testing = "1.0.0"
tokio = { version = "0.2", 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

@@ -381,6 +381,7 @@ impl Accept {
self.backpressure = true;
for (_, info) in self.sockets.iter() {
let _ = self.poll.deregister(&info.sock);
info!("Accepting connections on {} has been paused", info.addr);
}
}
}

View File

@@ -6,13 +6,13 @@ use std::{io, mem, net};
use actix_rt::net::TcpStream;
use actix_rt::time::{delay_until, Instant};
use actix_rt::{spawn, System};
use futures::channel::mpsc::{unbounded, UnboundedReceiver};
use futures::channel::oneshot;
use futures::future::ready;
use futures::stream::FuturesUnordered;
use futures::{ready, Future, FutureExt, Stream, StreamExt};
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 net2::TcpBuilder;
use socket2::{Domain, Protocol, Socket, Type};
use crate::accept::{AcceptLoop, AcceptNotify, Command};
use crate::config::{ConfiguredService, ServiceConfig};
@@ -72,8 +72,9 @@ impl ServerBuilder {
/// Set number of workers to start.
///
/// By default server uses number of available logical cpu as workers
/// count.
/// count. Workers must be greater than 0.
pub fn workers(mut self, num: usize) -> Self {
assert_ne!(num, 0, "workers must be greater than 0");
self.threads = num;
self
}
@@ -276,7 +277,7 @@ impl ServerBuilder {
info!("Starting \"{}\" service on {}", sock.1, sock.2);
}
self.accept.start(
mem::replace(&mut self.sockets, Vec::new())
mem::take(&mut self.sockets)
.into_iter()
.map(|t| (t.0, t.2))
.collect(),
@@ -355,7 +356,7 @@ impl ServerBuilder {
// stop accept thread
self.accept.send(Command::Stop);
let notify = std::mem::replace(&mut self.notify, Vec::new());
let notify = std::mem::take(&mut self.notify);
// stop workers
if !self.workers.is_empty() && graceful {
@@ -381,7 +382,7 @@ impl ServerBuilder {
.await;
System::current().stop();
}
.boxed(),
.boxed(),
);
}
ready(())
@@ -487,11 +488,13 @@ pub(super) fn bind_addr<S: net::ToSocketAddrs>(
}
fn create_tcp_listener(addr: net::SocketAddr, backlog: i32) -> io::Result<net::TcpListener> {
let builder = match addr {
net::SocketAddr::V4(_) => TcpBuilder::new_v4()?,
net::SocketAddr::V6(_) => TcpBuilder::new_v6()?,
let domain = match addr {
net::SocketAddr::V4(_) => Domain::ipv4(),
net::SocketAddr::V6(_) => Domain::ipv6(),
};
builder.reuse_address(true)?;
builder.bind(addr)?;
Ok(builder.listen(backlog)?)
let socket = Socket::new(domain, Type::stream(), Some(Protocol::tcp()))?;
socket.set_reuse_address(true)?;
socket.bind(&addr.into())?;
socket.listen(backlog)?;
Ok(socket.into_tcp_listener())
}

View File

@@ -4,7 +4,7 @@ use std::{fmt, io, net};
use actix_rt::net::TcpStream;
use actix_service as actix;
use actix_utils::counter::CounterGuard;
use futures::future::{ok, Future, FutureExt, LocalBoxFuture};
use futures_util::future::{ok, Future, FutureExt, LocalBoxFuture};
use log::error;
use super::builder::bind_addr;
@@ -151,7 +151,7 @@ impl InternalServiceFactory for ConfiguredService {
));
};
}
return Ok(res);
Ok(res)
}
.boxed_local()
}
@@ -218,7 +218,7 @@ impl ServiceRuntime {
// let name = name.to_owned();
if let Some(token) = self.names.get(name) {
self.services.insert(
token.clone(),
*token,
Box::new(ServiceFactory {
inner: service.into_factory(),
}),
@@ -272,13 +272,13 @@ where
fn new_service(&self, _: ()) -> Self::Future {
let fut = self.inner.new_service(());
async move {
return match fut.await {
match fut.await {
Ok(s) => Ok(Box::new(StreamService::new(s)) as BoxedServerService),
Err(e) => {
error!("Can not construct service: {:?}", e);
Err(())
}
};
}
}
.boxed_local()
}

View File

@@ -1,6 +1,6 @@
//! General purpose tcp server
#![deny(rust_2018_idioms, warnings)]
#![allow(clippy::type_complexity)]
//! General purpose TCP server.
#![deny(rust_2018_idioms)]
mod accept;
mod builder;
@@ -19,7 +19,7 @@ pub use self::service::ServiceFactory;
#[doc(hidden)]
pub use self::socket::FromStream;
/// Socket id token
/// Socket ID token
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) struct Token(usize);

View File

@@ -3,9 +3,9 @@ use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures::channel::mpsc::UnboundedSender;
use futures::channel::oneshot;
use futures::FutureExt;
use futures_channel::mpsc::UnboundedSender;
use futures_channel::oneshot;
use futures_util::FutureExt;
use crate::builder::ServerBuilder;
use crate::signals::Signal;

View File

@@ -6,8 +6,8 @@ use std::time::Duration;
use actix_rt::spawn;
use actix_service::{self as actix, Service, ServiceFactory as ActixServiceFactory};
use actix_utils::counter::CounterGuard;
use futures::future::{err, ok, LocalBoxFuture, Ready};
use futures::{FutureExt, TryFutureExt};
use futures_util::future::{err, ok, LocalBoxFuture, Ready};
use futures_util::{FutureExt, TryFutureExt};
use log::error;
use super::Token;
@@ -17,8 +17,10 @@ use crate::socket::{FromStream, StdStream};
pub(crate) enum ServerMessage {
/// New stream
Connect(StdStream),
/// Gracefull shutdown
/// Gracefully shutdown
Shutdown(Duration),
/// Force shutdown
ForceShutdown,
}

View File

@@ -3,7 +3,7 @@ use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures::future::lazy;
use futures_util::future::lazy;
use crate::server::Server;

View File

@@ -7,10 +7,10 @@ use std::time;
use actix_rt::time::{delay_until, Delay, Instant};
use actix_rt::{spawn, Arbiter};
use actix_utils::counter::Counter;
use futures::channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures::channel::oneshot;
use futures::future::{join_all, LocalBoxFuture, MapOk};
use futures::{Future, FutureExt, Stream, TryFutureExt};
use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
use futures_channel::oneshot;
use futures_util::future::{join_all, LocalBoxFuture, MapOk};
use futures_util::{future::Future, stream::Stream, FutureExt, TryFutureExt};
use log::{error, info, trace};
use crate::accept::AcceptNotify;
@@ -303,6 +303,7 @@ enum WorkerState {
Restarting(
usize,
Token,
#[allow(clippy::type_complexity)]
Pin<Box<dyn Future<Output = Result<Vec<(Token, BoxedServerService)>, ()>>>>,
),
Shutdown(

View File

@@ -4,15 +4,15 @@ use std::{net, thread, time};
use actix_server::Server;
use actix_service::fn_service;
use futures::future::{lazy, ok};
use net2::TcpBuilder;
use futures_util::future::{lazy, ok};
use socket2::{Domain, Protocol, Socket, Type};
fn unused_addr() -> net::SocketAddr {
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = TcpBuilder::new_v4().unwrap();
socket.bind(&addr).unwrap();
socket.reuse_address(true).unwrap();
let tcp = socket.to_tcp_listener().unwrap();
let socket = Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap();
socket.bind(&addr.into()).unwrap();
socket.set_reuse_address(true).unwrap();
let tcp = socket.into_tcp_listener();
tcp.local_addr().unwrap()
}
@@ -71,7 +71,7 @@ fn test_start() {
use actix_codec::{BytesCodec, Framed};
use actix_rt::net::TcpStream;
use bytes::Bytes;
use futures::SinkExt;
use futures_util::sink::SinkExt;
use std::io::Read;
let addr = unused_addr();
@@ -83,12 +83,10 @@ fn test_start() {
.backlog(100)
.disable_signals()
.bind("test", addr, move || {
fn_service(|io: TcpStream| {
async move {
let mut f = Framed::new(io, BytesCodec);
f.send(Bytes::from_static(b"test")).await.unwrap();
Ok::<_, ()>(())
}
fn_service(|io: TcpStream| async move {
let mut f = Framed::new(io, BytesCodec);
f.send(Bytes::from_static(b"test")).await.unwrap();
Ok::<_, ()>(())
})
})
.unwrap()

View File

@@ -1,5 +1,14 @@
# Changes
## Unreleased - 2020-xx-xx
* Upgrade `pin-project` to `1.0`.
## 1.0.6 - 2020-08-09
### Fixed
* Removed unsound custom Cell implementation that allowed obtaining several mutable references to the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through service combinators. Attempts to acquire several mutable references to the same data will instead result in a panic.
## [1.0.5] - 2020-01-16
### Fixed

View File

@@ -1,14 +1,15 @@
[package]
name = "actix-service"
version = "1.0.5"
version = "1.0.6"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix service"
keywords = ["network", "framework", "async", "futures"]
description = "Service trait and combinators for representing asynchronous request/response operations."
keywords = ["network", "framework", "async", "futures", "service"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-service/"
documentation = "https://docs.rs/actix-service"
readme = "README.md"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
@@ -17,7 +18,7 @@ path = "src/lib.rs"
[dependencies]
futures-util = "0.3.1"
pin-project = "0.4.6"
pin-project = "1.0.0"
[dev-dependencies]
actix-rt = "1.0.0"

7
actix-service/README.md Normal file
View File

@@ -0,0 +1,7 @@
# actix-service
> Service trait and combinators for representing asynchronous request/response operations.
See documentation for detailed explanations these components: [https://docs.rs/actix-service][docs].
[docs]: https://docs.rs/actix-service

View File

@@ -1,28 +1,26 @@
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::task::{Context, Poll};
use std::rc::Rc;
use actix_service::{Service};
use actix_service::IntoService;
use std::future::Future;
use std::pin::Pin;
use futures_util::future::TryFutureExt;
use actix_service::boxed::BoxFuture;
use std::rc::Rc;
use std::task::{Context, Poll};
/*
* Test services A,B for AndThen service implementations
*/
async fn svc1(_: ()) -> Result<usize, ()> {
Ok(1)
Ok(1)
}
async fn svc2(req: usize) -> Result<usize, ()> {
Ok(req + 1)
Ok(req + 1)
}
/*
@@ -30,45 +28,44 @@ async fn svc2(req: usize) -> Result<usize, ()> {
* Cut down version of actix_service::AndThenService based on actix-service::Cell
*/
struct AndThenUC<A, B>(Rc<UnsafeCell<(A, B)>>);
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> 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> Clone for AndThenUC<A, B> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<A,B> Service for AndThenUC<A,B>
impl<A, B> Service for AndThenUC<A, B>
where
A: Service,
B: Service<Request = A::Response, Error = A::Error>
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>;
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 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()))
}
}
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]
@@ -81,7 +78,7 @@ where
state: State<A, B>,
}
#[pin_project::pin_project]
#[pin_project::pin_project(project = StateProj)]
enum State<A, B>
where
A: Service,
@@ -99,13 +96,11 @@ where
{
type Output = Result<B::Response, A::Error>;
#[pin_project::project]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
#[project]
match this.state.as_mut().project() {
State::A(fut, b) => match fut.poll(cx)? {
StateProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let b = b.take().unwrap();
this.state.set(State::Empty); // drop fut A
@@ -115,163 +110,160 @@ where
}
Poll::Pending => Poll::Pending,
},
State::B(fut) => fut.poll(cx).map(|r| {
StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty);
r
}),
State::Empty => panic!("future must not be polled after it returned `Poll::Ready`"),
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)>>);
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]
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>;
#[pin_project::project]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
#[project]
match this.state.as_mut().project() {
StateRC::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,
},
StateRC::B(fut) => fut.poll(cx).map(|r| {
this.state.set(StateRC::Empty);
r
}),
StateRC::Empty => panic!("future must not be polled after it returned `Poll::Ready`"),
}
}
}
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))))
}
}
/*
* AndThenRCFuture - AndThen service based on RefCell
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)>>);
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)
)
}
}
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:
@@ -300,25 +292,41 @@ where
// 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
});
let elapsed = start.elapsed();
rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await });
// check that at least first request succeeded
elapsed
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");
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,73 +1,72 @@
use actix_service::Service;
use criterion::{criterion_main, Criterion};
use futures_util::future::join_all;
use std::cell::{RefCell, UnsafeCell};
use std::task::{Context, Poll};
use std::rc::Rc;
use actix_service::{Service};
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)))
}
fn default() -> Self {
Self(Rc::new(UnsafeCell::new(0)))
}
}
impl Clone for SrvUC {
fn clone(&self) -> Self {
Self(self.0.clone())
}
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, ()>>;
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 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() })
}
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)))
}
fn default() -> Self {
Self(Rc::new(RefCell::new(0)))
}
}
impl Clone for SrvRC {
fn clone(&self) -> Self {
Self(self.0.clone())
}
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, ()>>;
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 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())
}
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
@@ -95,17 +94,13 @@ where
// 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
});
let elapsed = start.elapsed();
rt.block_on(async move { join_all(srvs.iter_mut().map(|srv| srv.call(()))).await });
// check that at least first request succeeded
elapsed
start.elapsed()
})
});
}
pub fn service_benches() {
let mut criterion: ::criterion::Criterion<_> =
::criterion::Criterion::default().configure_from_args();

View File

@@ -1,16 +1,16 @@
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use super::{Service, ServiceFactory};
use crate::cell::Cell;
/// Service for the `and_then` combinator, chaining a computation onto the end
/// of another service which completes successfully.
///
/// This is created by the `ServiceExt::and_then` method.
pub(crate) struct AndThenService<A, B>(Cell<(A, B)>);
/// This is created by the `Pipeline::and_then` method.
pub(crate) struct AndThenService<A, B>(Rc<RefCell<(A, B)>>);
impl<A, B> AndThenService<A, B> {
/// Create new `AndThen` combinator
@@ -19,7 +19,7 @@ impl<A, B> AndThenService<A, B> {
A: Service,
B: Service<Request = A::Response, Error = A::Error>,
{
Self(Cell::new((a, b)))
Self(Rc::new(RefCell::new((a, b))))
}
}
@@ -40,7 +40,7 @@ where
type Future = AndThenServiceResponse<A, B>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let srv = self.0.get_mut();
let mut srv = self.0.borrow_mut();
let not_ready = !srv.0.poll_ready(cx)?.is_ready();
if !srv.1.poll_ready(cx)?.is_ready() || not_ready {
Poll::Pending
@@ -51,7 +51,7 @@ where
fn call(&mut self, req: A::Request) -> Self::Future {
AndThenServiceResponse {
state: State::A(self.0.get_mut().0.call(req), Some(self.0.clone())),
state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())),
}
}
}
@@ -66,13 +66,13 @@ where
state: State<A, B>,
}
#[pin_project::pin_project]
#[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<Cell<(A, B)>>),
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
@@ -84,27 +84,27 @@ where
{
type Output = Result<B::Response, A::Error>;
#[pin_project::project]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
#[project]
match this.state.as_mut().project() {
State::A(fut, b) => match fut.poll(cx)? {
StateProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let mut b = b.take().unwrap();
let b = b.take().unwrap();
this.state.set(State::Empty); // drop fut A
let fut = b.get_mut().1.call(res);
let fut = b.borrow_mut().1.call(res);
this.state.set(State::B(fut));
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
State::B(fut) => fut.poll(cx).map(|r| {
StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty);
r
}),
State::Empty => panic!("future must not be polled after it returned `Poll::Ready`"),
StateProj::Empty => {
panic!("future must not be polled after it returned `Poll::Ready`")
}
}
}
}

View File

@@ -1,10 +1,10 @@
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::cell::Cell;
use crate::{Service, ServiceFactory};
/// `Apply` service combinator
@@ -16,7 +16,7 @@ where
Fut: Future<Output = Result<Res, Err>>,
Err: From<A::Error> + From<B::Error>,
{
srv: Cell<(A, B, F)>,
srv: Rc<RefCell<(A, B, F)>>,
r: PhantomData<(Fut, Res, Err)>,
}
@@ -31,7 +31,7 @@ where
/// Create new `Apply` combinator
pub(crate) fn new(a: A, b: B, f: F) -> Self {
Self {
srv: Cell::new((a, b, f)),
srv: Rc::new(RefCell::new((a, b, f))),
r: PhantomData,
}
}
@@ -67,7 +67,7 @@ where
type Future = AndThenApplyFnFuture<A, B, F, Fut, Res, Err>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let inner = self.srv.get_mut();
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
@@ -77,7 +77,7 @@ where
}
fn call(&mut self, req: A::Request) -> Self::Future {
let fut = self.srv.get_mut().0.call(req);
let fut = self.srv.borrow_mut().0.call(req);
AndThenApplyFnFuture {
state: State::A(fut, Some(self.srv.clone())),
}
@@ -98,7 +98,7 @@ where
state: State<A, B, F, Fut, Res, Err>,
}
#[pin_project::pin_project]
#[pin_project::pin_project(project = StateProj)]
enum State<A, B, F, Fut, Res, Err>
where
A: Service,
@@ -108,7 +108,7 @@ where
Err: From<A::Error>,
Err: From<B::Error>,
{
A(#[pin] A::Future, Option<Cell<(A, B, F)>>),
A(#[pin] A::Future, Option<Rc<RefCell<(A, B, F)>>>),
B(#[pin] Fut),
Empty,
}
@@ -123,28 +123,28 @@ where
{
type Output = Result<Res, Err>;
#[pin_project::project]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
#[project]
match this.state.as_mut().project() {
State::A(fut, b) => match fut.poll(cx)? {
StateProj::A(fut, b) => match fut.poll(cx)? {
Poll::Ready(res) => {
let mut b = b.take().unwrap();
let b = b.take().unwrap();
this.state.set(State::Empty);
let b = b.get_mut();
let fut = (&mut b.2)(res, &mut b.1);
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,
},
State::B(fut) => fut.poll(cx).map(|r| {
StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty);
r
}),
State::Empty => panic!("future must not be polled after it returned `Poll::Ready`"),
StateProj::Empty => {
panic!("future must not be polled after it returned `Poll::Ready`")
}
}
}
}
@@ -257,11 +257,11 @@ where
if this.a.is_some() && this.b.is_some() {
Poll::Ready(Ok(AndThenApplyFn {
srv: Cell::new((
srv: Rc::new(RefCell::new((
this.a.take().unwrap(),
this.b.take().unwrap(),
this.f.clone(),
)),
))),
r: PhantomData,
}))
} else {
@@ -298,10 +298,9 @@ mod tests {
#[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 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(())));
@@ -312,11 +311,10 @@ mod tests {
#[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 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(())));

View File

@@ -5,7 +5,7 @@ use std::task::{Context, Poll};
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>
where
T: Service<Error = Err>,
@@ -16,7 +16,7 @@ where
Apply::new(service.into_service(), f)
}
/// 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,
f: F,

View File

@@ -1,9 +1,10 @@
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::cell::Cell;
use crate::{Service, ServiceFactory};
/// Convert `Fn(Config, &mut Service1) -> Future<Service2>` fn to a service factory
@@ -26,7 +27,7 @@ where
S: Service,
{
ApplyConfigService {
srv: Cell::new((srv, f)),
srv: Rc::new(RefCell::new((srv, f))),
_t: PhantomData,
}
}
@@ -53,7 +54,7 @@ where
S: Service,
{
ApplyConfigServiceFactory {
srv: Cell::new((factory, f)),
srv: Rc::new(RefCell::new((factory, f))),
_t: PhantomData,
}
}
@@ -66,7 +67,7 @@ where
R: Future<Output = Result<S, E>>,
S: Service,
{
srv: Cell<(T, F)>,
srv: Rc<RefCell<(T, F)>>,
_t: PhantomData<(C, R, S)>,
}
@@ -102,10 +103,8 @@ where
type Future = R;
fn new_service(&self, cfg: C) -> Self::Future {
unsafe {
let srv = self.srv.get_mut_unsafe();
(srv.1)(cfg, &mut srv.0)
}
let (t, f) = &mut *self.srv.borrow_mut();
f(cfg, t)
}
}
@@ -117,7 +116,7 @@ where
R: Future<Output = Result<S, T::InitError>>,
S: Service,
{
srv: Cell<(T, F)>,
srv: Rc<RefCell<(T, F)>>,
_t: PhantomData<(C, R, S)>,
}
@@ -157,7 +156,7 @@ where
ApplyConfigServiceFactoryResponse {
cfg: Some(cfg),
store: self.srv.clone(),
state: State::A(self.srv.get_ref().0.new_service(())),
state: State::A(self.srv.borrow().0.new_service(())),
}
}
}
@@ -172,12 +171,12 @@ where
S: Service,
{
cfg: Option<C>,
store: Cell<(T, F)>,
store: Rc<RefCell<(T, F)>>,
#[pin]
state: State<T, R, S>,
}
#[pin_project::pin_project]
#[pin_project::pin_project(project = StateProj)]
enum State<T, R, S>
where
T: ServiceFactory<Config = ()>,
@@ -200,28 +199,29 @@ where
{
type Output = Result<S, T::InitError>;
#[pin_project::project]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
#[project]
match this.state.as_mut().project() {
State::A(fut) => match fut.poll(cx)? {
StateProj::A(fut) => match fut.poll(cx)? {
Poll::Pending => Poll::Pending,
Poll::Ready(srv) => {
this.state.set(State::B(srv));
self.poll(cx)
}
},
State::B(srv) => match srv.poll_ready(cx)? {
StateProj::B(srv) => match srv.poll_ready(cx)? {
Poll::Ready(_) => {
let fut = (this.store.get_mut().1)(this.cfg.take().unwrap(), srv);
this.state.set(State::C(fut));
{
let (_, f) = &mut *this.store.borrow_mut();
let fut = f(this.cfg.take().unwrap(), srv);
this.state.set(State::C(fut));
}
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
State::C(fut) => fut.poll(cx),
StateProj::C(fut) => fut.poll(cx),
}
}
}

View File

@@ -1,57 +0,0 @@
//! Custom cell impl, internal use only
use std::task::{Context, Poll};
use std::{cell::UnsafeCell, fmt, rc::Rc};
pub(crate) struct Cell<T> {
inner: Rc<UnsafeCell<T>>,
}
impl<T> Clone for Cell<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<T: fmt::Debug> fmt::Debug for Cell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl<T> Cell<T> {
pub(crate) fn new(inner: T) -> Self {
Self {
inner: Rc::new(UnsafeCell::new(inner)),
}
}
pub(crate) fn get_ref(&self) -> &T {
unsafe { &*self.inner.as_ref().get() }
}
pub(crate) fn get_mut(&mut self) -> &mut T {
unsafe { &mut *self.inner.as_ref().get() }
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn get_mut_unsafe(&self) -> &mut T {
&mut *self.inner.as_ref().get()
}
}
impl<T: crate::Service> crate::Service for Cell<T> {
type Request = T::Request;
type Response = T::Response;
type Error = T::Error;
type Future = T::Future;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.get_mut().poll_ready(cx)
}
fn call(&mut self, req: Self::Request) -> Self::Future {
self.get_mut().call(req)
}
}

View File

@@ -29,7 +29,7 @@ where
/// /// Service that divides two usize values.
/// async fn div((x, y): (usize, usize)) -> Result<usize, io::Error> {
/// if y == 0 {
/// Err(io::Error::new(io::ErrorKind::Other, "divide by zdro"))
/// Err(io::Error::new(io::ErrorKind::Other, "divide by zero"))
/// } else {
/// Ok(x / y)
/// }

View File

@@ -1,3 +1,5 @@
//! See [`Service`](trait.Service.html) docs for information on this crate's foundational trait.
#![deny(rust_2018_idioms, warnings)]
#![allow(clippy::type_complexity)]
@@ -12,7 +14,6 @@ mod and_then_apply_fn;
mod apply;
mod apply_cfg;
pub mod boxed;
mod cell;
mod fn_service;
mod map;
mod map_config;
@@ -30,21 +31,23 @@ pub use self::map_config::{map_config, unit_config};
pub use self::pipeline::{pipeline, pipeline_factory, Pipeline, PipelineFactory};
pub use self::transform::{apply, Transform};
/// An asynchronous function from `Request` to a `Response`.
/// An asynchronous operation from `Request` to a `Response`.
///
/// `Service` represents a service that represanting interation, taking requests and giving back
/// replies. You can think about service as a function with one argument and result as a return
/// type. In general form it looks like `async fn(Req) -> Result<Res, Err>`. `Service`
/// trait just generalizing form of this function. Each parameter described as an assotiated type.
/// The `Service` trait models a request/response interaction, receiving requests and returning
/// replies. You can think about a service as a function with one argument that returns some result
/// asynchronously. Conceptually, the operation looks like this:
///
/// Services provides a symmetric and uniform API, same abstractions represents
/// clients and servers. Services describe only `transforamtion` operation
/// which encorouge to simplify api surface and phrases `value transformation`.
/// That leads to simplier design of each service. That also allows better testability
/// and better composition.
/// ```rust,ignore
/// async fn(Request) -> Result<Response, Err>
/// ```
///
/// Services could be represented in several different forms. In general,
/// Service is a type that implements `Service` trait.
/// The `Service` trait just generalizes this form where each parameter is described as an
/// associated type on the trait. Services can also have mutable state that influence computation.
///
/// `Service` provides a symmetric and uniform API; the same abstractions can be used to represent
/// both clients and servers. Services describe only _transformation_ operations which encourage
/// simple API surfaces. This leads to simpler design of each service, improves test-ability and
/// makes composition easier.
///
/// ```rust,ignore
/// struct MyService;
@@ -53,7 +56,7 @@ pub use self::transform::{apply, Transform};
/// type Request = u8;
/// type Response = u64;
/// type Error = MyError;
/// type Future = Pin<Box<Future<Output=Result<Self::Response, Self::Error>>>;
/// type Future = Pin<Box<Future<Output=Result<Self::Response, Self::Error>>>>;
///
/// fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { ... }
///
@@ -61,8 +64,8 @@ pub use self::transform::{apply, Transform};
/// }
/// ```
///
/// Service can have mutable state that influence computation.
/// This service could be rewritten as a simple function:
/// 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).
///
/// ```rust,ignore
/// async fn my_service(req: u8) -> Result<u64, MyError>;
@@ -90,11 +93,9 @@ pub trait Service {
/// It is permitted for the service to return `Ready` from a `poll_ready`
/// call and the next invocation of `call` results in an error.
///
/// There are several notes to consider:
///
/// # Notes
/// 1. `.poll_ready()` might be called on different task from actual service call.
///
/// 2. In case of chained services, `.poll_ready()` get called for all services at once.
/// 1. In case of chained services, `.poll_ready()` get called for all services at once.
fn poll_ready(&mut self, ctx: &mut task::Context<'_>) -> Poll<Result<(), Self::Error>>;
/// Process the request and return the response asynchronously.
@@ -128,7 +129,7 @@ pub trait Service {
/// Map this service's error to a different error, returning a new service.
///
/// This function is similar to the `Result::map_err` where it will change
/// the error type of the underlying service. This is useful for example to
/// 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
@@ -142,42 +143,42 @@ pub trait Service {
}
}
/// Creates new `Service` values.
/// Factory for creating `Service`s.
///
/// Acts as a service factory. This is useful for cases where new `Service`
/// values must be produced. One case is a TCP server listener. The listener
/// accepts new TCP streams, obtains a new `Service` value using the
/// `ServiceFactory` trait, and uses that new `Service` value to process inbound
/// Acts as a service factory. This is useful for cases where new `Service`s
/// must be produced. One case is a TCP server listener. The listener
/// accepts new TCP streams, obtains a new `Service` using the
/// `ServiceFactory` trait, and uses the new `Service` to process inbound
/// requests on that new TCP stream.
///
/// `Config` is a service factory configuration type.
pub trait ServiceFactory {
/// Requests handled by the service.
/// Requests handled by the created services.
type Request;
/// Responses given by the service
/// Responses given by the created services.
type Response;
/// Errors produced by the service
/// Errors produced by the created services.
type Error;
/// Service factory configuration
/// Service factory configuration.
type Config;
/// The `Service` value created by this factory
/// The kind of `Service` created by this factory.
type Service: Service<
Request = Self::Request,
Response = Self::Response,
Error = Self::Error,
>;
/// Errors produced while building a service.
/// Errors potentially raised while building a service.
type InitError;
/// The future of the `Service` instance.
type Future: Future<Output = Result<Self::Service, Self::InitError>>;
/// Create and return a new service value asynchronously.
/// Create and return a new service asynchronously.
fn new_service(&self, cfg: Self::Config) -> Self::Future;
/// Map this service's output to a different type, returning a new service

View File

@@ -9,7 +9,7 @@ use crate::map_init_err::MapInitErr;
use crate::then::{ThenService, ThenServiceFactory};
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>
where
F: IntoService<T>,
@@ -20,7 +20,7 @@ where
}
}
/// 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>
where
T: ServiceFactory,

View File

@@ -1,16 +1,16 @@
use std::cell::RefCell;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};
use super::{Service, ServiceFactory};
use crate::cell::Cell;
/// Service for the `then` combinator, chaining a computation onto the end of
/// another service.
///
/// This is created by the `Pipeline::then` method.
pub(crate) struct ThenService<A, B>(Cell<(A, B)>);
pub(crate) struct ThenService<A, B>(Rc<RefCell<(A, B)>>);
impl<A, B> ThenService<A, B> {
/// Create new `.then()` combinator
@@ -19,7 +19,7 @@ impl<A, B> ThenService<A, B> {
A: Service,
B: Service<Request = Result<A::Response, A::Error>, Error = A::Error>,
{
Self(Cell::new((a, b)))
Self(Rc::new(RefCell::new((a, b))))
}
}
@@ -40,7 +40,7 @@ where
type Future = ThenServiceResponse<A, B>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let srv = self.0.get_mut();
let mut srv = self.0.borrow_mut();
let not_ready = !srv.0.poll_ready(cx)?.is_ready();
if !srv.1.poll_ready(cx)?.is_ready() || not_ready {
Poll::Pending
@@ -51,7 +51,7 @@ where
fn call(&mut self, req: A::Request) -> Self::Future {
ThenServiceResponse {
state: State::A(self.0.get_mut().0.call(req), Some(self.0.clone())),
state: State::A(self.0.borrow_mut().0.call(req), Some(self.0.clone())),
}
}
}
@@ -66,13 +66,13 @@ where
state: State<A, B>,
}
#[pin_project::pin_project]
#[pin_project::pin_project(project = StateProj)]
enum State<A, B>
where
A: Service,
B: Service<Request = Result<A::Response, A::Error>>,
{
A(#[pin] A::Future, Option<Cell<(A, B)>>),
A(#[pin] A::Future, Option<Rc<RefCell<(A, B)>>>),
B(#[pin] B::Future),
Empty,
}
@@ -84,27 +84,27 @@ where
{
type Output = Result<B::Response, B::Error>;
#[pin_project::project]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
#[project]
match this.state.as_mut().project() {
State::A(fut, b) => match fut.poll(cx) {
StateProj::A(fut, b) => match fut.poll(cx) {
Poll::Ready(res) => {
let mut b = b.take().unwrap();
let b = b.take().unwrap();
this.state.set(State::Empty); // drop fut A
let fut = b.get_mut().1.call(res);
let fut = b.borrow_mut().1.call(res);
this.state.set(State::B(fut));
self.poll(cx)
}
Poll::Pending => Poll::Pending,
},
State::B(fut) => fut.poll(cx).map(|r| {
StateProj::B(fut) => fut.poll(cx).map(|r| {
this.state.set(State::Empty);
r
}),
State::Empty => panic!("future must not be polled after it returned `Poll::Ready`"),
StateProj::Empty => {
panic!("future must not be polled after it returned `Poll::Ready`")
}
}
}
}

View File

@@ -70,7 +70,7 @@ where
/// timeout: Duration,
/// }
///
/// impl<S> Transform<S> for TimeoutTransform<E>
/// impl<S> Transform<S> for TimeoutTransform
/// where
/// S: Service,
/// {
@@ -115,7 +115,7 @@ pub trait Transform<S> {
/// Creates and returns a new Transform component, asynchronously
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.
fn map_init_err<F, E>(self, f: F) -> TransformMapInitErr<Self, S, F, E>
where
@@ -211,7 +211,7 @@ where
state: ApplyTransformFutureState<T, S>,
}
#[pin_project::pin_project]
#[pin_project::pin_project(project = ApplyTransformFutureStateProj)]
pub enum ApplyTransformFutureState<T, S>
where
S: ServiceFactory,
@@ -228,13 +228,11 @@ where
{
type Output = Result<T::Transform, T::InitError>;
#[pin_project::project]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
#[project]
match this.state.as_mut().project() {
ApplyTransformFutureState::A(fut) => match fut.poll(cx)? {
ApplyTransformFutureStateProj::A(fut) => match fut.poll(cx)? {
Poll::Ready(srv) => {
let fut = this.store.0.new_transform(srv);
this.state.set(ApplyTransformFutureState::B(fut));
@@ -242,7 +240,7 @@ where
}
Poll::Pending => Poll::Pending,
},
ApplyTransformFutureState::B(fut) => fut.poll(cx),
ApplyTransformFutureStateProj::B(fut) => fut.poll(cx),
}
}
}

View File

@@ -1,5 +1,11 @@
# Changes
## [1.0.1] - 2020-05-19
* Replace deprecated `net2` crate with `socket2`
* Remove unused `futures` dependency
## [1.0.0] - 2019-12-11
* Update actix-server to 1.0.0

View File

@@ -1,6 +1,6 @@
[package]
name = "actix-testing"
version = "1.0.0"
version = "1.0.1"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix testing utils"
keywords = ["network", "framework", "async", "futures"]
@@ -8,9 +8,10 @@ homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-testing/"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
license = "MIT OR Apache-2.0"
edition = "2018"
workspace = ".."
readme = "README.md"
[lib]
name = "actix_testing"
@@ -23,5 +24,4 @@ actix-server = "1.0.0"
actix-service = "1.0.0"
log = "0.4"
net2 = "0.2"
futures = "0.3.1"
socket2 = "0.3"

View File

@@ -7,7 +7,7 @@ use std::{net, thread};
use actix_rt::{net::TcpStream, System};
use actix_server::{Server, ServerBuilder, ServiceFactory};
use net2::TcpBuilder;
use socket2::{Domain, Protocol, Socket, Type};
#[cfg(not(test))] // Work around for rust-lang/rust#62127
pub use actix_macros::test;
@@ -37,7 +37,7 @@ pub use actix_macros::test;
/// ```
pub struct TestServer;
/// Test server runstime
/// Test server runtime
pub struct TestServerRuntime {
addr: net::SocketAddr,
host: String,
@@ -107,13 +107,14 @@ impl TestServer {
}
}
/// Get firat available unused local address
/// Get first available unused local address
pub fn unused_addr() -> net::SocketAddr {
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
let socket = TcpBuilder::new_v4().unwrap();
socket.bind(&addr).unwrap();
socket.reuse_address(true).unwrap();
let tcp = socket.to_tcp_listener().unwrap();
let socket =
Socket::new(Domain::ipv4(), Type::stream(), Some(Protocol::tcp())).unwrap();
socket.bind(&addr.into()).unwrap();
socket.set_reuse_address(true).unwrap();
let tcp = socket.into_tcp_listener();
tcp.local_addr().unwrap()
}
}

View File

@@ -1,10 +1,24 @@
# Changes
## [0.3.3] - 2020-07-14
### Changed
* Update parking_lot to 0.11
## [0.3.2] - 2020-05-20
## Added
* Implement `std::error::Error` for `BlockingError` [#120]
[#120]: https://github.com/actix/actix-net/pull/120
## [0.3.1] - 2019-12-12
### Changed
* Use parking_lot 0.10
* Update parking_lot to 0.10
## [0.3.0] - 2019-12-02

View File

@@ -1,6 +1,6 @@
[package]
name = "actix-threadpool"
version = "0.3.1"
version = "0.3.3"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix thread pool for sync code"
keywords = ["actix", "network", "framework", "async", "futures"]
@@ -8,7 +8,7 @@ homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-threadpool/"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
license = "MIT OR Apache-2.0"
exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"]
edition = "2018"
workspace = ".."
@@ -20,7 +20,7 @@ path = "src/lib.rs"
[dependencies]
derive_more = "0.99.2"
futures-channel = "0.3.1"
parking_lot = "0.10"
parking_lot = "0.11"
lazy_static = "1.3"
log = "0.4"
num_cpus = "1.10"

View File

@@ -48,6 +48,8 @@ pub enum BlockingError<E: fmt::Debug> {
Canceled,
}
impl<E: fmt::Debug> std::error::Error for BlockingError<E> {}
/// Execute blocking function on a thread pool, returns future that resolves
/// to result of the function execution.
pub fn run<F, I, E>(f: F) -> CpuFuture<I, E>

View File

@@ -1,27 +1,37 @@
# Changes
## 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
* Update `rustls` dependency to 0.18
* Update `tokio-rustls` dependency to 0.14
* Update `webpki-roots` dependency to 0.20
## [2.0.0-alpha.1] - 2020-03-03
### Changed
* Update `rustls` dependency to 0.17
* Update `tokio-rustls` dependency to 0.13
* 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-alpha.3] - 2019-12-07
### Changed
* Migrate to tokio 0.2
* Enable rustls 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]
name = "actix-tls"
version = "2.0.0-alpha.1"
version = "2.0.0"
authors = ["Nikolay Kim <fafhrd91@gmail.com>"]
description = "Actix tls services"
keywords = ["network", "framework", "async", "futures"]
description = "TLS acceptor services for Actix ecosystem."
keywords = ["network", "framework", "async", "tls", "ssl"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-tls/"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
license = "MIT OR Apache-2.0"
edition = "2018"
workspace = ".."
[package.metadata.docs.rs]
features = ["openssl", "rustls", "nativetls"]
@@ -19,6 +18,10 @@ features = ["openssl", "rustls", "nativetls"]
name = "actix_tls"
path = "src/lib.rs"
[[example]]
name = "basic"
required-features = ["rustls"]
[features]
default = []
@@ -33,28 +36,29 @@ nativetls = ["native-tls", "tokio-tls"]
[dependencies]
actix-service = "1.0.0"
actix-codec = "0.2.0"
actix-utils = "1.0.0"
actix-rt = "1.0.0"
derive_more = "0.99.2"
either = "1.5.2"
futures = "0.3.1"
log = "0.4"
actix-codec = "0.3.0"
actix-utils = "2.0.0"
futures-util = { version = "0.3.4", default-features = false }
# 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 }
# rustls
rust-tls = { version = "0.17.0", package = "rustls", optional = true }
rust-tls = { package = "rustls", version = "0.18.0", optional = true }
webpki = { version = "0.21", optional = true }
webpki-roots = { version = "0.19", optional = true }
tokio-rustls = { version = "0.13.0", optional = true }
webpki-roots = { version = "0.20", optional = true }
tokio-rustls = { version = "0.14.0", optional = true }
# native-tls
native-tls = { version="0.2", optional = true }
tokio-tls = { version="0.3", optional = true }
native-tls = { version = "0.2", optional = true }
tokio-tls = { version = "0.3", optional = true }
[dev-dependencies]
bytes = "0.5"
actix-testing = { version="1.0.0" }
log = "0.4"
env_logger = "0.7"
actix-testing = "1.0.0"
actix-server = "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,11 @@
//! SSL Services
#![deny(rust_2018_idioms, warnings)]
#![allow(clippy::type_complexity)]
//! TLS acceptor services for Actix ecosystem.
//!
//! ## Crate Features
//! * `openssl` - TLS acceptor using the `openssl` crate.
//! * `rustls` - TLS acceptor using the `rustls` crate.
//! * `nativetls` - TLS acceptor using the `native-tls` crate.
#![deny(rust_2018_idioms)]
use std::sync::atomic::{AtomicUsize, Ordering};
@@ -15,25 +20,25 @@ pub mod rustls;
#[cfg(feature = "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);
thread_local! {
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)]
pub enum SslError<E1, E2> {
Ssl(E1),
pub enum TlsError<E1, E2> {
Tls(E1),
Service(E2),
}

View File

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

View File

@@ -3,26 +3,27 @@ use std::marker::PhantomData;
use std::pin::Pin;
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_service::{Service, ServiceFactory};
use actix_utils::counter::{Counter, CounterGuard};
use futures::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;
/// 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> {
acceptor: SslAcceptor,
io: PhantomData<T>,
}
impl<T: AsyncRead + AsyncWrite> Acceptor<T> {
/// Create default `OpensslAcceptor`
/// Create OpenSSL based `Acceptor` service factory.
#[inline]
pub fn new(acceptor: SslAcceptor) -> Self {
Acceptor {
acceptor,
@@ -32,6 +33,7 @@ impl<T: AsyncRead + AsyncWrite> Acceptor<T> {
}
impl<T: AsyncRead + AsyncWrite> Clone for Acceptor<T> {
#[inline]
fn clone(&self) -> Self {
Self {
acceptor: self.acceptor.clone(),
@@ -105,7 +107,7 @@ impl<T: AsyncRead + AsyncWrite + Unpin> Future for AcceptorServiceResponse<T> {
type Output = Result<SslStream<T>, HandshakeError<T>>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let io = futures::ready!(Pin::new(&mut self.fut).poll(cx))?;
let io = futures_util::ready!(Pin::new(&mut self.fut).poll(cx))?;
Poll::Ready(Ok(io))
}
}

View File

@@ -8,7 +8,7 @@ use std::task::{Context, Poll};
use actix_codec::{AsyncRead, AsyncWrite};
use actix_service::{Service, ServiceFactory};
use actix_utils::counter::{Counter, CounterGuard};
use futures::future::{ok, Ready};
use futures_util::future::{ok, Ready};
use tokio_rustls::{Accept, TlsAcceptor};
pub use rust_tls::{ServerConfig, Session};
@@ -17,16 +17,17 @@ pub use webpki_roots::TLS_SERVER_ROOTS;
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> {
config: Arc<ServerConfig>,
io: PhantomData<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 {
Acceptor {
config: Arc::new(config),
@@ -36,6 +37,7 @@ impl<T: AsyncRead + AsyncWrite> Acceptor<T> {
}
impl<T> Clone for Acceptor<T> {
#[inline]
fn clone(&self) -> Self {
Self {
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> {
acceptor: TlsAcceptor,
io: PhantomData<T>,
@@ -108,7 +110,7 @@ impl<T: AsyncRead + AsyncWrite + Unpin> Future for AcceptorServiceFut<T> {
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let res = futures::ready!(Pin::new(&mut this.fut).poll(cx));
let res = futures_util::ready!(Pin::new(&mut this.fut).poll(cx));
match res {
Ok(io) => Poll::Ready(Ok(io)),
Err(e) => Poll::Ready(Err(e)),

View File

@@ -8,7 +8,7 @@ homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-tracing/"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
@@ -17,7 +17,7 @@ path = "src/lib.rs"
[dependencies]
actix-service = "1.0.4"
futures-util = "0.3.1"
futures-util = { version = "0.3.4", default-features = false }
tracing = "0.1"
tracing-futures = "0.2"

View File

@@ -1,5 +1,16 @@
# Changes
## Unreleased - 2020-xx-xx
* Upgrade `pin-project` to `1.0`.
## 2.0.0 - 2020-08-23
* No changes from beta 1.
## 2.0.0-beta.1 - 2020-08-19
* Upgrade `tokio-util` to `0.3`.
* Remove unsound custom Cell and use `std::cell::RefCell` instead, as well as `actix-service`.
* Rename method to correctly spelled `LocalWaker::is_registered`.
## [1.0.6] - 2020-01-08
* Add `Clone` impl for `condition::Waiter`

View File

@@ -1,14 +1,14 @@
[package]
name = "actix-utils"
version = "1.0.6"
version = "2.0.0"
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"]
homepage = "https://actix.rs"
repository = "https://github.com/actix/actix-net.git"
documentation = "https://docs.rs/actix-utils/"
categories = ["network-programming", "asynchronous"]
license = "MIT/Apache-2.0"
license = "MIT OR Apache-2.0"
edition = "2018"
[lib]
@@ -16,13 +16,15 @@ name = "actix_utils"
path = "src/lib.rs"
[dependencies]
actix-service = "1.0.1"
actix-rt = "1.0.0"
actix-codec = "0.2.0"
bitflags = "1.2"
actix-codec = "0.3.0"
actix-rt = "1.1.1"
actix-service = "1.0.6"
bitflags = "1.2.1"
bytes = "0.5.3"
either = "1.5.3"
futures = "0.3.1"
pin-project = "0.4.6"
futures-channel = { version = "0.3.4", default-features = false }
futures-sink = { version = "0.3.4", default-features = false }
futures-util = { version = "0.3.4", default-features = false }
log = "0.4"
pin-project = "1.0.0"
slab = "0.4"

View File

@@ -1,48 +0,0 @@
//! Custom cell impl
use std::cell::UnsafeCell;
use std::fmt;
use std::rc::Rc;
pub(crate) struct Cell<T> {
pub(crate) inner: Rc<UnsafeCell<T>>,
}
impl<T> Clone for Cell<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<T: fmt::Debug> fmt::Debug for Cell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl<T> Cell<T> {
pub(crate) fn new(inner: T) -> Self {
Self {
inner: Rc::new(UnsafeCell::new(inner)),
}
}
pub(crate) fn strong_count(&self) -> usize {
Rc::strong_count(&self.inner)
}
pub(crate) fn get_ref(&self) -> &T {
unsafe { &*self.inner.as_ref().get() }
}
pub(crate) fn get_mut(&mut self) -> &mut T {
unsafe { &mut *self.inner.as_ref().get() }
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn get_mut_unsafe(&self) -> &mut T {
&mut *self.inner.as_ref().get()
}
}

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