mirror of
https://github.com/fafhrd91/actix-net
synced 2025-09-03 00:46:38 +02:00
Compare commits
251 Commits
tls-v3.1.1
...
proxy-prot
Author | SHA1 | Date | |
---|---|---|---|
|
e67d4521e5 | ||
|
567e9ad2ca | ||
|
d78e238415 | ||
|
26a28606d3 | ||
|
2daf68dc49 | ||
|
4d4367f5f2 | ||
|
b8ff2a47a6 | ||
|
160fdc5efc | ||
|
c856fcdcd5 | ||
|
4ee45b24b5 | ||
|
43f9da3c4e | ||
|
d24e5b4e95 | ||
|
0d45bf9b43 | ||
|
e85ae87b98 | ||
|
dc19b196da | ||
|
e22bca03d5 | ||
|
19ab9cc97a | ||
|
1ea0f773b6 | ||
|
ee3d54f574 | ||
|
75e56d56d8 | ||
|
222527a407 | ||
|
cfddd3ea2e | ||
|
d869455251 | ||
|
3e925d8f80 | ||
|
0c8337f1c9 | ||
|
20ae763da8 | ||
|
6c6f7f19f5 | ||
|
95d569d8d7 | ||
|
347fc17035 | ||
|
4cb04042f5 | ||
|
e6ddf0ccff | ||
|
21de88385e | ||
|
a91bdcb80c | ||
|
64a2d1c1eb | ||
|
637bd709ae | ||
|
bbac996677 | ||
|
c9cea86445 | ||
|
05065186ff | ||
|
3d54a00d5b | ||
|
120e0206eb | ||
|
1ad8a98f1b | ||
|
da1c0024f0 | ||
|
0be1dd8156 | ||
|
9c646e4998 | ||
|
758c37b80c | ||
|
1a1af68bf0 | ||
|
3ebed4701d | ||
|
15c13c3e32 | ||
|
3356038c80 | ||
|
7834d76d4f | ||
|
064da0e3ff | ||
|
270360e095 | ||
|
d9203797be | ||
|
67616902cc | ||
|
809071b857 | ||
|
8e7de17221 | ||
|
2972a5ac08 | ||
|
69041c4f6c | ||
|
d61b33928d | ||
|
d95eea8fbe | ||
|
53036ce9c4 | ||
|
f8bea79547 | ||
|
7030b26a7c | ||
|
d7f60b88d3 | ||
|
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 |
@@ -2,24 +2,14 @@
|
||||
lint = "clippy --workspace --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)
|
||||
ci-check-min = "hack --workspace check --no-default-features"
|
||||
ci-check-lib = "hack --workspace --feature-powerset --exclude-features=io-uring check"
|
||||
ci-check-lib-linux = "hack --workspace --feature-powerset check"
|
||||
ci-check-lib = "hack --workspace --feature-powerset --depth=2 --exclude-features=io-uring check"
|
||||
ci-check-lib-linux = "hack --workspace --feature-powerset --depth=2 check"
|
||||
|
||||
# check everything
|
||||
ci-check = "hack --workspace --feature-powerset --exclude-features=io-uring check --tests --examples"
|
||||
ci-check-linux = "hack --workspace --feature-powerset 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 --depth=2 check --tests --examples"
|
||||
|
||||
# tests avoiding io-uring feature
|
||||
ci-test = "hack --feature-powerset --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture"
|
||||
ci-test-rustls-020 = "hack --feature-powerset --exclude-features=io-uring,rustls-0_21 test --lib --tests --no-fail-fast -- --nocapture"
|
||||
ci-test-rustls-021 = "hack --feature-powerset --exclude-features=io-uring,rustls-0_20 test --lib --tests --no-fail-fast -- --nocapture"
|
||||
|
||||
# tests avoiding io-uring feature on Windows
|
||||
ci-test-win = "hack --feature-powerset --depth=2 --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture"
|
||||
|
||||
# test with io-uring feature
|
||||
ci-test-linux = "hack --feature-powerset --exclude-features=rustls-0_20 test --lib --tests --no-fail-fast -- --nocapture"
|
||||
ci-test = "hack --feature-powerset --depth=2 --exclude-features=io-uring test --lib --tests --no-fail-fast -- --nocapture"
|
||||
|
4
.clippy.toml
Normal file
4
.clippy.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
disallowed-names = [
|
||||
"..", # defaults
|
||||
"e", # prefer `err`
|
||||
]
|
23
.cspell.yml
Normal file
23
.cspell.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
version: "0.2"
|
||||
words:
|
||||
- actix
|
||||
- addrs
|
||||
- ALPN
|
||||
- arrayvec
|
||||
- bitflags
|
||||
- clippy
|
||||
- deque
|
||||
- itertools
|
||||
- itoa
|
||||
- mptcp
|
||||
- MSRV
|
||||
- nonblocking
|
||||
- oneshot
|
||||
- pemfile
|
||||
- rcgen
|
||||
- Rustls
|
||||
- rustup
|
||||
- smallvec
|
||||
- spki
|
||||
- uring
|
||||
- webpki
|
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,10 +1,12 @@
|
||||
## PR Type
|
||||
|
||||
<!-- What kind of change does this PR make? -->
|
||||
<!-- Bug Fix / Feature / Refactor / Code Style / Other -->
|
||||
|
||||
INSERT_PR_TYPE
|
||||
|
||||
|
||||
## PR Checklist
|
||||
|
||||
Check your PR fulfills the following:
|
||||
|
||||
<!-- For draft PRs check the boxes as you complete them. -->
|
||||
@@ -14,11 +16,10 @@ Check your PR fulfills the following:
|
||||
- [ ] A changelog entry has been made for the appropriate packages.
|
||||
- [ ] Format code with the latest stable rustfmt
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
<!-- Describe the current and new behavior. -->
|
||||
<!-- Emphasize any breaking changes. -->
|
||||
|
||||
|
||||
<!-- If this PR fixes or closes an issue, reference it here. -->
|
||||
<!-- Closes #000 -->
|
||||
|
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
- package-ecosystem: cargo
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
versioning-strategy: lockfile-only
|
114
.github/workflows/ci-post-merge.yml
vendored
114
.github/workflows/ci-post-merge.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: CI (master only)
|
||||
name: CI (post-merge)
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -22,7 +22,6 @@ jobs:
|
||||
- { 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 }
|
||||
- { name: Windows (32-bit), os: windows-latest, triple: i686-pc-windows-msvc }
|
||||
version:
|
||||
- nightly
|
||||
|
||||
@@ -36,28 +35,38 @@ jobs:
|
||||
if: matrix.target.os == 'macos-latest'
|
||||
run: sudo ifconfig lo0 alias 127.0.0.3
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- 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'
|
||||
run: choco install openssl -y --forcex64 --no-progress
|
||||
- name: Set OpenSSL dir in env
|
||||
if: matrix.target.os == 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL-Win64' | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
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
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
toolchain: ${{ matrix.version }}
|
||||
|
||||
- uses: taiki-e/install-action@v2
|
||||
with: { tool: cargo-hack }
|
||||
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
with:
|
||||
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
|
||||
|
||||
- name: check lib
|
||||
if: >
|
||||
@@ -82,78 +91,39 @@ jobs:
|
||||
run: cargo ci-check-linux
|
||||
|
||||
- name: tests
|
||||
if: >
|
||||
matrix.target.os != 'ubuntu-latest'
|
||||
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
|
||||
run: cargo ci-test
|
||||
- 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
|
||||
"
|
||||
run: just test
|
||||
|
||||
- name: Clear the cargo caches
|
||||
run: |
|
||||
cargo install cargo-cache --version 0.6.2 --no-default-features --features ci-autoclean
|
||||
cargo-cache
|
||||
# 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
|
||||
# "
|
||||
|
||||
coverage:
|
||||
name: coverage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust (nightly)
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with: { toolchain: nightly }
|
||||
|
||||
- name: Install cargo-tarpaulin
|
||||
uses: taiki-e/install-action@v1
|
||||
with: { tool: cargo-tarpaulin }
|
||||
|
||||
- name: Generate coverage file
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: cargo tarpaulin --out Xml --verbose
|
||||
- name: Upload to Codecov
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: codecov/codecov-action@v3
|
||||
with: { files: cobertura.xml }
|
||||
- name: CI cache clean
|
||||
run: cargo-ci-cache-clean
|
||||
|
||||
minimal-versions:
|
||||
name: minimal versions
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install Rust (nightly)
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with: { toolchain: nightly }
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
toolchain: nightly
|
||||
|
||||
- name: Install cargo-hack & cargo-minimal-versions
|
||||
uses: taiki-e/install-action@v1
|
||||
with: { tool: 'cargo-hack,cargo-minimal-versions' }
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
with:
|
||||
tool: cargo-hack,cargo-minimal-versions
|
||||
|
||||
- name: Check With Minimal Versions
|
||||
run: cargo minimal-versions check
|
||||
|
||||
nextest:
|
||||
name: nextest
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
|
||||
- name: Install cargo-nextest
|
||||
uses: taiki-e/install-action@v1
|
||||
with: { tool: cargo-nextest }
|
||||
|
||||
- name: Test with cargo-nextest
|
||||
run: cargo nextest run
|
||||
|
100
.github/workflows/ci.yml
vendored
100
.github/workflows/ci.yml
vendored
@@ -2,6 +2,7 @@ name: CI
|
||||
|
||||
on:
|
||||
pull_request: {}
|
||||
merge_group: { types: [checks_requested] }
|
||||
push: { branches: [master] }
|
||||
|
||||
permissions:
|
||||
@@ -12,7 +13,14 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
read_msrv:
|
||||
name: Read MSRV
|
||||
uses: actions-rust-lang/msrv/.github/workflows/msrv.yml@v0.1.0
|
||||
|
||||
build_and_test:
|
||||
needs:
|
||||
- read_msrv
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -22,12 +30,11 @@ jobs:
|
||||
- { 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 }
|
||||
- { name: Windows (32-bit), os: windows-latest, triple: i686-pc-windows-msvc }
|
||||
version:
|
||||
- 1.65.0 # MSRV
|
||||
- stable
|
||||
- { name: msrv, version: "${{ needs.read_msrv.outputs.msrv }}" }
|
||||
- { name: stable, version: stable }
|
||||
|
||||
name: ${{ matrix.target.name }} / ${{ matrix.version }}
|
||||
name: ${{ matrix.target.name }} / ${{ matrix.version.name }}
|
||||
runs-on: ${{ matrix.target.os }}
|
||||
|
||||
env: {}
|
||||
@@ -37,36 +44,45 @@ jobs:
|
||||
if: matrix.target.os == 'macos-latest'
|
||||
run: sudo ifconfig lo0 alias 127.0.0.3
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- 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'
|
||||
run: choco install openssl -y --forcex64 --no-progress
|
||||
- name: Set OpenSSL dir in env
|
||||
if: matrix.target.os == 'windows-latest'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL-Win64' | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
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
|
||||
- name: Install Rust (${{ matrix.version.name }})
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
toolchain: ${{ matrix.version }}
|
||||
toolchain: ${{ matrix.version.version }}
|
||||
|
||||
- uses: taiki-e/install-action@v1
|
||||
with: { tool: cargo-hack }
|
||||
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
with:
|
||||
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
|
||||
|
||||
- name: Generate Cargo.lock
|
||||
run: cargo generate-lockfile
|
||||
|
||||
- name: workaround MSRV issues
|
||||
if: matrix.version != 'stable'
|
||||
run: |
|
||||
cargo update -p=time --precise=0.3.16 # time is only a dev dep so shouldn't affect msrv
|
||||
if: matrix.version.name == 'msrv'
|
||||
run: just downgrade-for-msrv
|
||||
|
||||
- name: check lib
|
||||
if: >
|
||||
@@ -77,7 +93,7 @@ jobs:
|
||||
if: matrix.target.os == 'ubuntu-latest'
|
||||
run: cargo ci-check-lib-linux
|
||||
- name: check lib
|
||||
if: matrix.target.triple == 'x86_64-pc-windows-gnu'
|
||||
if: matrix.target.triple != 'x86_64-pc-windows-gnu'
|
||||
run: cargo ci-check-min
|
||||
|
||||
- name: check full
|
||||
@@ -91,41 +107,27 @@ jobs:
|
||||
run: cargo ci-check-linux
|
||||
|
||||
- name: tests
|
||||
if: matrix.target.os == 'macos-latest'
|
||||
run: cargo ci-test
|
||||
- name: tests
|
||||
if: >
|
||||
matrix.target.os == 'windows-latest'
|
||||
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
|
||||
run: cargo ci-test-win
|
||||
- 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
|
||||
"
|
||||
run: just test
|
||||
|
||||
- name: Clear the cargo caches
|
||||
run: |
|
||||
cargo install cargo-cache --version 0.6.2 --no-default-features --features ci-autoclean
|
||||
cargo-cache
|
||||
- name: CI cache clean
|
||||
run: cargo-ci-cache-clean
|
||||
|
||||
rustdoc:
|
||||
name: rustdoc
|
||||
docs:
|
||||
name: Documentation
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install Rust (nightly)
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with: { toolchain: nightly }
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
toolchain: nightly
|
||||
|
||||
- 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"
|
||||
- name: Install just
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
with:
|
||||
tool: just
|
||||
|
||||
- name: doc tests
|
||||
run: just test-docs
|
||||
|
43
.github/workflows/clippy-fmt.yml
vendored
43
.github/workflows/clippy-fmt.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Lint
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
fmt:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
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@v3
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with: { components: clippy }
|
||||
|
||||
- uses: giraffate/clippy-action@v1
|
||||
with:
|
||||
reporter: 'github-pr-check'
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
clippy_flags: --workspace --all-features --tests --examples --bins -- -Dclippy::todo
|
39
.github/workflows/coverage.yml
vendored
Normal file
39
.github/workflows/coverage.yml
vendored
Normal file
@@ -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@v5
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
components: llvm-tools-preview
|
||||
|
||||
- name: Install cargo-llvm-cov
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
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.5.0
|
||||
with:
|
||||
files: codecov.json
|
||||
fail_ci_if_error: true
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
69
.github/workflows/lint.yml
vendored
Normal file
69
.github/workflows/lint.yml
vendored
Normal file
@@ -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@v5
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1.14.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@v5
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1.14.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@v5
|
||||
|
||||
- name: Install Rust (${{ vars.RUST_VERSION_EXTERNAL_TYPES }})
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
toolchain: ${{ vars.RUST_VERSION_EXTERNAL_TYPES }}
|
||||
|
||||
- name: Install just
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
with:
|
||||
tool: just
|
||||
|
||||
- name: Install cargo-check-external-types
|
||||
uses: taiki-e/cache-cargo-install-action@v2.3.0
|
||||
with:
|
||||
tool: cargo-check-external-types
|
||||
|
||||
- name: check external types
|
||||
run: just check-external-types-all +${{ vars.RUST_VERSION_EXTERNAL_TYPES }}
|
35
.github/workflows/upload-doc.yml
vendored
35
.github/workflows/upload-doc.yml
vendored
@@ -1,35 +0,0 @@
|
||||
name: Upload documentation
|
||||
|
||||
on:
|
||||
push: { branches: [master] }
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with: { toolchain: nightly }
|
||||
|
||||
- name: Build Docs
|
||||
run: cargo doc --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@v4
|
||||
with:
|
||||
folder: target/doc
|
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
Cargo.lock
|
||||
target/
|
||||
guide/build/
|
||||
/gh-pages
|
||||
@@ -13,4 +12,8 @@ guide/build/
|
||||
# These are backup files generated by rustfmt
|
||||
**/*.rs.bk
|
||||
|
||||
# IDEs
|
||||
.idea
|
||||
|
||||
# direnv
|
||||
/.direnv
|
||||
|
29
.taplo.toml
Normal file
29
.taplo.toml
Normal file
@@ -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
|
32
.vscode/settings.json
vendored
Normal file
32
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"rust-analyzer.cargo.features": [
|
||||
"accept",
|
||||
"actix-macros",
|
||||
"connect",
|
||||
"default",
|
||||
"macros",
|
||||
"native-tls",
|
||||
"openssl",
|
||||
"rustls",
|
||||
"rustls-021",
|
||||
"rustls-0_20",
|
||||
"rustls-0_20-native-roots",
|
||||
"rustls-0_20-webpki-roots",
|
||||
"rustls-0_21",
|
||||
"rustls-0_21-native-roots",
|
||||
"rustls-0_21-webpki-roots",
|
||||
"rustls-0_22",
|
||||
"rustls-0_22-native-roots",
|
||||
"rustls-0_22-webpki-roots",
|
||||
"rustls-0_23",
|
||||
"rustls-0_23-native-roots",
|
||||
"rustls-0_23-webpki-roots",
|
||||
"rustls-webpki-0101",
|
||||
"serde",
|
||||
"tokio-rustls-023",
|
||||
"tokio-rustls-024",
|
||||
"uri",
|
||||
"webpki-roots-022",
|
||||
"webpki-roots-025",
|
||||
]
|
||||
}
|
3162
Cargo.lock
generated
Normal file
3162
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
Cargo.toml
16
Cargo.toml
@@ -1,7 +1,9 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"actix-codec",
|
||||
"actix-macros",
|
||||
"actix-proxy-protocol",
|
||||
"actix-rt",
|
||||
"actix-server",
|
||||
"actix-service",
|
||||
@@ -12,16 +14,16 @@ members = [
|
||||
"local-channel",
|
||||
"local-waker",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.65"
|
||||
rust-version = "1.75"
|
||||
|
||||
[patch.crates-io]
|
||||
actix-codec = { path = "actix-codec" }
|
||||
actix-macros = { path = "actix-macros" }
|
||||
actix-proxy-protocol = { path = "actix-proxy-protocol" }
|
||||
actix-rt = { path = "actix-rt" }
|
||||
actix-server = { path = "actix-server" }
|
||||
actix-service = { path = "actix-service" }
|
||||
@@ -36,3 +38,13 @@ local-waker = { path = "local-waker" }
|
||||
lto = true
|
||||
opt-level = 3
|
||||
codegen-units = 1
|
||||
|
||||
[workspace.lints.rust]
|
||||
rust-2018-idioms = "deny"
|
||||
nonstandard-style = "deny"
|
||||
future-incompatible = "deny"
|
||||
missing-docs = { level = "warn", priority = -1 }
|
||||
|
||||
[workspace.lints.clippy]
|
||||
uninlined-format-args = "warn"
|
||||
disallowed-names = "warn"
|
||||
|
@@ -3,7 +3,7 @@
|
||||
> A collection of lower-level libraries for composable network services.
|
||||
|
||||
[](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://deps.rs/repo/github/actix/actix-net)
|
||||
|
||||
|
@@ -1,56 +1,51 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2023-xx-xx
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 0.5.2
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.65.
|
||||
|
||||
## 0.5.1 - 2022-03-15
|
||||
## 0.5.1
|
||||
|
||||
- Logs emitted now use the `tracing` crate with `log` compatibility. [#451]
|
||||
- Logs emitted now use the `tracing` crate with `log` compatibility.
|
||||
- Minimum supported Rust version (MSRV) is now 1.49.
|
||||
|
||||
[#451]: https://github.com/actix/actix-net/pull/451
|
||||
## 0.5.0
|
||||
|
||||
## 0.5.0 - 2022-02-15
|
||||
- Updated `tokio-util` dependency to `0.7.0`.
|
||||
|
||||
- Updated `tokio-util` dependency to `0.7.0`. [#446]
|
||||
|
||||
[#446]: https://github.com/actix/actix-net/pull/446
|
||||
|
||||
## 0.4.2 - 2021-12-31
|
||||
## 0.4.2
|
||||
|
||||
- No significant changes since `0.4.1`.
|
||||
|
||||
## 0.4.1 - 2021-11-05
|
||||
## 0.4.1
|
||||
|
||||
- Added `LinesCodec.` [#338]
|
||||
- `Framed::poll_ready` flushes when the buffer is full. [#409]
|
||||
- Added `LinesCodec`.
|
||||
- `Framed::poll_ready` flushes when the buffer is full.
|
||||
|
||||
[#338]: https://github.com/actix/actix-net/pull/338
|
||||
[#409]: https://github.com/actix/actix-net/pull/409
|
||||
|
||||
## 0.4.0 - 2021-04-20
|
||||
## 0.4.0
|
||||
|
||||
- No significant changes since v0.4.0-beta.1.
|
||||
|
||||
## 0.4.0-beta.1 - 2020-12-28
|
||||
## 0.4.0-beta.1
|
||||
|
||||
- Replace `pin-project` with `pin-project-lite`. [#237]
|
||||
- Upgrade `tokio` dependency to `1`. [#237]
|
||||
- Upgrade `tokio-util` dependency to `0.6`. [#237]
|
||||
- Upgrade `bytes` dependency to `1`. [#237]
|
||||
- Replace `pin-project` with `pin-project-lite`.
|
||||
- Upgrade `tokio` dependency to `1`.
|
||||
- Upgrade `tokio-util` dependency to `0.6`.
|
||||
- Upgrade `bytes` dependency to `1`.
|
||||
|
||||
[#237]: https://github.com/actix/actix-net/pull/237
|
||||
|
||||
## 0.3.0 - 2020-08-23
|
||||
## 0.3.0
|
||||
|
||||
- No changes from beta 2.
|
||||
|
||||
## 0.3.0-beta.2 - 2020-08-19
|
||||
## 0.3.0-beta.2
|
||||
|
||||
- Remove unused type parameter from `Framed::replace_codec`.
|
||||
|
||||
## 0.3.0-beta.1 - 2020-08-19
|
||||
## 0.3.0-beta.1
|
||||
|
||||
- Use `.advance()` instead of `.split_to()`.
|
||||
- Upgrade `tokio-util` to `0.3`.
|
||||
@@ -60,7 +55,7 @@
|
||||
- Add method on `Framed` to get a pinned reference to the underlying I/O.
|
||||
- Add method on `Framed` check emptiness of read buffer.
|
||||
|
||||
## 0.2.0 - 2019-12-10
|
||||
## 0.2.0
|
||||
|
||||
- Use specific futures dependencies.
|
||||
|
||||
@@ -77,14 +72,14 @@
|
||||
|
||||
- Migrated to `std::future`.
|
||||
|
||||
## 0.1.2 - 2019-03-27
|
||||
## 0.1.2
|
||||
|
||||
- Added `Framed::map_io()` method.
|
||||
|
||||
## 0.1.1 - 2019-03-06
|
||||
## 0.1.1
|
||||
|
||||
- Added `FramedParts::with_read_buffer()` method.
|
||||
|
||||
## 0.1.0 - 2018-12-09
|
||||
## 0.1.0
|
||||
|
||||
- Move codec to separate crate.
|
||||
|
@@ -1,10 +1,7 @@
|
||||
[package]
|
||||
name = "actix-codec"
|
||||
version = "0.5.1"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Rob Ede <robjtede@icloud.com>",
|
||||
]
|
||||
version = "0.5.2"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||
description = "Codec utilities for working with framed protocols"
|
||||
keywords = ["network", "framework", "async", "futures"]
|
||||
repository = "https://github.com/actix/actix-net"
|
||||
@@ -13,6 +10,9 @@ license = "MIT OR Apache-2.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[package.metadata.cargo_check_external_types]
|
||||
allowed_external_types = ["bytes::*", "futures_core::*", "futures_sink::*", "tokio::*", "tokio_util::*"]
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2"
|
||||
bytes = "1"
|
||||
@@ -20,14 +20,17 @@ futures-core = { version = "0.3.7", default-features = false }
|
||||
futures-sink = { version = "0.3.7", default-features = false }
|
||||
memchr = "2.3"
|
||||
pin-project-lite = "0.2"
|
||||
tokio = "1.23.1"
|
||||
tokio = "1.44.2"
|
||||
tokio-util = { version = "0.7", features = ["codec", "io"] }
|
||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.4", features = ["html_reports"] }
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
tokio-test = "0.4.2"
|
||||
|
||||
[[bench]]
|
||||
name = "lines"
|
||||
harness = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use bytes::BytesMut;
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
|
@@ -6,8 +6,6 @@
|
||||
//! [`Sink`]: futures_sink::Sink
|
||||
//! [`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_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
io::{self, Write},
|
||||
@@ -55,7 +57,7 @@ impl Write for Bilateral {
|
||||
Ok(data.len())
|
||||
}
|
||||
Some(Err(err)) => Err(err),
|
||||
None => panic!("unexpected write; {:?}", src),
|
||||
None => panic!("unexpected write; {src:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 0.2.4
|
||||
|
||||
- Update `syn` dependency to `2`.
|
||||
|
@@ -2,17 +2,22 @@
|
||||
name = "actix-macros"
|
||||
version = "0.2.4"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Ibraheem Ahmed <ibrah1440@gmail.com>",
|
||||
"Rob Ede <robjtede@icloud.com>",
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Ibraheem Ahmed <ibrah1440@gmail.com>",
|
||||
"Rob Ede <robjtede@icloud.com>",
|
||||
]
|
||||
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"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = [
|
||||
"proc_macro2", # specified for minimal versions compat
|
||||
]
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
@@ -20,9 +25,15 @@ proc-macro = true
|
||||
quote = "1"
|
||||
syn = { version = "2", features = ["full"] }
|
||||
|
||||
# minimal versions compat
|
||||
[target.'cfg(any())'.dependencies]
|
||||
proc-macro2 = "1.0.60"
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "2"
|
||||
|
||||
futures-util = { version = "0.3.17", default-features = false }
|
||||
rustversion = "1"
|
||||
rustversion-msrv = "0.100"
|
||||
trybuild = "1"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@@ -8,8 +8,6 @@
|
||||
//! # Tests
|
||||
//! 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_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
|
@@ -1,4 +1,6 @@
|
||||
#[rustversion::stable(1.65)] // MSRV
|
||||
#![allow(missing_docs)]
|
||||
|
||||
#[rustversion_msrv::msrv]
|
||||
#[test]
|
||||
fn compile_macros() {
|
||||
let t = trybuild::TestCases::new();
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#![allow(unused_imports)]
|
||||
|
||||
mod system {
|
||||
pub use actix_rt::System as MySystem;
|
||||
}
|
||||
|
7
actix-proxy-protocol/CHANGES.md
Normal file
7
actix-proxy-protocol/CHANGES.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2022-xx-xx
|
||||
|
||||
## 0.0.1 - 2022-xx-xx
|
||||
|
||||
- delete me
|
40
actix-proxy-protocol/Cargo.toml
Executable file
40
actix-proxy-protocol/Cargo.toml
Executable file
@@ -0,0 +1,40 @@
|
||||
[package]
|
||||
name = "actix-proxy-protocol"
|
||||
version = "0.0.1"
|
||||
authors = ["Rob Ede <robjtede@icloud.com>"]
|
||||
description = "PROXY protocol utilities"
|
||||
keywords = ["proxy", "protocol", "network", "haproxy", "tcp"]
|
||||
categories = ["network-programming", "asynchronous"]
|
||||
homepage = "https://actix.rs"
|
||||
repository = "https://github.com/actix/actix-net"
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
actix-service = "2"
|
||||
actix-utils = "3"
|
||||
|
||||
arrayvec = "0.7"
|
||||
bitflags = "2"
|
||||
crc32fast = "1"
|
||||
futures-core = { version = "0.3.17", default-features = false, features = ["std"] }
|
||||
futures-util = { version = "0.3.17", default-features = false, features = ["std"] }
|
||||
itoa = "1"
|
||||
nom = "8"
|
||||
smallvec = "1"
|
||||
tokio = { version = "1.13.1", features = ["sync", "io-util"] }
|
||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-codec = "0.5"
|
||||
actix-rt = "2.6"
|
||||
actix-server = "2"
|
||||
bytes = "1"
|
||||
const-str = "0.5"
|
||||
futures-util = { version = "0.3.7", default-features = false, features = ["sink", "async-await-macro"] }
|
||||
hex = "0.4"
|
||||
once_cell = "1"
|
||||
pretty_assertions = "1"
|
||||
tokio = { version = "1.13.1", features = ["io-util", "rt-multi-thread", "macros", "fs"] }
|
||||
tracing-subscriber = "0.3"
|
1
actix-proxy-protocol/LICENSE-APACHE
Symbolic link
1
actix-proxy-protocol/LICENSE-APACHE
Symbolic link
@@ -0,0 +1 @@
|
||||
../LICENSE-APACHE
|
1
actix-proxy-protocol/LICENSE-MIT
Symbolic link
1
actix-proxy-protocol/LICENSE-MIT
Symbolic link
@@ -0,0 +1 @@
|
||||
../LICENSE-MIT
|
17
actix-proxy-protocol/README.md
Normal file
17
actix-proxy-protocol/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# actix-proxy-protocol
|
||||
|
||||
> Implementation of the [PROXY protocol].
|
||||
|
||||
[](https://crates.io/crates/actix-proxy-protocol)
|
||||
[](https://docs.rs/actix-proxy-protocol/0.1.0)
|
||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||

|
||||
[](https://deps.rs/crate/actix-proxy-protocol/0.1.0)
|
||||

|
||||
[](https://discord.gg/NWpN5mmg3x)
|
||||
|
||||
## Resources
|
||||
|
||||
- [Examples](./examples)
|
||||
|
||||
[proxy protocol]: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
173
actix-proxy-protocol/examples/proxy-server.rs
Normal file
173
actix-proxy-protocol/examples/proxy-server.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
//! Adds PROXY protocol v1 prelude to connections.
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
use std::{
|
||||
io, mem,
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use actix_proxy_protocol::{tlv, v1, v2, AddressFamily, Command, TransportProtocol};
|
||||
use actix_rt::net::TcpStream;
|
||||
use actix_server::Server;
|
||||
use actix_service::{fn_service, ServiceFactoryExt as _};
|
||||
use arrayvec::ArrayVec;
|
||||
use bytes::BytesMut;
|
||||
use const_str::concat_bytes;
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::io::{
|
||||
copy_bidirectional, AsyncBufReadExt as _, AsyncReadExt as _, AsyncWriteExt as _, BufReader,
|
||||
};
|
||||
|
||||
static UPSTREAM: Lazy<SocketAddr> = Lazy::new(|| SocketAddr::from(([127, 0, 0, 1], 8080)));
|
||||
|
||||
/*
|
||||
NOTES:
|
||||
108 byte buffer on receiver side is enough for any PROXY header
|
||||
after PROXY, receive until CRLF, *then* decode parts
|
||||
TLV = type-length-value
|
||||
|
||||
TO DO:
|
||||
handle UNKNOWN transport
|
||||
v2 UNSPEC mode
|
||||
AF_UNIX socket
|
||||
*/
|
||||
|
||||
fn extend_with_ip_bytes(buf: &mut Vec<u8>, ip: IpAddr) {
|
||||
match ip {
|
||||
IpAddr::V4(ip) => buf.extend_from_slice(&ip.octets()),
|
||||
IpAddr::V6(ip) => buf.extend_from_slice(&ip.octets()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn wrap_with_proxy_protocol_v1(mut stream: TcpStream) -> io::Result<()> {
|
||||
let mut upstream = TcpStream::connect(("127.0.0.1", 8080)).await?;
|
||||
|
||||
tracing::info!(
|
||||
"PROXYv1 {} -> {}",
|
||||
stream.peer_addr().unwrap(),
|
||||
UPSTREAM.to_string(),
|
||||
);
|
||||
|
||||
let proxy_header = v1::Header::new(
|
||||
AddressFamily::Inet,
|
||||
SocketAddr::from(([127, 0, 0, 1], 8081)),
|
||||
*UPSTREAM,
|
||||
);
|
||||
|
||||
proxy_header.write_to_tokio(&mut upstream).await?;
|
||||
|
||||
let (_bytes_read, _bytes_written) = copy_bidirectional(&mut stream, &mut upstream).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wrap_with_proxy_protocol_v2(mut stream: TcpStream) -> io::Result<()> {
|
||||
let mut upstream = TcpStream::connect(("127.0.0.1", 8080)).await?;
|
||||
|
||||
tracing::info!(
|
||||
"PROXYv2 {} -> {}",
|
||||
stream.peer_addr().unwrap(),
|
||||
UPSTREAM.to_string(),
|
||||
);
|
||||
|
||||
let mut proxy_header = v2::Header::new_tcp_ipv4_proxy(([127, 0, 0, 1], 8082), *UPSTREAM);
|
||||
|
||||
proxy_header.add_typed_tlv(tlv::UniqueId::new("4269")); // UNIQUE_ID
|
||||
proxy_header.add_typed_tlv(tlv::Noop::new("NOOP m8")); // NOOP
|
||||
proxy_header.add_typed_tlv(tlv::Authority::new("localhost")); // NOOP
|
||||
proxy_header.add_typed_tlv(tlv::Alpn::new("http/1.1")); // NOOP
|
||||
proxy_header.add_crc23c_checksum();
|
||||
|
||||
proxy_header.write_to_tokio(&mut upstream).await?;
|
||||
|
||||
let (_bytes_read, _bytes_written) = copy_bidirectional(&mut stream, &mut upstream).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn unwrap_proxy_protocol(mut stream: TcpStream) -> io::Result<()> {
|
||||
let mut upstream = TcpStream::connect(("127.0.0.1", 8080)).await?;
|
||||
|
||||
tracing::info!(
|
||||
"PROXY unwrap {} -> {}",
|
||||
stream.peer_addr().unwrap(),
|
||||
UPSTREAM.to_string(),
|
||||
);
|
||||
|
||||
let mut header = [0; 12];
|
||||
stream.peek(&mut header).await?;
|
||||
|
||||
eprintln!("header: {}", String::from_utf8_lossy(&header));
|
||||
|
||||
if &header[..v1::SIGNATURE.len()] == v1::SIGNATURE.as_bytes() {
|
||||
tracing::info!("v1");
|
||||
|
||||
let mut stream = BufReader::new(stream);
|
||||
let mut buf = Vec::with_capacity(v1::MAX_HEADER_SIZE);
|
||||
let _len = stream.read_until(b'\n', &mut buf).await?;
|
||||
|
||||
eprintln!("{}", String::from_utf8_lossy(&buf));
|
||||
|
||||
let (rest, header) = match v1::Header::try_from_bytes(&buf) {
|
||||
Ok((rest, header)) => (rest, header),
|
||||
Err(err) => {
|
||||
match err {
|
||||
nom::Err::Incomplete(needed) => todo!(),
|
||||
nom::Err::Error(err) => {
|
||||
eprintln!(
|
||||
"err {:?}, input: {}",
|
||||
err.code,
|
||||
String::from_utf8_lossy(err.input)
|
||||
)
|
||||
}
|
||||
nom::Err::Failure(_) => todo!(),
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
eprintln!("{:02X?} - {:?}", rest, header);
|
||||
|
||||
let (_bytes_read, _bytes_written) = copy_bidirectional(&mut stream, &mut upstream).await?;
|
||||
} else if header == v2::SIGNATURE {
|
||||
tracing::info!("v2");
|
||||
let (_bytes_read, _bytes_written) = copy_bidirectional(&mut stream, &mut upstream).await?;
|
||||
} else {
|
||||
tracing::warn!("No proxy header; closing");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_server() -> io::Result<Server> {
|
||||
tracing::info!("proxying to 127.0.0.1:8080");
|
||||
|
||||
Ok(Server::build()
|
||||
.bind("proxy-protocol-v1", ("127.0.0.1", 8081), move || {
|
||||
fn_service(wrap_with_proxy_protocol_v1)
|
||||
.map_err(|err| tracing::error!("service error: {err:?}"))
|
||||
})?
|
||||
.bind("proxy-protocol-v2", ("127.0.0.1", 8082), move || {
|
||||
fn_service(wrap_with_proxy_protocol_v2)
|
||||
.map_err(|err| tracing::error!("service error: {err:?}"))
|
||||
})?
|
||||
.bind("proxy-protocol-unwrap", ("127.0.0.1", 8083), move || {
|
||||
fn_service(unwrap_proxy_protocol)
|
||||
.map_err(|err| tracing::error!("service error: {err:?}"))
|
||||
})?
|
||||
.workers(2)
|
||||
.run())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
tracing_subscriber::fmt::fmt().without_time().init();
|
||||
|
||||
start_server()?.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
156
actix-proxy-protocol/src/lib.rs
Normal file
156
actix-proxy-protocol/src/lib.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
//! PROXY protocol.
|
||||
|
||||
#![expect(dead_code)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
pub mod tlv;
|
||||
pub mod v1;
|
||||
pub mod v2;
|
||||
|
||||
/// PROXY Protocol Version.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Version {
|
||||
/// Human-readable header format (Version 1)
|
||||
V1,
|
||||
|
||||
/// Binary header format (Version 2)
|
||||
V2,
|
||||
}
|
||||
|
||||
impl Version {
|
||||
const fn signature(&self) -> &'static [u8] {
|
||||
match self {
|
||||
Version::V1 => v1::SIGNATURE.as_bytes(),
|
||||
Version::V2 => v2::SIGNATURE.as_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
const fn v2_hi(&self) -> u8 {
|
||||
(match self {
|
||||
Version::V1 => panic!("v1 not supported in PROXY v2"),
|
||||
Version::V2 => 0x2,
|
||||
}) << 4
|
||||
}
|
||||
}
|
||||
|
||||
/// Command
|
||||
///
|
||||
/// other values are unassigned and must not be emitted by senders. Receivers
|
||||
/// must drop connections presenting unexpected values here.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Command {
|
||||
/// \x0 : LOCAL : the connection was established on purpose by the proxy
|
||||
/// without being relayed. The connection endpoints are the sender and the
|
||||
/// receiver. Such connections exist when the proxy sends health-checks to the
|
||||
/// server. The receiver must accept this connection as valid and must use the
|
||||
/// real connection endpoints and discard the protocol block including the
|
||||
/// family which is ignored.
|
||||
Local,
|
||||
|
||||
/// \x1 : PROXY : the connection was established on behalf of another node,
|
||||
/// and reflects the original connection endpoints. The receiver must then use
|
||||
/// the information provided in the protocol block to get original the address.
|
||||
Proxy,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
const fn v2_lo(&self) -> u8 {
|
||||
match self {
|
||||
Command::Local => 0x0,
|
||||
Command::Proxy => 0x1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Address Family.
|
||||
///
|
||||
/// maps to the original socket family without necessarily
|
||||
/// matching the values internally used by the system.
|
||||
///
|
||||
/// other values are unspecified and must not be emitted in version 2 of this
|
||||
/// protocol and must be rejected as invalid by receivers.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AddressFamily {
|
||||
/// 0x0 : AF_UNSPEC : the connection is forwarded for an unknown, unspecified
|
||||
/// or unsupported protocol. The sender should use this family when sending
|
||||
/// LOCAL commands or when dealing with unsupported protocol families. The
|
||||
/// receiver is free to accept the connection anyway and use the real endpoint
|
||||
/// addresses or to reject it. The receiver should ignore address information.
|
||||
Unspecified,
|
||||
|
||||
/// 0x1 : AF_INET : the forwarded connection uses the AF_INET address family
|
||||
/// (IPv4). The addresses are exactly 4 bytes each in network byte order,
|
||||
/// followed by transport protocol information (typically ports).
|
||||
Inet,
|
||||
|
||||
/// 0x2 : AF_INET6 : the forwarded connection uses the AF_INET6 address family
|
||||
/// (IPv6). The addresses are exactly 16 bytes each in network byte order,
|
||||
/// followed by transport protocol information (typically ports).
|
||||
Inet6,
|
||||
|
||||
/// 0x3 : AF_UNIX : the forwarded connection uses the AF_UNIX address family
|
||||
/// (UNIX). The addresses are exactly 108 bytes each.
|
||||
Unix,
|
||||
}
|
||||
|
||||
impl AddressFamily {
|
||||
pub(crate) fn v1_str(&self) -> &'static str {
|
||||
match self {
|
||||
AddressFamily::Inet => "TCP4",
|
||||
AddressFamily::Inet6 => "TCP6",
|
||||
af => panic!("{:?} is not supported in PROXY v1", af),
|
||||
}
|
||||
}
|
||||
|
||||
const fn v2_hi(&self) -> u8 {
|
||||
(match self {
|
||||
AddressFamily::Unspecified => 0x0,
|
||||
AddressFamily::Inet => 0x1,
|
||||
AddressFamily::Inet6 => 0x2,
|
||||
AddressFamily::Unix => 0x3,
|
||||
}) << 4
|
||||
}
|
||||
}
|
||||
|
||||
/// Transport Protocol.
|
||||
///
|
||||
/// other values are unspecified and must not be emitted in version 2 of this
|
||||
/// protocol and must be rejected as invalid by receivers.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TransportProtocol {
|
||||
/// 0x0 : UNSPEC : the connection is forwarded for an unknown, unspecified
|
||||
/// or unsupported protocol. The sender should use this family when sending
|
||||
/// LOCAL commands or when dealing with unsupported protocol families. The
|
||||
/// receiver is free to accept the connection anyway and use the real endpoint
|
||||
/// addresses or to reject it. The receiver should ignore address information.
|
||||
Unspecified,
|
||||
|
||||
/// 0x1 : STREAM : the forwarded connection uses a SOCK_STREAM protocol (eg:
|
||||
/// TCP or UNIX_STREAM). When used with AF_INET/AF_INET6 (TCP), the addresses
|
||||
/// are followed by the source and destination ports represented on 2 bytes
|
||||
/// each in network byte order.
|
||||
Stream,
|
||||
|
||||
/// 0x2 : DGRAM : the forwarded connection uses a SOCK_DGRAM protocol (eg:
|
||||
/// UDP or UNIX_DGRAM). When used with AF_INET/AF_INET6 (UDP), the addresses
|
||||
/// are followed by the source and destination ports represented on 2 bytes
|
||||
/// each in network byte order.
|
||||
Datagram,
|
||||
}
|
||||
|
||||
impl TransportProtocol {
|
||||
const fn v2_lo(&self) -> u8 {
|
||||
match self {
|
||||
TransportProtocol::Unspecified => 0x0,
|
||||
TransportProtocol::Stream => 0x1,
|
||||
TransportProtocol::Datagram => 0x2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ProxyProtocolHeader {
|
||||
V1(v1::Header),
|
||||
V2(v2::Header),
|
||||
}
|
292
actix-proxy-protocol/src/tlv.rs
Normal file
292
actix-proxy-protocol/src/tlv.rs
Normal file
@@ -0,0 +1,292 @@
|
||||
use std::{borrow::Cow, convert::TryFrom, str};
|
||||
|
||||
const PP2_TYPE_ALPN: u8 = 0x01; // done
|
||||
const PP2_TYPE_AUTHORITY: u8 = 0x02; // done
|
||||
const PP2_TYPE_CRC32C: u8 = 0x03; // done
|
||||
const PP2_TYPE_NOOP: u8 = 0x04; // done
|
||||
const PP2_TYPE_UNIQUE_ID: u8 = 0x05; // done
|
||||
const PP2_TYPE_SSL: u8 = 0x20;
|
||||
const PP2_SUBTYPE_SSL_VERSION: u8 = 0x21;
|
||||
const PP2_SUBTYPE_SSL_CN: u8 = 0x22;
|
||||
const PP2_SUBTYPE_SSL_CIPHER: u8 = 0x23;
|
||||
const PP2_SUBTYPE_SSL_SIG_ALG: u8 = 0x24;
|
||||
const PP2_SUBTYPE_SSL_KEY_ALG: u8 = 0x25;
|
||||
const PP2_TYPE_NETNS: u8 = 0x30;
|
||||
|
||||
pub trait Tlv: Sized {
|
||||
const TYPE: u8;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self>;
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]>;
|
||||
|
||||
fn try_from_parts(typ: u8, value: &[u8]) -> Option<Self> {
|
||||
if typ != Self::TYPE {
|
||||
return None;
|
||||
}
|
||||
|
||||
Self::try_from_value(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Application-Layer Protocol Negotiation (ALPN). It is a byte sequence defining
|
||||
/// the upper layer protocol in use over the connection. The most common use case
|
||||
/// will be to pass the exact copy of the ALPN extension of the Transport Layer
|
||||
/// Security (TLS) protocol as defined by RFC7301 [9].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Alpn {
|
||||
alpn: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Alpn {
|
||||
///
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `alpn` is empty (i.e., has length of 0).
|
||||
pub fn new(alpn: impl Into<Vec<u8>>) -> Self {
|
||||
let alpn = alpn.into();
|
||||
|
||||
assert!(!alpn.is_empty(), "ALPN TLV value cannot be empty");
|
||||
|
||||
Self { alpn }
|
||||
}
|
||||
}
|
||||
|
||||
impl Tlv for Alpn {
|
||||
const TYPE: u8 = PP2_TYPE_ALPN;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self> {
|
||||
Some(Self {
|
||||
alpn: value.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Borrowed(&self.alpn)
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the host name value passed by the client, as an UTF8-encoded string.
|
||||
/// In case of TLS being used on the client connection, this is the exact copy of
|
||||
/// the "server_name" extension as defined by RFC3546 [10], section 3.1, often
|
||||
/// referred to as "SNI". There are probably other situations where an authority
|
||||
/// can be mentioned on a connection without TLS being involved at all.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Authority {
|
||||
authority: String,
|
||||
}
|
||||
|
||||
impl Authority {
|
||||
/// A UTF-8
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `authority` is an empty string.
|
||||
pub fn new(authority: impl Into<String>) -> Self {
|
||||
let authority = authority.into();
|
||||
|
||||
assert!(!authority.is_empty(), "Authority TLV value cannot be empty");
|
||||
|
||||
Self { authority }
|
||||
}
|
||||
}
|
||||
|
||||
impl Tlv for Authority {
|
||||
const TYPE: u8 = PP2_TYPE_AUTHORITY;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self> {
|
||||
Some(Self {
|
||||
authority: str::from_utf8(value).ok()?.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Borrowed(self.authority.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
/// The value of the type PP2_TYPE_CRC32C is a 32-bit number storing the CRC32c
|
||||
/// checksum of the PROXY protocol header.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct Crc32c {
|
||||
pub(crate) checksum: u32,
|
||||
}
|
||||
|
||||
impl Tlv for Crc32c {
|
||||
const TYPE: u8 = PP2_TYPE_CRC32C;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self> {
|
||||
let checksum_bytes = <[u8; 4]>::try_from(value).ok()?;
|
||||
|
||||
Some(Self {
|
||||
checksum: u32::from_be_bytes(checksum_bytes),
|
||||
})
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Owned(self.checksum.to_be_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
/// The TLV of this type should be ignored when parsed. The value is zero or more
|
||||
/// bytes. Can be used for data padding or alignment. Note that it can be used
|
||||
/// to align only by 3 or more bytes because a TLV can not be smaller than that.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Noop {
|
||||
value: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Noop {
|
||||
///
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `value` is empty (i.e., has length of 0).
|
||||
pub fn new(value: impl Into<Vec<u8>>) -> Self {
|
||||
let value = value.into();
|
||||
|
||||
assert!(!value.is_empty(), "Noop TLV `value` cannot be empty");
|
||||
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
impl Tlv for Noop {
|
||||
const TYPE: u8 = PP2_TYPE_NOOP;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self> {
|
||||
Some(Self {
|
||||
value: value.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Borrowed(&self.value)
|
||||
}
|
||||
}
|
||||
|
||||
/// The value of the type PP2_TYPE_UNIQUE_ID is an opaque byte sequence of up to
|
||||
/// 128 bytes generated by the upstream proxy that uniquely identifies the
|
||||
/// connection.
|
||||
///
|
||||
/// The unique ID can be used to easily correlate connections across multiple
|
||||
/// layers of proxies, without needing to look up IP addresses and port numbers.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UniqueId {
|
||||
value: Vec<u8>,
|
||||
}
|
||||
|
||||
impl UniqueId {
|
||||
///
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `value` is 0 bytes or larger than 128 bytes.
|
||||
pub fn new(id: impl Into<Vec<u8>>) -> Self {
|
||||
let value = id.into();
|
||||
|
||||
assert!(!value.is_empty(), "UniqueId TLV `value` cannot be empty");
|
||||
assert!(
|
||||
value.len() < 128,
|
||||
"UniqueId TLV `value` cannot be larger than 128 bytes"
|
||||
);
|
||||
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
impl Tlv for UniqueId {
|
||||
const TYPE: u8 = PP2_TYPE_UNIQUE_ID;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self> {
|
||||
Some(Self {
|
||||
value: value.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Borrowed(&self.value)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct SslClientFlags: u8 {
|
||||
const PP2_CLIENT_SSL = 0x01;
|
||||
const PP2_CLIENT_CERT_CONN = 0x02;
|
||||
const PP2_CLIENT_CERT_SESS = 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
/// TLS (SSL).
|
||||
///
|
||||
/// Very broken atm.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Ssl {
|
||||
/// The <client> field is made of a bit field indicating which element is present.
|
||||
///
|
||||
/// Note, that each of these elements may lead to extra data being appended to
|
||||
/// this TLV using a second level of TLV encapsulation. It is thus possible to
|
||||
/// find multiple TLV values after this field. The total length of the pp2_tlv_ssl
|
||||
/// TLV will reflect this.
|
||||
client: SslClientFlags,
|
||||
|
||||
/// The <verify> field will be zero if the client presented a certificate
|
||||
/// and it was successfully verified, and non-zero otherwise.
|
||||
verify: bool,
|
||||
|
||||
/// Sub-TLVs.
|
||||
tlvs: Vec<SslTlv>,
|
||||
}
|
||||
|
||||
impl Tlv for Ssl {
|
||||
const TYPE: u8 = PP2_TYPE_SSL;
|
||||
|
||||
fn try_from_value(_value: &[u8]) -> Option<Self> {
|
||||
/// The PP2_CLIENT_SSL flag indicates that the client connected over SSL/TLS. When
|
||||
/// this field is present, the US-ASCII string representation of the TLS version is
|
||||
/// appended at the end of the field in the TLV format using the type
|
||||
/// PP2_SUBTYPE_SSL_VERSION.
|
||||
const PP2_CLIENT_SSL: u8 = 0x01;
|
||||
|
||||
/// PP2_CLIENT_CERT_CONN indicates that the client provided a certificate over the
|
||||
/// current connection.
|
||||
const PP2_CLIENT_CERT_CONN: u8 = 0x02;
|
||||
|
||||
/// PP2_CLIENT_CERT_SESS indicates that the client provided a
|
||||
/// certificate at least once over the TLS session this connection belongs to.
|
||||
const PP2_CLIENT_CERT_SESS: u8 = 0x04;
|
||||
|
||||
// TODO: finish parsing
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Borrowed(&[])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct SslTlv {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// #[test]
|
||||
// #[should_panic]
|
||||
// fn tlv_zero_len() {
|
||||
// Tlv::new(0x00, vec![]);
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn tlv_as_crc32c() {
|
||||
// noop
|
||||
assert_eq!(Crc32c::try_from_parts(0x04, &[0x00]), None);
|
||||
|
||||
assert_eq!(
|
||||
Crc32c::try_from_parts(0x03, &[0x08, 0x70, 0x17, 0x7b]),
|
||||
Some(Crc32c {
|
||||
checksum: 141563771
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
148
actix-proxy-protocol/src/v1.rs
Normal file
148
actix-proxy-protocol/src/v1.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use std::{fmt, io, net::SocketAddr};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use nom::{IResult, Parser as _};
|
||||
use tokio::io::{AsyncWrite, AsyncWriteExt as _};
|
||||
|
||||
use crate::AddressFamily;
|
||||
|
||||
pub const SIGNATURE: &str = "PROXY";
|
||||
pub const MAX_HEADER_SIZE: usize = 107;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Header {
|
||||
/// Address family.
|
||||
af: AddressFamily,
|
||||
|
||||
/// Source address.
|
||||
src: SocketAddr,
|
||||
|
||||
/// Destination address.
|
||||
dst: SocketAddr,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub const fn new(af: AddressFamily, src: SocketAddr, dst: SocketAddr) -> Self {
|
||||
Self { af, src, dst }
|
||||
}
|
||||
|
||||
pub const fn new_inet(src: SocketAddr, dst: SocketAddr) -> Self {
|
||||
Self::new(AddressFamily::Inet, src, dst)
|
||||
}
|
||||
|
||||
pub const fn new_inet6(src: SocketAddr, dst: SocketAddr) -> Self {
|
||||
Self::new(AddressFamily::Inet6, src, dst)
|
||||
}
|
||||
|
||||
pub fn write_to(&self, wrt: &mut impl io::Write) -> io::Result<()> {
|
||||
write!(wrt, "{self}")
|
||||
}
|
||||
|
||||
pub async fn write_to_tokio(&self, wrt: &mut (impl AsyncWrite + Unpin)) -> io::Result<()> {
|
||||
// max length of a V1 header is 107 bytes
|
||||
let mut buf = ArrayVec::<_, MAX_HEADER_SIZE>::new();
|
||||
self.write_to(&mut buf)?;
|
||||
wrt.write_all(&buf).await
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(slice: &[u8]) -> IResult<&[u8], Self> {
|
||||
parsing::parse(slice)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Header {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{proto_sig} {af} {src_ip} {dst_ip} {src_port} {dst_port}\r\n",
|
||||
proto_sig = SIGNATURE,
|
||||
af = self.af.v1_str(),
|
||||
src_ip = self.src.ip(),
|
||||
dst_ip = self.dst.ip(),
|
||||
src_port = itoa::Buffer::new().format(self.src.port()),
|
||||
dst_port = itoa::Buffer::new().format(self.dst.port()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
mod parsing {
|
||||
use std::{
|
||||
net::{Ipv4Addr, SocketAddrV4},
|
||||
str::{self, FromStr},
|
||||
};
|
||||
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::{tag, take_while},
|
||||
character::complete::char,
|
||||
combinator::{map, map_res},
|
||||
IResult,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Parses a number from serialized representation (as bytes).
|
||||
fn parse_number<T: FromStr>(input: &[u8]) -> IResult<&[u8], T> {
|
||||
map_res(take_while(|c: u8| c.is_ascii_digit()), |s: &[u8]| {
|
||||
let s = str::from_utf8(s).map_err(|_| "utf8 error")?;
|
||||
let val = s.parse::<T>().map_err(|_| "u8 parse error")?;
|
||||
Ok::<_, Box<dyn std::error::Error>>(val)
|
||||
})
|
||||
.parse(input)
|
||||
}
|
||||
|
||||
/// Parses an address family.
|
||||
fn parse_address_family(input: &[u8]) -> IResult<&[u8], AddressFamily> {
|
||||
map_res(alt((tag("TCP4"), tag("TCP6"))), |af: &[u8]| match af {
|
||||
b"TCP4" => Ok(AddressFamily::Inet),
|
||||
b"TCP6" => Ok(AddressFamily::Inet6),
|
||||
_ => Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"invalid address family",
|
||||
)),
|
||||
})
|
||||
.parse(input)
|
||||
}
|
||||
|
||||
/// Parses an IPv4 address from serialized representation (as bytes).
|
||||
fn parse_ipv4(input: &[u8]) -> IResult<&[u8], Ipv4Addr> {
|
||||
map(
|
||||
(
|
||||
parse_number::<u8>,
|
||||
char('.'),
|
||||
parse_number::<u8>,
|
||||
char('.'),
|
||||
parse_number::<u8>,
|
||||
char('.'),
|
||||
parse_number::<u8>,
|
||||
),
|
||||
|(a, _, b, _, c, _, d)| Ipv4Addr::new(a, b, c, d),
|
||||
)
|
||||
.parse(input)
|
||||
}
|
||||
|
||||
/// Parses an IPv4 address from ASCII bytes.
|
||||
pub(super) fn parse(input: &[u8]) -> IResult<&[u8], Header> {
|
||||
map(
|
||||
(
|
||||
tag(SIGNATURE),
|
||||
char(' '),
|
||||
parse_address_family,
|
||||
char(' '),
|
||||
parse_ipv4,
|
||||
char(' '),
|
||||
parse_ipv4,
|
||||
char(' '),
|
||||
parse_number::<u16>,
|
||||
char(' '),
|
||||
parse_number::<u16>,
|
||||
),
|
||||
|(_, _, af, _, src_ip, _, dst_ip, _, src_port, _, dst_port)| Header {
|
||||
af,
|
||||
src: SocketAddr::V4(SocketAddrV4::new(src_ip, src_port)),
|
||||
dst: SocketAddr::V4(SocketAddrV4::new(dst_ip, dst_port)),
|
||||
},
|
||||
)
|
||||
.parse(input)
|
||||
}
|
||||
}
|
304
actix-proxy-protocol/src/v2.rs
Normal file
304
actix-proxy-protocol/src/v2.rs
Normal file
@@ -0,0 +1,304 @@
|
||||
use std::{
|
||||
io,
|
||||
net::{IpAddr, SocketAddr},
|
||||
};
|
||||
|
||||
use smallvec::{SmallVec, ToSmallVec as _};
|
||||
use tokio::io::{AsyncWrite, AsyncWriteExt as _};
|
||||
|
||||
use crate::{
|
||||
tlv::{Crc32c, Tlv},
|
||||
AddressFamily, Command, TransportProtocol, Version,
|
||||
};
|
||||
|
||||
pub const SIGNATURE: [u8; 12] = [
|
||||
0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A,
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Header {
|
||||
command: Command,
|
||||
transport_protocol: TransportProtocol,
|
||||
address_family: AddressFamily,
|
||||
src: SocketAddr,
|
||||
dst: SocketAddr,
|
||||
tlvs: SmallVec<[(u8, SmallVec<[u8; 16]>); 4]>,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn new(
|
||||
command: Command,
|
||||
transport_protocol: TransportProtocol,
|
||||
address_family: AddressFamily,
|
||||
src: impl Into<SocketAddr>,
|
||||
dst: impl Into<SocketAddr>,
|
||||
) -> Self {
|
||||
Self {
|
||||
command,
|
||||
transport_protocol,
|
||||
address_family,
|
||||
src: src.into(),
|
||||
dst: dst.into(),
|
||||
tlvs: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_tcp_ipv4_proxy(src: impl Into<SocketAddr>, dst: impl Into<SocketAddr>) -> Self {
|
||||
Self::new(
|
||||
Command::Proxy,
|
||||
TransportProtocol::Stream,
|
||||
AddressFamily::Inet,
|
||||
src,
|
||||
dst,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_tlv(&mut self, typ: u8, value: impl AsRef<[u8]>) {
|
||||
self.tlvs.push((typ, SmallVec::from_slice(value.as_ref())));
|
||||
}
|
||||
|
||||
pub fn add_typed_tlv<T: Tlv>(&mut self, tlv: T) {
|
||||
self.add_tlv(T::TYPE, tlv.value_bytes());
|
||||
}
|
||||
|
||||
fn v2_len(&self) -> u16 {
|
||||
let addr_len = if self.src.is_ipv4() {
|
||||
4 + 2 // 4b IPv4 + 2b port number
|
||||
} else {
|
||||
16 + 2 // 16b IPv6 + 2b port number
|
||||
};
|
||||
|
||||
(addr_len * 2)
|
||||
+ self
|
||||
.tlvs
|
||||
.iter()
|
||||
.map(|(_, value)| 1 + 2 + value.len() as u16)
|
||||
.sum::<u16>()
|
||||
}
|
||||
|
||||
pub fn write_to(&self, wrt: &mut impl io::Write) -> io::Result<()> {
|
||||
// PROXY v2 signature
|
||||
wrt.write_all(&SIGNATURE)?;
|
||||
|
||||
// version | command
|
||||
wrt.write_all(&[Version::V2.v2_hi() | self.command.v2_lo()])?;
|
||||
|
||||
// address family | transport protocol
|
||||
wrt.write_all(&[self.address_family.v2_hi() | self.transport_protocol.v2_lo()])?;
|
||||
|
||||
// rest-of-header length
|
||||
wrt.write_all(&self.v2_len().to_be_bytes())?;
|
||||
|
||||
tracing::debug!("proxy rest-of-header len: {}", self.v2_len());
|
||||
|
||||
fn write_ip_bytes_to(wrt: &mut impl io::Write, ip: IpAddr) -> io::Result<()> {
|
||||
match ip {
|
||||
IpAddr::V4(ip) => wrt.write_all(&ip.octets()),
|
||||
IpAddr::V6(ip) => wrt.write_all(&ip.octets()),
|
||||
}
|
||||
}
|
||||
|
||||
// L3 (IP) address
|
||||
write_ip_bytes_to(wrt, self.src.ip())?;
|
||||
write_ip_bytes_to(wrt, self.dst.ip())?;
|
||||
|
||||
// L4 ports
|
||||
wrt.write_all(&self.src.port().to_be_bytes())?;
|
||||
wrt.write_all(&self.dst.port().to_be_bytes())?;
|
||||
|
||||
// TLVs
|
||||
for (typ, value) in &self.tlvs {
|
||||
wrt.write_all(&[*typ])?;
|
||||
wrt.write_all(&(value.len() as u16).to_be_bytes())?;
|
||||
wrt.write_all(value)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_to_tokio(&self, wrt: &mut (impl AsyncWrite + Unpin)) -> io::Result<()> {
|
||||
let buf = self.to_vec();
|
||||
wrt.write_all(&buf).await
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Vec<u8> {
|
||||
// TODO: figure out cap
|
||||
let mut buf = Vec::with_capacity(64);
|
||||
self.write_to(&mut buf).unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn has_tlv<T: Tlv>(&self) -> bool {
|
||||
self.tlvs.iter().any(|&(typ, _)| typ == T::TYPE)
|
||||
}
|
||||
|
||||
/// Calculates and adds a crc32c TLV to the PROXY header.
|
||||
///
|
||||
/// Uses method defined in spec.
|
||||
///
|
||||
/// If this is not called last thing it will be wrong.
|
||||
pub fn add_crc23c_checksum(&mut self) {
|
||||
// don't add a checksum if it is already set
|
||||
if self.has_tlv::<Crc32c>() {
|
||||
return;
|
||||
}
|
||||
|
||||
// When the checksum is supported by the sender after constructing the header
|
||||
// the sender MUST:
|
||||
// - initialize the checksum field to '0's.
|
||||
// - calculate the CRC32c checksum of the PROXY header as described in RFC4960,
|
||||
// Appendix B [8].
|
||||
// - put the resultant value into the checksum field, and leave the rest of
|
||||
// the bits unchanged.
|
||||
|
||||
// add zeroed checksum field to TLVs
|
||||
self.add_typed_tlv(Crc32c::default());
|
||||
|
||||
// write PROXY header to buffer
|
||||
let mut buf = Vec::new();
|
||||
self.write_to(&mut buf).unwrap();
|
||||
|
||||
// calculate CRC on buffer and update CRC TLV
|
||||
let crc_calc = crc32fast::hash(&buf);
|
||||
self.tlvs.last_mut().unwrap().1 = crc_calc.to_be_bytes().to_smallvec();
|
||||
|
||||
tracing::debug!("checksum is {}", crc_calc);
|
||||
}
|
||||
|
||||
pub fn validate_crc32c_tlv(&self) -> Option<bool> {
|
||||
// extract crc32c TLV or exit early if none is present
|
||||
let crc_sent = self
|
||||
.tlvs
|
||||
.iter()
|
||||
.filter_map(|(typ, value)| Crc32c::try_from_parts(*typ, value))
|
||||
.next()?;
|
||||
|
||||
// If the checksum is provided as part of the PROXY header and the checksum
|
||||
// functionality is supported by the receiver, the receiver MUST:
|
||||
// - store the received CRC32c checksum value aside.
|
||||
// - replace the 32 bits of the checksum field in the received PROXY header with
|
||||
// all '0's and calculate a CRC32c checksum value of the whole PROXY header.
|
||||
// - verify that the calculated CRC32c checksum is the same as the received
|
||||
// CRC32c checksum. If it is not, the receiver MUST treat the TCP connection
|
||||
// providing the header as invalid.
|
||||
// The default procedure for handling an invalid TCP connection is to abort it.
|
||||
|
||||
let mut this = self.clone();
|
||||
for (typ, value) in this.tlvs.iter_mut() {
|
||||
if Crc32c::try_from_parts(*typ, value).is_some() {
|
||||
value.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
let mut buf = Vec::new();
|
||||
this.write_to(&mut buf).unwrap();
|
||||
let crc_calc = crc32fast::hash(&buf);
|
||||
|
||||
Some(crc_sent.checksum == crc_calc)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::net::Ipv6Addr;
|
||||
|
||||
use const_str::hex;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn write_v2_no_tlvs() {
|
||||
let mut exp = Vec::new();
|
||||
exp.extend_from_slice(&SIGNATURE); // 0-11
|
||||
exp.extend_from_slice(&[0x21, 0x11]); // 12-13
|
||||
exp.extend_from_slice(&[0x00, 0x0C]); // 14-15
|
||||
exp.extend_from_slice(&[127, 0, 0, 1, 127, 0, 0, 2]); // 16-23
|
||||
exp.extend_from_slice(&[0x04, 0xd2, 0x00, 80]); // 24-27
|
||||
|
||||
let header = Header::new(
|
||||
Command::Proxy,
|
||||
TransportProtocol::Stream,
|
||||
AddressFamily::Inet,
|
||||
SocketAddr::from(([127, 0, 0, 1], 1234)),
|
||||
SocketAddr::from(([127, 0, 0, 2], 80)),
|
||||
);
|
||||
|
||||
assert_eq!(header.v2_len(), 12);
|
||||
assert_eq!(header.to_vec(), exp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_v2_ipv6_tlv_noop() {
|
||||
let mut exp = Vec::new();
|
||||
exp.extend_from_slice(&SIGNATURE); // 0-11
|
||||
exp.extend_from_slice(&[0x20, 0x11]); // 12-13
|
||||
exp.extend_from_slice(&[0x00, 0x28]); // 14-15
|
||||
exp.extend_from_slice(&hex!("00000000000000000000000000000001")); // 16-31
|
||||
exp.extend_from_slice(&hex!("000102030405060708090A0B0C0D0E0F")); // 32-45
|
||||
exp.extend_from_slice(&[0x00, 80, 0xff, 0xff]); // 45-49
|
||||
exp.extend_from_slice(&[0x04, 0x00, 0x01, 0x00]); // 50-53 NOOP TLV
|
||||
|
||||
let mut header = Header::new(
|
||||
Command::Local,
|
||||
TransportProtocol::Stream,
|
||||
AddressFamily::Inet,
|
||||
SocketAddr::from((Ipv6Addr::LOCALHOST, 80)),
|
||||
SocketAddr::from((
|
||||
Ipv6Addr::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]),
|
||||
65535,
|
||||
)),
|
||||
);
|
||||
|
||||
header.add_tlv(0x04, [0]);
|
||||
|
||||
assert_eq!(header.v2_len(), 36 + 4);
|
||||
assert_eq!(header.to_vec(), exp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_v2_tlv_c2c() {
|
||||
let mut exp = Vec::new();
|
||||
exp.extend_from_slice(&SIGNATURE); // 0-11
|
||||
exp.extend_from_slice(&[0x21, 0x11]); // 12-13
|
||||
exp.extend_from_slice(&[0x00, 0x13]); // 14-15
|
||||
exp.extend_from_slice(&[127, 0, 0, 1, 127, 0, 0, 1]); // 16-23
|
||||
exp.extend_from_slice(&[0x00, 80, 0x00, 80]); // 24-27
|
||||
exp.extend_from_slice(&[0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00]); // 28-35 TLV crc32c
|
||||
|
||||
assert_eq!(
|
||||
crc32fast::hash(&exp),
|
||||
// correct checksum calculated manually
|
||||
u32::from_be_bytes([0x08, 0x70, 0x17, 0x7b]),
|
||||
);
|
||||
|
||||
// re-assign actual checksum to last 4 bytes of expected byte array
|
||||
exp[31..35].copy_from_slice(&[0x08, 0x70, 0x17, 0x7b]);
|
||||
|
||||
let mut header = Header::new(
|
||||
Command::Proxy,
|
||||
TransportProtocol::Stream,
|
||||
AddressFamily::Inet,
|
||||
SocketAddr::from(([127, 0, 0, 1], 80)),
|
||||
SocketAddr::from(([127, 0, 0, 1], 80)),
|
||||
);
|
||||
|
||||
assert!(
|
||||
header.validate_crc32c_tlv().is_none(),
|
||||
"header doesn't have CRC TLV added yet"
|
||||
);
|
||||
|
||||
// add crc32c TLV to header
|
||||
header.add_crc23c_checksum();
|
||||
|
||||
assert_eq!(header.v2_len(), 12 + 7);
|
||||
assert_eq!(header.to_vec(), exp);
|
||||
|
||||
// struct can self-validate checksum
|
||||
assert_eq!(header.validate_crc32c_tlv().unwrap(), true);
|
||||
|
||||
// mangle crc32c TLV and assert that validate now fails
|
||||
*header.tlvs.last_mut().unwrap().1.last_mut().unwrap() = 0x00;
|
||||
assert_eq!(header.validate_crc32c_tlv().unwrap(), false);
|
||||
}
|
||||
}
|
@@ -1,6 +1,14 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2023-xx-xx
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 2.10.0
|
||||
|
||||
- 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
|
||||
|
||||
@@ -8,204 +16,159 @@
|
||||
- Add `actix_rt::Runtime::tokio_runtime()` method to retrieve the underlying Tokio runtime.
|
||||
- Minimum supported Rust version (MSRV) is now 1.65.
|
||||
|
||||
## 2.8.0 - 2022-12-21
|
||||
## 2.8.0
|
||||
|
||||
- Add `#[track_caller]` attribute to `spawn` functions and methods. [#454]
|
||||
- Update `tokio-uring` dependency to `0.4`. [#473]
|
||||
- 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.
|
||||
|
||||
[#454]: https://github.com/actix/actix-net/pull/454
|
||||
[#473]: https://github.com/actix/actix-net/pull/473
|
||||
## 2.7.0
|
||||
|
||||
## 2.7.0 - 2022-03-08
|
||||
|
||||
- Update `tokio-uring` dependency to `0.3`. [#448]
|
||||
- Update `tokio-uring` dependency to `0.3`.
|
||||
- Minimum supported Rust version (MSRV) is now 1.49.
|
||||
|
||||
[#448]: https://github.com/actix/actix-net/pull/448
|
||||
## 2.6.0
|
||||
|
||||
## 2.6.0 - 2022-01-12
|
||||
- Update `tokio-uring` dependency to `0.2`.
|
||||
|
||||
- Update `tokio-uring` dependency to `0.2`. [#436]
|
||||
## 2.5.1
|
||||
|
||||
[#436]: https://github.com/actix/actix-net/pull/436
|
||||
- Expose `System::with_tokio_rt` and `Arbiter::with_tokio_rt`.
|
||||
|
||||
## 2.5.1 - 2021-12-31
|
||||
## 2.5.0
|
||||
|
||||
- Expose `System::with_tokio_rt` and `Arbiter::with_tokio_rt`. [#430]
|
||||
- Add `System::run_with_code` to allow retrieving the exit code on stop.
|
||||
|
||||
[#430]: https://github.com/actix/actix-net/pull/430
|
||||
## 2.4.0
|
||||
|
||||
## 2.5.0 - 2021-11-22
|
||||
- Add `Arbiter::try_current` for situations where thread may or may not have Arbiter context.
|
||||
- Start io-uring with `System::new` when feature is enabled.
|
||||
|
||||
- Add `System::run_with_code` to allow retrieving the exit code on stop. [#411]
|
||||
## 2.3.0
|
||||
|
||||
[#411]: https://github.com/actix/actix-net/pull/411
|
||||
- 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.4.0 - 2021-11-05
|
||||
## 2.2.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
|
||||
[#408]: https://github.com/actix/actix-net/pull/408
|
||||
|
||||
## 2.3.0 - 2021-10-11
|
||||
|
||||
- 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
|
||||
[#374]: https://github.com/actix/actix-net/pull/374
|
||||
|
||||
## 2.2.0 - 2021-03-29
|
||||
|
||||
- **BREAKING** `ActixStream::{poll_read_ready, poll_write_ready}` methods now return `Ready` object in ok variant. [#293]
|
||||
- **BREAKING** `ActixStream::{poll_read_ready, poll_write_ready}` methods now return `Ready` object in ok variant.
|
||||
- Breakage is acceptable since `ActixStream` was not intended to be public.
|
||||
|
||||
[#293]: https://github.com/actix/actix-net/pull/293
|
||||
## 2.1.0
|
||||
|
||||
## 2.1.0 - 2021-02-24
|
||||
- Add `ActixStream` extension trait to include readiness methods.
|
||||
- Re-export `tokio::net::TcpSocket` in `net` module
|
||||
|
||||
- Add `ActixStream` extension trait to include readiness methods. [#276]
|
||||
- Re-export `tokio::net::TcpSocket` in `net` module [#282]
|
||||
## 2.0.2
|
||||
|
||||
[#276]: https://github.com/actix/actix-net/pull/276
|
||||
[#282]: https://github.com/actix/actix-net/pull/282
|
||||
- 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.2 - 2021-02-06
|
||||
## 2.0.1
|
||||
|
||||
- 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]
|
||||
- Expose `JoinError` from Tokio.
|
||||
|
||||
[#274]: https://github.com/actix/actix-net/pull/274
|
||||
[#275]: https://github.com/actix/actix-net/pull/275
|
||||
## 2.0.0
|
||||
|
||||
## 2.0.1 - 2021-02-06
|
||||
- Remove all Arbiter-local storage methods.
|
||||
- Re-export `tokio::pin`.
|
||||
|
||||
- Expose `JoinError` from Tokio. [#271]
|
||||
## 2.0.0-beta.3
|
||||
|
||||
[#271]: https://github.com/actix/actix-net/pull/271
|
||||
- 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 - 2021-02-02
|
||||
## 2.0.0-beta.2
|
||||
|
||||
- Remove all Arbiter-local storage methods. [#262]
|
||||
- Re-export `tokio::pin`. [#262]
|
||||
|
||||
[#262]: https://github.com/actix/actix-net/pull/262
|
||||
|
||||
## 2.0.0-beta.3 - 2021-01-31
|
||||
|
||||
- Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`. [#253]
|
||||
- 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
|
||||
[#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
|
||||
|
||||
## 2.0.0-beta.2 - 2021-01-09
|
||||
|
||||
- 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`.
|
||||
|
||||
[#245]: https://github.com/actix/actix-net/pull/245
|
||||
## 2.0.0-beta.1
|
||||
|
||||
## 2.0.0-beta.1 - 2020-12-28
|
||||
|
||||
- Add `System::attach_to_tokio` method. [#173]
|
||||
- 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]
|
||||
- Add `System::attach_to_tokio` method.
|
||||
- Update `tokio` dependency to `1.0`.
|
||||
- Rename `time` module `delay_for` to `sleep`, `delay_until` to `sleep_until`, `Delay` to `Sleep` to stay aligned with Tokio's naming.
|
||||
- Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`.
|
||||
- These methods now accept `&self` when calling. [#236]
|
||||
- Remove `'static` lifetime requirement for `System::run` and `Builder::run`. [#236]
|
||||
- `Arbiter::spawn` now panics when `System` is not in scope. [#207]
|
||||
- Fix work load issue by removing `PENDING` thread local. [#207]
|
||||
- These methods now accept `&self` when calling.
|
||||
- Remove `'static` lifetime requirement for `System::run` and `Builder::run`.
|
||||
- `Arbiter::spawn` now panics when `System` is not in scope.
|
||||
- Fix work load issue by removing `PENDING` thread local.
|
||||
|
||||
[#207]: https://github.com/actix/actix-net/pull/207
|
||||
[#236]: https://github.com/actix/actix-net/pull/236
|
||||
## 1.1.1
|
||||
|
||||
## 1.1.1 - 2020-04-30
|
||||
- Fix memory leak due to
|
||||
|
||||
- Fix memory leak due to [#94] (see [#129] for more detail)
|
||||
## 1.1.0 _(YANKED)_
|
||||
|
||||
[#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.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
|
||||
## 1.0.0
|
||||
|
||||
- Update dependencies
|
||||
|
||||
## 1.0.0-alpha.3 - 2019-12-07
|
||||
## 1.0.0-alpha.3
|
||||
|
||||
- Migrate to tokio 0.2
|
||||
- Fix compilation on non-unix platforms
|
||||
|
||||
## 1.0.0-alpha.2 - 2019-12-02
|
||||
## 1.0.0-alpha.2
|
||||
|
||||
- Export `main` and `test` attribute macros
|
||||
- Export `time` module (re-export of tokio-timer)
|
||||
- Export `net` module (re-export of tokio-net)
|
||||
|
||||
## 1.0.0-alpha.1 - 2019-11-22
|
||||
## 1.0.0-alpha.1
|
||||
|
||||
- Migrate to std::future and tokio 0.2
|
||||
|
||||
## 0.2.6 - 2019-11-14
|
||||
## 0.2.6
|
||||
|
||||
- Allow to join arbiter's thread. #60
|
||||
- Fix arbiter's thread panic message.
|
||||
|
||||
## 0.2.5 - 2019-09-02
|
||||
## 0.2.5
|
||||
|
||||
- Add arbiter specific storage
|
||||
|
||||
## 0.2.4 - 2019-07-17
|
||||
## 0.2.4
|
||||
|
||||
- Avoid a copy of the Future when initializing the Box. #29
|
||||
|
||||
## 0.2.3 - 2019-06-22
|
||||
## 0.2.3
|
||||
|
||||
- Allow to start System using existing CurrentThread Handle #22
|
||||
|
||||
## 0.2.2 - 2019-03-28
|
||||
## 0.2.2
|
||||
|
||||
- Moved `blocking` module to `actix-threadpool` crate
|
||||
|
||||
## 0.2.1 - 2019-03-11
|
||||
## 0.2.1
|
||||
|
||||
- Added `blocking` module
|
||||
- Added `Arbiter::exec_fn` - execute fn on the arbiter's thread
|
||||
- Added `Arbiter::exec` - execute fn on the arbiter's thread and wait result
|
||||
|
||||
## 0.2.0 - 2019-03-06
|
||||
## 0.2.0
|
||||
|
||||
- `run` method returns `io::Result<()>`
|
||||
- Removed `Handle`
|
||||
|
||||
## 0.1.0 - 2018-12-09
|
||||
## 0.1.0
|
||||
|
||||
- Initial release
|
||||
|
@@ -1,19 +1,19 @@
|
||||
[package]
|
||||
name = "actix-rt"
|
||||
version = "2.9.0"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Rob Ede <robjtede@icloud.com>",
|
||||
]
|
||||
version = "2.10.0"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||
description = "Tokio-based single-threaded async runtime for the Actix ecosystem"
|
||||
keywords = ["async", "futures", "io", "runtime"]
|
||||
homepage = "https://actix.rs"
|
||||
repository = "https://github.com/actix/actix-net.git"
|
||||
repository = "https://github.com/actix/actix-net"
|
||||
categories = ["network-programming", "asynchronous"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[package.metadata.cargo_check_external_types]
|
||||
allowed_external_types = ["actix_macros::*", "tokio::*"]
|
||||
|
||||
[features]
|
||||
default = ["macros"]
|
||||
macros = ["actix-macros"]
|
||||
@@ -23,12 +23,14 @@ io-uring = ["tokio-uring"]
|
||||
actix-macros = { version = "0.2.3", optional = true }
|
||||
|
||||
futures-core = { version = "0.3", default-features = false }
|
||||
tokio = { version = "1.23.1", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] }
|
||||
tokio = { version = "1.44.2", features = ["rt", "net", "parking_lot", "signal", "sync", "time"] }
|
||||
|
||||
# runtime for `io-uring` feature
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
tokio-uring = { version = "0.4", optional = true }
|
||||
tokio-uring = { version = "0.5", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.23.1", features = ["full"] }
|
||||
hyper = { version = "0.14.10", default-features = false, features = ["server", "tcp", "http1"] }
|
||||
tokio = { version = "1.44.2", features = ["full"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@@ -3,11 +3,11 @@
|
||||
> Tokio-based single-threaded async runtime for the Actix ecosystem.
|
||||
|
||||
[](https://crates.io/crates/actix-rt)
|
||||
[](https://docs.rs/actix-rt/2.9.0)
|
||||
[](https://docs.rs/actix-rt/2.10.0)
|
||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
|
||||

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

|
||||
[](https://discord.gg/WghFtEH6Hb)
|
||||
|
||||
|
@@ -1,29 +0,0 @@
|
||||
use std::{convert::Infallible, net::SocketAddr};
|
||||
|
||||
use hyper::{
|
||||
service::{make_service_fn, service_fn},
|
||||
Body, Request, Response, Server,
|
||||
};
|
||||
|
||||
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(err) = server.await {
|
||||
eprintln!("server error: {}", err);
|
||||
}
|
||||
})
|
||||
}
|
@@ -16,7 +16,7 @@ use crate::system::{System, SystemCommand};
|
||||
pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
thread_local!(
|
||||
static HANDLE: RefCell<Option<ArbiterHandle>> = RefCell::new(None);
|
||||
static HANDLE: RefCell<Option<ArbiterHandle>> = const { RefCell::new(None) };
|
||||
);
|
||||
|
||||
pub(crate) enum ArbiterCommand {
|
||||
@@ -109,13 +109,13 @@ impl Arbiter {
|
||||
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
|
||||
pub fn with_tokio_rt<F>(runtime_factory: F) -> Arbiter
|
||||
where
|
||||
F: Fn() -> tokio::runtime::Runtime + Send + 'static,
|
||||
F: FnOnce() -> tokio::runtime::Runtime + Send + 'static,
|
||||
{
|
||||
let sys = System::current();
|
||||
let system_id = sys.id();
|
||||
let arb_id = COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let name = format!("actix-rt|system:{}|arbiter:{}", system_id, arb_id);
|
||||
let name = format!("actix-rt|system:{system_id}|arbiter:{arb_id}");
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
|
||||
let (ready_tx, ready_rx) = std::sync::mpsc::channel::<()>();
|
||||
|
@@ -34,14 +34,13 @@
|
||||
//! ```
|
||||
//!
|
||||
//! # `io-uring` Support
|
||||
//!
|
||||
//! There is experimental support for using io-uring with this crate by enabling the
|
||||
//! `io-uring` feature. For now, it is semver exempt.
|
||||
//!
|
||||
//! 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.
|
||||
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![warn(future_incompatible, missing_docs)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
@@ -16,7 +16,7 @@ use crate::{arbiter::ArbiterHandle, Arbiter};
|
||||
static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
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.
|
||||
@@ -48,7 +48,7 @@ impl System {
|
||||
/// [tokio-runtime]: tokio::runtime::Runtime
|
||||
pub fn with_tokio_rt<F>(runtime_factory: F) -> SystemRunner
|
||||
where
|
||||
F: Fn() -> tokio::runtime::Runtime,
|
||||
F: FnOnce() -> tokio::runtime::Runtime,
|
||||
{
|
||||
let (stop_tx, stop_rx) = oneshot::channel();
|
||||
let (sys_tx, sys_rx) = mpsc::unbounded_channel();
|
||||
@@ -87,7 +87,7 @@ impl System {
|
||||
#[doc(hidden)]
|
||||
pub fn with_tokio_rt<F>(_: F) -> SystemRunner
|
||||
where
|
||||
F: Fn() -> tokio::runtime::Runtime,
|
||||
F: FnOnce() -> tokio::runtime::Runtime,
|
||||
{
|
||||
unimplemented!("System::with_tokio_rt is not implemented for io-uring feature yet")
|
||||
}
|
||||
@@ -187,10 +187,7 @@ impl SystemRunner {
|
||||
|
||||
match exit_code {
|
||||
0 => Ok(()),
|
||||
nonzero => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("Non-zero exit code: {}", nonzero),
|
||||
)),
|
||||
nonzero => Err(io::Error::other(format!("Non-zero exit code: {nonzero}"))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,20 +196,23 @@ impl SystemRunner {
|
||||
let SystemRunner { rt, stop_rx, .. } = self;
|
||||
|
||||
// run loop
|
||||
rt.block_on(stop_rx)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||
rt.block_on(stop_rx).map_err(io::Error::other)
|
||||
}
|
||||
|
||||
/// Retrieves a reference to the underlying Actix runtime associated with this SystemRunner instance.
|
||||
/// 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.
|
||||
/// 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 [`actix_rt::Runtime`] to spawn tasks on
|
||||
/// the same runtime.
|
||||
/// parts of your project. For example, some components might require a [`Runtime`] to spawn
|
||||
/// tasks on the same runtime.
|
||||
///
|
||||
/// # Example
|
||||
/// Read more in the documentation for [`Runtime`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let system_runner = actix_rt::System::new();
|
||||
@@ -221,19 +221,14 @@ impl SystemRunner {
|
||||
/// // Use the runtime to spawn an async task or perform other operations
|
||||
/// ```
|
||||
///
|
||||
/// Read more in the documentation for [`actix_rt::Runtime`]
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// An immutable reference to the [`actix_rt::Runtime`] instance associated with this
|
||||
/// [`actix_rt::SystemRunner`] instance.
|
||||
///
|
||||
/// # 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.
|
||||
/// 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
|
||||
}
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::{
|
||||
future::Future,
|
||||
time::{Duration, Instant},
|
||||
@@ -358,7 +360,7 @@ fn tokio_uring_arbiter() {
|
||||
let f = tokio_uring::fs::File::create("test.txt").await.unwrap();
|
||||
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());
|
||||
|
||||
f.sync_all().await.unwrap();
|
||||
|
@@ -2,6 +2,25 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 2.6.0
|
||||
|
||||
- Add `ServerBuilder::shutdown_signal()` method.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 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.
|
||||
@@ -10,9 +29,7 @@
|
||||
## 2.2.0
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.59.
|
||||
- Update `tokio-uring` dependency to `0.4`. [#473]
|
||||
|
||||
[#473]: https://github.com/actix/actix-net/pull/473
|
||||
- Update `tokio-uring` dependency to `0.4`.
|
||||
|
||||
## 2.1.1
|
||||
|
||||
@@ -20,12 +37,9 @@
|
||||
|
||||
## 2.1.0
|
||||
|
||||
- Update `tokio-uring` dependency to `0.3`. [#448]
|
||||
- Logs emitted now use the `tracing` crate with `log` compatibility. [#448]
|
||||
- Wait for accept thread to stop before sending completion signal. [#443]
|
||||
|
||||
[#443]: https://github.com/actix/actix-net/pull/443
|
||||
[#448]: https://github.com/actix/actix-net/pull/448
|
||||
- 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
|
||||
|
||||
@@ -33,9 +47,7 @@
|
||||
|
||||
## 2.0.0-rc.4
|
||||
|
||||
- Update `tokio-uring` dependency to `0.2`. [#436]
|
||||
|
||||
[#436]: https://github.com/actix/actix-net/pull/436
|
||||
- Update `tokio-uring` dependency to `0.2`.
|
||||
|
||||
## 2.0.0-rc.3
|
||||
|
||||
@@ -43,117 +55,80 @@
|
||||
|
||||
## 2.0.0-rc.2
|
||||
|
||||
- Simplify `TestServer`. [#431]
|
||||
|
||||
[#431]: https://github.com/actix/actix-net/pull/431
|
||||
- Simplify `TestServer`.
|
||||
|
||||
## 2.0.0-rc.1
|
||||
|
||||
- Hide implementation details of `Server`. [#424]
|
||||
- `Server` now runs only after awaiting it. [#425]
|
||||
|
||||
[#424]: https://github.com/actix/actix-net/pull/424
|
||||
[#425]: https://github.com/actix/actix-net/pull/425
|
||||
- Hide implementation details of `Server`.
|
||||
- `Server` now runs only after awaiting it.
|
||||
|
||||
## 2.0.0-beta.9
|
||||
|
||||
- Restore `Arbiter` support lost in `beta.8`. [#417]
|
||||
|
||||
[#417]: https://github.com/actix/actix-net/pull/417
|
||||
- Restore `Arbiter` support lost in `beta.8`.
|
||||
|
||||
## 2.0.0-beta.8
|
||||
|
||||
- Fix non-unix signal handler. [#410]
|
||||
|
||||
[#410]: https://github.com/actix/actix-net/pull/410
|
||||
- Fix non-unix signal handler.
|
||||
|
||||
## 2.0.0-beta.7
|
||||
|
||||
- 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]
|
||||
- Server can be started in regular Tokio runtime.
|
||||
- Expose new `Server` type whose `Future` impl resolves when server stops.
|
||||
- Rename `Server` to `ServerHandle`.
|
||||
- Add `Server::handle` to obtain handle to server.
|
||||
- Rename `ServerBuilder::{maxconn => max_concurrent_connections}`.
|
||||
- Deprecate crate-level `new` shortcut for server builder.
|
||||
- Minimum supported Rust version (MSRV) is now 1.52.
|
||||
|
||||
[#407]: https://github.com/actix/actix-net/pull/407
|
||||
[#408]: https://github.com/actix/actix-net/pull/408
|
||||
|
||||
## 2.0.0-beta.6
|
||||
|
||||
- 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
|
||||
[#349]: https://github.com/actix/actix-net/pull/349
|
||||
[#389]: https://github.com/actix/actix-net/pull/389
|
||||
- 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.5
|
||||
|
||||
- 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
|
||||
- Server shutdown notifies all workers to exit regardless if shutdown is graceful. This causes all workers to shutdown immediately in force shutdown case.
|
||||
|
||||
## 2.0.0-beta.4
|
||||
|
||||
- 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`. [#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
|
||||
[#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
|
||||
- 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.2
|
||||
|
||||
- Merge `actix-testing` to `actix-server` as `test_server` mod. [#242]
|
||||
|
||||
[#242]: https://github.com/actix/actix-net/pull/242
|
||||
- Merge `actix-testing` to `actix-server` as `test_server` mod.
|
||||
|
||||
## 2.0.0-beta.1
|
||||
|
||||
- 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
|
||||
- Added explicit info log message on accept queue pause.
|
||||
- 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.
|
||||
|
||||
## 1.0.4
|
||||
|
||||
- Update actix-codec to 0.3.0.
|
||||
- Workers must be greater than 0. [#167]
|
||||
|
||||
[#167]: https://github.com/actix/actix-net/pull/167
|
||||
- Workers must be greater than 0.
|
||||
|
||||
## 1.0.3
|
||||
|
||||
- Replace deprecated `net2` crate with `socket2` [#140]
|
||||
|
||||
[#140]: https://github.com/actix/actix-net/pull/140
|
||||
- Replace deprecated `net2` crate with `socket2`.
|
||||
|
||||
## 1.0.2
|
||||
|
||||
- Avoid error by calling `reregister()` on Windows [#103]
|
||||
|
||||
[#103]: https://github.com/actix/actix-net/pull/103
|
||||
- Avoid error by calling `reregister()` on Windows.
|
||||
|
||||
## 1.0.1
|
||||
|
||||
|
34
actix-server/Cargo.toml
Executable file → Normal file
34
actix-server/Cargo.toml
Executable file → Normal file
@@ -1,45 +1,53 @@
|
||||
[package]
|
||||
name = "actix-server"
|
||||
version = "2.3.0"
|
||||
version = "2.6.0"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Rob Ede <robjtede@icloud.com>",
|
||||
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Rob Ede <robjtede@icloud.com>",
|
||||
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",
|
||||
]
|
||||
description = "General purpose TCP server built for the Actix ecosystem"
|
||||
keywords = ["network", "tcp", "server", "framework", "async"]
|
||||
categories = ["network-programming", "asynchronous"]
|
||||
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"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[package.metadata.cargo_check_external_types]
|
||||
allowed_external_types = ["tokio::*"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
io-uring = ["tokio-uring", "actix-rt/io-uring"]
|
||||
|
||||
[dependencies]
|
||||
actix-rt = { version = "2.8", default-features = false }
|
||||
actix-rt = { version = "2.10", default-features = false }
|
||||
actix-service = "2"
|
||||
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 = "0.8", features = ["os-poll", "net"] }
|
||||
socket2 = "0.5"
|
||||
tokio = { version = "1.23.1", features = ["sync"] }
|
||||
mio = { version = "1", features = ["os-poll", "net"] }
|
||||
socket2 = "0.6"
|
||||
tokio = { version = "1.44.2", features = ["sync"] }
|
||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||
|
||||
# runtime for `io-uring` feature
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
tokio-uring = { version = "0.4", optional = true }
|
||||
tokio-uring = { version = "0.5", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-codec = "0.5"
|
||||
actix-rt = "2.8"
|
||||
|
||||
bytes = "1"
|
||||
env_logger = "0.10"
|
||||
futures-util = { version = "0.3.17", default-features = false, features = ["sink", "async-await-macro"] }
|
||||
tokio = { version = "1.23.1", features = ["io-util", "rt-multi-thread", "macros", "fs"] }
|
||||
pretty_env_logger = "0.5"
|
||||
static_assertions = "1"
|
||||
tokio = { version = "1.44.2", features = ["io-util", "rt-multi-thread", "macros", "fs", "time"] }
|
||||
tokio-util = "0.7"
|
||||
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@@ -2,14 +2,20 @@
|
||||
|
||||
> General purpose TCP server built for the Actix ecosystem.
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
|
||||
[](https://crates.io/crates/actix-server)
|
||||
[](https://docs.rs/actix-server/2.3.0)
|
||||
[](https://docs.rs/actix-server/2.6.0)
|
||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||

|
||||
[](https://deps.rs/crate/actix-server/2.3.0)
|
||||

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

|
||||
[](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.
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::io;
|
||||
|
||||
use actix_codec::{Framed, LinesCodec};
|
||||
@@ -18,7 +20,8 @@ use futures_util::{SinkExt as _, StreamExt as _};
|
||||
use tokio::{fs::File, io::AsyncReadExt as _};
|
||||
|
||||
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);
|
||||
tracing::info!("starting server on port: {}", &addr.0);
|
||||
|
51
actix-server/examples/shutdown-signal.rs
Normal file
51
actix-server/examples/shutdown-signal.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
//! Demonstrates use of the `ServerBuilder::shutdown_signal` method using `tokio-util`s
|
||||
//! `CancellationToken` helper using a nonsensical timer. In practice, this cancellation token would
|
||||
//! be wired throughout your application and typically triggered by OS signals elsewhere.
|
||||
|
||||
use std::{io, time::Duration};
|
||||
|
||||
use actix_rt::net::TcpStream;
|
||||
use actix_server::Server;
|
||||
use actix_service::fn_service;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::{prelude::*, EnvFilter};
|
||||
|
||||
async fn run(stop_signal: CancellationToken) -> io::Result<()> {
|
||||
tracing_subscriber::registry()
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.with(
|
||||
EnvFilter::builder()
|
||||
.with_default_directive(LevelFilter::INFO.into())
|
||||
.from_env_lossy(),
|
||||
)
|
||||
.init();
|
||||
|
||||
let addr = ("127.0.0.1", 8080);
|
||||
tracing::info!("starting server on port: {}", &addr.0);
|
||||
|
||||
Server::build()
|
||||
.bind("shutdown-signal", addr, || {
|
||||
fn_service(|_stream: TcpStream| async { Ok::<_, io::Error>(()) })
|
||||
})?
|
||||
.shutdown_signal(stop_signal.cancelled_owned())
|
||||
.workers(2)
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
let stop_signal = CancellationToken::new();
|
||||
|
||||
tokio::spawn({
|
||||
let stop_signal = stop_signal.clone();
|
||||
async move {
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
stop_signal.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
run(stop_signal).await?;
|
||||
Ok(())
|
||||
}
|
@@ -25,7 +25,8 @@ use futures_util::future::ok;
|
||||
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
|
||||
|
||||
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));
|
||||
|
||||
|
@@ -76,7 +76,7 @@ impl Accept {
|
||||
let accept_handle = thread::Builder::new()
|
||||
.name("actix-server acceptor".to_owned())
|
||||
.spawn(move || accept.poll_with(&mut sockets))
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
|
||||
.map_err(io::Error::other)?;
|
||||
|
||||
Ok((waker_queue, handles_server, accept_handle))
|
||||
}
|
||||
@@ -130,7 +130,7 @@ impl Accept {
|
||||
if let Err(err) = self.poll.poll(&mut events, self.timeout) {
|
||||
match err.kind() {
|
||||
io::ErrorKind::Interrupted => {}
|
||||
_ => panic!("Poll error: {}", err),
|
||||
_ => panic!("Poll error: {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,6 @@ impl Accept {
|
||||
// task is done. Take care not to take the guard again inside this loop.
|
||||
let mut guard = self.waker_queue.guard();
|
||||
|
||||
#[allow(clippy::significant_drop_in_scrutinee)]
|
||||
match guard.pop_front() {
|
||||
// Worker notified it became available.
|
||||
Some(WakerInterest::WorkerAvailable(idx)) => {
|
||||
@@ -455,8 +454,8 @@ impl Accept {
|
||||
/// All other errors will incur a timeout before next `accept()` call is attempted. The timeout is
|
||||
/// useful to handle resource exhaustion errors like `ENFILE` and `EMFILE`. Otherwise, it could
|
||||
/// enter into a temporary spin loop.
|
||||
fn connection_error(e: &io::Error) -> bool {
|
||||
e.kind() == io::ErrorKind::ConnectionRefused
|
||||
|| e.kind() == io::ErrorKind::ConnectionAborted
|
||||
|| e.kind() == io::ErrorKind::ConnectionReset
|
||||
fn connection_error(err: &io::Error) -> bool {
|
||||
err.kind() == io::ErrorKind::ConnectionRefused
|
||||
|| err.kind() == io::ErrorKind::ConnectionAborted
|
||||
|| err.kind() == io::ErrorKind::ConnectionReset
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
use std::{io, num::NonZeroUsize, time::Duration};
|
||||
use std::{future::Future, io, num::NonZeroUsize, time::Duration};
|
||||
|
||||
use actix_rt::net::TcpStream;
|
||||
use futures_core::future::BoxFuture;
|
||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||
use tracing::{info, trace};
|
||||
|
||||
use crate::{
|
||||
server::ServerCommand,
|
||||
@@ -14,7 +14,9 @@ use crate::{
|
||||
|
||||
/// Multipath TCP (MPTCP) preference.
|
||||
///
|
||||
/// Also see [`ServerBuilder::mptcp()`].
|
||||
/// 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.
|
||||
@@ -38,6 +40,7 @@ pub struct ServerBuilder {
|
||||
pub(crate) mptcp: MpTcp,
|
||||
pub(crate) exit: bool,
|
||||
pub(crate) listen_os_signals: bool,
|
||||
pub(crate) shutdown_signal: Option<BoxFuture<'static, ()>>,
|
||||
pub(crate) cmd_tx: UnboundedSender<ServerCommand>,
|
||||
pub(crate) cmd_rx: UnboundedReceiver<ServerCommand>,
|
||||
pub(crate) worker_config: ServerWorkerConfig,
|
||||
@@ -63,25 +66,26 @@ impl ServerBuilder {
|
||||
mptcp: MpTcp::Disabled,
|
||||
exit: false,
|
||||
listen_os_signals: true,
|
||||
shutdown_signal: None,
|
||||
cmd_tx,
|
||||
cmd_rx,
|
||||
worker_config: ServerWorkerConfig::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// The default worker count is the number of physical CPU cores available. If your benchmark
|
||||
/// 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
|
||||
///
|
||||
/// Panics if `num` is 0.
|
||||
///
|
||||
/// [`num_cpus`]: https://docs.rs/num_cpus
|
||||
pub fn workers(mut self, num: usize) -> Self {
|
||||
assert_ne!(num, 0, "workers must be greater than 0");
|
||||
self.threads = num;
|
||||
@@ -155,18 +159,55 @@ impl ServerBuilder {
|
||||
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 {
|
||||
self.exit = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Disable OS signal handling.
|
||||
/// Disables OS signal handling.
|
||||
pub fn disable_signals(mut self) -> Self {
|
||||
self.listen_os_signals = false;
|
||||
self
|
||||
}
|
||||
|
||||
/// Specify shutdown signal from a future.
|
||||
///
|
||||
/// Using this method will prevent OS signal handlers being set up.
|
||||
///
|
||||
/// Typically, a `CancellationToken` will be used, but any future _can_ be.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use std::io;
|
||||
/// # use tokio::net::TcpStream;
|
||||
/// # use actix_server::Server;
|
||||
/// # async fn run() -> io::Result<()> {
|
||||
/// use actix_service::fn_service;
|
||||
/// use tokio_util::sync::CancellationToken;
|
||||
///
|
||||
/// let stop_signal = CancellationToken::new();
|
||||
///
|
||||
/// Server::build()
|
||||
/// .bind("shutdown-signal", "127.0.0.1:12345", || {
|
||||
/// fn_service(|_stream: TcpStream| async { Ok::<_, io::Error>(()) })
|
||||
/// })?
|
||||
/// .shutdown_signal(stop_signal.cancelled_owned())
|
||||
/// .run()
|
||||
/// .await
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn shutdown_signal<Fut>(mut self, shutdown_signal: Fut) -> Self
|
||||
where
|
||||
Fut: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
self.shutdown_signal = Some(Box::pin(shutdown_signal));
|
||||
self
|
||||
}
|
||||
|
||||
/// Timeout for graceful workers shutdown in seconds.
|
||||
///
|
||||
/// After receiving a stop signal, workers have this much time to finish serving requests.
|
||||
@@ -179,25 +220,49 @@ impl ServerBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Add new service to the server.
|
||||
pub fn bind<F, U, N>(mut self, name: N, addr: U, factory: F) -> io::Result<Self>
|
||||
/// Adds new service to the server.
|
||||
///
|
||||
/// 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
|
||||
F: ServerServiceFactory<TcpStream>,
|
||||
U: ToSocketAddrs,
|
||||
N: AsRef<str>,
|
||||
{
|
||||
let sockets = bind_addr(addr, self.backlog, &self.mptcp)?;
|
||||
let sockets = bind_addr(addrs, self.backlog, &self.mptcp)?;
|
||||
|
||||
trace!("binding server to: {:?}", &sockets);
|
||||
tracing::trace!("binding server to: {sockets:?}");
|
||||
|
||||
for lst in sockets {
|
||||
let token = self.next_token();
|
||||
|
||||
self.factories.push(StreamNewService::create(
|
||||
name.as_ref().to_string(),
|
||||
token,
|
||||
factory.clone(),
|
||||
lst.local_addr()?,
|
||||
));
|
||||
|
||||
self.sockets
|
||||
.push((token, name.as_ref().to_string(), MioListener::Tcp(lst)));
|
||||
}
|
||||
@@ -205,7 +270,12 @@ impl ServerBuilder {
|
||||
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>>(
|
||||
mut self,
|
||||
name: N,
|
||||
@@ -237,7 +307,7 @@ impl ServerBuilder {
|
||||
if self.sockets.is_empty() {
|
||||
panic!("Server should have at least one bound socket");
|
||||
} else {
|
||||
info!("starting {} workers", self.threads);
|
||||
tracing::info!("starting {} workers", self.threads);
|
||||
Server::new(self)
|
||||
}
|
||||
}
|
||||
@@ -251,7 +321,12 @@ impl ServerBuilder {
|
||||
|
||||
#[cfg(unix)]
|
||||
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>
|
||||
where
|
||||
F: ServerServiceFactory<actix_rt::net::UnixStream>,
|
||||
@@ -271,9 +346,14 @@ impl ServerBuilder {
|
||||
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.
|
||||
///
|
||||
/// # 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>>(
|
||||
mut self,
|
||||
name: N,
|
||||
@@ -284,17 +364,22 @@ impl ServerBuilder {
|
||||
F: ServerServiceFactory<actix_rt::net::UnixStream>,
|
||||
{
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
lst.set_nonblocking(true)?;
|
||||
|
||||
let token = self.next_token();
|
||||
let addr = crate::socket::StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
|
||||
|
||||
self.factories.push(StreamNewService::create(
|
||||
name.as_ref().to_string(),
|
||||
token,
|
||||
factory,
|
||||
addr,
|
||||
));
|
||||
|
||||
self.sockets
|
||||
.push((token, name.as_ref().to_string(), MioListener::from(lst)));
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
@@ -323,9 +408,6 @@ pub(super) fn bind_addr<S: ToSocketAddrs>(
|
||||
} else if let Some(err) = opt_err.take() {
|
||||
Err(err)
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"Can not bind to address.",
|
||||
))
|
||||
Err(io::Error::other("Can not bind to address."))
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,5 @@
|
||||
//! 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_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
|
@@ -18,7 +18,7 @@ use crate::{
|
||||
builder::ServerBuilder,
|
||||
join_all::join_all,
|
||||
service::InternalServiceFactory,
|
||||
signals::{SignalKind, Signals},
|
||||
signals::{OsSignals, SignalKind, StopSignal},
|
||||
waker_queue::{WakerInterest, WakerQueue},
|
||||
worker::{ServerWorker, ServerWorkerConfig, WorkerHandleServer},
|
||||
ServerHandle,
|
||||
@@ -183,11 +183,6 @@ impl ServerInner {
|
||||
}
|
||||
|
||||
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.
|
||||
let is_actix = actix_rt::System::try_current().is_some();
|
||||
let is_tokio = tokio::runtime::Handle::try_current().is_ok();
|
||||
@@ -207,10 +202,20 @@ impl ServerInner {
|
||||
);
|
||||
}
|
||||
|
||||
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 {
|
||||
signal_fut: (builder.listen_os_signals).then(Signals::new),
|
||||
signal_fut: builder.shutdown_signal.map(StopSignal::Cancel).or_else(|| {
|
||||
builder
|
||||
.listen_os_signals
|
||||
.then(OsSignals::new)
|
||||
.map(StopSignal::Os)
|
||||
}),
|
||||
cmd_rx: builder.cmd_rx,
|
||||
};
|
||||
|
||||
@@ -315,7 +320,16 @@ impl ServerInner {
|
||||
|
||||
fn map_signal(signal: SignalKind) -> ServerCommand {
|
||||
match signal {
|
||||
SignalKind::Int => {
|
||||
SignalKind::Cancel => {
|
||||
info!("Cancellation token/channel received; starting graceful shutdown");
|
||||
ServerCommand::Stop {
|
||||
graceful: true,
|
||||
completion: None,
|
||||
force_system_stop: true,
|
||||
}
|
||||
}
|
||||
|
||||
SignalKind::OsInt => {
|
||||
info!("SIGINT received; starting forced shutdown");
|
||||
ServerCommand::Stop {
|
||||
graceful: false,
|
||||
@@ -324,7 +338,7 @@ impl ServerInner {
|
||||
}
|
||||
}
|
||||
|
||||
SignalKind::Term => {
|
||||
SignalKind::OsTerm => {
|
||||
info!("SIGTERM received; starting graceful shutdown");
|
||||
ServerCommand::Stop {
|
||||
graceful: true,
|
||||
@@ -333,7 +347,7 @@ impl ServerInner {
|
||||
}
|
||||
}
|
||||
|
||||
SignalKind::Quit => {
|
||||
SignalKind::OsQuit => {
|
||||
info!("SIGQUIT received; starting forced shutdown");
|
||||
ServerCommand::Stop {
|
||||
graceful: false,
|
||||
@@ -347,7 +361,7 @@ impl ServerInner {
|
||||
|
||||
struct ServerEventMultiplexer {
|
||||
cmd_rx: UnboundedReceiver<ServerCommand>,
|
||||
signal_fut: Option<Signals>,
|
||||
signal_fut: Option<StopSignal>,
|
||||
}
|
||||
|
||||
impl Stream for ServerEventMultiplexer {
|
||||
|
@@ -1,10 +1,11 @@
|
||||
use std::{
|
||||
fmt,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
pin::{pin, Pin},
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
use futures_core::future::BoxFuture;
|
||||
use tracing::trace;
|
||||
|
||||
/// Types of process signals.
|
||||
@@ -12,28 +13,51 @@ use tracing::trace;
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[allow(dead_code)] // variants are never constructed on non-unix
|
||||
pub(crate) enum SignalKind {
|
||||
/// `SIGINT`
|
||||
Int,
|
||||
/// Cancellation token or channel.
|
||||
Cancel,
|
||||
|
||||
/// `SIGTERM`
|
||||
Term,
|
||||
/// OS `SIGINT`.
|
||||
OsInt,
|
||||
|
||||
/// `SIGQUIT`
|
||||
Quit,
|
||||
/// OS `SIGTERM`.
|
||||
OsTerm,
|
||||
|
||||
/// OS `SIGQUIT`.
|
||||
OsQuit,
|
||||
}
|
||||
|
||||
impl fmt::Display for SignalKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
SignalKind::Int => "SIGINT",
|
||||
SignalKind::Term => "SIGTERM",
|
||||
SignalKind::Quit => "SIGQUIT",
|
||||
SignalKind::Cancel => "Cancellation token or channel",
|
||||
SignalKind::OsInt => "SIGINT",
|
||||
SignalKind::OsTerm => "SIGTERM",
|
||||
SignalKind::OsQuit => "SIGQUIT",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) enum StopSignal {
|
||||
/// OS signal handling is configured.
|
||||
Os(OsSignals),
|
||||
|
||||
/// Cancellation token or channel.
|
||||
Cancel(BoxFuture<'static, ()>),
|
||||
}
|
||||
|
||||
impl Future for StopSignal {
|
||||
type Output = SignalKind;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match self.get_mut() {
|
||||
StopSignal::Os(os_signals) => pin!(os_signals).poll(cx),
|
||||
StopSignal::Cancel(cancel) => pin!(cancel).poll(cx).map(|()| SignalKind::Cancel),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process signal listener.
|
||||
pub(crate) struct Signals {
|
||||
pub(crate) struct OsSignals {
|
||||
#[cfg(not(unix))]
|
||||
signals: futures_core::future::BoxFuture<'static, std::io::Result<()>>,
|
||||
|
||||
@@ -41,14 +65,14 @@ pub(crate) struct Signals {
|
||||
signals: Vec<(SignalKind, actix_rt::signal::unix::Signal)>,
|
||||
}
|
||||
|
||||
impl Signals {
|
||||
impl OsSignals {
|
||||
/// Constructs an OS signal listening future.
|
||||
pub(crate) fn new() -> Self {
|
||||
trace!("setting up OS signal listener");
|
||||
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
Signals {
|
||||
OsSignals {
|
||||
signals: Box::pin(actix_rt::signal::ctrl_c()),
|
||||
}
|
||||
}
|
||||
@@ -58,9 +82,9 @@ impl Signals {
|
||||
use actix_rt::signal::unix;
|
||||
|
||||
let sig_map = [
|
||||
(unix::SignalKind::interrupt(), SignalKind::Int),
|
||||
(unix::SignalKind::terminate(), SignalKind::Term),
|
||||
(unix::SignalKind::quit(), SignalKind::Quit),
|
||||
(unix::SignalKind::interrupt(), SignalKind::OsInt),
|
||||
(unix::SignalKind::terminate(), SignalKind::OsTerm),
|
||||
(unix::SignalKind::quit(), SignalKind::OsQuit),
|
||||
];
|
||||
|
||||
let signals = sig_map
|
||||
@@ -68,29 +92,27 @@ impl Signals {
|
||||
.filter_map(|(kind, sig)| {
|
||||
unix::signal(*kind)
|
||||
.map(|tokio_sig| (*sig, tokio_sig))
|
||||
.map_err(|e| {
|
||||
.map_err(|err| {
|
||||
tracing::error!(
|
||||
"can not initialize stream handler for {:?} err: {}",
|
||||
sig,
|
||||
e
|
||||
"can not initialize stream handler for {sig:?} err: {err}",
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Signals { signals }
|
||||
OsSignals { signals }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for Signals {
|
||||
impl Future for OsSignals {
|
||||
type Output = SignalKind;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
self.signals.as_mut().poll(cx).map(|_| SignalKind::Int)
|
||||
self.signals.as_mut().poll(cx).map(|_| SignalKind::OsInt)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
@@ -106,3 +128,10 @@ impl Future for Signals {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
static_assertions::assert_impl_all!(StopSignal: Send, Unpin);
|
||||
}
|
||||
|
@@ -105,9 +105,9 @@ impl From<StdUnixListener> for MioListener {
|
||||
impl fmt::Debug for MioListener {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
MioListener::Tcp(ref lst) => write!(f, "{:?}", lst),
|
||||
MioListener::Tcp(ref lst) => write!(f, "{lst:?}"),
|
||||
#[cfg(unix)]
|
||||
MioListener::Uds(ref lst) => write!(f, "{:?}", lst),
|
||||
MioListener::Uds(ref lst) => write!(f, "{lst:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,9 +115,9 @@ impl fmt::Debug for MioListener {
|
||||
impl fmt::Display for MioListener {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
MioListener::Tcp(ref lst) => write!(f, "{:?}", lst),
|
||||
MioListener::Tcp(ref lst) => write!(f, "{lst:?}"),
|
||||
#[cfg(unix)]
|
||||
MioListener::Uds(ref lst) => write!(f, "{:?}", lst),
|
||||
MioListener::Uds(ref lst) => write!(f, "{lst:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,16 +126,16 @@ pub(crate) enum SocketAddr {
|
||||
Unknown,
|
||||
Tcp(StdSocketAddr),
|
||||
#[cfg(unix)]
|
||||
Uds(mio::net::SocketAddr),
|
||||
Uds(std::os::unix::net::SocketAddr),
|
||||
}
|
||||
|
||||
impl fmt::Display for SocketAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Self::Unknown => write!(f, "Unknown SocketAddr"),
|
||||
Self::Tcp(ref addr) => write!(f, "{}", addr),
|
||||
Self::Tcp(ref addr) => write!(f, "{addr}"),
|
||||
#[cfg(unix)]
|
||||
Self::Uds(ref addr) => write!(f, "{:?}", addr),
|
||||
Self::Uds(ref addr) => write!(f, "{addr:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,9 +144,9 @@ impl fmt::Debug for SocketAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Self::Unknown => write!(f, "Unknown SocketAddr"),
|
||||
Self::Tcp(ref addr) => write!(f, "{:?}", addr),
|
||||
Self::Tcp(ref addr) => write!(f, "{addr:?}"),
|
||||
#[cfg(unix)]
|
||||
Self::Uds(ref addr) => write!(f, "{:?}", addr),
|
||||
Self::Uds(ref addr) => write!(f, "{addr:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,6 +160,7 @@ pub enum MioStream {
|
||||
|
||||
/// Helper trait for converting a Mio stream into a Tokio stream.
|
||||
pub trait FromStream: Sized {
|
||||
/// Creates stream from a `mio` stream.
|
||||
fn from_mio(sock: MioStream) -> io::Result<Self>;
|
||||
}
|
||||
|
||||
@@ -265,14 +266,14 @@ mod tests {
|
||||
#[test]
|
||||
fn socket_addr() {
|
||||
let addr = SocketAddr::Tcp("127.0.0.1:8080".parse().unwrap());
|
||||
assert!(format!("{:?}", addr).contains("127.0.0.1:8080"));
|
||||
assert_eq!(format!("{}", addr), "127.0.0.1:8080");
|
||||
assert!(format!("{addr:?}").contains("127.0.0.1:8080"));
|
||||
assert_eq!(format!("{addr}"), "127.0.0.1:8080");
|
||||
|
||||
let addr: StdSocketAddr = "127.0.0.1:0".parse().unwrap();
|
||||
let lst = create_mio_tcp_listener(addr, 128, &MpTcp::Disabled).unwrap();
|
||||
let lst = MioListener::Tcp(lst);
|
||||
assert!(format!("{:?}", lst).contains("TcpListener"));
|
||||
assert!(format!("{}", lst).contains("127.0.0.1"));
|
||||
assert!(format!("{lst:?}").contains("TcpListener"));
|
||||
assert!(format!("{lst}").contains("127.0.0.1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -282,12 +283,12 @@ mod tests {
|
||||
if let Ok(socket) = MioUnixListener::bind("/tmp/sock.xxxxx") {
|
||||
let addr = socket.local_addr().expect("Couldn't get local address");
|
||||
let a = SocketAddr::Uds(addr);
|
||||
assert!(format!("{:?}", a).contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{}", a).contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{a:?}").contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{a}").contains("/tmp/sock.xxxxx"));
|
||||
|
||||
let lst = MioListener::Uds(socket);
|
||||
assert!(format!("{:?}", lst).contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{}", lst).contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{lst:?}").contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{lst}").contains("/tmp/sock.xxxxx"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -123,7 +123,9 @@ impl TestServerHandle {
|
||||
|
||||
/// Connect to server, returning a Tokio `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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -52,7 +52,7 @@ impl WakerQueue {
|
||||
|
||||
waker
|
||||
.wake()
|
||||
.unwrap_or_else(|e| panic!("can not wake up Accept Poll: {}", e));
|
||||
.unwrap_or_else(|err| panic!("can not wake up Accept Poll: {err}"));
|
||||
}
|
||||
|
||||
/// Get a MutexGuard of the waker queue.
|
||||
@@ -62,7 +62,7 @@ impl WakerQueue {
|
||||
|
||||
/// Reset the waker queue so it does not grow infinitely.
|
||||
pub(crate) fn reset(queue: &mut VecDeque<WakerInterest>) {
|
||||
std::mem::swap(&mut VecDeque::<WakerInterest>::with_capacity(16), queue);
|
||||
*queue = VecDeque::<WakerInterest>::with_capacity(16);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -325,7 +325,7 @@ impl ServerWorker {
|
||||
// no actix system
|
||||
(None, Some(rt_handle)) => {
|
||||
std::thread::Builder::new()
|
||||
.name(format!("actix-server worker {}", idx))
|
||||
.name(format!("actix-server worker {idx}"))
|
||||
.spawn(move || {
|
||||
let (worker_stopped_tx, worker_stopped_rx) = oneshot::channel();
|
||||
|
||||
@@ -341,11 +341,10 @@ impl ServerWorker {
|
||||
Ok((token, svc)) => services.push((idx, token, svc)),
|
||||
|
||||
Err(err) => {
|
||||
error!("can not start worker: {:?}", err);
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("can not start server service {}", idx),
|
||||
));
|
||||
error!("can not start worker: {err:?}");
|
||||
return Err(io::Error::other(format!(
|
||||
"can not start server service {idx}",
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -440,13 +439,12 @@ impl ServerWorker {
|
||||
Ok((token, svc)) => services.push((idx, token, svc)),
|
||||
|
||||
Err(err) => {
|
||||
error!("can not start worker: {:?}", err);
|
||||
error!("can not start worker: {err:?}");
|
||||
Arbiter::current().stop();
|
||||
factory_tx
|
||||
.send(Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("can not start server service {}", idx),
|
||||
)))
|
||||
.send(Err(io::Error::other(format!(
|
||||
"can not start server service {idx}",
|
||||
))))
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#![allow(clippy::let_underscore_future)]
|
||||
#![allow(clippy::let_underscore_future, missing_docs)]
|
||||
|
||||
use std::{
|
||||
net,
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::net;
|
||||
|
||||
use actix_rt::net::TcpStream;
|
||||
@@ -69,5 +71,7 @@ async fn new_with_builder() {
|
||||
srv.connect().unwrap();
|
||||
|
||||
// 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,28 +1,32 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2023-xx-xx
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.65.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 2.0.2 - 2021-12-18
|
||||
## 2.0.3
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||
|
||||
## 2.0.2
|
||||
|
||||
- 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
|
||||
|
||||
## 2.0.1 - 2021-10-11
|
||||
## 2.0.1
|
||||
|
||||
- Documentation fix. [#388]
|
||||
|
||||
[#388]: https://github.com/actix/actix-net/pull/388
|
||||
|
||||
## 2.0.0 - 2021-04-16
|
||||
## 2.0.0
|
||||
|
||||
- Removed pipeline and related structs/functions. [#335]
|
||||
|
||||
[#335]: https://github.com/actix/actix-net/pull/335
|
||||
|
||||
## 2.0.0-beta.5 - 2021-03-15
|
||||
## 2.0.0-beta.5
|
||||
|
||||
- Add default `Service` trait impl for `Rc<S: Service>` and `&S: Service`. [#288]
|
||||
- Add `boxed::rc_service` function for constructing `boxed::RcService` type [#290]
|
||||
@@ -30,7 +34,7 @@
|
||||
[#288]: https://github.com/actix/actix-net/pull/288
|
||||
[#290]: https://github.com/actix/actix-net/pull/290
|
||||
|
||||
## 2.0.0-beta.4 - 2021-02-04
|
||||
## 2.0.0-beta.4
|
||||
|
||||
- `Service::poll_ready` and `Service::call` receive `&self`. [#247]
|
||||
- `apply_fn` and `apply_fn_factory` now receive `Fn(Req, &Service)` function type. [#247]
|
||||
@@ -39,17 +43,17 @@
|
||||
|
||||
[#247]: https://github.com/actix/actix-net/pull/247
|
||||
|
||||
## 2.0.0-beta.3 - 2021-01-09
|
||||
## 2.0.0-beta.3
|
||||
|
||||
- The `forward_ready!` macro converts errors. [#246]
|
||||
|
||||
[#246]: https://github.com/actix/actix-net/pull/246
|
||||
|
||||
## 2.0.0-beta.2 - 2021-01-03
|
||||
## 2.0.0-beta.2
|
||||
|
||||
- Remove redundant type parameter from `map_config`.
|
||||
|
||||
## 2.0.0-beta.1 - 2020-12-28
|
||||
## 2.0.0-beta.1
|
||||
|
||||
- `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]
|
||||
@@ -62,82 +66,82 @@
|
||||
[#233]: https://github.com/actix/actix-net/pull/233
|
||||
[#235]: https://github.com/actix/actix-net/pull/235
|
||||
|
||||
## 1.0.6 - 2020-08-09
|
||||
## 1.0.6
|
||||
|
||||
- Removed unsound custom Cell implementation that allowed obtaining several mutable references to the same data, which is undefined behavior in Rust and could lead to violations of memory safety. External code could obtain several mutable references to the same data through service combinators. Attempts to acquire several mutable references to the same data will instead result in a panic.
|
||||
|
||||
## 1.0.5 - 2020-01-16
|
||||
## 1.0.5
|
||||
|
||||
- Fixed unsoundness in .and_then()/.then() service combinators.
|
||||
|
||||
## 1.0.4 - 2020-01-15
|
||||
## 1.0.4
|
||||
|
||||
- Revert 1.0.3 change
|
||||
|
||||
## 1.0.3 - 2020-01-15
|
||||
## 1.0.3
|
||||
|
||||
- Fixed unsoundness in `AndThenService` impl.
|
||||
|
||||
## 1.0.2 - 2020-01-08
|
||||
## 1.0.2
|
||||
|
||||
- Add `into_service` helper function.
|
||||
|
||||
## 1.0.1 - 2019-12-22
|
||||
## 1.0.1
|
||||
|
||||
- `map_config()` and `unit_config()` now accept `IntoServiceFactory` type.
|
||||
|
||||
## 1.0.0 - 2019-12-11
|
||||
## 1.0.0
|
||||
|
||||
- Add Clone impl for Apply service
|
||||
|
||||
## 1.0.0-alpha.4 - 2019-12-08
|
||||
## 1.0.0-alpha.4
|
||||
|
||||
- Renamed `service_fn` to `fn_service`
|
||||
- Renamed `factory_fn` to `fn_factory`
|
||||
- Renamed `factory_fn_cfg` to `fn_factory_with_config`
|
||||
|
||||
## 1.0.0-alpha.3 - 2019-12-06
|
||||
## 1.0.0-alpha.3
|
||||
|
||||
- Add missing Clone impls
|
||||
- Restore `Transform::map_init_err()` combinator
|
||||
- Restore `Service/Factory::apply_fn()` in form of `Pipeline/Factory::and_then_apply_fn()`
|
||||
- Optimize service combinators and futures memory layout
|
||||
|
||||
## 1.0.0-alpha.2 - 2019-12-02
|
||||
## 1.0.0-alpha.2
|
||||
|
||||
- Use owned config value for service factory
|
||||
- Renamed BoxedNewService/BoxedService to BoxServiceFactory/BoxService
|
||||
|
||||
## 1.0.0-alpha.1 - 2019-11-25
|
||||
## 1.0.0-alpha.1
|
||||
|
||||
- Migrated to `std::future`
|
||||
- `NewService` renamed to `ServiceFactory`
|
||||
- Added `pipeline` and `pipeline_factory` function
|
||||
|
||||
## 0.4.2 - 2019-08-27
|
||||
## 0.4.2
|
||||
|
||||
- Check service readiness for `new_apply_cfg` combinator
|
||||
|
||||
## 0.4.1 - 2019-06-06
|
||||
## 0.4.1
|
||||
|
||||
- Add `new_apply_cfg` function
|
||||
|
||||
## 0.4.0 - 2019-05-12
|
||||
## 0.4.0
|
||||
|
||||
- Add `NewService::map_config` and `NewService::unit_config` combinators.
|
||||
- Use associated type for `NewService` config.
|
||||
- Change `apply_cfg` function.
|
||||
- Renamed helper functions.
|
||||
|
||||
## 0.3.6 - 2019-04-07
|
||||
## 0.3.6
|
||||
|
||||
- Poll boxed service call result immediately
|
||||
|
||||
## 0.3.5 - 2019-03-29
|
||||
## 0.3.5
|
||||
|
||||
- Add `impl<S: Service> Service for Rc<RefCell<S>>`.
|
||||
|
||||
## 0.3.4 - 2019-03-12
|
||||
## 0.3.4
|
||||
|
||||
- Add `Transform::from_err()` combinator
|
||||
- Add `apply_fn` helper
|
||||
@@ -145,37 +149,37 @@
|
||||
- Add `apply_transform` helper
|
||||
- Add `apply_cfg` helper
|
||||
|
||||
## 0.3.3 - 2019-03-09
|
||||
## 0.3.3
|
||||
|
||||
- Add `ApplyTransform` new service for transform and new service.
|
||||
- Add `NewService::apply_cfg()` combinator, allows to use nested `NewService` with different config parameter.
|
||||
- Revert IntoFuture change
|
||||
|
||||
## 0.3.2 - 2019-03-04
|
||||
## 0.3.2
|
||||
|
||||
- Change `NewService::Future` and `Transform::Future` to the `IntoFuture` trait.
|
||||
- Export `AndThenTransform` type
|
||||
|
||||
## 0.3.1 - 2019-03-04
|
||||
## 0.3.1
|
||||
|
||||
- Simplify Transform trait
|
||||
|
||||
## 0.3.0 - 2019-03-02
|
||||
## 0.3.0
|
||||
|
||||
- Added boxed NewService and Service.
|
||||
- Added `Config` parameter to `NewService` trait.
|
||||
- Added `Config` parameter to `NewTransform` trait.
|
||||
|
||||
## 0.2.2 - 2019-02-19
|
||||
## 0.2.2
|
||||
|
||||
- Added `NewService` impl for `Rc<S> where S: NewService`
|
||||
- Added `NewService` impl for `Arc<S> where S: NewService`
|
||||
|
||||
## 0.2.1 - 2019-02-03
|
||||
## 0.2.1
|
||||
|
||||
- Generalize `.apply` combinator with Transform trait
|
||||
|
||||
## 0.2.0 - 2019-02-01
|
||||
## 0.2.0
|
||||
|
||||
- Use associated type instead of generic for Service definition.
|
||||
- Before:
|
||||
@@ -194,31 +198,31 @@
|
||||
}
|
||||
```
|
||||
|
||||
## 0.1.6 - 2019-01-24
|
||||
## 0.1.6
|
||||
|
||||
- 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>`
|
||||
|
||||
## 0.1.5 - 2019-01-13
|
||||
## 0.1.5
|
||||
|
||||
- Make `Out::Error` convertible from `T::Error` for apply combinator
|
||||
|
||||
## 0.1.4 - 2019-01-11
|
||||
## 0.1.4
|
||||
|
||||
- Use `FnMut` instead of `Fn` for `FnService`
|
||||
|
||||
## 0.1.3 - 2018-12-12
|
||||
## 0.1.3
|
||||
|
||||
- Split service combinators to separate trait
|
||||
|
||||
## 0.1.2 - 2018-12-12
|
||||
## 0.1.2
|
||||
|
||||
- Release future early for `.and_then()` and `.then()` combinators
|
||||
|
||||
## 0.1.1 - 2018-12-09
|
||||
## 0.1.1
|
||||
|
||||
- Added Service impl for `Box<S: Service>`
|
||||
|
||||
## 0.1.0 - 2018-12-09
|
||||
## 0.1.0
|
||||
|
||||
- Initial import
|
||||
|
@@ -1,10 +1,7 @@
|
||||
[package]
|
||||
name = "actix-service"
|
||||
version = "2.0.2"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Rob Ede <robjtede@icloud.com>",
|
||||
]
|
||||
version = "2.0.3"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||
description = "Service trait and combinators for representing asynchronous request/response operations."
|
||||
keywords = ["network", "framework", "async", "futures", "service"]
|
||||
categories = ["network-programming", "asynchronous", "no-std"]
|
||||
@@ -15,10 +12,12 @@ rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
futures-core = { version = "0.3.17", default-features = false }
|
||||
paste = "1"
|
||||
pin-project-lite = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "2"
|
||||
actix-utils = "3"
|
||||
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.
|
||||
|
||||
[](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://deps.rs/crate/actix-service/2.0.2)
|
||||

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

|
||||
[](https://discord.gg/NWpN5mmg3x)
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#![allow(missing_docs)]
|
||||
|
||||
use std::{future::Future, sync::mpsc, time::Duration};
|
||||
|
||||
async fn oracle<F, Fut>(f: F) -> (u32, u32)
|
||||
|
@@ -208,15 +208,13 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::task::Poll;
|
||||
|
||||
use futures_util::future::lazy;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
ok,
|
||||
pipeline::{pipeline, pipeline_factory},
|
||||
Ready, Service, ServiceFactory,
|
||||
Ready,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@@ -3,36 +3,38 @@
|
||||
use alloc::{boxed::Box, rc::Rc};
|
||||
use core::{future::Future, pin::Pin};
|
||||
|
||||
use paste::paste;
|
||||
|
||||
use crate::{Service, ServiceFactory};
|
||||
|
||||
/// A boxed future with no send bound or lifetime parameters.
|
||||
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
|
||||
|
||||
macro_rules! service_object {
|
||||
($name: ident, $type: tt, $fn_name: ident) => {
|
||||
paste! {
|
||||
#[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>>>,
|
||||
>;
|
||||
/// Type alias for service trait object using [`Box`].
|
||||
pub type BoxService<Req, Res, Err> =
|
||||
Box<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>;
|
||||
|
||||
#[doc = "Wraps service as a trait object using [`" $name "`]."]
|
||||
pub fn $fn_name<S, Req>(service: S) -> $name<Req, S::Response, S::Error>
|
||||
where
|
||||
S: Service<Req> + 'static,
|
||||
Req: 'static,
|
||||
S::Future: 'static,
|
||||
{
|
||||
$type::new(ServiceWrapper::new(service))
|
||||
}
|
||||
}
|
||||
};
|
||||
/// Wraps service as a trait object using [`BoxService`].
|
||||
pub fn service<S, Req>(service: S) -> BoxService<Req, S::Response, S::Error>
|
||||
where
|
||||
S: Service<Req> + 'static,
|
||||
Req: 'static,
|
||||
S::Future: 'static,
|
||||
{
|
||||
Box::new(ServiceWrapper::new(service))
|
||||
}
|
||||
|
||||
service_object!(BoxService, Box, service);
|
||||
service_object!(RcService, Rc, rc_service);
|
||||
/// Type alias for service trait object using [`Rc`].
|
||||
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> {
|
||||
inner: S,
|
||||
|
@@ -44,7 +44,7 @@ pub trait ServiceExt<Req>: Service<Req> {
|
||||
/// Call another service after call to this one has resolved successfully.
|
||||
///
|
||||
/// This function can be used to chain two services together and ensure that the second service
|
||||
/// isn't called until call to the fist service have finished. Result of the call to the first
|
||||
/// 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.
|
||||
///
|
||||
/// Note that this function consumes the receiving service and returns a wrapped version of it.
|
||||
|
@@ -351,7 +351,6 @@ mod tests {
|
||||
use futures_util::future::lazy;
|
||||
|
||||
use super::*;
|
||||
use crate::{ok, Service, ServiceFactory};
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_fn_service() {
|
||||
|
@@ -1,8 +1,6 @@
|
||||
//! See [`Service`] docs for information on this crate's foundational trait.
|
||||
|
||||
#![no_std]
|
||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
||||
#![warn(future_incompatible, missing_docs)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
@@ -202,9 +202,7 @@ mod tests {
|
||||
use futures_util::future::lazy;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt,
|
||||
};
|
||||
use crate::{ok, IntoServiceFactory, Ready, ServiceExt, ServiceFactoryExt};
|
||||
|
||||
struct Srv;
|
||||
|
||||
|
@@ -205,9 +205,7 @@ mod tests {
|
||||
use futures_util::future::lazy;
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
err, ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt,
|
||||
};
|
||||
use crate::{err, ok, IntoServiceFactory, Ready, ServiceExt, ServiceFactoryExt};
|
||||
|
||||
struct Srv;
|
||||
|
||||
|
@@ -52,7 +52,7 @@ where
|
||||
/// Call another service after call to this one has resolved successfully.
|
||||
///
|
||||
/// This function can be used to chain two services together and ensure that
|
||||
/// the second service isn't called until call to the fist service have
|
||||
/// 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
|
||||
/// input parameter for the second service's call.
|
||||
///
|
||||
|
@@ -1,4 +1,4 @@
|
||||
//! When MSRV is 1.48, replace with `core::future::Ready` and `core::future::ready()`.
|
||||
//! When MSRV is 1.82, replace with `core::future::Ready` and `core::future::ready()`.
|
||||
|
||||
use core::{
|
||||
future::Future,
|
||||
|
@@ -226,9 +226,9 @@ mod tests {
|
||||
use actix_utils::future::{ready, Ready};
|
||||
|
||||
use super::*;
|
||||
use crate::Service;
|
||||
|
||||
// pseudo-doctest for Transform trait
|
||||
#[allow(unused)]
|
||||
pub struct TimeoutTransform {
|
||||
timeout: Duration,
|
||||
}
|
||||
@@ -250,6 +250,7 @@ mod tests {
|
||||
}
|
||||
|
||||
// pseudo-doctest for Transform trait
|
||||
#[allow(unused)]
|
||||
pub struct Timeout<S> {
|
||||
service: S,
|
||||
_timeout: Duration,
|
||||
|
@@ -2,6 +2,24 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 3.4.0
|
||||
|
||||
- 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.
|
||||
@@ -9,8 +27,8 @@
|
||||
## 3.1.0
|
||||
|
||||
- Support Rustls v0.21.
|
||||
- Added `{accept, connect}::rustls_0_21` modules.
|
||||
- Added `{accept, connect}::rustls_0_20` alias for `{accept, connect}::rustls` modules.
|
||||
- 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
|
||||
|
82
actix-tls/Cargo.toml
Executable file → Normal file
82
actix-tls/Cargo.toml
Executable file → Normal file
@@ -1,10 +1,7 @@
|
||||
[package]
|
||||
name = "actix-tls"
|
||||
version = "3.1.1"
|
||||
authors = [
|
||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||
"Rob Ede <robjtede@icloud.com>",
|
||||
]
|
||||
version = "3.4.0"
|
||||
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||
description = "TLS acceptor and connector services for Actix ecosystem"
|
||||
keywords = ["network", "tls", "ssl", "async", "transport"]
|
||||
repository = "https://github.com/actix/actix-net.git"
|
||||
@@ -17,10 +14,13 @@ rust-version.workspace = true
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[package.metadata.cargo_check_external_types]
|
||||
allowed_external_types = ["actix_service::*", "actix_utils::*", "futures_core::*", "tokio::*"]
|
||||
|
||||
[package.metadata.cargo-machete]
|
||||
ignored = [
|
||||
"rustls_021", # specified to force version with add_trust_anchors method
|
||||
"rustls_webpki_0101", # specified to force secure version
|
||||
"rustls_021", # specified to force version with add_trust_anchors method
|
||||
"rustls_webpki_0101", # specified to force secure version
|
||||
]
|
||||
|
||||
[features]
|
||||
@@ -33,67 +33,103 @@ accept = []
|
||||
connect = []
|
||||
|
||||
# use openssl impls
|
||||
openssl = ["tls-openssl", "tokio-openssl"]
|
||||
openssl = ["dep:tls-openssl", "dep:tokio-openssl"]
|
||||
|
||||
# alias for backwards compat
|
||||
rustls = ["rustls-0_20"]
|
||||
|
||||
# use rustls v0.20 impls
|
||||
rustls-0_20 = ["tokio-rustls-023", "webpki-roots-022"]
|
||||
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 = ["tokio-rustls-024", "webpki-roots-025"]
|
||||
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
|
||||
native-tls = ["tokio-native-tls"]
|
||||
native-tls = ["dep:tokio-native-tls"]
|
||||
|
||||
# support http::Uri as connect address
|
||||
uri = ["http"]
|
||||
uri = ["dep:http-0_2", "dep:http-1"]
|
||||
|
||||
[dependencies]
|
||||
actix-rt = { version = "2.2", default-features = false }
|
||||
actix-service = "2"
|
||||
actix-utils = "3"
|
||||
|
||||
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||
impl-more = "0.1"
|
||||
pin-project-lite = "0.2.7"
|
||||
tokio = "1.23.1"
|
||||
tokio = "1.44.2"
|
||||
tokio-util = "0.7"
|
||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||
|
||||
# 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
|
||||
tls-openssl = { package = "openssl", version = "0.10.55", optional = true }
|
||||
tokio-openssl = { version = "0.6", optional = true }
|
||||
|
||||
# rustls PKI types
|
||||
rustls-pki-types-1 = { package = "rustls-pki-types", version = "1", optional = true }
|
||||
|
||||
# rustls v0.20
|
||||
tokio-rustls-023 = { package = "tokio-rustls", version = "0.23", optional = true }
|
||||
webpki-roots-022 = { package = "webpki-roots", version = "0.22", optional = true }
|
||||
|
||||
# rustls v0.21
|
||||
rustls-021 = { package = "rustls", version = "0.21.6" }
|
||||
rustls-webpki-0101 = { package = "rustls-webpki", version = "0.101.4" }
|
||||
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
|
||||
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]
|
||||
actix-codec = "0.5"
|
||||
actix-rt = "2.2"
|
||||
actix-server = "2"
|
||||
bytes = "1"
|
||||
env_logger = "0.10"
|
||||
futures-util = { version = "0.3.17", default-features = false, features = ["sink"] }
|
||||
rcgen = "0.11"
|
||||
rustls-pemfile = "1"
|
||||
tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", features = ["dangerous_configuration"] }
|
||||
itertools = "0.14"
|
||||
pretty_env_logger = "0.5"
|
||||
rcgen = "0.13"
|
||||
rustls-pemfile = "2"
|
||||
tokio-rustls-026 = { package = "tokio-rustls", version = "0.26" }
|
||||
trust-dns-resolver = "0.23"
|
||||
|
||||
[[example]]
|
||||
name = "accept-rustls"
|
||||
required-features = ["accept", "rustls-0_21"]
|
||||
required-features = ["accept", "rustls-0_23"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
21
actix-tls/README.md
Normal file
21
actix-tls/README.md
Normal file
@@ -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,11 +15,8 @@
|
||||
//! http --verify=false https://127.0.0.1:8443
|
||||
//! ```
|
||||
|
||||
#[rustfmt::skip]
|
||||
// this `use` is only exists because of how we have organised the crate
|
||||
// it is not necessary for your actual code; you should import from `rustls` normally
|
||||
use tokio_rustls_024::rustls;
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{self, BufReader},
|
||||
@@ -33,15 +30,19 @@ use std::{
|
||||
use actix_rt::net::TcpStream;
|
||||
use actix_server::Server;
|
||||
use actix_service::ServiceFactoryExt as _;
|
||||
use actix_tls::accept::rustls_0_21::{Acceptor as RustlsAcceptor, TlsStream};
|
||||
use actix_tls::accept::rustls_0_23::{Acceptor as RustlsAcceptor, TlsStream};
|
||||
use futures_util::future::ok;
|
||||
use rustls::{server::ServerConfig, Certificate, PrivateKey};
|
||||
use itertools::Itertools as _;
|
||||
use rustls::server::ServerConfig;
|
||||
use rustls_pemfile::{certs, rsa_private_keys};
|
||||
use rustls_pki_types_1::PrivateKeyDer;
|
||||
use tokio_rustls_026::rustls;
|
||||
use tracing::info;
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> 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 root_path = env!("CARGO_MANIFEST_DIR")
|
||||
.parse::<PathBuf>()
|
||||
@@ -54,17 +55,15 @@ async fn main() -> io::Result<()> {
|
||||
let cert_file = &mut BufReader::new(File::open(cert_path).unwrap());
|
||||
let key_file = &mut BufReader::new(File::open(key_path).unwrap());
|
||||
|
||||
let cert_chain = certs(cert_file)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(Certificate)
|
||||
.collect();
|
||||
let mut keys = rsa_private_keys(key_file).unwrap();
|
||||
let cert_chain = certs(cert_file);
|
||||
let mut keys = rsa_private_keys(key_file);
|
||||
|
||||
let tls_config = ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.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();
|
||||
|
||||
let tls_acceptor = RustlsAcceptor::new(tls_config);
|
||||
@@ -81,7 +80,7 @@ async fn main() -> io::Result<()> {
|
||||
// Set up TLS service factory
|
||||
tls_acceptor
|
||||
.clone()
|
||||
.map_err(|err| println!("Rustls error: {:?}", err))
|
||||
.map_err(|err| println!("Rustls error: {err:?}"))
|
||||
.and_then(move |stream: TlsStream<TcpStream>| {
|
||||
let num = count.fetch_add(1, Ordering::Relaxed);
|
||||
info!("[{}] Got TLS connection: {:?}", num, &*stream);
|
||||
|
@@ -22,6 +22,12 @@ 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")]
|
||||
pub mod native_tls;
|
||||
|
||||
@@ -31,6 +37,8 @@ pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
|
||||
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 =
|
||||
|
@@ -23,7 +23,7 @@ use actix_utils::{
|
||||
};
|
||||
use pin_project_lite::pin_project;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tokio_rustls::{rustls::ServerConfig, Accept, TlsAcceptor};
|
||||
use tokio_rustls::{Accept, TlsAcceptor};
|
||||
use tokio_rustls_023 as tokio_rustls;
|
||||
|
||||
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
||||
@@ -92,13 +92,13 @@ impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
||||
|
||||
/// Accept TLS connections via the `rustls` crate.
|
||||
pub struct Acceptor {
|
||||
config: Arc<ServerConfig>,
|
||||
config: Arc<reexports::ServerConfig>,
|
||||
handshake_timeout: Duration,
|
||||
}
|
||||
|
||||
impl Acceptor {
|
||||
/// Constructs `rustls` based acceptor service factory.
|
||||
pub fn new(config: ServerConfig) -> Self {
|
||||
pub fn new(config: reexports::ServerConfig) -> Self {
|
||||
Acceptor {
|
||||
config: Arc::new(config),
|
||||
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
||||
|
@@ -23,7 +23,7 @@ use actix_utils::{
|
||||
};
|
||||
use pin_project_lite::pin_project;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tokio_rustls::{rustls::ServerConfig, Accept, TlsAcceptor};
|
||||
use tokio_rustls::{Accept, TlsAcceptor};
|
||||
use tokio_rustls_024 as tokio_rustls;
|
||||
|
||||
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
||||
@@ -92,13 +92,13 @@ impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
||||
|
||||
/// Accept TLS connections via the `rustls` crate.
|
||||
pub struct Acceptor {
|
||||
config: Arc<ServerConfig>,
|
||||
config: Arc<reexports::ServerConfig>,
|
||||
handshake_timeout: Duration,
|
||||
}
|
||||
|
||||
impl Acceptor {
|
||||
/// Constructs `rustls` based acceptor service factory.
|
||||
pub fn new(config: ServerConfig) -> Self {
|
||||
pub fn new(config: reexports::ServerConfig) -> Self {
|
||||
Acceptor {
|
||||
config: Arc::new(config),
|
||||
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
||||
|
198
actix-tls/src/accept/rustls_0_22.rs
Normal file
198
actix-tls/src/accept/rustls_0_22.rs
Normal file
@@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
198
actix-tls/src/accept/rustls_0_23.rs
Normal file
198
actix-tls/src/accept/rustls_0_23.rs
Normal file
@@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
@@ -118,6 +118,7 @@ impl<R: Host> ConnectInfo<R> {
|
||||
/// let mut addrs = conn.addrs();
|
||||
/// assert_eq!(addrs.next().unwrap(), addr);
|
||||
/// ```
|
||||
#[allow(clippy::implied_bounds_in_impls)]
|
||||
pub fn addrs(
|
||||
&self,
|
||||
) -> impl Iterator<Item = SocketAddr>
|
||||
@@ -149,6 +150,7 @@ impl<R: Host> ConnectInfo<R> {
|
||||
/// let mut addrs = conn.take_addrs();
|
||||
/// assert_eq!(addrs.next().unwrap(), addr);
|
||||
/// ```
|
||||
#[allow(clippy::implied_bounds_in_impls)]
|
||||
pub fn take_addrs(
|
||||
&mut self,
|
||||
) -> impl Iterator<Item = SocketAddr>
|
||||
|
@@ -27,16 +27,31 @@ mod uri;
|
||||
#[cfg(feature = "openssl")]
|
||||
pub mod openssl;
|
||||
|
||||
#[cfg(feature = "rustls-0_20")]
|
||||
#[cfg(any(
|
||||
feature = "rustls-0_20-webpki-roots",
|
||||
feature = "rustls-0_20-native-roots",
|
||||
))]
|
||||
pub mod rustls_0_20;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "rustls-0_20")]
|
||||
#[cfg(any(
|
||||
feature = "rustls-0_20-webpki-roots",
|
||||
feature = "rustls-0_20-native-roots",
|
||||
))]
|
||||
pub use rustls_0_20 as rustls;
|
||||
|
||||
#[cfg(feature = "rustls-0_21")]
|
||||
#[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")]
|
||||
pub mod native_tls;
|
||||
|
||||
|
@@ -81,9 +81,9 @@ where
|
||||
trace!("TLS handshake success: {:?}", stream.hostname());
|
||||
stream.replace_io(res).1
|
||||
})
|
||||
.map_err(|e| {
|
||||
trace!("TLS handshake error: {:?}", e);
|
||||
io::Error::new(io::ErrorKind::Other, format!("{}", e))
|
||||
.map_err(|err| {
|
||||
trace!("TLS handshake error: {err:?}");
|
||||
io::Error::other(format!("{err}"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@@ -141,10 +141,7 @@ where
|
||||
}
|
||||
Err(err) => {
|
||||
trace!("TLS handshake error: {:?}", err);
|
||||
Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("{}", err),
|
||||
)))
|
||||
Poll::Ready(Err(io::Error::other(format!("{err}"))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@
|
||||
//! See [`TlsConnector`] for main connector service factory docs.
|
||||
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
future::Future,
|
||||
io,
|
||||
pin::Pin,
|
||||
@@ -17,7 +16,7 @@ use actix_utils::future::{ok, Ready};
|
||||
use futures_core::ready;
|
||||
use tokio_rustls::{
|
||||
client::TlsStream as AsyncTlsStream,
|
||||
rustls::{client::ServerName, ClientConfig, OwnedTrustAnchor, RootCertStore},
|
||||
rustls::{client::ServerName, ClientConfig, RootCertStore},
|
||||
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
|
||||
};
|
||||
use tokio_rustls_023 as tokio_rustls;
|
||||
@@ -25,17 +24,40 @@ use tokio_rustls_023 as tokio_rustls;
|
||||
use crate::connect::{Connection, Host};
|
||||
|
||||
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_023::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
||||
#[cfg(feature = "rustls-0_20-webpki-roots")]
|
||||
pub use webpki_roots_022::TLS_SERVER_ROOTS;
|
||||
}
|
||||
|
||||
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
|
||||
pub fn webpki_roots_cert_store() -> RootCertStore {
|
||||
/// 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();
|
||||
|
||||
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.
|
||||
#[cfg(feature = "rustls-0_20-webpki-roots")]
|
||||
pub fn webpki_roots_cert_store() -> RootCertStore {
|
||||
use tokio_rustls_023::rustls;
|
||||
|
||||
let mut root_certs = RootCertStore::empty();
|
||||
|
||||
for cert in webpki_roots_022::TLS_SERVER_ROOTS.0 {
|
||||
let cert = OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
let cert = rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
cert.subject,
|
||||
cert.spki,
|
||||
cert.name_constraints,
|
||||
@@ -43,6 +65,7 @@ pub fn webpki_roots_cert_store() -> RootCertStore {
|
||||
let certs = vec![cert].into_iter();
|
||||
root_certs.add_server_trust_anchors(certs);
|
||||
}
|
||||
|
||||
root_certs
|
||||
}
|
||||
|
||||
@@ -136,8 +159,7 @@ where
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match self.get_mut() {
|
||||
Self::InvalidDns => Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
Self::InvalidDns => Poll::Ready(Err(io::Error::other(
|
||||
"Rustls v0.20 can only handle hostname-based connections. Enable the `rustls-0_21` \
|
||||
feature and use the Rustls v0.21 utilities to gain this feature.",
|
||||
))),
|
||||
|
@@ -3,7 +3,6 @@
|
||||
//! See [`TlsConnector`] for main connector service factory docs.
|
||||
|
||||
use std::{
|
||||
convert::TryFrom,
|
||||
future::Future,
|
||||
io,
|
||||
pin::Pin,
|
||||
@@ -17,7 +16,7 @@ use actix_utils::future::{ok, Ready};
|
||||
use futures_core::ready;
|
||||
use tokio_rustls::{
|
||||
client::TlsStream as AsyncTlsStream,
|
||||
rustls::{client::ServerName, ClientConfig, OwnedTrustAnchor, RootCertStore},
|
||||
rustls::{client::ServerName, ClientConfig, RootCertStore},
|
||||
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
|
||||
};
|
||||
use tokio_rustls_024 as tokio_rustls;
|
||||
@@ -25,17 +24,40 @@ use tokio_rustls_024 as tokio_rustls;
|
||||
use crate::connect::{Connection, Host};
|
||||
|
||||
pub mod reexports {
|
||||
//! Re-exports from `rustls` and `webpki_roots` that are useful for connectors.
|
||||
//! 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 standard root certificates from `webpki-roots` crate as a rustls certificate store.
|
||||
pub fn webpki_roots_cert_store() -> RootCertStore {
|
||||
/// 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 = OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
let cert = rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
|
||||
cert.subject,
|
||||
cert.spki,
|
||||
cert.name_constraints,
|
||||
@@ -43,6 +65,7 @@ pub fn webpki_roots_cert_store() -> RootCertStore {
|
||||
let certs = vec![cert].into_iter();
|
||||
root_certs.add_trust_anchors(certs);
|
||||
}
|
||||
|
||||
root_certs
|
||||
}
|
||||
|
||||
|
163
actix-tls/src/connect/rustls_0_22.rs
Normal file
163
actix-tls/src/connect/rustls_0_22.rs
Normal file
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
163
actix-tls/src/connect/rustls_0_23.rs
Normal file
163
actix-tls/src/connect/rustls_0_23.rs
Normal file
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,8 +1,19 @@
|
||||
use http::Uri;
|
||||
|
||||
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 {
|
||||
self.host().unwrap_or("")
|
||||
}
|
||||
|
@@ -1,7 +1,5 @@
|
||||
//! 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_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
|
@@ -3,27 +3,30 @@
|
||||
#![cfg(all(
|
||||
feature = "accept",
|
||||
feature = "connect",
|
||||
feature = "rustls-0_21",
|
||||
feature = "rustls-0_23",
|
||||
feature = "openssl"
|
||||
))]
|
||||
|
||||
use std::{convert::TryFrom, io::Write, sync::Arc};
|
||||
use std::{io::Write as _, sync::Arc};
|
||||
|
||||
use actix_rt::net::TcpStream;
|
||||
use actix_server::TestServer;
|
||||
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 tokio_rustls::rustls::{Certificate, ClientConfig, RootCertStore, ServerName};
|
||||
use tokio_rustls_024 as tokio_rustls;
|
||||
use rustls_pki_types_1::ServerName;
|
||||
use tokio_rustls_026::rustls::RootCertStore;
|
||||
|
||||
fn new_cert_and_key() -> (String, String) {
|
||||
let cert =
|
||||
let rcgen::CertifiedKey { cert, key_pair } =
|
||||
rcgen::generate_simple_self_signed(vec!["127.0.0.1".to_owned(), "localhost".to_owned()])
|
||||
.unwrap();
|
||||
|
||||
let key = cert.serialize_private_key_pem();
|
||||
let cert = cert.serialize_pem().unwrap();
|
||||
let key = key_pair.serialize_pem();
|
||||
let cert = cert.pem();
|
||||
|
||||
(cert, key)
|
||||
}
|
||||
@@ -46,30 +49,48 @@ fn openssl_acceptor(cert: String, key: String) -> tls_openssl::ssl::SslAcceptor
|
||||
builder.build()
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
mod danger {
|
||||
use std::time::SystemTime;
|
||||
|
||||
use tokio_rustls_024::rustls::{
|
||||
self,
|
||||
client::{ServerCertVerified, ServerCertVerifier},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use rustls_pki_types_1::{CertificateDer, ServerName, UnixTime};
|
||||
use tokio_rustls_026::rustls;
|
||||
|
||||
/// Disables certificate verification to allow self-signed certs from rcgen.
|
||||
#[derive(Debug)]
|
||||
pub struct NoCertificateVerification;
|
||||
|
||||
impl ServerCertVerifier for NoCertificateVerification {
|
||||
impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_end_entity: &Certificate,
|
||||
_intermediates: &[Certificate],
|
||||
_server_name: &ServerName,
|
||||
_scts: &mut dyn Iterator<Item = &[u8]>,
|
||||
_ocsp_response: &[u8],
|
||||
_now: SystemTime,
|
||||
) -> Result<ServerCertVerified, rustls::Error> {
|
||||
Ok(ServerCertVerified::assertion())
|
||||
_end_entity: &CertificateDer<'_>,
|
||||
_intermediates: &[CertificateDer<'_>],
|
||||
_server_name: &ServerName<'_>,
|
||||
_ocsp: &[u8],
|
||||
_now: UnixTime,
|
||||
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
|
||||
Ok(rustls::client::danger::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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +98,6 @@ mod danger {
|
||||
#[allow(dead_code)]
|
||||
fn rustls_connector(_cert: String, _key: String) -> ClientConfig {
|
||||
let mut config = ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_root_certificates(RootCertStore::empty())
|
||||
.with_no_client_auth();
|
||||
|
||||
@@ -91,6 +111,10 @@ fn rustls_connector(_cert: String, _key: String) -> ClientConfig {
|
||||
|
||||
#[actix_rt::test]
|
||||
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 srv = TestServer::start({
|
||||
@@ -102,7 +126,7 @@ async fn accepts_connections() {
|
||||
let tls_acceptor = Acceptor::new(openssl_acceptor);
|
||||
|
||||
tls_acceptor
|
||||
.map_err(|err| println!("OpenSSL error: {:?}", err))
|
||||
.map_err(|err| println!("OpenSSL error: {err:?}"))
|
||||
.and_then(move |_stream: TlsStream<TcpStream>| ok(()))
|
||||
}
|
||||
});
|
||||
@@ -117,13 +141,13 @@ async fn accepts_connections() {
|
||||
let config = rustls_connector(cert, key);
|
||||
let config = Arc::new(config);
|
||||
|
||||
let mut conn = tokio_rustls::rustls::ClientConnection::new(
|
||||
let mut conn = tokio_rustls_026::rustls::ClientConnection::new(
|
||||
config,
|
||||
ServerName::try_from("localhost").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");
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
#![cfg(all(
|
||||
feature = "accept",
|
||||
feature = "connect",
|
||||
feature = "rustls-0_21",
|
||||
feature = "rustls-0_23",
|
||||
feature = "openssl"
|
||||
))]
|
||||
|
||||
@@ -15,39 +15,39 @@ use actix_rt::net::TcpStream;
|
||||
use actix_server::TestServer;
|
||||
use actix_service::ServiceFactoryExt as _;
|
||||
use actix_tls::{
|
||||
accept::rustls_0_21::{Acceptor, TlsStream},
|
||||
accept::rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},
|
||||
connect::openssl::reexports::SslConnector,
|
||||
};
|
||||
use actix_utils::future::ok;
|
||||
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||
use rustls_pki_types_1::PrivateKeyDer;
|
||||
use tls_openssl::ssl::SslVerifyMode;
|
||||
use tokio_rustls::rustls::{self, Certificate, PrivateKey, ServerConfig};
|
||||
use tokio_rustls_024 as tokio_rustls;
|
||||
|
||||
fn new_cert_and_key() -> (String, String) {
|
||||
let cert =
|
||||
let rcgen::CertifiedKey { cert, key_pair } =
|
||||
rcgen::generate_simple_self_signed(vec!["127.0.0.1".to_owned(), "localhost".to_owned()])
|
||||
.unwrap();
|
||||
|
||||
let key = cert.serialize_private_key_pem();
|
||||
let cert = cert.serialize_pem().unwrap();
|
||||
let key = key_pair.serialize_pem();
|
||||
let cert = cert.pem();
|
||||
|
||||
(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
|
||||
|
||||
let cert = &mut BufReader::new(cert.as_bytes());
|
||||
let key = &mut BufReader::new(key.as_bytes());
|
||||
|
||||
let cert_chain = certs(cert).unwrap().into_iter().map(Certificate).collect();
|
||||
let mut keys = pkcs8_private_keys(key).unwrap();
|
||||
let cert_chain = certs(cert).collect::<Result<Vec<_>, _>>().unwrap();
|
||||
let mut keys = pkcs8_private_keys(key)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap();
|
||||
|
||||
let mut config = ServerConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.with_no_client_auth()
|
||||
.with_single_cert(cert_chain, PrivateKey(keys.remove(0)))
|
||||
.with_single_cert(cert_chain, PrivateKeyDer::Pkcs8(keys.remove(0)))
|
||||
.unwrap();
|
||||
|
||||
config.alpn_protocols = vec![b"http/1.1".to_vec()];
|
||||
@@ -73,6 +73,10 @@ fn openssl_connector(cert: String, key: String) -> SslConnector {
|
||||
|
||||
#[actix_rt::test]
|
||||
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 srv = TestServer::start({
|
||||
@@ -83,7 +87,7 @@ async fn accepts_connections() {
|
||||
let tls_acceptor = Acceptor::new(rustls_server_config(cert.clone(), key.clone()));
|
||||
|
||||
tls_acceptor
|
||||
.map_err(|err| println!("Rustls error: {:?}", err))
|
||||
.map_err(|err| println!("Rustls error: {err:?}"))
|
||||
.and_then(move |_stream: TlsStream<TcpStream>| ok(()))
|
||||
}
|
||||
});
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#![allow(missing_docs)]
|
||||
#![cfg(feature = "connect")]
|
||||
|
||||
use std::{
|
||||
@@ -11,7 +12,7 @@ use actix_server::TestServer;
|
||||
use actix_service::{fn_service, Service, ServiceFactory};
|
||||
use actix_tls::connect::{ConnectError, ConnectInfo, Connection, Connector, Host};
|
||||
use bytes::Bytes;
|
||||
use futures_util::sink::SinkExt;
|
||||
use futures_util::sink::SinkExt as _;
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
#[actix_rt::test]
|
||||
@@ -30,7 +31,7 @@ async fn test_string() {
|
||||
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustls-0_21")]
|
||||
#[cfg(feature = "rustls-0_23")]
|
||||
#[actix_rt::test]
|
||||
async fn test_rustls_string() {
|
||||
let srv = TestServer::start(|| {
|
||||
@@ -98,8 +99,6 @@ async fn service_factory() {
|
||||
#[cfg(all(feature = "openssl", feature = "uri"))]
|
||||
#[actix_rt::test]
|
||||
async fn test_openssl_uri() {
|
||||
use std::convert::TryFrom;
|
||||
|
||||
let srv = TestServer::start(|| {
|
||||
fn_service(|io: TcpStream| async {
|
||||
let mut framed = Framed::new(io, BytesCodec);
|
||||
@@ -109,16 +108,14 @@ async fn test_openssl_uri() {
|
||||
});
|
||||
|
||||
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();
|
||||
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rustls-0_21", feature = "uri"))]
|
||||
#[cfg(all(feature = "rustls-0_23", feature = "uri"))]
|
||||
#[actix_rt::test]
|
||||
async fn test_rustls_uri() {
|
||||
use std::convert::TryFrom;
|
||||
|
||||
async fn test_rustls_uri_http1() {
|
||||
let srv = TestServer::start(|| {
|
||||
fn_service(|io: TcpStream| async {
|
||||
let mut framed = Framed::new(io, BytesCodec);
|
||||
@@ -128,7 +125,24 @@ async fn test_rustls_uri() {
|
||||
});
|
||||
|
||||
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();
|
||||
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||
}
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#![allow(missing_docs)]
|
||||
#![cfg(feature = "connect")]
|
||||
|
||||
use std::{
|
||||
@@ -25,7 +26,7 @@ async fn custom_resolver() {
|
||||
port: u16,
|
||||
) -> LocalBoxFuture<'a, Result<Vec<SocketAddr>, Box<dyn std::error::Error>>> {
|
||||
Box::pin(async move {
|
||||
let local = format!("127.0.0.1:{}", port).parse().unwrap();
|
||||
let local = format!("127.0.0.1:{port}").parse().unwrap();
|
||||
Ok(vec![local])
|
||||
})
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user