mirror of https://github.com/fafhrd91/actix-net
Compare commits
289 Commits
Author | SHA1 | Date |
---|---|---|
|
b57ab7e8f0 | |
|
887975ab11 | |
|
9069fd8590 | |
|
8204690568 | |
|
bbb3139c45 | |
|
0915904bdb | |
|
70f0008f42 | |
|
12df4d7027 | |
|
6f5b81d2a0 | |
|
4cf37171b5 | |
|
00f40e1471 | |
|
15f0b63492 | |
|
9d4b1673aa | |
|
fc902b2d56 | |
|
f4175a4ad4 | |
|
323a2e2931 | |
|
4b9f7ae46d | |
|
0e119fd9b2 | |
|
b04b88e81a | |
|
0a8f2baa11 | |
|
d79d500ffe | |
|
7a7e3de430 | |
|
f062ede06f | |
|
1338276934 | |
|
4746b4e2fa | |
|
0509f0cede | |
|
3831e0d7fe | |
|
60945d0481 | |
|
2615a19e28 | |
|
1a0f44fff1 | |
|
8d1cd2ec87 | |
|
b87174b5f2 | |
|
8097af6a27 | |
|
ecba6e21da | |
|
23f797a81d | |
|
d2a5091451 | |
|
e0c09c2aa4 | |
|
8234543066 | |
|
34826c6253 | |
|
42b788d131 | |
|
9796593b24 | |
|
c362fc4414 | |
|
52733337e4 | |
|
0e36c5f5c4 | |
|
47f0017899 | |
|
bb4fc31461 | |
|
01a104eb82 | |
|
4ab27bfc4a | |
|
8084cec705 | |
|
a2517da225 | |
|
a4b6943ddc | |
|
7d24196d5c | |
|
582edf5444 | |
|
57485f1a21 | |
|
e8871d0d06 | |
|
83e896a6e5 | |
|
af00dada5c | |
|
0681b515de | |
|
3672137d17 | |
|
fad1fda194 | |
|
cfae737314 | |
|
4583daa3c2 | |
|
b1cbacc7f6 | |
|
77588aba81 | |
|
aad3a48edd | |
|
97e8c571cf | |
|
0d8c7e5085 | |
|
baf1b6042a | |
|
779fa28bd5 | |
|
e282811d69 | |
|
5c44115978 | |
|
ead0e2b200 | |
|
ace737fc4c | |
|
c45ae294fb | |
|
939377f6ab | |
|
20149f957b | |
|
544e5d3b40 | |
|
d482af529c | |
|
0030800b9a | |
|
64fa2f8462 | |
|
b0d1c8d193 | |
|
46cc62c6d8 | |
|
912daa3d0a | |
|
aefa810496 | |
|
2c443a7620 | |
|
b3b1583115 | |
|
0d3d1926bc | |
|
0c26ecf9fa | |
|
1bdb15ec20 | |
|
a524f15e34 | |
|
451a44c2e0 | |
|
18071d1fc0 | |
|
786014cc2f | |
|
a7ef438f25 | |
|
375c352810 | |
|
2d1b5468d0 | |
|
3696cda155 | |
|
55e89d1f30 | |
|
24be36b18d | |
|
38ae762569 | |
|
8cf79d3d13 | |
|
73451070db | |
|
2632c984cc | |
|
af8e6cd656 | |
|
db7988609e | |
|
1db640f62e | |
|
9e7d612121 | |
|
5edbf9e3dc | |
|
f028a74240 | |
|
f4139a0878 | |
|
3147dbe7ca | |
|
95ca8f0318 | |
|
f947374a73 | |
|
85191934c8 | |
|
234a4c9c7f | |
|
d5171c2ab3 | |
|
875218488c | |
|
b826bf8471 | |
|
10bd847177 | |
|
481cf55414 | |
|
b4990023c4 | |
|
eb5cec0064 | |
|
db925bf8e6 | |
|
850f6c0491 | |
|
0f71fd5a7a | |
|
40b10847df | |
|
39bab04800 | |
|
3cb247874e | |
|
5792d9f010 | |
|
bd8bd1020b | |
|
21be7d84bd | |
|
57fd6ea809 | |
|
9a3f3eef6a | |
|
e427911cdb | |
|
a1ae524512 | |
|
88833355e4 | |
|
7737ba5cfb | |
|
fd32a0a97a | |
|
07e7f82345 | |
|
1a5d85ec8b | |
|
bd1467e928 | |
|
968ad3b854 | |
|
079f0f66f0 | |
|
d85903b31a | |
|
a0675fb0dd | |
|
af9ccd17d9 | |
|
eb977e9aeb | |
|
d28c7db3b3 | |
|
c5b2d0cd36 | |
|
02ac0bb4f7 | |
|
86b000fe71 | |
|
951e46186b | |
|
9edc0b393a | |
|
1945fa0675 | |
|
923a443950 | |
|
b526197a9a | |
|
8fc2253c61 | |
|
4c12b81492 | |
|
ef716a8488 | |
|
2a4df30c63 | |
|
2d9b147cc3 | |
|
5515a37002 | |
|
4067fbe8f0 | |
|
01f9910e7c | |
|
df12c10a3f | |
|
f632ef2ba8 | |
|
19d03f0454 | |
|
e9c2a0c318 | |
|
f967562ac4 | |
|
61b6e01b02 | |
|
392e591820 | |
|
87440e5734 | |
|
665dec456f | |
|
7d138f0c31 | |
|
3cd5d8b07a | |
|
09548c96b0 | |
|
17fd135349 | |
|
b9b628c47b | |
|
580af3dec4 | |
|
db54639f0f | |
|
6d9eb7e162 | |
|
69e50b5e66 | |
|
17409cd203 | |
|
4a7f2c95af | |
|
c69b8e9ade | |
|
9f59093adc | |
|
bfeb4cd9e7 | |
|
14272a1762 | |
|
7e043048a0 | |
|
45a7dcba90 | |
|
5029beb866 | |
|
910c181251 | |
|
150d2c05d3 | |
|
3b5716c23e | |
|
0bc310a656 | |
|
6ce8307060 | |
|
9cb8a1fadc | |
|
9017de439f | |
|
3eba5b152e | |
|
3c4b0c2755 | |
|
462ab6a4f0 | |
|
e539f83615 | |
|
755b231e00 | |
|
8d5d1dbf6f | |
|
177590a7d8 | |
|
4cbe741230 | |
|
80320a0325 | |
|
6d0dc9628b | |
|
dbce150993 | |
|
54ec06cd23 | |
|
c0693da9ba | |
|
28f36e4e30 | |
|
c6ebbcf21b | |
|
c60d2f9ddb | |
|
a6bece7b33 | |
|
fbb53f54ef | |
|
9b388a83c9 | |
|
878d3a1c74 | |
|
045cf3f3e8 | |
|
d13461b337 | |
|
dde38bbe06 | |
|
d973d5974a | |
|
d7afd60606 | |
|
7e47bf4055 | |
|
05e7be337e | |
|
8d964713c9 | |
|
fe38312db0 | |
|
2b83f08a40 | |
|
8e9401f8e1 | |
|
9ede174e81 | |
|
bb36e2a072 | |
|
6061a44a22 | |
|
363984ad75 | |
|
00654aadc5 | |
|
428914e65e | |
|
df9a9d1a1e | |
|
056d2cd573 | |
|
68228a6cf2 | |
|
d5a9a6a1c5 | |
|
ade71b7bd3 | |
|
cb83922b29 | |
|
25209f5bd8 | |
|
c4a0f37d0c | |
|
0e649329b9 | |
|
66756bc448 | |
|
126ed4c2e3 | |
|
283974f3e6 | |
|
bf2aa3902c | |
|
71b4e55c92 | |
|
eb5fa30ada | |
|
49a034259f | |
|
3337f63b4e | |
|
86ce140249 | |
|
635aebe887 | |
|
4c1e581a54 | |
|
dc67ba770d | |
|
855e3f96fe | |
|
737b438f73 | |
|
0cd70b0536 | |
|
4b6a581ef3 | |
|
3e132d2bc6 | |
|
c5d6174cec | |
|
77d4a69b2f | |
|
ae5377fd6e | |
|
bd9bda0504 | |
|
41ed48219d | |
|
7804ed12eb | |
|
2a54065fae | |
|
217cbd2228 | |
|
d229c1e886 | |
|
6792f799a6 | |
|
72481313cc | |
|
59b629c74b | |
|
7988694242 | |
|
b8a7741524 | |
|
5e290d76f8 | |
|
0edb64575f | |
|
941f67dec9 | |
|
3e624b8376 | |
|
26446fdbad | |
|
b7b7bd2cbf | |
|
637625f9b7 | |
|
b1d5d85e72 | |
|
ed2c07b304 | |
|
4fe7fec5ef | |
|
4c9ee88ec4 | |
|
9ec3cc0fe7 | |
|
01e0f922de | |
|
10d3bb6d0d |
|
@ -2,25 +2,14 @@
|
||||||
lint = "clippy --workspace --tests --examples --bins -- -Dclippy::todo"
|
lint = "clippy --workspace --tests --examples --bins -- -Dclippy::todo"
|
||||||
lint-all = "clippy --workspace --all-features --tests --examples --bins -- -Dclippy::todo"
|
lint-all = "clippy --workspace --all-features --tests --examples --bins -- -Dclippy::todo"
|
||||||
|
|
||||||
ci-doctest = "test --workspace --all-features --doc --no-fail-fast -- --nocapture"
|
|
||||||
|
|
||||||
# just check the library (without dev deps)
|
# just check the library (without dev deps)
|
||||||
ci-check-min = "hack --workspace check --no-default-features"
|
ci-check-min = "hack --workspace check --no-default-features"
|
||||||
ci-check-lib = "hack --workspace --feature-powerset --exclude-features=io-uring check"
|
ci-check-lib = "hack --workspace --feature-powerset --depth=2 --exclude-features=io-uring check"
|
||||||
ci-check-lib-linux = "hack --workspace --feature-powerset check"
|
ci-check-lib-linux = "hack --workspace --feature-powerset --depth=2 check"
|
||||||
|
|
||||||
# check everything
|
# check everything
|
||||||
ci-check = "hack --workspace --feature-powerset --exclude-features=io-uring check --tests --examples"
|
ci-check = "hack --workspace --feature-powerset --depth=2 --exclude-features=io-uring check --tests --examples"
|
||||||
ci-check-linux = "hack --workspace --feature-powerset check --tests --examples"
|
ci-check-linux = "hack --workspace --feature-powerset --depth=2 check --tests --examples"
|
||||||
|
|
||||||
# tests avoiding io-uring feature
|
# tests avoiding io-uring feature
|
||||||
ci-test = " hack --feature-powerset --exclude=actix-rt --exclude=actix-server --exclude-features=io-uring test --workspace --lib --tests --no-fail-fast -- --nocapture"
|
ci-test = "hack --feature-powerset --depth=2 --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture"
|
||||||
ci-test-rt = " hack --feature-powerset --exclude-features=io-uring test --package=actix-rt --lib --tests --no-fail-fast -- --nocapture"
|
|
||||||
ci-test-server = "hack --feature-powerset --exclude-features=io-uring test --package=actix-server --lib --tests --no-fail-fast -- --nocapture"
|
|
||||||
|
|
||||||
# test with io-uring feature
|
|
||||||
ci-test-rt-linux = " hack --feature-powerset test --package=actix-rt --lib --tests --no-fail-fast -- --nocapture"
|
|
||||||
ci-test-server-linux = "hack --feature-powerset test --package=actix-server --lib --tests --no-fail-fast -- --nocapture"
|
|
||||||
|
|
||||||
# test lower msrv
|
|
||||||
ci-test-lower-msrv = "hack --workspace --exclude=actix-server --exclude=actix-tls --feature-powerset test --lib --tests --no-fail-fast -- --nocapture"
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
## PR Type
|
## PR Type
|
||||||
|
|
||||||
<!-- What kind of change does this PR make? -->
|
<!-- What kind of change does this PR make? -->
|
||||||
<!-- Bug Fix / Feature / Refactor / Code Style / Other -->
|
<!-- Bug Fix / Feature / Refactor / Code Style / Other -->
|
||||||
|
|
||||||
INSERT_PR_TYPE
|
INSERT_PR_TYPE
|
||||||
|
|
||||||
|
|
||||||
## PR Checklist
|
## PR Checklist
|
||||||
|
|
||||||
Check your PR fulfills the following:
|
Check your PR fulfills the following:
|
||||||
|
|
||||||
<!-- For draft PRs check the boxes as you complete them. -->
|
<!-- For draft PRs check the boxes as you complete them. -->
|
||||||
|
@ -14,11 +16,10 @@ Check your PR fulfills the following:
|
||||||
- [ ] A changelog entry has been made for the appropriate packages.
|
- [ ] A changelog entry has been made for the appropriate packages.
|
||||||
- [ ] Format code with the latest stable rustfmt
|
- [ ] Format code with the latest stable rustfmt
|
||||||
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
<!-- Describe the current and new behavior. -->
|
<!-- Describe the current and new behavior. -->
|
||||||
<!-- Emphasize any breaking changes. -->
|
<!-- Emphasize any breaking changes. -->
|
||||||
|
|
||||||
|
|
||||||
<!-- If this PR fixes or closes an issue, reference it here. -->
|
<!-- If this PR fixes or closes an issue, reference it here. -->
|
||||||
<!-- Closes #000 -->
|
<!-- Closes #000 -->
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
- package-ecosystem: cargo
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
|
@ -0,0 +1,129 @@
|
||||||
|
name: CI (post-merge)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_and_test_nightly:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# prettier-ignore
|
||||||
|
target:
|
||||||
|
- { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
|
||||||
|
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
|
||||||
|
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
|
||||||
|
- { name: Windows (MinGW), os: windows-latest, triple: x86_64-pc-windows-gnu }
|
||||||
|
version:
|
||||||
|
- nightly
|
||||||
|
|
||||||
|
name: ${{ matrix.target.name }} / ${{ matrix.version }}
|
||||||
|
runs-on: ${{ matrix.target.os }}
|
||||||
|
|
||||||
|
env: {}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Setup Routing
|
||||||
|
if: matrix.target.os == 'macos-latest'
|
||||||
|
run: sudo ifconfig lo0 alias 127.0.0.3
|
||||||
|
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Free Disk Space
|
||||||
|
if: matrix.target.os == 'ubuntu-latest'
|
||||||
|
run: ./scripts/free-disk-space.sh
|
||||||
|
|
||||||
|
- name: Setup mold linker
|
||||||
|
if: matrix.target.os == 'ubuntu-latest'
|
||||||
|
uses: rui314/setup-mold@v1
|
||||||
|
|
||||||
|
- name: Install nasm
|
||||||
|
if: matrix.target.os == 'windows-latest'
|
||||||
|
uses: ilammy/setup-nasm@v1.5.2
|
||||||
|
|
||||||
|
- name: Install OpenSSL
|
||||||
|
if: matrix.target.os == 'windows-latest'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -e
|
||||||
|
choco install openssl --version=1.1.1.2100 -y --no-progress
|
||||||
|
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' >> $GITHUB_ENV
|
||||||
|
echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Install Rust (${{ matrix.version }})
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.version }}
|
||||||
|
|
||||||
|
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
|
||||||
|
uses: taiki-e/install-action@v2.49.34
|
||||||
|
with:
|
||||||
|
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
|
||||||
|
|
||||||
|
- name: check lib
|
||||||
|
if: >
|
||||||
|
matrix.target.os != 'ubuntu-latest'
|
||||||
|
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
|
||||||
|
run: cargo ci-check-lib
|
||||||
|
- name: check lib
|
||||||
|
if: matrix.target.os == 'ubuntu-latest'
|
||||||
|
run: cargo ci-check-lib-linux
|
||||||
|
- name: check lib
|
||||||
|
if: matrix.target.triple == 'x86_64-pc-windows-gnu'
|
||||||
|
run: cargo ci-check-min
|
||||||
|
|
||||||
|
- name: check full
|
||||||
|
# TODO: compile OpenSSL and run tests on MinGW
|
||||||
|
if: >
|
||||||
|
matrix.target.os != 'ubuntu-latest'
|
||||||
|
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
|
||||||
|
run: cargo ci-check
|
||||||
|
- name: check all
|
||||||
|
if: matrix.target.os == 'ubuntu-latest'
|
||||||
|
run: cargo ci-check-linux
|
||||||
|
|
||||||
|
- name: tests
|
||||||
|
run: just test
|
||||||
|
|
||||||
|
# TODO: re-instate some io-uring tests PRs
|
||||||
|
# - name: tests
|
||||||
|
# if: matrix.target.os == 'ubuntu-latest'
|
||||||
|
# run: >-
|
||||||
|
# sudo bash -c "
|
||||||
|
# ulimit -Sl 512
|
||||||
|
# && ulimit -Hl 512
|
||||||
|
# && PATH=$PATH:/usr/share/rust/.cargo/bin
|
||||||
|
# && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-020
|
||||||
|
# && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-021
|
||||||
|
# && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux
|
||||||
|
# "
|
||||||
|
|
||||||
|
- name: CI cache clean
|
||||||
|
run: cargo-ci-cache-clean
|
||||||
|
|
||||||
|
minimal-versions:
|
||||||
|
name: minimal versions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust (nightly)
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
|
||||||
|
- name: Install cargo-hack & cargo-minimal-versions
|
||||||
|
uses: taiki-e/install-action@v2.49.34
|
||||||
|
with:
|
||||||
|
tool: cargo-hack,cargo-minimal-versions
|
||||||
|
|
||||||
|
- name: Check With Minimal Versions
|
||||||
|
run: cargo minimal-versions check
|
|
@ -1,201 +1,133 @@
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request: {}
|
||||||
types: [opened, synchronize, reopened]
|
merge_group: { types: [checks_requested] }
|
||||||
push:
|
push: { branches: [master] }
|
||||||
branches: [master]
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
read_msrv:
|
||||||
|
name: Read MSRV
|
||||||
|
uses: actions-rust-lang/msrv/.github/workflows/msrv.yml@v0.1.0
|
||||||
|
|
||||||
build_and_test:
|
build_and_test:
|
||||||
|
needs:
|
||||||
|
- read_msrv
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
|
# prettier-ignore
|
||||||
target:
|
target:
|
||||||
- { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
|
- { name: Linux, os: ubuntu-latest, triple: x86_64-unknown-linux-gnu }
|
||||||
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
|
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
|
||||||
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
|
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
|
||||||
- { name: Windows (MinGW), os: windows-latest, triple: x86_64-pc-windows-gnu }
|
- { name: Windows (MinGW), os: windows-latest, triple: x86_64-pc-windows-gnu }
|
||||||
- { name: Windows (32-bit), os: windows-latest, triple: i686-pc-windows-msvc }
|
|
||||||
version:
|
version:
|
||||||
- 1.52.0 # MSRV for -server and -tls
|
- { name: msrv, version: "${{ needs.read_msrv.outputs.msrv }}" }
|
||||||
- stable
|
- { name: stable, version: stable }
|
||||||
- nightly
|
|
||||||
|
|
||||||
name: ${{ matrix.target.name }} / ${{ matrix.version }}
|
name: ${{ matrix.target.name }} / ${{ matrix.version.name }}
|
||||||
runs-on: ${{ matrix.target.os }}
|
runs-on: ${{ matrix.target.os }}
|
||||||
|
|
||||||
env:
|
env: {}
|
||||||
VCPKGRS_DYNAMIC: 1
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Routing
|
- name: Setup Routing
|
||||||
if: matrix.target.os == 'macos-latest'
|
if: matrix.target.os == 'macos-latest'
|
||||||
run: sudo ifconfig lo0 alias 127.0.0.3
|
run: sudo ifconfig lo0 alias 127.0.0.3
|
||||||
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Free Disk Space
|
||||||
|
if: matrix.target.os == 'ubuntu-latest'
|
||||||
|
run: ./scripts/free-disk-space.sh
|
||||||
|
|
||||||
|
- name: Setup mold linker
|
||||||
|
if: matrix.target.os == 'ubuntu-latest'
|
||||||
|
uses: rui314/setup-mold@v1
|
||||||
|
|
||||||
|
- name: Install nasm
|
||||||
|
if: matrix.target.os == 'windows-latest'
|
||||||
|
uses: ilammy/setup-nasm@v1.5.2
|
||||||
|
|
||||||
# install OpenSSL on Windows
|
|
||||||
- name: Set vcpkg root
|
|
||||||
if: matrix.target.triple == 'x86_64-pc-windows-msvc' || matrix.target.triple == 'i686-pc-windows-msvc'
|
|
||||||
run: echo "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append
|
|
||||||
- name: Install OpenSSL
|
- name: Install OpenSSL
|
||||||
if: matrix.target.triple == 'x86_64-pc-windows-msvc'
|
if: matrix.target.os == 'windows-latest'
|
||||||
run: vcpkg install openssl:x64-windows
|
shell: bash
|
||||||
- name: Install OpenSSL
|
run: |
|
||||||
if: matrix.target.triple == 'i686-pc-windows-msvc'
|
set -e
|
||||||
run: vcpkg install openssl:x86-windows
|
choco install openssl --version=1.1.1.2100 -y --no-progress
|
||||||
|
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' >> $GITHUB_ENV
|
||||||
|
echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Install ${{ matrix.version }}
|
- name: Install Rust (${{ matrix.version.name }})
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.version }}-${{ matrix.target.triple }}
|
toolchain: ${{ matrix.version.version }}
|
||||||
profile: minimal
|
|
||||||
override: true
|
|
||||||
|
|
||||||
# - name: Install MSYS2
|
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
|
||||||
# if: matrix.target.triple == 'x86_64-pc-windows-gnu'
|
uses: taiki-e/install-action@v2.49.34
|
||||||
# uses: msys2/setup-msys2@v2
|
|
||||||
# - name: Install MinGW Packages
|
|
||||||
# if: matrix.target.triple == 'x86_64-pc-windows-gnu'
|
|
||||||
# run: |
|
|
||||||
# msys2 -c 'pacman -Sy --noconfirm pacman'
|
|
||||||
# msys2 -c 'pacman --noconfirm -S base-devel pkg-config'
|
|
||||||
|
|
||||||
# - name: Generate Cargo.lock
|
|
||||||
# uses: actions-rs/cargo@v1
|
|
||||||
# with: { command: generate-lockfile }
|
|
||||||
# - name: Cache Dependencies
|
|
||||||
# uses: Swatinem/rust-cache@v1.2.0
|
|
||||||
|
|
||||||
- name: Install cargo-hack
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
with:
|
||||||
command: install
|
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
|
||||||
args: cargo-hack
|
|
||||||
|
- name: Generate Cargo.lock
|
||||||
|
run: cargo generate-lockfile
|
||||||
|
|
||||||
|
- name: workaround MSRV issues
|
||||||
|
if: matrix.version.name == 'msrv'
|
||||||
|
run: just downgrade-for-msrv
|
||||||
|
|
||||||
- name: check lib
|
- name: check lib
|
||||||
if: >
|
if: >
|
||||||
matrix.target.os != 'ubuntu-latest'
|
matrix.target.os != 'ubuntu-latest'
|
||||||
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
|
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo ci-check-lib
|
||||||
with: { command: ci-check-lib }
|
|
||||||
- name: check lib
|
- name: check lib
|
||||||
if: matrix.target.os == 'ubuntu-latest'
|
if: matrix.target.os == 'ubuntu-latest'
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo ci-check-lib-linux
|
||||||
with: { command: ci-check-lib-linux }
|
|
||||||
- name: check lib
|
- name: check lib
|
||||||
if: matrix.target.triple == 'x86_64-pc-windows-gnu'
|
if: matrix.target.triple != 'x86_64-pc-windows-gnu'
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo ci-check-min
|
||||||
with: { command: ci-check-min }
|
|
||||||
|
|
||||||
- name: check full
|
- name: check full
|
||||||
# TODO: compile OpenSSL and run tests on MinGW
|
# TODO: compile OpenSSL and run tests on MinGW
|
||||||
if: >
|
if: >
|
||||||
matrix.target.os != 'ubuntu-latest'
|
matrix.target.os != 'ubuntu-latest'
|
||||||
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
|
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo ci-check
|
||||||
with: { command: ci-check }
|
|
||||||
- name: check all
|
- name: check all
|
||||||
if: matrix.target.os == 'ubuntu-latest'
|
if: matrix.target.os == 'ubuntu-latest'
|
||||||
uses: actions-rs/cargo@v1
|
run: cargo ci-check-linux
|
||||||
with: { command: ci-check-linux }
|
|
||||||
|
|
||||||
- name: tests
|
- name: tests
|
||||||
if: >
|
run: just test
|
||||||
matrix.target.os != 'ubuntu-latest'
|
|
||||||
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
|
|
||||||
run: |
|
|
||||||
cargo ci-test
|
|
||||||
cargo ci-test-rt
|
|
||||||
cargo ci-test-server
|
|
||||||
- name: tests
|
|
||||||
if: matrix.target.os == 'ubuntu-latest'
|
|
||||||
run: |
|
|
||||||
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rt-linux && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-server-linux"
|
|
||||||
|
|
||||||
- name: Clear the cargo caches
|
- name: CI cache clean
|
||||||
run: |
|
run: cargo-ci-cache-clean
|
||||||
cargo install cargo-cache --version 0.6.2 --no-default-features --features ci-autoclean
|
|
||||||
cargo-cache
|
docs:
|
||||||
|
name: Documentation
|
||||||
build_and_test_lower_msrv:
|
|
||||||
name: Linux / 1.46 (lower MSRV)
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install 1.46.0 # MSRV for all but -server and -tls
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: 1.46.0-x86_64-unknown-linux-gnu
|
|
||||||
profile: minimal
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Install cargo-hack
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: install
|
|
||||||
args: cargo-hack
|
|
||||||
|
|
||||||
- name: tests
|
|
||||||
run: |
|
|
||||||
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=1.46 cargo ci-test-lower-msrv"
|
|
||||||
|
|
||||||
- name: Clear the cargo caches
|
|
||||||
run: |
|
|
||||||
cargo install cargo-cache --version 0.6.2 --no-default-features --features ci-autoclean
|
|
||||||
cargo-cache
|
|
||||||
|
|
||||||
coverage:
|
|
||||||
name: coverage
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install Rust (nightly)
|
- name: Install Rust (nightly)
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
with:
|
with:
|
||||||
toolchain: stable-x86_64-unknown-linux-gnu
|
toolchain: nightly
|
||||||
profile: minimal
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Generate Cargo.lock
|
- name: Install just
|
||||||
uses: actions-rs/cargo@v1
|
uses: taiki-e/install-action@v2.49.34
|
||||||
with: { command: generate-lockfile }
|
|
||||||
- name: Cache Dependencies
|
|
||||||
uses: Swatinem/rust-cache@v1.3.0
|
|
||||||
|
|
||||||
- name: Generate coverage file
|
|
||||||
if: github.ref == 'refs/heads/master'
|
|
||||||
run: |
|
|
||||||
cargo install cargo-tarpaulin
|
|
||||||
cargo tarpaulin --out Xml --verbose
|
|
||||||
- name: Upload to Codecov
|
|
||||||
if: github.ref == 'refs/heads/master'
|
|
||||||
uses: codecov/codecov-action@v1
|
|
||||||
with: { file: cobertura.xml }
|
|
||||||
|
|
||||||
rustdoc:
|
|
||||||
name: rustdoc
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install Rust (nightly)
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
with:
|
||||||
toolchain: nightly-x86_64-unknown-linux-gnu
|
tool: just
|
||||||
profile: minimal
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Generate Cargo.lock
|
- name: doc tests
|
||||||
uses: actions-rs/cargo@v1
|
run: just test-docs
|
||||||
with: { command: generate-lockfile }
|
|
||||||
- name: Cache Dependencies
|
|
||||||
uses: Swatinem/rust-cache@v1.3.0
|
|
||||||
|
|
||||||
- name: doc tests io-uring
|
|
||||||
run: |
|
|
||||||
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=nightly cargo ci-doctest"
|
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
name: Lint
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
fmt:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install Rust
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
profile: minimal
|
|
||||||
components: rustfmt
|
|
||||||
override: true
|
|
||||||
- name: Rustfmt Check
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: fmt
|
|
||||||
args: --all -- --check
|
|
||||||
|
|
||||||
clippy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install Rust
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: stable
|
|
||||||
profile: minimal
|
|
||||||
components: clippy
|
|
||||||
override: true
|
|
||||||
- name: Clippy Check
|
|
||||||
uses: actions-rs/clippy-check@v1
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
args: --workspace --all-features --tests --examples --bins -- -Dclippy::todo
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
name: Coverage
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
coverage:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
|
with:
|
||||||
|
components: llvm-tools-preview
|
||||||
|
|
||||||
|
- name: Install cargo-llvm-cov
|
||||||
|
uses: taiki-e/install-action@v2.49.34
|
||||||
|
with:
|
||||||
|
tool: cargo-llvm-cov
|
||||||
|
|
||||||
|
- name: Generate code coverage
|
||||||
|
run: cargo llvm-cov --workspace --all-features --codecov --output-path codecov.json
|
||||||
|
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v5.4.0
|
||||||
|
with:
|
||||||
|
files: codecov.json
|
||||||
|
fail_ci_if_error: true
|
||||||
|
env:
|
||||||
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
@ -0,0 +1,69 @@
|
||||||
|
name: Lint
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request: {}
|
||||||
|
merge_group: { types: [checks_requested] }
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
fmt:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
|
- name: Rustfmt Check
|
||||||
|
run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
|
with: { components: clippy }
|
||||||
|
|
||||||
|
- uses: giraffate/clippy-action@v1.0.1
|
||||||
|
with:
|
||||||
|
reporter: "github-pr-check"
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
clippy_flags: --workspace --all-features --tests --examples --bins -- -Dclippy::todo -Aunknown_lints
|
||||||
|
|
||||||
|
check-external-types:
|
||||||
|
if: false # rustdoc mismatch currently
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust (${{ vars.RUST_VERSION_EXTERNAL_TYPES }})
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
|
with:
|
||||||
|
toolchain: ${{ vars.RUST_VERSION_EXTERNAL_TYPES }}
|
||||||
|
|
||||||
|
- name: Install just
|
||||||
|
uses: taiki-e/install-action@v2.49.34
|
||||||
|
with:
|
||||||
|
tool: just
|
||||||
|
|
||||||
|
- name: Install cargo-check-external-types
|
||||||
|
uses: taiki-e/cache-cargo-install-action@v2.1.1
|
||||||
|
with:
|
||||||
|
tool: cargo-check-external-types
|
||||||
|
|
||||||
|
- name: check external types
|
||||||
|
run: just check-external-types-all +${{ vars.RUST_VERSION_EXTERNAL_TYPES }}
|
|
@ -1,35 +0,0 @@
|
||||||
name: Upload documentation
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [master]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install Rust
|
|
||||||
uses: actions-rs/toolchain@v1
|
|
||||||
with:
|
|
||||||
toolchain: nightly-x86_64-unknown-linux-gnu
|
|
||||||
profile: minimal
|
|
||||||
override: true
|
|
||||||
|
|
||||||
- name: Build Docs
|
|
||||||
uses: actions-rs/cargo@v1
|
|
||||||
with:
|
|
||||||
command: doc
|
|
||||||
args: --workspace --all-features --no-deps
|
|
||||||
|
|
||||||
- name: Tweak HTML
|
|
||||||
run: echo '<meta http-equiv="refresh" content="0;url=actix_server/index.html">' > target/doc/index.html
|
|
||||||
|
|
||||||
- name: Deploy to GitHub Pages
|
|
||||||
uses: JamesIves/github-pages-deploy-action@3.7.1
|
|
||||||
with:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
BRANCH: gh-pages
|
|
||||||
FOLDER: target/doc
|
|
|
@ -1,4 +1,3 @@
|
||||||
Cargo.lock
|
|
||||||
target/
|
target/
|
||||||
guide/build/
|
guide/build/
|
||||||
/gh-pages
|
/gh-pages
|
||||||
|
@ -13,4 +12,8 @@ guide/build/
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# IDEs
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
# direnv
|
||||||
|
/.direnv
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
group_imports = "StdExternalCrate"
|
||||||
|
imports_granularity = "Crate"
|
||||||
|
use_field_init_shorthand = true
|
|
@ -0,0 +1,29 @@
|
||||||
|
exclude = ["target/*"]
|
||||||
|
include = ["**/*.toml"]
|
||||||
|
|
||||||
|
[formatting]
|
||||||
|
column_width = 110
|
||||||
|
|
||||||
|
[[rule]]
|
||||||
|
include = ["**/Cargo.toml"]
|
||||||
|
keys = [
|
||||||
|
"dependencies",
|
||||||
|
"*-dependencies",
|
||||||
|
"workspace.dependencies",
|
||||||
|
"workspace.*-dependencies",
|
||||||
|
"target.*.dependencies",
|
||||||
|
"target.*.*-dependencies",
|
||||||
|
]
|
||||||
|
formatting.reorder_keys = true
|
||||||
|
|
||||||
|
[[rule]]
|
||||||
|
include = ["**/Cargo.toml"]
|
||||||
|
keys = [
|
||||||
|
"dependencies.*",
|
||||||
|
"*-dependencies.*",
|
||||||
|
"workspace.dependencies.*",
|
||||||
|
"workspace.*-dependencies.*",
|
||||||
|
"target.*.dependencies",
|
||||||
|
"target.*.*-dependencies",
|
||||||
|
]
|
||||||
|
formatting.reorder_keys = false
|
|
@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
|
||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment include:
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
- Using welcoming and inclusive language
|
||||||
* Being respectful of differing viewpoints and experiences
|
- Being respectful of differing viewpoints and experiences
|
||||||
* Gracefully accepting constructive criticism
|
- Gracefully accepting constructive criticism
|
||||||
* Focusing on what is best for the community
|
- Focusing on what is best for the community
|
||||||
* Showing empathy towards other community members
|
- Showing empathy towards other community members
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
- Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
## Our Responsibilities
|
## Our Responsibilities
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ Instances of abusive, harassing, or otherwise unacceptable behavior may be repor
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||||
|
|
||||||
[@robjtede]: https://github.com/robjtede
|
[@robjtede]: https://github.com/robjtede
|
||||||
[@JohnTitor]: https://github.com/JohnTitor
|
[@johntitor]: https://github.com/JohnTitor
|
||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
|
@ -1,4 +1,5 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"actix-codec",
|
"actix-codec",
|
||||||
"actix-macros",
|
"actix-macros",
|
||||||
|
@ -13,6 +14,11 @@ members = [
|
||||||
"local-waker",
|
"local-waker",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.71.1"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
actix-codec = { path = "actix-codec" }
|
actix-codec = { path = "actix-codec" }
|
||||||
actix-macros = { path = "actix-macros" }
|
actix-macros = { path = "actix-macros" }
|
||||||
|
@ -30,3 +36,9 @@ local-waker = { path = "local-waker" }
|
||||||
lto = true
|
lto = true
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
|
[workspace.lints.rust]
|
||||||
|
rust_2018_idioms = "deny"
|
||||||
|
nonstandard-style = "deny"
|
||||||
|
future_incompatible = "deny"
|
||||||
|
missing_docs = { level = "warn", priority = -1 }
|
||||||
|
|
14
README.md
14
README.md
|
@ -2,18 +2,18 @@
|
||||||
|
|
||||||
> A collection of lower-level libraries for composable network services.
|
> A collection of lower-level libraries for composable network services.
|
||||||
|
|
||||||
[](https://github.com/actix/actix-net/actions/workflows/ci.yml)
|
[](https://github.com/actix/actix-net/actions/workflows/ci.yml)
|
||||||
[](https://codecov.io/gh/actix/actix-net)
|
[](https://codecov.io/gh/actix/actix-net)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
[](https://deps.rs/repo/github/actix/actix-extras)
|
[](https://deps.rs/repo/github/actix/actix-net)
|
||||||
|
|
||||||
## Example
|
## Examples
|
||||||
|
|
||||||
See `actix-server/examples` and `actix-tls/examples` for some basic examples.
|
See example folders for [`actix-server`](./actix-server/examples) and [`actix-tls`](./actix-tls/examples).
|
||||||
|
|
||||||
### MSRV
|
## MSRV
|
||||||
|
|
||||||
This repo's Minimum Supported Rust Version (MSRV) is 1.46.0.
|
Crates in this repo currently have a Minimum Supported Rust Version (MSRV) of 1.65. As a policy, we permit MSRV increases in non-breaking releases.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|
|
@ -1,42 +1,52 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
|
## 0.5.2
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.65.
|
||||||
|
|
||||||
|
## 0.5.1
|
||||||
|
|
||||||
|
- Logs emitted now use the `tracing` crate with `log` compatibility.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.49.
|
||||||
|
|
||||||
|
## 0.5.0
|
||||||
|
|
||||||
|
- Updated `tokio-util` dependency to `0.7.0`.
|
||||||
|
|
||||||
|
## 0.4.2
|
||||||
|
|
||||||
## 0.4.2 - 2021-12-31
|
|
||||||
- No significant changes since `0.4.1`.
|
- No significant changes since `0.4.1`.
|
||||||
|
|
||||||
|
## 0.4.1
|
||||||
|
|
||||||
## 0.4.1 - 2021-11-05
|
- Added `LinesCodec`.
|
||||||
- Added `LinesCodec.` [#338]
|
- `Framed::poll_ready` flushes when the buffer is full.
|
||||||
- `Framed::poll_ready` flushes when the buffer is full. [#409]
|
|
||||||
|
|
||||||
[#338]: https://github.com/actix/actix-net/pull/338
|
## 0.4.0
|
||||||
[#409]: https://github.com/actix/actix-net/pull/409
|
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0 - 2021-04-20
|
|
||||||
- No significant changes since v0.4.0-beta.1.
|
- No significant changes since v0.4.0-beta.1.
|
||||||
|
|
||||||
|
## 0.4.0-beta.1
|
||||||
|
|
||||||
## 0.4.0-beta.1 - 2020-12-28
|
- Replace `pin-project` with `pin-project-lite`.
|
||||||
- Replace `pin-project` with `pin-project-lite`. [#237]
|
- Upgrade `tokio` dependency to `1`.
|
||||||
- Upgrade `tokio` dependency to `1`. [#237]
|
- Upgrade `tokio-util` dependency to `0.6`.
|
||||||
- Upgrade `tokio-util` dependency to `0.6`. [#237]
|
- Upgrade `bytes` dependency to `1`.
|
||||||
- Upgrade `bytes` dependency to `1`. [#237]
|
|
||||||
|
|
||||||
[#237]: https://github.com/actix/actix-net/pull/237
|
## 0.3.0
|
||||||
|
|
||||||
|
|
||||||
## 0.3.0 - 2020-08-23
|
|
||||||
- No changes from beta 2.
|
- No changes from beta 2.
|
||||||
|
|
||||||
|
## 0.3.0-beta.2
|
||||||
|
|
||||||
## 0.3.0-beta.2 - 2020-08-19
|
|
||||||
- Remove unused type parameter from `Framed::replace_codec`.
|
- Remove unused type parameter from `Framed::replace_codec`.
|
||||||
|
|
||||||
|
## 0.3.0-beta.1
|
||||||
|
|
||||||
## 0.3.0-beta.1 - 2020-08-19
|
|
||||||
- Use `.advance()` instead of `.split_to()`.
|
- Use `.advance()` instead of `.split_to()`.
|
||||||
- Upgrade `tokio-util` to `0.3`.
|
- Upgrade `tokio-util` to `0.3`.
|
||||||
- Improve `BytesCodec::encode()` performance.
|
- Improve `BytesCodec::encode()` performance.
|
||||||
|
@ -45,31 +55,31 @@
|
||||||
- Add method on `Framed` to get a pinned reference to the underlying I/O.
|
- Add method on `Framed` to get a pinned reference to the underlying I/O.
|
||||||
- Add method on `Framed` check emptiness of read buffer.
|
- Add method on `Framed` check emptiness of read buffer.
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
## 0.2.0 - 2019-12-10
|
|
||||||
- Use specific futures dependencies.
|
- Use specific futures dependencies.
|
||||||
|
|
||||||
|
|
||||||
## 0.2.0-alpha.4
|
## 0.2.0-alpha.4
|
||||||
|
|
||||||
- Fix buffer remaining capacity calculation.
|
- Fix buffer remaining capacity calculation.
|
||||||
|
|
||||||
|
|
||||||
## 0.2.0-alpha.3
|
## 0.2.0-alpha.3
|
||||||
|
|
||||||
- Use tokio 0.2.
|
- Use tokio 0.2.
|
||||||
- Fix low/high watermark for write/read buffers.
|
- Fix low/high watermark for write/read buffers.
|
||||||
|
|
||||||
|
|
||||||
## 0.2.0-alpha.2
|
## 0.2.0-alpha.2
|
||||||
|
|
||||||
- Migrated to `std::future`.
|
- Migrated to `std::future`.
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
|
||||||
## 0.1.2 - 2019-03-27
|
|
||||||
- Added `Framed::map_io()` method.
|
- Added `Framed::map_io()` method.
|
||||||
|
|
||||||
|
## 0.1.1
|
||||||
|
|
||||||
## 0.1.1 - 2019-03-06
|
|
||||||
- Added `FramedParts::with_read_buffer()` method.
|
- Added `FramedParts::with_read_buffer()` method.
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
## 0.1.0 - 2018-12-09
|
|
||||||
- Move codec to separate crate.
|
- Move codec to separate crate.
|
||||||
|
|
|
@ -1,36 +1,36 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-codec"
|
name = "actix-codec"
|
||||||
version = "0.4.2"
|
version = "0.5.2"
|
||||||
authors = [
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
|
||||||
]
|
|
||||||
description = "Codec utilities for working with framed protocols"
|
description = "Codec utilities for working with framed protocols"
|
||||||
keywords = ["network", "framework", "async", "futures"]
|
keywords = ["network", "framework", "async", "futures"]
|
||||||
repository = "https://github.com/actix/actix-net"
|
repository = "https://github.com/actix/actix-net"
|
||||||
categories = ["network-programming", "asynchronous"]
|
categories = ["network-programming", "asynchronous"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
[lib]
|
[package.metadata.cargo_check_external_types]
|
||||||
name = "actix_codec"
|
allowed_external_types = ["bytes::*", "futures_core::*", "futures_sink::*", "tokio::*", "tokio_util::*"]
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.2.1"
|
bitflags = "2"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
futures-core = { version = "0.3.7", default-features = false }
|
futures-core = { version = "0.3.7", default-features = false }
|
||||||
futures-sink = { version = "0.3.7", default-features = false }
|
futures-sink = { version = "0.3.7", default-features = false }
|
||||||
log = "0.4"
|
|
||||||
memchr = "2.3"
|
memchr = "2.3"
|
||||||
pin-project-lite = "0.2"
|
pin-project-lite = "0.2"
|
||||||
tokio = "1.13.1"
|
tokio = "1.23.1"
|
||||||
tokio-util = { version = "0.6", features = ["codec", "io"] }
|
tokio-util = { version = "0.7", features = ["codec", "io"] }
|
||||||
|
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.3", features = ["html_reports"] }
|
criterion = { version = "0.5", features = ["html_reports"] }
|
||||||
tokio-test = "0.4.2"
|
tokio-test = "0.4.2"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "lines"
|
name = "lines"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ const LW: usize = 1024;
|
||||||
const HW: usize = 8 * 1024;
|
const HW: usize = 8 * 1024;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct Flags: u8 {
|
struct Flags: u8 {
|
||||||
const EOF = 0b0001;
|
const EOF = 0b0001;
|
||||||
const READABLE = 0b0010;
|
const READABLE = 0b0010;
|
||||||
|
@ -193,18 +194,18 @@ impl<T, U> Framed<T, U> {
|
||||||
match this.codec.decode_eof(this.read_buf) {
|
match this.codec.decode_eof(this.read_buf) {
|
||||||
Ok(Some(frame)) => return Poll::Ready(Some(Ok(frame))),
|
Ok(Some(frame)) => return Poll::Ready(Some(Ok(frame))),
|
||||||
Ok(None) => return Poll::Ready(None),
|
Ok(None) => return Poll::Ready(None),
|
||||||
Err(e) => return Poll::Ready(Some(Err(e))),
|
Err(err) => return Poll::Ready(Some(Err(err))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log::trace!("attempting to decode a frame");
|
tracing::trace!("attempting to decode a frame");
|
||||||
|
|
||||||
match this.codec.decode(this.read_buf) {
|
match this.codec.decode(this.read_buf) {
|
||||||
Ok(Some(frame)) => {
|
Ok(Some(frame)) => {
|
||||||
log::trace!("frame decoded from buffer");
|
tracing::trace!("frame decoded from buffer");
|
||||||
return Poll::Ready(Some(Ok(frame)));
|
return Poll::Ready(Some(Ok(frame)));
|
||||||
}
|
}
|
||||||
Err(e) => return Poll::Ready(Some(Err(e))),
|
Err(err) => return Poll::Ready(Some(Err(err))),
|
||||||
_ => (), // Need more data
|
_ => (), // Need more data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,7 +222,7 @@ impl<T, U> Framed<T, U> {
|
||||||
|
|
||||||
let cnt = match tokio_util::io::poll_read_buf(this.io, cx, this.read_buf) {
|
let cnt = match tokio_util::io::poll_read_buf(this.io, cx, this.read_buf) {
|
||||||
Poll::Pending => return Poll::Pending,
|
Poll::Pending => return Poll::Pending,
|
||||||
Poll::Ready(Err(e)) => return Poll::Ready(Some(Err(e.into()))),
|
Poll::Ready(Err(err)) => return Poll::Ready(Some(Err(err.into()))),
|
||||||
Poll::Ready(Ok(cnt)) => cnt,
|
Poll::Ready(Ok(cnt)) => cnt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -233,19 +234,16 @@ impl<T, U> Framed<T, U> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flush write buffer to underlying I/O stream.
|
/// Flush write buffer to underlying I/O stream.
|
||||||
pub fn flush<I>(
|
pub fn flush<I>(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), U::Error>>
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<Result<(), U::Error>>
|
|
||||||
where
|
where
|
||||||
T: AsyncWrite,
|
T: AsyncWrite,
|
||||||
U: Encoder<I>,
|
U: Encoder<I>,
|
||||||
{
|
{
|
||||||
let mut this = self.as_mut().project();
|
let mut this = self.as_mut().project();
|
||||||
log::trace!("flushing framed transport");
|
tracing::trace!("flushing framed transport");
|
||||||
|
|
||||||
while !this.write_buf.is_empty() {
|
while !this.write_buf.is_empty() {
|
||||||
log::trace!("writing; remaining={}", this.write_buf.len());
|
tracing::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))?;
|
||||||
|
|
||||||
|
@ -264,15 +262,12 @@ impl<T, U> Framed<T, U> {
|
||||||
// Try flushing the underlying IO
|
// Try flushing the underlying IO
|
||||||
ready!(this.io.poll_flush(cx))?;
|
ready!(this.io.poll_flush(cx))?;
|
||||||
|
|
||||||
log::trace!("framed transport flushed");
|
tracing::trace!("framed transport flushed");
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flush write buffer and shutdown underlying I/O stream.
|
/// Flush write buffer and shutdown underlying I/O stream.
|
||||||
pub fn close<I>(
|
pub fn close<I>(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), U::Error>>
|
||||||
mut self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
) -> Poll<Result<(), U::Error>>
|
|
||||||
where
|
where
|
||||||
T: AsyncWrite,
|
T: AsyncWrite,
|
||||||
U: Encoder<I>,
|
U: Encoder<I>,
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
//! Codec utilities for working with framed protocols.
|
//! Codec utilities for working with framed protocols.
|
||||||
//!
|
//!
|
||||||
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and
|
//! Contains adapters to go from streams of bytes, [`AsyncRead`] and [`AsyncWrite`], to framed
|
||||||
//! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
|
//! streams implementing [`Sink`] and [`Stream`]. Framed streams are also known as `transports`.
|
||||||
//! Framed streams are also known as `transports`.
|
|
||||||
//!
|
//!
|
||||||
//! [`Sink`]: futures_sink::Sink
|
//! [`Sink`]: futures_sink::Sink
|
||||||
//! [`Stream`]: futures_core::Stream
|
//! [`Stream`]: futures_core::Stream
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible, missing_docs)]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
|
pub use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
|
pub use tokio_util::{
|
||||||
|
codec::{Decoder, Encoder},
|
||||||
|
io::poll_read_buf,
|
||||||
|
};
|
||||||
|
|
||||||
mod bcodec;
|
mod bcodec;
|
||||||
mod framed;
|
mod framed;
|
||||||
mod lines;
|
mod lines;
|
||||||
|
|
||||||
pub use self::bcodec::BytesCodec;
|
pub use self::{
|
||||||
pub use self::framed::{Framed, FramedParts};
|
bcodec::BytesCodec,
|
||||||
pub use self::lines::LinesCodec;
|
framed::{Framed, FramedParts},
|
||||||
|
lines::LinesCodec,
|
||||||
pub use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
};
|
||||||
pub use tokio_util::codec::{Decoder, Encoder};
|
|
||||||
pub use tokio_util::io::poll_read_buf;
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ use super::{Decoder, Encoder};
|
||||||
|
|
||||||
/// Lines codec. Reads/writes line delimited strings.
|
/// Lines codec. Reads/writes line delimited strings.
|
||||||
///
|
///
|
||||||
/// Will split input up by LF or CRLF delimiters. I.e. carriage return characters at the end of
|
/// Will split input up by LF or CRLF delimiters. Carriage return characters at the end of lines are
|
||||||
/// lines are not preserved.
|
/// not preserved.
|
||||||
#[derive(Debug, Copy, Clone, Default)]
|
#[derive(Debug, Copy, Clone, Default)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub struct LinesCodec;
|
pub struct LinesCodec;
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
io::{self, Write},
|
||||||
|
pin::Pin,
|
||||||
|
task::{
|
||||||
|
Context,
|
||||||
|
Poll::{self, Pending, Ready},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
use actix_codec::*;
|
use actix_codec::*;
|
||||||
use bytes::Buf;
|
use bytes::{Buf as _, BufMut as _, BytesMut};
|
||||||
use bytes::{BufMut, BytesMut};
|
|
||||||
use futures_sink::Sink;
|
use futures_sink::Sink;
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::io::{self, Write};
|
|
||||||
use std::pin::Pin;
|
|
||||||
use std::task::Poll::{Pending, Ready};
|
|
||||||
use std::task::{Context, Poll};
|
|
||||||
use tokio_test::{assert_ready, task};
|
use tokio_test::{assert_ready, task};
|
||||||
|
|
||||||
macro_rules! bilateral {
|
macro_rules! bilateral {
|
||||||
|
@ -50,7 +56,7 @@ impl Write for Bilateral {
|
||||||
assert_eq!(&data[..], &src[..data.len()]);
|
assert_eq!(&data[..], &src[..data.len()]);
|
||||||
Ok(data.len())
|
Ok(data.len())
|
||||||
}
|
}
|
||||||
Some(Err(e)) => Err(e),
|
Some(Err(err)) => Err(err),
|
||||||
None => panic!("unexpected write; {:?}", src),
|
None => panic!("unexpected write; {:?}", src),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,20 +73,17 @@ impl AsyncWrite for Bilateral {
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
) -> Poll<Result<usize, io::Error>> {
|
) -> Poll<Result<usize, io::Error>> {
|
||||||
match Pin::get_mut(self).write(buf) {
|
match Pin::get_mut(self).write(buf) {
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Pending,
|
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Pending,
|
||||||
other => Ready(other),
|
other => Ready(other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||||
match Pin::get_mut(self).flush() {
|
match Pin::get_mut(self).flush() {
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Pending,
|
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Pending,
|
||||||
other => Ready(other),
|
other => Ready(other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn poll_shutdown(
|
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||||
self: Pin<&mut Self>,
|
|
||||||
_cx: &mut Context<'_>,
|
|
||||||
) -> Poll<Result<(), io::Error>> {
|
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,8 +102,8 @@ impl AsyncRead for Bilateral {
|
||||||
buf.put_slice(&data);
|
buf.put_slice(&data);
|
||||||
Ready(Ok(()))
|
Ready(Ok(()))
|
||||||
}
|
}
|
||||||
Some(Err(ref e)) if e.kind() == WouldBlock => Pending,
|
Some(Err(ref err)) if err.kind() == WouldBlock => Pending,
|
||||||
Some(Err(e)) => Ready(Err(e)),
|
Some(Err(err)) => Ready(Err(err)),
|
||||||
None => Ready(Ok(())),
|
None => Ready(Ok(())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +1,53 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
|
## 0.2.4
|
||||||
|
|
||||||
|
- Update `syn` dependency to `2`.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.65.
|
||||||
|
|
||||||
|
## 0.2.3
|
||||||
|
|
||||||
## 0.2.3 - 2021-10-19
|
|
||||||
- Fix test macro in presence of other imports named "test". [#399]
|
- Fix test macro in presence of other imports named "test". [#399]
|
||||||
|
|
||||||
[#399]: https://github.com/actix/actix-net/pull/399
|
[#399]: https://github.com/actix/actix-net/pull/399
|
||||||
|
|
||||||
|
## 0.2.2
|
||||||
|
|
||||||
## 0.2.2 - 2021-10-14
|
|
||||||
- Improve error recovery potential when macro input is invalid. [#391]
|
- Improve error recovery potential when macro input is invalid. [#391]
|
||||||
- Allow custom `System`s on test macro. [#391]
|
- Allow custom `System`s on test macro. [#391]
|
||||||
|
|
||||||
[#391]: https://github.com/actix/actix-net/pull/391
|
[#391]: https://github.com/actix/actix-net/pull/391
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
## 0.2.1 - 2021-02-02
|
|
||||||
- Add optional argument `system` to `main` macro which can be used to specify the path to `actix_rt::System` (useful for re-exports). [#363]
|
- Add optional argument `system` to `main` macro which can be used to specify the path to `actix_rt::System` (useful for re-exports). [#363]
|
||||||
|
|
||||||
[#363]: https://github.com/actix/actix-net/pull/363
|
[#363]: https://github.com/actix/actix-net/pull/363
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
## 0.2.0 - 2021-02-02
|
|
||||||
- Update to latest `actix_rt::System::new` signature. [#261]
|
- Update to latest `actix_rt::System::new` signature. [#261]
|
||||||
|
|
||||||
[#261]: https://github.com/actix/actix-net/pull/261
|
[#261]: https://github.com/actix/actix-net/pull/261
|
||||||
|
|
||||||
|
## 0.2.0-beta.1
|
||||||
|
|
||||||
## 0.2.0-beta.1 - 2021-01-09
|
|
||||||
- Remove `actix-reexport` feature. [#218]
|
- Remove `actix-reexport` feature. [#218]
|
||||||
|
|
||||||
[#218]: https://github.com/actix/actix-net/pull/218
|
[#218]: https://github.com/actix/actix-net/pull/218
|
||||||
|
|
||||||
|
## 0.1.3
|
||||||
|
|
||||||
## 0.1.3 - 2020-12-03
|
|
||||||
- Add `actix-reexport` feature. [#218]
|
- Add `actix-reexport` feature. [#218]
|
||||||
|
|
||||||
[#218]: https://github.com/actix/actix-net/pull/218
|
[#218]: https://github.com/actix/actix-net/pull/218
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
|
||||||
## 0.1.2 - 2020-05-18
|
|
||||||
- Forward actix_rt::test arguments to test function [#127]
|
- Forward actix_rt::test arguments to test function [#127]
|
||||||
|
|
||||||
[#127]: https://github.com/actix/actix-net/pull/127
|
[#127]: https://github.com/actix/actix-net/pull/127
|
||||||
|
|
|
@ -1,27 +1,39 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-macros"
|
name = "actix-macros"
|
||||||
version = "0.2.3"
|
version = "0.2.4"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"Ibraheem Ahmed <ibrah1440@gmail.com>",
|
"Ibraheem Ahmed <ibrah1440@gmail.com>",
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
]
|
]
|
||||||
description = "Macros for Actix system and runtime"
|
description = "Macros for Actix system and runtime"
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
repository = "https://github.com/actix/actix-net"
|
||||||
categories = ["network-programming", "asynchronous"]
|
categories = ["network-programming", "asynchronous"]
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
edition = "2018"
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[package.metadata.cargo-machete]
|
||||||
|
ignored = [
|
||||||
|
"proc_macro2", # specified for minimal versions compat
|
||||||
|
]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
quote = "1.0.3"
|
quote = "1"
|
||||||
syn = { version = "^1", features = ["full"] }
|
syn = { version = "2", features = ["full"] }
|
||||||
|
|
||||||
|
# minimal versions compat
|
||||||
|
[target.'cfg(any())'.dependencies]
|
||||||
|
proc-macro2 = "1.0.60"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.0.0"
|
actix-rt = "2"
|
||||||
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
rustversion-msrv = "0.100"
|
||||||
rustversion = "1"
|
|
||||||
trybuild = "1"
|
trybuild = "1"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
|
@ -8,13 +8,14 @@
|
||||||
//! # Tests
|
//! # Tests
|
||||||
//! See docs for the [`#[test]`](macro@test) macro.
|
//! See docs for the [`#[test]`](macro@test) macro.
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible)]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use syn::parse::Parser as _;
|
||||||
|
|
||||||
|
type AttributeArgs = syn::punctuated::Punctuated<syn::Meta, syn::Token![,]>;
|
||||||
|
|
||||||
/// Marks async entry-point function to be executed by Actix system.
|
/// Marks async entry-point function to be executed by Actix system.
|
||||||
///
|
///
|
||||||
|
@ -25,9 +26,7 @@ use quote::quote;
|
||||||
/// println!("Hello world");
|
/// println!("Hello world");
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(clippy::needless_doctest_main)]
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
|
||||||
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let mut input = match syn::parse::<syn::ItemFn>(item.clone()) {
|
let mut input = match syn::parse::<syn::ItemFn>(item.clone()) {
|
||||||
Ok(input) => input,
|
Ok(input) => input,
|
||||||
|
@ -35,7 +34,11 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
Err(err) => return input_and_compile_error(item, err),
|
Err(err) => return input_and_compile_error(item, err),
|
||||||
};
|
};
|
||||||
|
|
||||||
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
let parser = AttributeArgs::parse_terminated;
|
||||||
|
let args = match parser.parse(args.clone()) {
|
||||||
|
Ok(args) => args,
|
||||||
|
Err(err) => return input_and_compile_error(args, err),
|
||||||
|
};
|
||||||
|
|
||||||
let attrs = &input.attrs;
|
let attrs = &input.attrs;
|
||||||
let vis = &input.vis;
|
let vis = &input.vis;
|
||||||
|
@ -55,11 +58,15 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
for arg in &args {
|
for arg in &args {
|
||||||
match arg {
|
match arg {
|
||||||
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
|
syn::Meta::NameValue(syn::MetaNameValue {
|
||||||
lit: syn::Lit::Str(lit),
|
|
||||||
path,
|
path,
|
||||||
|
value:
|
||||||
|
syn::Expr::Lit(syn::ExprLit {
|
||||||
|
lit: syn::Lit::Str(lit),
|
||||||
|
..
|
||||||
|
}),
|
||||||
..
|
..
|
||||||
})) => match path
|
}) => match path
|
||||||
.get_ident()
|
.get_ident()
|
||||||
.map(|i| i.to_string().to_lowercase())
|
.map(|i| i.to_string().to_lowercase())
|
||||||
.as_deref()
|
.as_deref()
|
||||||
|
@ -78,6 +85,7 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
return syn::Error::new_spanned(arg, "Unknown attribute specified")
|
return syn::Error::new_spanned(arg, "Unknown attribute specified")
|
||||||
.to_compile_error()
|
.to_compile_error()
|
||||||
|
@ -114,7 +122,11 @@ pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
Err(err) => return input_and_compile_error(item, err),
|
Err(err) => return input_and_compile_error(item, err),
|
||||||
};
|
};
|
||||||
|
|
||||||
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
let parser = AttributeArgs::parse_terminated;
|
||||||
|
let args = match parser.parse(args.clone()) {
|
||||||
|
Ok(args) => args,
|
||||||
|
Err(err) => return input_and_compile_error(args, err),
|
||||||
|
};
|
||||||
|
|
||||||
let attrs = &input.attrs;
|
let attrs = &input.attrs;
|
||||||
let vis = &input.vis;
|
let vis = &input.vis;
|
||||||
|
@ -123,7 +135,7 @@ pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
let mut has_test_attr = false;
|
let mut has_test_attr = false;
|
||||||
|
|
||||||
for attr in attrs {
|
for attr in attrs {
|
||||||
if attr.path.is_ident("test") {
|
if attr.path().is_ident("test") {
|
||||||
has_test_attr = true;
|
has_test_attr = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,11 +161,15 @@ pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
for arg in &args {
|
for arg in &args {
|
||||||
match arg {
|
match arg {
|
||||||
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
|
syn::Meta::NameValue(syn::MetaNameValue {
|
||||||
lit: syn::Lit::Str(lit),
|
|
||||||
path,
|
path,
|
||||||
|
value:
|
||||||
|
syn::Expr::Lit(syn::ExprLit {
|
||||||
|
lit: syn::Lit::Str(lit),
|
||||||
|
..
|
||||||
|
}),
|
||||||
..
|
..
|
||||||
})) => match path
|
}) => match path
|
||||||
.get_ident()
|
.get_ident()
|
||||||
.map(|i| i.to_string().to_lowercase())
|
.map(|i| i.to_string().to_lowercase())
|
||||||
.as_deref()
|
.as_deref()
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
#[rustversion::stable(1.46)] // MSRV
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
#[rustversion_msrv::msrv]
|
||||||
#[test]
|
#[test]
|
||||||
fn compile_macros() {
|
fn compile_macros() {
|
||||||
let t = trybuild::TestCases::new();
|
let t = trybuild::TestCases::new();
|
||||||
|
|
||||||
t.pass("tests/trybuild/main-01-basic.rs");
|
t.pass("tests/trybuild/main-01-basic.rs");
|
||||||
t.compile_fail("tests/trybuild/main-02-only-async.rs");
|
t.compile_fail("tests/trybuild/main-02-only-async.rs");
|
||||||
t.pass("tests/trybuild/main-03-fn-params.rs");
|
t.pass("tests/trybuild/main-03-fn-params.rs");
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
error: the async keyword is missing from the function declaration
|
error: the async keyword is missing from the function declaration
|
||||||
--> $DIR/main-02-only-async.rs:2:1
|
--> tests/trybuild/main-02-only-async.rs:2:1
|
||||||
|
|
|
|
||||||
2 | fn main() {
|
2 | fn main() {
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error[E0601]: `main` function not found in crate `$CRATE`
|
error[E0601]: `main` function not found in crate `$CRATE`
|
||||||
--> $DIR/main-02-only-async.rs:1:1
|
--> tests/trybuild/main-02-only-async.rs:4:2
|
||||||
|
|
|
|
||||||
1 | / #[actix_rt::main]
|
4 | }
|
||||||
2 | | fn main() {
|
| ^ consider adding a `main` function to `$DIR/tests/trybuild/main-02-only-async.rs`
|
||||||
3 | | futures_util::future::ready(()).await
|
|
||||||
4 | | }
|
|
||||||
| |_^ consider adding a `main` function to `$DIR/tests/trybuild/main-02-only-async.rs`
|
|
||||||
|
|
|
@ -1,186 +1,174 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
## 2.5.1 - 2021-12-31
|
## 2.10.0
|
||||||
- Expose `System::with_tokio_rt` and `Arbiter::with_tokio_rt`. [#430]
|
|
||||||
|
|
||||||
[#430]: https://github.com/actix/actix-net/pull/430
|
- Relax `F`'s bound (`Fn => FnOnce`) on `{Arbiter, System}::with_tokio_rt()` functions.
|
||||||
|
- Update `tokio-uring` dependency to `0.5`.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.70.
|
||||||
|
|
||||||
|
## 2.9.0
|
||||||
|
|
||||||
## 2.5.0 - 2021-11-22
|
- Add `actix_rt::System::runtime()` method to retrieve the underlying `actix_rt::Runtime` runtime.
|
||||||
- Add `System::run_with_code` to allow retrieving the exit code on stop. [#411]
|
- Add `actix_rt::Runtime::tokio_runtime()` method to retrieve the underlying Tokio runtime.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.65.
|
||||||
|
|
||||||
[#411]: https://github.com/actix/actix-net/pull/411
|
## 2.8.0
|
||||||
|
|
||||||
|
- Add `#[track_caller]` attribute to `spawn` functions and methods.
|
||||||
|
- Update `tokio-uring` dependency to `0.4`.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.59.
|
||||||
|
|
||||||
## 2.4.0 - 2021-11-05
|
## 2.7.0
|
||||||
- Add `Arbiter::try_current` for situations where thread may or may not have Arbiter context. [#408]
|
|
||||||
- Start io-uring with `System::new` when feature is enabled. [#395]
|
|
||||||
|
|
||||||
[#395]: https://github.com/actix/actix-net/pull/395
|
- Update `tokio-uring` dependency to `0.3`.
|
||||||
[#408]: https://github.com/actix/actix-net/pull/408
|
- Minimum supported Rust version (MSRV) is now 1.49.
|
||||||
|
|
||||||
|
## 2.6.0
|
||||||
|
|
||||||
## 2.3.0 - 2021-10-11
|
- Update `tokio-uring` dependency to `0.2`.
|
||||||
- The `spawn` method can now resolve with non-unit outputs. [#369]
|
|
||||||
- Add experimental (semver-exempt) `io-uring` feature for enabling async file I/O on linux. [#374]
|
|
||||||
|
|
||||||
[#369]: https://github.com/actix/actix-net/pull/369
|
## 2.5.1
|
||||||
[#374]: https://github.com/actix/actix-net/pull/374
|
|
||||||
|
|
||||||
|
- Expose `System::with_tokio_rt` and `Arbiter::with_tokio_rt`.
|
||||||
|
|
||||||
## 2.2.0 - 2021-03-29
|
## 2.5.0
|
||||||
- **BREAKING** `ActixStream::{poll_read_ready, poll_write_ready}` methods now return
|
|
||||||
`Ready` object in ok variant. [#293]
|
|
||||||
* Breakage is acceptable since `ActixStream` was not intended to be public.
|
|
||||||
|
|
||||||
[#293]: https://github.com/actix/actix-net/pull/293
|
- Add `System::run_with_code` to allow retrieving the exit code on stop.
|
||||||
|
|
||||||
|
## 2.4.0
|
||||||
|
|
||||||
## 2.1.0 - 2021-02-24
|
- Add `Arbiter::try_current` for situations where thread may or may not have Arbiter context.
|
||||||
- Add `ActixStream` extension trait to include readiness methods. [#276]
|
- Start io-uring with `System::new` when feature is enabled.
|
||||||
- Re-export `tokio::net::TcpSocket` in `net` module [#282]
|
|
||||||
|
|
||||||
[#276]: https://github.com/actix/actix-net/pull/276
|
## 2.3.0
|
||||||
[#282]: https://github.com/actix/actix-net/pull/282
|
|
||||||
|
|
||||||
|
- The `spawn` method can now resolve with non-unit outputs.
|
||||||
|
- Add experimental (semver-exempt) `io-uring` feature for enabling async file I/O on linux.
|
||||||
|
|
||||||
## 2.0.2 - 2021-02-06
|
## 2.2.0
|
||||||
- Add `Arbiter::handle` to get a handle of an owned Arbiter. [#274]
|
|
||||||
- Add `System::try_current` for situations where actix may or may not be running a System. [#275]
|
|
||||||
|
|
||||||
[#274]: https://github.com/actix/actix-net/pull/274
|
- **BREAKING** `ActixStream::{poll_read_ready, poll_write_ready}` methods now return `Ready` object in ok variant.
|
||||||
[#275]: https://github.com/actix/actix-net/pull/275
|
- Breakage is acceptable since `ActixStream` was not intended to be public.
|
||||||
|
|
||||||
|
## 2.1.0
|
||||||
|
|
||||||
## 2.0.1 - 2021-02-06
|
- Add `ActixStream` extension trait to include readiness methods.
|
||||||
- Expose `JoinError` from Tokio. [#271]
|
- Re-export `tokio::net::TcpSocket` in `net` module
|
||||||
|
|
||||||
[#271]: https://github.com/actix/actix-net/pull/271
|
## 2.0.2
|
||||||
|
|
||||||
|
- Add `Arbiter::handle` to get a handle of an owned Arbiter.
|
||||||
|
- Add `System::try_current` for situations where actix may or may not be running a System.
|
||||||
|
|
||||||
## 2.0.0 - 2021-02-02
|
## 2.0.1
|
||||||
- Remove all Arbiter-local storage methods. [#262]
|
|
||||||
- Re-export `tokio::pin`. [#262]
|
|
||||||
|
|
||||||
[#262]: https://github.com/actix/actix-net/pull/262
|
- Expose `JoinError` from Tokio.
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
## 2.0.0-beta.3 - 2021-01-31
|
- Remove all Arbiter-local storage methods.
|
||||||
- Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`. [#253]
|
- Re-export `tokio::pin`.
|
||||||
- Return `JoinHandle` from `actix_rt::spawn`. [#253]
|
|
||||||
- Remove old `Arbiter::spawn`. Implementation is now inlined into `actix_rt::spawn`. [#253]
|
|
||||||
- Rename `Arbiter::{send => spawn}` and `Arbiter::{exec_fn => spawn_fn}`. [#253]
|
|
||||||
- Remove `Arbiter::exec`. [#253]
|
|
||||||
- Remove deprecated `Arbiter::local_join` and `Arbiter::is_running`. [#253]
|
|
||||||
- `Arbiter::spawn` now accepts !Unpin futures. [#256]
|
|
||||||
- `System::new` no longer takes arguments. [#257]
|
|
||||||
- Remove `System::with_current`. [#257]
|
|
||||||
- Remove `Builder`. [#257]
|
|
||||||
- Add `System::with_init` as replacement for `Builder::run`. [#257]
|
|
||||||
- Rename `System::{is_set => is_registered}`. [#257]
|
|
||||||
- Add `ArbiterHandle` for sending messages to non-current-thread arbiters. [#257].
|
|
||||||
- `System::arbiter` now returns an `&ArbiterHandle`. [#257]
|
|
||||||
- `Arbiter::current` now returns an `ArbiterHandle` instead. [#257]
|
|
||||||
- `Arbiter::join` now takes self by value. [#257]
|
|
||||||
|
|
||||||
[#253]: https://github.com/actix/actix-net/pull/253
|
## 2.0.0-beta.3
|
||||||
[#254]: https://github.com/actix/actix-net/pull/254
|
|
||||||
[#256]: https://github.com/actix/actix-net/pull/256
|
|
||||||
[#257]: https://github.com/actix/actix-net/pull/257
|
|
||||||
|
|
||||||
|
- Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`.
|
||||||
|
- Return `JoinHandle` from `actix_rt::spawn`.
|
||||||
|
- Remove old `Arbiter::spawn`. Implementation is now inlined into `actix_rt::spawn`.
|
||||||
|
- Rename `Arbiter::{send => spawn}` and `Arbiter::{exec_fn => spawn_fn}`.
|
||||||
|
- Remove `Arbiter::exec`.
|
||||||
|
- Remove deprecated `Arbiter::local_join` and `Arbiter::is_running`.
|
||||||
|
- `Arbiter::spawn` now accepts !Unpin futures.
|
||||||
|
- `System::new` no longer takes arguments.
|
||||||
|
- Remove `System::with_current`.
|
||||||
|
- Remove `Builder`.
|
||||||
|
- Add `System::with_init` as replacement for `Builder::run`.
|
||||||
|
- Rename `System::{is_set => is_registered}`.
|
||||||
|
- Add `ArbiterHandle` for sending messages to non-current-thread arbiters.
|
||||||
|
- `System::arbiter` now returns an `&ArbiterHandle`.
|
||||||
|
- `Arbiter::current` now returns an `ArbiterHandle` instead.
|
||||||
|
- `Arbiter::join` now takes self by value.
|
||||||
|
|
||||||
## 2.0.0-beta.2 - 2021-01-09
|
## 2.0.0-beta.2
|
||||||
- Add `task` mod with re-export of `tokio::task::{spawn_blocking, yield_now, JoinHandle}` [#245]
|
|
||||||
|
- Add `task` mod with re-export of `tokio::task::{spawn_blocking, yield_now, JoinHandle}`
|
||||||
- Add default "macros" feature to allow faster compile times when using `default-features=false`.
|
- Add default "macros" feature to allow faster compile times when using `default-features=false`.
|
||||||
|
|
||||||
[#245]: https://github.com/actix/actix-net/pull/245
|
## 2.0.0-beta.1
|
||||||
|
|
||||||
|
- Add `System::attach_to_tokio` method.
|
||||||
## 2.0.0-beta.1 - 2020-12-28
|
- Update `tokio` dependency to `1.0`.
|
||||||
- Add `System::attach_to_tokio` method. [#173]
|
- Rename `time` module `delay_for` to `sleep`, `delay_until` to `sleep_until`, `Delay` to `Sleep` to stay aligned with Tokio's naming.
|
||||||
- Update `tokio` dependency to `1.0`. [#236]
|
|
||||||
- Rename `time` module `delay_for` to `sleep`, `delay_until` to `sleep_until`, `Delay` to `Sleep`
|
|
||||||
to stay aligned with Tokio's naming. [#236]
|
|
||||||
- Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`.
|
- Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`.
|
||||||
* These methods now accept `&self` when calling. [#236]
|
- These methods now accept `&self` when calling.
|
||||||
- Remove `'static` lifetime requirement for `System::run` and `Builder::run`. [#236]
|
- Remove `'static` lifetime requirement for `System::run` and `Builder::run`.
|
||||||
- `Arbiter::spawn` now panics when `System` is not in scope. [#207]
|
- `Arbiter::spawn` now panics when `System` is not in scope.
|
||||||
- Fix work load issue by removing `PENDING` thread local. [#207]
|
- Fix work load issue by removing `PENDING` thread local.
|
||||||
|
|
||||||
[#207]: https://github.com/actix/actix-net/pull/207
|
## 1.1.1
|
||||||
[#236]: https://github.com/actix/actix-net/pull/236
|
|
||||||
|
|
||||||
|
- Fix memory leak due to
|
||||||
|
|
||||||
## 1.1.1 - 2020-04-30
|
## 1.1.0 _(YANKED)_
|
||||||
- Fix memory leak due to [#94] (see [#129] for more detail)
|
|
||||||
|
|
||||||
[#129]: https://github.com/actix/actix-net/issues/129
|
- Expose `System::is_set` to check if current system has ben started
|
||||||
|
- Add `Arbiter::is_running` to check if event loop is running
|
||||||
|
- Add `Arbiter::local_join` associated function to get be able to `await` for spawned futures
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
## 1.1.0 - 2020-04-08 _(YANKED)_
|
|
||||||
- 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
|
|
||||||
- Update dependencies
|
- Update dependencies
|
||||||
|
|
||||||
|
## 1.0.0-alpha.3
|
||||||
|
|
||||||
## 1.0.0-alpha.3 - 2019-12-07
|
|
||||||
- Migrate to tokio 0.2
|
- Migrate to tokio 0.2
|
||||||
- Fix compilation on non-unix platforms
|
- Fix compilation on non-unix platforms
|
||||||
|
|
||||||
|
## 1.0.0-alpha.2
|
||||||
|
|
||||||
## 1.0.0-alpha.2 - 2019-12-02
|
|
||||||
- Export `main` and `test` attribute macros
|
- Export `main` and `test` attribute macros
|
||||||
- Export `time` module (re-export of tokio-timer)
|
- Export `time` module (re-export of tokio-timer)
|
||||||
- Export `net` module (re-export of tokio-net)
|
- Export `net` module (re-export of tokio-net)
|
||||||
|
|
||||||
|
## 1.0.0-alpha.1
|
||||||
|
|
||||||
## 1.0.0-alpha.1 - 2019-11-22
|
|
||||||
- Migrate to std::future and tokio 0.2
|
- Migrate to std::future and tokio 0.2
|
||||||
|
|
||||||
|
## 0.2.6
|
||||||
|
|
||||||
## 0.2.6 - 2019-11-14
|
|
||||||
- Allow to join arbiter's thread. #60
|
- Allow to join arbiter's thread. #60
|
||||||
- Fix arbiter's thread panic message.
|
- Fix arbiter's thread panic message.
|
||||||
|
|
||||||
|
## 0.2.5
|
||||||
|
|
||||||
## 0.2.5 - 2019-09-02
|
|
||||||
- Add arbiter specific storage
|
- Add arbiter specific storage
|
||||||
|
|
||||||
|
## 0.2.4
|
||||||
|
|
||||||
## 0.2.4 - 2019-07-17
|
|
||||||
- Avoid a copy of the Future when initializing the Box. #29
|
- Avoid a copy of the Future when initializing the Box. #29
|
||||||
|
|
||||||
|
## 0.2.3
|
||||||
|
|
||||||
## 0.2.3 - 2019-06-22
|
|
||||||
- Allow to start System using existing CurrentThread Handle #22
|
- Allow to start System using existing CurrentThread Handle #22
|
||||||
|
|
||||||
|
## 0.2.2
|
||||||
|
|
||||||
## 0.2.2 - 2019-03-28
|
|
||||||
- Moved `blocking` module to `actix-threadpool` crate
|
- Moved `blocking` module to `actix-threadpool` crate
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
## 0.2.1 - 2019-03-11
|
|
||||||
- Added `blocking` module
|
- Added `blocking` module
|
||||||
- Added `Arbiter::exec_fn` - execute fn on the arbiter's thread
|
- Added `Arbiter::exec_fn` - execute fn on the arbiter's thread
|
||||||
- Added `Arbiter::exec` - execute fn on the arbiter's thread and wait result
|
- Added `Arbiter::exec` - execute fn on the arbiter's thread and wait result
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
## 0.2.0 - 2019-03-06
|
|
||||||
- `run` method returns `io::Result<()>`
|
- `run` method returns `io::Result<()>`
|
||||||
- Removed `Handle`
|
- Removed `Handle`
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
## 0.1.0 - 2018-12-09
|
|
||||||
- Initial release
|
- Initial release
|
||||||
|
|
|
@ -1,22 +1,18 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-rt"
|
name = "actix-rt"
|
||||||
version = "2.5.1"
|
version = "2.10.0"
|
||||||
authors = [
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
|
||||||
"fakeshadow <24548779@qq.com>",
|
|
||||||
]
|
|
||||||
description = "Tokio-based single-threaded async runtime for the Actix ecosystem"
|
description = "Tokio-based single-threaded async runtime for the Actix ecosystem"
|
||||||
keywords = ["async", "futures", "io", "runtime"]
|
keywords = ["async", "futures", "io", "runtime"]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
repository = "https://github.com/actix/actix-net"
|
||||||
categories = ["network-programming", "asynchronous"]
|
categories = ["network-programming", "asynchronous"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
[lib]
|
[package.metadata.cargo_check_external_types]
|
||||||
name = "actix_rt"
|
allowed_external_types = ["actix_macros::*", "tokio::*"]
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["macros"]
|
default = ["macros"]
|
||||||
|
@ -27,11 +23,14 @@ io-uring = ["tokio-uring"]
|
||||||
actix-macros = { version = "0.2.3", optional = true }
|
actix-macros = { version = "0.2.3", optional = true }
|
||||||
|
|
||||||
futures-core = { version = "0.3", default-features = false }
|
futures-core = { version = "0.3", default-features = false }
|
||||||
tokio = { version = "1.13.1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] }
|
tokio = { version = "1.23.1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] }
|
||||||
|
|
||||||
|
# runtime for `io-uring` feature
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
tokio-uring = { version = "0.1", optional = true }
|
tokio-uring = { version = "0.5", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "1.13.1", features = ["full"] }
|
tokio = { version = "1.23.1", features = ["full"] }
|
||||||
hyper = { version = "0.14", default-features = false, features = ["server", "tcp", "http1"] }
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
> Tokio-based single-threaded async runtime for the Actix ecosystem.
|
> Tokio-based single-threaded async runtime for the Actix ecosystem.
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-rt)
|
[](https://crates.io/crates/actix-rt)
|
||||||
[](https://docs.rs/actix-rt/2.5.1)
|
[](https://docs.rs/actix-rt/2.10.0)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
|
[](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
|
||||||

|

|
||||||
<br />
|
<br />
|
||||||
[](https://deps.rs/crate/actix-rt/2.5.1)
|
[](https://deps.rs/crate/actix-rt/2.10.0)
|
||||||

|

|
||||||
[](https://discord.gg/WghFtEH6Hb)
|
[](https://discord.gg/WghFtEH6Hb)
|
||||||
|
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
use hyper::service::{make_service_fn, service_fn};
|
|
||||||
use hyper::{Body, Request, Response, Server};
|
|
||||||
use std::convert::Infallible;
|
|
||||||
use std::net::SocketAddr;
|
|
||||||
|
|
||||||
async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
|
|
||||||
Ok(Response::new(Body::from("Hello World")))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
actix_rt::System::with_tokio_rt(|| {
|
|
||||||
tokio::runtime::Builder::new_multi_thread()
|
|
||||||
.enable_all()
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
})
|
|
||||||
.block_on(async {
|
|
||||||
let make_service =
|
|
||||||
make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(handle)) });
|
|
||||||
|
|
||||||
let server =
|
|
||||||
Server::bind(&SocketAddr::from(([127, 0, 0, 1], 3000))).serve(make_service);
|
|
||||||
|
|
||||||
if let Err(e) = server.await {
|
|
||||||
eprintln!("server error: {}", e);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -16,7 +16,7 @@ use crate::system::{System, SystemCommand};
|
||||||
pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0);
|
pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
thread_local!(
|
thread_local!(
|
||||||
static HANDLE: RefCell<Option<ArbiterHandle>> = RefCell::new(None);
|
static HANDLE: RefCell<Option<ArbiterHandle>> = const { RefCell::new(None) };
|
||||||
);
|
);
|
||||||
|
|
||||||
pub(crate) enum ArbiterCommand {
|
pub(crate) enum ArbiterCommand {
|
||||||
|
@ -99,8 +99,7 @@ impl Arbiter {
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
pub fn new() -> Arbiter {
|
pub fn new() -> Arbiter {
|
||||||
Self::with_tokio_rt(|| {
|
Self::with_tokio_rt(|| {
|
||||||
crate::runtime::default_tokio_runtime()
|
crate::runtime::default_tokio_runtime().expect("Cannot create new Arbiter's Runtime.")
|
||||||
.expect("Cannot create new Arbiter's Runtime.")
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +109,7 @@ impl Arbiter {
|
||||||
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
|
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
|
||||||
pub fn with_tokio_rt<F>(runtime_factory: F) -> Arbiter
|
pub fn with_tokio_rt<F>(runtime_factory: F) -> Arbiter
|
||||||
where
|
where
|
||||||
F: Fn() -> tokio::runtime::Runtime + Send + 'static,
|
F: FnOnce() -> tokio::runtime::Runtime + Send + 'static,
|
||||||
{
|
{
|
||||||
let sys = System::current();
|
let sys = System::current();
|
||||||
let system_id = sys.id();
|
let system_id = sys.id();
|
||||||
|
@ -149,9 +148,7 @@ impl Arbiter {
|
||||||
.send(SystemCommand::DeregisterArbiter(arb_id));
|
.send(SystemCommand::DeregisterArbiter(arb_id));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| panic!("Cannot spawn Arbiter's thread: {name:?}: {err:?}"));
|
||||||
panic!("Cannot spawn Arbiter's thread: {:?}. {:?}", &name, err)
|
|
||||||
});
|
|
||||||
|
|
||||||
ready_rx.recv().unwrap();
|
ready_rx.recv().unwrap();
|
||||||
|
|
||||||
|
@ -201,9 +198,7 @@ impl Arbiter {
|
||||||
.send(SystemCommand::DeregisterArbiter(arb_id));
|
.send(SystemCommand::DeregisterArbiter(arb_id));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| panic!("Cannot spawn Arbiter's thread: {name:?}: {err:?}"));
|
||||||
panic!("Cannot spawn Arbiter's thread: {:?}. {:?}", &name, err)
|
|
||||||
});
|
|
||||||
|
|
||||||
ready_rx.recv().unwrap();
|
ready_rx.recv().unwrap();
|
||||||
|
|
||||||
|
@ -260,6 +255,7 @@ impl Arbiter {
|
||||||
/// If you require a result, include a response channel in the future.
|
/// If you require a result, include a response channel in the future.
|
||||||
///
|
///
|
||||||
/// Returns true if future was sent successfully and false if the Arbiter has died.
|
/// Returns true if future was sent successfully and false if the Arbiter has died.
|
||||||
|
#[track_caller]
|
||||||
pub fn spawn<Fut>(&self, future: Fut) -> bool
|
pub fn spawn<Fut>(&self, future: Fut) -> bool
|
||||||
where
|
where
|
||||||
Fut: Future<Output = ()> + Send + 'static,
|
Fut: Future<Output = ()> + Send + 'static,
|
||||||
|
@ -275,6 +271,7 @@ impl Arbiter {
|
||||||
/// channel in the function.
|
/// channel in the function.
|
||||||
///
|
///
|
||||||
/// Returns true if function was sent successfully and false if the Arbiter has died.
|
/// Returns true if function was sent successfully and false if the Arbiter has died.
|
||||||
|
#[track_caller]
|
||||||
pub fn spawn_fn<F>(&self, f: F) -> bool
|
pub fn spawn_fn<F>(&self, f: F) -> bool
|
||||||
where
|
where
|
||||||
F: FnOnce() + Send + 'static,
|
F: FnOnce() + Send + 'static,
|
||||||
|
@ -301,7 +298,7 @@ impl Future for ArbiterRunner {
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
// process all items currently buffered in channel
|
// process all items currently buffered in channel
|
||||||
loop {
|
loop {
|
||||||
match ready!(Pin::new(&mut self.rx).poll_recv(cx)) {
|
match ready!(self.rx.poll_recv(cx)) {
|
||||||
// channel closed; no more messages can be received
|
// channel closed; no more messages can be received
|
||||||
None => return Poll::Ready(()),
|
None => return Poll::Ready(()),
|
||||||
|
|
||||||
|
|
|
@ -34,14 +34,13 @@
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # `io-uring` Support
|
//! # `io-uring` Support
|
||||||
|
//!
|
||||||
//! There is experimental support for using io-uring with this crate by enabling the
|
//! There is experimental support for using io-uring with this crate by enabling the
|
||||||
//! `io-uring` feature. For now, it is semver exempt.
|
//! `io-uring` feature. For now, it is semver exempt.
|
||||||
//!
|
//!
|
||||||
//! Note that there are currently some unimplemented parts of using `actix-rt` with `io-uring`.
|
//! Note that there are currently some unimplemented parts of using `actix-rt` with `io-uring`.
|
||||||
//! In particular, when running a `System`, only `System::block_on` is supported.
|
//! In particular, when running a `System`, only `System::block_on` is supported.
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible, missing_docs)]
|
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
@ -51,13 +50,10 @@ compile_error!("io_uring is a linux only feature.");
|
||||||
|
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
||||||
use tokio::task::JoinHandle;
|
|
||||||
|
|
||||||
// Cannot define a main macro when compiled into test harness.
|
// Cannot define a main macro when compiled into test harness.
|
||||||
// Workaround for https://github.com/rust-lang/rust/issues/62127.
|
// Workaround for https://github.com/rust-lang/rust/issues/62127.
|
||||||
#[cfg(all(feature = "macros", not(test)))]
|
#[cfg(all(feature = "macros", not(test)))]
|
||||||
pub use actix_macros::main;
|
pub use actix_macros::main;
|
||||||
|
|
||||||
#[cfg(feature = "macros")]
|
#[cfg(feature = "macros")]
|
||||||
pub use actix_macros::test;
|
pub use actix_macros::test;
|
||||||
|
|
||||||
|
@ -65,11 +61,14 @@ mod arbiter;
|
||||||
mod runtime;
|
mod runtime;
|
||||||
mod system;
|
mod system;
|
||||||
|
|
||||||
pub use self::arbiter::{Arbiter, ArbiterHandle};
|
|
||||||
pub use self::runtime::Runtime;
|
|
||||||
pub use self::system::{System, SystemRunner};
|
|
||||||
|
|
||||||
pub use tokio::pin;
|
pub use tokio::pin;
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
|
pub use self::{
|
||||||
|
arbiter::{Arbiter, ArbiterHandle},
|
||||||
|
runtime::Runtime,
|
||||||
|
system::{System, SystemRunner},
|
||||||
|
};
|
||||||
|
|
||||||
pub mod signal {
|
pub mod signal {
|
||||||
//! Asynchronous signal handling (Tokio re-exports).
|
//! Asynchronous signal handling (Tokio re-exports).
|
||||||
|
@ -91,13 +90,13 @@ pub mod net {
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use tokio::io::Ready;
|
|
||||||
use tokio::io::{AsyncRead, AsyncWrite, Interest};
|
use tokio::io::{AsyncRead, AsyncWrite, Interest};
|
||||||
pub use tokio::net::UdpSocket;
|
|
||||||
pub use tokio::net::{TcpListener, TcpSocket, TcpStream};
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub use tokio::net::{UnixDatagram, UnixListener, UnixStream};
|
pub use tokio::net::{UnixDatagram, UnixListener, UnixStream};
|
||||||
|
pub use tokio::{
|
||||||
|
io::Ready,
|
||||||
|
net::{TcpListener, TcpSocket, TcpStream, UdpSocket},
|
||||||
|
};
|
||||||
|
|
||||||
/// Extension trait over async read+write types that can also signal readiness.
|
/// Extension trait over async read+write types that can also signal readiness.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
@ -156,10 +155,9 @@ pub mod net {
|
||||||
pub mod time {
|
pub mod time {
|
||||||
//! Utilities for tracking time (Tokio re-exports).
|
//! Utilities for tracking time (Tokio re-exports).
|
||||||
|
|
||||||
pub use tokio::time::Instant;
|
pub use tokio::time::{
|
||||||
pub use tokio::time::{interval, interval_at, Interval};
|
interval, interval_at, sleep, sleep_until, timeout, Instant, Interval, Sleep, Timeout,
|
||||||
pub use tokio::time::{sleep, sleep_until, Sleep};
|
};
|
||||||
pub use tokio::time::{timeout, Timeout};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod task {
|
pub mod task {
|
||||||
|
@ -198,6 +196,7 @@ pub mod task {
|
||||||
/// assert!(handle.await.unwrap_err().is_cancelled());
|
/// assert!(handle.await.unwrap_err().is_cancelled());
|
||||||
/// # });
|
/// # });
|
||||||
/// ```
|
/// ```
|
||||||
|
#[track_caller]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn spawn<Fut>(f: Fut) -> JoinHandle<Fut::Output>
|
pub fn spawn<Fut>(f: Fut) -> JoinHandle<Fut::Output>
|
||||||
where
|
where
|
||||||
|
|
|
@ -53,6 +53,7 @@ impl Runtime {
|
||||||
/// # Panics
|
/// # Panics
|
||||||
/// This function panics if the spawn fails. Failure occurs if the executor is currently at
|
/// This function panics if the spawn fails. Failure occurs if the executor is currently at
|
||||||
/// capacity and is unable to spawn a new future.
|
/// capacity and is unable to spawn a new future.
|
||||||
|
#[track_caller]
|
||||||
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
|
pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
|
||||||
where
|
where
|
||||||
F: Future + 'static,
|
F: Future + 'static,
|
||||||
|
@ -60,6 +61,62 @@ impl Runtime {
|
||||||
self.local.spawn_local(future)
|
self.local.spawn_local(future)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves a reference to the underlying Tokio runtime associated with this instance.
|
||||||
|
///
|
||||||
|
/// The Tokio runtime is responsible for executing asynchronous tasks and managing
|
||||||
|
/// the event loop for an asynchronous Rust program. This method allows accessing
|
||||||
|
/// the runtime to interact with its features directly.
|
||||||
|
///
|
||||||
|
/// In a typical use case, you might need to share the same runtime between different
|
||||||
|
/// modules of your project. For example, a module might require a `tokio::runtime::Handle`
|
||||||
|
/// to spawn tasks on the same runtime, or the runtime itself to configure more complex
|
||||||
|
/// behaviours.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use actix_rt::Runtime;
|
||||||
|
///
|
||||||
|
/// mod module_a {
|
||||||
|
/// pub fn do_something(handle: tokio::runtime::Handle) {
|
||||||
|
/// handle.spawn(async {
|
||||||
|
/// // Some asynchronous task here
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// mod module_b {
|
||||||
|
/// pub fn do_something_else(rt: &tokio::runtime::Runtime) {
|
||||||
|
/// rt.spawn(async {
|
||||||
|
/// // Another asynchronous task here
|
||||||
|
/// });
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let actix_runtime = actix_rt::Runtime::new().unwrap();
|
||||||
|
/// let tokio_runtime = actix_runtime.tokio_runtime();
|
||||||
|
///
|
||||||
|
/// let handle = tokio_runtime.handle().clone();
|
||||||
|
///
|
||||||
|
/// module_a::do_something(handle);
|
||||||
|
/// module_b::do_something_else(tokio_runtime);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// An immutable reference to the `tokio::runtime::Runtime` instance associated with this
|
||||||
|
/// `Runtime` instance.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// While this method provides an immutable reference to the Tokio runtime, which is safe to share across threads,
|
||||||
|
/// be aware that spawning blocking tasks on the Tokio runtime could potentially impact the execution
|
||||||
|
/// of the Actix runtime. This is because Tokio is responsible for driving the Actix system,
|
||||||
|
/// and blocking tasks could delay or deadlock other tasks in run loop.
|
||||||
|
pub fn tokio_runtime(&self) -> &tokio::runtime::Runtime {
|
||||||
|
&self.rt
|
||||||
|
}
|
||||||
|
|
||||||
/// Runs the provided future, blocking the current thread until the future completes.
|
/// Runs the provided future, blocking the current thread until the future completes.
|
||||||
///
|
///
|
||||||
/// This function can be used to synchronously block the current thread until the provided
|
/// This function can be used to synchronously block the current thread until the provided
|
||||||
|
@ -73,6 +130,7 @@ impl Runtime {
|
||||||
///
|
///
|
||||||
/// The caller is responsible for ensuring that other spawned futures complete execution by
|
/// The caller is responsible for ensuring that other spawned futures complete execution by
|
||||||
/// calling `block_on` or `run`.
|
/// calling `block_on` or `run`.
|
||||||
|
#[track_caller]
|
||||||
pub fn block_on<F>(&self, f: F) -> F::Output
|
pub fn block_on<F>(&self, f: F) -> F::Output
|
||||||
where
|
where
|
||||||
F: Future,
|
F: Future,
|
||||||
|
|
|
@ -16,7 +16,7 @@ use crate::{arbiter::ArbiterHandle, Arbiter};
|
||||||
static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0);
|
static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
thread_local!(
|
thread_local!(
|
||||||
static CURRENT: RefCell<Option<System>> = RefCell::new(None);
|
static CURRENT: RefCell<Option<System>> = const { RefCell::new(None) };
|
||||||
);
|
);
|
||||||
|
|
||||||
/// A manager for a per-thread distributed async runtime.
|
/// A manager for a per-thread distributed async runtime.
|
||||||
|
@ -48,7 +48,7 @@ impl System {
|
||||||
/// [tokio-runtime]: tokio::runtime::Runtime
|
/// [tokio-runtime]: tokio::runtime::Runtime
|
||||||
pub fn with_tokio_rt<F>(runtime_factory: F) -> SystemRunner
|
pub fn with_tokio_rt<F>(runtime_factory: F) -> SystemRunner
|
||||||
where
|
where
|
||||||
F: Fn() -> tokio::runtime::Runtime,
|
F: FnOnce() -> tokio::runtime::Runtime,
|
||||||
{
|
{
|
||||||
let (stop_tx, stop_rx) = oneshot::channel();
|
let (stop_tx, stop_rx) = oneshot::channel();
|
||||||
let (sys_tx, sys_rx) = mpsc::unbounded_channel();
|
let (sys_tx, sys_rx) = mpsc::unbounded_channel();
|
||||||
|
@ -87,7 +87,7 @@ impl System {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn with_tokio_rt<F>(_: F) -> SystemRunner
|
pub fn with_tokio_rt<F>(_: F) -> SystemRunner
|
||||||
where
|
where
|
||||||
F: Fn() -> tokio::runtime::Runtime,
|
F: FnOnce() -> tokio::runtime::Runtime,
|
||||||
{
|
{
|
||||||
unimplemented!("System::with_tokio_rt is not implemented for io-uring feature yet")
|
unimplemented!("System::with_tokio_rt is not implemented for io-uring feature yet")
|
||||||
}
|
}
|
||||||
|
@ -203,7 +203,42 @@ impl SystemRunner {
|
||||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves a reference to the underlying [Actix runtime](crate::Runtime) associated with this
|
||||||
|
/// `SystemRunner` instance.
|
||||||
|
///
|
||||||
|
/// The Actix runtime is responsible for managing the event loop for an Actix system and
|
||||||
|
/// executing asynchronous tasks. This method provides access to the runtime, allowing direct
|
||||||
|
/// interaction with its features.
|
||||||
|
///
|
||||||
|
/// In a typical use case, you might need to share the same runtime between different
|
||||||
|
/// parts of your project. For example, some components might require a [`Runtime`] to spawn
|
||||||
|
/// tasks on the same runtime.
|
||||||
|
///
|
||||||
|
/// Read more in the documentation for [`Runtime`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let system_runner = actix_rt::System::new();
|
||||||
|
/// let actix_runtime = system_runner.runtime();
|
||||||
|
///
|
||||||
|
/// // Use the runtime to spawn an async task or perform other operations
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// While this method provides an immutable reference to the Actix runtime, which is safe to
|
||||||
|
/// share across threads, be aware that spawning blocking tasks on the Actix runtime could
|
||||||
|
/// potentially impact system performance. This is because the Actix runtime is responsible for
|
||||||
|
/// driving the system, and blocking tasks could delay other tasks in the run loop.
|
||||||
|
///
|
||||||
|
/// [`Runtime`]: crate::Runtime
|
||||||
|
pub fn runtime(&self) -> &crate::runtime::Runtime {
|
||||||
|
&self.rt
|
||||||
|
}
|
||||||
|
|
||||||
/// Runs the provided future, blocking the current thread until the future completes.
|
/// Runs the provided future, blocking the current thread until the future completes.
|
||||||
|
#[track_caller]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn block_on<F: Future>(&self, fut: F) -> F::Output {
|
pub fn block_on<F: Future>(&self, fut: F) -> F::Output {
|
||||||
self.rt.block_on(fut)
|
self.rt.block_on(fut)
|
||||||
|
@ -225,9 +260,7 @@ impl SystemRunner {
|
||||||
|
|
||||||
/// Runs the event loop until [stopped](System::stop_with_code), returning the exit code.
|
/// Runs the event loop until [stopped](System::stop_with_code), returning the exit code.
|
||||||
pub fn run_with_code(self) -> io::Result<i32> {
|
pub fn run_with_code(self) -> io::Result<i32> {
|
||||||
unimplemented!(
|
unimplemented!("SystemRunner::run_with_code is not implemented for io-uring feature yet");
|
||||||
"SystemRunner::run_with_code is not implemented for io-uring feature yet"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the provided future, blocking the current thread until the future completes.
|
/// Runs the provided future, blocking the current thread until the future completes.
|
||||||
|
@ -291,7 +324,7 @@ impl Future for SystemController {
|
||||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
// process all items currently buffered in channel
|
// process all items currently buffered in channel
|
||||||
loop {
|
loop {
|
||||||
match ready!(Pin::new(&mut self.cmd_rx).poll_recv(cx)) {
|
match ready!(self.cmd_rx.poll_recv(cx)) {
|
||||||
// channel closed; no more messages can be received
|
// channel closed; no more messages can be received
|
||||||
None => return Poll::Ready(()),
|
None => return Poll::Ready(()),
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_rt::{task::JoinError, Arbiter, System};
|
use actix_rt::{task::JoinError, Arbiter, System};
|
||||||
|
|
||||||
#[cfg(not(feature = "io-uring"))]
|
#[cfg(not(feature = "io-uring"))]
|
||||||
use {
|
use {
|
||||||
std::{sync::mpsc::channel, thread},
|
std::{sync::mpsc::channel, thread},
|
||||||
|
@ -359,7 +360,7 @@ fn tokio_uring_arbiter() {
|
||||||
let f = tokio_uring::fs::File::create("test.txt").await.unwrap();
|
let f = tokio_uring::fs::File::create("test.txt").await.unwrap();
|
||||||
let buf = b"Hello World!";
|
let buf = b"Hello World!";
|
||||||
|
|
||||||
let (res, _) = f.write_at(&buf[..], 0).await;
|
let (res, _) = f.write_all_at(&buf[..], 0).await;
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
|
|
||||||
f.sync_all().await.unwrap();
|
f.sync_all().await.unwrap();
|
||||||
|
|
|
@ -1,231 +1,232 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased
|
||||||
|
|
||||||
|
## 2.5.1
|
||||||
|
|
||||||
|
- Fix panic in test server.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
|
## 2.5.0
|
||||||
|
|
||||||
|
- Update `mio` dependency to `1`.
|
||||||
|
|
||||||
|
## 2.4.0
|
||||||
|
|
||||||
|
- Update `tokio-uring` dependency to `0.5`.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.70.
|
||||||
|
|
||||||
|
## 2.3.0
|
||||||
|
|
||||||
|
- Add support for MultiPath TCP (MPTCP) with `MpTcp` enum and `ServerBuilder::mptcp()` method.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.65.
|
||||||
|
|
||||||
|
## 2.2.0
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.59.
|
||||||
|
- Update `tokio-uring` dependency to `0.4`.
|
||||||
|
|
||||||
|
## 2.1.1
|
||||||
|
|
||||||
|
- No significant changes since `2.1.0`.
|
||||||
|
|
||||||
|
## 2.1.0
|
||||||
|
|
||||||
|
- Update `tokio-uring` dependency to `0.3`.
|
||||||
|
- Logs emitted now use the `tracing` crate with `log` compatibility.
|
||||||
|
- Wait for accept thread to stop before sending completion signal.
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
- No significant changes since `2.0.0-rc.4`.
|
||||||
|
|
||||||
|
## 2.0.0-rc.4
|
||||||
|
|
||||||
|
- Update `tokio-uring` dependency to `0.2`.
|
||||||
|
|
||||||
|
## 2.0.0-rc.3
|
||||||
|
|
||||||
## 2.0.0-rc.3 - 2021-12-31
|
|
||||||
- No significant changes since `2.0.0-rc.2`.
|
- No significant changes since `2.0.0-rc.2`.
|
||||||
|
|
||||||
|
## 2.0.0-rc.2
|
||||||
|
|
||||||
## 2.0.0-rc.2 - 2021-12-27
|
- Simplify `TestServer`.
|
||||||
- Simplify `TestServer`. [#431]
|
|
||||||
|
|
||||||
[#431]: https://github.com/actix/actix-net/pull/431
|
## 2.0.0-rc.1
|
||||||
|
|
||||||
|
- Hide implementation details of `Server`.
|
||||||
|
- `Server` now runs only after awaiting it.
|
||||||
|
|
||||||
## 2.0.0-rc.1 - 2021-12-05
|
## 2.0.0-beta.9
|
||||||
- Hide implementation details of `Server`. [#424]
|
|
||||||
- `Server` now runs only after awaiting it. [#425]
|
|
||||||
|
|
||||||
[#424]: https://github.com/actix/actix-net/pull/424
|
- Restore `Arbiter` support lost in `beta.8`.
|
||||||
[#425]: https://github.com/actix/actix-net/pull/425
|
|
||||||
|
|
||||||
|
## 2.0.0-beta.8
|
||||||
|
|
||||||
## 2.0.0-beta.9 - 2021-11-15
|
- Fix non-unix signal handler.
|
||||||
- Restore `Arbiter` support lost in `beta.8`. [#417]
|
|
||||||
|
|
||||||
[#417]: https://github.com/actix/actix-net/pull/417
|
## 2.0.0-beta.7
|
||||||
|
|
||||||
|
- Server can be started in regular Tokio runtime.
|
||||||
## 2.0.0-beta.8 - 2021-11-05 _(YANKED)_
|
- Expose new `Server` type whose `Future` impl resolves when server stops.
|
||||||
- Fix non-unix signal handler. [#410]
|
- Rename `Server` to `ServerHandle`.
|
||||||
|
- Add `Server::handle` to obtain handle to server.
|
||||||
[#410]: https://github.com/actix/actix-net/pull/410
|
- Rename `ServerBuilder::{maxconn => max_concurrent_connections}`.
|
||||||
|
- Deprecate crate-level `new` shortcut for server builder.
|
||||||
|
|
||||||
## 2.0.0-beta.7 - 2021-11-05 _(YANKED)_
|
|
||||||
- Server can be started in regular Tokio runtime. [#408]
|
|
||||||
- Expose new `Server` type whose `Future` impl resolves when server stops. [#408]
|
|
||||||
- Rename `Server` to `ServerHandle`. [#407]
|
|
||||||
- Add `Server::handle` to obtain handle to server. [#408]
|
|
||||||
- Rename `ServerBuilder::{maxconn => max_concurrent_connections}`. [#407]
|
|
||||||
- Deprecate crate-level `new` shortcut for server builder. [#408]
|
|
||||||
- Minimum supported Rust version (MSRV) is now 1.52.
|
- Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
[#407]: https://github.com/actix/actix-net/pull/407
|
## 2.0.0-beta.6
|
||||||
[#408]: https://github.com/actix/actix-net/pull/408
|
|
||||||
|
|
||||||
|
- Add experimental (semver-exempt) `io-uring` feature for enabling async file I/O on linux.
|
||||||
|
- Server no long listens to `SIGHUP` signal. Previously, the received was not used but did block subsequent exit signals from working.
|
||||||
|
- Remove `config` module. `ServiceConfig`, `ServiceRuntime` public types are removed due to this change.
|
||||||
|
- Remove `ServerBuilder::configure`.
|
||||||
|
|
||||||
## 2.0.0-beta.6 - 2021-10-11
|
## 2.0.0-beta.5
|
||||||
- Add experimental (semver-exempt) `io-uring` feature for enabling async file I/O on linux. [#374]
|
|
||||||
- Server no long listens to `SIGHUP` signal. Previously, the received was not used but did block
|
|
||||||
subsequent exit signals from working. [#389]
|
|
||||||
- Remove `config` module. `ServiceConfig`, `ServiceRuntime` public types are removed due to
|
|
||||||
this change. [#349]
|
|
||||||
- Remove `ServerBuilder::configure` [#349]
|
|
||||||
|
|
||||||
[#374]: https://github.com/actix/actix-net/pull/374
|
- Server shutdown notifies all workers to exit regardless if shutdown is graceful. This causes all workers to shutdown immediately in force shutdown case.
|
||||||
[#349]: https://github.com/actix/actix-net/pull/349
|
|
||||||
[#389]: https://github.com/actix/actix-net/pull/389
|
|
||||||
|
|
||||||
|
## 2.0.0-beta.4
|
||||||
|
|
||||||
## 2.0.0-beta.5 - 2021-04-20
|
|
||||||
- Server shutdown notifies all workers to exit regardless if shutdown is graceful. This causes all
|
|
||||||
workers to shutdown immediately in force shutdown case. [#333]
|
|
||||||
|
|
||||||
[#333]: https://github.com/actix/actix-net/pull/333
|
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0-beta.4 - 2021-04-01
|
|
||||||
- Prevent panic when `shutdown_timeout` is very large. [f9262db]
|
- Prevent panic when `shutdown_timeout` is very large. [f9262db]
|
||||||
|
|
||||||
[f9262db]: https://github.com/actix/actix-net/commit/f9262db
|
## 2.0.0-beta.3
|
||||||
|
|
||||||
|
- Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`.
|
||||||
|
- Add retry for EINTR signal (`io::Interrupted`) in `Accept`'s poll loop.
|
||||||
|
- Add `ServerBuilder::worker_max_blocking_threads` to customize blocking thread pool size.
|
||||||
|
- Update `actix-rt` to `2.0.0`.
|
||||||
|
|
||||||
## 2.0.0-beta.3 - 2021-02-06
|
## 2.0.0-beta.2
|
||||||
- Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`. [#246]
|
|
||||||
- Add retry for EINTR signal (`io::Interrupted`) in `Accept`'s poll loop. [#264]
|
|
||||||
- Add `ServerBuilder::worker_max_blocking_threads` to customize blocking thread pool size. [#265]
|
|
||||||
- Update `actix-rt` to `2.0.0`. [#273]
|
|
||||||
|
|
||||||
[#246]: https://github.com/actix/actix-net/pull/246
|
- Merge `actix-testing` to `actix-server` as `test_server` mod.
|
||||||
[#264]: https://github.com/actix/actix-net/pull/264
|
|
||||||
[#265]: https://github.com/actix/actix-net/pull/265
|
|
||||||
[#273]: https://github.com/actix/actix-net/pull/273
|
|
||||||
|
|
||||||
|
## 2.0.0-beta.1
|
||||||
|
|
||||||
## 2.0.0-beta.2 - 2021-01-03
|
- Added explicit info log message on accept queue pause.
|
||||||
- Merge `actix-testing` to `actix-server` as `test_server` mod. [#242]
|
- Prevent double registration of sockets when back-pressure is resolved.
|
||||||
|
- Update `mio` dependency to `0.7.3`.
|
||||||
|
- Remove `socket2` dependency.
|
||||||
|
- `ServerBuilder::backlog` now accepts `u32` instead of `i32`.
|
||||||
|
- Remove `AcceptNotify` type and pass `WakerQueue` to `Worker` to wake up `Accept`'s `Poll`.
|
||||||
|
- Convert `mio::net::TcpStream` to `actix_rt::net::TcpStream`(`UnixStream` for uds) using `FromRawFd` and `IntoRawFd`(`FromRawSocket` and `IntoRawSocket` on windows).
|
||||||
|
- Remove `AsyncRead` and `AsyncWrite` trait bound for `socket::FromStream` trait.
|
||||||
|
|
||||||
[#242]: https://github.com/actix/actix-net/pull/242
|
## 1.0.4
|
||||||
|
|
||||||
|
|
||||||
## 2.0.0-beta.1 - 2020-12-28
|
|
||||||
- Added explicit info log message on accept queue pause. [#215]
|
|
||||||
- Prevent double registration of sockets when back-pressure is resolved. [#223]
|
|
||||||
- Update `mio` dependency to `0.7.3`. [#239]
|
|
||||||
- Remove `socket2` dependency. [#239]
|
|
||||||
- `ServerBuilder::backlog` now accepts `u32` instead of `i32`. [#239]
|
|
||||||
- Remove `AcceptNotify` type and pass `WakerQueue` to `Worker` to wake up `Accept`'s `Poll`. [#239]
|
|
||||||
- Convert `mio::net::TcpStream` to `actix_rt::net::TcpStream`(`UnixStream` for uds) using
|
|
||||||
`FromRawFd` and `IntoRawFd`(`FromRawSocket` and `IntoRawSocket` on windows). [#239]
|
|
||||||
- Remove `AsyncRead` and `AsyncWrite` trait bound for `socket::FromStream` trait. [#239]
|
|
||||||
|
|
||||||
[#215]: https://github.com/actix/actix-net/pull/215
|
|
||||||
[#223]: https://github.com/actix/actix-net/pull/223
|
|
||||||
[#239]: https://github.com/actix/actix-net/pull/239
|
|
||||||
|
|
||||||
|
|
||||||
## 1.0.4 - 2020-09-12
|
|
||||||
- Update actix-codec to 0.3.0.
|
- Update actix-codec to 0.3.0.
|
||||||
- Workers must be greater than 0. [#167]
|
- Workers must be greater than 0.
|
||||||
|
|
||||||
[#167]: https://github.com/actix/actix-net/pull/167
|
## 1.0.3
|
||||||
|
|
||||||
|
- Replace deprecated `net2` crate with `socket2`.
|
||||||
|
|
||||||
## 1.0.3 - 2020-05-19
|
## 1.0.2
|
||||||
- Replace deprecated `net2` crate with `socket2` [#140]
|
|
||||||
|
|
||||||
[#140]: https://github.com/actix/actix-net/pull/140
|
- Avoid error by calling `reregister()` on Windows.
|
||||||
|
|
||||||
|
## 1.0.1
|
||||||
|
|
||||||
## 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
|
|
||||||
- Rename `.start()` method to `.run()`
|
- Rename `.start()` method to `.run()`
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
## 1.0.0 - 2019-12-11
|
|
||||||
- Use actix-net releases
|
- Use actix-net releases
|
||||||
|
|
||||||
|
## 1.0.0-alpha.4
|
||||||
|
|
||||||
## 1.0.0-alpha.4 - 2019-12-08
|
|
||||||
- Use actix-service 1.0.0-alpha.4
|
- Use actix-service 1.0.0-alpha.4
|
||||||
|
|
||||||
|
## 1.0.0-alpha.3
|
||||||
|
|
||||||
## 1.0.0-alpha.3 - 2019-12-07
|
|
||||||
- Migrate to tokio 0.2
|
- Migrate to tokio 0.2
|
||||||
- Fix compilation on non-unix platforms
|
- Fix compilation on non-unix platforms
|
||||||
- Better handling server configuration
|
- Better handling server configuration
|
||||||
|
|
||||||
|
## 1.0.0-alpha.2
|
||||||
|
|
||||||
## 1.0.0-alpha.2 - 2019-12-02
|
|
||||||
- Simplify server service (remove actix-server-config)
|
- Simplify server service (remove actix-server-config)
|
||||||
- Allow to wait on `Server` until server stops
|
- Allow to wait on `Server` until server stops
|
||||||
|
|
||||||
|
## 0.8.0-alpha.1
|
||||||
|
|
||||||
## 0.8.0-alpha.1 - 2019-11-22
|
|
||||||
- Migrate to `std::future`
|
- Migrate to `std::future`
|
||||||
|
|
||||||
|
## 0.7.0
|
||||||
|
|
||||||
## 0.7.0 - 2019-10-04
|
|
||||||
- Update `rustls` to 0.16
|
- Update `rustls` to 0.16
|
||||||
- Minimum required Rust version upped to 1.37.0
|
- Minimum required Rust version upped to 1.37.0
|
||||||
|
|
||||||
|
## 0.6.1
|
||||||
|
|
||||||
## 0.6.1 - 2019-09-25
|
|
||||||
- Add UDS listening support to `ServerBuilder`
|
- Add UDS listening support to `ServerBuilder`
|
||||||
|
|
||||||
|
## 0.6.0
|
||||||
|
|
||||||
## 0.6.0 - 2019-07-18
|
|
||||||
- Support Unix domain sockets #3
|
- Support Unix domain sockets #3
|
||||||
|
|
||||||
|
## 0.5.1
|
||||||
|
|
||||||
## 0.5.1 - 2019-05-18
|
|
||||||
- ServerBuilder::shutdown_timeout() accepts u64
|
- ServerBuilder::shutdown_timeout() accepts u64
|
||||||
|
|
||||||
|
## 0.5.0
|
||||||
|
|
||||||
## 0.5.0 - 2019-05-12
|
|
||||||
- Add `Debug` impl for `SslError`
|
- Add `Debug` impl for `SslError`
|
||||||
- Derive debug for `Server` and `ServerCommand`
|
- Derive debug for `Server` and `ServerCommand`
|
||||||
- Upgrade to actix-service 0.4
|
- Upgrade to actix-service 0.4
|
||||||
|
|
||||||
|
## 0.4.3
|
||||||
|
|
||||||
## 0.4.3 - 2019-04-16
|
|
||||||
- Re-export `IoStream` trait
|
- Re-export `IoStream` trait
|
||||||
- Depend on `ssl` and `rust-tls` features from actix-server-config
|
- Depend on `ssl` and `rust-tls` features from actix-server-config
|
||||||
|
|
||||||
|
## 0.4.2
|
||||||
|
|
||||||
## 0.4.2 - 2019-03-30
|
|
||||||
- Fix SIGINT force shutdown
|
- Fix SIGINT force shutdown
|
||||||
|
|
||||||
|
## 0.4.1
|
||||||
|
|
||||||
## 0.4.1 - 2019-03-14
|
|
||||||
- `SystemRuntime::on_start()` - allow to run future before server service initialization
|
- `SystemRuntime::on_start()` - allow to run future before server service initialization
|
||||||
|
|
||||||
|
## 0.4.0
|
||||||
|
|
||||||
## 0.4.0 - 2019-03-12
|
|
||||||
- Use `ServerConfig` for service factory
|
- Use `ServerConfig` for service factory
|
||||||
- Wrap tcp socket to `Io` type
|
- Wrap tcp socket to `Io` type
|
||||||
- Upgrade actix-service
|
- Upgrade actix-service
|
||||||
|
|
||||||
|
## 0.3.1
|
||||||
|
|
||||||
## 0.3.1 - 2019-03-04
|
|
||||||
- Add `ServerBuilder::maxconnrate` sets the maximum per-worker number of concurrent connections
|
- Add `ServerBuilder::maxconnrate` sets the maximum per-worker number of concurrent connections
|
||||||
- Add helper ssl error `SslError`
|
- Add helper ssl error `SslError`
|
||||||
- Rename `StreamServiceFactory` to `ServiceFactory`
|
- Rename `StreamServiceFactory` to `ServiceFactory`
|
||||||
- Deprecate `StreamServiceFactory`
|
- Deprecate `StreamServiceFactory`
|
||||||
|
|
||||||
|
## 0.3.0
|
||||||
|
|
||||||
## 0.3.0 - 2019-03-02
|
|
||||||
- Use new `NewService` trait
|
- Use new `NewService` trait
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
## 0.2.1 - 2019-02-09
|
|
||||||
- Drop service response
|
- Drop service response
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
## 0.2.0 - 2019-02-01
|
|
||||||
- Migrate to actix-service 0.2
|
- Migrate to actix-service 0.2
|
||||||
- Updated rustls dependency
|
- Updated rustls dependency
|
||||||
|
|
||||||
|
## 0.1.3
|
||||||
|
|
||||||
## 0.1.3 - 2018-12-21
|
|
||||||
- Fix max concurrent connections handling
|
- Fix max concurrent connections handling
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
|
||||||
## 0.1.2 - 2018-12-12
|
|
||||||
- rename ServiceConfig::rt() to ServiceConfig::apply()
|
- rename ServiceConfig::rt() to ServiceConfig::apply()
|
||||||
- Fix back-pressure for concurrent ssl handshakes
|
- Fix back-pressure for concurrent ssl handshakes
|
||||||
|
|
||||||
|
## 0.1.1
|
||||||
|
|
||||||
## 0.1.1 - 2018-12-11
|
|
||||||
- Fix signal handling on windows
|
- Fix signal handling on windows
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
## 0.1.0 - 2018-12-09
|
|
||||||
- Move server to separate crate
|
- Move server to separate crate
|
||||||
|
|
|
@ -1,49 +1,50 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-server"
|
name = "actix-server"
|
||||||
version = "2.0.0-rc.3"
|
version = "2.5.1"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"fakeshadow <24548779@qq.com>",
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",
|
||||||
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",
|
|
||||||
]
|
]
|
||||||
description = "General purpose TCP server built for the Actix ecosystem"
|
description = "General purpose TCP server built for the Actix ecosystem"
|
||||||
keywords = ["network", "tcp", "server", "framework", "async"]
|
keywords = ["network", "tcp", "server", "framework", "async"]
|
||||||
categories = ["network-programming", "asynchronous"]
|
categories = ["network-programming", "asynchronous"]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
repository = "https://github.com/actix/actix-net/tree/master/actix-server"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
[lib]
|
[package.metadata.cargo_check_external_types]
|
||||||
name = "actix_server"
|
allowed_external_types = ["tokio::*"]
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
io-uring = ["tokio-uring", "actix-rt/io-uring"]
|
io-uring = ["tokio-uring", "actix-rt/io-uring"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-rt = { version = "2.4.0", default-features = false }
|
actix-rt = { version = "2.10", default-features = false }
|
||||||
actix-service = "2.0.0"
|
actix-service = "2"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3"
|
||||||
|
futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] }
|
||||||
|
futures-util = { version = "0.3.17", default-features = false, features = ["alloc"] }
|
||||||
|
mio = { version = "1", features = ["os-poll", "net"] }
|
||||||
|
socket2 = "0.5"
|
||||||
|
tokio = { version = "1.23.1", features = ["sync"] }
|
||||||
|
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||||
|
|
||||||
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
# runtime for `io-uring` feature
|
||||||
futures-util = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
log = "0.4"
|
tokio-uring = { version = "0.5", optional = true }
|
||||||
mio = { version = "0.8", features = ["os-poll", "net"] }
|
|
||||||
num_cpus = "1.13"
|
|
||||||
socket2 = "0.4.2"
|
|
||||||
tokio = { version = "1.13.1", features = ["sync"] }
|
|
||||||
|
|
||||||
# runtime for io-uring feature
|
|
||||||
tokio-uring = { version = "0.1", optional = true }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-codec = "0.4.2"
|
actix-codec = "0.5"
|
||||||
actix-rt = "2.4.0"
|
actix-rt = "2.8"
|
||||||
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
env_logger = "0.9"
|
futures-util = { version = "0.3.17", default-features = false, features = ["sink", "async-await-macro"] }
|
||||||
futures-util = { version = "0.3.7", default-features = false, features = ["sink", "async-await-macro"] }
|
pretty_env_logger = "0.5"
|
||||||
tokio = { version = "1.13.1", features = ["io-util", "rt-multi-thread", "macros", "fs"] }
|
tokio = { version = "1.23.1", features = ["io-util", "rt-multi-thread", "macros", "fs"] }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# actix-server
|
||||||
|
|
||||||
|
> General purpose TCP server built for the Actix ecosystem.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
|
[](https://crates.io/crates/actix-server)
|
||||||
|
[](https://docs.rs/actix-server/2.5.1)
|
||||||
|
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||||
|

|
||||||
|
<br />
|
||||||
|
[](https://deps.rs/crate/actix-server/2.5.1)
|
||||||
|

|
||||||
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Library Documentation](https://docs.rs/actix-server)
|
||||||
|
- [Examples](/actix-server/examples)
|
|
@ -8,6 +8,8 @@
|
||||||
//!
|
//!
|
||||||
//! Follow the prompt and enter a file path, relative or absolute.
|
//! Follow the prompt and enter a file path, relative or absolute.
|
||||||
|
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use actix_codec::{Framed, LinesCodec};
|
use actix_codec::{Framed, LinesCodec};
|
||||||
|
@ -18,10 +20,11 @@ use futures_util::{SinkExt as _, StreamExt as _};
|
||||||
use tokio::{fs::File, io::AsyncReadExt as _};
|
use tokio::{fs::File, io::AsyncReadExt as _};
|
||||||
|
|
||||||
async fn run() -> io::Result<()> {
|
async fn run() -> io::Result<()> {
|
||||||
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
pretty_env_logger::formatted_timed_builder()
|
||||||
|
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"));
|
||||||
|
|
||||||
let addr = ("127.0.0.1", 8080);
|
let addr = ("127.0.0.1", 8080);
|
||||||
log::info!("starting server on port: {}", &addr.0);
|
tracing::info!("starting server on port: {}", &addr.0);
|
||||||
|
|
||||||
// Bind socket address and start worker(s). By default, the server uses the number of physical
|
// Bind socket address and start worker(s). By default, the server uses the number of physical
|
||||||
// CPU cores as the worker count. For this reason, the closure passed to bind needs to return
|
// CPU cores as the worker count. For this reason, the closure passed to bind needs to return
|
||||||
|
@ -39,8 +42,10 @@ async fn run() -> io::Result<()> {
|
||||||
// wait for next line
|
// wait for next line
|
||||||
match framed.next().await {
|
match framed.next().await {
|
||||||
Some(Ok(line)) => {
|
Some(Ok(line)) => {
|
||||||
match File::open(line).await {
|
match File::open(&line).await {
|
||||||
Ok(mut file) => {
|
Ok(mut file) => {
|
||||||
|
tracing::info!("reading file: {}", &line);
|
||||||
|
|
||||||
// read file into String buffer
|
// read file into String buffer
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
file.read_to_string(&mut buf).await?;
|
file.read_to_string(&mut buf).await?;
|
||||||
|
@ -52,7 +57,7 @@ async fn run() -> io::Result<()> {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("{}", err);
|
tracing::error!("{}", err);
|
||||||
framed
|
framed
|
||||||
.send("File not found or not readable. Try again.")
|
.send("File not found or not readable. Try again.")
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -72,7 +77,7 @@ async fn run() -> io::Result<()> {
|
||||||
// close connection after file has been copied to TCP stream
|
// close connection after file has been copied to TCP stream
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.map_err(|err| log::error!("Service Error: {:?}", err))
|
.map_err(|err| tracing::error!("service error: {:?}", err))
|
||||||
})?
|
})?
|
||||||
.workers(2)
|
.workers(2)
|
||||||
.run()
|
.run()
|
||||||
|
|
|
@ -22,16 +22,16 @@ use actix_server::Server;
|
||||||
use actix_service::{fn_service, ServiceFactoryExt as _};
|
use actix_service::{fn_service, ServiceFactoryExt as _};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use futures_util::future::ok;
|
use futures_util::future::ok;
|
||||||
use log::{error, info};
|
|
||||||
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
|
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
|
||||||
|
|
||||||
async fn run() -> io::Result<()> {
|
async fn run() -> io::Result<()> {
|
||||||
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
pretty_env_logger::formatted_timed_builder()
|
||||||
|
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"));
|
||||||
|
|
||||||
let count = Arc::new(AtomicUsize::new(0));
|
let count = Arc::new(AtomicUsize::new(0));
|
||||||
|
|
||||||
let addr = ("127.0.0.1", 8080);
|
let addr = ("127.0.0.1", 8080);
|
||||||
info!("starting server on port: {}", &addr.0);
|
tracing::info!("starting server on port: {}", &addr.0);
|
||||||
|
|
||||||
// Bind socket address and start worker(s). By default, the server uses the number of physical
|
// Bind socket address and start worker(s). By default, the server uses the number of physical
|
||||||
// CPU cores as the worker count. For this reason, the closure passed to bind needs to return
|
// CPU cores as the worker count. For this reason, the closure passed to bind needs to return
|
||||||
|
@ -58,14 +58,14 @@ async fn run() -> io::Result<()> {
|
||||||
|
|
||||||
// more bytes to process
|
// more bytes to process
|
||||||
Ok(bytes_read) => {
|
Ok(bytes_read) => {
|
||||||
info!("[{}] read {} bytes", num, bytes_read);
|
tracing::info!("[{}] read {} bytes", num, bytes_read);
|
||||||
stream.write_all(&buf[size..]).await.unwrap();
|
stream.write_all(&buf[size..]).await.unwrap();
|
||||||
size += bytes_read;
|
size += bytes_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
// stream error; bail from loop with error
|
// stream error; bail from loop with error
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Stream Error: {:?}", err);
|
tracing::error!("stream error: {:?}", err);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,10 +75,10 @@ async fn run() -> io::Result<()> {
|
||||||
Ok((buf.freeze(), size))
|
Ok((buf.freeze(), size))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map_err(|err| error!("Service Error: {:?}", err))
|
.map_err(|err| tracing::error!("service error: {:?}", err))
|
||||||
.and_then(move |(_, size)| {
|
.and_then(move |(_, size)| {
|
||||||
let num = num2.load(Ordering::SeqCst);
|
let num = num2.load(Ordering::SeqCst);
|
||||||
info!("[{}] total bytes read: {}", num, size);
|
tracing::info!("[{}] total bytes read: {}", num, size);
|
||||||
ok(size)
|
ok(size)
|
||||||
})
|
})
|
||||||
})?
|
})?
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::{io, thread, time::Duration};
|
use std::{io, thread, time::Duration};
|
||||||
|
|
||||||
use actix_rt::time::Instant;
|
use actix_rt::time::Instant;
|
||||||
use log::{debug, error, info};
|
|
||||||
use mio::{Interest, Poll, Token as MioToken};
|
use mio::{Interest, Poll, Token as MioToken};
|
||||||
|
use tracing::{debug, error, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
availability::Availability,
|
availability::Availability,
|
||||||
|
@ -24,7 +24,7 @@ struct ServerSocketInfo {
|
||||||
timeout: Option<actix_rt::time::Instant>,
|
timeout: Option<actix_rt::time::Instant>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// poll instance of the server.
|
/// Poll instance of the server.
|
||||||
pub(crate) struct Accept {
|
pub(crate) struct Accept {
|
||||||
poll: Poll,
|
poll: Poll,
|
||||||
waker_queue: WakerQueue,
|
waker_queue: WakerQueue,
|
||||||
|
@ -41,7 +41,7 @@ impl Accept {
|
||||||
pub(crate) fn start(
|
pub(crate) fn start(
|
||||||
sockets: Vec<(usize, MioListener)>,
|
sockets: Vec<(usize, MioListener)>,
|
||||||
builder: &ServerBuilder,
|
builder: &ServerBuilder,
|
||||||
) -> io::Result<(WakerQueue, Vec<WorkerHandleServer>)> {
|
) -> io::Result<(WakerQueue, Vec<WorkerHandleServer>, thread::JoinHandle<()>)> {
|
||||||
let handle_server = ServerHandle::new(builder.cmd_tx.clone());
|
let handle_server = ServerHandle::new(builder.cmd_tx.clone());
|
||||||
|
|
||||||
// construct poll instance and its waker
|
// construct poll instance and its waker
|
||||||
|
@ -73,12 +73,12 @@ impl Accept {
|
||||||
handle_server,
|
handle_server,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
thread::Builder::new()
|
let accept_handle = thread::Builder::new()
|
||||||
.name("actix-server acceptor".to_owned())
|
.name("actix-server acceptor".to_owned())
|
||||||
.spawn(move || accept.poll_with(&mut sockets))
|
.spawn(move || accept.poll_with(&mut sockets))
|
||||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
|
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
|
||||||
|
|
||||||
Ok((waker_queue, handles_server))
|
Ok((waker_queue, handles_server, accept_handle))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_with_sockets(
|
fn new_with_sockets(
|
||||||
|
@ -127,10 +127,10 @@ impl Accept {
|
||||||
let mut events = mio::Events::with_capacity(256);
|
let mut events = mio::Events::with_capacity(256);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if let Err(e) = self.poll.poll(&mut events, self.timeout) {
|
if let Err(err) = self.poll.poll(&mut events, self.timeout) {
|
||||||
match e.kind() {
|
match err.kind() {
|
||||||
io::ErrorKind::Interrupted => {}
|
io::ErrorKind::Interrupted => {}
|
||||||
_ => panic!("Poll error: {}", e),
|
_ => panic!("Poll error: {}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ impl Accept {
|
||||||
WAKER_TOKEN => {
|
WAKER_TOKEN => {
|
||||||
let exit = self.handle_waker(sockets);
|
let exit = self.handle_waker(sockets);
|
||||||
if exit {
|
if exit {
|
||||||
info!("Accept thread stopped");
|
info!("accept thread stopped");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,11 +161,13 @@ impl Accept {
|
||||||
// a loop that would try to drain the command channel. It's yet unknown
|
// a loop that would try to drain the command channel. It's yet unknown
|
||||||
// if it's necessary/good practice to actively drain the waker queue.
|
// if it's necessary/good practice to actively drain the waker queue.
|
||||||
loop {
|
loop {
|
||||||
// take guard with every iteration so no new interest can be added
|
// Take guard with every iteration so no new interests can be added until the current
|
||||||
// until the current task is done.
|
// task is done. Take care not to take the guard again inside this loop.
|
||||||
let mut guard = self.waker_queue.guard();
|
let mut guard = self.waker_queue.guard();
|
||||||
|
|
||||||
|
#[allow(clippy::significant_drop_in_scrutinee)]
|
||||||
match guard.pop_front() {
|
match guard.pop_front() {
|
||||||
// worker notify it becomes available.
|
// Worker notified it became available.
|
||||||
Some(WakerInterest::WorkerAvailable(idx)) => {
|
Some(WakerInterest::WorkerAvailable(idx)) => {
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
|
@ -176,7 +178,7 @@ impl Accept {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// a new worker thread is made and it's handle would be added to Accept
|
// A new worker thread has been created so store its handle.
|
||||||
Some(WakerInterest::Worker(handle)) => {
|
Some(WakerInterest::Worker(handle)) => {
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
|
@ -297,16 +299,16 @@ impl Accept {
|
||||||
|
|
||||||
fn register_logged(&self, info: &mut ServerSocketInfo) {
|
fn register_logged(&self, info: &mut ServerSocketInfo) {
|
||||||
match self.register(info) {
|
match self.register(info) {
|
||||||
Ok(_) => debug!("Resume accepting connections on {}", info.lst.local_addr()),
|
Ok(_) => debug!("resume accepting connections on {}", info.lst.local_addr()),
|
||||||
Err(e) => error!("Can not register server socket {}", e),
|
Err(err) => error!("can not register server socket {}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deregister_logged(&self, info: &mut ServerSocketInfo) {
|
fn deregister_logged(&self, info: &mut ServerSocketInfo) {
|
||||||
match self.poll.registry().deregister(&mut info.lst) {
|
match self.poll.registry().deregister(&mut info.lst) {
|
||||||
Ok(_) => debug!("Paused accepting connections on {}", info.lst.local_addr()),
|
Ok(_) => debug!("paused accepting connections on {}", info.lst.local_addr()),
|
||||||
Err(e) => {
|
Err(err) => {
|
||||||
error!("Can not deregister server socket {}", e)
|
error!("can not deregister server socket {}", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -350,7 +352,7 @@ impl Accept {
|
||||||
self.remove_next();
|
self.remove_next();
|
||||||
|
|
||||||
if self.handles.is_empty() {
|
if self.handles.is_empty() {
|
||||||
error!("No workers");
|
error!("no workers");
|
||||||
// All workers are gone and Conn is nowhere to be sent.
|
// All workers are gone and Conn is nowhere to be sent.
|
||||||
// Treat this situation as Ok and drop Conn.
|
// Treat this situation as Ok and drop Conn.
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -396,10 +398,10 @@ impl Accept {
|
||||||
let conn = Conn { io, token };
|
let conn = Conn { io, token };
|
||||||
self.accept_one(conn);
|
self.accept_one(conn);
|
||||||
}
|
}
|
||||||
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => return,
|
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => return,
|
||||||
Err(ref e) if connection_error(e) => continue,
|
Err(ref err) if connection_error(err) => continue,
|
||||||
Err(e) => {
|
Err(err) => {
|
||||||
error!("Error accepting connection: {}", e);
|
error!("error accepting connection: {}", err);
|
||||||
|
|
||||||
// deregister listener temporary
|
// deregister listener temporary
|
||||||
self.deregister_logged(info);
|
self.deregister_logged(info);
|
||||||
|
|
|
@ -1,19 +1,34 @@
|
||||||
use std::{io, time::Duration};
|
use std::{io, num::NonZeroUsize, time::Duration};
|
||||||
|
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use log::{info, trace};
|
|
||||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
server::ServerCommand,
|
server::ServerCommand,
|
||||||
service::{InternalServiceFactory, ServerServiceFactory, StreamNewService},
|
service::{InternalServiceFactory, ServerServiceFactory, StreamNewService},
|
||||||
socket::{
|
socket::{create_mio_tcp_listener, MioListener, MioTcpListener, StdTcpListener, ToSocketAddrs},
|
||||||
create_mio_tcp_listener, MioListener, MioTcpListener, StdTcpListener, ToSocketAddrs,
|
|
||||||
},
|
|
||||||
worker::ServerWorkerConfig,
|
worker::ServerWorkerConfig,
|
||||||
Server,
|
Server,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Multipath TCP (MPTCP) preference.
|
||||||
|
///
|
||||||
|
/// Currently only useful on Linux.
|
||||||
|
///
|
||||||
|
#[cfg_attr(target_os = "linux", doc = "Also see [`ServerBuilder::mptcp()`].")]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum MpTcp {
|
||||||
|
/// MPTCP will not be used when binding sockets.
|
||||||
|
Disabled,
|
||||||
|
|
||||||
|
/// MPTCP will be attempted when binding sockets. If errors occur, regular TCP will be
|
||||||
|
/// attempted, too.
|
||||||
|
TcpFallback,
|
||||||
|
|
||||||
|
/// MPTCP will be used when binding sockets (with no fallback).
|
||||||
|
NoFallback,
|
||||||
|
}
|
||||||
|
|
||||||
/// [Server] builder.
|
/// [Server] builder.
|
||||||
pub struct ServerBuilder {
|
pub struct ServerBuilder {
|
||||||
pub(crate) threads: usize,
|
pub(crate) threads: usize,
|
||||||
|
@ -21,6 +36,7 @@ pub struct ServerBuilder {
|
||||||
pub(crate) backlog: u32,
|
pub(crate) backlog: u32,
|
||||||
pub(crate) factories: Vec<Box<dyn InternalServiceFactory>>,
|
pub(crate) factories: Vec<Box<dyn InternalServiceFactory>>,
|
||||||
pub(crate) sockets: Vec<(usize, String, MioListener)>,
|
pub(crate) sockets: Vec<(usize, String, MioListener)>,
|
||||||
|
pub(crate) mptcp: MpTcp,
|
||||||
pub(crate) exit: bool,
|
pub(crate) exit: bool,
|
||||||
pub(crate) listen_os_signals: bool,
|
pub(crate) listen_os_signals: bool,
|
||||||
pub(crate) cmd_tx: UnboundedSender<ServerCommand>,
|
pub(crate) cmd_tx: UnboundedSender<ServerCommand>,
|
||||||
|
@ -40,11 +56,12 @@ impl ServerBuilder {
|
||||||
let (cmd_tx, cmd_rx) = unbounded_channel();
|
let (cmd_tx, cmd_rx) = unbounded_channel();
|
||||||
|
|
||||||
ServerBuilder {
|
ServerBuilder {
|
||||||
threads: num_cpus::get_physical(),
|
threads: std::thread::available_parallelism().map_or(2, NonZeroUsize::get),
|
||||||
token: 0,
|
token: 0,
|
||||||
factories: Vec::new(),
|
factories: Vec::new(),
|
||||||
sockets: Vec::new(),
|
sockets: Vec::new(),
|
||||||
backlog: 2048,
|
backlog: 2048,
|
||||||
|
mptcp: MpTcp::Disabled,
|
||||||
exit: false,
|
exit: false,
|
||||||
listen_os_signals: true,
|
listen_os_signals: true,
|
||||||
cmd_tx,
|
cmd_tx,
|
||||||
|
@ -53,13 +70,19 @@ impl ServerBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set number of workers to start.
|
/// Sets number of workers to start.
|
||||||
|
///
|
||||||
|
/// See [`bind()`](Self::bind()) for more details on how worker count affects the number of
|
||||||
|
/// server factory instantiations.
|
||||||
|
///
|
||||||
|
/// The default worker count is the determined by [`std::thread::available_parallelism()`]. See
|
||||||
|
/// its documentation to determine what behavior you should expect when server is run.
|
||||||
///
|
///
|
||||||
/// `num` must be greater than 0.
|
/// `num` must be greater than 0.
|
||||||
///
|
///
|
||||||
/// The default worker count is the number of physical CPU cores available. If your benchmark
|
/// # Panics
|
||||||
/// testing indicates that simultaneous multi-threading is beneficial to your app, you can use
|
///
|
||||||
/// the [`num_cpus`] crate to acquire the _logical_ core count instead.
|
/// Panics if `num` is 0.
|
||||||
pub fn workers(mut self, num: usize) -> Self {
|
pub fn workers(mut self, num: usize) -> Self {
|
||||||
assert_ne!(num, 0, "workers must be greater than 0");
|
assert_ne!(num, 0, "workers must be greater than 0");
|
||||||
self.threads = num;
|
self.threads = num;
|
||||||
|
@ -98,6 +121,24 @@ impl ServerBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets MultiPath TCP (MPTCP) preference on bound sockets.
|
||||||
|
///
|
||||||
|
/// Multipath TCP (MPTCP) builds on top of TCP to improve connection redundancy and performance
|
||||||
|
/// by sharing a network data stream across multiple underlying TCP sessions. See [mptcp.dev]
|
||||||
|
/// for more info about MPTCP itself.
|
||||||
|
///
|
||||||
|
/// MPTCP is available on Linux kernel version 5.6 and higher. In addition, you'll also need to
|
||||||
|
/// ensure the kernel option is enabled using `sysctl net.mptcp.enabled=1`.
|
||||||
|
///
|
||||||
|
/// This method will have no effect if called after a `bind()`.
|
||||||
|
///
|
||||||
|
/// [mptcp.dev]: https://www.mptcp.dev
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn mptcp(mut self, mptcp_enabled: MpTcp) -> Self {
|
||||||
|
self.mptcp = mptcp_enabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the maximum per-worker number of concurrent connections.
|
/// Sets the maximum per-worker number of concurrent connections.
|
||||||
///
|
///
|
||||||
/// All socket listeners will stop accepting connections when this limit is reached for
|
/// All socket listeners will stop accepting connections when this limit is reached for
|
||||||
|
@ -115,13 +156,15 @@ impl ServerBuilder {
|
||||||
self.max_concurrent_connections(num)
|
self.max_concurrent_connections(num)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop Actix `System` after server shutdown.
|
/// Sets flag to stop Actix `System` after server shutdown.
|
||||||
|
///
|
||||||
|
/// This has no effect when server is running in a Tokio-only runtime.
|
||||||
pub fn system_exit(mut self) -> Self {
|
pub fn system_exit(mut self) -> Self {
|
||||||
self.exit = true;
|
self.exit = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable OS signal handling.
|
/// Disables OS signal handling.
|
||||||
pub fn disable_signals(mut self) -> Self {
|
pub fn disable_signals(mut self) -> Self {
|
||||||
self.listen_os_signals = false;
|
self.listen_os_signals = false;
|
||||||
self
|
self
|
||||||
|
@ -139,25 +182,49 @@ impl ServerBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add new service to the server.
|
/// Adds new service to the server.
|
||||||
pub fn bind<F, U, N>(mut self, name: N, addr: U, factory: F) -> io::Result<Self>
|
///
|
||||||
|
/// Note that, if a DNS lookup is required, resolving hostnames is a blocking operation.
|
||||||
|
///
|
||||||
|
/// # Worker Count
|
||||||
|
///
|
||||||
|
/// The `factory` will be instantiated multiple times in most scenarios. The number of
|
||||||
|
/// instantiations is number of [`workers`](Self::workers()) × number of sockets resolved by
|
||||||
|
/// `addrs`.
|
||||||
|
///
|
||||||
|
/// For example, if you've manually set [`workers`](Self::workers()) to 2, and use `127.0.0.1`
|
||||||
|
/// as the bind `addrs`, then `factory` will be instantiated twice. However, using `localhost`
|
||||||
|
/// as the bind `addrs` can often resolve to both `127.0.0.1` (IPv4) _and_ `::1` (IPv6), causing
|
||||||
|
/// the `factory` to be instantiated 4 times (2 workers × 2 bind addresses).
|
||||||
|
///
|
||||||
|
/// Using a bind address of `0.0.0.0`, which signals to use all interfaces, may also multiple
|
||||||
|
/// the number of instantiations in a similar way.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `io::Error` if:
|
||||||
|
/// - `addrs` cannot be resolved into one or more socket addresses;
|
||||||
|
/// - all the resolved socket addresses are already bound.
|
||||||
|
pub fn bind<F, U, N>(mut self, name: N, addrs: U, factory: F) -> io::Result<Self>
|
||||||
where
|
where
|
||||||
F: ServerServiceFactory<TcpStream>,
|
F: ServerServiceFactory<TcpStream>,
|
||||||
U: ToSocketAddrs,
|
U: ToSocketAddrs,
|
||||||
N: AsRef<str>,
|
N: AsRef<str>,
|
||||||
{
|
{
|
||||||
let sockets = bind_addr(addr, self.backlog)?;
|
let sockets = bind_addr(addrs, self.backlog, &self.mptcp)?;
|
||||||
|
|
||||||
trace!("binding server to: {:?}", &sockets);
|
tracing::trace!("binding server to: {sockets:?}");
|
||||||
|
|
||||||
for lst in sockets {
|
for lst in sockets {
|
||||||
let token = self.next_token();
|
let token = self.next_token();
|
||||||
|
|
||||||
self.factories.push(StreamNewService::create(
|
self.factories.push(StreamNewService::create(
|
||||||
name.as_ref().to_string(),
|
name.as_ref().to_string(),
|
||||||
token,
|
token,
|
||||||
factory.clone(),
|
factory.clone(),
|
||||||
lst.local_addr()?,
|
lst.local_addr()?,
|
||||||
));
|
));
|
||||||
|
|
||||||
self.sockets
|
self.sockets
|
||||||
.push((token, name.as_ref().to_string(), MioListener::Tcp(lst)));
|
.push((token, name.as_ref().to_string(), MioListener::Tcp(lst)));
|
||||||
}
|
}
|
||||||
|
@ -165,7 +232,12 @@ impl ServerBuilder {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add new service to the server.
|
/// Adds service to the server using a socket listener already bound.
|
||||||
|
///
|
||||||
|
/// # Worker Count
|
||||||
|
///
|
||||||
|
/// The `factory` will be instantiated multiple times in most scenarios. The number of
|
||||||
|
/// instantiations is: number of [`workers`](Self::workers()).
|
||||||
pub fn listen<F, N: AsRef<str>>(
|
pub fn listen<F, N: AsRef<str>>(
|
||||||
mut self,
|
mut self,
|
||||||
name: N,
|
name: N,
|
||||||
|
@ -197,7 +269,7 @@ impl ServerBuilder {
|
||||||
if self.sockets.is_empty() {
|
if self.sockets.is_empty() {
|
||||||
panic!("Server should have at least one bound socket");
|
panic!("Server should have at least one bound socket");
|
||||||
} else {
|
} else {
|
||||||
info!("Starting {} workers", self.threads);
|
tracing::info!("starting {} workers", self.threads);
|
||||||
Server::new(self)
|
Server::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,7 +283,12 @@ impl ServerBuilder {
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
impl ServerBuilder {
|
impl ServerBuilder {
|
||||||
/// Add new unix domain service to the server.
|
/// Adds new service to the server using a UDS (unix domain socket) address.
|
||||||
|
///
|
||||||
|
/// # Worker Count
|
||||||
|
///
|
||||||
|
/// The `factory` will be instantiated multiple times in most scenarios. The number of
|
||||||
|
/// instantiations is: number of [`workers`](Self::workers()).
|
||||||
pub fn bind_uds<F, U, N>(self, name: N, addr: U, factory: F) -> io::Result<Self>
|
pub fn bind_uds<F, U, N>(self, name: N, addr: U, factory: F) -> io::Result<Self>
|
||||||
where
|
where
|
||||||
F: ServerServiceFactory<actix_rt::net::UnixStream>,
|
F: ServerServiceFactory<actix_rt::net::UnixStream>,
|
||||||
|
@ -220,10 +297,10 @@ impl ServerBuilder {
|
||||||
{
|
{
|
||||||
// The path must not exist when we try to bind.
|
// The path must not exist when we try to bind.
|
||||||
// Try to remove it to avoid bind error.
|
// Try to remove it to avoid bind error.
|
||||||
if let Err(e) = std::fs::remove_file(addr.as_ref()) {
|
if let Err(err) = std::fs::remove_file(addr.as_ref()) {
|
||||||
// NotFound is expected and not an issue. Anything else is.
|
// NotFound is expected and not an issue. Anything else is.
|
||||||
if e.kind() != std::io::ErrorKind::NotFound {
|
if err.kind() != std::io::ErrorKind::NotFound {
|
||||||
return Err(e);
|
return Err(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,9 +308,14 @@ impl ServerBuilder {
|
||||||
self.listen_uds(name, lst, factory)
|
self.listen_uds(name, lst, factory)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add new unix domain service to the server.
|
/// Adds new service to the server using a UDS (unix domain socket) listener already bound.
|
||||||
///
|
///
|
||||||
/// Useful when running as a systemd service and a socket FD is acquired externally.
|
/// Useful when running as a systemd service and a socket FD is acquired externally.
|
||||||
|
///
|
||||||
|
/// # Worker Count
|
||||||
|
///
|
||||||
|
/// The `factory` will be instantiated multiple times in most scenarios. The number of
|
||||||
|
/// instantiations is: number of [`workers`](Self::workers()).
|
||||||
pub fn listen_uds<F, N: AsRef<str>>(
|
pub fn listen_uds<F, N: AsRef<str>>(
|
||||||
mut self,
|
mut self,
|
||||||
name: N,
|
name: N,
|
||||||
|
@ -244,18 +326,22 @@ impl ServerBuilder {
|
||||||
F: ServerServiceFactory<actix_rt::net::UnixStream>,
|
F: ServerServiceFactory<actix_rt::net::UnixStream>,
|
||||||
{
|
{
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
|
||||||
lst.set_nonblocking(true)?;
|
lst.set_nonblocking(true)?;
|
||||||
|
|
||||||
let token = self.next_token();
|
let token = self.next_token();
|
||||||
let addr =
|
let addr = crate::socket::StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
|
||||||
crate::socket::StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
|
|
||||||
self.factories.push(StreamNewService::create(
|
self.factories.push(StreamNewService::create(
|
||||||
name.as_ref().to_string(),
|
name.as_ref().to_string(),
|
||||||
token,
|
token,
|
||||||
factory,
|
factory,
|
||||||
addr,
|
addr,
|
||||||
));
|
));
|
||||||
|
|
||||||
self.sockets
|
self.sockets
|
||||||
.push((token, name.as_ref().to_string(), MioListener::from(lst)));
|
.push((token, name.as_ref().to_string(), MioListener::from(lst)));
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,23 +349,25 @@ impl ServerBuilder {
|
||||||
pub(super) fn bind_addr<S: ToSocketAddrs>(
|
pub(super) fn bind_addr<S: ToSocketAddrs>(
|
||||||
addr: S,
|
addr: S,
|
||||||
backlog: u32,
|
backlog: u32,
|
||||||
|
mptcp: &MpTcp,
|
||||||
) -> io::Result<Vec<MioTcpListener>> {
|
) -> io::Result<Vec<MioTcpListener>> {
|
||||||
let mut err = None;
|
let mut opt_err = None;
|
||||||
let mut success = false;
|
let mut success = false;
|
||||||
let mut sockets = Vec::new();
|
let mut sockets = Vec::new();
|
||||||
|
|
||||||
for addr in addr.to_socket_addrs()? {
|
for addr in addr.to_socket_addrs()? {
|
||||||
match create_mio_tcp_listener(addr, backlog) {
|
match create_mio_tcp_listener(addr, backlog, mptcp) {
|
||||||
Ok(lst) => {
|
Ok(lst) => {
|
||||||
success = true;
|
success = true;
|
||||||
sockets.push(lst);
|
sockets.push(lst);
|
||||||
}
|
}
|
||||||
Err(e) => err = Some(e),
|
Err(err) => opt_err = Some(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if success {
|
if success {
|
||||||
Ok(sockets)
|
Ok(sockets)
|
||||||
} else if let Some(err) = err.take() {
|
} else if let Some(err) = opt_err.take() {
|
||||||
Err(err)
|
Err(err)
|
||||||
} else {
|
} else {
|
||||||
Err(io::Error::new(
|
Err(io::Error::new(
|
||||||
|
|
|
@ -63,10 +63,10 @@ impl<T> Future for JoinAll<T> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use actix_utils::future::ready;
|
use actix_utils::future::ready;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_join_all() {
|
async fn test_join_all() {
|
||||||
let futs = vec![ready(Ok(1)), ready(Err(3)), ready(Ok(9))];
|
let futs = vec![ready(Ok(1)), ready(Err(3)), ready(Ok(9))];
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
//! General purpose TCP server.
|
//! General purpose TCP server.
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible)]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
|
@ -18,14 +16,15 @@ mod test_server;
|
||||||
mod waker_queue;
|
mod waker_queue;
|
||||||
mod worker;
|
mod worker;
|
||||||
|
|
||||||
pub use self::builder::ServerBuilder;
|
|
||||||
pub use self::handle::ServerHandle;
|
|
||||||
pub use self::server::Server;
|
|
||||||
pub use self::service::ServerServiceFactory;
|
|
||||||
pub use self::test_server::TestServer;
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub use self::socket::FromStream;
|
pub use self::socket::FromStream;
|
||||||
|
pub use self::{
|
||||||
|
builder::{MpTcp, ServerBuilder},
|
||||||
|
handle::ServerHandle,
|
||||||
|
server::Server,
|
||||||
|
service::ServerServiceFactory,
|
||||||
|
test_server::TestServer,
|
||||||
|
};
|
||||||
|
|
||||||
/// Start server building process
|
/// Start server building process
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
@ -3,14 +3,15 @@ use std::{
|
||||||
io, mem,
|
io, mem,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
|
thread,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_rt::{time::sleep, System};
|
use actix_rt::{time::sleep, System};
|
||||||
use futures_core::{future::BoxFuture, Stream};
|
use futures_core::{future::BoxFuture, Stream};
|
||||||
use futures_util::stream::StreamExt as _;
|
use futures_util::stream::StreamExt as _;
|
||||||
use log::{error, info};
|
|
||||||
use tokio::sync::{mpsc::UnboundedReceiver, oneshot};
|
use tokio::sync::{mpsc::UnboundedReceiver, oneshot};
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
accept::Accept,
|
accept::Accept,
|
||||||
|
@ -65,7 +66,7 @@ pub(crate) enum ServerCommand {
|
||||||
/// server has fully shut down.
|
/// server has fully shut down.
|
||||||
///
|
///
|
||||||
/// # Shutdown Signals
|
/// # Shutdown Signals
|
||||||
/// On UNIX systems, `SIGQUIT` will start a graceful shutdown and `SIGTERM` or `SIGINT` will start a
|
/// On UNIX systems, `SIGTERM` will start a graceful shutdown and `SIGQUIT` or `SIGINT` will start a
|
||||||
/// forced shutdown. On Windows, a Ctrl-C signal will start a forced shutdown.
|
/// forced shutdown. On Windows, a Ctrl-C signal will start a forced shutdown.
|
||||||
///
|
///
|
||||||
/// A graceful shutdown will wait for all workers to stop first.
|
/// A graceful shutdown will wait for all workers to stop first.
|
||||||
|
@ -158,6 +159,7 @@ impl Future for Server {
|
||||||
|
|
||||||
pub struct ServerInner {
|
pub struct ServerInner {
|
||||||
worker_handles: Vec<WorkerHandleServer>,
|
worker_handles: Vec<WorkerHandleServer>,
|
||||||
|
accept_handle: Option<thread::JoinHandle<()>>,
|
||||||
worker_config: ServerWorkerConfig,
|
worker_config: ServerWorkerConfig,
|
||||||
services: Vec<Box<dyn InternalServiceFactory>>,
|
services: Vec<Box<dyn InternalServiceFactory>>,
|
||||||
waker_queue: WakerQueue,
|
waker_queue: WakerQueue,
|
||||||
|
@ -181,11 +183,6 @@ impl ServerInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sync(mut builder: ServerBuilder) -> io::Result<(Self, ServerEventMultiplexer)> {
|
fn run_sync(mut builder: ServerBuilder) -> io::Result<(Self, ServerEventMultiplexer)> {
|
||||||
let sockets = mem::take(&mut builder.sockets)
|
|
||||||
.into_iter()
|
|
||||||
.map(|t| (t.0, t.2))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Give log information on what runtime will be used.
|
// Give log information on what runtime will be used.
|
||||||
let is_actix = actix_rt::System::try_current().is_some();
|
let is_actix = actix_rt::System::try_current().is_some();
|
||||||
let is_tokio = tokio::runtime::Handle::try_current().is_ok();
|
let is_tokio = tokio::runtime::Handle::try_current().is_ok();
|
||||||
|
@ -198,14 +195,19 @@ impl ServerInner {
|
||||||
|
|
||||||
for (_, name, lst) in &builder.sockets {
|
for (_, name, lst) in &builder.sockets {
|
||||||
info!(
|
info!(
|
||||||
r#"Starting service: "{}", workers: {}, listening on: {}"#,
|
r#"starting service: "{}", workers: {}, listening on: {}"#,
|
||||||
name,
|
name,
|
||||||
builder.threads,
|
builder.threads,
|
||||||
lst.local_addr()
|
lst.local_addr()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (waker_queue, worker_handles) = Accept::start(sockets, &builder)?;
|
let sockets = mem::take(&mut builder.sockets)
|
||||||
|
.into_iter()
|
||||||
|
.map(|t| (t.0, t.2))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let (waker_queue, worker_handles, accept_handle) = Accept::start(sockets, &builder)?;
|
||||||
|
|
||||||
let mux = ServerEventMultiplexer {
|
let mux = ServerEventMultiplexer {
|
||||||
signal_fut: (builder.listen_os_signals).then(Signals::new),
|
signal_fut: (builder.listen_os_signals).then(Signals::new),
|
||||||
|
@ -214,6 +216,7 @@ impl ServerInner {
|
||||||
|
|
||||||
let server = ServerInner {
|
let server = ServerInner {
|
||||||
waker_queue,
|
waker_queue,
|
||||||
|
accept_handle: Some(accept_handle),
|
||||||
worker_handles,
|
worker_handles,
|
||||||
worker_config: builder.worker_config,
|
worker_config: builder.worker_config,
|
||||||
services: builder.factories,
|
services: builder.factories,
|
||||||
|
@ -243,7 +246,8 @@ impl ServerInner {
|
||||||
} => {
|
} => {
|
||||||
self.stopping = true;
|
self.stopping = true;
|
||||||
|
|
||||||
// stop accept thread
|
// Signal accept thread to stop.
|
||||||
|
// Signal is non-blocking; we wait for thread to stop later.
|
||||||
self.waker_queue.wake(WakerInterest::Stop);
|
self.waker_queue.wake(WakerInterest::Stop);
|
||||||
|
|
||||||
// send stop signal to workers
|
// send stop signal to workers
|
||||||
|
@ -258,6 +262,13 @@ impl ServerInner {
|
||||||
let _ = join_all(workers_stop).await;
|
let _ = join_all(workers_stop).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wait for accept thread stop
|
||||||
|
self.accept_handle
|
||||||
|
.take()
|
||||||
|
.unwrap()
|
||||||
|
.join()
|
||||||
|
.expect("Accept thread must not panic in any case");
|
||||||
|
|
||||||
if let Some(tx) = completion {
|
if let Some(tx) = completion {
|
||||||
let _ = tx.send(());
|
let _ = tx.send(());
|
||||||
}
|
}
|
||||||
|
@ -272,7 +283,7 @@ impl ServerInner {
|
||||||
// TODO: maybe just return with warning log if not found ?
|
// TODO: maybe just return with warning log if not found ?
|
||||||
assert!(self.worker_handles.iter().any(|wrk| wrk.idx == idx));
|
assert!(self.worker_handles.iter().any(|wrk| wrk.idx == idx));
|
||||||
|
|
||||||
error!("Worker {} has died; restarting", idx);
|
error!("worker {} has died; restarting", idx);
|
||||||
|
|
||||||
let factories = self
|
let factories = self
|
||||||
.services
|
.services
|
||||||
|
@ -352,6 +363,6 @@ impl Stream for ServerEventMultiplexer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Pin::new(&mut this.cmd_rx).poll_recv(cx)
|
this.cmd_rx.poll_recv(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use std::{
|
||||||
use actix_service::{Service, ServiceFactory as BaseServiceFactory};
|
use actix_service::{Service, ServiceFactory as BaseServiceFactory};
|
||||||
use actix_utils::future::{ready, Ready};
|
use actix_utils::future::{ready, Ready};
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use log::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
socket::{FromStream, MioStream},
|
socket::{FromStream, MioStream},
|
||||||
|
@ -77,8 +77,8 @@ where
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(err) => {
|
||||||
error!("Can not convert to an async tcp stream: {}", e);
|
error!("can not convert to an async TCP stream: {err}");
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
/// Types of process signals.
|
/// Types of process signals.
|
||||||
// #[allow(dead_code)]
|
// #[allow(dead_code)]
|
||||||
|
@ -69,8 +69,8 @@ impl Signals {
|
||||||
unix::signal(*kind)
|
unix::signal(*kind)
|
||||||
.map(|tokio_sig| (*sig, tokio_sig))
|
.map(|tokio_sig| (*sig, tokio_sig))
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
log::error!(
|
tracing::error!(
|
||||||
"Can not initialize stream handler for {:?} err: {}",
|
"can not initialize stream handler for {:?} err: {}",
|
||||||
sig,
|
sig,
|
||||||
e
|
e
|
||||||
)
|
)
|
||||||
|
@ -96,7 +96,7 @@ impl Future for Signals {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
for (sig, fut) in self.signals.iter_mut() {
|
for (sig, fut) in self.signals.iter_mut() {
|
||||||
if Pin::new(fut).poll_recv(cx).is_ready() {
|
if fut.poll_recv(cx).is_ready() {
|
||||||
trace!("{} received", sig);
|
trace!("{} received", sig);
|
||||||
return Poll::Ready(*sig);
|
return Poll::Ready(*sig);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,13 @@ use std::{fmt, io};
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
pub(crate) use mio::net::TcpListener as MioTcpListener;
|
pub(crate) use mio::net::TcpListener as MioTcpListener;
|
||||||
use mio::{event::Source, Interest, Registry, Token};
|
use mio::{event::Source, Interest, Registry, Token};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
pub(crate) use {
|
pub(crate) use {
|
||||||
mio::net::UnixListener as MioUnixListener,
|
mio::net::UnixListener as MioUnixListener, std::os::unix::net::UnixListener as StdUnixListener,
|
||||||
std::os::unix::net::UnixListener as StdUnixListener,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::builder::MpTcp;
|
||||||
|
|
||||||
pub(crate) enum MioListener {
|
pub(crate) enum MioListener {
|
||||||
Tcp(MioTcpListener),
|
Tcp(MioTcpListener),
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
@ -106,7 +106,7 @@ impl fmt::Debug for MioListener {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
MioListener::Tcp(ref lst) => write!(f, "{:?}", lst),
|
MioListener::Tcp(ref lst) => write!(f, "{:?}", lst),
|
||||||
#[cfg(all(unix))]
|
#[cfg(unix)]
|
||||||
MioListener::Uds(ref lst) => write!(f, "{:?}", lst),
|
MioListener::Uds(ref lst) => write!(f, "{:?}", lst),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ pub(crate) enum SocketAddr {
|
||||||
Unknown,
|
Unknown,
|
||||||
Tcp(StdSocketAddr),
|
Tcp(StdSocketAddr),
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
Uds(mio::net::SocketAddr),
|
Uds(std::os::unix::net::SocketAddr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SocketAddr {
|
impl fmt::Display for SocketAddr {
|
||||||
|
@ -160,6 +160,7 @@ pub enum MioStream {
|
||||||
|
|
||||||
/// Helper trait for converting a Mio stream into a Tokio stream.
|
/// Helper trait for converting a Mio stream into a Tokio stream.
|
||||||
pub trait FromStream: Sized {
|
pub trait FromStream: Sized {
|
||||||
|
/// Creates stream from a `mio` stream.
|
||||||
fn from_mio(sock: MioStream) -> io::Result<Self>;
|
fn from_mio(sock: MioStream) -> io::Result<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,10 +226,30 @@ mod unix_impl {
|
||||||
pub(crate) fn create_mio_tcp_listener(
|
pub(crate) fn create_mio_tcp_listener(
|
||||||
addr: StdSocketAddr,
|
addr: StdSocketAddr,
|
||||||
backlog: u32,
|
backlog: u32,
|
||||||
|
mptcp: &MpTcp,
|
||||||
) -> io::Result<MioTcpListener> {
|
) -> io::Result<MioTcpListener> {
|
||||||
use socket2::{Domain, Protocol, Socket, Type};
|
use socket2::{Domain, Protocol, Socket, Type};
|
||||||
|
|
||||||
let socket = Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?;
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
let protocol = Protocol::TCP;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
let protocol = if matches!(mptcp, MpTcp::Disabled) {
|
||||||
|
Protocol::TCP
|
||||||
|
} else {
|
||||||
|
Protocol::MPTCP
|
||||||
|
};
|
||||||
|
|
||||||
|
let socket = match Socket::new(Domain::for_address(addr), Type::STREAM, Some(protocol)) {
|
||||||
|
Ok(sock) => sock,
|
||||||
|
|
||||||
|
Err(err) if matches!(mptcp, MpTcp::TcpFallback) => {
|
||||||
|
tracing::warn!("binding socket as MPTCP failed: {err}");
|
||||||
|
tracing::warn!("falling back to TCP");
|
||||||
|
Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(err) => return Err(err),
|
||||||
|
};
|
||||||
|
|
||||||
socket.set_reuse_address(true)?;
|
socket.set_reuse_address(true)?;
|
||||||
socket.set_nonblocking(true)?;
|
socket.set_nonblocking(true)?;
|
||||||
|
@ -249,7 +270,7 @@ mod tests {
|
||||||
assert_eq!(format!("{}", addr), "127.0.0.1:8080");
|
assert_eq!(format!("{}", addr), "127.0.0.1:8080");
|
||||||
|
|
||||||
let addr: StdSocketAddr = "127.0.0.1:0".parse().unwrap();
|
let addr: StdSocketAddr = "127.0.0.1:0".parse().unwrap();
|
||||||
let lst = create_mio_tcp_listener(addr, 128).unwrap();
|
let lst = create_mio_tcp_listener(addr, 128, &MpTcp::Disabled).unwrap();
|
||||||
let lst = MioListener::Tcp(lst);
|
let lst = MioListener::Tcp(lst);
|
||||||
assert!(format!("{:?}", lst).contains("TcpListener"));
|
assert!(format!("{:?}", lst).contains("TcpListener"));
|
||||||
assert!(format!("{}", lst).contains("127.0.0.1"));
|
assert!(format!("{}", lst).contains("127.0.0.1"));
|
||||||
|
|
|
@ -117,13 +117,15 @@ impl TestServerHandle {
|
||||||
|
|
||||||
/// Stop server.
|
/// Stop server.
|
||||||
fn stop(&mut self) {
|
fn stop(&mut self) {
|
||||||
let _ = self.server_handle.stop(false);
|
drop(self.server_handle.stop(false));
|
||||||
self.thread_handle.take().unwrap().join().unwrap().unwrap();
|
self.thread_handle.take().unwrap().join().unwrap().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Connect to server, returning a Tokio `TcpStream`.
|
/// Connect to server, returning a Tokio `TcpStream`.
|
||||||
pub fn connect(&self) -> io::Result<TcpStream> {
|
pub fn connect(&self) -> io::Result<TcpStream> {
|
||||||
TcpStream::from_std(net::TcpStream::connect(self.addr)?)
|
let stream = net::TcpStream::connect(self.addr)?;
|
||||||
|
stream.set_nonblocking(true)?;
|
||||||
|
TcpStream::from_std(stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
io, mem,
|
io, mem,
|
||||||
|
num::NonZeroUsize,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{
|
sync::{
|
||||||
|
@ -17,11 +18,11 @@ use actix_rt::{
|
||||||
Arbiter, ArbiterHandle, System,
|
Arbiter, ArbiterHandle, System,
|
||||||
};
|
};
|
||||||
use futures_core::{future::LocalBoxFuture, ready};
|
use futures_core::{future::LocalBoxFuture, ready};
|
||||||
use log::{error, info, trace};
|
|
||||||
use tokio::sync::{
|
use tokio::sync::{
|
||||||
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
||||||
oneshot,
|
oneshot,
|
||||||
};
|
};
|
||||||
|
use tracing::{error, info, trace};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
service::{BoxedServerService, InternalServiceFactory},
|
service::{BoxedServerService, InternalServiceFactory},
|
||||||
|
@ -249,8 +250,11 @@ pub(crate) struct ServerWorkerConfig {
|
||||||
|
|
||||||
impl Default for ServerWorkerConfig {
|
impl Default for ServerWorkerConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
// 512 is the default max blocking thread count of tokio runtime.
|
let parallelism = std::thread::available_parallelism().map_or(2, NonZeroUsize::get);
|
||||||
let max_blocking_threads = std::cmp::max(512 / num_cpus::get_physical(), 1);
|
|
||||||
|
// 512 is the default max blocking thread count of a Tokio runtime.
|
||||||
|
let max_blocking_threads = std::cmp::max(512 / parallelism, 1);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
shutdown_timeout: Duration::from_secs(30),
|
shutdown_timeout: Duration::from_secs(30),
|
||||||
max_blocking_threads,
|
max_blocking_threads,
|
||||||
|
@ -337,7 +341,7 @@ impl ServerWorker {
|
||||||
Ok((token, svc)) => services.push((idx, token, svc)),
|
Ok((token, svc)) => services.push((idx, token, svc)),
|
||||||
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Can not start worker: {:?}", err);
|
error!("can not start worker: {:?}", err);
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
format!("can not start server service {}", idx),
|
format!("can not start server service {}", idx),
|
||||||
|
@ -436,7 +440,7 @@ impl ServerWorker {
|
||||||
Ok((token, svc)) => services.push((idx, token, svc)),
|
Ok((token, svc)) => services.push((idx, token, svc)),
|
||||||
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Can not start worker: {:?}", err);
|
error!("can not start worker: {:?}", err);
|
||||||
Arbiter::current().stop();
|
Arbiter::current().stop();
|
||||||
factory_tx
|
factory_tx
|
||||||
.send(Err(io::Error::new(
|
.send(Err(io::Error::new(
|
||||||
|
@ -476,7 +480,7 @@ impl ServerWorker {
|
||||||
|
|
||||||
fn restart_service(&mut self, idx: usize, factory_id: usize) {
|
fn restart_service(&mut self, idx: usize, factory_id: usize) {
|
||||||
let factory = &self.factories[factory_id];
|
let factory = &self.factories[factory_id];
|
||||||
trace!("Service {:?} failed, restarting", factory.name(idx));
|
trace!("service {:?} failed, restarting", factory.name(idx));
|
||||||
self.services[idx].status = WorkerServiceStatus::Restarting;
|
self.services[idx].status = WorkerServiceStatus::Restarting;
|
||||||
self.state = WorkerState::Restarting(Restart {
|
self.state = WorkerState::Restarting(Restart {
|
||||||
factory_id,
|
factory_id,
|
||||||
|
@ -508,7 +512,7 @@ impl ServerWorker {
|
||||||
Poll::Ready(Ok(_)) => {
|
Poll::Ready(Ok(_)) => {
|
||||||
if srv.status == WorkerServiceStatus::Unavailable {
|
if srv.status == WorkerServiceStatus::Unavailable {
|
||||||
trace!(
|
trace!(
|
||||||
"Service {:?} is available",
|
"service {:?} is available",
|
||||||
self.factories[srv.factory_idx].name(idx)
|
self.factories[srv.factory_idx].name(idx)
|
||||||
);
|
);
|
||||||
srv.status = WorkerServiceStatus::Available;
|
srv.status = WorkerServiceStatus::Available;
|
||||||
|
@ -519,7 +523,7 @@ impl ServerWorker {
|
||||||
|
|
||||||
if srv.status == WorkerServiceStatus::Available {
|
if srv.status == WorkerServiceStatus::Available {
|
||||||
trace!(
|
trace!(
|
||||||
"Service {:?} is unavailable",
|
"service {:?} is unavailable",
|
||||||
self.factories[srv.factory_idx].name(idx)
|
self.factories[srv.factory_idx].name(idx)
|
||||||
);
|
);
|
||||||
srv.status = WorkerServiceStatus::Unavailable;
|
srv.status = WorkerServiceStatus::Unavailable;
|
||||||
|
@ -527,7 +531,7 @@ impl ServerWorker {
|
||||||
}
|
}
|
||||||
Poll::Ready(Err(_)) => {
|
Poll::Ready(Err(_)) => {
|
||||||
error!(
|
error!(
|
||||||
"Service {:?} readiness check returned error, restarting",
|
"service {:?} readiness check returned error, restarting",
|
||||||
self.factories[srv.factory_idx].name(idx)
|
self.factories[srv.factory_idx].name(idx)
|
||||||
);
|
);
|
||||||
srv.status = WorkerServiceStatus::Failed;
|
srv.status = WorkerServiceStatus::Failed;
|
||||||
|
@ -585,16 +589,14 @@ impl Future for ServerWorker {
|
||||||
let this = self.as_mut().get_mut();
|
let this = self.as_mut().get_mut();
|
||||||
|
|
||||||
// `StopWorker` message handler
|
// `StopWorker` message handler
|
||||||
if let Poll::Ready(Some(Stop { graceful, tx })) =
|
if let Poll::Ready(Some(Stop { graceful, tx })) = this.stop_rx.poll_recv(cx) {
|
||||||
Pin::new(&mut this.stop_rx).poll_recv(cx)
|
|
||||||
{
|
|
||||||
let num = this.counter.total();
|
let num = this.counter.total();
|
||||||
if num == 0 {
|
if num == 0 {
|
||||||
info!("Shutting down idle worker");
|
info!("shutting down idle worker");
|
||||||
let _ = tx.send(true);
|
let _ = tx.send(true);
|
||||||
return Poll::Ready(());
|
return Poll::Ready(());
|
||||||
} else if graceful {
|
} else if graceful {
|
||||||
info!("Graceful worker shutdown; finishing {} connections", num);
|
info!("graceful worker shutdown; finishing {} connections", num);
|
||||||
this.shutdown(false);
|
this.shutdown(false);
|
||||||
|
|
||||||
this.state = WorkerState::Shutdown(Shutdown {
|
this.state = WorkerState::Shutdown(Shutdown {
|
||||||
|
@ -603,7 +605,7 @@ impl Future for ServerWorker {
|
||||||
tx,
|
tx,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
info!("Force shutdown worker, closing {} connections", num);
|
info!("force shutdown worker, closing {} connections", num);
|
||||||
this.shutdown(true);
|
this.shutdown(true);
|
||||||
|
|
||||||
let _ = tx.send(false);
|
let _ = tx.send(false);
|
||||||
|
@ -623,12 +625,13 @@ impl Future for ServerWorker {
|
||||||
self.poll(cx)
|
self.poll(cx)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
WorkerState::Restarting(ref mut restart) => {
|
WorkerState::Restarting(ref mut restart) => {
|
||||||
let factory_id = restart.factory_id;
|
let factory_id = restart.factory_id;
|
||||||
let token = restart.token;
|
let token = restart.token;
|
||||||
|
|
||||||
let (token_new, service) = ready!(restart.fut.as_mut().poll(cx))
|
let (token_new, service) =
|
||||||
.unwrap_or_else(|_| {
|
ready!(restart.fut.as_mut().poll(cx)).unwrap_or_else(|_| {
|
||||||
panic!(
|
panic!(
|
||||||
"Can not restart {:?} service",
|
"Can not restart {:?} service",
|
||||||
this.factories[factory_id].name(token)
|
this.factories[factory_id].name(token)
|
||||||
|
@ -638,7 +641,7 @@ impl Future for ServerWorker {
|
||||||
assert_eq!(token, token_new);
|
assert_eq!(token, token_new);
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
"Service {:?} has been restarted",
|
"service {:?} has been restarted",
|
||||||
this.factories[factory_id].name(token)
|
this.factories[factory_id].name(token)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -647,9 +650,10 @@ impl Future for ServerWorker {
|
||||||
|
|
||||||
self.poll(cx)
|
self.poll(cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
WorkerState::Shutdown(ref mut shutdown) => {
|
WorkerState::Shutdown(ref mut shutdown) => {
|
||||||
// drop all pending connections in rx channel.
|
// drop all pending connections in rx channel.
|
||||||
while let Poll::Ready(Some(conn)) = Pin::new(&mut this.conn_rx).poll_recv(cx) {
|
while let Poll::Ready(Some(conn)) = this.conn_rx.poll_recv(cx) {
|
||||||
// WorkerCounterGuard is needed as Accept thread has incremented counter.
|
// WorkerCounterGuard is needed as Accept thread has incremented counter.
|
||||||
// It's guard's job to decrement the counter together with drop of Conn.
|
// It's guard's job to decrement the counter together with drop of Conn.
|
||||||
let guard = this.counter.guard();
|
let guard = this.counter.guard();
|
||||||
|
@ -680,12 +684,13 @@ impl Future for ServerWorker {
|
||||||
shutdown.timer.as_mut().poll(cx)
|
shutdown.timer.as_mut().poll(cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// actively poll stream and handle worker command
|
// actively poll stream and handle worker command
|
||||||
WorkerState::Available => loop {
|
WorkerState::Available => loop {
|
||||||
match this.check_readiness(cx) {
|
match this.check_readiness(cx) {
|
||||||
Ok(true) => {}
|
Ok(true) => {}
|
||||||
Ok(false) => {
|
Ok(false) => {
|
||||||
trace!("Worker is unavailable");
|
trace!("worker is unavailable");
|
||||||
this.state = WorkerState::Unavailable;
|
this.state = WorkerState::Unavailable;
|
||||||
return self.poll(cx);
|
return self.poll(cx);
|
||||||
}
|
}
|
||||||
|
@ -696,10 +701,13 @@ impl Future for ServerWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle incoming io stream
|
// handle incoming io stream
|
||||||
match ready!(Pin::new(&mut this.conn_rx).poll_recv(cx)) {
|
match ready!(this.conn_rx.poll_recv(cx)) {
|
||||||
Some(msg) => {
|
Some(msg) => {
|
||||||
let guard = this.counter.guard();
|
let guard = this.counter.guard();
|
||||||
let _ = this.services[msg.token].service.call((guard, msg.io));
|
let _ = this.services[msg.token]
|
||||||
|
.service
|
||||||
|
.call((guard, msg.io))
|
||||||
|
.into_inner();
|
||||||
}
|
}
|
||||||
None => return Poll::Ready(()),
|
None => return Poll::Ready(()),
|
||||||
};
|
};
|
||||||
|
@ -708,9 +716,7 @@ impl Future for ServerWorker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_worker_services(
|
fn wrap_worker_services(services: Vec<(usize, usize, BoxedServerService)>) -> Vec<WorkerService> {
|
||||||
services: Vec<(usize, usize, BoxedServerService)>,
|
|
||||||
) -> Vec<WorkerService> {
|
|
||||||
services
|
services
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(Vec::new(), |mut services, (idx, token, service)| {
|
.fold(Vec::new(), |mut services, (idx, token, service)| {
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(clippy::let_underscore_future, missing_docs)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
net,
|
net,
|
||||||
sync::{
|
sync::{
|
||||||
|
@ -186,9 +188,9 @@ fn test_start() {
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_max_concurrent_connections() {
|
async fn test_max_concurrent_connections() {
|
||||||
// Note:
|
// Note:
|
||||||
// A tcp listener would accept connects based on it's backlog setting.
|
// A TCP listener would accept connects based on it's backlog setting.
|
||||||
//
|
//
|
||||||
// The limit test on the other hand is only for concurrent tcp stream limiting a work
|
// The limit test on the other hand is only for concurrent TCP stream limiting a work
|
||||||
// thread accept.
|
// thread accept.
|
||||||
|
|
||||||
use tokio::io::AsyncWriteExt;
|
use tokio::io::AsyncWriteExt;
|
||||||
|
@ -254,6 +256,7 @@ async fn test_max_concurrent_connections() {
|
||||||
h.join().unwrap().unwrap();
|
h.join().unwrap().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: race-y failures detected due to integer underflow when calling Counter::total
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_service_restart() {
|
async fn test_service_restart() {
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::net;
|
use std::net;
|
||||||
|
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
|
@ -69,5 +71,7 @@ async fn new_with_builder() {
|
||||||
srv.connect().unwrap();
|
srv.connect().unwrap();
|
||||||
|
|
||||||
// connect to alt service defined in custom ServerBuilder
|
// connect to alt service defined in custom ServerBuilder
|
||||||
TcpStream::from_std(net::TcpStream::connect(alt_addr).unwrap()).unwrap();
|
let stream = net::TcpStream::connect(alt_addr).unwrap();
|
||||||
|
stream.set_nonblocking(true).unwrap();
|
||||||
|
TcpStream::from_std(stream).unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,39 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased
|
||||||
|
|
||||||
|
## 2.0.3
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
|
## 2.0.2
|
||||||
|
|
||||||
## 2.0.2 - 2021-12-18
|
|
||||||
- Service types can now be `Send` and `'static` regardless of request, response, and config types, etc. [#397]
|
- Service types can now be `Send` and `'static` regardless of request, response, and config types, etc. [#397]
|
||||||
|
|
||||||
[#397]: https://github.com/actix/actix-net/pull/397
|
[#397]: https://github.com/actix/actix-net/pull/397
|
||||||
|
|
||||||
|
## 2.0.1
|
||||||
|
|
||||||
## 2.0.1 - 2021-10-11
|
|
||||||
- Documentation fix. [#388]
|
- Documentation fix. [#388]
|
||||||
|
|
||||||
[#388]: https://github.com/actix/actix-net/pull/388
|
[#388]: https://github.com/actix/actix-net/pull/388
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
## 2.0.0 - 2021-04-16
|
|
||||||
- Removed pipeline and related structs/functions. [#335]
|
- Removed pipeline and related structs/functions. [#335]
|
||||||
|
|
||||||
[#335]: https://github.com/actix/actix-net/pull/335
|
[#335]: https://github.com/actix/actix-net/pull/335
|
||||||
|
|
||||||
|
## 2.0.0-beta.5
|
||||||
|
|
||||||
## 2.0.0-beta.5 - 2021-03-15
|
|
||||||
- Add default `Service` trait impl for `Rc<S: Service>` and `&S: Service`. [#288]
|
- Add default `Service` trait impl for `Rc<S: Service>` and `&S: Service`. [#288]
|
||||||
- Add `boxed::rc_service` function for constructing `boxed::RcService` type [#290]
|
- Add `boxed::rc_service` function for constructing `boxed::RcService` type [#290]
|
||||||
|
|
||||||
[#288]: https://github.com/actix/actix-net/pull/288
|
[#288]: https://github.com/actix/actix-net/pull/288
|
||||||
[#290]: https://github.com/actix/actix-net/pull/290
|
[#290]: https://github.com/actix/actix-net/pull/290
|
||||||
|
|
||||||
|
## 2.0.0-beta.4
|
||||||
|
|
||||||
## 2.0.0-beta.4 - 2021-02-04
|
|
||||||
- `Service::poll_ready` and `Service::call` receive `&self`. [#247]
|
- `Service::poll_ready` and `Service::call` receive `&self`. [#247]
|
||||||
- `apply_fn` and `apply_fn_factory` now receive `Fn(Req, &Service)` function type. [#247]
|
- `apply_fn` and `apply_fn_factory` now receive `Fn(Req, &Service)` function type. [#247]
|
||||||
- `apply_cfg` and `apply_cfg_factory` now receive `Fn(Req, &Service)` function type. [#247]
|
- `apply_cfg` and `apply_cfg_factory` now receive `Fn(Req, &Service)` function type. [#247]
|
||||||
|
@ -37,158 +41,153 @@
|
||||||
|
|
||||||
[#247]: https://github.com/actix/actix-net/pull/247
|
[#247]: https://github.com/actix/actix-net/pull/247
|
||||||
|
|
||||||
|
## 2.0.0-beta.3
|
||||||
|
|
||||||
## 2.0.0-beta.3 - 2021-01-09
|
|
||||||
- The `forward_ready!` macro converts errors. [#246]
|
- The `forward_ready!` macro converts errors. [#246]
|
||||||
|
|
||||||
[#246]: https://github.com/actix/actix-net/pull/246
|
[#246]: https://github.com/actix/actix-net/pull/246
|
||||||
|
|
||||||
|
## 2.0.0-beta.2
|
||||||
|
|
||||||
## 2.0.0-beta.2 - 2021-01-03
|
|
||||||
- Remove redundant type parameter from `map_config`.
|
- Remove redundant type parameter from `map_config`.
|
||||||
|
|
||||||
|
## 2.0.0-beta.1
|
||||||
|
|
||||||
## 2.0.0-beta.1 - 2020-12-28
|
- `Service`, other traits, and many type signatures now take the the request type as a type parameter instead of an associated type. [#232]
|
||||||
- `Service`, other traits, and many type signatures now take the the request type as a type
|
|
||||||
parameter instead of an associated type. [#232]
|
|
||||||
- Add `always_ready!` and `forward_ready!` macros. [#233]
|
- Add `always_ready!` and `forward_ready!` macros. [#233]
|
||||||
- Crate is now `no_std`. [#233]
|
- Crate is now `no_std`. [#233]
|
||||||
- Migrate pin projections to `pin-project-lite`. [#233]
|
- Migrate pin projections to `pin-project-lite`. [#233]
|
||||||
- Remove `AndThenApplyFn` and Pipeline `and_then_apply_fn`. Use the
|
- Remove `AndThenApplyFn` and Pipeline `and_then_apply_fn`. Use the `.and_then(apply_fn(...))` construction. [#233]
|
||||||
`.and_then(apply_fn(...))` construction. [#233]
|
|
||||||
- Move non-vital methods to `ServiceExt` and `ServiceFactoryExt` extension traits. [#235]
|
- Move non-vital methods to `ServiceExt` and `ServiceFactoryExt` extension traits. [#235]
|
||||||
|
|
||||||
[#232]: https://github.com/actix/actix-net/pull/232
|
[#232]: https://github.com/actix/actix-net/pull/232
|
||||||
[#233]: https://github.com/actix/actix-net/pull/233
|
[#233]: https://github.com/actix/actix-net/pull/233
|
||||||
[#235]: https://github.com/actix/actix-net/pull/235
|
[#235]: https://github.com/actix/actix-net/pull/235
|
||||||
|
|
||||||
|
## 1.0.6
|
||||||
|
|
||||||
## 1.0.6 - 2020-08-09
|
- Removed unsound custom Cell implementation that allowed obtaining several mutable references to the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through service combinators. Attempts to acquire several mutable references to the same data will instead result in a panic.
|
||||||
- Removed unsound custom Cell implementation that allowed obtaining several mutable references to
|
|
||||||
the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through
|
|
||||||
service combinators. Attempts to acquire several mutable references to the same data will instead
|
|
||||||
result in a panic.
|
|
||||||
|
|
||||||
|
## 1.0.5
|
||||||
|
|
||||||
## 1.0.5 - 2020-01-16
|
|
||||||
- Fixed unsoundness in .and_then()/.then() service combinators.
|
- Fixed unsoundness in .and_then()/.then() service combinators.
|
||||||
|
|
||||||
|
## 1.0.4
|
||||||
|
|
||||||
## 1.0.4 - 2020-01-15
|
|
||||||
- Revert 1.0.3 change
|
- Revert 1.0.3 change
|
||||||
|
|
||||||
|
## 1.0.3
|
||||||
|
|
||||||
## 1.0.3 - 2020-01-15
|
|
||||||
- Fixed unsoundness in `AndThenService` impl.
|
- Fixed unsoundness in `AndThenService` impl.
|
||||||
|
|
||||||
|
## 1.0.2
|
||||||
|
|
||||||
## 1.0.2 - 2020-01-08
|
|
||||||
- Add `into_service` helper function.
|
- Add `into_service` helper function.
|
||||||
|
|
||||||
|
## 1.0.1
|
||||||
|
|
||||||
## 1.0.1 - 2019-12-22
|
|
||||||
- `map_config()` and `unit_config()` now accept `IntoServiceFactory` type.
|
- `map_config()` and `unit_config()` now accept `IntoServiceFactory` type.
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
## 1.0.0 - 2019-12-11
|
|
||||||
- Add Clone impl for Apply service
|
- Add Clone impl for Apply service
|
||||||
|
|
||||||
|
## 1.0.0-alpha.4
|
||||||
|
|
||||||
## 1.0.0-alpha.4 - 2019-12-08
|
|
||||||
- Renamed `service_fn` to `fn_service`
|
- Renamed `service_fn` to `fn_service`
|
||||||
- Renamed `factory_fn` to `fn_factory`
|
- Renamed `factory_fn` to `fn_factory`
|
||||||
- Renamed `factory_fn_cfg` to `fn_factory_with_config`
|
- Renamed `factory_fn_cfg` to `fn_factory_with_config`
|
||||||
|
|
||||||
|
## 1.0.0-alpha.3
|
||||||
|
|
||||||
## 1.0.0-alpha.3 - 2019-12-06
|
|
||||||
- Add missing Clone impls
|
- Add missing Clone impls
|
||||||
- Restore `Transform::map_init_err()` combinator
|
- Restore `Transform::map_init_err()` combinator
|
||||||
- Restore `Service/Factory::apply_fn()` in form of `Pipeline/Factory::and_then_apply_fn()`
|
- Restore `Service/Factory::apply_fn()` in form of `Pipeline/Factory::and_then_apply_fn()`
|
||||||
- Optimize service combinators and futures memory layout
|
- Optimize service combinators and futures memory layout
|
||||||
|
|
||||||
|
## 1.0.0-alpha.2
|
||||||
|
|
||||||
## 1.0.0-alpha.2 - 2019-12-02
|
|
||||||
- Use owned config value for service factory
|
- Use owned config value for service factory
|
||||||
- Renamed BoxedNewService/BoxedService to BoxServiceFactory/BoxService
|
- Renamed BoxedNewService/BoxedService to BoxServiceFactory/BoxService
|
||||||
|
|
||||||
|
## 1.0.0-alpha.1
|
||||||
|
|
||||||
## 1.0.0-alpha.1 - 2019-11-25
|
|
||||||
- Migrated to `std::future`
|
- Migrated to `std::future`
|
||||||
- `NewService` renamed to `ServiceFactory`
|
- `NewService` renamed to `ServiceFactory`
|
||||||
- Added `pipeline` and `pipeline_factory` function
|
- Added `pipeline` and `pipeline_factory` function
|
||||||
|
|
||||||
|
## 0.4.2
|
||||||
|
|
||||||
## 0.4.2 - 2019-08-27
|
|
||||||
- Check service readiness for `new_apply_cfg` combinator
|
- Check service readiness for `new_apply_cfg` combinator
|
||||||
|
|
||||||
|
## 0.4.1
|
||||||
|
|
||||||
## 0.4.1 - 2019-06-06
|
|
||||||
- Add `new_apply_cfg` function
|
- Add `new_apply_cfg` function
|
||||||
|
|
||||||
|
## 0.4.0
|
||||||
|
|
||||||
## 0.4.0 - 2019-05-12
|
|
||||||
- Add `NewService::map_config` and `NewService::unit_config` combinators.
|
- Add `NewService::map_config` and `NewService::unit_config` combinators.
|
||||||
- Use associated type for `NewService` config.
|
- Use associated type for `NewService` config.
|
||||||
- Change `apply_cfg` function.
|
- Change `apply_cfg` function.
|
||||||
- Renamed helper functions.
|
- Renamed helper functions.
|
||||||
|
|
||||||
|
## 0.3.6
|
||||||
|
|
||||||
## 0.3.6 - 2019-04-07
|
|
||||||
- Poll boxed service call result immediately
|
- Poll boxed service call result immediately
|
||||||
|
|
||||||
|
## 0.3.5
|
||||||
|
|
||||||
## 0.3.5 - 2019-03-29
|
|
||||||
- Add `impl<S: Service> Service for Rc<RefCell<S>>`.
|
- Add `impl<S: Service> Service for Rc<RefCell<S>>`.
|
||||||
|
|
||||||
|
## 0.3.4
|
||||||
|
|
||||||
## 0.3.4 - 2019-03-12
|
|
||||||
- Add `Transform::from_err()` combinator
|
- Add `Transform::from_err()` combinator
|
||||||
- Add `apply_fn` helper
|
- Add `apply_fn` helper
|
||||||
- Add `apply_fn_factory` helper
|
- Add `apply_fn_factory` helper
|
||||||
- Add `apply_transform` helper
|
- Add `apply_transform` helper
|
||||||
- Add `apply_cfg` helper
|
- Add `apply_cfg` helper
|
||||||
|
|
||||||
|
## 0.3.3
|
||||||
|
|
||||||
## 0.3.3 - 2019-03-09
|
|
||||||
- Add `ApplyTransform` new service for transform and new service.
|
- Add `ApplyTransform` new service for transform and new service.
|
||||||
- Add `NewService::apply_cfg()` combinator, allows to use nested `NewService` with different config parameter.
|
- Add `NewService::apply_cfg()` combinator, allows to use nested `NewService` with different config parameter.
|
||||||
- Revert IntoFuture change
|
- Revert IntoFuture change
|
||||||
|
|
||||||
|
## 0.3.2
|
||||||
|
|
||||||
## 0.3.2 - 2019-03-04
|
|
||||||
- Change `NewService::Future` and `Transform::Future` to the `IntoFuture` trait.
|
- Change `NewService::Future` and `Transform::Future` to the `IntoFuture` trait.
|
||||||
- Export `AndThenTransform` type
|
- Export `AndThenTransform` type
|
||||||
|
|
||||||
|
## 0.3.1
|
||||||
|
|
||||||
## 0.3.1 - 2019-03-04
|
|
||||||
- Simplify Transform trait
|
- Simplify Transform trait
|
||||||
|
|
||||||
|
## 0.3.0
|
||||||
|
|
||||||
## 0.3.0 - 2019-03-02
|
|
||||||
- Added boxed NewService and Service.
|
- Added boxed NewService and Service.
|
||||||
- Added `Config` parameter to `NewService` trait.
|
- Added `Config` parameter to `NewService` trait.
|
||||||
- Added `Config` parameter to `NewTransform` trait.
|
- Added `Config` parameter to `NewTransform` trait.
|
||||||
|
|
||||||
|
## 0.2.2
|
||||||
|
|
||||||
## 0.2.2 - 2019-02-19
|
|
||||||
- Added `NewService` impl for `Rc<S> where S: NewService`
|
- Added `NewService` impl for `Rc<S> where S: NewService`
|
||||||
- Added `NewService` impl for `Arc<S> where S: NewService`
|
- Added `NewService` impl for `Arc<S> where S: NewService`
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
## 0.2.1 - 2019-02-03
|
|
||||||
- Generalize `.apply` combinator with Transform trait
|
- Generalize `.apply` combinator with Transform trait
|
||||||
|
|
||||||
|
## 0.2.0
|
||||||
|
|
||||||
## 0.2.0 - 2019-02-01
|
|
||||||
- Use associated type instead of generic for Service definition.
|
- Use associated type instead of generic for Service definition.
|
||||||
* Before:
|
- Before:
|
||||||
```rust
|
```rust
|
||||||
impl Service<Request> for Client {
|
impl Service<Request> for Client {
|
||||||
type Response = Response;
|
type Response = Response;
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
* After:
|
- After:
|
||||||
```rust
|
```rust
|
||||||
impl Service for Client {
|
impl Service for Client {
|
||||||
type Request = Request;
|
type Request = Request;
|
||||||
|
@ -197,31 +196,31 @@
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 0.1.6
|
||||||
|
|
||||||
## 0.1.6 - 2019-01-24
|
|
||||||
- Use `FnMut` instead of `Fn` for .apply() and .map() combinators and `FnService` type
|
- Use `FnMut` instead of `Fn` for .apply() and .map() combinators and `FnService` type
|
||||||
- Change `.apply()` error semantic, new service's error is `From<Self::Error>`
|
- Change `.apply()` error semantic, new service's error is `From<Self::Error>`
|
||||||
|
|
||||||
|
## 0.1.5
|
||||||
|
|
||||||
## 0.1.5 - 2019-01-13
|
|
||||||
- Make `Out::Error` convertible from `T::Error` for apply combinator
|
- Make `Out::Error` convertible from `T::Error` for apply combinator
|
||||||
|
|
||||||
|
## 0.1.4
|
||||||
|
|
||||||
## 0.1.4 - 2019-01-11
|
|
||||||
- Use `FnMut` instead of `Fn` for `FnService`
|
- Use `FnMut` instead of `Fn` for `FnService`
|
||||||
|
|
||||||
|
## 0.1.3
|
||||||
|
|
||||||
## 0.1.3 - 2018-12-12
|
|
||||||
- Split service combinators to separate trait
|
- Split service combinators to separate trait
|
||||||
|
|
||||||
|
## 0.1.2
|
||||||
|
|
||||||
## 0.1.2 - 2018-12-12
|
|
||||||
- Release future early for `.and_then()` and `.then()` combinators
|
- Release future early for `.and_then()` and `.then()` combinators
|
||||||
|
|
||||||
|
## 0.1.1
|
||||||
|
|
||||||
## 0.1.1 - 2018-12-09
|
|
||||||
- Added Service impl for `Box<S: Service>`
|
- Added Service impl for `Box<S: Service>`
|
||||||
|
|
||||||
|
## 0.1.0
|
||||||
|
|
||||||
## 0.1.0 - 2018-12-09
|
|
||||||
- Initial import
|
- Initial import
|
||||||
|
|
|
@ -1,28 +1,23 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-service"
|
name = "actix-service"
|
||||||
version = "2.0.2"
|
version = "2.0.3"
|
||||||
authors = [
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
|
||||||
"fakeshadow <24548779@qq.com>",
|
|
||||||
]
|
|
||||||
description = "Service trait and combinators for representing asynchronous request/response operations."
|
description = "Service trait and combinators for representing asynchronous request/response operations."
|
||||||
keywords = ["network", "framework", "async", "futures", "service"]
|
keywords = ["network", "framework", "async", "futures", "service"]
|
||||||
categories = ["network-programming", "asynchronous", "no-std"]
|
categories = ["network-programming", "asynchronous", "no-std"]
|
||||||
repository = "https://github.com/actix/actix-net"
|
repository = "https://github.com/actix/actix-net"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2018"
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
[lib]
|
|
||||||
name = "actix_service"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures-core = { version = "0.3.7", default-features = false }
|
futures-core = { version = "0.3.17", default-features = false }
|
||||||
paste = "1"
|
|
||||||
pin-project-lite = "0.2"
|
pin-project-lite = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.0.0"
|
actix-rt = "2"
|
||||||
actix-utils = "3.0.0"
|
actix-utils = "3"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
> Service trait and combinators for representing asynchronous request/response operations.
|
> Service trait and combinators for representing asynchronous request/response operations.
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-service)
|
[](https://crates.io/crates/actix-service)
|
||||||
[](https://docs.rs/actix-service/2.0.2)
|
[](https://docs.rs/actix-service/2.0.3)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
|
[](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
|
||||||

|

|
||||||
[](https://deps.rs/crate/actix-service/2.0.2)
|
[](https://deps.rs/crate/actix-service/2.0.3)
|
||||||

|

|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::{future::Future, sync::mpsc, time::Duration};
|
use std::{future::Future, sync::mpsc, time::Duration};
|
||||||
|
|
||||||
async fn oracle<F, Fut>(f: F) -> (u32, u32)
|
async fn oracle<F, Fut>(f: F) -> (u32, u32)
|
||||||
|
|
|
@ -121,12 +121,7 @@ pub struct AndThenServiceFactory<A, B, Req>
|
||||||
where
|
where
|
||||||
A: ServiceFactory<Req>,
|
A: ServiceFactory<Req>,
|
||||||
A::Config: Clone,
|
A::Config: Clone,
|
||||||
B: ServiceFactory<
|
B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
|
||||||
A::Response,
|
|
||||||
Config = A::Config,
|
|
||||||
Error = A::Error,
|
|
||||||
InitError = A::InitError,
|
|
||||||
>,
|
|
||||||
{
|
{
|
||||||
inner: Rc<(A, B)>,
|
inner: Rc<(A, B)>,
|
||||||
_phantom: PhantomData<Req>,
|
_phantom: PhantomData<Req>,
|
||||||
|
@ -136,12 +131,7 @@ impl<A, B, Req> AndThenServiceFactory<A, B, Req>
|
||||||
where
|
where
|
||||||
A: ServiceFactory<Req>,
|
A: ServiceFactory<Req>,
|
||||||
A::Config: Clone,
|
A::Config: Clone,
|
||||||
B: ServiceFactory<
|
B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
|
||||||
A::Response,
|
|
||||||
Config = A::Config,
|
|
||||||
Error = A::Error,
|
|
||||||
InitError = A::InitError,
|
|
||||||
>,
|
|
||||||
{
|
{
|
||||||
/// Create new `AndThenFactory` combinator
|
/// Create new `AndThenFactory` combinator
|
||||||
pub(crate) fn new(a: A, b: B) -> Self {
|
pub(crate) fn new(a: A, b: B) -> Self {
|
||||||
|
@ -156,12 +146,7 @@ impl<A, B, Req> ServiceFactory<Req> for AndThenServiceFactory<A, B, Req>
|
||||||
where
|
where
|
||||||
A: ServiceFactory<Req>,
|
A: ServiceFactory<Req>,
|
||||||
A::Config: Clone,
|
A::Config: Clone,
|
||||||
B: ServiceFactory<
|
B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
|
||||||
A::Response,
|
|
||||||
Config = A::Config,
|
|
||||||
Error = A::Error,
|
|
||||||
InitError = A::InitError,
|
|
||||||
>,
|
|
||||||
{
|
{
|
||||||
type Response = B::Response;
|
type Response = B::Response;
|
||||||
type Error = A::Error;
|
type Error = A::Error;
|
||||||
|
@ -184,12 +169,7 @@ impl<A, B, Req> Clone for AndThenServiceFactory<A, B, Req>
|
||||||
where
|
where
|
||||||
A: ServiceFactory<Req>,
|
A: ServiceFactory<Req>,
|
||||||
A::Config: Clone,
|
A::Config: Clone,
|
||||||
B: ServiceFactory<
|
B: ServiceFactory<A::Response, Config = A::Config, Error = A::Error, InitError = A::InitError>,
|
||||||
A::Response,
|
|
||||||
Config = A::Config,
|
|
||||||
Error = A::Error,
|
|
||||||
InitError = A::InitError,
|
|
||||||
>,
|
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -334,9 +314,8 @@ mod tests {
|
||||||
async fn test_new_service() {
|
async fn test_new_service() {
|
||||||
let cnt = Rc::new(Cell::new(0));
|
let cnt = Rc::new(Cell::new(0));
|
||||||
let cnt2 = cnt.clone();
|
let cnt2 = cnt.clone();
|
||||||
let new_srv =
|
let new_srv = pipeline_factory(fn_factory(move || ready(Ok::<_, ()>(Srv1(cnt2.clone())))))
|
||||||
pipeline_factory(fn_factory(move || ready(Ok::<_, ()>(Srv1(cnt2.clone())))))
|
.and_then(move || ready(Ok(Srv2(cnt.clone()))));
|
||||||
.and_then(move || ready(Ok(Srv2(cnt.clone()))));
|
|
||||||
|
|
||||||
let srv = new_srv.new_service(()).await.unwrap();
|
let srv = new_srv.new_service(()).await.unwrap();
|
||||||
let res = srv.call("srv1").await;
|
let res = srv.call("srv1").await;
|
||||||
|
|
|
@ -140,8 +140,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SF, F, Fut, Req, In, Res, Err> ServiceFactory<Req>
|
impl<SF, F, Fut, Req, In, Res, Err> ServiceFactory<Req> for ApplyFactory<SF, F, Req, In, Res, Err>
|
||||||
for ApplyFactory<SF, F, Req, In, Res, Err>
|
|
||||||
where
|
where
|
||||||
SF: ServiceFactory<In, Error = Err>,
|
SF: ServiceFactory<In, Error = Err>,
|
||||||
F: Fn(Req, &SF::Service) -> Fut + Clone,
|
F: Fn(Req, &SF::Service) -> Fut + Clone,
|
||||||
|
@ -209,15 +208,13 @@ where
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use core::task::Poll;
|
|
||||||
|
|
||||||
use futures_util::future::lazy;
|
use futures_util::future::lazy;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
ok,
|
ok,
|
||||||
pipeline::{pipeline, pipeline_factory},
|
pipeline::{pipeline, pipeline_factory},
|
||||||
Ready, Service, ServiceFactory,
|
Ready,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -198,8 +198,7 @@ pin_project! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SF, Req, F, Cfg, Fut, S> Future
|
impl<SF, Req, F, Cfg, Fut, S> Future for ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>
|
||||||
for ApplyConfigServiceFactoryResponse<SF, Req, F, Cfg, Fut, S>
|
|
||||||
where
|
where
|
||||||
SF: ServiceFactory<Req, Config = ()>,
|
SF: ServiceFactory<Req, Config = ()>,
|
||||||
SF::InitError: From<SF::Error>,
|
SF::InitError: From<SF::Error>,
|
||||||
|
|
|
@ -3,36 +3,38 @@
|
||||||
use alloc::{boxed::Box, rc::Rc};
|
use alloc::{boxed::Box, rc::Rc};
|
||||||
use core::{future::Future, pin::Pin};
|
use core::{future::Future, pin::Pin};
|
||||||
|
|
||||||
use paste::paste;
|
|
||||||
|
|
||||||
use crate::{Service, ServiceFactory};
|
use crate::{Service, ServiceFactory};
|
||||||
|
|
||||||
/// A boxed future with no send bound or lifetime parameters.
|
/// A boxed future with no send bound or lifetime parameters.
|
||||||
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
|
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
|
||||||
|
|
||||||
macro_rules! service_object {
|
/// Type alias for service trait object using [`Box`].
|
||||||
($name: ident, $type: tt, $fn_name: ident) => {
|
pub type BoxService<Req, Res, Err> =
|
||||||
paste! {
|
Box<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>;
|
||||||
#[doc = "Type alias for service trait object using `" $type "`."]
|
|
||||||
pub type $name<Req, Res, Err> = $type<
|
|
||||||
dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
#[doc = "Wraps service as a trait object using [`" $name "`]."]
|
/// Wraps service as a trait object using [`BoxService`].
|
||||||
pub fn $fn_name<S, Req>(service: S) -> $name<Req, S::Response, S::Error>
|
pub fn service<S, Req>(service: S) -> BoxService<Req, S::Response, S::Error>
|
||||||
where
|
where
|
||||||
S: Service<Req> + 'static,
|
S: Service<Req> + 'static,
|
||||||
Req: 'static,
|
Req: 'static,
|
||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
{
|
{
|
||||||
$type::new(ServiceWrapper::new(service))
|
Box::new(ServiceWrapper::new(service))
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
service_object!(BoxService, Box, service);
|
/// Type alias for service trait object using [`Rc`].
|
||||||
service_object!(RcService, Rc, rc_service);
|
pub type RcService<Req, Res, Err> =
|
||||||
|
Rc<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>;
|
||||||
|
|
||||||
|
/// Wraps service as a trait object using [`RcService`].
|
||||||
|
pub fn rc_service<S, Req>(service: S) -> RcService<Req, S::Response, S::Error>
|
||||||
|
where
|
||||||
|
S: Service<Req> + 'static,
|
||||||
|
Req: 'static,
|
||||||
|
S::Future: 'static,
|
||||||
|
{
|
||||||
|
Rc::new(ServiceWrapper::new(service))
|
||||||
|
}
|
||||||
|
|
||||||
struct ServiceWrapper<S> {
|
struct ServiceWrapper<S> {
|
||||||
inner: S,
|
inner: S,
|
||||||
|
@ -91,8 +93,7 @@ type Inner<C, Req, Res, Err, InitErr> = Box<
|
||||||
>,
|
>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
impl<C, Req, Res, Err, InitErr> ServiceFactory<Req>
|
impl<C, Req, Res, Err, InitErr> ServiceFactory<Req> for BoxServiceFactory<C, Req, Res, Err, InitErr>
|
||||||
for BoxServiceFactory<C, Req, Res, Err, InitErr>
|
|
||||||
where
|
where
|
||||||
Req: 'static,
|
Req: 'static,
|
||||||
Res: 'static,
|
Res: 'static,
|
||||||
|
|
|
@ -44,7 +44,7 @@ pub trait ServiceExt<Req>: Service<Req> {
|
||||||
/// Call another service after call to this one has resolved successfully.
|
/// Call another service after call to this one has resolved successfully.
|
||||||
///
|
///
|
||||||
/// This function can be used to chain two services together and ensure that the second service
|
/// This function can be used to chain two services together and ensure that the second service
|
||||||
/// isn't called until call to the fist service have finished. Result of the call to the first
|
/// isn't called until call to the first service have finished. Result of the call to the first
|
||||||
/// service is used as an input parameter for the second service's call.
|
/// service is used as an input parameter for the second service's call.
|
||||||
///
|
///
|
||||||
/// Note that this function consumes the receiving service and returns a wrapped version of it.
|
/// Note that this function consumes the receiving service and returns a wrapped version of it.
|
||||||
|
|
|
@ -3,9 +3,7 @@ use core::{future::Future, marker::PhantomData};
|
||||||
use crate::{ok, IntoService, IntoServiceFactory, Ready, Service, ServiceFactory};
|
use crate::{ok, IntoService, IntoServiceFactory, Ready, Service, ServiceFactory};
|
||||||
|
|
||||||
/// Create `ServiceFactory` for function that can act as a `Service`
|
/// Create `ServiceFactory` for function that can act as a `Service`
|
||||||
pub fn fn_service<F, Fut, Req, Res, Err, Cfg>(
|
pub fn fn_service<F, Fut, Req, Res, Err, Cfg>(f: F) -> FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
|
||||||
f: F,
|
|
||||||
) -> FnServiceFactory<F, Fut, Req, Res, Err, Cfg>
|
|
||||||
where
|
where
|
||||||
F: Fn(Req) -> Fut + Clone,
|
F: Fn(Req) -> Fut + Clone,
|
||||||
Fut: Future<Output = Result<Res, Err>>,
|
Fut: Future<Output = Result<Res, Err>>,
|
||||||
|
@ -48,9 +46,7 @@ where
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn fn_factory<F, Cfg, Srv, Req, Fut, Err>(
|
pub fn fn_factory<F, Cfg, Srv, Req, Fut, Err>(f: F) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
|
||||||
f: F,
|
|
||||||
) -> FnServiceNoConfig<F, Cfg, Srv, Req, Fut, Err>
|
|
||||||
where
|
where
|
||||||
F: Fn() -> Fut,
|
F: Fn() -> Fut,
|
||||||
Fut: Future<Output = Result<Srv, Err>>,
|
Fut: Future<Output = Result<Srv, Err>>,
|
||||||
|
@ -265,8 +261,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F, Fut, Cfg, Srv, Req, Err> ServiceFactory<Req>
|
impl<F, Fut, Cfg, Srv, Req, Err> ServiceFactory<Req> for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
|
||||||
for FnServiceConfig<F, Fut, Cfg, Srv, Req, Err>
|
|
||||||
where
|
where
|
||||||
F: Fn(Cfg) -> Fut,
|
F: Fn(Cfg) -> Fut,
|
||||||
Fut: Future<Output = Result<Srv, Err>>,
|
Fut: Future<Output = Result<Srv, Err>>,
|
||||||
|
@ -356,7 +351,6 @@ mod tests {
|
||||||
use futures_util::future::lazy;
|
use futures_util::future::lazy;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{ok, Service, ServiceFactory};
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_fn_service() {
|
async fn test_fn_service() {
|
||||||
|
@ -394,18 +388,18 @@ mod tests {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_auto_impl_send() {
|
async fn test_auto_impl_send() {
|
||||||
use crate::{map_config, ServiceExt, ServiceFactoryExt};
|
|
||||||
use alloc::rc::Rc;
|
use alloc::rc::Rc;
|
||||||
|
|
||||||
|
use crate::{map_config, ServiceExt, ServiceFactoryExt};
|
||||||
|
|
||||||
let srv_1 = fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8)));
|
let srv_1 = fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8)));
|
||||||
|
|
||||||
let fac_1 = fn_factory_with_config(|_: Rc<u8>| {
|
let fac_1 = fn_factory_with_config(|_: Rc<u8>| {
|
||||||
ok::<_, Rc<u8>>(fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8))))
|
ok::<_, Rc<u8>>(fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8))))
|
||||||
});
|
});
|
||||||
|
|
||||||
let fac_2 = fn_factory(|| {
|
let fac_2 =
|
||||||
ok::<_, Rc<u8>>(fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8))))
|
fn_factory(|| ok::<_, Rc<u8>>(fn_service(|_: Rc<u8>| ok::<_, Rc<u8>>(Rc::new(0u8)))));
|
||||||
});
|
|
||||||
|
|
||||||
fn is_send<T: Send + Sync + Clone>(_: &T) {}
|
fn is_send<T: Send + Sync + Clone>(_: &T) {}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
//! See [`Service`] docs for information on this crate's foundational trait.
|
//! See [`Service`] docs for information on this crate's foundational trait.
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible, missing_docs)]
|
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
@ -33,15 +31,16 @@ mod then;
|
||||||
mod transform;
|
mod transform;
|
||||||
mod transform_err;
|
mod transform_err;
|
||||||
|
|
||||||
pub use self::apply::{apply_fn, apply_fn_factory};
|
|
||||||
pub use self::apply_cfg::{apply_cfg, apply_cfg_factory};
|
|
||||||
pub use self::ext::{ServiceExt, ServiceFactoryExt, TransformExt};
|
|
||||||
pub use self::fn_service::{fn_factory, fn_factory_with_config, fn_service};
|
|
||||||
pub use self::map_config::{map_config, unit_config};
|
|
||||||
pub use self::transform::{apply, ApplyTransform, Transform};
|
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use self::ready::{err, ok, ready, Ready};
|
use self::ready::{err, ok, ready, Ready};
|
||||||
|
pub use self::{
|
||||||
|
apply::{apply_fn, apply_fn_factory},
|
||||||
|
apply_cfg::{apply_cfg, apply_cfg_factory},
|
||||||
|
ext::{ServiceExt, ServiceFactoryExt, TransformExt},
|
||||||
|
fn_service::{fn_factory, fn_factory_with_config, fn_service},
|
||||||
|
map_config::{map_config, unit_config},
|
||||||
|
transform::{apply, ApplyTransform, Transform},
|
||||||
|
};
|
||||||
|
|
||||||
/// An asynchronous operation from `Request` to a `Response`.
|
/// An asynchronous operation from `Request` to a `Response`.
|
||||||
///
|
///
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`forward_ready!`]: crate::forward_ready
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! always_ready {
|
macro_rules! always_ready {
|
||||||
() => {
|
() => {
|
||||||
|
|
|
@ -97,7 +97,7 @@ where
|
||||||
|
|
||||||
match this.fut.poll(cx) {
|
match this.fut.poll(cx) {
|
||||||
Poll::Ready(Ok(resp)) => Poll::Ready(Ok((this.f)(resp))),
|
Poll::Ready(Ok(resp)) => Poll::Ready(Ok((this.f)(resp))),
|
||||||
Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
|
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
|
||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,9 +202,7 @@ mod tests {
|
||||||
use futures_util::future::lazy;
|
use futures_util::future::lazy;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{ok, IntoServiceFactory, Ready, ServiceExt, ServiceFactoryExt};
|
||||||
ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Srv;
|
struct Srv;
|
||||||
|
|
||||||
|
|
|
@ -205,10 +205,7 @@ mod tests {
|
||||||
use futures_util::future::lazy;
|
use futures_util::future::lazy;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{err, ok, IntoServiceFactory, Ready, ServiceExt, ServiceFactoryExt};
|
||||||
err, ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory,
|
|
||||||
ServiceFactoryExt,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Srv;
|
struct Srv;
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,14 @@ use core::{
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::and_then::{AndThenService, AndThenServiceFactory};
|
use crate::{
|
||||||
use crate::map::{Map, MapServiceFactory};
|
and_then::{AndThenService, AndThenServiceFactory},
|
||||||
use crate::map_err::{MapErr, MapErrServiceFactory};
|
map::{Map, MapServiceFactory},
|
||||||
use crate::map_init_err::MapInitErr;
|
map_err::{MapErr, MapErrServiceFactory},
|
||||||
use crate::then::{ThenService, ThenServiceFactory};
|
map_init_err::MapInitErr,
|
||||||
use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory};
|
then::{ThenService, ThenServiceFactory},
|
||||||
|
IntoService, IntoServiceFactory, Service, ServiceFactory,
|
||||||
|
};
|
||||||
|
|
||||||
/// Construct new pipeline with one service in pipeline chain.
|
/// Construct new pipeline with one service in pipeline chain.
|
||||||
pub(crate) fn pipeline<I, S, Req>(service: I) -> Pipeline<S, Req>
|
pub(crate) fn pipeline<I, S, Req>(service: I) -> Pipeline<S, Req>
|
||||||
|
@ -50,7 +52,7 @@ where
|
||||||
/// Call another service after call to this one has resolved successfully.
|
/// Call another service after call to this one has resolved successfully.
|
||||||
///
|
///
|
||||||
/// This function can be used to chain two services together and ensure that
|
/// This function can be used to chain two services together and ensure that
|
||||||
/// the second service isn't called until call to the fist service have
|
/// the second service isn't called until call to the first service have
|
||||||
/// finished. Result of the call to the first service is used as an
|
/// finished. Result of the call to the first service is used as an
|
||||||
/// input parameter for the second service's call.
|
/// input parameter for the second service's call.
|
||||||
///
|
///
|
||||||
|
@ -252,10 +254,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map this service's error to a different error, returning a new service.
|
/// Map this service's error to a different error, returning a new service.
|
||||||
pub fn map_err<F, E>(
|
pub fn map_err<F, E>(self, f: F) -> PipelineFactory<MapErrServiceFactory<SF, Req, F, E>, Req>
|
||||||
self,
|
|
||||||
f: F,
|
|
||||||
) -> PipelineFactory<MapErrServiceFactory<SF, Req, F, E>, Req>
|
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
F: Fn(SF::Error) -> E + Clone,
|
F: Fn(SF::Error) -> E + Clone,
|
||||||
|
|
|
@ -226,9 +226,9 @@ mod tests {
|
||||||
use actix_utils::future::{ready, Ready};
|
use actix_utils::future::{ready, Ready};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Service;
|
|
||||||
|
|
||||||
// pseudo-doctest for Transform trait
|
// pseudo-doctest for Transform trait
|
||||||
|
#[allow(unused)]
|
||||||
pub struct TimeoutTransform {
|
pub struct TimeoutTransform {
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
}
|
}
|
||||||
|
@ -250,6 +250,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
// pseudo-doctest for Transform trait
|
// pseudo-doctest for Transform trait
|
||||||
|
#[allow(unused)]
|
||||||
pub struct Timeout<S> {
|
pub struct Timeout<S> {
|
||||||
service: S,
|
service: S,
|
||||||
_timeout: Duration,
|
_timeout: Duration,
|
||||||
|
|
|
@ -1,19 +1,70 @@
|
||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
## 3.0.0 - 2021-12-26
|
## 3.4.0
|
||||||
* No significant changes since `3.0.0-rc.2`.
|
|
||||||
|
|
||||||
|
- Add `rustls-0_23`, `rustls-0_23-webpki-roots`, and `rustls-0_23-native-roots` crate features.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.70.
|
||||||
|
|
||||||
|
## 3.3.0
|
||||||
|
|
||||||
|
- Add `rustls-0_22` crate feature which excludes any root certificate methods or re-exports.
|
||||||
|
|
||||||
|
## 3.2.0
|
||||||
|
|
||||||
|
- Support Rustls v0.22.
|
||||||
|
- Add `{accept, connect}::rustls_0_22` modules.
|
||||||
|
- Add `rustls-0_21-native-roots` and `rustls-0_20-native-roots` crate features which utilize the `rustls-native-certs` crate to enable a `native_roots_cert_store()` functions in each rustls-based `connect` module.
|
||||||
|
- Implement `Host` for `http::Uri` (`http` crate version `1`).
|
||||||
|
|
||||||
|
## 3.1.1
|
||||||
|
|
||||||
|
- Fix `rustls` v0.21 version requirement.
|
||||||
|
|
||||||
|
## 3.1.0
|
||||||
|
|
||||||
|
- Support Rustls v0.21.
|
||||||
|
- Add `{accept, connect}::rustls_0_21` modules.
|
||||||
|
- Add `{accept, connect}::rustls_0_20` alias for `{accept, connect}::rustls` modules.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.65.
|
||||||
|
|
||||||
|
## 3.0.4
|
||||||
|
|
||||||
|
- Logs emitted now use the `tracing` crate with `log` compatibility. [#451]
|
||||||
|
|
||||||
|
[#451]: https://github.com/actix/actix-net/pull/451
|
||||||
|
|
||||||
|
## 3.0.3
|
||||||
|
|
||||||
|
- No significant changes since `3.0.2`.
|
||||||
|
|
||||||
|
## 3.0.2
|
||||||
|
|
||||||
|
- Expose `connect::Connection::new`. [#439]
|
||||||
|
|
||||||
|
[#439]: https://github.com/actix/actix-net/pull/439
|
||||||
|
|
||||||
|
## 3.0.1
|
||||||
|
|
||||||
|
- No significant changes since `3.0.0`.
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
|
- No significant changes since `3.0.0-rc.2`.
|
||||||
|
|
||||||
|
## 3.0.0-rc.2
|
||||||
|
|
||||||
## 3.0.0-rc.2 - 2021-12-10
|
|
||||||
- Re-export `openssl::SslConnectorBuilder` in `connect::openssl::reexports`. [#429]
|
- Re-export `openssl::SslConnectorBuilder` in `connect::openssl::reexports`. [#429]
|
||||||
|
|
||||||
[#429]: https://github.com/actix/actix-net/pull/429
|
[#429]: https://github.com/actix/actix-net/pull/429
|
||||||
|
|
||||||
## 3.0.0-rc.1 - 2021-11-29
|
## 3.0.0-rc.1
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Derive `Debug` for `connect::Connection`. [#422]
|
- Derive `Debug` for `connect::Connection`. [#422]
|
||||||
- Implement `Display` for `accept::TlsError`. [#422]
|
- Implement `Display` for `accept::TlsError`. [#422]
|
||||||
- Implement `Error` for `accept::TlsError` where both types also implement `Error`. [#422]
|
- Implement `Error` for `accept::TlsError` where both types also implement `Error`. [#422]
|
||||||
|
@ -23,6 +74,7 @@
|
||||||
- Implement `Default` for `connect::ConnectorService`. [#423]
|
- Implement `Default` for `connect::ConnectorService`. [#423]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- The crate's default features flags no longer include `uri`. [#422]
|
- The crate's default features flags no longer include `uri`. [#422]
|
||||||
- Useful re-exports from underlying TLS crates are exposed in a `reexports` modules in all acceptors and connectors.
|
- Useful re-exports from underlying TLS crates are exposed in a `reexports` modules in all acceptors and connectors.
|
||||||
- Convert `connect::ResolverService` from enum to struct. [#422]
|
- Convert `connect::ResolverService` from enum to struct. [#422]
|
||||||
|
@ -39,6 +91,7 @@
|
||||||
- Inline modules in `connect::tls` to `connect` module. [#422]
|
- Inline modules in `connect::tls` to `connect` module. [#422]
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Remove `connect::{new_connector, new_connector_factory, default_connector, default_connector_factory}` methods. [#422]
|
- Remove `connect::{new_connector, new_connector_factory, default_connector, default_connector_factory}` methods. [#422]
|
||||||
- Remove `connect::native_tls::Connector::service` method. [#422]
|
- Remove `connect::native_tls::Connector::service` method. [#422]
|
||||||
- Remove redundant `connect::Connection::from_parts` method. [#422]
|
- Remove redundant `connect::Connection::from_parts` method. [#422]
|
||||||
|
@ -46,8 +99,8 @@
|
||||||
[#422]: https://github.com/actix/actix-net/pull/422
|
[#422]: https://github.com/actix/actix-net/pull/422
|
||||||
[#423]: https://github.com/actix/actix-net/pull/423
|
[#423]: https://github.com/actix/actix-net/pull/423
|
||||||
|
|
||||||
|
## 3.0.0-beta.9
|
||||||
|
|
||||||
## 3.0.0-beta.9 - 2021-11-22
|
|
||||||
- Add configurable timeout for accepting TLS connection. [#393]
|
- Add configurable timeout for accepting TLS connection. [#393]
|
||||||
- Added `TlsError::Timeout` variant. [#393]
|
- Added `TlsError::Timeout` variant. [#393]
|
||||||
- All TLS acceptor services now use `TlsError` for their error types. [#393]
|
- All TLS acceptor services now use `TlsError` for their error types. [#393]
|
||||||
|
@ -56,31 +109,30 @@
|
||||||
[#393]: https://github.com/actix/actix-net/pull/393
|
[#393]: https://github.com/actix/actix-net/pull/393
|
||||||
[#420]: https://github.com/actix/actix-net/pull/420
|
[#420]: https://github.com/actix/actix-net/pull/420
|
||||||
|
|
||||||
|
## 3.0.0-beta.8
|
||||||
|
|
||||||
## 3.0.0-beta.8 - 2021-11-15
|
|
||||||
- Add `Connect::request` for getting a reference to the connection request. [#415]
|
- Add `Connect::request` for getting a reference to the connection request. [#415]
|
||||||
|
|
||||||
[#415]: https://github.com/actix/actix-net/pull/415
|
[#415]: https://github.com/actix/actix-net/pull/415
|
||||||
|
|
||||||
|
## 3.0.0-beta.7
|
||||||
|
|
||||||
## 3.0.0-beta.7 - 2021-10-20
|
|
||||||
- Add `webpki_roots_cert_store()` to get rustls compatible webpki roots cert store. [#401]
|
- Add `webpki_roots_cert_store()` to get rustls compatible webpki roots cert store. [#401]
|
||||||
- Alias `connect::ssl` to `connect::tls`. [#401]
|
- Alias `connect::ssl` to `connect::tls`. [#401]
|
||||||
|
|
||||||
[#401]: https://github.com/actix/actix-net/pull/401
|
[#401]: https://github.com/actix/actix-net/pull/401
|
||||||
|
|
||||||
|
## 3.0.0-beta.6
|
||||||
|
|
||||||
## 3.0.0-beta.6 - 2021-10-19
|
|
||||||
- Update `tokio-rustls` to `0.23` which uses `rustls` `0.20`. [#396]
|
- Update `tokio-rustls` to `0.23` which uses `rustls` `0.20`. [#396]
|
||||||
- Removed a re-export of `Session` from `rustls` as it no longer exist. [#396]
|
- Removed a re-export of `Session` from `rustls` as it no longer exist. [#396]
|
||||||
- Minimum supported Rust version (MSRV) is now 1.52.
|
- Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
[#396]: https://github.com/actix/actix-net/pull/396
|
[#396]: https://github.com/actix/actix-net/pull/396
|
||||||
|
|
||||||
|
## 3.0.0-beta.5
|
||||||
|
|
||||||
## 3.0.0-beta.5 - 2021-03-29
|
- Changed `connect::ssl::rustls::RustlsConnectorService` to return error when `DNSNameRef` generation failed instead of panic. [#296]
|
||||||
- Changed `connect::ssl::rustls::RustlsConnectorService` to return error when `DNSNameRef`
|
|
||||||
generation failed instead of panic. [#296]
|
|
||||||
- Remove `connect::ssl::openssl::OpensslConnectServiceFactory`. [#297]
|
- Remove `connect::ssl::openssl::OpensslConnectServiceFactory`. [#297]
|
||||||
- Remove `connect::ssl::openssl::OpensslConnectService`. [#297]
|
- Remove `connect::ssl::openssl::OpensslConnectService`. [#297]
|
||||||
- Add `connect::ssl::native_tls` module for native tls support. [#295]
|
- Add `connect::ssl::native_tls` module for native tls support. [#295]
|
||||||
|
@ -92,74 +144,72 @@
|
||||||
[#297]: https://github.com/actix/actix-net/pull/297
|
[#297]: https://github.com/actix/actix-net/pull/297
|
||||||
[#299]: https://github.com/actix/actix-net/pull/299
|
[#299]: https://github.com/actix/actix-net/pull/299
|
||||||
|
|
||||||
|
## 3.0.0-beta.4
|
||||||
|
|
||||||
## 3.0.0-beta.4 - 2021-02-24
|
|
||||||
- Rename `accept::openssl::{SslStream => TlsStream}`.
|
- Rename `accept::openssl::{SslStream => TlsStream}`.
|
||||||
- Add `connect::Connect::set_local_addr` to attach local `IpAddr`. [#282]
|
- Add `connect::Connect::set_local_addr` to attach local `IpAddr`. [#282]
|
||||||
- `connector::TcpConnector` service will try to bind to local_addr of `IpAddr` when given. [#282]
|
- `connector::TcpConnector` service will try to bind to local_addr of `IpAddr` when given. [#282]
|
||||||
|
|
||||||
[#282]: https://github.com/actix/actix-net/pull/282
|
[#282]: https://github.com/actix/actix-net/pull/282
|
||||||
|
|
||||||
|
## 3.0.0-beta.3
|
||||||
|
|
||||||
## 3.0.0-beta.3 - 2021-02-06
|
|
||||||
- Remove `trust-dns-proto` and `trust-dns-resolver`. [#248]
|
- Remove `trust-dns-proto` and `trust-dns-resolver`. [#248]
|
||||||
- Use `std::net::ToSocketAddrs` as simple and basic default resolver. [#248]
|
- Use `std::net::ToSocketAddrs` as simple and basic default resolver. [#248]
|
||||||
- Add `Resolve` trait for custom DNS resolvers. [#248]
|
- Add `Resolve` trait for custom DNS resolvers. [#248]
|
||||||
- Add `Resolver::new_custom` function to construct custom resolvers. [#248]
|
- Add `Resolver::new_custom` function to construct custom resolvers. [#248]
|
||||||
- Export `webpki_roots::TLS_SERVER_ROOTS` in `actix_tls::connect` mod and remove
|
- Export `webpki_roots::TLS_SERVER_ROOTS` in `actix_tls::connect` mod and remove the export from `actix_tls::accept` [#248]
|
||||||
the export from `actix_tls::accept` [#248]
|
- Remove `ConnectTakeAddrsIter`. `Connect::take_addrs` now returns `ConnectAddrsIter<'static>` as owned iterator. [#248]
|
||||||
- Remove `ConnectTakeAddrsIter`. `Connect::take_addrs` now returns `ConnectAddrsIter<'static>`
|
|
||||||
as owned iterator. [#248]
|
|
||||||
- Rename `Address::{host => hostname}` to more accurately describe which URL segment is returned.
|
- Rename `Address::{host => hostname}` to more accurately describe which URL segment is returned.
|
||||||
- Update `actix-rt` to `2.0.0`. [#273]
|
- Update `actix-rt` to `2.0.0`. [#273]
|
||||||
|
|
||||||
[#248]: https://github.com/actix/actix-net/pull/248
|
[#248]: https://github.com/actix/actix-net/pull/248
|
||||||
[#273]: https://github.com/actix/actix-net/pull/273
|
[#273]: https://github.com/actix/actix-net/pull/273
|
||||||
|
|
||||||
|
## 3.0.0-beta.2
|
||||||
|
|
||||||
## 3.0.0-beta.2 - 2021-xx-xx
|
|
||||||
- Depend on stable trust-dns packages. [#204]
|
- Depend on stable trust-dns packages. [#204]
|
||||||
|
|
||||||
[#204]: https://github.com/actix/actix-net/pull/204
|
[#204]: https://github.com/actix/actix-net/pull/204
|
||||||
|
|
||||||
|
## 3.0.0-beta.1
|
||||||
|
|
||||||
## 3.0.0-beta.1 - 2020-12-29
|
|
||||||
- Move acceptors under `accept` module. [#238]
|
- Move acceptors under `accept` module. [#238]
|
||||||
- Merge `actix-connect` crate under `connect` module. [#238]
|
- Merge `actix-connect` crate under `connect` module. [#238]
|
||||||
- Add feature flags to enable acceptors and/or connectors individually. [#238]
|
- Add feature flags to enable acceptors and/or connectors individually. [#238]
|
||||||
|
|
||||||
[#238]: https://github.com/actix/actix-net/pull/238
|
[#238]: https://github.com/actix/actix-net/pull/238
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
## 2.0.0 - 2020-09-03
|
|
||||||
- `nativetls::NativeTlsAcceptor` is renamed to `nativetls::Acceptor`.
|
- `nativetls::NativeTlsAcceptor` is renamed to `nativetls::Acceptor`.
|
||||||
- Where possible, "SSL" terminology is replaced with "TLS".
|
- Where possible, "SSL" terminology is replaced with "TLS".
|
||||||
* `SslError` is renamed to `TlsError`.
|
- `SslError` is renamed to `TlsError`.
|
||||||
* `TlsError::Ssl` enum variant is renamed to `TlsError::Tls`.
|
- `TlsError::Ssl` enum variant is renamed to `TlsError::Tls`.
|
||||||
* `max_concurrent_ssl_connect` is renamed to `max_concurrent_tls_connect`.
|
- `max_concurrent_ssl_connect` is renamed to `max_concurrent_tls_connect`.
|
||||||
|
|
||||||
|
## 2.0.0-alpha.2
|
||||||
|
|
||||||
## 2.0.0-alpha.2 - 2020-08-17
|
|
||||||
- Update `rustls` dependency to 0.18
|
- Update `rustls` dependency to 0.18
|
||||||
- Update `tokio-rustls` dependency to 0.14
|
- Update `tokio-rustls` dependency to 0.14
|
||||||
- Update `webpki-roots` dependency to 0.20
|
- Update `webpki-roots` dependency to 0.20
|
||||||
|
|
||||||
|
## [2.0.0-alpha.1]
|
||||||
|
|
||||||
## [2.0.0-alpha.1] - 2020-03-03
|
|
||||||
- Update `rustls` dependency to 0.17
|
- Update `rustls` dependency to 0.17
|
||||||
- Update `tokio-rustls` dependency to 0.13
|
- Update `tokio-rustls` dependency to 0.13
|
||||||
- Update `webpki-roots` dependency to 0.19
|
- Update `webpki-roots` dependency to 0.19
|
||||||
|
|
||||||
|
## [1.0.0]
|
||||||
|
|
||||||
## [1.0.0] - 2019-12-11
|
|
||||||
- 1.0.0 release
|
- 1.0.0 release
|
||||||
|
|
||||||
|
## [1.0.0-alpha.3]
|
||||||
|
|
||||||
## [1.0.0-alpha.3] - 2019-12-07
|
|
||||||
- Migrate to tokio 0.2
|
- Migrate to tokio 0.2
|
||||||
- Enable rustls acceptor service
|
- Enable rustls acceptor service
|
||||||
- Enable native-tls acceptor service
|
- Enable native-tls acceptor service
|
||||||
|
|
||||||
|
## [1.0.0-alpha.1]
|
||||||
|
|
||||||
## [1.0.0-alpha.1] - 2019-12-02
|
|
||||||
- Split openssl acceptor from actix-server package
|
- Split openssl acceptor from actix-server package
|
||||||
|
|
|
@ -1,24 +1,27 @@
|
||||||
[package]
|
[package]
|
||||||
name = "actix-tls"
|
name = "actix-tls"
|
||||||
version = "3.0.0"
|
version = "3.4.0"
|
||||||
authors = [
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
|
||||||
]
|
|
||||||
description = "TLS acceptor and connector services for Actix ecosystem"
|
description = "TLS acceptor and connector services for Actix ecosystem"
|
||||||
keywords = ["network", "tls", "ssl", "async", "transport"]
|
keywords = ["network", "tls", "ssl", "async", "transport"]
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
repository = "https://github.com/actix/actix-net.git"
|
||||||
categories = ["network-programming", "asynchronous", "cryptography"]
|
categories = ["network-programming", "asynchronous", "cryptography"]
|
||||||
license = "MIT OR Apache-2.0"
|
license.workspace = true
|
||||||
edition = "2018"
|
edition.workspace = true
|
||||||
|
rust-version.workspace = true
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
[lib]
|
[package.metadata.cargo_check_external_types]
|
||||||
name = "actix_tls"
|
allowed_external_types = ["actix_service::*", "actix_utils::*", "futures_core::*", "tokio::*"]
|
||||||
path = "src/lib.rs"
|
|
||||||
|
[package.metadata.cargo-machete]
|
||||||
|
ignored = [
|
||||||
|
"rustls_021", # specified to force version with add_trust_anchors method
|
||||||
|
"rustls_webpki_0101", # specified to force secure version
|
||||||
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["accept", "connect"]
|
default = ["accept", "connect"]
|
||||||
|
@ -30,55 +33,103 @@ accept = []
|
||||||
connect = []
|
connect = []
|
||||||
|
|
||||||
# use openssl impls
|
# use openssl impls
|
||||||
openssl = ["tls-openssl", "tokio-openssl"]
|
openssl = ["dep:tls-openssl", "dep:tokio-openssl"]
|
||||||
|
|
||||||
# use rustls impls
|
# alias for backwards compat
|
||||||
rustls = ["tokio-rustls", "webpki-roots"]
|
rustls = ["rustls-0_20"]
|
||||||
|
|
||||||
|
# use rustls v0.20 impls
|
||||||
|
rustls-0_20 = ["rustls-0_20-webpki-roots"]
|
||||||
|
rustls-0_20-webpki-roots = ["tokio-rustls-023", "webpki-roots-022"]
|
||||||
|
rustls-0_20-native-roots = ["tokio-rustls-023", "dep:rustls-native-certs-06"]
|
||||||
|
|
||||||
|
# use rustls v0.21 impls
|
||||||
|
rustls-0_21 = ["rustls-0_21-webpki-roots"]
|
||||||
|
rustls-0_21-webpki-roots = ["tokio-rustls-024", "webpki-roots-025"]
|
||||||
|
rustls-0_21-native-roots = ["tokio-rustls-024", "dep:rustls-native-certs-06"]
|
||||||
|
|
||||||
|
# use rustls v0.22 impls
|
||||||
|
rustls-0_22 = ["dep:tokio-rustls-025", "dep:rustls-pki-types-1"]
|
||||||
|
rustls-0_22-webpki-roots = ["rustls-0_22", "dep:webpki-roots-026"]
|
||||||
|
rustls-0_22-native-roots = ["rustls-0_22", "dep:rustls-native-certs-07"]
|
||||||
|
|
||||||
|
# use rustls v0.23 impls
|
||||||
|
rustls-0_23 = ["dep:tokio-rustls-026", "dep:rustls-pki-types-1"]
|
||||||
|
rustls-0_23-webpki-roots = ["rustls-0_23", "dep:webpki-roots-026"]
|
||||||
|
rustls-0_23-native-roots = ["rustls-0_23", "dep:rustls-native-certs-07"]
|
||||||
|
|
||||||
# use native-tls impls
|
# use native-tls impls
|
||||||
native-tls = ["tokio-native-tls"]
|
native-tls = ["dep:tokio-native-tls"]
|
||||||
|
|
||||||
# support http::Uri as connect address
|
# support http::Uri as connect address
|
||||||
uri = ["http"]
|
uri = ["dep:http-0_2", "dep:http-1"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.4.2"
|
actix-rt = { version = "2.2", default-features = false }
|
||||||
actix-rt = { version = "2.2.0", default-features = false }
|
actix-service = "2"
|
||||||
actix-service = "2.0.0"
|
actix-utils = "3"
|
||||||
actix-utils = "3.0.0"
|
|
||||||
|
|
||||||
derive_more = "0.99.5"
|
|
||||||
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||||
log = "0.4"
|
impl-more = "0.1"
|
||||||
pin-project-lite = "0.2.7"
|
pin-project-lite = "0.2.7"
|
||||||
tokio-util = { version = "0.6.3", default-features = false }
|
tokio = "1.23.1"
|
||||||
|
tokio-util = "0.7"
|
||||||
|
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||||
|
|
||||||
# uri
|
# uri
|
||||||
http = { version = "0.2.3", optional = true }
|
http-0_2 = { package = "http", version = "0.2.3", optional = true }
|
||||||
|
http-1 = { package = "http", version = "1", optional = true }
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
tls-openssl = { package = "openssl", version = "0.10.9", optional = true }
|
tls-openssl = { package = "openssl", version = "0.10.55", optional = true }
|
||||||
tokio-openssl = { version = "0.6", optional = true }
|
tokio-openssl = { version = "0.6", optional = true }
|
||||||
|
|
||||||
# rustls
|
# rustls PKI types
|
||||||
tokio-rustls = { version = "0.23", optional = true }
|
rustls-pki-types-1 = { package = "rustls-pki-types", version = "1", optional = true }
|
||||||
webpki-roots = { version = "0.22", optional = true }
|
|
||||||
|
# rustls v0.20
|
||||||
|
tokio-rustls-023 = { package = "tokio-rustls", version = "0.23", optional = true }
|
||||||
|
|
||||||
|
# rustls v0.21
|
||||||
|
tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", optional = true }
|
||||||
|
|
||||||
|
# rustls v0.22
|
||||||
|
tokio-rustls-025 = { package = "tokio-rustls", version = "0.25", optional = true }
|
||||||
|
|
||||||
|
# rustls v0.23
|
||||||
|
tokio-rustls-026 = { package = "tokio-rustls", version = "0.26", default-features = false, optional = true }
|
||||||
|
|
||||||
|
# webpki-roots used with rustls features
|
||||||
|
webpki-roots-022 = { package = "webpki-roots", version = "0.22", optional = true }
|
||||||
|
webpki-roots-025 = { package = "webpki-roots", version = "0.25", optional = true }
|
||||||
|
webpki-roots-026 = { package = "webpki-roots", version = "0.26", optional = true }
|
||||||
|
|
||||||
|
# native root certificates for rustls impls
|
||||||
|
rustls-native-certs-06 = { package = "rustls-native-certs", version = "0.6", optional = true }
|
||||||
|
rustls-native-certs-07 = { package = "rustls-native-certs", version = "0.7", optional = true }
|
||||||
|
|
||||||
# native-tls
|
# native-tls
|
||||||
tokio-native-tls = { version = "0.3", optional = true }
|
tokio-native-tls = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(any())'.dependencies]
|
||||||
|
rustls-021 = { package = "rustls", version = "0.21.6", optional = true } # force version with add_trust_anchors method
|
||||||
|
rustls-webpki-0101 = { package = "rustls-webpki", version = "0.101.4", optional = true } # force secure version
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2.0"
|
actix-codec = "0.5"
|
||||||
actix-server = "2.0.0-rc.3"
|
actix-rt = "2.2"
|
||||||
|
actix-server = "2"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
env_logger = "0.9"
|
futures-util = { version = "0.3.17", default-features = false, features = ["sink"] }
|
||||||
futures-util = { version = "0.3.7", default-features = false, features = ["sink"] }
|
itertools = "0.14"
|
||||||
log = "0.4"
|
pretty_env_logger = "0.5"
|
||||||
rcgen = "0.8"
|
rcgen = "0.13"
|
||||||
rustls-pemfile = "0.2.1"
|
rustls-pemfile = "2"
|
||||||
tokio-rustls = { version = "0.23", features = ["dangerous_configuration"] }
|
tokio-rustls-026 = { package = "tokio-rustls", version = "0.26" }
|
||||||
trust-dns-resolver = "0.20.0"
|
trust-dns-resolver = "0.23"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "accept-rustls"
|
name = "accept-rustls"
|
||||||
required-features = ["accept", "rustls"]
|
required-features = ["accept", "rustls-0_23"]
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# `actix-tls`
|
||||||
|
|
||||||
|
> TLS acceptor and connector services for the Actix ecosystem.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
|
[](https://crates.io/crates/actix-tls)
|
||||||
|
[](https://docs.rs/actix-tls/3.4.0)
|
||||||
|
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||||
|

|
||||||
|
<br />
|
||||||
|
[](https://deps.rs/crate/actix-tls/3.4.0)
|
||||||
|

|
||||||
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [Library Documentation](https://docs.rs/actix-tls)
|
||||||
|
- [Examples](/actix-tls/examples)
|
|
@ -15,14 +15,12 @@
|
||||||
//! http --verify=false https://127.0.0.1:8443
|
//! http --verify=false https://127.0.0.1:8443
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
// this use only exists because of how we have organised the crate
|
// this `use` is only exists because of how we have organised the crate
|
||||||
// it is not necessary for your actual code
|
// it is not necessary for your actual code; you should import from `rustls` normally
|
||||||
use tokio_rustls::rustls;
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
env,
|
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{self, BufReader},
|
io::{self, BufReader},
|
||||||
|
path::PathBuf,
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicUsize, Ordering},
|
atomic::{AtomicUsize, Ordering},
|
||||||
Arc,
|
Arc,
|
||||||
|
@ -32,32 +30,40 @@ use std::{
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use actix_server::Server;
|
use actix_server::Server;
|
||||||
use actix_service::ServiceFactoryExt as _;
|
use actix_service::ServiceFactoryExt as _;
|
||||||
use actix_tls::accept::rustls::{Acceptor as RustlsAcceptor, TlsStream};
|
use actix_tls::accept::rustls_0_23::{Acceptor as RustlsAcceptor, TlsStream};
|
||||||
use futures_util::future::ok;
|
use futures_util::future::ok;
|
||||||
use log::info;
|
use itertools::Itertools as _;
|
||||||
use rustls::{server::ServerConfig, Certificate, PrivateKey};
|
use rustls::server::ServerConfig;
|
||||||
use rustls_pemfile::{certs, rsa_private_keys};
|
use rustls_pemfile::{certs, rsa_private_keys};
|
||||||
|
use rustls_pki_types_1::PrivateKeyDer;
|
||||||
|
use tokio_rustls_026::rustls;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
#[actix_rt::main]
|
#[actix_rt::main]
|
||||||
async fn main() -> io::Result<()> {
|
async fn main() -> io::Result<()> {
|
||||||
env::set_var("RUST_LOG", "info");
|
pretty_env_logger::formatted_timed_builder()
|
||||||
env_logger::init();
|
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"));
|
||||||
|
|
||||||
|
let root_path = env!("CARGO_MANIFEST_DIR")
|
||||||
|
.parse::<PathBuf>()
|
||||||
|
.unwrap()
|
||||||
|
.join("examples");
|
||||||
|
let cert_path = root_path.clone().join("cert.pem");
|
||||||
|
let key_path = root_path.clone().join("key.pem");
|
||||||
|
|
||||||
// Load TLS key and cert files
|
// Load TLS key and cert files
|
||||||
let cert_file = &mut BufReader::new(File::open("./examples/cert.pem").unwrap());
|
let cert_file = &mut BufReader::new(File::open(cert_path).unwrap());
|
||||||
let key_file = &mut BufReader::new(File::open("./examples/key.pem").unwrap());
|
let key_file = &mut BufReader::new(File::open(key_path).unwrap());
|
||||||
|
|
||||||
let cert_chain = certs(cert_file)
|
let cert_chain = certs(cert_file);
|
||||||
.unwrap()
|
let mut keys = rsa_private_keys(key_file);
|
||||||
.into_iter()
|
|
||||||
.map(Certificate)
|
|
||||||
.collect();
|
|
||||||
let mut keys = rsa_private_keys(key_file).unwrap();
|
|
||||||
|
|
||||||
let tls_config = ServerConfig::builder()
|
let tls_config = ServerConfig::builder()
|
||||||
.with_safe_defaults()
|
|
||||||
.with_no_client_auth()
|
.with_no_client_auth()
|
||||||
.with_single_cert(cert_chain, PrivateKey(keys.remove(0)))
|
.with_single_cert(
|
||||||
|
cert_chain.try_collect::<_, Vec<_>, _>()?,
|
||||||
|
PrivateKeyDer::Pkcs1(keys.next().unwrap()?),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let tls_acceptor = RustlsAcceptor::new(tls_config);
|
let tls_acceptor = RustlsAcceptor::new(tls_config);
|
||||||
|
@ -65,7 +71,7 @@ async fn main() -> io::Result<()> {
|
||||||
let count = Arc::new(AtomicUsize::new(0));
|
let count = Arc::new(AtomicUsize::new(0));
|
||||||
|
|
||||||
let addr = ("127.0.0.1", 8443);
|
let addr = ("127.0.0.1", 8443);
|
||||||
info!("starting server on port: {}", &addr.0);
|
info!("starting server at: {addr:?}");
|
||||||
|
|
||||||
Server::build()
|
Server::build()
|
||||||
.bind("tls-example", addr, move || {
|
.bind("tls-example", addr, move || {
|
||||||
|
|
|
@ -2,27 +2,45 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
convert::Infallible,
|
convert::Infallible,
|
||||||
|
error::Error,
|
||||||
|
fmt,
|
||||||
sync::atomic::{AtomicUsize, Ordering},
|
sync::atomic::{AtomicUsize, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_utils::counter::Counter;
|
use actix_utils::counter::Counter;
|
||||||
use derive_more::{Display, Error};
|
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
|
|
||||||
pub mod openssl;
|
pub mod openssl;
|
||||||
|
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls-0_20")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
|
pub mod rustls_0_20;
|
||||||
pub mod rustls;
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[cfg(feature = "rustls-0_20")]
|
||||||
|
pub use rustls_0_20 as rustls;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls-0_21")]
|
||||||
|
pub mod rustls_0_21;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls-0_22")]
|
||||||
|
pub mod rustls_0_22;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls-0_23")]
|
||||||
|
pub mod rustls_0_23;
|
||||||
|
|
||||||
#[cfg(feature = "native-tls")]
|
#[cfg(feature = "native-tls")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
|
|
||||||
pub mod native_tls;
|
pub mod native_tls;
|
||||||
|
|
||||||
pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
|
pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
|
||||||
|
|
||||||
#[cfg(any(feature = "openssl", feature = "rustls", feature = "native-tls"))]
|
#[cfg(any(
|
||||||
|
feature = "openssl",
|
||||||
|
feature = "rustls-0_20",
|
||||||
|
feature = "rustls-0_21",
|
||||||
|
feature = "rustls-0_22",
|
||||||
|
feature = "rustls-0_23",
|
||||||
|
feature = "native-tls",
|
||||||
|
))]
|
||||||
pub(crate) const DEFAULT_TLS_HANDSHAKE_TIMEOUT: std::time::Duration =
|
pub(crate) const DEFAULT_TLS_HANDSHAKE_TIMEOUT: std::time::Duration =
|
||||||
std::time::Duration::from_secs(3);
|
std::time::Duration::from_secs(3);
|
||||||
|
|
||||||
|
@ -43,20 +61,18 @@ pub fn max_concurrent_tls_connect(num: usize) {
|
||||||
/// TLS handshake error, TLS timeout, or inner service error.
|
/// TLS handshake error, TLS timeout, or inner service error.
|
||||||
///
|
///
|
||||||
/// All TLS acceptors from this crate will return the `SvcErr` type parameter as [`Infallible`],
|
/// All TLS acceptors from this crate will return the `SvcErr` type parameter as [`Infallible`],
|
||||||
/// which can be cast to your own service type, inferred or otherwise,
|
/// which can be cast to your own service type, inferred or otherwise, using [`into_service_error`].
|
||||||
/// using [`into_service_error`](Self::into_service_error).
|
///
|
||||||
#[derive(Debug, Display, Error)]
|
/// [`into_service_error`]: Self::into_service_error
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum TlsError<TlsErr, SvcErr> {
|
pub enum TlsError<TlsErr, SvcErr> {
|
||||||
/// TLS handshake has timed-out.
|
/// TLS handshake has timed-out.
|
||||||
#[display(fmt = "TLS handshake has timed-out")]
|
|
||||||
Timeout,
|
Timeout,
|
||||||
|
|
||||||
/// Wraps TLS service errors.
|
/// Wraps TLS service errors.
|
||||||
#[display(fmt = "TLS handshake error")]
|
|
||||||
Tls(TlsErr),
|
Tls(TlsErr),
|
||||||
|
|
||||||
/// Wraps service errors.
|
/// Wraps service errors.
|
||||||
#[display(fmt = "Service error")]
|
|
||||||
Service(SvcErr),
|
Service(SvcErr),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +95,30 @@ impl<TlsErr> TlsError<TlsErr, Infallible> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<TlsErr, SvcErr> fmt::Display for TlsError<TlsErr, SvcErr> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Timeout => f.write_str("TLS handshake has timed-out"),
|
||||||
|
Self::Tls(_) => f.write_str("TLS handshake error"),
|
||||||
|
Self::Service(_) => f.write_str("Service error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TlsErr, SvcErr> Error for TlsError<TlsErr, SvcErr>
|
||||||
|
where
|
||||||
|
TlsErr: Error + 'static,
|
||||||
|
SvcErr: Error + 'static,
|
||||||
|
{
|
||||||
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
TlsError::Tls(err) => Some(err),
|
||||||
|
TlsError::Service(err) => Some(err),
|
||||||
|
TlsError::Timeout => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -10,7 +10,6 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, ReadBuf};
|
|
||||||
use actix_rt::{
|
use actix_rt::{
|
||||||
net::{ActixStream, Ready},
|
net::{ActixStream, Ready},
|
||||||
time::timeout,
|
time::timeout,
|
||||||
|
@ -20,8 +19,8 @@ use actix_utils::{
|
||||||
counter::Counter,
|
counter::Counter,
|
||||||
future::{ready, Ready as FutReady},
|
future::{ready, Ready as FutReady},
|
||||||
};
|
};
|
||||||
use derive_more::{Deref, DerefMut, From};
|
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
use tokio_native_tls::{native_tls::Error, TlsAcceptor};
|
use tokio_native_tls::{native_tls::Error, TlsAcceptor};
|
||||||
|
|
||||||
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
||||||
|
@ -33,9 +32,11 @@ pub mod reexports {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps a `native-tls` based async TLS stream in order to implement [`ActixStream`].
|
/// Wraps a `native-tls` based async TLS stream in order to implement [`ActixStream`].
|
||||||
#[derive(Deref, DerefMut, From)]
|
|
||||||
pub struct TlsStream<IO>(tokio_native_tls::TlsStream<IO>);
|
pub struct TlsStream<IO>(tokio_native_tls::TlsStream<IO>);
|
||||||
|
|
||||||
|
impl_more::impl_from!(<IO> in tokio_native_tls::TlsStream<IO> => TlsStream<IO>);
|
||||||
|
impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_native_tls::TlsStream<IO>);
|
||||||
|
|
||||||
impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
|
impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
|
||||||
fn poll_read(
|
fn poll_read(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
|
@ -72,17 +73,17 @@ impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_write_vectored(&self) -> bool {
|
fn is_write_vectored(&self) -> bool {
|
||||||
(&**self).is_write_vectored()
|
(**self).is_write_vectored()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
||||||
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
IO::poll_read_ready((&**self).get_ref().get_ref().get_ref(), cx)
|
IO::poll_read_ready((**self).get_ref().get_ref().get_ref(), cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
IO::poll_write_ready((&**self).get_ref().get_ref().get_ref(), cx)
|
IO::poll_write_ready((**self).get_ref().get_ref().get_ref(), cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, ReadBuf};
|
|
||||||
use actix_rt::{
|
use actix_rt::{
|
||||||
net::{ActixStream, Ready},
|
net::{ActixStream, Ready},
|
||||||
time::{sleep, Sleep},
|
time::{sleep, Sleep},
|
||||||
|
@ -21,9 +20,9 @@ use actix_utils::{
|
||||||
counter::{Counter, CounterGuard},
|
counter::{Counter, CounterGuard},
|
||||||
future::{ready, Ready as FutReady},
|
future::{ready, Ready as FutReady},
|
||||||
};
|
};
|
||||||
use derive_more::{Deref, DerefMut, From};
|
|
||||||
use openssl::ssl::{Error, Ssl, SslAcceptor};
|
use openssl::ssl::{Error, Ssl, SslAcceptor};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
|
|
||||||
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
||||||
|
|
||||||
|
@ -36,9 +35,11 @@ pub mod reexports {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps an `openssl` based async TLS stream in order to implement [`ActixStream`].
|
/// Wraps an `openssl` based async TLS stream in order to implement [`ActixStream`].
|
||||||
#[derive(Deref, DerefMut, From)]
|
|
||||||
pub struct TlsStream<IO>(tokio_openssl::SslStream<IO>);
|
pub struct TlsStream<IO>(tokio_openssl::SslStream<IO>);
|
||||||
|
|
||||||
|
impl_more::impl_from!(<IO> in tokio_openssl::SslStream<IO> => TlsStream<IO>);
|
||||||
|
impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_openssl::SslStream<IO>);
|
||||||
|
|
||||||
impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
|
impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
|
||||||
fn poll_read(
|
fn poll_read(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
|
@ -75,17 +76,17 @@ impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_write_vectored(&self) -> bool {
|
fn is_write_vectored(&self) -> bool {
|
||||||
(&**self).is_write_vectored()
|
(**self).is_write_vectored()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
||||||
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
IO::poll_read_ready((&**self).get_ref(), cx)
|
IO::poll_read_ready((**self).get_ref(), cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
IO::poll_write_ready((&**self).get_ref(), cx)
|
IO::poll_write_ready((**self).get_ref(), cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//! `rustls` based TLS connection acceptor service.
|
//! `rustls` v0.20 based TLS connection acceptor service.
|
||||||
//!
|
//!
|
||||||
//! See [`Acceptor`] for main service factory docs.
|
//! See [`Acceptor`] for main service factory docs.
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, ReadBuf};
|
|
||||||
use actix_rt::{
|
use actix_rt::{
|
||||||
net::{ActixStream, Ready},
|
net::{ActixStream, Ready},
|
||||||
time::{sleep, Sleep},
|
time::{sleep, Sleep},
|
||||||
|
@ -22,23 +21,25 @@ use actix_utils::{
|
||||||
counter::{Counter, CounterGuard},
|
counter::{Counter, CounterGuard},
|
||||||
future::{ready, Ready as FutReady},
|
future::{ready, Ready as FutReady},
|
||||||
};
|
};
|
||||||
use derive_more::{Deref, DerefMut, From};
|
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
use tokio_rustls::rustls::ServerConfig;
|
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
use tokio_rustls::{Accept, TlsAcceptor};
|
use tokio_rustls::{Accept, TlsAcceptor};
|
||||||
|
use tokio_rustls_023 as tokio_rustls;
|
||||||
|
|
||||||
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
||||||
|
|
||||||
pub mod reexports {
|
pub mod reexports {
|
||||||
//! Re-exports from `rustls` that are useful for acceptors.
|
//! Re-exports from `rustls` that are useful for acceptors.
|
||||||
|
|
||||||
pub use tokio_rustls::rustls::ServerConfig;
|
pub use tokio_rustls_023::rustls::ServerConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
|
/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
|
||||||
#[derive(Deref, DerefMut, From)]
|
|
||||||
pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);
|
pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);
|
||||||
|
|
||||||
|
impl_more::impl_from!(<IO> in tokio_rustls::server::TlsStream<IO> => TlsStream<IO>);
|
||||||
|
impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_rustls::server::TlsStream<IO>);
|
||||||
|
|
||||||
impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
|
impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
|
||||||
fn poll_read(
|
fn poll_read(
|
||||||
self: Pin<&mut Self>,
|
self: Pin<&mut Self>,
|
||||||
|
@ -75,29 +76,29 @@ impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_write_vectored(&self) -> bool {
|
fn is_write_vectored(&self) -> bool {
|
||||||
(&**self).is_write_vectored()
|
(**self).is_write_vectored()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
||||||
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
IO::poll_read_ready((&**self).get_ref().0, cx)
|
IO::poll_read_ready((**self).get_ref().0, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
IO::poll_write_ready((&**self).get_ref().0, cx)
|
IO::poll_write_ready((**self).get_ref().0, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Accept TLS connections via the `rustls` crate.
|
/// Accept TLS connections via the `rustls` crate.
|
||||||
pub struct Acceptor {
|
pub struct Acceptor {
|
||||||
config: Arc<ServerConfig>,
|
config: Arc<reexports::ServerConfig>,
|
||||||
handshake_timeout: Duration,
|
handshake_timeout: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Acceptor {
|
impl Acceptor {
|
||||||
/// Constructs `rustls` based acceptor service factory.
|
/// Constructs `rustls` based acceptor service factory.
|
||||||
pub fn new(config: ServerConfig) -> Self {
|
pub fn new(config: reexports::ServerConfig) -> Self {
|
||||||
Acceptor {
|
Acceptor {
|
||||||
config: Arc::new(config),
|
config: Arc::new(config),
|
||||||
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
|
@ -0,0 +1,198 @@
|
||||||
|
//! `rustls` v0.21 based TLS connection acceptor service.
|
||||||
|
//!
|
||||||
|
//! See [`Acceptor`] for main service factory docs.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
convert::Infallible,
|
||||||
|
future::Future,
|
||||||
|
io::{self, IoSlice},
|
||||||
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
|
task::{Context, Poll},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_rt::{
|
||||||
|
net::{ActixStream, Ready},
|
||||||
|
time::{sleep, Sleep},
|
||||||
|
};
|
||||||
|
use actix_service::{Service, ServiceFactory};
|
||||||
|
use actix_utils::{
|
||||||
|
counter::{Counter, CounterGuard},
|
||||||
|
future::{ready, Ready as FutReady},
|
||||||
|
};
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
|
use tokio_rustls::{Accept, TlsAcceptor};
|
||||||
|
use tokio_rustls_024 as tokio_rustls;
|
||||||
|
|
||||||
|
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
||||||
|
|
||||||
|
pub mod reexports {
|
||||||
|
//! Re-exports from `rustls` that are useful for acceptors.
|
||||||
|
|
||||||
|
pub use tokio_rustls_024::rustls::ServerConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
|
||||||
|
pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);
|
||||||
|
|
||||||
|
impl_more::impl_from!(<IO> in tokio_rustls::server::TlsStream<IO> => TlsStream<IO>);
|
||||||
|
impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_rustls::server::TlsStream<IO>);
|
||||||
|
|
||||||
|
impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
|
||||||
|
fn poll_read(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut ReadBuf<'_>,
|
||||||
|
) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
|
||||||
|
fn poll_write(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_write_vectored(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
bufs: &[IoSlice<'_>],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
(**self).is_write_vectored()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
||||||
|
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
|
IO::poll_read_ready((**self).get_ref().0, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
|
IO::poll_write_ready((**self).get_ref().0, cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accept TLS connections via the `rustls` crate.
|
||||||
|
pub struct Acceptor {
|
||||||
|
config: Arc<reexports::ServerConfig>,
|
||||||
|
handshake_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Acceptor {
|
||||||
|
/// Constructs `rustls` based acceptor service factory.
|
||||||
|
pub fn new(config: reexports::ServerConfig) -> Self {
|
||||||
|
Acceptor {
|
||||||
|
config: Arc::new(config),
|
||||||
|
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
|
||||||
|
///
|
||||||
|
/// Default timeout is 3 seconds.
|
||||||
|
pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
|
||||||
|
self.handshake_timeout = handshake_timeout;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Acceptor {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
config: self.config.clone(),
|
||||||
|
handshake_timeout: self.handshake_timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> ServiceFactory<IO> for Acceptor {
|
||||||
|
type Response = TlsStream<IO>;
|
||||||
|
type Error = TlsError<io::Error, Infallible>;
|
||||||
|
type Config = ();
|
||||||
|
type Service = AcceptorService;
|
||||||
|
type InitError = ();
|
||||||
|
type Future = FutReady<Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
|
fn new_service(&self, _: ()) -> Self::Future {
|
||||||
|
let res = MAX_CONN_COUNTER.with(|conns| {
|
||||||
|
Ok(AcceptorService {
|
||||||
|
acceptor: self.config.clone().into(),
|
||||||
|
conns: conns.clone(),
|
||||||
|
handshake_timeout: self.handshake_timeout,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
ready(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rustls based acceptor service.
|
||||||
|
pub struct AcceptorService {
|
||||||
|
acceptor: TlsAcceptor,
|
||||||
|
conns: Counter,
|
||||||
|
handshake_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> Service<IO> for AcceptorService {
|
||||||
|
type Response = TlsStream<IO>;
|
||||||
|
type Error = TlsError<io::Error, Infallible>;
|
||||||
|
type Future = AcceptFut<IO>;
|
||||||
|
|
||||||
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
if self.conns.available(cx) {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, req: IO) -> Self::Future {
|
||||||
|
AcceptFut {
|
||||||
|
fut: self.acceptor.accept(req),
|
||||||
|
timeout: sleep(self.handshake_timeout),
|
||||||
|
_guard: self.conns.get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
/// Accept future for Rustls service.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct AcceptFut<IO: ActixStream> {
|
||||||
|
fut: Accept<IO>,
|
||||||
|
#[pin]
|
||||||
|
timeout: Sleep,
|
||||||
|
_guard: CounterGuard,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> Future for AcceptFut<IO> {
|
||||||
|
type Output = Result<TlsStream<IO>, TlsError<io::Error, Infallible>>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut this = self.project();
|
||||||
|
match Pin::new(&mut this.fut).poll(cx) {
|
||||||
|
Poll::Ready(Ok(stream)) => Poll::Ready(Ok(TlsStream(stream))),
|
||||||
|
Poll::Ready(Err(err)) => Poll::Ready(Err(TlsError::Tls(err))),
|
||||||
|
Poll::Pending => this.timeout.poll(cx).map(|_| Err(TlsError::Timeout)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,198 @@
|
||||||
|
//! `rustls` v0.22 based TLS connection acceptor service.
|
||||||
|
//!
|
||||||
|
//! See [`Acceptor`] for main service factory docs.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
convert::Infallible,
|
||||||
|
future::Future,
|
||||||
|
io::{self, IoSlice},
|
||||||
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
|
task::{Context, Poll},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_rt::{
|
||||||
|
net::{ActixStream, Ready},
|
||||||
|
time::{sleep, Sleep},
|
||||||
|
};
|
||||||
|
use actix_service::{Service, ServiceFactory};
|
||||||
|
use actix_utils::{
|
||||||
|
counter::{Counter, CounterGuard},
|
||||||
|
future::{ready, Ready as FutReady},
|
||||||
|
};
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
|
use tokio_rustls::{Accept, TlsAcceptor};
|
||||||
|
use tokio_rustls_025 as tokio_rustls;
|
||||||
|
|
||||||
|
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
||||||
|
|
||||||
|
pub mod reexports {
|
||||||
|
//! Re-exports from `rustls` that are useful for acceptors.
|
||||||
|
|
||||||
|
pub use tokio_rustls_025::rustls::ServerConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
|
||||||
|
pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);
|
||||||
|
|
||||||
|
impl_more::impl_from!(<IO> in tokio_rustls::server::TlsStream<IO> => TlsStream<IO>);
|
||||||
|
impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_rustls::server::TlsStream<IO>);
|
||||||
|
|
||||||
|
impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
|
||||||
|
fn poll_read(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut ReadBuf<'_>,
|
||||||
|
) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
|
||||||
|
fn poll_write(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_write_vectored(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
bufs: &[IoSlice<'_>],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
(**self).is_write_vectored()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
||||||
|
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
|
IO::poll_read_ready((**self).get_ref().0, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
|
IO::poll_write_ready((**self).get_ref().0, cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accept TLS connections via the `rustls` crate.
|
||||||
|
pub struct Acceptor {
|
||||||
|
config: Arc<reexports::ServerConfig>,
|
||||||
|
handshake_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Acceptor {
|
||||||
|
/// Constructs `rustls` based acceptor service factory.
|
||||||
|
pub fn new(config: reexports::ServerConfig) -> Self {
|
||||||
|
Acceptor {
|
||||||
|
config: Arc::new(config),
|
||||||
|
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
|
||||||
|
///
|
||||||
|
/// Default timeout is 3 seconds.
|
||||||
|
pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
|
||||||
|
self.handshake_timeout = handshake_timeout;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Acceptor {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
config: self.config.clone(),
|
||||||
|
handshake_timeout: self.handshake_timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> ServiceFactory<IO> for Acceptor {
|
||||||
|
type Response = TlsStream<IO>;
|
||||||
|
type Error = TlsError<io::Error, Infallible>;
|
||||||
|
type Config = ();
|
||||||
|
type Service = AcceptorService;
|
||||||
|
type InitError = ();
|
||||||
|
type Future = FutReady<Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
|
fn new_service(&self, _: ()) -> Self::Future {
|
||||||
|
let res = MAX_CONN_COUNTER.with(|conns| {
|
||||||
|
Ok(AcceptorService {
|
||||||
|
acceptor: self.config.clone().into(),
|
||||||
|
conns: conns.clone(),
|
||||||
|
handshake_timeout: self.handshake_timeout,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
ready(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rustls based acceptor service.
|
||||||
|
pub struct AcceptorService {
|
||||||
|
acceptor: TlsAcceptor,
|
||||||
|
conns: Counter,
|
||||||
|
handshake_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> Service<IO> for AcceptorService {
|
||||||
|
type Response = TlsStream<IO>;
|
||||||
|
type Error = TlsError<io::Error, Infallible>;
|
||||||
|
type Future = AcceptFut<IO>;
|
||||||
|
|
||||||
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
if self.conns.available(cx) {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, req: IO) -> Self::Future {
|
||||||
|
AcceptFut {
|
||||||
|
fut: self.acceptor.accept(req),
|
||||||
|
timeout: sleep(self.handshake_timeout),
|
||||||
|
_guard: self.conns.get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
/// Accept future for Rustls service.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct AcceptFut<IO: ActixStream> {
|
||||||
|
fut: Accept<IO>,
|
||||||
|
#[pin]
|
||||||
|
timeout: Sleep,
|
||||||
|
_guard: CounterGuard,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> Future for AcceptFut<IO> {
|
||||||
|
type Output = Result<TlsStream<IO>, TlsError<io::Error, Infallible>>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut this = self.project();
|
||||||
|
match Pin::new(&mut this.fut).poll(cx) {
|
||||||
|
Poll::Ready(Ok(stream)) => Poll::Ready(Ok(TlsStream(stream))),
|
||||||
|
Poll::Ready(Err(err)) => Poll::Ready(Err(TlsError::Tls(err))),
|
||||||
|
Poll::Pending => this.timeout.poll(cx).map(|_| Err(TlsError::Timeout)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,198 @@
|
||||||
|
//! `rustls` v0.23 based TLS connection acceptor service.
|
||||||
|
//!
|
||||||
|
//! See [`Acceptor`] for main service factory docs.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
convert::Infallible,
|
||||||
|
future::Future,
|
||||||
|
io::{self, IoSlice},
|
||||||
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
|
task::{Context, Poll},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_rt::{
|
||||||
|
net::{ActixStream, Ready},
|
||||||
|
time::{sleep, Sleep},
|
||||||
|
};
|
||||||
|
use actix_service::{Service, ServiceFactory};
|
||||||
|
use actix_utils::{
|
||||||
|
counter::{Counter, CounterGuard},
|
||||||
|
future::{ready, Ready as FutReady},
|
||||||
|
};
|
||||||
|
use pin_project_lite::pin_project;
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||||
|
use tokio_rustls::{Accept, TlsAcceptor};
|
||||||
|
use tokio_rustls_026 as tokio_rustls;
|
||||||
|
|
||||||
|
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
||||||
|
|
||||||
|
pub mod reexports {
|
||||||
|
//! Re-exports from `rustls` that are useful for acceptors.
|
||||||
|
|
||||||
|
pub use tokio_rustls_026::rustls::ServerConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps a `rustls` based async TLS stream in order to implement [`ActixStream`].
|
||||||
|
pub struct TlsStream<IO>(tokio_rustls::server::TlsStream<IO>);
|
||||||
|
|
||||||
|
impl_more::impl_from!(<IO> in tokio_rustls::server::TlsStream<IO> => TlsStream<IO>);
|
||||||
|
impl_more::impl_deref_and_mut!(<IO> in TlsStream<IO> => tokio_rustls::server::TlsStream<IO>);
|
||||||
|
|
||||||
|
impl<IO: ActixStream> AsyncRead for TlsStream<IO> {
|
||||||
|
fn poll_read(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut ReadBuf<'_>,
|
||||||
|
) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_read(cx, buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> AsyncWrite for TlsStream<IO> {
|
||||||
|
fn poll_write(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_write(cx, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_flush(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_shutdown(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_write_vectored(
|
||||||
|
self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
bufs: &[IoSlice<'_>],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
Pin::new(&mut **self.get_mut()).poll_write_vectored(cx, bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
(**self).is_write_vectored()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
||||||
|
fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
|
IO::poll_read_ready((**self).get_ref().0, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<Ready>> {
|
||||||
|
IO::poll_write_ready((**self).get_ref().0, cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accept TLS connections via the `rustls` crate.
|
||||||
|
pub struct Acceptor {
|
||||||
|
config: Arc<reexports::ServerConfig>,
|
||||||
|
handshake_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Acceptor {
|
||||||
|
/// Constructs `rustls` based acceptor service factory.
|
||||||
|
pub fn new(config: reexports::ServerConfig) -> Self {
|
||||||
|
Acceptor {
|
||||||
|
config: Arc::new(config),
|
||||||
|
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Limit the amount of time that the acceptor will wait for a TLS handshake to complete.
|
||||||
|
///
|
||||||
|
/// Default timeout is 3 seconds.
|
||||||
|
pub fn set_handshake_timeout(&mut self, handshake_timeout: Duration) -> &mut Self {
|
||||||
|
self.handshake_timeout = handshake_timeout;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Acceptor {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
config: self.config.clone(),
|
||||||
|
handshake_timeout: self.handshake_timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> ServiceFactory<IO> for Acceptor {
|
||||||
|
type Response = TlsStream<IO>;
|
||||||
|
type Error = TlsError<io::Error, Infallible>;
|
||||||
|
type Config = ();
|
||||||
|
type Service = AcceptorService;
|
||||||
|
type InitError = ();
|
||||||
|
type Future = FutReady<Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
|
fn new_service(&self, _: ()) -> Self::Future {
|
||||||
|
let res = MAX_CONN_COUNTER.with(|conns| {
|
||||||
|
Ok(AcceptorService {
|
||||||
|
acceptor: self.config.clone().into(),
|
||||||
|
conns: conns.clone(),
|
||||||
|
handshake_timeout: self.handshake_timeout,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
ready(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rustls based acceptor service.
|
||||||
|
pub struct AcceptorService {
|
||||||
|
acceptor: TlsAcceptor,
|
||||||
|
conns: Counter,
|
||||||
|
handshake_timeout: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> Service<IO> for AcceptorService {
|
||||||
|
type Response = TlsStream<IO>;
|
||||||
|
type Error = TlsError<io::Error, Infallible>;
|
||||||
|
type Future = AcceptFut<IO>;
|
||||||
|
|
||||||
|
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
if self.conns.available(cx) {
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
} else {
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&self, req: IO) -> Self::Future {
|
||||||
|
AcceptFut {
|
||||||
|
fut: self.acceptor.accept(req),
|
||||||
|
timeout: sleep(self.handshake_timeout),
|
||||||
|
_guard: self.conns.get(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_project! {
|
||||||
|
/// Accept future for Rustls service.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub struct AcceptFut<IO: ActixStream> {
|
||||||
|
fut: Accept<IO>,
|
||||||
|
#[pin]
|
||||||
|
timeout: Sleep,
|
||||||
|
_guard: CounterGuard,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<IO: ActixStream> Future for AcceptFut<IO> {
|
||||||
|
type Output = Result<TlsStream<IO>, TlsError<io::Error, Infallible>>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
let mut this = self.project();
|
||||||
|
match Pin::new(&mut this.fut).poll(cx) {
|
||||||
|
Poll::Ready(Ok(stream)) => Poll::Ready(Ok(TlsStream(stream))),
|
||||||
|
Poll::Ready(Err(err)) => Poll::Ready(Err(TlsError::Tls(err))),
|
||||||
|
Poll::Pending => this.timeout.poll(cx).map(|_| Err(TlsError::Timeout)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,17 @@
|
||||||
use derive_more::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use super::Host;
|
use super::Host;
|
||||||
|
|
||||||
/// Wraps underlying I/O and the connection request that initiated it.
|
/// Wraps underlying I/O and the connection request that initiated it.
|
||||||
#[derive(Debug, Deref, DerefMut)]
|
#[derive(Debug)]
|
||||||
pub struct Connection<R, IO> {
|
pub struct Connection<R, IO> {
|
||||||
pub(crate) req: R,
|
pub(crate) req: R,
|
||||||
|
|
||||||
#[deref]
|
|
||||||
#[deref_mut]
|
|
||||||
pub(crate) io: IO,
|
pub(crate) io: IO,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_more::impl_deref_and_mut!(<R, IO> in Connection<R, IO> => io: IO);
|
||||||
|
|
||||||
impl<R, IO> Connection<R, IO> {
|
impl<R, IO> Connection<R, IO> {
|
||||||
/// Construct new `Connection` from request and IO parts.
|
/// Construct new `Connection` from request and IO parts.
|
||||||
pub(crate) fn new(req: R, io: IO) -> Self {
|
pub fn new(req: R, io: IO) -> Self {
|
||||||
Self { req, io }
|
Self { req, io }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,38 @@
|
||||||
use std::{error::Error, io};
|
use std::{error::Error, fmt, io};
|
||||||
|
|
||||||
use derive_more::Display;
|
|
||||||
|
|
||||||
/// Errors that can result from using a connector service.
|
/// Errors that can result from using a connector service.
|
||||||
#[derive(Debug, Display)]
|
#[derive(Debug)]
|
||||||
pub enum ConnectError {
|
pub enum ConnectError {
|
||||||
/// Failed to resolve the hostname
|
/// Failed to resolve the hostname.
|
||||||
#[display(fmt = "Failed resolving hostname")]
|
|
||||||
Resolver(Box<dyn std::error::Error>),
|
Resolver(Box<dyn std::error::Error>),
|
||||||
|
|
||||||
/// No DNS records
|
/// No DNS records.
|
||||||
#[display(fmt = "No DNS records found for the input")]
|
|
||||||
NoRecords,
|
NoRecords,
|
||||||
|
|
||||||
/// Invalid input
|
/// Invalid input.
|
||||||
InvalidInput,
|
InvalidInput,
|
||||||
|
|
||||||
/// Unresolved host name
|
/// Unresolved host name.
|
||||||
#[display(fmt = "Connector received `Connect` method with unresolved host")]
|
|
||||||
Unresolved,
|
Unresolved,
|
||||||
|
|
||||||
/// Connection IO error
|
/// Connection IO error.
|
||||||
#[display(fmt = "{}", _0)]
|
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ConnectError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::NoRecords => f.write_str("No DNS records found for the input"),
|
||||||
|
Self::InvalidInput => f.write_str("Invalid input"),
|
||||||
|
Self::Unresolved => {
|
||||||
|
f.write_str("Connector received `Connect` method with unresolved host")
|
||||||
|
}
|
||||||
|
Self::Resolver(_) => f.write_str("Failed to resolve hostname"),
|
||||||
|
Self::Io(_) => f.write_str("I/O error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Error for ConnectError {
|
impl Error for ConnectError {
|
||||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
@ -118,6 +118,7 @@ impl<R: Host> ConnectInfo<R> {
|
||||||
/// let mut addrs = conn.addrs();
|
/// let mut addrs = conn.addrs();
|
||||||
/// assert_eq!(addrs.next().unwrap(), addr);
|
/// assert_eq!(addrs.next().unwrap(), addr);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[allow(clippy::implied_bounds_in_impls)]
|
||||||
pub fn addrs(
|
pub fn addrs(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = SocketAddr>
|
) -> impl Iterator<Item = SocketAddr>
|
||||||
|
@ -149,6 +150,7 @@ impl<R: Host> ConnectInfo<R> {
|
||||||
/// let mut addrs = conn.take_addrs();
|
/// let mut addrs = conn.take_addrs();
|
||||||
/// assert_eq!(addrs.next().unwrap(), addr);
|
/// assert_eq!(addrs.next().unwrap(), addr);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[allow(clippy::implied_bounds_in_impls)]
|
||||||
pub fn take_addrs(
|
pub fn take_addrs(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> impl Iterator<Item = SocketAddr>
|
) -> impl Iterator<Item = SocketAddr>
|
||||||
|
|
|
@ -22,25 +22,45 @@ mod resolver;
|
||||||
pub mod tcp;
|
pub mod tcp;
|
||||||
|
|
||||||
#[cfg(feature = "uri")]
|
#[cfg(feature = "uri")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "uri")))]
|
|
||||||
mod uri;
|
mod uri;
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
|
|
||||||
pub mod openssl;
|
pub mod openssl;
|
||||||
|
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(any(
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
|
feature = "rustls-0_20-webpki-roots",
|
||||||
pub mod rustls;
|
feature = "rustls-0_20-native-roots",
|
||||||
|
))]
|
||||||
|
pub mod rustls_0_20;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "rustls-0_20-webpki-roots",
|
||||||
|
feature = "rustls-0_20-native-roots",
|
||||||
|
))]
|
||||||
|
pub use rustls_0_20 as rustls;
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "rustls-0_21-webpki-roots",
|
||||||
|
feature = "rustls-0_21-native-roots",
|
||||||
|
))]
|
||||||
|
pub mod rustls_0_21;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls-0_22")]
|
||||||
|
pub mod rustls_0_22;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls-0_23")]
|
||||||
|
pub mod rustls_0_23;
|
||||||
|
|
||||||
#[cfg(feature = "native-tls")]
|
#[cfg(feature = "native-tls")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
|
|
||||||
pub mod native_tls;
|
pub mod native_tls;
|
||||||
|
|
||||||
pub use self::connection::Connection;
|
pub use self::{
|
||||||
pub use self::connector::{Connector, ConnectorService};
|
connection::Connection,
|
||||||
pub use self::error::ConnectError;
|
connector::{Connector, ConnectorService},
|
||||||
pub use self::host::Host;
|
error::ConnectError,
|
||||||
pub use self::info::ConnectInfo;
|
host::Host,
|
||||||
pub use self::resolve::Resolve;
|
info::ConnectInfo,
|
||||||
pub use self::resolver::{Resolver, ResolverService};
|
resolve::Resolve,
|
||||||
|
resolver::{Resolver, ResolverService},
|
||||||
|
};
|
||||||
|
|
|
@ -8,20 +8,18 @@ use actix_rt::net::ActixStream;
|
||||||
use actix_service::{Service, ServiceFactory};
|
use actix_service::{Service, ServiceFactory};
|
||||||
use actix_utils::future::{ok, Ready};
|
use actix_utils::future::{ok, Ready};
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use log::trace;
|
|
||||||
use tokio_native_tls::{
|
use tokio_native_tls::{
|
||||||
native_tls::TlsConnector as NativeTlsConnector, TlsConnector as AsyncNativeTlsConnector,
|
native_tls::TlsConnector as NativeTlsConnector, TlsConnector as AsyncNativeTlsConnector,
|
||||||
TlsStream as AsyncTlsStream,
|
TlsStream as AsyncTlsStream,
|
||||||
};
|
};
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
use crate::connect::{Connection, Host};
|
use crate::connect::{Connection, Host};
|
||||||
|
|
||||||
pub mod reexports {
|
pub mod reexports {
|
||||||
//! Re-exports from `native-tls` and `tokio-native-tls` that are useful for connectors.
|
//! Re-exports from `native-tls` and `tokio-native-tls` that are useful for connectors.
|
||||||
|
|
||||||
pub use tokio_native_tls::native_tls::TlsConnector;
|
pub use tokio_native_tls::{native_tls::TlsConnector, TlsStream as AsyncTlsStream};
|
||||||
|
|
||||||
pub use tokio_native_tls::TlsStream as AsyncTlsStream;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Connector service and factory using `native-tls`.
|
/// Connector service and factory using `native-tls`.
|
||||||
|
@ -75,16 +73,16 @@ where
|
||||||
let connector = self.connector.clone();
|
let connector = self.connector.clone();
|
||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
trace!("SSL Handshake start for: {:?}", stream.hostname());
|
trace!("TLS handshake start for: {:?}", stream.hostname());
|
||||||
connector
|
connector
|
||||||
.connect(stream.hostname(), io)
|
.connect(stream.hostname(), io)
|
||||||
.await
|
.await
|
||||||
.map(|res| {
|
.map(|res| {
|
||||||
trace!("SSL Handshake success: {:?}", stream.hostname());
|
trace!("TLS handshake success: {:?}", stream.hostname());
|
||||||
stream.replace_io(res).1
|
stream.replace_io(res).1
|
||||||
})
|
})
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
trace!("SSL Handshake error: {:?}", e);
|
trace!("TLS handshake error: {:?}", e);
|
||||||
io::Error::new(io::ErrorKind::Other, format!("{}", e))
|
io::Error::new(io::ErrorKind::Other, format!("{}", e))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,19 +13,16 @@ use actix_rt::net::ActixStream;
|
||||||
use actix_service::{Service, ServiceFactory};
|
use actix_service::{Service, ServiceFactory};
|
||||||
use actix_utils::future::{ok, Ready};
|
use actix_utils::future::{ok, Ready};
|
||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use log::trace;
|
|
||||||
use openssl::ssl::SslConnector;
|
use openssl::ssl::SslConnector;
|
||||||
use tokio_openssl::SslStream as AsyncSslStream;
|
use tokio_openssl::SslStream as AsyncSslStream;
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
use crate::connect::{Connection, Host};
|
use crate::connect::{Connection, Host};
|
||||||
|
|
||||||
pub mod reexports {
|
pub mod reexports {
|
||||||
//! Re-exports from `openssl` and `tokio-openssl` that are useful for connectors.
|
//! Re-exports from `openssl` and `tokio-openssl` that are useful for connectors.
|
||||||
|
|
||||||
pub use openssl::ssl::{
|
pub use openssl::ssl::{Error, HandshakeError, SslConnector, SslConnectorBuilder, SslMethod};
|
||||||
Error, HandshakeError, SslConnector, SslConnectorBuilder, SslMethod,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use tokio_openssl::SslStream as AsyncSslStream;
|
pub use tokio_openssl::SslStream as AsyncSslStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +95,8 @@ where
|
||||||
actix_service::always_ready!();
|
actix_service::always_ready!();
|
||||||
|
|
||||||
fn call(&self, stream: Connection<R, IO>) -> Self::Future {
|
fn call(&self, stream: Connection<R, IO>) -> Self::Future {
|
||||||
trace!("SSL Handshake start for: {:?}", stream.hostname());
|
trace!("TLS handshake start for: {:?}", stream.hostname());
|
||||||
|
|
||||||
let (io, stream) = stream.replace_io(());
|
let (io, stream) = stream.replace_io(());
|
||||||
let host = stream.hostname();
|
let host = stream.hostname();
|
||||||
|
|
||||||
|
@ -138,12 +136,15 @@ where
|
||||||
match ready!(Pin::new(this.io.as_mut().unwrap()).poll_connect(cx)) {
|
match ready!(Pin::new(this.io.as_mut().unwrap()).poll_connect(cx)) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let stream = this.stream.take().unwrap();
|
let stream = this.stream.take().unwrap();
|
||||||
trace!("SSL Handshake success: {:?}", stream.hostname());
|
trace!("TLS handshake success: {:?}", stream.hostname());
|
||||||
Poll::Ready(Ok(stream.replace_io(this.io.take().unwrap()).1))
|
Poll::Ready(Ok(stream.replace_io(this.io.take().unwrap()).1))
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(err) => {
|
||||||
trace!("SSL Handshake error: {:?}", e);
|
trace!("TLS handshake error: {:?}", err);
|
||||||
Poll::Ready(Err(io::Error::new(io::ErrorKind::Other, format!("{}", e))))
|
Poll::Ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("{}", err),
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use actix_rt::task::{spawn_blocking, JoinHandle};
|
||||||
use actix_service::{Service, ServiceFactory};
|
use actix_service::{Service, ServiceFactory};
|
||||||
use actix_utils::future::{ok, Ready};
|
use actix_utils::future::{ok, Ready};
|
||||||
use futures_core::{future::LocalBoxFuture, ready};
|
use futures_core::{future::LocalBoxFuture, ready};
|
||||||
use log::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
use super::{ConnectError, ConnectInfo, Host, Resolve};
|
use super::{ConnectError, ConnectInfo, Host, Resolve};
|
||||||
|
|
||||||
|
@ -164,8 +164,8 @@ impl<R: Host> Future for ResolverFut<R> {
|
||||||
Self::LookUp(fut, req) => {
|
Self::LookUp(fut, req) => {
|
||||||
let res = match ready!(Pin::new(fut).poll(cx)) {
|
let res = match ready!(Pin::new(fut).poll(cx)) {
|
||||||
Ok(Ok(res)) => Ok(res),
|
Ok(Ok(res)) => Ok(res),
|
||||||
Ok(Err(e)) => Err(ConnectError::Resolver(Box::new(e))),
|
Ok(Err(err)) => Err(ConnectError::Resolver(Box::new(err))),
|
||||||
Err(e) => Err(ConnectError::Io(e.into())),
|
Err(err) => Err(ConnectError::Io(err.into())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let req = req.take().unwrap();
|
let req = req.take().unwrap();
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
//! See [`TlsConnector`] for main connector service factory docs.
|
//! See [`TlsConnector`] for main connector service factory docs.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryFrom,
|
|
||||||
future::Future,
|
future::Future,
|
||||||
io,
|
io,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
|
@ -15,29 +14,50 @@ use actix_rt::net::ActixStream;
|
||||||
use actix_service::{Service, ServiceFactory};
|
use actix_service::{Service, ServiceFactory};
|
||||||
use actix_utils::future::{ok, Ready};
|
use actix_utils::future::{ok, Ready};
|
||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use log::trace;
|
use tokio_rustls::{
|
||||||
use tokio_rustls::rustls::{client::ServerName, OwnedTrustAnchor, RootCertStore};
|
client::TlsStream as AsyncTlsStream,
|
||||||
use tokio_rustls::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
rustls::{client::ServerName, ClientConfig, RootCertStore},
|
||||||
use tokio_rustls::{Connect as RustlsConnect, TlsConnector as RustlsTlsConnector};
|
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
|
||||||
use webpki_roots::TLS_SERVER_ROOTS;
|
};
|
||||||
|
use tokio_rustls_023 as tokio_rustls;
|
||||||
|
|
||||||
use crate::connect::{Connection, Host};
|
use crate::connect::{Connection, Host};
|
||||||
|
|
||||||
pub mod reexports {
|
pub mod reexports {
|
||||||
//! Re-exports from `rustls` and `webpki_roots` that are useful for connectors.
|
//! Re-exports from the `rustls` v0.20 ecosystem that are useful for connectors.
|
||||||
|
|
||||||
pub use tokio_rustls::rustls::ClientConfig;
|
pub use tokio_rustls_023::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
||||||
|
#[cfg(feature = "rustls-0_20-webpki-roots")]
|
||||||
|
pub use webpki_roots_022::TLS_SERVER_ROOTS;
|
||||||
|
}
|
||||||
|
|
||||||
pub use tokio_rustls::client::TlsStream as AsyncTlsStream;
|
/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store.
|
||||||
|
///
|
||||||
|
/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors.
|
||||||
|
///
|
||||||
|
/// [`rustls_native_certs::load_native_certs()`]: rustls_native_certs_06::load_native_certs()
|
||||||
|
#[cfg(feature = "rustls-0_20-native-roots")]
|
||||||
|
pub fn native_roots_cert_store() -> io::Result<RootCertStore> {
|
||||||
|
let mut root_certs = RootCertStore::empty();
|
||||||
|
|
||||||
pub use webpki_roots::TLS_SERVER_ROOTS;
|
for cert in rustls_native_certs_06::load_native_certs()? {
|
||||||
|
root_certs
|
||||||
|
.add(&tokio_rustls_023::rustls::Certificate(cert.0))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(root_certs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
|
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
|
||||||
|
#[cfg(feature = "rustls-0_20-webpki-roots")]
|
||||||
pub fn webpki_roots_cert_store() -> RootCertStore {
|
pub fn webpki_roots_cert_store() -> RootCertStore {
|
||||||
|
use tokio_rustls_023::rustls;
|
||||||
|
|
||||||
let mut root_certs = RootCertStore::empty();
|
let mut root_certs = RootCertStore::empty();
|
||||||
for cert in TLS_SERVER_ROOTS.0 {
|
|
||||||
let cert = OwnedTrustAnchor::from_subject_spki_name_constraints(
|
for cert in webpki_roots_022::TLS_SERVER_ROOTS.0 {
|
||||||
|
let cert = rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||||
cert.subject,
|
cert.subject,
|
||||||
cert.spki,
|
cert.spki,
|
||||||
cert.name_constraints,
|
cert.name_constraints,
|
||||||
|
@ -45,6 +65,7 @@ pub fn webpki_roots_cert_store() -> RootCertStore {
|
||||||
let certs = vec![cert].into_iter();
|
let certs = vec![cert].into_iter();
|
||||||
root_certs.add_server_trust_anchors(certs);
|
root_certs.add_server_trust_anchors(certs);
|
||||||
}
|
}
|
||||||
|
|
||||||
root_certs
|
root_certs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,12 +124,13 @@ where
|
||||||
actix_service::always_ready!();
|
actix_service::always_ready!();
|
||||||
|
|
||||||
fn call(&self, connection: Connection<R, IO>) -> Self::Future {
|
fn call(&self, connection: Connection<R, IO>) -> Self::Future {
|
||||||
trace!("SSL Handshake start for: {:?}", connection.hostname());
|
tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
|
||||||
let (stream, connection) = connection.replace_io(());
|
let (stream, connection) = connection.replace_io(());
|
||||||
|
|
||||||
match ServerName::try_from(connection.hostname()) {
|
match ServerName::try_from(connection.hostname()) {
|
||||||
Ok(host) => ConnectFut::Future {
|
Ok(host) => ConnectFut::Future {
|
||||||
connect: RustlsTlsConnector::from(self.connector.clone()).connect(host, stream),
|
connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
|
||||||
|
.connect(host, stream),
|
||||||
connection: Some(connection),
|
connection: Some(connection),
|
||||||
},
|
},
|
||||||
Err(_) => ConnectFut::InvalidDns,
|
Err(_) => ConnectFut::InvalidDns,
|
||||||
|
@ -118,6 +140,7 @@ where
|
||||||
|
|
||||||
/// Connect future for Rustls service.
|
/// Connect future for Rustls service.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
pub enum ConnectFut<R, IO> {
|
pub enum ConnectFut<R, IO> {
|
||||||
/// See issue <https://github.com/briansmith/webpki/issues/54>
|
/// See issue <https://github.com/briansmith/webpki/issues/54>
|
||||||
InvalidDns,
|
InvalidDns,
|
||||||
|
@ -132,17 +155,23 @@ where
|
||||||
R: Host,
|
R: Host,
|
||||||
IO: ActixStream,
|
IO: ActixStream,
|
||||||
{
|
{
|
||||||
type Output = Result<Connection<R, AsyncTlsStream<IO>>, io::Error>;
|
type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
match self.get_mut() {
|
match self.get_mut() {
|
||||||
Self::InvalidDns => Poll::Ready(Err(
|
Self::InvalidDns => Poll::Ready(Err(io::Error::new(
|
||||||
io::Error::new(io::ErrorKind::Other, "rustls currently only handles hostname-based connections. See https://github.com/briansmith/webpki/issues/54")
|
io::ErrorKind::Other,
|
||||||
)),
|
"Rustls v0.20 can only handle hostname-based connections. Enable the `rustls-0_21` \
|
||||||
Self::Future { connect, connection } => {
|
feature and use the Rustls v0.21 utilities to gain this feature.",
|
||||||
|
))),
|
||||||
|
|
||||||
|
Self::Future {
|
||||||
|
connect,
|
||||||
|
connection,
|
||||||
|
} => {
|
||||||
let stream = ready!(Pin::new(connect).poll(cx))?;
|
let stream = ready!(Pin::new(connect).poll(cx))?;
|
||||||
let connection = connection.take().unwrap();
|
let connection = connection.take().unwrap();
|
||||||
trace!("SSL Handshake success: {:?}", connection.hostname());
|
tracing::trace!("TLS handshake success: {:?}", connection.hostname());
|
||||||
Poll::Ready(Ok(connection.replace_io(stream).1))
|
Poll::Ready(Ok(connection.replace_io(stream).1))
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,177 @@
|
||||||
|
//! Rustls based connector service.
|
||||||
|
//!
|
||||||
|
//! See [`TlsConnector`] for main connector service factory docs.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
future::Future,
|
||||||
|
io,
|
||||||
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_rt::net::ActixStream;
|
||||||
|
use actix_service::{Service, ServiceFactory};
|
||||||
|
use actix_utils::future::{ok, Ready};
|
||||||
|
use futures_core::ready;
|
||||||
|
use tokio_rustls::{
|
||||||
|
client::TlsStream as AsyncTlsStream,
|
||||||
|
rustls::{client::ServerName, ClientConfig, RootCertStore},
|
||||||
|
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
|
||||||
|
};
|
||||||
|
use tokio_rustls_024 as tokio_rustls;
|
||||||
|
|
||||||
|
use crate::connect::{Connection, Host};
|
||||||
|
|
||||||
|
pub mod reexports {
|
||||||
|
//! Re-exports from the `rustls` v0.21 ecosystem that are useful for connectors.
|
||||||
|
|
||||||
|
pub use tokio_rustls_024::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
||||||
|
#[cfg(feature = "rustls-0_21-webpki-roots")]
|
||||||
|
pub use webpki_roots_025::TLS_SERVER_ROOTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store.
|
||||||
|
///
|
||||||
|
/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors.
|
||||||
|
///
|
||||||
|
/// [`rustls_native_certs::load_native_certs()`]: rustls_native_certs_06::load_native_certs()
|
||||||
|
#[cfg(feature = "rustls-0_21-native-roots")]
|
||||||
|
pub fn native_roots_cert_store() -> io::Result<RootCertStore> {
|
||||||
|
let mut root_certs = RootCertStore::empty();
|
||||||
|
|
||||||
|
for cert in rustls_native_certs_06::load_native_certs()? {
|
||||||
|
root_certs
|
||||||
|
.add(&tokio_rustls_024::rustls::Certificate(cert.0))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(root_certs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
|
||||||
|
#[cfg(feature = "rustls-0_21-webpki-roots")]
|
||||||
|
pub fn webpki_roots_cert_store() -> RootCertStore {
|
||||||
|
use tokio_rustls_024::rustls;
|
||||||
|
|
||||||
|
let mut root_certs = RootCertStore::empty();
|
||||||
|
|
||||||
|
for cert in webpki_roots_025::TLS_SERVER_ROOTS {
|
||||||
|
let cert = rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||||
|
cert.subject,
|
||||||
|
cert.spki,
|
||||||
|
cert.name_constraints,
|
||||||
|
);
|
||||||
|
let certs = vec![cert].into_iter();
|
||||||
|
root_certs.add_trust_anchors(certs);
|
||||||
|
}
|
||||||
|
|
||||||
|
root_certs
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connector service factory using `rustls`.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TlsConnector {
|
||||||
|
connector: Arc<ClientConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TlsConnector {
|
||||||
|
/// Constructs new connector service factory from a `rustls` client configuration.
|
||||||
|
pub fn new(connector: Arc<ClientConfig>) -> Self {
|
||||||
|
TlsConnector { connector }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs new connector service from a `rustls` client configuration.
|
||||||
|
pub fn service(connector: Arc<ClientConfig>) -> TlsConnectorService {
|
||||||
|
TlsConnectorService { connector }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
|
||||||
|
where
|
||||||
|
R: Host,
|
||||||
|
IO: ActixStream + 'static,
|
||||||
|
{
|
||||||
|
type Response = Connection<R, AsyncTlsStream<IO>>;
|
||||||
|
type Error = io::Error;
|
||||||
|
type Config = ();
|
||||||
|
type Service = TlsConnectorService;
|
||||||
|
type InitError = ();
|
||||||
|
type Future = Ready<Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
|
fn new_service(&self, _: ()) -> Self::Future {
|
||||||
|
ok(TlsConnectorService {
|
||||||
|
connector: self.connector.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connector service using `rustls`.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TlsConnectorService {
|
||||||
|
connector: Arc<ClientConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
|
||||||
|
where
|
||||||
|
R: Host,
|
||||||
|
IO: ActixStream,
|
||||||
|
{
|
||||||
|
type Response = Connection<R, AsyncTlsStream<IO>>;
|
||||||
|
type Error = io::Error;
|
||||||
|
type Future = ConnectFut<R, IO>;
|
||||||
|
|
||||||
|
actix_service::always_ready!();
|
||||||
|
|
||||||
|
fn call(&self, connection: Connection<R, IO>) -> Self::Future {
|
||||||
|
tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
|
||||||
|
let (stream, connection) = connection.replace_io(());
|
||||||
|
|
||||||
|
match ServerName::try_from(connection.hostname()) {
|
||||||
|
Ok(host) => ConnectFut::Future {
|
||||||
|
connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
|
||||||
|
.connect(host, stream),
|
||||||
|
connection: Some(connection),
|
||||||
|
},
|
||||||
|
Err(_) => ConnectFut::InvalidServerName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connect future for Rustls service.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
pub enum ConnectFut<R, IO> {
|
||||||
|
InvalidServerName,
|
||||||
|
Future {
|
||||||
|
connect: RustlsConnect<IO>,
|
||||||
|
connection: Option<Connection<R, ()>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, IO> Future for ConnectFut<R, IO>
|
||||||
|
where
|
||||||
|
R: Host,
|
||||||
|
IO: ActixStream,
|
||||||
|
{
|
||||||
|
type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
match self.get_mut() {
|
||||||
|
Self::InvalidServerName => Poll::Ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"connection parameters specified invalid server name",
|
||||||
|
))),
|
||||||
|
|
||||||
|
Self::Future {
|
||||||
|
connect,
|
||||||
|
connection,
|
||||||
|
} => {
|
||||||
|
let stream = ready!(Pin::new(connect).poll(cx))?;
|
||||||
|
let connection = connection.take().unwrap();
|
||||||
|
tracing::trace!("TLS handshake success: {:?}", connection.hostname());
|
||||||
|
Poll::Ready(Ok(connection.replace_io(stream).1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
//! Rustls based connector service.
|
||||||
|
//!
|
||||||
|
//! See [`TlsConnector`] for main connector service factory docs.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
future::Future,
|
||||||
|
io,
|
||||||
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_rt::net::ActixStream;
|
||||||
|
use actix_service::{Service, ServiceFactory};
|
||||||
|
use actix_utils::future::{ok, Ready};
|
||||||
|
use futures_core::ready;
|
||||||
|
use rustls_pki_types_1::ServerName;
|
||||||
|
use tokio_rustls::{
|
||||||
|
client::TlsStream as AsyncTlsStream, rustls::ClientConfig, Connect as RustlsConnect,
|
||||||
|
TlsConnector as RustlsTlsConnector,
|
||||||
|
};
|
||||||
|
use tokio_rustls_025 as tokio_rustls;
|
||||||
|
|
||||||
|
use crate::connect::{Connection, Host};
|
||||||
|
|
||||||
|
pub mod reexports {
|
||||||
|
//! Re-exports from the `rustls` v0.22 ecosystem that are useful for connectors.
|
||||||
|
|
||||||
|
pub use tokio_rustls_025::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
||||||
|
#[cfg(feature = "rustls-0_22-webpki-roots")]
|
||||||
|
pub use webpki_roots_026::TLS_SERVER_ROOTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store.
|
||||||
|
///
|
||||||
|
/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors.
|
||||||
|
///
|
||||||
|
/// [`rustls_native_certs::load_native_certs()`]: rustls_native_certs_07::load_native_certs()
|
||||||
|
#[cfg(feature = "rustls-0_22-native-roots")]
|
||||||
|
pub fn native_roots_cert_store() -> io::Result<tokio_rustls::rustls::RootCertStore> {
|
||||||
|
let mut root_certs = tokio_rustls::rustls::RootCertStore::empty();
|
||||||
|
|
||||||
|
for cert in rustls_native_certs_07::load_native_certs()? {
|
||||||
|
root_certs.add(cert).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(root_certs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
|
||||||
|
#[cfg(feature = "rustls-0_22-webpki-roots")]
|
||||||
|
pub fn webpki_roots_cert_store() -> tokio_rustls::rustls::RootCertStore {
|
||||||
|
let mut root_certs = tokio_rustls::rustls::RootCertStore::empty();
|
||||||
|
root_certs.extend(webpki_roots_026::TLS_SERVER_ROOTS.to_owned());
|
||||||
|
root_certs
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connector service factory using `rustls`.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TlsConnector {
|
||||||
|
connector: Arc<ClientConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TlsConnector {
|
||||||
|
/// Constructs new connector service factory from a `rustls` client configuration.
|
||||||
|
pub fn new(connector: Arc<ClientConfig>) -> Self {
|
||||||
|
TlsConnector { connector }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs new connector service from a `rustls` client configuration.
|
||||||
|
pub fn service(connector: Arc<ClientConfig>) -> TlsConnectorService {
|
||||||
|
TlsConnectorService { connector }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
|
||||||
|
where
|
||||||
|
R: Host,
|
||||||
|
IO: ActixStream + 'static,
|
||||||
|
{
|
||||||
|
type Response = Connection<R, AsyncTlsStream<IO>>;
|
||||||
|
type Error = io::Error;
|
||||||
|
type Config = ();
|
||||||
|
type Service = TlsConnectorService;
|
||||||
|
type InitError = ();
|
||||||
|
type Future = Ready<Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
|
fn new_service(&self, _: ()) -> Self::Future {
|
||||||
|
ok(TlsConnectorService {
|
||||||
|
connector: self.connector.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connector service using `rustls`.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TlsConnectorService {
|
||||||
|
connector: Arc<ClientConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
|
||||||
|
where
|
||||||
|
R: Host,
|
||||||
|
IO: ActixStream,
|
||||||
|
{
|
||||||
|
type Response = Connection<R, AsyncTlsStream<IO>>;
|
||||||
|
type Error = io::Error;
|
||||||
|
type Future = ConnectFut<R, IO>;
|
||||||
|
|
||||||
|
actix_service::always_ready!();
|
||||||
|
|
||||||
|
fn call(&self, connection: Connection<R, IO>) -> Self::Future {
|
||||||
|
tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
|
||||||
|
let (stream, conn) = connection.replace_io(());
|
||||||
|
|
||||||
|
match ServerName::try_from(conn.hostname()) {
|
||||||
|
Ok(host) => ConnectFut::Future {
|
||||||
|
connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
|
||||||
|
.connect(host.to_owned(), stream),
|
||||||
|
connection: Some(conn),
|
||||||
|
},
|
||||||
|
Err(_) => ConnectFut::InvalidServerName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connect future for Rustls service.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
pub enum ConnectFut<R, IO> {
|
||||||
|
InvalidServerName,
|
||||||
|
Future {
|
||||||
|
connect: RustlsConnect<IO>,
|
||||||
|
connection: Option<Connection<R, ()>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, IO> Future for ConnectFut<R, IO>
|
||||||
|
where
|
||||||
|
R: Host,
|
||||||
|
IO: ActixStream,
|
||||||
|
{
|
||||||
|
type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
match self.get_mut() {
|
||||||
|
Self::InvalidServerName => Poll::Ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"connection parameters specified invalid server name",
|
||||||
|
))),
|
||||||
|
|
||||||
|
Self::Future {
|
||||||
|
connect,
|
||||||
|
connection,
|
||||||
|
} => {
|
||||||
|
let stream = ready!(Pin::new(connect).poll(cx))?;
|
||||||
|
let connection = connection.take().unwrap();
|
||||||
|
tracing::trace!("TLS handshake success: {:?}", connection.hostname());
|
||||||
|
Poll::Ready(Ok(connection.replace_io(stream).1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,163 @@
|
||||||
|
//! Rustls based connector service.
|
||||||
|
//!
|
||||||
|
//! See [`TlsConnector`] for main connector service factory docs.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
future::Future,
|
||||||
|
io,
|
||||||
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
|
task::{Context, Poll},
|
||||||
|
};
|
||||||
|
|
||||||
|
use actix_rt::net::ActixStream;
|
||||||
|
use actix_service::{Service, ServiceFactory};
|
||||||
|
use actix_utils::future::{ok, Ready};
|
||||||
|
use futures_core::ready;
|
||||||
|
use rustls_pki_types_1::ServerName;
|
||||||
|
use tokio_rustls::{
|
||||||
|
client::TlsStream as AsyncTlsStream, rustls::ClientConfig, Connect as RustlsConnect,
|
||||||
|
TlsConnector as RustlsTlsConnector,
|
||||||
|
};
|
||||||
|
use tokio_rustls_026 as tokio_rustls;
|
||||||
|
|
||||||
|
use crate::connect::{Connection, Host};
|
||||||
|
|
||||||
|
pub mod reexports {
|
||||||
|
//! Re-exports from the `rustls` v0.23 ecosystem that are useful for connectors.
|
||||||
|
|
||||||
|
pub use tokio_rustls_026::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
||||||
|
#[cfg(feature = "rustls-0_23-webpki-roots")]
|
||||||
|
pub use webpki_roots_026::TLS_SERVER_ROOTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store.
|
||||||
|
///
|
||||||
|
/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors.
|
||||||
|
///
|
||||||
|
/// [`rustls_native_certs::load_native_certs()`]: rustls_native_certs_07::load_native_certs()
|
||||||
|
#[cfg(feature = "rustls-0_23-native-roots")]
|
||||||
|
pub fn native_roots_cert_store() -> io::Result<tokio_rustls::rustls::RootCertStore> {
|
||||||
|
let mut root_certs = tokio_rustls::rustls::RootCertStore::empty();
|
||||||
|
|
||||||
|
for cert in rustls_native_certs_07::load_native_certs()? {
|
||||||
|
root_certs.add(cert).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(root_certs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
|
||||||
|
#[cfg(feature = "rustls-0_23-webpki-roots")]
|
||||||
|
pub fn webpki_roots_cert_store() -> tokio_rustls::rustls::RootCertStore {
|
||||||
|
let mut root_certs = tokio_rustls::rustls::RootCertStore::empty();
|
||||||
|
root_certs.extend(webpki_roots_026::TLS_SERVER_ROOTS.to_owned());
|
||||||
|
root_certs
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connector service factory using `rustls`.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TlsConnector {
|
||||||
|
connector: Arc<ClientConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TlsConnector {
|
||||||
|
/// Constructs new connector service factory from a `rustls` client configuration.
|
||||||
|
pub fn new(connector: Arc<ClientConfig>) -> Self {
|
||||||
|
TlsConnector { connector }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Constructs new connector service from a `rustls` client configuration.
|
||||||
|
pub fn service(connector: Arc<ClientConfig>) -> TlsConnectorService {
|
||||||
|
TlsConnectorService { connector }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
|
||||||
|
where
|
||||||
|
R: Host,
|
||||||
|
IO: ActixStream + 'static,
|
||||||
|
{
|
||||||
|
type Response = Connection<R, AsyncTlsStream<IO>>;
|
||||||
|
type Error = io::Error;
|
||||||
|
type Config = ();
|
||||||
|
type Service = TlsConnectorService;
|
||||||
|
type InitError = ();
|
||||||
|
type Future = Ready<Result<Self::Service, Self::InitError>>;
|
||||||
|
|
||||||
|
fn new_service(&self, _: ()) -> Self::Future {
|
||||||
|
ok(TlsConnectorService {
|
||||||
|
connector: self.connector.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connector service using `rustls`.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TlsConnectorService {
|
||||||
|
connector: Arc<ClientConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
|
||||||
|
where
|
||||||
|
R: Host,
|
||||||
|
IO: ActixStream,
|
||||||
|
{
|
||||||
|
type Response = Connection<R, AsyncTlsStream<IO>>;
|
||||||
|
type Error = io::Error;
|
||||||
|
type Future = ConnectFut<R, IO>;
|
||||||
|
|
||||||
|
actix_service::always_ready!();
|
||||||
|
|
||||||
|
fn call(&self, connection: Connection<R, IO>) -> Self::Future {
|
||||||
|
tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
|
||||||
|
let (stream, conn) = connection.replace_io(());
|
||||||
|
|
||||||
|
match ServerName::try_from(conn.hostname()) {
|
||||||
|
Ok(host) => ConnectFut::Future {
|
||||||
|
connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
|
||||||
|
.connect(host.to_owned(), stream),
|
||||||
|
connection: Some(conn),
|
||||||
|
},
|
||||||
|
Err(_) => ConnectFut::InvalidServerName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connect future for Rustls service.
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[allow(clippy::large_enum_variant)]
|
||||||
|
pub enum ConnectFut<R, IO> {
|
||||||
|
InvalidServerName,
|
||||||
|
Future {
|
||||||
|
connect: RustlsConnect<IO>,
|
||||||
|
connection: Option<Connection<R, ()>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R, IO> Future for ConnectFut<R, IO>
|
||||||
|
where
|
||||||
|
R: Host,
|
||||||
|
IO: ActixStream,
|
||||||
|
{
|
||||||
|
type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
match self.get_mut() {
|
||||||
|
Self::InvalidServerName => Poll::Ready(Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"connection parameters specified invalid server name",
|
||||||
|
))),
|
||||||
|
|
||||||
|
Self::Future {
|
||||||
|
connect,
|
||||||
|
connection,
|
||||||
|
} => {
|
||||||
|
let stream = ready!(Pin::new(connect).poll(cx))?;
|
||||||
|
let connection = connection.take().unwrap();
|
||||||
|
tracing::trace!("TLS handshake success: {:?}", connection.hostname());
|
||||||
|
Poll::Ready(Ok(connection.replace_io(stream).1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,8 +15,8 @@ use actix_rt::net::{TcpSocket, TcpStream};
|
||||||
use actix_service::{Service, ServiceFactory};
|
use actix_service::{Service, ServiceFactory};
|
||||||
use actix_utils::future::{ok, Ready};
|
use actix_utils::future::{ok, Ready};
|
||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use log::{error, trace};
|
|
||||||
use tokio_util::sync::ReusableBoxFuture;
|
use tokio_util::sync::ReusableBoxFuture;
|
||||||
|
use tracing::{error, trace};
|
||||||
|
|
||||||
use super::{connect_addrs::ConnectAddrs, error::ConnectError, ConnectInfo, Connection, Host};
|
use super::{connect_addrs::ConnectAddrs, error::ConnectError, ConnectInfo, Connection, Host};
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ pub enum TcpConnectorFut<R> {
|
||||||
port: u16,
|
port: u16,
|
||||||
local_addr: Option<IpAddr>,
|
local_addr: Option<IpAddr>,
|
||||||
addrs: Option<VecDeque<SocketAddr>>,
|
addrs: Option<VecDeque<SocketAddr>>,
|
||||||
stream: ReusableBoxFuture<Result<TcpStream, io::Error>>,
|
stream: ReusableBoxFuture<'static, Result<TcpStream, io::Error>>,
|
||||||
},
|
},
|
||||||
|
|
||||||
Error(Option<ConnectError>),
|
Error(Option<ConnectError>),
|
||||||
|
@ -114,8 +114,8 @@ impl<R: Host> TcpConnectorFut<R> {
|
||||||
stream: ReusableBoxFuture::new(connect(addr, local_addr)),
|
stream: ReusableBoxFuture::new(connect(addr, local_addr)),
|
||||||
},
|
},
|
||||||
|
|
||||||
// when resolver returns multiple socket addr for request they would be popped from
|
// When resolver returns multiple socket addr for request they would be popped from
|
||||||
// front end of queue and returns with the first successful tcp connection.
|
// front end of queue and returns with the first successful TCP connection.
|
||||||
ConnectAddrs::Multi(mut addrs) => {
|
ConnectAddrs::Multi(mut addrs) => {
|
||||||
let addr = addrs.pop_front().unwrap();
|
let addr = addrs.pop_front().unwrap();
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,19 @@
|
||||||
use http::Uri;
|
|
||||||
|
|
||||||
use super::Host;
|
use super::Host;
|
||||||
|
|
||||||
impl Host for Uri {
|
impl Host for http_0_2::Uri {
|
||||||
|
fn hostname(&self) -> &str {
|
||||||
|
self.host().unwrap_or("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn port(&self) -> Option<u16> {
|
||||||
|
match self.port_u16() {
|
||||||
|
Some(port) => Some(port),
|
||||||
|
None => scheme_to_port(self.scheme_str()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Host for http_1::Uri {
|
||||||
fn hostname(&self) -> &str {
|
fn hostname(&self) -> &str {
|
||||||
self.host().unwrap_or("")
|
self.host().unwrap_or("")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,15 @@
|
||||||
//! TLS acceptor and connector services for the Actix ecosystem.
|
//! TLS acceptor and connector services for the Actix ecosystem.
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible, missing_docs)]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
// enable unstable doc_cfg feature only on on docs.rs where nightly compiler is used
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
#[allow(unused_extern_crates)]
|
#[allow(unused_extern_crates)]
|
||||||
extern crate tls_openssl as openssl;
|
extern crate tls_openssl as openssl;
|
||||||
|
|
||||||
#[cfg(feature = "accept")]
|
#[cfg(feature = "accept")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "accept")))]
|
|
||||||
pub mod accept;
|
pub mod accept;
|
||||||
|
|
||||||
#[cfg(feature = "connect")]
|
#[cfg(feature = "connect")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "connect")))]
|
|
||||||
pub mod connect;
|
pub mod connect;
|
||||||
|
|
|
@ -3,28 +3,30 @@
|
||||||
#![cfg(all(
|
#![cfg(all(
|
||||||
feature = "accept",
|
feature = "accept",
|
||||||
feature = "connect",
|
feature = "connect",
|
||||||
feature = "rustls",
|
feature = "rustls-0_23",
|
||||||
feature = "openssl"
|
feature = "openssl"
|
||||||
))]
|
))]
|
||||||
|
|
||||||
use std::{convert::TryFrom, io::Write, sync::Arc};
|
use std::{io::Write as _, sync::Arc};
|
||||||
|
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use actix_server::TestServer;
|
use actix_server::TestServer;
|
||||||
use actix_service::ServiceFactoryExt as _;
|
use actix_service::ServiceFactoryExt as _;
|
||||||
use actix_tls::accept::openssl::{Acceptor, TlsStream};
|
use actix_tls::{
|
||||||
|
accept::openssl::{Acceptor, TlsStream},
|
||||||
|
connect::rustls_0_23::reexports::ClientConfig,
|
||||||
|
};
|
||||||
use actix_utils::future::ok;
|
use actix_utils::future::ok;
|
||||||
use tokio_rustls::rustls::{Certificate, ClientConfig, RootCertStore, ServerName};
|
use rustls_pki_types_1::ServerName;
|
||||||
|
use tokio_rustls_026::rustls::RootCertStore;
|
||||||
|
|
||||||
fn new_cert_and_key() -> (String, String) {
|
fn new_cert_and_key() -> (String, String) {
|
||||||
let cert = rcgen::generate_simple_self_signed(vec![
|
let rcgen::CertifiedKey { cert, key_pair } =
|
||||||
"127.0.0.1".to_owned(),
|
rcgen::generate_simple_self_signed(vec!["127.0.0.1".to_owned(), "localhost".to_owned()])
|
||||||
"localhost".to_owned(),
|
.unwrap();
|
||||||
])
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let key = cert.serialize_private_key_pem();
|
let key = key_pair.serialize_pem();
|
||||||
let cert = cert.serialize_pem().unwrap();
|
let cert = cert.pem();
|
||||||
|
|
||||||
(cert, key)
|
(cert, key)
|
||||||
}
|
}
|
||||||
|
@ -47,30 +49,48 @@ fn openssl_acceptor(cert: String, key: String) -> tls_openssl::ssl::SslAcceptor
|
||||||
builder.build()
|
builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
mod danger {
|
mod danger {
|
||||||
use std::time::SystemTime;
|
use rustls_pki_types_1::{CertificateDer, ServerName, UnixTime};
|
||||||
|
use tokio_rustls_026::rustls;
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use tokio_rustls::rustls::{
|
|
||||||
self,
|
|
||||||
client::{ServerCertVerified, ServerCertVerifier},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
/// Disables certificate verification to allow self-signed certs from rcgen.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct NoCertificateVerification;
|
pub struct NoCertificateVerification;
|
||||||
|
|
||||||
impl ServerCertVerifier for NoCertificateVerification {
|
impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification {
|
||||||
fn verify_server_cert(
|
fn verify_server_cert(
|
||||||
&self,
|
&self,
|
||||||
_end_entity: &Certificate,
|
_end_entity: &CertificateDer<'_>,
|
||||||
_intermediates: &[Certificate],
|
_intermediates: &[CertificateDer<'_>],
|
||||||
_server_name: &ServerName,
|
_server_name: &ServerName<'_>,
|
||||||
_scts: &mut dyn Iterator<Item = &[u8]>,
|
_ocsp: &[u8],
|
||||||
_ocsp_response: &[u8],
|
_now: UnixTime,
|
||||||
_now: SystemTime,
|
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
|
||||||
) -> Result<ServerCertVerified, rustls::Error> {
|
Ok(rustls::client::danger::ServerCertVerified::assertion())
|
||||||
Ok(ServerCertVerified::assertion())
|
}
|
||||||
|
|
||||||
|
fn verify_tls12_signature(
|
||||||
|
&self,
|
||||||
|
_message: &[u8],
|
||||||
|
_cert: &rustls_pki_types_1::CertificateDer<'_>,
|
||||||
|
_dss: &rustls::DigitallySignedStruct,
|
||||||
|
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
|
||||||
|
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_tls13_signature(
|
||||||
|
&self,
|
||||||
|
_message: &[u8],
|
||||||
|
_cert: &rustls_pki_types_1::CertificateDer<'_>,
|
||||||
|
_dss: &rustls::DigitallySignedStruct,
|
||||||
|
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
|
||||||
|
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
|
||||||
|
rustls::crypto::aws_lc_rs::default_provider()
|
||||||
|
.signature_verification_algorithms
|
||||||
|
.supported_schemes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +98,6 @@ mod danger {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn rustls_connector(_cert: String, _key: String) -> ClientConfig {
|
fn rustls_connector(_cert: String, _key: String) -> ClientConfig {
|
||||||
let mut config = ClientConfig::builder()
|
let mut config = ClientConfig::builder()
|
||||||
.with_safe_defaults()
|
|
||||||
.with_root_certificates(RootCertStore::empty())
|
.with_root_certificates(RootCertStore::empty())
|
||||||
.with_no_client_auth();
|
.with_no_client_auth();
|
||||||
|
|
||||||
|
@ -92,6 +111,10 @@ fn rustls_connector(_cert: String, _key: String) -> ClientConfig {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn accepts_connections() {
|
async fn accepts_connections() {
|
||||||
|
tokio_rustls_026::rustls::crypto::aws_lc_rs::default_provider()
|
||||||
|
.install_default()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (cert, key) = new_cert_and_key();
|
let (cert, key) = new_cert_and_key();
|
||||||
|
|
||||||
let srv = TestServer::start({
|
let srv = TestServer::start({
|
||||||
|
@ -118,13 +141,13 @@ async fn accepts_connections() {
|
||||||
let config = rustls_connector(cert, key);
|
let config = rustls_connector(cert, key);
|
||||||
let config = Arc::new(config);
|
let config = Arc::new(config);
|
||||||
|
|
||||||
let mut conn = tokio_rustls::rustls::ClientConnection::new(
|
let mut conn = tokio_rustls_026::rustls::ClientConnection::new(
|
||||||
config,
|
config,
|
||||||
ServerName::try_from("localhost").unwrap(),
|
ServerName::try_from("localhost").unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut stream = tokio_rustls::rustls::Stream::new(&mut conn, &mut sock);
|
let mut stream = tokio_rustls_026::rustls::Stream::new(&mut conn, &mut sock);
|
||||||
|
|
||||||
stream.flush().expect("TLS handshake failed");
|
stream.flush().expect("TLS handshake failed");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#![cfg(all(
|
#![cfg(all(
|
||||||
feature = "accept",
|
feature = "accept",
|
||||||
feature = "connect",
|
feature = "connect",
|
||||||
feature = "rustls",
|
feature = "rustls-0_23",
|
||||||
feature = "openssl"
|
feature = "openssl"
|
||||||
))]
|
))]
|
||||||
|
|
||||||
|
@ -14,39 +14,40 @@ use std::io::{BufReader, Write};
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use actix_server::TestServer;
|
use actix_server::TestServer;
|
||||||
use actix_service::ServiceFactoryExt as _;
|
use actix_service::ServiceFactoryExt as _;
|
||||||
use actix_tls::accept::rustls::{Acceptor, TlsStream};
|
use actix_tls::{
|
||||||
use actix_tls::connect::openssl::reexports::SslConnector;
|
accept::rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},
|
||||||
|
connect::openssl::reexports::SslConnector,
|
||||||
|
};
|
||||||
use actix_utils::future::ok;
|
use actix_utils::future::ok;
|
||||||
use rustls_pemfile::{certs, pkcs8_private_keys};
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||||
|
use rustls_pki_types_1::PrivateKeyDer;
|
||||||
use tls_openssl::ssl::SslVerifyMode;
|
use tls_openssl::ssl::SslVerifyMode;
|
||||||
use tokio_rustls::rustls::{self, Certificate, PrivateKey, ServerConfig};
|
|
||||||
|
|
||||||
fn new_cert_and_key() -> (String, String) {
|
fn new_cert_and_key() -> (String, String) {
|
||||||
let cert = rcgen::generate_simple_self_signed(vec![
|
let rcgen::CertifiedKey { cert, key_pair } =
|
||||||
"127.0.0.1".to_owned(),
|
rcgen::generate_simple_self_signed(vec!["127.0.0.1".to_owned(), "localhost".to_owned()])
|
||||||
"localhost".to_owned(),
|
.unwrap();
|
||||||
])
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let key = cert.serialize_private_key_pem();
|
let key = key_pair.serialize_pem();
|
||||||
let cert = cert.serialize_pem().unwrap();
|
let cert = cert.pem();
|
||||||
|
|
||||||
(cert, key)
|
(cert, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rustls_server_config(cert: String, key: String) -> rustls::ServerConfig {
|
fn rustls_server_config(cert: String, key: String) -> ServerConfig {
|
||||||
// Load TLS key and cert files
|
// Load TLS key and cert files
|
||||||
|
|
||||||
let cert = &mut BufReader::new(cert.as_bytes());
|
let cert = &mut BufReader::new(cert.as_bytes());
|
||||||
let key = &mut BufReader::new(key.as_bytes());
|
let key = &mut BufReader::new(key.as_bytes());
|
||||||
|
|
||||||
let cert_chain = certs(cert).unwrap().into_iter().map(Certificate).collect();
|
let cert_chain = certs(cert).collect::<Result<Vec<_>, _>>().unwrap();
|
||||||
let mut keys = pkcs8_private_keys(key).unwrap();
|
let mut keys = pkcs8_private_keys(key)
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut config = ServerConfig::builder()
|
let mut config = ServerConfig::builder()
|
||||||
.with_safe_defaults()
|
|
||||||
.with_no_client_auth()
|
.with_no_client_auth()
|
||||||
.with_single_cert(cert_chain, PrivateKey(keys.remove(0)))
|
.with_single_cert(cert_chain, PrivateKeyDer::Pkcs8(keys.remove(0)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
config.alpn_protocols = vec![b"http/1.1".to_vec()];
|
config.alpn_protocols = vec![b"http/1.1".to_vec()];
|
||||||
|
@ -72,6 +73,10 @@ fn openssl_connector(cert: String, key: String) -> SslConnector {
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn accepts_connections() {
|
async fn accepts_connections() {
|
||||||
|
tokio_rustls_026::rustls::crypto::aws_lc_rs::default_provider()
|
||||||
|
.install_default()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (cert, key) = new_cert_and_key();
|
let (cert, key) = new_cert_and_key();
|
||||||
|
|
||||||
let srv = TestServer::start({
|
let srv = TestServer::start({
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![allow(missing_docs)]
|
||||||
#![cfg(feature = "connect")]
|
#![cfg(feature = "connect")]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -9,10 +10,9 @@ use actix_codec::{BytesCodec, Framed};
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use actix_server::TestServer;
|
use actix_server::TestServer;
|
||||||
use actix_service::{fn_service, Service, ServiceFactory};
|
use actix_service::{fn_service, Service, ServiceFactory};
|
||||||
use bytes::Bytes;
|
|
||||||
use futures_util::sink::SinkExt;
|
|
||||||
|
|
||||||
use actix_tls::connect::{ConnectError, ConnectInfo, Connection, Connector, Host};
|
use actix_tls::connect::{ConnectError, ConnectInfo, Connection, Connector, Host};
|
||||||
|
use bytes::Bytes;
|
||||||
|
use futures_util::sink::SinkExt as _;
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
|
@ -31,7 +31,7 @@ async fn test_string() {
|
||||||
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rustls")]
|
#[cfg(feature = "rustls-0_23")]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_rustls_string() {
|
async fn test_rustls_string() {
|
||||||
let srv = TestServer::start(|| {
|
let srv = TestServer::start(|| {
|
||||||
|
@ -99,8 +99,6 @@ async fn service_factory() {
|
||||||
#[cfg(all(feature = "openssl", feature = "uri"))]
|
#[cfg(all(feature = "openssl", feature = "uri"))]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_openssl_uri() {
|
async fn test_openssl_uri() {
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
let srv = TestServer::start(|| {
|
let srv = TestServer::start(|| {
|
||||||
fn_service(|io: TcpStream| async {
|
fn_service(|io: TcpStream| async {
|
||||||
let mut framed = Framed::new(io, BytesCodec);
|
let mut framed = Framed::new(io, BytesCodec);
|
||||||
|
@ -110,16 +108,14 @@ async fn test_openssl_uri() {
|
||||||
});
|
});
|
||||||
|
|
||||||
let connector = Connector::default().service();
|
let connector = Connector::default().service();
|
||||||
let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
|
let addr = http_0_2::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
|
||||||
let con = connector.call(addr.into()).await.unwrap();
|
let con = connector.call(addr.into()).await.unwrap();
|
||||||
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "rustls", feature = "uri"))]
|
#[cfg(all(feature = "rustls-0_23", feature = "uri"))]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_rustls_uri() {
|
async fn test_rustls_uri_http1() {
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
let srv = TestServer::start(|| {
|
let srv = TestServer::start(|| {
|
||||||
fn_service(|io: TcpStream| async {
|
fn_service(|io: TcpStream| async {
|
||||||
let mut framed = Framed::new(io, BytesCodec);
|
let mut framed = Framed::new(io, BytesCodec);
|
||||||
|
@ -129,7 +125,24 @@ async fn test_rustls_uri() {
|
||||||
});
|
});
|
||||||
|
|
||||||
let conn = Connector::default().service();
|
let conn = Connector::default().service();
|
||||||
let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
|
let addr = http_1::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
|
||||||
|
let con = conn.call(addr.into()).await.unwrap();
|
||||||
|
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "rustls-0_23", feature = "uri"))]
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_rustls_uri() {
|
||||||
|
let srv = TestServer::start(|| {
|
||||||
|
fn_service(|io: TcpStream| async {
|
||||||
|
let mut framed = Framed::new(io, BytesCodec);
|
||||||
|
framed.send(Bytes::from_static(b"test")).await?;
|
||||||
|
Ok::<_, io::Error>(())
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let conn = Connector::default().service();
|
||||||
|
let addr = http_1::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
|
||||||
let con = conn.call(addr.into()).await.unwrap();
|
let con = conn.call(addr.into()).await.unwrap();
|
||||||
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||||
}
|
}
|
||||||
|
@ -144,6 +157,9 @@ async fn test_local_addr() {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// if you've arrived here because of a failing test on macOS run this in your terminal:
|
||||||
|
// sudo ifconfig lo0 alias 127.0.0.3
|
||||||
|
|
||||||
let conn = Connector::default().service();
|
let conn = Connector::default().service();
|
||||||
let local = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3));
|
let local = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 3));
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![allow(missing_docs)]
|
||||||
#![cfg(feature = "connect")]
|
#![cfg(feature = "connect")]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -8,11 +9,10 @@ use std::{
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use actix_server::TestServer;
|
use actix_server::TestServer;
|
||||||
use actix_service::{fn_service, Service, ServiceFactory};
|
use actix_service::{fn_service, Service, ServiceFactory};
|
||||||
use futures_core::future::LocalBoxFuture;
|
|
||||||
|
|
||||||
use actix_tls::connect::{
|
use actix_tls::connect::{
|
||||||
ConnectError, ConnectInfo, Connection, Connector, Host, Resolve, Resolver,
|
ConnectError, ConnectInfo, Connection, Connector, Host, Resolve, Resolver,
|
||||||
};
|
};
|
||||||
|
use futures_core::future::LocalBoxFuture;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn custom_resolver() {
|
async fn custom_resolver() {
|
||||||
|
@ -52,8 +52,7 @@ async fn custom_resolver_connect() {
|
||||||
|
|
||||||
use trust_dns_resolver::TokioAsyncResolver;
|
use trust_dns_resolver::TokioAsyncResolver;
|
||||||
|
|
||||||
let srv =
|
let srv = TestServer::start(|| fn_service(|_io: TcpStream| async { Ok::<_, io::Error>(()) }));
|
||||||
TestServer::start(|| fn_service(|_io: TcpStream| async { Ok::<_, io::Error>(()) }));
|
|
||||||
|
|
||||||
struct MyResolver {
|
struct MyResolver {
|
||||||
trust_dns: TokioAsyncResolver,
|
trust_dns: TokioAsyncResolver,
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue