mirror of
https://github.com/fafhrd91/actix-net
synced 2025-08-14 10:00:31 +02:00
Compare commits
177 Commits
proxy-prot
...
service-v2
Author | SHA1 | Date | |
---|---|---|---|
|
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 |
@@ -2,24 +2,14 @@
|
|||||||
lint = "clippy --workspace --tests --examples --bins -- -Dclippy::todo"
|
lint = "clippy --workspace --tests --examples --bins -- -Dclippy::todo"
|
||||||
lint-all = "clippy --workspace --all-features --tests --examples --bins -- -Dclippy::todo"
|
lint-all = "clippy --workspace --all-features --tests --examples --bins -- -Dclippy::todo"
|
||||||
|
|
||||||
ci-doctest = "test --workspace --all-features --doc --no-fail-fast -- --nocapture"
|
|
||||||
|
|
||||||
# just check the library (without dev deps)
|
# just check the library (without dev deps)
|
||||||
ci-check-min = "hack --workspace check --no-default-features"
|
ci-check-min = "hack --workspace check --no-default-features"
|
||||||
ci-check-lib = "hack --workspace --feature-powerset --exclude-features=io-uring check"
|
ci-check-lib = "hack --workspace --feature-powerset --depth=2 --exclude-features=io-uring check"
|
||||||
ci-check-lib-linux = "hack --workspace --feature-powerset check"
|
ci-check-lib-linux = "hack --workspace --feature-powerset --depth=2 check"
|
||||||
|
|
||||||
# check everything
|
# check everything
|
||||||
ci-check = "hack --workspace --feature-powerset --exclude-features=io-uring check --tests --examples"
|
ci-check = "hack --workspace --feature-powerset --depth=2 --exclude-features=io-uring check --tests --examples"
|
||||||
ci-check-linux = "hack --workspace --feature-powerset check --tests --examples"
|
ci-check-linux = "hack --workspace --feature-powerset --depth=2 check --tests --examples"
|
||||||
|
|
||||||
# tests avoiding io-uring feature
|
# tests avoiding io-uring feature
|
||||||
ci-test = "hack --feature-powerset --exclude-features=io-uring 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"
|
||||||
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"
|
|
||||||
|
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,10 +1,12 @@
|
|||||||
## PR Type
|
## PR Type
|
||||||
|
|
||||||
<!-- What kind of change does this PR make? -->
|
<!-- What kind of change does this PR make? -->
|
||||||
<!-- Bug Fix / Feature / Refactor / Code Style / Other -->
|
<!-- Bug Fix / Feature / Refactor / Code Style / Other -->
|
||||||
|
|
||||||
INSERT_PR_TYPE
|
INSERT_PR_TYPE
|
||||||
|
|
||||||
|
|
||||||
## PR Checklist
|
## PR Checklist
|
||||||
|
|
||||||
Check your PR fulfills the following:
|
Check your PR fulfills the following:
|
||||||
|
|
||||||
<!-- For draft PRs check the boxes as you complete them. -->
|
<!-- For draft PRs check the boxes as you complete them. -->
|
||||||
@@ -14,11 +16,10 @@ Check your PR fulfills the following:
|
|||||||
- [ ] A changelog entry has been made for the appropriate packages.
|
- [ ] A changelog entry has been made for the appropriate packages.
|
||||||
- [ ] Format code with the latest stable rustfmt
|
- [ ] Format code with the latest stable rustfmt
|
||||||
|
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
<!-- Describe the current and new behavior. -->
|
<!-- Describe the current and new behavior. -->
|
||||||
<!-- Emphasize any breaking changes. -->
|
<!-- Emphasize any breaking changes. -->
|
||||||
|
|
||||||
|
|
||||||
<!-- If this PR fixes or closes an issue, reference it here. -->
|
<!-- If this PR fixes or closes an issue, reference it here. -->
|
||||||
<!-- Closes #000 -->
|
<!-- Closes #000 -->
|
||||||
|
10
.github/dependabot.yml
vendored
Normal file
10
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
- package-ecosystem: cargo
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
88
.github/workflows/ci-post-merge.yml
vendored
88
.github/workflows/ci-post-merge.yml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: CI (master only)
|
name: CI (post-merge)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -22,7 +22,6 @@ jobs:
|
|||||||
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
|
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
|
||||||
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
|
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
|
||||||
- { name: Windows (MinGW), os: windows-latest, triple: x86_64-pc-windows-gnu }
|
- { name: Windows (MinGW), os: windows-latest, triple: x86_64-pc-windows-gnu }
|
||||||
- { name: Windows (32-bit), os: windows-latest, triple: i686-pc-windows-msvc }
|
|
||||||
version:
|
version:
|
||||||
- nightly
|
- nightly
|
||||||
|
|
||||||
@@ -36,28 +35,38 @@ jobs:
|
|||||||
if: matrix.target.os == 'macos-latest'
|
if: matrix.target.os == 'macos-latest'
|
||||||
run: sudo ifconfig lo0 alias 127.0.0.3
|
run: sudo ifconfig lo0 alias 127.0.0.3
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Free Disk Space
|
- name: Free Disk Space
|
||||||
if: matrix.target.os == 'ubuntu-latest'
|
if: matrix.target.os == 'ubuntu-latest'
|
||||||
run: ./scripts/free-disk-space.sh
|
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
|
- name: Install OpenSSL
|
||||||
if: matrix.target.os == 'windows-latest'
|
if: matrix.target.os == 'windows-latest'
|
||||||
run: choco install openssl -y --forcex64 --no-progress
|
shell: bash
|
||||||
- name: Set OpenSSL dir in env
|
|
||||||
if: matrix.target.os == 'windows-latest'
|
|
||||||
run: |
|
run: |
|
||||||
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL-Win64' | Out-File -FilePath $env:GITHUB_ENV -Append
|
set -e
|
||||||
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' | Out-File -FilePath $env:GITHUB_ENV -Append
|
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 }})
|
- name: Install Rust (${{ matrix.version }})
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.version }}
|
toolchain: ${{ matrix.version }}
|
||||||
|
|
||||||
- uses: taiki-e/install-action@v2
|
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
|
||||||
with: { tool: cargo-hack }
|
uses: taiki-e/install-action@v2.49.11
|
||||||
|
with:
|
||||||
|
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
|
||||||
|
|
||||||
- name: check lib
|
- name: check lib
|
||||||
if: >
|
if: >
|
||||||
@@ -82,58 +91,39 @@ jobs:
|
|||||||
run: cargo ci-check-linux
|
run: cargo ci-check-linux
|
||||||
|
|
||||||
- name: tests
|
- name: tests
|
||||||
if: >
|
run: just test
|
||||||
matrix.target.os != 'ubuntu-latest'
|
|
||||||
&& matrix.target.triple != 'x86_64-pc-windows-gnu'
|
|
||||||
run: cargo ci-test
|
|
||||||
- name: tests
|
|
||||||
if: matrix.target.os == 'ubuntu-latest'
|
|
||||||
run: >-
|
|
||||||
sudo bash -c "
|
|
||||||
ulimit -Sl 512
|
|
||||||
&& ulimit -Hl 512
|
|
||||||
&& PATH=$PATH:/usr/share/rust/.cargo/bin
|
|
||||||
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-020
|
|
||||||
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-021
|
|
||||||
&& RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux
|
|
||||||
"
|
|
||||||
|
|
||||||
- name: Clear the cargo caches
|
# TODO: re-instate some io-uring tests PRs
|
||||||
run: |
|
# - name: tests
|
||||||
cargo install cargo-cache --version 0.6.2 --no-default-features --features ci-autoclean
|
# if: matrix.target.os == 'ubuntu-latest'
|
||||||
cargo-cache
|
# run: >-
|
||||||
|
# sudo bash -c "
|
||||||
|
# ulimit -Sl 512
|
||||||
|
# && ulimit -Hl 512
|
||||||
|
# && PATH=$PATH:/usr/share/rust/.cargo/bin
|
||||||
|
# && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-020
|
||||||
|
# && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-rustls-021
|
||||||
|
# && RUSTUP_TOOLCHAIN=${{ matrix.version }} cargo ci-test-linux
|
||||||
|
# "
|
||||||
|
|
||||||
|
- name: CI cache clean
|
||||||
|
run: cargo-ci-cache-clean
|
||||||
|
|
||||||
minimal-versions:
|
minimal-versions:
|
||||||
name: minimal versions
|
name: minimal versions
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust (nightly)
|
- name: Install Rust (nightly)
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
with:
|
with:
|
||||||
toolchain: nightly
|
toolchain: nightly
|
||||||
|
|
||||||
- name: Install cargo-hack & cargo-minimal-versions
|
- name: Install cargo-hack & cargo-minimal-versions
|
||||||
uses: taiki-e/install-action@v1
|
uses: taiki-e/install-action@v2.49.11
|
||||||
with:
|
with:
|
||||||
tool: cargo-hack,cargo-minimal-versions
|
tool: cargo-hack,cargo-minimal-versions
|
||||||
|
|
||||||
- name: Check With Minimal Versions
|
- name: Check With Minimal Versions
|
||||||
run: cargo minimal-versions check
|
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:
|
on:
|
||||||
pull_request: {}
|
pull_request: {}
|
||||||
|
merge_group: { types: [checks_requested] }
|
||||||
push: { branches: [master] }
|
push: { branches: [master] }
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -12,7 +13,14 @@ concurrency:
|
|||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
read_msrv:
|
||||||
|
name: Read MSRV
|
||||||
|
uses: actions-rust-lang/msrv/.github/workflows/msrv.yml@v0.1.0
|
||||||
|
|
||||||
build_and_test:
|
build_and_test:
|
||||||
|
needs:
|
||||||
|
- read_msrv
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -22,12 +30,11 @@ jobs:
|
|||||||
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
|
- { name: macOS, os: macos-latest, triple: x86_64-apple-darwin }
|
||||||
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
|
- { name: Windows, os: windows-latest, triple: x86_64-pc-windows-msvc }
|
||||||
- { name: Windows (MinGW), os: windows-latest, triple: x86_64-pc-windows-gnu }
|
- { name: Windows (MinGW), os: windows-latest, triple: x86_64-pc-windows-gnu }
|
||||||
- { name: Windows (32-bit), os: windows-latest, triple: i686-pc-windows-msvc }
|
|
||||||
version:
|
version:
|
||||||
- 1.65.0 # MSRV
|
- { name: msrv, version: "${{ needs.read_msrv.outputs.msrv }}" }
|
||||||
- stable
|
- { name: stable, version: stable }
|
||||||
|
|
||||||
name: ${{ matrix.target.name }} / ${{ matrix.version }}
|
name: ${{ matrix.target.name }} / ${{ matrix.version.name }}
|
||||||
runs-on: ${{ matrix.target.os }}
|
runs-on: ${{ matrix.target.os }}
|
||||||
|
|
||||||
env: {}
|
env: {}
|
||||||
@@ -37,36 +44,45 @@ jobs:
|
|||||||
if: matrix.target.os == 'macos-latest'
|
if: matrix.target.os == 'macos-latest'
|
||||||
run: sudo ifconfig lo0 alias 127.0.0.3
|
run: sudo ifconfig lo0 alias 127.0.0.3
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Free Disk Space
|
- name: Free Disk Space
|
||||||
if: matrix.target.os == 'ubuntu-latest'
|
if: matrix.target.os == 'ubuntu-latest'
|
||||||
run: ./scripts/free-disk-space.sh
|
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
|
- name: Install OpenSSL
|
||||||
if: matrix.target.os == 'windows-latest'
|
if: matrix.target.os == 'windows-latest'
|
||||||
run: choco install openssl -y --forcex64 --no-progress
|
shell: bash
|
||||||
- name: Set OpenSSL dir in env
|
|
||||||
if: matrix.target.os == 'windows-latest'
|
|
||||||
run: |
|
run: |
|
||||||
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL-Win64' | Out-File -FilePath $env:GITHUB_ENV -Append
|
set -e
|
||||||
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' | Out-File -FilePath $env:GITHUB_ENV -Append
|
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 }})
|
- name: Install Rust (${{ matrix.version.name }})
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
with:
|
with:
|
||||||
toolchain: ${{ matrix.version }}
|
toolchain: ${{ matrix.version.version }}
|
||||||
|
|
||||||
- uses: taiki-e/install-action@v1
|
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
|
||||||
with: { tool: cargo-hack }
|
uses: taiki-e/install-action@v2.49.11
|
||||||
|
with:
|
||||||
|
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
|
||||||
|
|
||||||
- name: Generate Cargo.lock
|
- name: Generate Cargo.lock
|
||||||
run: cargo generate-lockfile
|
run: cargo generate-lockfile
|
||||||
|
|
||||||
- name: workaround MSRV issues
|
- name: workaround MSRV issues
|
||||||
if: matrix.version != 'stable'
|
if: matrix.version.name == 'msrv'
|
||||||
run: |
|
run: just downgrade-for-msrv
|
||||||
cargo update -p=time --precise=0.3.16 # time is only a dev dep so shouldn't affect msrv
|
|
||||||
|
|
||||||
- name: check lib
|
- name: check lib
|
||||||
if: >
|
if: >
|
||||||
@@ -77,7 +93,7 @@ jobs:
|
|||||||
if: matrix.target.os == 'ubuntu-latest'
|
if: matrix.target.os == 'ubuntu-latest'
|
||||||
run: cargo ci-check-lib-linux
|
run: cargo ci-check-lib-linux
|
||||||
- name: check lib
|
- name: check lib
|
||||||
if: matrix.target.triple == 'x86_64-pc-windows-gnu'
|
if: matrix.target.triple != 'x86_64-pc-windows-gnu'
|
||||||
run: cargo ci-check-min
|
run: cargo ci-check-min
|
||||||
|
|
||||||
- name: check full
|
- name: check full
|
||||||
@@ -91,41 +107,27 @@ jobs:
|
|||||||
run: cargo ci-check-linux
|
run: cargo ci-check-linux
|
||||||
|
|
||||||
- name: tests
|
- name: tests
|
||||||
if: matrix.target.os == 'macos-latest'
|
run: just test
|
||||||
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
|
|
||||||
"
|
|
||||||
|
|
||||||
- name: Clear the cargo caches
|
- name: CI cache clean
|
||||||
run: |
|
run: cargo-ci-cache-clean
|
||||||
cargo install cargo-cache --version 0.6.2 --no-default-features --features ci-autoclean
|
|
||||||
cargo-cache
|
|
||||||
|
|
||||||
rustdoc:
|
docs:
|
||||||
name: rustdoc
|
name: Documentation
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust (nightly)
|
- name: Install Rust (nightly)
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
with: { toolchain: nightly }
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
|
||||||
- name: doc tests io-uring
|
- name: Install just
|
||||||
run: |
|
uses: taiki-e/install-action@v2.49.11
|
||||||
sudo bash -c "ulimit -Sl 512 && ulimit -Hl 512 && PATH=$PATH:/usr/share/rust/.cargo/bin && RUSTUP_TOOLCHAIN=nightly cargo ci-doctest"
|
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
|
|
8
.github/workflows/coverage.yml
vendored
8
.github/workflows/coverage.yml
vendored
@@ -18,12 +18,12 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Install Rust
|
- name: Install Rust
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
with:
|
with:
|
||||||
components: llvm-tools-preview
|
components: llvm-tools-preview
|
||||||
|
|
||||||
- name: Install cargo-llvm-cov
|
- name: Install cargo-llvm-cov
|
||||||
uses: taiki-e/install-action@v2.18.9
|
uses: taiki-e/install-action@v2.49.11
|
||||||
with:
|
with:
|
||||||
tool: cargo-llvm-cov
|
tool: cargo-llvm-cov
|
||||||
|
|
||||||
@@ -31,7 +31,9 @@ jobs:
|
|||||||
run: cargo llvm-cov --workspace --all-features --codecov --output-path codecov.json
|
run: cargo llvm-cov --workspace --all-features --codecov --output-path codecov.json
|
||||||
|
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v3.1.4
|
uses: codecov/codecov-action@v5.4.0
|
||||||
with:
|
with:
|
||||||
files: codecov.json
|
files: codecov.json
|
||||||
fail_ci_if_error: true
|
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@v4
|
||||||
|
|
||||||
|
- uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
|
with:
|
||||||
|
toolchain: nightly
|
||||||
|
components: rustfmt
|
||||||
|
|
||||||
|
- name: Rustfmt Check
|
||||||
|
run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
|
with: { components: clippy }
|
||||||
|
|
||||||
|
- uses: giraffate/clippy-action@v1.0.1
|
||||||
|
with:
|
||||||
|
reporter: "github-pr-check"
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
clippy_flags: --workspace --all-features --tests --examples --bins -- -Dclippy::todo -Aunknown_lints
|
||||||
|
|
||||||
|
check-external-types:
|
||||||
|
if: false # rustdoc mismatch currently
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust (${{ vars.RUST_VERSION_EXTERNAL_TYPES }})
|
||||||
|
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||||
|
with:
|
||||||
|
toolchain: ${{ vars.RUST_VERSION_EXTERNAL_TYPES }}
|
||||||
|
|
||||||
|
- name: Install just
|
||||||
|
uses: taiki-e/install-action@v2.49.11
|
||||||
|
with:
|
||||||
|
tool: just
|
||||||
|
|
||||||
|
- name: Install cargo-check-external-types
|
||||||
|
uses: taiki-e/cache-cargo-install-action@v2.1.1
|
||||||
|
with:
|
||||||
|
tool: cargo-check-external-types
|
||||||
|
|
||||||
|
- name: check external types
|
||||||
|
run: just check-external-types-all +${{ vars.RUST_VERSION_EXTERNAL_TYPES }}
|
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/
|
target/
|
||||||
guide/build/
|
guide/build/
|
||||||
/gh-pages
|
/gh-pages
|
||||||
@@ -13,4 +12,8 @@ guide/build/
|
|||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
|
# IDEs
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
# direnv
|
||||||
|
/.direnv
|
||||||
|
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
|
2914
Cargo.lock
generated
Normal file
2914
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@@ -1,8 +1,8 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"actix-codec",
|
"actix-codec",
|
||||||
"actix-macros",
|
"actix-macros",
|
||||||
"actix-proxy-protocol",
|
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
"actix-server",
|
"actix-server",
|
||||||
"actix-service",
|
"actix-service",
|
||||||
@@ -13,17 +13,15 @@ members = [
|
|||||||
"local-channel",
|
"local-channel",
|
||||||
"local-waker",
|
"local-waker",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.65"
|
rust-version = "1.71.1"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
actix-codec = { path = "actix-codec" }
|
actix-codec = { path = "actix-codec" }
|
||||||
actix-macros = { path = "actix-macros" }
|
actix-macros = { path = "actix-macros" }
|
||||||
actix-proxy-protocol = { path = "actix-proxy-protocol" }
|
|
||||||
actix-rt = { path = "actix-rt" }
|
actix-rt = { path = "actix-rt" }
|
||||||
actix-server = { path = "actix-server" }
|
actix-server = { path = "actix-server" }
|
||||||
actix-service = { path = "actix-service" }
|
actix-service = { path = "actix-service" }
|
||||||
@@ -38,3 +36,9 @@ local-waker = { path = "local-waker" }
|
|||||||
lto = true
|
lto = true
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
|
[workspace.lints.rust]
|
||||||
|
rust_2018_idioms = "deny"
|
||||||
|
nonstandard-style = "deny"
|
||||||
|
future_incompatible = "deny"
|
||||||
|
missing_docs = { level = "warn", priority = -1 }
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
> A collection of lower-level libraries for composable network services.
|
> A collection of lower-level libraries for composable network services.
|
||||||
|
|
||||||
[](https://github.com/actix/actix-net/actions/workflows/ci.yml)
|
[](https://github.com/actix/actix-net/actions/workflows/ci.yml)
|
||||||
[](https://codecov.io/gh/actix/actix-net)
|
[](https://codecov.io/gh/actix/actix-net)
|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
[](https://deps.rs/repo/github/actix/actix-net)
|
[](https://deps.rs/repo/github/actix/actix-net)
|
||||||
|
|
||||||
|
@@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
|
## 0.5.2
|
||||||
|
|
||||||
- Minimum supported Rust version (MSRV) is now 1.65.
|
- Minimum supported Rust version (MSRV) is now 1.65.
|
||||||
|
|
||||||
## 0.5.1
|
## 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.
|
- Minimum supported Rust version (MSRV) is now 1.49.
|
||||||
|
|
||||||
[#451]: https://github.com/actix/actix-net/pull/451
|
|
||||||
|
|
||||||
## 0.5.0
|
## 0.5.0
|
||||||
|
|
||||||
- Updated `tokio-util` dependency to `0.7.0`. [#446]
|
- Updated `tokio-util` dependency to `0.7.0`.
|
||||||
|
|
||||||
[#446]: https://github.com/actix/actix-net/pull/446
|
|
||||||
|
|
||||||
## 0.4.2
|
## 0.4.2
|
||||||
|
|
||||||
@@ -23,11 +23,8 @@
|
|||||||
|
|
||||||
## 0.4.1
|
## 0.4.1
|
||||||
|
|
||||||
- Added `LinesCodec.` [#338]
|
- Added `LinesCodec`.
|
||||||
- `Framed::poll_ready` flushes when the buffer is full. [#409]
|
- `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
|
## 0.4.0
|
||||||
|
|
||||||
@@ -35,12 +32,10 @@
|
|||||||
|
|
||||||
## 0.4.0-beta.1
|
## 0.4.0-beta.1
|
||||||
|
|
||||||
- Replace `pin-project` with `pin-project-lite`. [#237]
|
- Replace `pin-project` with `pin-project-lite`.
|
||||||
- Upgrade `tokio` dependency to `1`. [#237]
|
- Upgrade `tokio` dependency to `1`.
|
||||||
- Upgrade `tokio-util` dependency to `0.6`. [#237]
|
- Upgrade `tokio-util` dependency to `0.6`.
|
||||||
- Upgrade `bytes` dependency to `1`. [#237]
|
- Upgrade `bytes` dependency to `1`.
|
||||||
|
|
||||||
[#237]: https://github.com/actix/actix-net/pull/237
|
|
||||||
|
|
||||||
## 0.3.0
|
## 0.3.0
|
||||||
|
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-codec"
|
name = "actix-codec"
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
authors = [
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
|
||||||
]
|
|
||||||
description = "Codec utilities for working with framed protocols"
|
description = "Codec utilities for working with framed protocols"
|
||||||
keywords = ["network", "framework", "async", "futures"]
|
keywords = ["network", "framework", "async", "futures"]
|
||||||
repository = "https://github.com/actix/actix-net"
|
repository = "https://github.com/actix/actix-net"
|
||||||
@@ -13,6 +10,9 @@ license = "MIT OR Apache-2.0"
|
|||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[package.metadata.cargo_check_external_types]
|
||||||
|
allowed_external_types = ["bytes::*", "futures_core::*", "futures_sink::*", "tokio::*", "tokio_util::*"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "2"
|
bitflags = "2"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
@@ -25,9 +25,12 @@ tokio-util = { version = "0.7", features = ["codec", "io"] }
|
|||||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = { version = "0.4", features = ["html_reports"] }
|
criterion = { version = "0.5", features = ["html_reports"] }
|
||||||
tokio-test = "0.4.2"
|
tokio-test = "0.4.2"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "lines"
|
name = "lines"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
|
@@ -6,8 +6,6 @@
|
|||||||
//! [`Sink`]: futures_sink::Sink
|
//! [`Sink`]: futures_sink::Sink
|
||||||
//! [`Stream`]: futures_core::Stream
|
//! [`Stream`]: futures_core::Stream
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible, missing_docs)]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
## 0.2.4
|
## 0.2.4
|
||||||
|
|
||||||
- Update `syn` dependency to `2`.
|
- Update `syn` dependency to `2`.
|
||||||
|
@@ -2,9 +2,9 @@
|
|||||||
name = "actix-macros"
|
name = "actix-macros"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"Ibraheem Ahmed <ibrah1440@gmail.com>",
|
"Ibraheem Ahmed <ibrah1440@gmail.com>",
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
]
|
]
|
||||||
description = "Macros for Actix system and runtime"
|
description = "Macros for Actix system and runtime"
|
||||||
repository = "https://github.com/actix/actix-net"
|
repository = "https://github.com/actix/actix-net"
|
||||||
@@ -15,7 +15,7 @@ rust-version.workspace = true
|
|||||||
|
|
||||||
[package.metadata.cargo-machete]
|
[package.metadata.cargo-machete]
|
||||||
ignored = [
|
ignored = [
|
||||||
"proc_macro2", # specified for minimal versions compat
|
"proc_macro2", # specified for minimal versions compat
|
||||||
]
|
]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
@@ -26,11 +26,14 @@ quote = "1"
|
|||||||
syn = { version = "2", features = ["full"] }
|
syn = { version = "2", features = ["full"] }
|
||||||
|
|
||||||
# minimal versions compat
|
# minimal versions compat
|
||||||
|
[target.'cfg(any())'.dependencies]
|
||||||
proc-macro2 = "1.0.60"
|
proc-macro2 = "1.0.60"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2"
|
actix-rt = "2"
|
||||||
|
|
||||||
futures-util = { version = "0.3.17", default-features = false }
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
rustversion = "1"
|
rustversion-msrv = "0.100"
|
||||||
trybuild = "1"
|
trybuild = "1"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
@@ -8,8 +8,6 @@
|
|||||||
//! # Tests
|
//! # Tests
|
||||||
//! See docs for the [`#[test]`](macro@test) macro.
|
//! See docs for the [`#[test]`](macro@test) macro.
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible)]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
#[rustversion::stable(1.65)] // MSRV
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
|
#[rustversion_msrv::msrv]
|
||||||
#[test]
|
#[test]
|
||||||
fn compile_macros() {
|
fn compile_macros() {
|
||||||
let t = trybuild::TestCases::new();
|
let t = trybuild::TestCases::new();
|
||||||
|
@@ -1,7 +0,0 @@
|
|||||||
# Changes
|
|
||||||
|
|
||||||
## Unreleased - 2022-xx-xx
|
|
||||||
|
|
||||||
## 0.0.1 - 2022-xx-xx
|
|
||||||
|
|
||||||
- delete me
|
|
@@ -1,41 +0,0 @@
|
|||||||
[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"
|
|
||||||
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"
|
|
||||||
env_logger = "0.9"
|
|
||||||
futures-util = { version = "0.3.7", default-features = false, features = ["sink", "async-await-macro"] }
|
|
||||||
once_cell = "1"
|
|
||||||
pretty_assertions = "1"
|
|
||||||
tokio = { version = "1.13.1", features = ["io-util", "rt-multi-thread", "macros", "fs"] }
|
|
@@ -1 +0,0 @@
|
|||||||
../LICENSE-APACHE
|
|
@@ -1 +0,0 @@
|
|||||||
../LICENSE-MIT
|
|
@@ -1,17 +0,0 @@
|
|||||||
# 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
|
|
@@ -1,113 +0,0 @@
|
|||||||
//! 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 bytes::BytesMut;
|
|
||||||
use const_str::concat_bytes;
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
use tokio::io::{copy_bidirectional, AsyncReadExt as _, AsyncWriteExt as _};
|
|
||||||
|
|
||||||
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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_server() -> io::Result<Server> {
|
|
||||||
let addr = ("127.0.0.1", 8082);
|
|
||||||
tracing::info!("starting proxy server on port: {}", &addr.0);
|
|
||||||
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", addr, move || {
|
|
||||||
fn_service(wrap_with_proxy_protocol_v2)
|
|
||||||
.map_err(|err| tracing::error!("service error: {:?}", err))
|
|
||||||
})?
|
|
||||||
.workers(2)
|
|
||||||
.run())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> io::Result<()> {
|
|
||||||
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
|
||||||
start_server()?.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@@ -1,168 +0,0 @@
|
|||||||
//! PROXY protocol.
|
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible)]
|
|
||||||
// #![warn(missing_docs)]
|
|
||||||
#![allow(unused)]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
convert::TryFrom as _,
|
|
||||||
fmt, io,
|
|
||||||
net::{IpAddr, SocketAddr},
|
|
||||||
};
|
|
||||||
|
|
||||||
use arrayvec::{ArrayString, ArrayVec};
|
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt as _};
|
|
||||||
|
|
||||||
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 {
|
|
||||||
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),
|
|
||||||
}
|
|
@@ -1,292 +0,0 @@
|
|||||||
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).
|
|
||||||
///
|
|
||||||
/// Heckin 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
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,60 +0,0 @@
|
|||||||
use std::{fmt, io, net::SocketAddr};
|
|
||||||
|
|
||||||
use arrayvec::ArrayVec;
|
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt as _};
|
|
||||||
|
|
||||||
use crate::AddressFamily;
|
|
||||||
|
|
||||||
pub(crate) const SIGNATURE: &str = "PROXY";
|
|
||||||
|
|
||||||
#[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::<_, 107>::new();
|
|
||||||
self.write_to(&mut buf)?;
|
|
||||||
wrt.write_all(&buf).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,304 +0,0 @@
|
|||||||
use std::{
|
|
||||||
io,
|
|
||||||
net::{IpAddr, SocketAddr},
|
|
||||||
};
|
|
||||||
|
|
||||||
use smallvec::{smallvec, SmallVec, ToSmallVec as _};
|
|
||||||
use tokio::io::{AsyncWrite, AsyncWriteExt as _};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
tlv::{Crc32c, Tlv},
|
|
||||||
AddressFamily, Command, TransportProtocol, Version,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) 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 mut 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);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
|
## 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
|
## 2.9.0
|
||||||
|
|
||||||
- Add `actix_rt::System::runtime()` method to retrieve the underlying `actix_rt::Runtime` runtime.
|
- Add `actix_rt::System::runtime()` method to retrieve the underlying `actix_rt::Runtime` runtime.
|
||||||
@@ -10,150 +18,105 @@
|
|||||||
|
|
||||||
## 2.8.0
|
## 2.8.0
|
||||||
|
|
||||||
- Add `#[track_caller]` attribute to `spawn` functions and methods. [#454]
|
- Add `#[track_caller]` attribute to `spawn` functions and methods.
|
||||||
- Update `tokio-uring` dependency to `0.4`. [#473]
|
- Update `tokio-uring` dependency to `0.4`.
|
||||||
- Minimum supported Rust version (MSRV) is now 1.59.
|
- 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
|
||||||
|
|
||||||
- Update `tokio-uring` dependency to `0.3`. [#448]
|
- Update `tokio-uring` dependency to `0.3`.
|
||||||
- Minimum supported Rust version (MSRV) is now 1.49.
|
- Minimum supported Rust version (MSRV) is now 1.49.
|
||||||
|
|
||||||
[#448]: https://github.com/actix/actix-net/pull/448
|
|
||||||
|
|
||||||
## 2.6.0
|
## 2.6.0
|
||||||
|
|
||||||
- Update `tokio-uring` dependency to `0.2`. [#436]
|
- Update `tokio-uring` dependency to `0.2`.
|
||||||
|
|
||||||
[#436]: https://github.com/actix/actix-net/pull/436
|
|
||||||
|
|
||||||
## 2.5.1
|
## 2.5.1
|
||||||
|
|
||||||
- Expose `System::with_tokio_rt` and `Arbiter::with_tokio_rt`. [#430]
|
- Expose `System::with_tokio_rt` and `Arbiter::with_tokio_rt`.
|
||||||
|
|
||||||
[#430]: https://github.com/actix/actix-net/pull/430
|
|
||||||
|
|
||||||
## 2.5.0
|
## 2.5.0
|
||||||
|
|
||||||
- Add `System::run_with_code` to allow retrieving the exit code on stop. [#411]
|
- Add `System::run_with_code` to allow retrieving the exit code on stop.
|
||||||
|
|
||||||
[#411]: https://github.com/actix/actix-net/pull/411
|
|
||||||
|
|
||||||
## 2.4.0
|
## 2.4.0
|
||||||
|
|
||||||
- Add `Arbiter::try_current` for situations where thread may or may not have Arbiter context. [#408]
|
- 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. [#395]
|
- Start io-uring with `System::new` when feature is enabled.
|
||||||
|
|
||||||
[#395]: https://github.com/actix/actix-net/pull/395
|
|
||||||
[#408]: https://github.com/actix/actix-net/pull/408
|
|
||||||
|
|
||||||
## 2.3.0
|
## 2.3.0
|
||||||
|
|
||||||
- The `spawn` method can now resolve with non-unit outputs. [#369]
|
- 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. [#374]
|
- Add experimental (semver-exempt) `io-uring` feature for enabling async file I/O on linux.
|
||||||
|
|
||||||
[#369]: https://github.com/actix/actix-net/pull/369
|
|
||||||
[#374]: https://github.com/actix/actix-net/pull/374
|
|
||||||
|
|
||||||
## 2.2.0
|
## 2.2.0
|
||||||
|
|
||||||
- **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.
|
- 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
|
||||||
|
|
||||||
- Add `ActixStream` extension trait to include readiness methods. [#276]
|
- Add `ActixStream` extension trait to include readiness methods.
|
||||||
- Re-export `tokio::net::TcpSocket` in `net` module [#282]
|
- Re-export `tokio::net::TcpSocket` in `net` module
|
||||||
|
|
||||||
[#276]: https://github.com/actix/actix-net/pull/276
|
|
||||||
[#282]: https://github.com/actix/actix-net/pull/282
|
|
||||||
|
|
||||||
## 2.0.2
|
## 2.0.2
|
||||||
|
|
||||||
- Add `Arbiter::handle` to get a handle of an owned Arbiter. [#274]
|
- 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. [#275]
|
- Add `System::try_current` for situations where actix may or may not be running a System.
|
||||||
|
|
||||||
[#274]: https://github.com/actix/actix-net/pull/274
|
|
||||||
[#275]: https://github.com/actix/actix-net/pull/275
|
|
||||||
|
|
||||||
## 2.0.1
|
## 2.0.1
|
||||||
|
|
||||||
- Expose `JoinError` from Tokio. [#271]
|
- Expose `JoinError` from Tokio.
|
||||||
|
|
||||||
[#271]: https://github.com/actix/actix-net/pull/271
|
|
||||||
|
|
||||||
## 2.0.0
|
## 2.0.0
|
||||||
|
|
||||||
- Remove all Arbiter-local storage methods. [#262]
|
- Remove all Arbiter-local storage methods.
|
||||||
- Re-export `tokio::pin`. [#262]
|
- Re-export `tokio::pin`.
|
||||||
|
|
||||||
[#262]: https://github.com/actix/actix-net/pull/262
|
|
||||||
|
|
||||||
## 2.0.0-beta.3
|
## 2.0.0-beta.3
|
||||||
|
|
||||||
- Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`. [#253]
|
- Remove `run_in_tokio`, `attach_to_tokio` and `AsyncSystemRunner`.
|
||||||
- Return `JoinHandle` from `actix_rt::spawn`. [#253]
|
- Return `JoinHandle` from `actix_rt::spawn`.
|
||||||
- Remove old `Arbiter::spawn`. Implementation is now inlined into `actix_rt::spawn`. [#253]
|
- Remove old `Arbiter::spawn`. Implementation is now inlined into `actix_rt::spawn`.
|
||||||
- Rename `Arbiter::{send => spawn}` and `Arbiter::{exec_fn => spawn_fn}`. [#253]
|
- Rename `Arbiter::{send => spawn}` and `Arbiter::{exec_fn => spawn_fn}`.
|
||||||
- Remove `Arbiter::exec`. [#253]
|
- Remove `Arbiter::exec`.
|
||||||
- Remove deprecated `Arbiter::local_join` and `Arbiter::is_running`. [#253]
|
- Remove deprecated `Arbiter::local_join` and `Arbiter::is_running`.
|
||||||
- `Arbiter::spawn` now accepts !Unpin futures. [#256]
|
- `Arbiter::spawn` now accepts !Unpin futures.
|
||||||
- `System::new` no longer takes arguments. [#257]
|
- `System::new` no longer takes arguments.
|
||||||
- Remove `System::with_current`. [#257]
|
- Remove `System::with_current`.
|
||||||
- Remove `Builder`. [#257]
|
- Remove `Builder`.
|
||||||
- Add `System::with_init` as replacement for `Builder::run`. [#257]
|
- Add `System::with_init` as replacement for `Builder::run`.
|
||||||
- Rename `System::{is_set => is_registered}`. [#257]
|
- Rename `System::{is_set => is_registered}`.
|
||||||
- Add `ArbiterHandle` for sending messages to non-current-thread arbiters. [#257].
|
- Add `ArbiterHandle` for sending messages to non-current-thread arbiters.
|
||||||
- `System::arbiter` now returns an `&ArbiterHandle`. [#257]
|
- `System::arbiter` now returns an `&ArbiterHandle`.
|
||||||
- `Arbiter::current` now returns an `ArbiterHandle` instead. [#257]
|
- `Arbiter::current` now returns an `ArbiterHandle` instead.
|
||||||
- `Arbiter::join` now takes self by value. [#257]
|
- `Arbiter::join` now takes self by value.
|
||||||
|
|
||||||
[#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
|
## 2.0.0-beta.2
|
||||||
|
|
||||||
- Add `task` mod with re-export of `tokio::task::{spawn_blocking, yield_now, JoinHandle}` [#245]
|
- Add `task` mod with re-export of `tokio::task::{spawn_blocking, yield_now, JoinHandle}`
|
||||||
- Add default "macros" feature to allow faster compile times when using `default-features=false`.
|
- Add default "macros" feature to allow faster compile times when using `default-features=false`.
|
||||||
|
|
||||||
[#245]: https://github.com/actix/actix-net/pull/245
|
|
||||||
|
|
||||||
## 2.0.0-beta.1
|
## 2.0.0-beta.1
|
||||||
|
|
||||||
- Add `System::attach_to_tokio` method. [#173]
|
- Add `System::attach_to_tokio` method.
|
||||||
- Update `tokio` dependency to `1.0`. [#236]
|
- 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. [#236]
|
- 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`.
|
- Remove `'static` lifetime requirement for `Runtime::block_on` and `SystemRunner::block_on`.
|
||||||
- These methods now accept `&self` when calling. [#236]
|
- These methods now accept `&self` when calling.
|
||||||
- Remove `'static` lifetime requirement for `System::run` and `Builder::run`. [#236]
|
- Remove `'static` lifetime requirement for `System::run` and `Builder::run`.
|
||||||
- `Arbiter::spawn` now panics when `System` is not in scope. [#207]
|
- `Arbiter::spawn` now panics when `System` is not in scope.
|
||||||
- Fix work load issue by removing `PENDING` thread local. [#207]
|
- Fix work load issue by removing `PENDING` thread local.
|
||||||
|
|
||||||
[#207]: https://github.com/actix/actix-net/pull/207
|
|
||||||
[#236]: https://github.com/actix/actix-net/pull/236
|
|
||||||
|
|
||||||
## 1.1.1
|
## 1.1.1
|
||||||
|
|
||||||
- Fix memory leak due to [#94] (see [#129] for more detail)
|
- Fix memory leak due to
|
||||||
|
|
||||||
[#129]: https://github.com/actix/actix-net/issues/129
|
|
||||||
|
|
||||||
## 1.1.0 _(YANKED)_
|
## 1.1.0 _(YANKED)_
|
||||||
|
|
||||||
- Expose `System::is_set` to check if current system has ben started [#99]
|
- Expose `System::is_set` to check if current system has ben started
|
||||||
- Add `Arbiter::is_running` to check if event loop is running [#124]
|
- 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 [#94]
|
- Add `Arbiter::local_join` associated function to get be able to `await` for spawned futures
|
||||||
|
|
||||||
[#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
|
## 1.0.0
|
||||||
|
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-rt"
|
name = "actix-rt"
|
||||||
version = "2.9.0"
|
version = "2.10.0"
|
||||||
authors = [
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
|
||||||
]
|
|
||||||
description = "Tokio-based single-threaded async runtime for the Actix ecosystem"
|
description = "Tokio-based single-threaded async runtime for the Actix ecosystem"
|
||||||
keywords = ["async", "futures", "io", "runtime"]
|
keywords = ["async", "futures", "io", "runtime"]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
repository = "https://github.com/actix/actix-net"
|
||||||
categories = ["network-programming", "asynchronous"]
|
categories = ["network-programming", "asynchronous"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[package.metadata.cargo_check_external_types]
|
||||||
|
allowed_external_types = ["actix_macros::*", "tokio::*"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["macros"]
|
default = ["macros"]
|
||||||
macros = ["actix-macros"]
|
macros = ["actix-macros"]
|
||||||
@@ -27,8 +27,10 @@ tokio = { version = "1.23.1", features = ["rt", "net", "parking_lot", "signal",
|
|||||||
|
|
||||||
# runtime for `io-uring` feature
|
# runtime for `io-uring` feature
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
tokio-uring = { version = "0.4", optional = true }
|
tokio-uring = { version = "0.5", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "1.23.1", features = ["full"] }
|
tokio = { version = "1.23.1", features = ["full"] }
|
||||||
hyper = { version = "0.14.10", default-features = false, features = ["server", "tcp", "http1"] }
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
@@ -3,11 +3,11 @@
|
|||||||
> Tokio-based single-threaded async runtime for the Actix ecosystem.
|
> Tokio-based single-threaded async runtime for the Actix ecosystem.
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-rt)
|
[](https://crates.io/crates/actix-rt)
|
||||||
[](https://docs.rs/actix-rt/2.9.0)
|
[](https://docs.rs/actix-rt/2.10.0)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
|
[](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
|
||||||

|

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

|

|
||||||
[](https://discord.gg/WghFtEH6Hb)
|
[](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);
|
pub(crate) static COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
thread_local!(
|
thread_local!(
|
||||||
static HANDLE: RefCell<Option<ArbiterHandle>> = RefCell::new(None);
|
static HANDLE: RefCell<Option<ArbiterHandle>> = const { RefCell::new(None) };
|
||||||
);
|
);
|
||||||
|
|
||||||
pub(crate) enum ArbiterCommand {
|
pub(crate) enum ArbiterCommand {
|
||||||
@@ -109,7 +109,7 @@ impl Arbiter {
|
|||||||
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
|
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
|
||||||
pub fn with_tokio_rt<F>(runtime_factory: F) -> Arbiter
|
pub fn with_tokio_rt<F>(runtime_factory: F) -> Arbiter
|
||||||
where
|
where
|
||||||
F: Fn() -> tokio::runtime::Runtime + Send + 'static,
|
F: FnOnce() -> tokio::runtime::Runtime + Send + 'static,
|
||||||
{
|
{
|
||||||
let sys = System::current();
|
let sys = System::current();
|
||||||
let system_id = sys.id();
|
let system_id = sys.id();
|
||||||
|
@@ -34,14 +34,13 @@
|
|||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # `io-uring` Support
|
//! # `io-uring` Support
|
||||||
|
//!
|
||||||
//! There is experimental support for using io-uring with this crate by enabling the
|
//! There is experimental support for using io-uring with this crate by enabling the
|
||||||
//! `io-uring` feature. For now, it is semver exempt.
|
//! `io-uring` feature. For now, it is semver exempt.
|
||||||
//!
|
//!
|
||||||
//! Note that there are currently some unimplemented parts of using `actix-rt` with `io-uring`.
|
//! Note that there are currently some unimplemented parts of using `actix-rt` with `io-uring`.
|
||||||
//! In particular, when running a `System`, only `System::block_on` is supported.
|
//! In particular, when running a `System`, only `System::block_on` is supported.
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible, missing_docs)]
|
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
@@ -16,7 +16,7 @@ use crate::{arbiter::ArbiterHandle, Arbiter};
|
|||||||
static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0);
|
static SYSTEM_COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
thread_local!(
|
thread_local!(
|
||||||
static CURRENT: RefCell<Option<System>> = RefCell::new(None);
|
static CURRENT: RefCell<Option<System>> = const { RefCell::new(None) };
|
||||||
);
|
);
|
||||||
|
|
||||||
/// A manager for a per-thread distributed async runtime.
|
/// A manager for a per-thread distributed async runtime.
|
||||||
@@ -48,7 +48,7 @@ impl System {
|
|||||||
/// [tokio-runtime]: tokio::runtime::Runtime
|
/// [tokio-runtime]: tokio::runtime::Runtime
|
||||||
pub fn with_tokio_rt<F>(runtime_factory: F) -> SystemRunner
|
pub fn with_tokio_rt<F>(runtime_factory: F) -> SystemRunner
|
||||||
where
|
where
|
||||||
F: Fn() -> tokio::runtime::Runtime,
|
F: FnOnce() -> tokio::runtime::Runtime,
|
||||||
{
|
{
|
||||||
let (stop_tx, stop_rx) = oneshot::channel();
|
let (stop_tx, stop_rx) = oneshot::channel();
|
||||||
let (sys_tx, sys_rx) = mpsc::unbounded_channel();
|
let (sys_tx, sys_rx) = mpsc::unbounded_channel();
|
||||||
@@ -87,7 +87,7 @@ impl System {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn with_tokio_rt<F>(_: F) -> SystemRunner
|
pub fn with_tokio_rt<F>(_: F) -> SystemRunner
|
||||||
where
|
where
|
||||||
F: Fn() -> tokio::runtime::Runtime,
|
F: FnOnce() -> tokio::runtime::Runtime,
|
||||||
{
|
{
|
||||||
unimplemented!("System::with_tokio_rt is not implemented for io-uring feature yet")
|
unimplemented!("System::with_tokio_rt is not implemented for io-uring feature yet")
|
||||||
}
|
}
|
||||||
@@ -203,16 +203,20 @@ impl SystemRunner {
|
|||||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieves a reference to the underlying Actix runtime 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.
|
/// The Actix runtime is responsible for managing the event loop for an Actix system and
|
||||||
/// This method provides access to the runtime, allowing direct interaction with its features.
|
/// 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
|
/// 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
|
/// parts of your project. For example, some components might require a [`Runtime`] to spawn
|
||||||
/// the same runtime.
|
/// tasks on the same runtime.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// Read more in the documentation for [`Runtime`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// let system_runner = actix_rt::System::new();
|
/// let system_runner = actix_rt::System::new();
|
||||||
@@ -221,19 +225,14 @@ impl SystemRunner {
|
|||||||
/// // Use the runtime to spawn an async task or perform other operations
|
/// // 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
|
/// # Note
|
||||||
///
|
///
|
||||||
/// While this method provides an immutable reference to the Actix runtime, which is safe to share across threads,
|
/// While this method provides an immutable reference to the Actix runtime, which is safe to
|
||||||
/// be aware that spawning blocking tasks on the Actix runtime could potentially impact system performance.
|
/// share across threads, be aware that spawning blocking tasks on the Actix runtime could
|
||||||
/// This is because the Actix runtime is responsible for driving the system,
|
/// potentially impact system performance. This is because the Actix runtime is responsible for
|
||||||
/// and blocking tasks could delay other tasks in the run loop.
|
/// driving the system, and blocking tasks could delay other tasks in the run loop.
|
||||||
|
///
|
||||||
|
/// [`Runtime`]: crate::Runtime
|
||||||
pub fn runtime(&self) -> &crate::runtime::Runtime {
|
pub fn runtime(&self) -> &crate::runtime::Runtime {
|
||||||
&self.rt
|
&self.rt
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
future::Future,
|
future::Future,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
@@ -358,7 +360,7 @@ fn tokio_uring_arbiter() {
|
|||||||
let f = tokio_uring::fs::File::create("test.txt").await.unwrap();
|
let f = tokio_uring::fs::File::create("test.txt").await.unwrap();
|
||||||
let buf = b"Hello World!";
|
let buf = b"Hello World!";
|
||||||
|
|
||||||
let (res, _) = f.write_at(&buf[..], 0).await;
|
let (res, _) = f.write_all_at(&buf[..], 0).await;
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
|
|
||||||
f.sync_all().await.unwrap();
|
f.sync_all().await.unwrap();
|
||||||
|
@@ -2,6 +2,20 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 2.5.1
|
||||||
|
|
||||||
|
- Fix panic in test server.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
|
## 2.5.0
|
||||||
|
|
||||||
|
- Update `mio` dependency to `1`.
|
||||||
|
|
||||||
|
## 2.4.0
|
||||||
|
|
||||||
|
- Update `tokio-uring` dependency to `0.5`.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.70.
|
||||||
|
|
||||||
## 2.3.0
|
## 2.3.0
|
||||||
|
|
||||||
- Add support for MultiPath TCP (MPTCP) with `MpTcp` enum and `ServerBuilder::mptcp()` method.
|
- Add support for MultiPath TCP (MPTCP) with `MpTcp` enum and `ServerBuilder::mptcp()` method.
|
||||||
@@ -10,9 +24,7 @@
|
|||||||
## 2.2.0
|
## 2.2.0
|
||||||
|
|
||||||
- Minimum supported Rust version (MSRV) is now 1.59.
|
- Minimum supported Rust version (MSRV) is now 1.59.
|
||||||
- Update `tokio-uring` dependency to `0.4`. [#473]
|
- Update `tokio-uring` dependency to `0.4`.
|
||||||
|
|
||||||
[#473]: https://github.com/actix/actix-net/pull/473
|
|
||||||
|
|
||||||
## 2.1.1
|
## 2.1.1
|
||||||
|
|
||||||
@@ -20,12 +32,9 @@
|
|||||||
|
|
||||||
## 2.1.0
|
## 2.1.0
|
||||||
|
|
||||||
- Update `tokio-uring` dependency to `0.3`. [#448]
|
- Update `tokio-uring` dependency to `0.3`.
|
||||||
- Logs emitted now use the `tracing` crate with `log` compatibility. [#448]
|
- Logs emitted now use the `tracing` crate with `log` compatibility.
|
||||||
- Wait for accept thread to stop before sending completion signal. [#443]
|
- Wait for accept thread to stop before sending completion signal.
|
||||||
|
|
||||||
[#443]: https://github.com/actix/actix-net/pull/443
|
|
||||||
[#448]: https://github.com/actix/actix-net/pull/448
|
|
||||||
|
|
||||||
## 2.0.0
|
## 2.0.0
|
||||||
|
|
||||||
@@ -33,9 +42,7 @@
|
|||||||
|
|
||||||
## 2.0.0-rc.4
|
## 2.0.0-rc.4
|
||||||
|
|
||||||
- Update `tokio-uring` dependency to `0.2`. [#436]
|
- Update `tokio-uring` dependency to `0.2`.
|
||||||
|
|
||||||
[#436]: https://github.com/actix/actix-net/pull/436
|
|
||||||
|
|
||||||
## 2.0.0-rc.3
|
## 2.0.0-rc.3
|
||||||
|
|
||||||
@@ -43,117 +50,80 @@
|
|||||||
|
|
||||||
## 2.0.0-rc.2
|
## 2.0.0-rc.2
|
||||||
|
|
||||||
- Simplify `TestServer`. [#431]
|
- Simplify `TestServer`.
|
||||||
|
|
||||||
[#431]: https://github.com/actix/actix-net/pull/431
|
|
||||||
|
|
||||||
## 2.0.0-rc.1
|
## 2.0.0-rc.1
|
||||||
|
|
||||||
- Hide implementation details of `Server`. [#424]
|
- Hide implementation details of `Server`.
|
||||||
- `Server` now runs only after awaiting it. [#425]
|
- `Server` now runs only after awaiting it.
|
||||||
|
|
||||||
[#424]: https://github.com/actix/actix-net/pull/424
|
|
||||||
[#425]: https://github.com/actix/actix-net/pull/425
|
|
||||||
|
|
||||||
## 2.0.0-beta.9
|
## 2.0.0-beta.9
|
||||||
|
|
||||||
- Restore `Arbiter` support lost in `beta.8`. [#417]
|
- Restore `Arbiter` support lost in `beta.8`.
|
||||||
|
|
||||||
[#417]: https://github.com/actix/actix-net/pull/417
|
|
||||||
|
|
||||||
## 2.0.0-beta.8
|
## 2.0.0-beta.8
|
||||||
|
|
||||||
- Fix non-unix signal handler. [#410]
|
- Fix non-unix signal handler.
|
||||||
|
|
||||||
[#410]: https://github.com/actix/actix-net/pull/410
|
|
||||||
|
|
||||||
## 2.0.0-beta.7
|
## 2.0.0-beta.7
|
||||||
|
|
||||||
- Server can be started in regular Tokio runtime. [#408]
|
- Server can be started in regular Tokio runtime.
|
||||||
- Expose new `Server` type whose `Future` impl resolves when server stops. [#408]
|
- Expose new `Server` type whose `Future` impl resolves when server stops.
|
||||||
- Rename `Server` to `ServerHandle`. [#407]
|
- Rename `Server` to `ServerHandle`.
|
||||||
- Add `Server::handle` to obtain handle to server. [#408]
|
- Add `Server::handle` to obtain handle to server.
|
||||||
- Rename `ServerBuilder::{maxconn => max_concurrent_connections}`. [#407]
|
- Rename `ServerBuilder::{maxconn => max_concurrent_connections}`.
|
||||||
- Deprecate crate-level `new` shortcut for server builder. [#408]
|
- Deprecate crate-level `new` shortcut for server builder.
|
||||||
- Minimum supported Rust version (MSRV) is now 1.52.
|
- Minimum supported Rust version (MSRV) is now 1.52.
|
||||||
|
|
||||||
[#407]: https://github.com/actix/actix-net/pull/407
|
|
||||||
[#408]: https://github.com/actix/actix-net/pull/408
|
|
||||||
|
|
||||||
## 2.0.0-beta.6
|
## 2.0.0-beta.6
|
||||||
|
|
||||||
- Add experimental (semver-exempt) `io-uring` feature for enabling async file I/O on linux. [#374]
|
- 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. [#389]
|
- 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. [#349]
|
- Remove `config` module. `ServiceConfig`, `ServiceRuntime` public types are removed due to this change.
|
||||||
- Remove `ServerBuilder::configure` [#349]
|
- Remove `ServerBuilder::configure`.
|
||||||
|
|
||||||
[#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
|
|
||||||
|
|
||||||
## 2.0.0-beta.5
|
## 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]
|
- Server shutdown notifies all workers to exit regardless if shutdown is graceful. This causes all workers to shutdown immediately in force shutdown case.
|
||||||
|
|
||||||
[#333]: https://github.com/actix/actix-net/pull/333
|
|
||||||
|
|
||||||
## 2.0.0-beta.4
|
## 2.0.0-beta.4
|
||||||
|
|
||||||
- Prevent panic when `shutdown_timeout` is very large. [f9262db]
|
- Prevent panic when `shutdown_timeout` is very large. [f9262db]
|
||||||
|
|
||||||
[f9262db]: https://github.com/actix/actix-net/commit/f9262db
|
|
||||||
|
|
||||||
## 2.0.0-beta.3
|
## 2.0.0-beta.3
|
||||||
|
|
||||||
- Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`. [#246]
|
- Hidden `ServerBuilder::start` method has been removed. Use `ServerBuilder::run`.
|
||||||
- Add retry for EINTR signal (`io::Interrupted`) in `Accept`'s poll loop. [#264]
|
- Add retry for EINTR signal (`io::Interrupted`) in `Accept`'s poll loop.
|
||||||
- Add `ServerBuilder::worker_max_blocking_threads` to customize blocking thread pool size. [#265]
|
- Add `ServerBuilder::worker_max_blocking_threads` to customize blocking thread pool size.
|
||||||
- Update `actix-rt` to `2.0.0`. [#273]
|
- Update `actix-rt` to `2.0.0`.
|
||||||
|
|
||||||
[#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
|
|
||||||
|
|
||||||
## 2.0.0-beta.2
|
## 2.0.0-beta.2
|
||||||
|
|
||||||
- Merge `actix-testing` to `actix-server` as `test_server` mod. [#242]
|
- Merge `actix-testing` to `actix-server` as `test_server` mod.
|
||||||
|
|
||||||
[#242]: https://github.com/actix/actix-net/pull/242
|
|
||||||
|
|
||||||
## 2.0.0-beta.1
|
## 2.0.0-beta.1
|
||||||
|
|
||||||
- Added explicit info log message on accept queue pause. [#215]
|
- Added explicit info log message on accept queue pause.
|
||||||
- Prevent double registration of sockets when back-pressure is resolved. [#223]
|
- Prevent double registration of sockets when back-pressure is resolved.
|
||||||
- Update `mio` dependency to `0.7.3`. [#239]
|
- Update `mio` dependency to `0.7.3`.
|
||||||
- Remove `socket2` dependency. [#239]
|
- Remove `socket2` dependency.
|
||||||
- `ServerBuilder::backlog` now accepts `u32` instead of `i32`. [#239]
|
- `ServerBuilder::backlog` now accepts `u32` instead of `i32`.
|
||||||
- Remove `AcceptNotify` type and pass `WakerQueue` to `Worker` to wake up `Accept`'s `Poll`. [#239]
|
- 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). [#239]
|
- 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. [#239]
|
- Remove `AsyncRead` and `AsyncWrite` trait bound for `socket::FromStream` trait.
|
||||||
|
|
||||||
[#215]: https://github.com/actix/actix-net/pull/215
|
|
||||||
[#223]: https://github.com/actix/actix-net/pull/223
|
|
||||||
[#239]: https://github.com/actix/actix-net/pull/239
|
|
||||||
|
|
||||||
## 1.0.4
|
## 1.0.4
|
||||||
|
|
||||||
- Update actix-codec to 0.3.0.
|
- Update actix-codec to 0.3.0.
|
||||||
- Workers must be greater than 0. [#167]
|
- Workers must be greater than 0.
|
||||||
|
|
||||||
[#167]: https://github.com/actix/actix-net/pull/167
|
|
||||||
|
|
||||||
## 1.0.3
|
## 1.0.3
|
||||||
|
|
||||||
- Replace deprecated `net2` crate with `socket2` [#140]
|
- Replace deprecated `net2` crate with `socket2`.
|
||||||
|
|
||||||
[#140]: https://github.com/actix/actix-net/pull/140
|
|
||||||
|
|
||||||
## 1.0.2
|
## 1.0.2
|
||||||
|
|
||||||
- Avoid error by calling `reregister()` on Windows [#103]
|
- Avoid error by calling `reregister()` on Windows.
|
||||||
|
|
||||||
[#103]: https://github.com/actix/actix-net/pull/103
|
|
||||||
|
|
||||||
## 1.0.1
|
## 1.0.1
|
||||||
|
|
||||||
|
25
actix-server/Cargo.toml
Executable file → Normal file
25
actix-server/Cargo.toml
Executable file → Normal file
@@ -1,45 +1,50 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-server"
|
name = "actix-server"
|
||||||
version = "2.3.0"
|
version = "2.5.1"
|
||||||
authors = [
|
authors = [
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
"Nikolay Kim <fafhrd91@gmail.com>",
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
"Rob Ede <robjtede@icloud.com>",
|
||||||
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",
|
"Ali MJ Al-Nasrawy <alimjalnasrawy@gmail.com>",
|
||||||
]
|
]
|
||||||
description = "General purpose TCP server built for the Actix ecosystem"
|
description = "General purpose TCP server built for the Actix ecosystem"
|
||||||
keywords = ["network", "tcp", "server", "framework", "async"]
|
keywords = ["network", "tcp", "server", "framework", "async"]
|
||||||
categories = ["network-programming", "asynchronous"]
|
categories = ["network-programming", "asynchronous"]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
repository = "https://github.com/actix/actix-net/tree/master/actix-server"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[package.metadata.cargo_check_external_types]
|
||||||
|
allowed_external_types = ["tokio::*"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
io-uring = ["tokio-uring", "actix-rt/io-uring"]
|
io-uring = ["tokio-uring", "actix-rt/io-uring"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-rt = { version = "2.8", default-features = false }
|
actix-rt = { version = "2.10", default-features = false }
|
||||||
actix-service = "2"
|
actix-service = "2"
|
||||||
actix-utils = "3"
|
actix-utils = "3"
|
||||||
|
|
||||||
futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] }
|
futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] }
|
||||||
futures-util = { 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"] }
|
mio = { version = "1", features = ["os-poll", "net"] }
|
||||||
socket2 = "0.5"
|
socket2 = "0.5"
|
||||||
tokio = { version = "1.23.1", features = ["sync"] }
|
tokio = { version = "1.23.1", features = ["sync"] }
|
||||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||||
|
|
||||||
# runtime for `io-uring` feature
|
# runtime for `io-uring` feature
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
tokio-uring = { version = "0.4", optional = true }
|
tokio-uring = { version = "0.5", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-codec = "0.5"
|
actix-codec = "0.5"
|
||||||
actix-rt = "2.8"
|
actix-rt = "2.8"
|
||||||
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
env_logger = "0.10"
|
|
||||||
futures-util = { version = "0.3.17", default-features = false, features = ["sink", "async-await-macro"] }
|
futures-util = { version = "0.3.17", default-features = false, features = ["sink", "async-await-macro"] }
|
||||||
|
pretty_env_logger = "0.5"
|
||||||
tokio = { version = "1.23.1", features = ["io-util", "rt-multi-thread", "macros", "fs"] }
|
tokio = { version = "1.23.1", features = ["io-util", "rt-multi-thread", "macros", "fs"] }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
@@ -2,14 +2,20 @@
|
|||||||
|
|
||||||
> General purpose TCP server built for the Actix ecosystem.
|
> General purpose TCP server built for the Actix ecosystem.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-server)
|
[](https://crates.io/crates/actix-server)
|
||||||
[](https://docs.rs/actix-server/2.3.0)
|
[](https://docs.rs/actix-server/2.5.1)
|
||||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
[](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.5.1)
|
||||||

|

|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
- [Library Documentation](https://docs.rs/actix-server)
|
- [Library Documentation](https://docs.rs/actix-server)
|
||||||
- [Examples](/actix-server/examples)
|
- [Examples](/actix-server/examples)
|
||||||
|
@@ -8,6 +8,8 @@
|
|||||||
//!
|
//!
|
||||||
//! Follow the prompt and enter a file path, relative or absolute.
|
//! Follow the prompt and enter a file path, relative or absolute.
|
||||||
|
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use actix_codec::{Framed, LinesCodec};
|
use actix_codec::{Framed, LinesCodec};
|
||||||
@@ -18,7 +20,8 @@ use futures_util::{SinkExt as _, StreamExt as _};
|
|||||||
use tokio::{fs::File, io::AsyncReadExt as _};
|
use tokio::{fs::File, io::AsyncReadExt as _};
|
||||||
|
|
||||||
async fn run() -> io::Result<()> {
|
async fn run() -> io::Result<()> {
|
||||||
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
pretty_env_logger::formatted_timed_builder()
|
||||||
|
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"));
|
||||||
|
|
||||||
let addr = ("127.0.0.1", 8080);
|
let addr = ("127.0.0.1", 8080);
|
||||||
tracing::info!("starting server on port: {}", &addr.0);
|
tracing::info!("starting server on port: {}", &addr.0);
|
||||||
|
@@ -25,7 +25,8 @@ use futures_util::future::ok;
|
|||||||
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
|
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
|
||||||
|
|
||||||
async fn run() -> io::Result<()> {
|
async fn run() -> io::Result<()> {
|
||||||
env_logger::init_from_env(env_logger::Env::default().default_filter_or("info"));
|
pretty_env_logger::formatted_timed_builder()
|
||||||
|
.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info"));
|
||||||
|
|
||||||
let count = Arc::new(AtomicUsize::new(0));
|
let count = Arc::new(AtomicUsize::new(0));
|
||||||
|
|
||||||
|
@@ -2,7 +2,6 @@ use std::{io, num::NonZeroUsize, time::Duration};
|
|||||||
|
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||||
use tracing::{info, trace};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
server::ServerCommand,
|
server::ServerCommand,
|
||||||
@@ -14,7 +13,9 @@ use crate::{
|
|||||||
|
|
||||||
/// Multipath TCP (MPTCP) preference.
|
/// 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)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum MpTcp {
|
pub enum MpTcp {
|
||||||
/// MPTCP will not be used when binding sockets.
|
/// MPTCP will not be used when binding sockets.
|
||||||
@@ -69,19 +70,19 @@ impl ServerBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set number of workers to start.
|
/// Sets number of workers to start.
|
||||||
|
///
|
||||||
|
/// See [`bind()`](Self::bind()) for more details on how worker count affects the number of
|
||||||
|
/// server factory instantiations.
|
||||||
|
///
|
||||||
|
/// The default worker count is the determined by [`std::thread::available_parallelism()`]. See
|
||||||
|
/// its documentation to determine what behavior you should expect when server is run.
|
||||||
///
|
///
|
||||||
/// `num` must be greater than 0.
|
/// `num` must be greater than 0.
|
||||||
///
|
///
|
||||||
/// The default worker count is the number of physical CPU cores available. If your benchmark
|
|
||||||
/// 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
|
||||||
///
|
///
|
||||||
/// Panics if `num` is 0.
|
/// Panics if `num` is 0.
|
||||||
///
|
|
||||||
/// [`num_cpus`]: https://docs.rs/num_cpus
|
|
||||||
pub fn workers(mut self, num: usize) -> Self {
|
pub fn workers(mut self, num: usize) -> Self {
|
||||||
assert_ne!(num, 0, "workers must be greater than 0");
|
assert_ne!(num, 0, "workers must be greater than 0");
|
||||||
self.threads = num;
|
self.threads = num;
|
||||||
@@ -155,13 +156,15 @@ impl ServerBuilder {
|
|||||||
self.max_concurrent_connections(num)
|
self.max_concurrent_connections(num)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stop Actix `System` after server shutdown.
|
/// Sets flag to stop Actix `System` after server shutdown.
|
||||||
|
///
|
||||||
|
/// This has no effect when server is running in a Tokio-only runtime.
|
||||||
pub fn system_exit(mut self) -> Self {
|
pub fn system_exit(mut self) -> Self {
|
||||||
self.exit = true;
|
self.exit = true;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable OS signal handling.
|
/// Disables OS signal handling.
|
||||||
pub fn disable_signals(mut self) -> Self {
|
pub fn disable_signals(mut self) -> Self {
|
||||||
self.listen_os_signals = false;
|
self.listen_os_signals = false;
|
||||||
self
|
self
|
||||||
@@ -179,25 +182,49 @@ impl ServerBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add new service to the server.
|
/// Adds new service to the server.
|
||||||
pub fn bind<F, U, N>(mut self, name: N, addr: U, factory: F) -> io::Result<Self>
|
///
|
||||||
|
/// Note that, if a DNS lookup is required, resolving hostnames is a blocking operation.
|
||||||
|
///
|
||||||
|
/// # Worker Count
|
||||||
|
///
|
||||||
|
/// The `factory` will be instantiated multiple times in most scenarios. The number of
|
||||||
|
/// instantiations is number of [`workers`](Self::workers()) × number of sockets resolved by
|
||||||
|
/// `addrs`.
|
||||||
|
///
|
||||||
|
/// For example, if you've manually set [`workers`](Self::workers()) to 2, and use `127.0.0.1`
|
||||||
|
/// as the bind `addrs`, then `factory` will be instantiated twice. However, using `localhost`
|
||||||
|
/// as the bind `addrs` can often resolve to both `127.0.0.1` (IPv4) _and_ `::1` (IPv6), causing
|
||||||
|
/// the `factory` to be instantiated 4 times (2 workers × 2 bind addresses).
|
||||||
|
///
|
||||||
|
/// Using a bind address of `0.0.0.0`, which signals to use all interfaces, may also multiple
|
||||||
|
/// the number of instantiations in a similar way.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an `io::Error` if:
|
||||||
|
/// - `addrs` cannot be resolved into one or more socket addresses;
|
||||||
|
/// - all the resolved socket addresses are already bound.
|
||||||
|
pub fn bind<F, U, N>(mut self, name: N, addrs: U, factory: F) -> io::Result<Self>
|
||||||
where
|
where
|
||||||
F: ServerServiceFactory<TcpStream>,
|
F: ServerServiceFactory<TcpStream>,
|
||||||
U: ToSocketAddrs,
|
U: ToSocketAddrs,
|
||||||
N: AsRef<str>,
|
N: AsRef<str>,
|
||||||
{
|
{
|
||||||
let sockets = bind_addr(addr, self.backlog, &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 {
|
for lst in sockets {
|
||||||
let token = self.next_token();
|
let token = self.next_token();
|
||||||
|
|
||||||
self.factories.push(StreamNewService::create(
|
self.factories.push(StreamNewService::create(
|
||||||
name.as_ref().to_string(),
|
name.as_ref().to_string(),
|
||||||
token,
|
token,
|
||||||
factory.clone(),
|
factory.clone(),
|
||||||
lst.local_addr()?,
|
lst.local_addr()?,
|
||||||
));
|
));
|
||||||
|
|
||||||
self.sockets
|
self.sockets
|
||||||
.push((token, name.as_ref().to_string(), MioListener::Tcp(lst)));
|
.push((token, name.as_ref().to_string(), MioListener::Tcp(lst)));
|
||||||
}
|
}
|
||||||
@@ -205,7 +232,12 @@ impl ServerBuilder {
|
|||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add new service to the server.
|
/// Adds service to the server using a socket listener already bound.
|
||||||
|
///
|
||||||
|
/// # Worker Count
|
||||||
|
///
|
||||||
|
/// The `factory` will be instantiated multiple times in most scenarios. The number of
|
||||||
|
/// instantiations is: number of [`workers`](Self::workers()).
|
||||||
pub fn listen<F, N: AsRef<str>>(
|
pub fn listen<F, N: AsRef<str>>(
|
||||||
mut self,
|
mut self,
|
||||||
name: N,
|
name: N,
|
||||||
@@ -237,7 +269,7 @@ impl ServerBuilder {
|
|||||||
if self.sockets.is_empty() {
|
if self.sockets.is_empty() {
|
||||||
panic!("Server should have at least one bound socket");
|
panic!("Server should have at least one bound socket");
|
||||||
} else {
|
} else {
|
||||||
info!("starting {} workers", self.threads);
|
tracing::info!("starting {} workers", self.threads);
|
||||||
Server::new(self)
|
Server::new(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -251,7 +283,12 @@ impl ServerBuilder {
|
|||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
impl ServerBuilder {
|
impl ServerBuilder {
|
||||||
/// Add new unix domain service to the server.
|
/// Adds new service to the server using a UDS (unix domain socket) address.
|
||||||
|
///
|
||||||
|
/// # Worker Count
|
||||||
|
///
|
||||||
|
/// The `factory` will be instantiated multiple times in most scenarios. The number of
|
||||||
|
/// instantiations is: number of [`workers`](Self::workers()).
|
||||||
pub fn bind_uds<F, U, N>(self, name: N, addr: U, factory: F) -> io::Result<Self>
|
pub fn bind_uds<F, U, N>(self, name: N, addr: U, factory: F) -> io::Result<Self>
|
||||||
where
|
where
|
||||||
F: ServerServiceFactory<actix_rt::net::UnixStream>,
|
F: ServerServiceFactory<actix_rt::net::UnixStream>,
|
||||||
@@ -271,9 +308,14 @@ impl ServerBuilder {
|
|||||||
self.listen_uds(name, lst, factory)
|
self.listen_uds(name, lst, factory)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add new unix domain service to the server.
|
/// Adds new service to the server using a UDS (unix domain socket) listener already bound.
|
||||||
///
|
///
|
||||||
/// Useful when running as a systemd service and a socket FD is acquired externally.
|
/// Useful when running as a systemd service and a socket FD is acquired externally.
|
||||||
|
///
|
||||||
|
/// # Worker Count
|
||||||
|
///
|
||||||
|
/// The `factory` will be instantiated multiple times in most scenarios. The number of
|
||||||
|
/// instantiations is: number of [`workers`](Self::workers()).
|
||||||
pub fn listen_uds<F, N: AsRef<str>>(
|
pub fn listen_uds<F, N: AsRef<str>>(
|
||||||
mut self,
|
mut self,
|
||||||
name: N,
|
name: N,
|
||||||
@@ -284,17 +326,22 @@ impl ServerBuilder {
|
|||||||
F: ServerServiceFactory<actix_rt::net::UnixStream>,
|
F: ServerServiceFactory<actix_rt::net::UnixStream>,
|
||||||
{
|
{
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
|
||||||
lst.set_nonblocking(true)?;
|
lst.set_nonblocking(true)?;
|
||||||
|
|
||||||
let token = self.next_token();
|
let token = self.next_token();
|
||||||
let addr = crate::socket::StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
|
let addr = crate::socket::StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
|
||||||
|
|
||||||
self.factories.push(StreamNewService::create(
|
self.factories.push(StreamNewService::create(
|
||||||
name.as_ref().to_string(),
|
name.as_ref().to_string(),
|
||||||
token,
|
token,
|
||||||
factory,
|
factory,
|
||||||
addr,
|
addr,
|
||||||
));
|
));
|
||||||
|
|
||||||
self.sockets
|
self.sockets
|
||||||
.push((token, name.as_ref().to_string(), MioListener::from(lst)));
|
.push((token, name.as_ref().to_string(), MioListener::from(lst)));
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
//! General purpose TCP server.
|
//! General purpose TCP server.
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible)]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
|
@@ -183,11 +183,6 @@ impl ServerInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run_sync(mut builder: ServerBuilder) -> io::Result<(Self, ServerEventMultiplexer)> {
|
fn run_sync(mut builder: ServerBuilder) -> io::Result<(Self, ServerEventMultiplexer)> {
|
||||||
let sockets = mem::take(&mut builder.sockets)
|
|
||||||
.into_iter()
|
|
||||||
.map(|t| (t.0, t.2))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Give log information on what runtime will be used.
|
// Give log information on what runtime will be used.
|
||||||
let is_actix = actix_rt::System::try_current().is_some();
|
let is_actix = actix_rt::System::try_current().is_some();
|
||||||
let is_tokio = tokio::runtime::Handle::try_current().is_ok();
|
let is_tokio = tokio::runtime::Handle::try_current().is_ok();
|
||||||
@@ -207,6 +202,11 @@ 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 (waker_queue, worker_handles, accept_handle) = Accept::start(sockets, &builder)?;
|
||||||
|
|
||||||
let mux = ServerEventMultiplexer {
|
let mux = ServerEventMultiplexer {
|
||||||
|
@@ -126,7 +126,7 @@ pub(crate) enum SocketAddr {
|
|||||||
Unknown,
|
Unknown,
|
||||||
Tcp(StdSocketAddr),
|
Tcp(StdSocketAddr),
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
Uds(mio::net::SocketAddr),
|
Uds(std::os::unix::net::SocketAddr),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SocketAddr {
|
impl fmt::Display for SocketAddr {
|
||||||
@@ -160,6 +160,7 @@ pub enum MioStream {
|
|||||||
|
|
||||||
/// Helper trait for converting a Mio stream into a Tokio stream.
|
/// Helper trait for converting a Mio stream into a Tokio stream.
|
||||||
pub trait FromStream: Sized {
|
pub trait FromStream: Sized {
|
||||||
|
/// Creates stream from a `mio` stream.
|
||||||
fn from_mio(sock: MioStream) -> io::Result<Self>;
|
fn from_mio(sock: MioStream) -> io::Result<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -123,7 +123,9 @@ impl TestServerHandle {
|
|||||||
|
|
||||||
/// Connect to server, returning a Tokio `TcpStream`.
|
/// Connect to server, returning a Tokio `TcpStream`.
|
||||||
pub fn connect(&self) -> io::Result<TcpStream> {
|
pub fn connect(&self) -> io::Result<TcpStream> {
|
||||||
TcpStream::from_std(net::TcpStream::connect(self.addr)?)
|
let stream = net::TcpStream::connect(self.addr)?;
|
||||||
|
stream.set_nonblocking(true)?;
|
||||||
|
TcpStream::from_std(stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
#![allow(clippy::let_underscore_future)]
|
#![allow(clippy::let_underscore_future, missing_docs)]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
net,
|
net,
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::net;
|
use std::net;
|
||||||
|
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
@@ -69,5 +71,7 @@ async fn new_with_builder() {
|
|||||||
srv.connect().unwrap();
|
srv.connect().unwrap();
|
||||||
|
|
||||||
// connect to alt service defined in custom ServerBuilder
|
// connect to alt service defined in custom ServerBuilder
|
||||||
TcpStream::from_std(net::TcpStream::connect(alt_addr).unwrap()).unwrap();
|
let stream = net::TcpStream::connect(alt_addr).unwrap();
|
||||||
|
stream.set_nonblocking(true).unwrap();
|
||||||
|
TcpStream::from_std(stream).unwrap();
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
- Minimum supported Rust version (MSRV) is now 1.65.
|
## 2.0.3
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
## 2.0.2
|
## 2.0.2
|
||||||
|
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-service"
|
name = "actix-service"
|
||||||
version = "2.0.2"
|
version = "2.0.3"
|
||||||
authors = [
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
|
||||||
]
|
|
||||||
description = "Service trait and combinators for representing asynchronous request/response operations."
|
description = "Service trait and combinators for representing asynchronous request/response operations."
|
||||||
keywords = ["network", "framework", "async", "futures", "service"]
|
keywords = ["network", "framework", "async", "futures", "service"]
|
||||||
categories = ["network-programming", "asynchronous", "no-std"]
|
categories = ["network-programming", "asynchronous", "no-std"]
|
||||||
@@ -15,10 +12,12 @@ rust-version.workspace = true
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures-core = { version = "0.3.17", default-features = false }
|
futures-core = { version = "0.3.17", default-features = false }
|
||||||
paste = "1"
|
|
||||||
pin-project-lite = "0.2"
|
pin-project-lite = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2"
|
actix-rt = "2"
|
||||||
actix-utils = "3"
|
actix-utils = "3"
|
||||||
futures-util = { version = "0.3.17", default-features = false }
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
@@ -3,10 +3,10 @@
|
|||||||
> Service trait and combinators for representing asynchronous request/response operations.
|
> Service trait and combinators for representing asynchronous request/response operations.
|
||||||
|
|
||||||
[](https://crates.io/crates/actix-service)
|
[](https://crates.io/crates/actix-service)
|
||||||
[](https://docs.rs/actix-service/2.0.2)
|
[](https://docs.rs/actix-service/2.0.3)
|
||||||
[](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
|
[](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
|
||||||

|

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

|

|
||||||
[](https://discord.gg/NWpN5mmg3x)
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use std::{future::Future, sync::mpsc, time::Duration};
|
use std::{future::Future, sync::mpsc, time::Duration};
|
||||||
|
|
||||||
async fn oracle<F, Fut>(f: F) -> (u32, u32)
|
async fn oracle<F, Fut>(f: F) -> (u32, u32)
|
||||||
|
@@ -208,15 +208,13 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use core::task::Poll;
|
|
||||||
|
|
||||||
use futures_util::future::lazy;
|
use futures_util::future::lazy;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
ok,
|
ok,
|
||||||
pipeline::{pipeline, pipeline_factory},
|
pipeline::{pipeline, pipeline_factory},
|
||||||
Ready, Service, ServiceFactory,
|
Ready,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@@ -3,36 +3,38 @@
|
|||||||
use alloc::{boxed::Box, rc::Rc};
|
use alloc::{boxed::Box, rc::Rc};
|
||||||
use core::{future::Future, pin::Pin};
|
use core::{future::Future, pin::Pin};
|
||||||
|
|
||||||
use paste::paste;
|
|
||||||
|
|
||||||
use crate::{Service, ServiceFactory};
|
use crate::{Service, ServiceFactory};
|
||||||
|
|
||||||
/// A boxed future with no send bound or lifetime parameters.
|
/// A boxed future with no send bound or lifetime parameters.
|
||||||
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
|
pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
|
||||||
|
|
||||||
macro_rules! service_object {
|
/// Type alias for service trait object using [`Box`].
|
||||||
($name: ident, $type: tt, $fn_name: ident) => {
|
pub type BoxService<Req, Res, Err> =
|
||||||
paste! {
|
Box<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>;
|
||||||
#[doc = "Type alias for service trait object using `" $type "`."]
|
|
||||||
pub type $name<Req, Res, Err> = $type<
|
|
||||||
dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
#[doc = "Wraps service as a trait object using [`" $name "`]."]
|
/// Wraps service as a trait object using [`BoxService`].
|
||||||
pub fn $fn_name<S, Req>(service: S) -> $name<Req, S::Response, S::Error>
|
pub fn service<S, Req>(service: S) -> BoxService<Req, S::Response, S::Error>
|
||||||
where
|
where
|
||||||
S: Service<Req> + 'static,
|
S: Service<Req> + 'static,
|
||||||
Req: 'static,
|
Req: 'static,
|
||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
{
|
{
|
||||||
$type::new(ServiceWrapper::new(service))
|
Box::new(ServiceWrapper::new(service))
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
service_object!(BoxService, Box, service);
|
/// Type alias for service trait object using [`Rc`].
|
||||||
service_object!(RcService, Rc, rc_service);
|
pub type RcService<Req, Res, Err> =
|
||||||
|
Rc<dyn Service<Req, Response = Res, Error = Err, Future = BoxFuture<Result<Res, Err>>>>;
|
||||||
|
|
||||||
|
/// Wraps service as a trait object using [`RcService`].
|
||||||
|
pub fn rc_service<S, Req>(service: S) -> RcService<Req, S::Response, S::Error>
|
||||||
|
where
|
||||||
|
S: Service<Req> + 'static,
|
||||||
|
Req: 'static,
|
||||||
|
S::Future: 'static,
|
||||||
|
{
|
||||||
|
Rc::new(ServiceWrapper::new(service))
|
||||||
|
}
|
||||||
|
|
||||||
struct ServiceWrapper<S> {
|
struct ServiceWrapper<S> {
|
||||||
inner: S,
|
inner: S,
|
||||||
|
@@ -44,7 +44,7 @@ pub trait ServiceExt<Req>: Service<Req> {
|
|||||||
/// Call another service after call to this one has resolved successfully.
|
/// Call another service after call to this one has resolved successfully.
|
||||||
///
|
///
|
||||||
/// This function can be used to chain two services together and ensure that the second service
|
/// This function can be used to chain two services together and ensure that the second service
|
||||||
/// isn't called until call to the fist service have finished. Result of the call to the first
|
/// isn't called until call to the first service have finished. Result of the call to the first
|
||||||
/// service is used as an input parameter for the second service's call.
|
/// service is used as an input parameter for the second service's call.
|
||||||
///
|
///
|
||||||
/// Note that this function consumes the receiving service and returns a wrapped version of it.
|
/// Note that this function consumes the receiving service and returns a wrapped version of it.
|
||||||
|
@@ -351,7 +351,6 @@ mod tests {
|
|||||||
use futures_util::future::lazy;
|
use futures_util::future::lazy;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{ok, Service, ServiceFactory};
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_fn_service() {
|
async fn test_fn_service() {
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
//! See [`Service`] docs for information on this crate's foundational trait.
|
//! See [`Service`] docs for information on this crate's foundational trait.
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible, missing_docs)]
|
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
@@ -202,9 +202,7 @@ mod tests {
|
|||||||
use futures_util::future::lazy;
|
use futures_util::future::lazy;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{ok, IntoServiceFactory, Ready, ServiceExt, ServiceFactoryExt};
|
||||||
ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Srv;
|
struct Srv;
|
||||||
|
|
||||||
|
@@ -205,9 +205,7 @@ mod tests {
|
|||||||
use futures_util::future::lazy;
|
use futures_util::future::lazy;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{err, ok, IntoServiceFactory, Ready, ServiceExt, ServiceFactoryExt};
|
||||||
err, ok, IntoServiceFactory, Ready, Service, ServiceExt, ServiceFactory, ServiceFactoryExt,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Srv;
|
struct Srv;
|
||||||
|
|
||||||
|
@@ -52,7 +52,7 @@ where
|
|||||||
/// Call another service after call to this one has resolved successfully.
|
/// Call another service after call to this one has resolved successfully.
|
||||||
///
|
///
|
||||||
/// This function can be used to chain two services together and ensure that
|
/// This function can be used to chain two services together and ensure that
|
||||||
/// the second service isn't called until call to the fist service have
|
/// the second service isn't called until call to the first service have
|
||||||
/// finished. Result of the call to the first service is used as an
|
/// finished. Result of the call to the first service is used as an
|
||||||
/// input parameter for the second service's call.
|
/// input parameter for the second service's call.
|
||||||
///
|
///
|
||||||
|
@@ -226,9 +226,9 @@ mod tests {
|
|||||||
use actix_utils::future::{ready, Ready};
|
use actix_utils::future::{ready, Ready};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::Service;
|
|
||||||
|
|
||||||
// pseudo-doctest for Transform trait
|
// pseudo-doctest for Transform trait
|
||||||
|
#[allow(unused)]
|
||||||
pub struct TimeoutTransform {
|
pub struct TimeoutTransform {
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
}
|
}
|
||||||
@@ -250,6 +250,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// pseudo-doctest for Transform trait
|
// pseudo-doctest for Transform trait
|
||||||
|
#[allow(unused)]
|
||||||
pub struct Timeout<S> {
|
pub struct Timeout<S> {
|
||||||
service: S,
|
service: S,
|
||||||
_timeout: Duration,
|
_timeout: Duration,
|
||||||
|
@@ -2,6 +2,24 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
|
## 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
|
## 3.1.1
|
||||||
|
|
||||||
- Fix `rustls` v0.21 version requirement.
|
- Fix `rustls` v0.21 version requirement.
|
||||||
@@ -9,8 +27,8 @@
|
|||||||
## 3.1.0
|
## 3.1.0
|
||||||
|
|
||||||
- Support Rustls v0.21.
|
- Support Rustls v0.21.
|
||||||
- Added `{accept, connect}::rustls_0_21` modules.
|
- Add `{accept, connect}::rustls_0_21` modules.
|
||||||
- Added `{accept, connect}::rustls_0_20` alias for `{accept, connect}::rustls` modules.
|
- Add `{accept, connect}::rustls_0_20` alias for `{accept, connect}::rustls` modules.
|
||||||
- Minimum supported Rust version (MSRV) is now 1.65.
|
- Minimum supported Rust version (MSRV) is now 1.65.
|
||||||
|
|
||||||
## 3.0.4
|
## 3.0.4
|
||||||
|
80
actix-tls/Cargo.toml
Executable file → Normal file
80
actix-tls/Cargo.toml
Executable file → Normal file
@@ -1,10 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-tls"
|
name = "actix-tls"
|
||||||
version = "3.1.1"
|
version = "3.4.0"
|
||||||
authors = [
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
|
||||||
]
|
|
||||||
description = "TLS acceptor and connector services for Actix ecosystem"
|
description = "TLS acceptor and connector services for Actix ecosystem"
|
||||||
keywords = ["network", "tls", "ssl", "async", "transport"]
|
keywords = ["network", "tls", "ssl", "async", "transport"]
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
repository = "https://github.com/actix/actix-net.git"
|
||||||
@@ -17,10 +14,13 @@ rust-version.workspace = true
|
|||||||
all-features = true
|
all-features = true
|
||||||
rustdoc-args = ["--cfg", "docsrs"]
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|
||||||
|
[package.metadata.cargo_check_external_types]
|
||||||
|
allowed_external_types = ["actix_service::*", "actix_utils::*", "futures_core::*", "tokio::*"]
|
||||||
|
|
||||||
[package.metadata.cargo-machete]
|
[package.metadata.cargo-machete]
|
||||||
ignored = [
|
ignored = [
|
||||||
"rustls_021", # specified to force version with add_trust_anchors method
|
"rustls_021", # specified to force version with add_trust_anchors method
|
||||||
"rustls_webpki_0101", # specified to force secure version
|
"rustls_webpki_0101", # specified to force secure version
|
||||||
]
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
@@ -33,28 +33,41 @@ accept = []
|
|||||||
connect = []
|
connect = []
|
||||||
|
|
||||||
# use openssl impls
|
# use openssl impls
|
||||||
openssl = ["tls-openssl", "tokio-openssl"]
|
openssl = ["dep:tls-openssl", "dep:tokio-openssl"]
|
||||||
|
|
||||||
# alias for backwards compat
|
# alias for backwards compat
|
||||||
rustls = ["rustls-0_20"]
|
rustls = ["rustls-0_20"]
|
||||||
|
|
||||||
# use rustls v0.20 impls
|
# 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
|
# 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
|
# use native-tls impls
|
||||||
native-tls = ["tokio-native-tls"]
|
native-tls = ["dep:tokio-native-tls"]
|
||||||
|
|
||||||
# support http::Uri as connect address
|
# support http::Uri as connect address
|
||||||
uri = ["http"]
|
uri = ["dep:http-0_2", "dep:http-1"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-rt = { version = "2.2", default-features = false }
|
actix-rt = { version = "2.2", default-features = false }
|
||||||
actix-service = "2"
|
actix-service = "2"
|
||||||
actix-utils = "3"
|
actix-utils = "3"
|
||||||
|
|
||||||
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||||
impl-more = "0.1"
|
impl-more = "0.1"
|
||||||
pin-project-lite = "0.2.7"
|
pin-project-lite = "0.2.7"
|
||||||
@@ -63,37 +76,60 @@ tokio-util = "0.7"
|
|||||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||||
|
|
||||||
# uri
|
# uri
|
||||||
http = { version = "0.2.3", optional = true }
|
http-0_2 = { package = "http", version = "0.2.3", optional = true }
|
||||||
|
http-1 = { package = "http", version = "1", optional = true }
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
tls-openssl = { package = "openssl", version = "0.10.55", optional = true }
|
tls-openssl = { package = "openssl", version = "0.10.55", optional = true }
|
||||||
tokio-openssl = { version = "0.6", optional = true }
|
tokio-openssl = { version = "0.6", optional = true }
|
||||||
|
|
||||||
|
# rustls PKI types
|
||||||
|
rustls-pki-types-1 = { package = "rustls-pki-types", version = "1", optional = true }
|
||||||
|
|
||||||
# rustls v0.20
|
# rustls v0.20
|
||||||
tokio-rustls-023 = { package = "tokio-rustls", version = "0.23", optional = true }
|
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 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 }
|
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-025 = { package = "webpki-roots", version = "0.25", optional = true }
|
||||||
|
webpki-roots-026 = { package = "webpki-roots", version = "0.26", optional = true }
|
||||||
|
|
||||||
|
# native root certificates for rustls impls
|
||||||
|
rustls-native-certs-06 = { package = "rustls-native-certs", version = "0.6", optional = true }
|
||||||
|
rustls-native-certs-07 = { package = "rustls-native-certs", version = "0.7", optional = true }
|
||||||
|
|
||||||
# native-tls
|
# native-tls
|
||||||
tokio-native-tls = { version = "0.3", optional = true }
|
tokio-native-tls = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(any())'.dependencies]
|
||||||
|
rustls-021 = { package = "rustls", version = "0.21.6", optional = true } # force version with add_trust_anchors method
|
||||||
|
rustls-webpki-0101 = { package = "rustls-webpki", version = "0.101.4", optional = true } # force secure version
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-codec = "0.5"
|
actix-codec = "0.5"
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-server = "2"
|
actix-server = "2"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
env_logger = "0.10"
|
|
||||||
futures-util = { version = "0.3.17", default-features = false, features = ["sink"] }
|
futures-util = { version = "0.3.17", default-features = false, features = ["sink"] }
|
||||||
rcgen = "0.11"
|
itertools = "0.14"
|
||||||
rustls-pemfile = "1"
|
pretty_env_logger = "0.5"
|
||||||
tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", features = ["dangerous_configuration"] }
|
rcgen = "0.13"
|
||||||
|
rustls-pemfile = "2"
|
||||||
|
tokio-rustls-026 = { package = "tokio-rustls", version = "0.26" }
|
||||||
trust-dns-resolver = "0.23"
|
trust-dns-resolver = "0.23"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "accept-rustls"
|
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
|
//! http --verify=false https://127.0.0.1:8443
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
#[rustfmt::skip]
|
|
||||||
// this `use` is only exists because of how we have organised the crate
|
// this `use` is only exists because of how we have organised the crate
|
||||||
// it is not necessary for your actual code; you should import from `rustls` normally
|
// it is not necessary for your actual code; you should import from `rustls` normally
|
||||||
use tokio_rustls_024::rustls;
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{self, BufReader},
|
io::{self, BufReader},
|
||||||
@@ -33,15 +30,19 @@ use std::{
|
|||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use actix_server::Server;
|
use actix_server::Server;
|
||||||
use actix_service::ServiceFactoryExt as _;
|
use actix_service::ServiceFactoryExt as _;
|
||||||
use actix_tls::accept::rustls_0_21::{Acceptor as RustlsAcceptor, TlsStream};
|
use actix_tls::accept::rustls_0_23::{Acceptor as RustlsAcceptor, TlsStream};
|
||||||
use futures_util::future::ok;
|
use futures_util::future::ok;
|
||||||
use rustls::{server::ServerConfig, Certificate, PrivateKey};
|
use itertools::Itertools as _;
|
||||||
|
use rustls::server::ServerConfig;
|
||||||
use rustls_pemfile::{certs, rsa_private_keys};
|
use rustls_pemfile::{certs, rsa_private_keys};
|
||||||
|
use rustls_pki_types_1::PrivateKeyDer;
|
||||||
|
use tokio_rustls_026::rustls;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
#[actix_rt::main]
|
#[actix_rt::main]
|
||||||
async fn main() -> io::Result<()> {
|
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")
|
let root_path = env!("CARGO_MANIFEST_DIR")
|
||||||
.parse::<PathBuf>()
|
.parse::<PathBuf>()
|
||||||
@@ -54,17 +55,15 @@ async fn main() -> io::Result<()> {
|
|||||||
let cert_file = &mut BufReader::new(File::open(cert_path).unwrap());
|
let cert_file = &mut BufReader::new(File::open(cert_path).unwrap());
|
||||||
let key_file = &mut BufReader::new(File::open(key_path).unwrap());
|
let key_file = &mut BufReader::new(File::open(key_path).unwrap());
|
||||||
|
|
||||||
let cert_chain = certs(cert_file)
|
let cert_chain = certs(cert_file);
|
||||||
.unwrap()
|
let mut keys = rsa_private_keys(key_file);
|
||||||
.into_iter()
|
|
||||||
.map(Certificate)
|
|
||||||
.collect();
|
|
||||||
let mut keys = rsa_private_keys(key_file).unwrap();
|
|
||||||
|
|
||||||
let tls_config = ServerConfig::builder()
|
let tls_config = ServerConfig::builder()
|
||||||
.with_safe_defaults()
|
|
||||||
.with_no_client_auth()
|
.with_no_client_auth()
|
||||||
.with_single_cert(cert_chain, PrivateKey(keys.remove(0)))
|
.with_single_cert(
|
||||||
|
cert_chain.try_collect::<_, Vec<_>, _>()?,
|
||||||
|
PrivateKeyDer::Pkcs1(keys.next().unwrap()?),
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let tls_acceptor = RustlsAcceptor::new(tls_config);
|
let tls_acceptor = RustlsAcceptor::new(tls_config);
|
||||||
|
@@ -22,6 +22,12 @@ pub use rustls_0_20 as rustls;
|
|||||||
#[cfg(feature = "rustls-0_21")]
|
#[cfg(feature = "rustls-0_21")]
|
||||||
pub mod rustls_0_21;
|
pub mod rustls_0_21;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls-0_22")]
|
||||||
|
pub mod rustls_0_22;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls-0_23")]
|
||||||
|
pub mod rustls_0_23;
|
||||||
|
|
||||||
#[cfg(feature = "native-tls")]
|
#[cfg(feature = "native-tls")]
|
||||||
pub mod native_tls;
|
pub mod native_tls;
|
||||||
|
|
||||||
@@ -31,6 +37,8 @@ pub(crate) static MAX_CONN: AtomicUsize = AtomicUsize::new(256);
|
|||||||
feature = "openssl",
|
feature = "openssl",
|
||||||
feature = "rustls-0_20",
|
feature = "rustls-0_20",
|
||||||
feature = "rustls-0_21",
|
feature = "rustls-0_21",
|
||||||
|
feature = "rustls-0_22",
|
||||||
|
feature = "rustls-0_23",
|
||||||
feature = "native-tls",
|
feature = "native-tls",
|
||||||
))]
|
))]
|
||||||
pub(crate) const DEFAULT_TLS_HANDSHAKE_TIMEOUT: std::time::Duration =
|
pub(crate) const DEFAULT_TLS_HANDSHAKE_TIMEOUT: std::time::Duration =
|
||||||
|
@@ -23,7 +23,7 @@ use actix_utils::{
|
|||||||
};
|
};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
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 tokio_rustls_023 as tokio_rustls;
|
||||||
|
|
||||||
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
||||||
@@ -92,13 +92,13 @@ impl<IO: ActixStream> ActixStream for TlsStream<IO> {
|
|||||||
|
|
||||||
/// Accept TLS connections via the `rustls` crate.
|
/// Accept TLS connections via the `rustls` crate.
|
||||||
pub struct Acceptor {
|
pub struct Acceptor {
|
||||||
config: Arc<ServerConfig>,
|
config: Arc<reexports::ServerConfig>,
|
||||||
handshake_timeout: Duration,
|
handshake_timeout: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Acceptor {
|
impl Acceptor {
|
||||||
/// Constructs `rustls` based acceptor service factory.
|
/// Constructs `rustls` based acceptor service factory.
|
||||||
pub fn new(config: ServerConfig) -> Self {
|
pub fn new(config: reexports::ServerConfig) -> Self {
|
||||||
Acceptor {
|
Acceptor {
|
||||||
config: Arc::new(config),
|
config: Arc::new(config),
|
||||||
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
||||||
|
@@ -23,7 +23,7 @@ use actix_utils::{
|
|||||||
};
|
};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
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 tokio_rustls_024 as tokio_rustls;
|
||||||
|
|
||||||
use super::{TlsError, DEFAULT_TLS_HANDSHAKE_TIMEOUT, MAX_CONN_COUNTER};
|
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.
|
/// Accept TLS connections via the `rustls` crate.
|
||||||
pub struct Acceptor {
|
pub struct Acceptor {
|
||||||
config: Arc<ServerConfig>,
|
config: Arc<reexports::ServerConfig>,
|
||||||
handshake_timeout: Duration,
|
handshake_timeout: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Acceptor {
|
impl Acceptor {
|
||||||
/// Constructs `rustls` based acceptor service factory.
|
/// Constructs `rustls` based acceptor service factory.
|
||||||
pub fn new(config: ServerConfig) -> Self {
|
pub fn new(config: reexports::ServerConfig) -> Self {
|
||||||
Acceptor {
|
Acceptor {
|
||||||
config: Arc::new(config),
|
config: Arc::new(config),
|
||||||
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
handshake_timeout: DEFAULT_TLS_HANDSHAKE_TIMEOUT,
|
||||||
|
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();
|
/// let mut addrs = conn.addrs();
|
||||||
/// assert_eq!(addrs.next().unwrap(), addr);
|
/// assert_eq!(addrs.next().unwrap(), addr);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[allow(clippy::implied_bounds_in_impls)]
|
||||||
pub fn addrs(
|
pub fn addrs(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = SocketAddr>
|
) -> impl Iterator<Item = SocketAddr>
|
||||||
@@ -149,6 +150,7 @@ impl<R: Host> ConnectInfo<R> {
|
|||||||
/// let mut addrs = conn.take_addrs();
|
/// let mut addrs = conn.take_addrs();
|
||||||
/// assert_eq!(addrs.next().unwrap(), addr);
|
/// assert_eq!(addrs.next().unwrap(), addr);
|
||||||
/// ```
|
/// ```
|
||||||
|
#[allow(clippy::implied_bounds_in_impls)]
|
||||||
pub fn take_addrs(
|
pub fn take_addrs(
|
||||||
&mut self,
|
&mut self,
|
||||||
) -> impl Iterator<Item = SocketAddr>
|
) -> impl Iterator<Item = SocketAddr>
|
||||||
|
@@ -27,16 +27,31 @@ mod uri;
|
|||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
pub mod 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;
|
pub mod rustls_0_20;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[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;
|
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;
|
pub mod rustls_0_21;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls-0_22")]
|
||||||
|
pub mod rustls_0_22;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustls-0_23")]
|
||||||
|
pub mod rustls_0_23;
|
||||||
|
|
||||||
#[cfg(feature = "native-tls")]
|
#[cfg(feature = "native-tls")]
|
||||||
pub mod native_tls;
|
pub mod native_tls;
|
||||||
|
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
//! See [`TlsConnector`] for main connector service factory docs.
|
//! See [`TlsConnector`] for main connector service factory docs.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryFrom,
|
|
||||||
future::Future,
|
future::Future,
|
||||||
io,
|
io,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
@@ -17,7 +16,7 @@ use actix_utils::future::{ok, Ready};
|
|||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use tokio_rustls::{
|
use tokio_rustls::{
|
||||||
client::TlsStream as AsyncTlsStream,
|
client::TlsStream as AsyncTlsStream,
|
||||||
rustls::{client::ServerName, ClientConfig, OwnedTrustAnchor, RootCertStore},
|
rustls::{client::ServerName, ClientConfig, RootCertStore},
|
||||||
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
|
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
|
||||||
};
|
};
|
||||||
use tokio_rustls_023 as tokio_rustls;
|
use tokio_rustls_023 as tokio_rustls;
|
||||||
@@ -25,17 +24,40 @@ use tokio_rustls_023 as tokio_rustls;
|
|||||||
use crate::connect::{Connection, Host};
|
use crate::connect::{Connection, Host};
|
||||||
|
|
||||||
pub mod reexports {
|
pub mod reexports {
|
||||||
//! Re-exports from `rustls` and `webpki_roots` that are useful for connectors.
|
//! Re-exports from the `rustls` v0.20 ecosystem that are useful for connectors.
|
||||||
|
|
||||||
pub use tokio_rustls_023::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
pub use tokio_rustls_023::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
||||||
|
#[cfg(feature = "rustls-0_20-webpki-roots")]
|
||||||
pub use webpki_roots_022::TLS_SERVER_ROOTS;
|
pub use webpki_roots_022::TLS_SERVER_ROOTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
|
/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store.
|
||||||
pub fn webpki_roots_cert_store() -> RootCertStore {
|
///
|
||||||
|
/// 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();
|
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 {
|
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.subject,
|
||||||
cert.spki,
|
cert.spki,
|
||||||
cert.name_constraints,
|
cert.name_constraints,
|
||||||
@@ -43,6 +65,7 @@ pub fn webpki_roots_cert_store() -> RootCertStore {
|
|||||||
let certs = vec![cert].into_iter();
|
let certs = vec![cert].into_iter();
|
||||||
root_certs.add_server_trust_anchors(certs);
|
root_certs.add_server_trust_anchors(certs);
|
||||||
}
|
}
|
||||||
|
|
||||||
root_certs
|
root_certs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,7 +3,6 @@
|
|||||||
//! See [`TlsConnector`] for main connector service factory docs.
|
//! See [`TlsConnector`] for main connector service factory docs.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryFrom,
|
|
||||||
future::Future,
|
future::Future,
|
||||||
io,
|
io,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
@@ -17,7 +16,7 @@ use actix_utils::future::{ok, Ready};
|
|||||||
use futures_core::ready;
|
use futures_core::ready;
|
||||||
use tokio_rustls::{
|
use tokio_rustls::{
|
||||||
client::TlsStream as AsyncTlsStream,
|
client::TlsStream as AsyncTlsStream,
|
||||||
rustls::{client::ServerName, ClientConfig, OwnedTrustAnchor, RootCertStore},
|
rustls::{client::ServerName, ClientConfig, RootCertStore},
|
||||||
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
|
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
|
||||||
};
|
};
|
||||||
use tokio_rustls_024 as tokio_rustls;
|
use tokio_rustls_024 as tokio_rustls;
|
||||||
@@ -25,17 +24,40 @@ use tokio_rustls_024 as tokio_rustls;
|
|||||||
use crate::connect::{Connection, Host};
|
use crate::connect::{Connection, Host};
|
||||||
|
|
||||||
pub mod reexports {
|
pub mod reexports {
|
||||||
//! Re-exports from `rustls` and `webpki_roots` that are useful for connectors.
|
//! Re-exports from the `rustls` v0.21 ecosystem that are useful for connectors.
|
||||||
|
|
||||||
pub use tokio_rustls_024::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
|
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;
|
pub use webpki_roots_025::TLS_SERVER_ROOTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
|
/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store.
|
||||||
pub fn webpki_roots_cert_store() -> RootCertStore {
|
///
|
||||||
|
/// 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();
|
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 {
|
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.subject,
|
||||||
cert.spki,
|
cert.spki,
|
||||||
cert.name_constraints,
|
cert.name_constraints,
|
||||||
@@ -43,6 +65,7 @@ pub fn webpki_roots_cert_store() -> RootCertStore {
|
|||||||
let certs = vec![cert].into_iter();
|
let certs = vec![cert].into_iter();
|
||||||
root_certs.add_trust_anchors(certs);
|
root_certs.add_trust_anchors(certs);
|
||||||
}
|
}
|
||||||
|
|
||||||
root_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;
|
use super::Host;
|
||||||
|
|
||||||
impl Host for Uri {
|
impl Host for http_0_2::Uri {
|
||||||
|
fn hostname(&self) -> &str {
|
||||||
|
self.host().unwrap_or("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn port(&self) -> Option<u16> {
|
||||||
|
match self.port_u16() {
|
||||||
|
Some(port) => Some(port),
|
||||||
|
None => scheme_to_port(self.scheme_str()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Host for http_1::Uri {
|
||||||
fn hostname(&self) -> &str {
|
fn hostname(&self) -> &str {
|
||||||
self.host().unwrap_or("")
|
self.host().unwrap_or("")
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
//! TLS acceptor and connector services for the Actix ecosystem.
|
//! TLS acceptor and connector services for the Actix ecosystem.
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible, missing_docs)]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
@@ -3,27 +3,30 @@
|
|||||||
#![cfg(all(
|
#![cfg(all(
|
||||||
feature = "accept",
|
feature = "accept",
|
||||||
feature = "connect",
|
feature = "connect",
|
||||||
feature = "rustls-0_21",
|
feature = "rustls-0_23",
|
||||||
feature = "openssl"
|
feature = "openssl"
|
||||||
))]
|
))]
|
||||||
|
|
||||||
use std::{convert::TryFrom, io::Write, sync::Arc};
|
use std::{io::Write as _, sync::Arc};
|
||||||
|
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use actix_server::TestServer;
|
use actix_server::TestServer;
|
||||||
use actix_service::ServiceFactoryExt as _;
|
use actix_service::ServiceFactoryExt as _;
|
||||||
use actix_tls::accept::openssl::{Acceptor, TlsStream};
|
use actix_tls::{
|
||||||
|
accept::openssl::{Acceptor, TlsStream},
|
||||||
|
connect::rustls_0_23::reexports::ClientConfig,
|
||||||
|
};
|
||||||
use actix_utils::future::ok;
|
use actix_utils::future::ok;
|
||||||
use tokio_rustls::rustls::{Certificate, ClientConfig, RootCertStore, ServerName};
|
use rustls_pki_types_1::ServerName;
|
||||||
use tokio_rustls_024 as tokio_rustls;
|
use tokio_rustls_026::rustls::RootCertStore;
|
||||||
|
|
||||||
fn new_cert_and_key() -> (String, String) {
|
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()])
|
rcgen::generate_simple_self_signed(vec!["127.0.0.1".to_owned(), "localhost".to_owned()])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let key = cert.serialize_private_key_pem();
|
let key = key_pair.serialize_pem();
|
||||||
let cert = cert.serialize_pem().unwrap();
|
let cert = cert.pem();
|
||||||
|
|
||||||
(cert, key)
|
(cert, key)
|
||||||
}
|
}
|
||||||
@@ -46,30 +49,48 @@ fn openssl_acceptor(cert: String, key: String) -> tls_openssl::ssl::SslAcceptor
|
|||||||
builder.build()
|
builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
mod danger {
|
mod danger {
|
||||||
use std::time::SystemTime;
|
use rustls_pki_types_1::{CertificateDer, ServerName, UnixTime};
|
||||||
|
use tokio_rustls_026::rustls;
|
||||||
use tokio_rustls_024::rustls::{
|
|
||||||
self,
|
|
||||||
client::{ServerCertVerified, ServerCertVerifier},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
|
/// Disables certificate verification to allow self-signed certs from rcgen.
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct NoCertificateVerification;
|
pub struct NoCertificateVerification;
|
||||||
|
|
||||||
impl ServerCertVerifier for NoCertificateVerification {
|
impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification {
|
||||||
fn verify_server_cert(
|
fn verify_server_cert(
|
||||||
&self,
|
&self,
|
||||||
_end_entity: &Certificate,
|
_end_entity: &CertificateDer<'_>,
|
||||||
_intermediates: &[Certificate],
|
_intermediates: &[CertificateDer<'_>],
|
||||||
_server_name: &ServerName,
|
_server_name: &ServerName<'_>,
|
||||||
_scts: &mut dyn Iterator<Item = &[u8]>,
|
_ocsp: &[u8],
|
||||||
_ocsp_response: &[u8],
|
_now: UnixTime,
|
||||||
_now: SystemTime,
|
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
|
||||||
) -> Result<ServerCertVerified, rustls::Error> {
|
Ok(rustls::client::danger::ServerCertVerified::assertion())
|
||||||
Ok(ServerCertVerified::assertion())
|
}
|
||||||
|
|
||||||
|
fn verify_tls12_signature(
|
||||||
|
&self,
|
||||||
|
_message: &[u8],
|
||||||
|
_cert: &rustls_pki_types_1::CertificateDer<'_>,
|
||||||
|
_dss: &rustls::DigitallySignedStruct,
|
||||||
|
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
|
||||||
|
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_tls13_signature(
|
||||||
|
&self,
|
||||||
|
_message: &[u8],
|
||||||
|
_cert: &rustls_pki_types_1::CertificateDer<'_>,
|
||||||
|
_dss: &rustls::DigitallySignedStruct,
|
||||||
|
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
|
||||||
|
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
|
||||||
|
rustls::crypto::aws_lc_rs::default_provider()
|
||||||
|
.signature_verification_algorithms
|
||||||
|
.supported_schemes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,7 +98,6 @@ mod danger {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn rustls_connector(_cert: String, _key: String) -> ClientConfig {
|
fn rustls_connector(_cert: String, _key: String) -> ClientConfig {
|
||||||
let mut config = ClientConfig::builder()
|
let mut config = ClientConfig::builder()
|
||||||
.with_safe_defaults()
|
|
||||||
.with_root_certificates(RootCertStore::empty())
|
.with_root_certificates(RootCertStore::empty())
|
||||||
.with_no_client_auth();
|
.with_no_client_auth();
|
||||||
|
|
||||||
@@ -91,6 +111,10 @@ fn rustls_connector(_cert: String, _key: String) -> ClientConfig {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn accepts_connections() {
|
async fn accepts_connections() {
|
||||||
|
tokio_rustls_026::rustls::crypto::aws_lc_rs::default_provider()
|
||||||
|
.install_default()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (cert, key) = new_cert_and_key();
|
let (cert, key) = new_cert_and_key();
|
||||||
|
|
||||||
let srv = TestServer::start({
|
let srv = TestServer::start({
|
||||||
@@ -117,13 +141,13 @@ async fn accepts_connections() {
|
|||||||
let config = rustls_connector(cert, key);
|
let config = rustls_connector(cert, key);
|
||||||
let config = Arc::new(config);
|
let config = Arc::new(config);
|
||||||
|
|
||||||
let mut conn = tokio_rustls::rustls::ClientConnection::new(
|
let mut conn = tokio_rustls_026::rustls::ClientConnection::new(
|
||||||
config,
|
config,
|
||||||
ServerName::try_from("localhost").unwrap(),
|
ServerName::try_from("localhost").unwrap(),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mut stream = tokio_rustls::rustls::Stream::new(&mut conn, &mut sock);
|
let mut stream = tokio_rustls_026::rustls::Stream::new(&mut conn, &mut sock);
|
||||||
|
|
||||||
stream.flush().expect("TLS handshake failed");
|
stream.flush().expect("TLS handshake failed");
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
#![cfg(all(
|
#![cfg(all(
|
||||||
feature = "accept",
|
feature = "accept",
|
||||||
feature = "connect",
|
feature = "connect",
|
||||||
feature = "rustls-0_21",
|
feature = "rustls-0_23",
|
||||||
feature = "openssl"
|
feature = "openssl"
|
||||||
))]
|
))]
|
||||||
|
|
||||||
@@ -15,39 +15,39 @@ use actix_rt::net::TcpStream;
|
|||||||
use actix_server::TestServer;
|
use actix_server::TestServer;
|
||||||
use actix_service::ServiceFactoryExt as _;
|
use actix_service::ServiceFactoryExt as _;
|
||||||
use actix_tls::{
|
use actix_tls::{
|
||||||
accept::rustls_0_21::{Acceptor, TlsStream},
|
accept::rustls_0_23::{reexports::ServerConfig, Acceptor, TlsStream},
|
||||||
connect::openssl::reexports::SslConnector,
|
connect::openssl::reexports::SslConnector,
|
||||||
};
|
};
|
||||||
use actix_utils::future::ok;
|
use actix_utils::future::ok;
|
||||||
use rustls_pemfile::{certs, pkcs8_private_keys};
|
use rustls_pemfile::{certs, pkcs8_private_keys};
|
||||||
|
use rustls_pki_types_1::PrivateKeyDer;
|
||||||
use tls_openssl::ssl::SslVerifyMode;
|
use tls_openssl::ssl::SslVerifyMode;
|
||||||
use tokio_rustls::rustls::{self, Certificate, PrivateKey, ServerConfig};
|
|
||||||
use tokio_rustls_024 as tokio_rustls;
|
|
||||||
|
|
||||||
fn new_cert_and_key() -> (String, String) {
|
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()])
|
rcgen::generate_simple_self_signed(vec!["127.0.0.1".to_owned(), "localhost".to_owned()])
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let key = cert.serialize_private_key_pem();
|
let key = key_pair.serialize_pem();
|
||||||
let cert = cert.serialize_pem().unwrap();
|
let cert = cert.pem();
|
||||||
|
|
||||||
(cert, key)
|
(cert, key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rustls_server_config(cert: String, key: String) -> rustls::ServerConfig {
|
fn rustls_server_config(cert: String, key: String) -> ServerConfig {
|
||||||
// Load TLS key and cert files
|
// Load TLS key and cert files
|
||||||
|
|
||||||
let cert = &mut BufReader::new(cert.as_bytes());
|
let cert = &mut BufReader::new(cert.as_bytes());
|
||||||
let key = &mut BufReader::new(key.as_bytes());
|
let key = &mut BufReader::new(key.as_bytes());
|
||||||
|
|
||||||
let cert_chain = certs(cert).unwrap().into_iter().map(Certificate).collect();
|
let cert_chain = certs(cert).collect::<Result<Vec<_>, _>>().unwrap();
|
||||||
let mut keys = pkcs8_private_keys(key).unwrap();
|
let mut keys = pkcs8_private_keys(key)
|
||||||
|
.collect::<Result<Vec<_>, _>>()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut config = ServerConfig::builder()
|
let mut config = ServerConfig::builder()
|
||||||
.with_safe_defaults()
|
|
||||||
.with_no_client_auth()
|
.with_no_client_auth()
|
||||||
.with_single_cert(cert_chain, PrivateKey(keys.remove(0)))
|
.with_single_cert(cert_chain, PrivateKeyDer::Pkcs8(keys.remove(0)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
config.alpn_protocols = vec![b"http/1.1".to_vec()];
|
config.alpn_protocols = vec![b"http/1.1".to_vec()];
|
||||||
@@ -73,6 +73,10 @@ fn openssl_connector(cert: String, key: String) -> SslConnector {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn accepts_connections() {
|
async fn accepts_connections() {
|
||||||
|
tokio_rustls_026::rustls::crypto::aws_lc_rs::default_provider()
|
||||||
|
.install_default()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let (cert, key) = new_cert_and_key();
|
let (cert, key) = new_cert_and_key();
|
||||||
|
|
||||||
let srv = TestServer::start({
|
let srv = TestServer::start({
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
#![cfg(feature = "connect")]
|
#![cfg(feature = "connect")]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@@ -11,7 +12,7 @@ use actix_server::TestServer;
|
|||||||
use actix_service::{fn_service, Service, ServiceFactory};
|
use actix_service::{fn_service, Service, ServiceFactory};
|
||||||
use actix_tls::connect::{ConnectError, ConnectInfo, Connection, Connector, Host};
|
use actix_tls::connect::{ConnectError, ConnectInfo, Connection, Connector, Host};
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use futures_util::sink::SinkExt;
|
use futures_util::sink::SinkExt as _;
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
@@ -30,7 +31,7 @@ async fn test_string() {
|
|||||||
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rustls-0_21")]
|
#[cfg(feature = "rustls-0_23")]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_rustls_string() {
|
async fn test_rustls_string() {
|
||||||
let srv = TestServer::start(|| {
|
let srv = TestServer::start(|| {
|
||||||
@@ -98,8 +99,6 @@ async fn service_factory() {
|
|||||||
#[cfg(all(feature = "openssl", feature = "uri"))]
|
#[cfg(all(feature = "openssl", feature = "uri"))]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_openssl_uri() {
|
async fn test_openssl_uri() {
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
let srv = TestServer::start(|| {
|
let srv = TestServer::start(|| {
|
||||||
fn_service(|io: TcpStream| async {
|
fn_service(|io: TcpStream| async {
|
||||||
let mut framed = Framed::new(io, BytesCodec);
|
let mut framed = Framed::new(io, BytesCodec);
|
||||||
@@ -109,16 +108,14 @@ async fn test_openssl_uri() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let connector = Connector::default().service();
|
let connector = Connector::default().service();
|
||||||
let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
|
let addr = http_0_2::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
|
||||||
let con = connector.call(addr.into()).await.unwrap();
|
let con = connector.call(addr.into()).await.unwrap();
|
||||||
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(all(feature = "rustls-0_21", feature = "uri"))]
|
#[cfg(all(feature = "rustls-0_23", feature = "uri"))]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_rustls_uri() {
|
async fn test_rustls_uri_http1() {
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
let srv = TestServer::start(|| {
|
let srv = TestServer::start(|| {
|
||||||
fn_service(|io: TcpStream| async {
|
fn_service(|io: TcpStream| async {
|
||||||
let mut framed = Framed::new(io, BytesCodec);
|
let mut framed = Framed::new(io, BytesCodec);
|
||||||
@@ -128,7 +125,24 @@ async fn test_rustls_uri() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let conn = Connector::default().service();
|
let conn = Connector::default().service();
|
||||||
let addr = http::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
|
let addr = http_1::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
|
||||||
|
let con = conn.call(addr.into()).await.unwrap();
|
||||||
|
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "rustls-0_23", feature = "uri"))]
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_rustls_uri() {
|
||||||
|
let srv = TestServer::start(|| {
|
||||||
|
fn_service(|io: TcpStream| async {
|
||||||
|
let mut framed = Framed::new(io, BytesCodec);
|
||||||
|
framed.send(Bytes::from_static(b"test")).await?;
|
||||||
|
Ok::<_, io::Error>(())
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
let conn = Connector::default().service();
|
||||||
|
let addr = http_1::Uri::try_from(format!("https://localhost:{}", srv.port())).unwrap();
|
||||||
let con = conn.call(addr.into()).await.unwrap();
|
let con = conn.call(addr.into()).await.unwrap();
|
||||||
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
assert_eq!(con.peer_addr().unwrap(), srv.addr());
|
||||||
}
|
}
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
#![allow(missing_docs)]
|
||||||
#![cfg(feature = "connect")]
|
#![cfg(feature = "connect")]
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
- Minimum supported Rust version (MSRV) is now 1.65.
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
## 0.1.0
|
## 0.1.0
|
||||||
|
|
||||||
|
@@ -5,20 +5,25 @@ authors = ["Rajasekharan Vengalil <avranju@gmail.com>"]
|
|||||||
description = "Support for tokio tracing with Actix services"
|
description = "Support for tokio tracing with Actix services"
|
||||||
keywords = ["network", "framework", "tracing"]
|
keywords = ["network", "framework", "tracing"]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
repository = "https://github.com/actix/actix-net/tree/master/actix-tracing"
|
||||||
documentation = "https://docs.rs/actix-tracing"
|
documentation = "https://docs.rs/actix-tracing"
|
||||||
categories = ["network-programming", "asynchronous"]
|
categories = ["network-programming", "asynchronous"]
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[package.metadata.cargo_check_external_types]
|
||||||
|
allowed_external_types = ["actix_service::*", "actix_utils::*", "tracing::*", "tracing_futures::*"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "2"
|
actix-service = "2"
|
||||||
actix-utils = "3"
|
actix-utils = "3"
|
||||||
|
|
||||||
tracing = "0.1.35"
|
tracing = "0.1.35"
|
||||||
tracing-futures = "0.2"
|
tracing-futures = "0.2"
|
||||||
|
|
||||||
[dev_dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2"
|
actix-rt = "2"
|
||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
//! Actix tracing - support for tokio tracing with Actix services.
|
//! Actix tracing - support for tokio tracing with Actix services.
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible)]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
@@ -22,6 +20,7 @@ pub struct TracingService<S, F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S, F> TracingService<S, F> {
|
impl<S, F> TracingService<S, F> {
|
||||||
|
/// Constructs new tracing middleware.
|
||||||
pub fn new(inner: S, make_span: F) -> Self {
|
pub fn new(inner: S, make_span: F) -> Self {
|
||||||
TracingService { inner, make_span }
|
TracingService { inner, make_span }
|
||||||
}
|
}
|
||||||
@@ -63,6 +62,7 @@ pub struct TracingTransform<S, U, F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S, U, F> TracingTransform<S, U, F> {
|
impl<S, U, F> TracingTransform<S, U, F> {
|
||||||
|
/// Constructs new tracing middleware.
|
||||||
pub fn new(make_span: F) -> Self {
|
pub fn new(make_span: F) -> Self {
|
||||||
TracingTransform {
|
TracingTransform {
|
||||||
make_span,
|
make_span,
|
||||||
@@ -131,7 +131,7 @@ mod test {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static SPAN: RefCell<Vec<span::Id>> = RefCell::new(Vec::new());
|
static SPAN: RefCell<Vec<span::Id>> = const { RefCell::new(Vec::new()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
- Minimum supported Rust version (MSRV) is now 1.65.
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
## 3.0.1
|
## 3.0.1
|
||||||
|
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "actix-utils"
|
name = "actix-utils"
|
||||||
version = "3.0.1"
|
version = "3.0.1"
|
||||||
authors = [
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
|
||||||
]
|
|
||||||
description = "Various utilities used in the Actix ecosystem"
|
description = "Various utilities used in the Actix ecosystem"
|
||||||
keywords = ["network", "framework", "async", "futures"]
|
keywords = ["network", "framework", "async", "futures"]
|
||||||
categories = ["network-programming", "asynchronous"]
|
categories = ["network-programming", "asynchronous"]
|
||||||
@@ -14,10 +11,13 @@ edition.workspace = true
|
|||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pin-project-lite = "0.2"
|
|
||||||
local-waker = "0.1"
|
local-waker = "0.1"
|
||||||
|
pin-project-lite = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2"
|
actix-rt = "2"
|
||||||
futures-util = { version = "0.3.17", default-features = false }
|
futures-util = { version = "0.3.17", default-features = false }
|
||||||
static_assertions = "1.1"
|
static_assertions = "1.1"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
@@ -103,6 +103,7 @@ mod tests {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
fn require_sync<T: Sync>(_t: &T) {}
|
fn require_sync<T: Sync>(_t: &T) {}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
trait AmbiguousIfUnpin<A> {
|
trait AmbiguousIfUnpin<A> {
|
||||||
fn some_item(&self) {}
|
fn some_item(&self) {}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
//! Various utilities used in the Actix ecosystem.
|
//! Various utilities used in the Actix ecosystem.
|
||||||
|
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible, missing_docs)]
|
|
||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
|
@@ -2,6 +2,15 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
## 1.4.0
|
||||||
|
|
||||||
|
- Add `ByteString::split_at()` method.
|
||||||
|
- Minimum supported Rust version (MSRV) is now 1.71.
|
||||||
|
|
||||||
|
## 1.3.1
|
||||||
|
|
||||||
|
- No significant changes since `1.3.0`.
|
||||||
|
|
||||||
## 1.3.0
|
## 1.3.0
|
||||||
|
|
||||||
- Implement `AsRef<ByteString>` for `ByteString`.
|
- Implement `AsRef<ByteString>` for `ByteString`.
|
||||||
|
@@ -1,24 +1,27 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bytestring"
|
name = "bytestring"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
authors = [
|
description = "A UTF-8 encoded read-only string using `Bytes` as storage"
|
||||||
"Nikolay Kim <fafhrd91@gmail.com>",
|
authors = ["Nikolay Kim <fafhrd91@gmail.com>", "Rob Ede <robjtede@icloud.com>"]
|
||||||
"Rob Ede <robjtede@icloud.com>",
|
keywords = ["string", "bytes", "utf8", "web", "bytestring"]
|
||||||
]
|
|
||||||
description = "An immutable UTF-8 encoded string using Bytes as storage"
|
|
||||||
keywords = ["string", "bytes", "utf8", "web", "actix"]
|
|
||||||
categories = ["no-std", "web-programming"]
|
categories = ["no-std", "web-programming"]
|
||||||
homepage = "https://actix.rs"
|
homepage = "https://actix.rs"
|
||||||
repository = "https://github.com/actix/actix-net.git"
|
repository = "https://github.com/actix/actix-net"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
|
[package.metadata.cargo_check_external_types]
|
||||||
|
allowed_external_types = ["bytes::*", "serde::*"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytes = { version = "1.2", default-features = false }
|
bytes = { version = "1.2", default-features = false }
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ahash = { version = "0.8", default-features = false }
|
ahash = { version = "0.8", default-features = false }
|
||||||
serde_json = "1.0"
|
serde_json = "1"
|
||||||
static_assertions = "1.1"
|
static_assertions = "1.1"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
16
bytestring/README.md
Normal file
16
bytestring/README.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# `bytestring`
|
||||||
|
|
||||||
|
> A UTF-8 encoded read-only string using `Bytes` as storage.
|
||||||
|
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
|
||||||
|
[](https://crates.io/crates/bytestring)
|
||||||
|
[](https://docs.rs/bytestring/1.4.0)
|
||||||
|
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||||
|

|
||||||
|
<br />
|
||||||
|
[](https://deps.rs/crate/bytestring/1.4.0)
|
||||||
|

|
||||||
|
[](https://discord.gg/NWpN5mmg3x)
|
||||||
|
|
||||||
|
<!-- prettier-ignore-end -->
|
@@ -1,8 +1,8 @@
|
|||||||
//! A UTF-8 encoded read-only string using Bytes as storage.
|
//! A UTF-8 encoded read-only string using `Bytes` as storage.
|
||||||
|
//!
|
||||||
|
//! See docs for [`ByteString`].
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![deny(rust_2018_idioms, nonstandard_style)]
|
|
||||||
#![warn(future_incompatible, missing_docs)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
@@ -11,11 +11,11 @@ use alloc::{
|
|||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use core::{borrow::Borrow, convert::TryFrom, fmt, hash, ops, str};
|
use core::{borrow::Borrow, fmt, hash, ops, str};
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
/// An immutable UTF-8 encoded string with [`Bytes`] as a storage.
|
/// An immutable UTF-8 encoded string using [`Bytes`] as the storage.
|
||||||
#[derive(Clone, Default, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Default, Eq, PartialOrd, Ord)]
|
||||||
pub struct ByteString(Bytes);
|
pub struct ByteString(Bytes);
|
||||||
|
|
||||||
@@ -51,7 +51,29 @@ impl ByteString {
|
|||||||
Self(src)
|
Self(src)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a new byte string that is equivalent to the given `subset`.
|
/// Divides one bytestring into two at an index, returning both parts.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if `mid` is not on a UTF-8 code point boundary, or if it is past the end of the last
|
||||||
|
/// code point of the bytestring.
|
||||||
|
pub fn split_at(&self, mid: usize) -> (ByteString, ByteString) {
|
||||||
|
let this: &str = self.as_ref();
|
||||||
|
let _valid_midpoint_check = this.split_at(mid);
|
||||||
|
|
||||||
|
let mut bytes = self.0.clone();
|
||||||
|
let first = bytes.split_to(mid);
|
||||||
|
let last = bytes;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
(
|
||||||
|
ByteString::from_bytes_unchecked(first),
|
||||||
|
ByteString::from_bytes_unchecked(last),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new `ByteString` that is equivalent to the given `subset`.
|
||||||
///
|
///
|
||||||
/// When processing a `ByteString` buffer with other tools, one often gets a `&str` which is in
|
/// When processing a `ByteString` buffer with other tools, one often gets a `&str` which is in
|
||||||
/// fact a slice of the original `ByteString`; i.e., a subset of it. This function turns that
|
/// fact a slice of the original `ByteString`; i.e., a subset of it. This function turns that
|
||||||
@@ -463,4 +485,33 @@ mod test {
|
|||||||
// being a logical subset of the string
|
// being a logical subset of the string
|
||||||
ByteString::from_static("foo bar").slice_ref("foo");
|
ByteString::from_static("foo bar").slice_ref("foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn split_at() {
|
||||||
|
let buf = ByteString::from_static("foo bar");
|
||||||
|
|
||||||
|
let (first, last) = buf.split_at(0);
|
||||||
|
assert_eq!(ByteString::from_static(""), first);
|
||||||
|
assert_eq!(ByteString::from_static("foo bar"), last);
|
||||||
|
|
||||||
|
let (first, last) = buf.split_at(4);
|
||||||
|
assert_eq!(ByteString::from_static("foo "), first);
|
||||||
|
assert_eq!(ByteString::from_static("bar"), last);
|
||||||
|
|
||||||
|
let (first, last) = buf.split_at(7);
|
||||||
|
assert_eq!(ByteString::from_static("foo bar"), first);
|
||||||
|
assert_eq!(ByteString::from_static(""), last);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic = "byte index 1 is not a char boundary;"]
|
||||||
|
fn split_at_invalid_code_point() {
|
||||||
|
ByteString::from_static("µ").split_at(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic = "byte index 9 is out of bounds"]
|
||||||
|
fn split_at_outside_string() {
|
||||||
|
ByteString::from_static("foo").split_at(9);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ coverage:
|
|||||||
default:
|
default:
|
||||||
threshold: 10% # make CI green
|
threshold: 10% # make CI green
|
||||||
|
|
||||||
ignore: # ignore codecoverage on following paths
|
ignore: # ignore code coverage on following paths
|
||||||
- "examples"
|
- "examples"
|
||||||
- ".github"
|
- ".github"
|
||||||
- "**/*.md"
|
- "**/*.md"
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user