mirror of
https://github.com/fafhrd91/actix-net
synced 2025-09-03 00:46:38 +02:00
Compare commits
50 Commits
server-v2.
...
proxy-prot
Author | SHA1 | Date | |
---|---|---|---|
|
e67d4521e5 | ||
|
567e9ad2ca | ||
|
d78e238415 | ||
|
26a28606d3 | ||
|
2daf68dc49 | ||
|
4d4367f5f2 | ||
|
b8ff2a47a6 | ||
|
160fdc5efc | ||
|
c856fcdcd5 | ||
|
4ee45b24b5 | ||
|
43f9da3c4e | ||
|
d24e5b4e95 | ||
|
0d45bf9b43 | ||
|
e85ae87b98 | ||
|
dc19b196da | ||
|
e22bca03d5 | ||
|
19ab9cc97a | ||
|
1ea0f773b6 | ||
|
ee3d54f574 | ||
|
75e56d56d8 | ||
|
222527a407 | ||
|
cfddd3ea2e | ||
|
d869455251 | ||
|
3e925d8f80 | ||
|
0c8337f1c9 | ||
|
20ae763da8 | ||
|
6c6f7f19f5 | ||
|
95d569d8d7 | ||
|
347fc17035 | ||
|
4cb04042f5 | ||
|
e6ddf0ccff | ||
|
21de88385e | ||
|
a91bdcb80c | ||
|
64a2d1c1eb | ||
|
637bd709ae | ||
|
bbac996677 | ||
|
c9cea86445 | ||
|
05065186ff | ||
|
3d54a00d5b | ||
|
120e0206eb | ||
|
1ad8a98f1b | ||
|
da1c0024f0 | ||
|
0be1dd8156 | ||
|
9c646e4998 | ||
|
758c37b80c | ||
|
1a1af68bf0 | ||
|
3ebed4701d | ||
|
15c13c3e32 | ||
|
3356038c80 | ||
|
7834d76d4f |
4
.clippy.toml
Normal file
4
.clippy.toml
Normal file
@@ -0,0 +1,4 @@
|
||||
disallowed-names = [
|
||||
"..", # defaults
|
||||
"e", # prefer `err`
|
||||
]
|
15
.cspell.yml
15
.cspell.yml
@@ -2,7 +2,22 @@ version: "0.2"
|
||||
words:
|
||||
- actix
|
||||
- addrs
|
||||
- ALPN
|
||||
- arrayvec
|
||||
- bitflags
|
||||
- clippy
|
||||
- deque
|
||||
- itertools
|
||||
- itoa
|
||||
- mptcp
|
||||
- MSRV
|
||||
- nonblocking
|
||||
- oneshot
|
||||
- pemfile
|
||||
- rcgen
|
||||
- Rustls
|
||||
- rustup
|
||||
- smallvec
|
||||
- spki
|
||||
- uring
|
||||
- webpki
|
||||
|
12
.github/workflows/ci-post-merge.yml
vendored
12
.github/workflows/ci-post-merge.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
if: matrix.target.os == 'macos-latest'
|
||||
run: sudo ifconfig lo0 alias 127.0.0.3
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Free Disk Space
|
||||
if: matrix.target.os == 'ubuntu-latest'
|
||||
@@ -59,12 +59,12 @@ jobs:
|
||||
echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV
|
||||
|
||||
- name: Install Rust (${{ matrix.version }})
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
toolchain: ${{ matrix.version }}
|
||||
|
||||
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
|
||||
uses: taiki-e/install-action@v2.50.10
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
with:
|
||||
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
|
||||
|
||||
@@ -113,15 +113,15 @@ jobs:
|
||||
name: minimal versions
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install Rust (nightly)
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
toolchain: nightly
|
||||
|
||||
- name: Install cargo-hack & cargo-minimal-versions
|
||||
uses: taiki-e/install-action@v2.50.10
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
with:
|
||||
tool: cargo-hack,cargo-minimal-versions
|
||||
|
||||
|
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
if: matrix.target.os == 'macos-latest'
|
||||
run: sudo ifconfig lo0 alias 127.0.0.3
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Free Disk Space
|
||||
if: matrix.target.os == 'ubuntu-latest'
|
||||
@@ -68,12 +68,12 @@ jobs:
|
||||
echo "RUSTFLAGS=-C target-feature=+crt-static" >> $GITHUB_ENV
|
||||
|
||||
- name: Install Rust (${{ matrix.version.name }})
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
toolchain: ${{ matrix.version.version }}
|
||||
|
||||
- name: Install just, cargo-hack, cargo-nextest, cargo-ci-cache-clean
|
||||
uses: taiki-e/install-action@v2.50.10
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
with:
|
||||
tool: just,cargo-hack,cargo-nextest,cargo-ci-cache-clean
|
||||
|
||||
@@ -117,15 +117,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install Rust (nightly)
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
toolchain: nightly
|
||||
|
||||
- name: Install just
|
||||
uses: taiki-e/install-action@v2.50.10
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
with:
|
||||
tool: just
|
||||
|
||||
|
8
.github/workflows/coverage.yml
vendored
8
.github/workflows/coverage.yml
vendored
@@ -15,15 +15,15 @@ jobs:
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
components: llvm-tools-preview
|
||||
|
||||
- name: Install cargo-llvm-cov
|
||||
uses: taiki-e/install-action@v2.50.10
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
with:
|
||||
tool: cargo-llvm-cov
|
||||
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
run: cargo llvm-cov --workspace --all-features --codecov --output-path codecov.json
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v5.4.2
|
||||
uses: codecov/codecov-action@v5.5.0
|
||||
with:
|
||||
files: codecov.json
|
||||
fail_ci_if_error: true
|
||||
|
16
.github/workflows/lint.yml
vendored
16
.github/workflows/lint.yml
vendored
@@ -16,9 +16,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: rustfmt
|
||||
@@ -33,9 +33,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with: { components: clippy }
|
||||
|
||||
- uses: giraffate/clippy-action@v1.0.1
|
||||
@@ -48,20 +48,20 @@ jobs:
|
||||
if: false # rustdoc mismatch currently
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install Rust (${{ vars.RUST_VERSION_EXTERNAL_TYPES }})
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.14.0
|
||||
with:
|
||||
toolchain: ${{ vars.RUST_VERSION_EXTERNAL_TYPES }}
|
||||
|
||||
- name: Install just
|
||||
uses: taiki-e/install-action@v2.50.10
|
||||
uses: taiki-e/install-action@v2.58.21
|
||||
with:
|
||||
tool: just
|
||||
|
||||
- name: Install cargo-check-external-types
|
||||
uses: taiki-e/cache-cargo-install-action@v2.1.1
|
||||
uses: taiki-e/cache-cargo-install-action@v2.3.0
|
||||
with:
|
||||
tool: cargo-check-external-types
|
||||
|
||||
|
32
.vscode/settings.json
vendored
Normal file
32
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"rust-analyzer.cargo.features": [
|
||||
"accept",
|
||||
"actix-macros",
|
||||
"connect",
|
||||
"default",
|
||||
"macros",
|
||||
"native-tls",
|
||||
"openssl",
|
||||
"rustls",
|
||||
"rustls-021",
|
||||
"rustls-0_20",
|
||||
"rustls-0_20-native-roots",
|
||||
"rustls-0_20-webpki-roots",
|
||||
"rustls-0_21",
|
||||
"rustls-0_21-native-roots",
|
||||
"rustls-0_21-webpki-roots",
|
||||
"rustls-0_22",
|
||||
"rustls-0_22-native-roots",
|
||||
"rustls-0_22-webpki-roots",
|
||||
"rustls-0_23",
|
||||
"rustls-0_23-native-roots",
|
||||
"rustls-0_23-webpki-roots",
|
||||
"rustls-webpki-0101",
|
||||
"serde",
|
||||
"tokio-rustls-023",
|
||||
"tokio-rustls-024",
|
||||
"uri",
|
||||
"webpki-roots-022",
|
||||
"webpki-roots-025",
|
||||
]
|
||||
}
|
604
Cargo.lock
generated
604
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
14
Cargo.toml
@@ -3,6 +3,7 @@ resolver = "2"
|
||||
members = [
|
||||
"actix-codec",
|
||||
"actix-macros",
|
||||
"actix-proxy-protocol",
|
||||
"actix-rt",
|
||||
"actix-server",
|
||||
"actix-service",
|
||||
@@ -17,11 +18,12 @@ members = [
|
||||
[workspace.package]
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.74"
|
||||
rust-version = "1.75"
|
||||
|
||||
[patch.crates-io]
|
||||
actix-codec = { path = "actix-codec" }
|
||||
actix-macros = { path = "actix-macros" }
|
||||
actix-proxy-protocol = { path = "actix-proxy-protocol" }
|
||||
actix-rt = { path = "actix-rt" }
|
||||
actix-server = { path = "actix-server" }
|
||||
actix-service = { path = "actix-service" }
|
||||
@@ -38,7 +40,11 @@ opt-level = 3
|
||||
codegen-units = 1
|
||||
|
||||
[workspace.lints.rust]
|
||||
rust_2018_idioms = "deny"
|
||||
rust-2018-idioms = "deny"
|
||||
nonstandard-style = "deny"
|
||||
future_incompatible = "deny"
|
||||
missing_docs = { level = "warn", priority = -1 }
|
||||
future-incompatible = "deny"
|
||||
missing-docs = { level = "warn", priority = -1 }
|
||||
|
||||
[workspace.lints.clippy]
|
||||
uninlined-format-args = "warn"
|
||||
disallowed-names = "warn"
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.74.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 0.5.2
|
||||
|
||||
|
@@ -57,7 +57,7 @@ impl Write for Bilateral {
|
||||
Ok(data.len())
|
||||
}
|
||||
Some(Err(err)) => Err(err),
|
||||
None => panic!("unexpected write; {:?}", src),
|
||||
None => panic!("unexpected write; {src:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.74.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 0.2.4
|
||||
|
||||
|
@@ -1,3 +1,5 @@
|
||||
#![allow(unused_imports)]
|
||||
|
||||
mod system {
|
||||
pub use actix_rt::System as MySystem;
|
||||
}
|
||||
|
7
actix-proxy-protocol/CHANGES.md
Normal file
7
actix-proxy-protocol/CHANGES.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2022-xx-xx
|
||||
|
||||
## 0.0.1 - 2022-xx-xx
|
||||
|
||||
- delete me
|
40
actix-proxy-protocol/Cargo.toml
Executable file
40
actix-proxy-protocol/Cargo.toml
Executable file
@@ -0,0 +1,40 @@
|
||||
[package]
|
||||
name = "actix-proxy-protocol"
|
||||
version = "0.0.1"
|
||||
authors = ["Rob Ede <robjtede@icloud.com>"]
|
||||
description = "PROXY protocol utilities"
|
||||
keywords = ["proxy", "protocol", "network", "haproxy", "tcp"]
|
||||
categories = ["network-programming", "asynchronous"]
|
||||
homepage = "https://actix.rs"
|
||||
repository = "https://github.com/actix/actix-net"
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
actix-service = "2"
|
||||
actix-utils = "3"
|
||||
|
||||
arrayvec = "0.7"
|
||||
bitflags = "2"
|
||||
crc32fast = "1"
|
||||
futures-core = { version = "0.3.17", default-features = false, features = ["std"] }
|
||||
futures-util = { version = "0.3.17", default-features = false, features = ["std"] }
|
||||
itoa = "1"
|
||||
nom = "8"
|
||||
smallvec = "1"
|
||||
tokio = { version = "1.13.1", features = ["sync", "io-util"] }
|
||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-codec = "0.5"
|
||||
actix-rt = "2.6"
|
||||
actix-server = "2"
|
||||
bytes = "1"
|
||||
const-str = "0.5"
|
||||
futures-util = { version = "0.3.7", default-features = false, features = ["sink", "async-await-macro"] }
|
||||
hex = "0.4"
|
||||
once_cell = "1"
|
||||
pretty_assertions = "1"
|
||||
tokio = { version = "1.13.1", features = ["io-util", "rt-multi-thread", "macros", "fs"] }
|
||||
tracing-subscriber = "0.3"
|
1
actix-proxy-protocol/LICENSE-APACHE
Symbolic link
1
actix-proxy-protocol/LICENSE-APACHE
Symbolic link
@@ -0,0 +1 @@
|
||||
../LICENSE-APACHE
|
1
actix-proxy-protocol/LICENSE-MIT
Symbolic link
1
actix-proxy-protocol/LICENSE-MIT
Symbolic link
@@ -0,0 +1 @@
|
||||
../LICENSE-MIT
|
17
actix-proxy-protocol/README.md
Normal file
17
actix-proxy-protocol/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# actix-proxy-protocol
|
||||
|
||||
> Implementation of the [PROXY protocol].
|
||||
|
||||
[](https://crates.io/crates/actix-proxy-protocol)
|
||||
[](https://docs.rs/actix-proxy-protocol/0.1.0)
|
||||
[](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
|
||||

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

|
||||
[](https://discord.gg/NWpN5mmg3x)
|
||||
|
||||
## Resources
|
||||
|
||||
- [Examples](./examples)
|
||||
|
||||
[proxy protocol]: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
|
173
actix-proxy-protocol/examples/proxy-server.rs
Normal file
173
actix-proxy-protocol/examples/proxy-server.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
//! Adds PROXY protocol v1 prelude to connections.
|
||||
|
||||
#![allow(unused)]
|
||||
|
||||
use std::{
|
||||
io, mem,
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
|
||||
use actix_proxy_protocol::{tlv, v1, v2, AddressFamily, Command, TransportProtocol};
|
||||
use actix_rt::net::TcpStream;
|
||||
use actix_server::Server;
|
||||
use actix_service::{fn_service, ServiceFactoryExt as _};
|
||||
use arrayvec::ArrayVec;
|
||||
use bytes::BytesMut;
|
||||
use const_str::concat_bytes;
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::io::{
|
||||
copy_bidirectional, AsyncBufReadExt as _, AsyncReadExt as _, AsyncWriteExt as _, BufReader,
|
||||
};
|
||||
|
||||
static UPSTREAM: Lazy<SocketAddr> = Lazy::new(|| SocketAddr::from(([127, 0, 0, 1], 8080)));
|
||||
|
||||
/*
|
||||
NOTES:
|
||||
108 byte buffer on receiver side is enough for any PROXY header
|
||||
after PROXY, receive until CRLF, *then* decode parts
|
||||
TLV = type-length-value
|
||||
|
||||
TO DO:
|
||||
handle UNKNOWN transport
|
||||
v2 UNSPEC mode
|
||||
AF_UNIX socket
|
||||
*/
|
||||
|
||||
fn extend_with_ip_bytes(buf: &mut Vec<u8>, ip: IpAddr) {
|
||||
match ip {
|
||||
IpAddr::V4(ip) => buf.extend_from_slice(&ip.octets()),
|
||||
IpAddr::V6(ip) => buf.extend_from_slice(&ip.octets()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn wrap_with_proxy_protocol_v1(mut stream: TcpStream) -> io::Result<()> {
|
||||
let mut upstream = TcpStream::connect(("127.0.0.1", 8080)).await?;
|
||||
|
||||
tracing::info!(
|
||||
"PROXYv1 {} -> {}",
|
||||
stream.peer_addr().unwrap(),
|
||||
UPSTREAM.to_string(),
|
||||
);
|
||||
|
||||
let proxy_header = v1::Header::new(
|
||||
AddressFamily::Inet,
|
||||
SocketAddr::from(([127, 0, 0, 1], 8081)),
|
||||
*UPSTREAM,
|
||||
);
|
||||
|
||||
proxy_header.write_to_tokio(&mut upstream).await?;
|
||||
|
||||
let (_bytes_read, _bytes_written) = copy_bidirectional(&mut stream, &mut upstream).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn wrap_with_proxy_protocol_v2(mut stream: TcpStream) -> io::Result<()> {
|
||||
let mut upstream = TcpStream::connect(("127.0.0.1", 8080)).await?;
|
||||
|
||||
tracing::info!(
|
||||
"PROXYv2 {} -> {}",
|
||||
stream.peer_addr().unwrap(),
|
||||
UPSTREAM.to_string(),
|
||||
);
|
||||
|
||||
let mut proxy_header = v2::Header::new_tcp_ipv4_proxy(([127, 0, 0, 1], 8082), *UPSTREAM);
|
||||
|
||||
proxy_header.add_typed_tlv(tlv::UniqueId::new("4269")); // UNIQUE_ID
|
||||
proxy_header.add_typed_tlv(tlv::Noop::new("NOOP m8")); // NOOP
|
||||
proxy_header.add_typed_tlv(tlv::Authority::new("localhost")); // NOOP
|
||||
proxy_header.add_typed_tlv(tlv::Alpn::new("http/1.1")); // NOOP
|
||||
proxy_header.add_crc23c_checksum();
|
||||
|
||||
proxy_header.write_to_tokio(&mut upstream).await?;
|
||||
|
||||
let (_bytes_read, _bytes_written) = copy_bidirectional(&mut stream, &mut upstream).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn unwrap_proxy_protocol(mut stream: TcpStream) -> io::Result<()> {
|
||||
let mut upstream = TcpStream::connect(("127.0.0.1", 8080)).await?;
|
||||
|
||||
tracing::info!(
|
||||
"PROXY unwrap {} -> {}",
|
||||
stream.peer_addr().unwrap(),
|
||||
UPSTREAM.to_string(),
|
||||
);
|
||||
|
||||
let mut header = [0; 12];
|
||||
stream.peek(&mut header).await?;
|
||||
|
||||
eprintln!("header: {}", String::from_utf8_lossy(&header));
|
||||
|
||||
if &header[..v1::SIGNATURE.len()] == v1::SIGNATURE.as_bytes() {
|
||||
tracing::info!("v1");
|
||||
|
||||
let mut stream = BufReader::new(stream);
|
||||
let mut buf = Vec::with_capacity(v1::MAX_HEADER_SIZE);
|
||||
let _len = stream.read_until(b'\n', &mut buf).await?;
|
||||
|
||||
eprintln!("{}", String::from_utf8_lossy(&buf));
|
||||
|
||||
let (rest, header) = match v1::Header::try_from_bytes(&buf) {
|
||||
Ok((rest, header)) => (rest, header),
|
||||
Err(err) => {
|
||||
match err {
|
||||
nom::Err::Incomplete(needed) => todo!(),
|
||||
nom::Err::Error(err) => {
|
||||
eprintln!(
|
||||
"err {:?}, input: {}",
|
||||
err.code,
|
||||
String::from_utf8_lossy(err.input)
|
||||
)
|
||||
}
|
||||
nom::Err::Failure(_) => todo!(),
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
eprintln!("{:02X?} - {:?}", rest, header);
|
||||
|
||||
let (_bytes_read, _bytes_written) = copy_bidirectional(&mut stream, &mut upstream).await?;
|
||||
} else if header == v2::SIGNATURE {
|
||||
tracing::info!("v2");
|
||||
let (_bytes_read, _bytes_written) = copy_bidirectional(&mut stream, &mut upstream).await?;
|
||||
} else {
|
||||
tracing::warn!("No proxy header; closing");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_server() -> io::Result<Server> {
|
||||
tracing::info!("proxying to 127.0.0.1:8080");
|
||||
|
||||
Ok(Server::build()
|
||||
.bind("proxy-protocol-v1", ("127.0.0.1", 8081), move || {
|
||||
fn_service(wrap_with_proxy_protocol_v1)
|
||||
.map_err(|err| tracing::error!("service error: {err:?}"))
|
||||
})?
|
||||
.bind("proxy-protocol-v2", ("127.0.0.1", 8082), move || {
|
||||
fn_service(wrap_with_proxy_protocol_v2)
|
||||
.map_err(|err| tracing::error!("service error: {err:?}"))
|
||||
})?
|
||||
.bind("proxy-protocol-unwrap", ("127.0.0.1", 8083), move || {
|
||||
fn_service(unwrap_proxy_protocol)
|
||||
.map_err(|err| tracing::error!("service error: {err:?}"))
|
||||
})?
|
||||
.workers(2)
|
||||
.run())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
tracing_subscriber::fmt::fmt().without_time().init();
|
||||
|
||||
start_server()?.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
156
actix-proxy-protocol/src/lib.rs
Normal file
156
actix-proxy-protocol/src/lib.rs
Normal file
@@ -0,0 +1,156 @@
|
||||
//! PROXY protocol.
|
||||
|
||||
#![expect(dead_code)]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||
|
||||
pub mod tlv;
|
||||
pub mod v1;
|
||||
pub mod v2;
|
||||
|
||||
/// PROXY Protocol Version.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Version {
|
||||
/// Human-readable header format (Version 1)
|
||||
V1,
|
||||
|
||||
/// Binary header format (Version 2)
|
||||
V2,
|
||||
}
|
||||
|
||||
impl Version {
|
||||
const fn signature(&self) -> &'static [u8] {
|
||||
match self {
|
||||
Version::V1 => v1::SIGNATURE.as_bytes(),
|
||||
Version::V2 => v2::SIGNATURE.as_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
const fn v2_hi(&self) -> u8 {
|
||||
(match self {
|
||||
Version::V1 => panic!("v1 not supported in PROXY v2"),
|
||||
Version::V2 => 0x2,
|
||||
}) << 4
|
||||
}
|
||||
}
|
||||
|
||||
/// Command
|
||||
///
|
||||
/// other values are unassigned and must not be emitted by senders. Receivers
|
||||
/// must drop connections presenting unexpected values here.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Command {
|
||||
/// \x0 : LOCAL : the connection was established on purpose by the proxy
|
||||
/// without being relayed. The connection endpoints are the sender and the
|
||||
/// receiver. Such connections exist when the proxy sends health-checks to the
|
||||
/// server. The receiver must accept this connection as valid and must use the
|
||||
/// real connection endpoints and discard the protocol block including the
|
||||
/// family which is ignored.
|
||||
Local,
|
||||
|
||||
/// \x1 : PROXY : the connection was established on behalf of another node,
|
||||
/// and reflects the original connection endpoints. The receiver must then use
|
||||
/// the information provided in the protocol block to get original the address.
|
||||
Proxy,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
const fn v2_lo(&self) -> u8 {
|
||||
match self {
|
||||
Command::Local => 0x0,
|
||||
Command::Proxy => 0x1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Address Family.
|
||||
///
|
||||
/// maps to the original socket family without necessarily
|
||||
/// matching the values internally used by the system.
|
||||
///
|
||||
/// other values are unspecified and must not be emitted in version 2 of this
|
||||
/// protocol and must be rejected as invalid by receivers.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum AddressFamily {
|
||||
/// 0x0 : AF_UNSPEC : the connection is forwarded for an unknown, unspecified
|
||||
/// or unsupported protocol. The sender should use this family when sending
|
||||
/// LOCAL commands or when dealing with unsupported protocol families. The
|
||||
/// receiver is free to accept the connection anyway and use the real endpoint
|
||||
/// addresses or to reject it. The receiver should ignore address information.
|
||||
Unspecified,
|
||||
|
||||
/// 0x1 : AF_INET : the forwarded connection uses the AF_INET address family
|
||||
/// (IPv4). The addresses are exactly 4 bytes each in network byte order,
|
||||
/// followed by transport protocol information (typically ports).
|
||||
Inet,
|
||||
|
||||
/// 0x2 : AF_INET6 : the forwarded connection uses the AF_INET6 address family
|
||||
/// (IPv6). The addresses are exactly 16 bytes each in network byte order,
|
||||
/// followed by transport protocol information (typically ports).
|
||||
Inet6,
|
||||
|
||||
/// 0x3 : AF_UNIX : the forwarded connection uses the AF_UNIX address family
|
||||
/// (UNIX). The addresses are exactly 108 bytes each.
|
||||
Unix,
|
||||
}
|
||||
|
||||
impl AddressFamily {
|
||||
pub(crate) fn v1_str(&self) -> &'static str {
|
||||
match self {
|
||||
AddressFamily::Inet => "TCP4",
|
||||
AddressFamily::Inet6 => "TCP6",
|
||||
af => panic!("{:?} is not supported in PROXY v1", af),
|
||||
}
|
||||
}
|
||||
|
||||
const fn v2_hi(&self) -> u8 {
|
||||
(match self {
|
||||
AddressFamily::Unspecified => 0x0,
|
||||
AddressFamily::Inet => 0x1,
|
||||
AddressFamily::Inet6 => 0x2,
|
||||
AddressFamily::Unix => 0x3,
|
||||
}) << 4
|
||||
}
|
||||
}
|
||||
|
||||
/// Transport Protocol.
|
||||
///
|
||||
/// other values are unspecified and must not be emitted in version 2 of this
|
||||
/// protocol and must be rejected as invalid by receivers.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TransportProtocol {
|
||||
/// 0x0 : UNSPEC : the connection is forwarded for an unknown, unspecified
|
||||
/// or unsupported protocol. The sender should use this family when sending
|
||||
/// LOCAL commands or when dealing with unsupported protocol families. The
|
||||
/// receiver is free to accept the connection anyway and use the real endpoint
|
||||
/// addresses or to reject it. The receiver should ignore address information.
|
||||
Unspecified,
|
||||
|
||||
/// 0x1 : STREAM : the forwarded connection uses a SOCK_STREAM protocol (eg:
|
||||
/// TCP or UNIX_STREAM). When used with AF_INET/AF_INET6 (TCP), the addresses
|
||||
/// are followed by the source and destination ports represented on 2 bytes
|
||||
/// each in network byte order.
|
||||
Stream,
|
||||
|
||||
/// 0x2 : DGRAM : the forwarded connection uses a SOCK_DGRAM protocol (eg:
|
||||
/// UDP or UNIX_DGRAM). When used with AF_INET/AF_INET6 (UDP), the addresses
|
||||
/// are followed by the source and destination ports represented on 2 bytes
|
||||
/// each in network byte order.
|
||||
Datagram,
|
||||
}
|
||||
|
||||
impl TransportProtocol {
|
||||
const fn v2_lo(&self) -> u8 {
|
||||
match self {
|
||||
TransportProtocol::Unspecified => 0x0,
|
||||
TransportProtocol::Stream => 0x1,
|
||||
TransportProtocol::Datagram => 0x2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ProxyProtocolHeader {
|
||||
V1(v1::Header),
|
||||
V2(v2::Header),
|
||||
}
|
292
actix-proxy-protocol/src/tlv.rs
Normal file
292
actix-proxy-protocol/src/tlv.rs
Normal file
@@ -0,0 +1,292 @@
|
||||
use std::{borrow::Cow, convert::TryFrom, str};
|
||||
|
||||
const PP2_TYPE_ALPN: u8 = 0x01; // done
|
||||
const PP2_TYPE_AUTHORITY: u8 = 0x02; // done
|
||||
const PP2_TYPE_CRC32C: u8 = 0x03; // done
|
||||
const PP2_TYPE_NOOP: u8 = 0x04; // done
|
||||
const PP2_TYPE_UNIQUE_ID: u8 = 0x05; // done
|
||||
const PP2_TYPE_SSL: u8 = 0x20;
|
||||
const PP2_SUBTYPE_SSL_VERSION: u8 = 0x21;
|
||||
const PP2_SUBTYPE_SSL_CN: u8 = 0x22;
|
||||
const PP2_SUBTYPE_SSL_CIPHER: u8 = 0x23;
|
||||
const PP2_SUBTYPE_SSL_SIG_ALG: u8 = 0x24;
|
||||
const PP2_SUBTYPE_SSL_KEY_ALG: u8 = 0x25;
|
||||
const PP2_TYPE_NETNS: u8 = 0x30;
|
||||
|
||||
pub trait Tlv: Sized {
|
||||
const TYPE: u8;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self>;
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]>;
|
||||
|
||||
fn try_from_parts(typ: u8, value: &[u8]) -> Option<Self> {
|
||||
if typ != Self::TYPE {
|
||||
return None;
|
||||
}
|
||||
|
||||
Self::try_from_value(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Application-Layer Protocol Negotiation (ALPN). It is a byte sequence defining
|
||||
/// the upper layer protocol in use over the connection. The most common use case
|
||||
/// will be to pass the exact copy of the ALPN extension of the Transport Layer
|
||||
/// Security (TLS) protocol as defined by RFC7301 [9].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Alpn {
|
||||
alpn: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Alpn {
|
||||
///
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `alpn` is empty (i.e., has length of 0).
|
||||
pub fn new(alpn: impl Into<Vec<u8>>) -> Self {
|
||||
let alpn = alpn.into();
|
||||
|
||||
assert!(!alpn.is_empty(), "ALPN TLV value cannot be empty");
|
||||
|
||||
Self { alpn }
|
||||
}
|
||||
}
|
||||
|
||||
impl Tlv for Alpn {
|
||||
const TYPE: u8 = PP2_TYPE_ALPN;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self> {
|
||||
Some(Self {
|
||||
alpn: value.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Borrowed(&self.alpn)
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the host name value passed by the client, as an UTF8-encoded string.
|
||||
/// In case of TLS being used on the client connection, this is the exact copy of
|
||||
/// the "server_name" extension as defined by RFC3546 [10], section 3.1, often
|
||||
/// referred to as "SNI". There are probably other situations where an authority
|
||||
/// can be mentioned on a connection without TLS being involved at all.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Authority {
|
||||
authority: String,
|
||||
}
|
||||
|
||||
impl Authority {
|
||||
/// A UTF-8
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `authority` is an empty string.
|
||||
pub fn new(authority: impl Into<String>) -> Self {
|
||||
let authority = authority.into();
|
||||
|
||||
assert!(!authority.is_empty(), "Authority TLV value cannot be empty");
|
||||
|
||||
Self { authority }
|
||||
}
|
||||
}
|
||||
|
||||
impl Tlv for Authority {
|
||||
const TYPE: u8 = PP2_TYPE_AUTHORITY;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self> {
|
||||
Some(Self {
|
||||
authority: str::from_utf8(value).ok()?.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Borrowed(self.authority.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
/// The value of the type PP2_TYPE_CRC32C is a 32-bit number storing the CRC32c
|
||||
/// checksum of the PROXY protocol header.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct Crc32c {
|
||||
pub(crate) checksum: u32,
|
||||
}
|
||||
|
||||
impl Tlv for Crc32c {
|
||||
const TYPE: u8 = PP2_TYPE_CRC32C;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self> {
|
||||
let checksum_bytes = <[u8; 4]>::try_from(value).ok()?;
|
||||
|
||||
Some(Self {
|
||||
checksum: u32::from_be_bytes(checksum_bytes),
|
||||
})
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Owned(self.checksum.to_be_bytes().to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
/// The TLV of this type should be ignored when parsed. The value is zero or more
|
||||
/// bytes. Can be used for data padding or alignment. Note that it can be used
|
||||
/// to align only by 3 or more bytes because a TLV can not be smaller than that.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Noop {
|
||||
value: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Noop {
|
||||
///
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `value` is empty (i.e., has length of 0).
|
||||
pub fn new(value: impl Into<Vec<u8>>) -> Self {
|
||||
let value = value.into();
|
||||
|
||||
assert!(!value.is_empty(), "Noop TLV `value` cannot be empty");
|
||||
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
impl Tlv for Noop {
|
||||
const TYPE: u8 = PP2_TYPE_NOOP;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self> {
|
||||
Some(Self {
|
||||
value: value.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Borrowed(&self.value)
|
||||
}
|
||||
}
|
||||
|
||||
/// The value of the type PP2_TYPE_UNIQUE_ID is an opaque byte sequence of up to
|
||||
/// 128 bytes generated by the upstream proxy that uniquely identifies the
|
||||
/// connection.
|
||||
///
|
||||
/// The unique ID can be used to easily correlate connections across multiple
|
||||
/// layers of proxies, without needing to look up IP addresses and port numbers.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct UniqueId {
|
||||
value: Vec<u8>,
|
||||
}
|
||||
|
||||
impl UniqueId {
|
||||
///
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `value` is 0 bytes or larger than 128 bytes.
|
||||
pub fn new(id: impl Into<Vec<u8>>) -> Self {
|
||||
let value = id.into();
|
||||
|
||||
assert!(!value.is_empty(), "UniqueId TLV `value` cannot be empty");
|
||||
assert!(
|
||||
value.len() < 128,
|
||||
"UniqueId TLV `value` cannot be larger than 128 bytes"
|
||||
);
|
||||
|
||||
Self { value }
|
||||
}
|
||||
}
|
||||
|
||||
impl Tlv for UniqueId {
|
||||
const TYPE: u8 = PP2_TYPE_UNIQUE_ID;
|
||||
|
||||
fn try_from_value(value: &[u8]) -> Option<Self> {
|
||||
Some(Self {
|
||||
value: value.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Borrowed(&self.value)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct SslClientFlags: u8 {
|
||||
const PP2_CLIENT_SSL = 0x01;
|
||||
const PP2_CLIENT_CERT_CONN = 0x02;
|
||||
const PP2_CLIENT_CERT_SESS = 0x04;
|
||||
}
|
||||
}
|
||||
|
||||
/// TLS (SSL).
|
||||
///
|
||||
/// Very broken atm.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Ssl {
|
||||
/// The <client> field is made of a bit field indicating which element is present.
|
||||
///
|
||||
/// Note, that each of these elements may lead to extra data being appended to
|
||||
/// this TLV using a second level of TLV encapsulation. It is thus possible to
|
||||
/// find multiple TLV values after this field. The total length of the pp2_tlv_ssl
|
||||
/// TLV will reflect this.
|
||||
client: SslClientFlags,
|
||||
|
||||
/// The <verify> field will be zero if the client presented a certificate
|
||||
/// and it was successfully verified, and non-zero otherwise.
|
||||
verify: bool,
|
||||
|
||||
/// Sub-TLVs.
|
||||
tlvs: Vec<SslTlv>,
|
||||
}
|
||||
|
||||
impl Tlv for Ssl {
|
||||
const TYPE: u8 = PP2_TYPE_SSL;
|
||||
|
||||
fn try_from_value(_value: &[u8]) -> Option<Self> {
|
||||
/// The PP2_CLIENT_SSL flag indicates that the client connected over SSL/TLS. When
|
||||
/// this field is present, the US-ASCII string representation of the TLS version is
|
||||
/// appended at the end of the field in the TLV format using the type
|
||||
/// PP2_SUBTYPE_SSL_VERSION.
|
||||
const PP2_CLIENT_SSL: u8 = 0x01;
|
||||
|
||||
/// PP2_CLIENT_CERT_CONN indicates that the client provided a certificate over the
|
||||
/// current connection.
|
||||
const PP2_CLIENT_CERT_CONN: u8 = 0x02;
|
||||
|
||||
/// PP2_CLIENT_CERT_SESS indicates that the client provided a
|
||||
/// certificate at least once over the TLS session this connection belongs to.
|
||||
const PP2_CLIENT_CERT_SESS: u8 = 0x04;
|
||||
|
||||
// TODO: finish parsing
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn value_bytes(&self) -> Cow<'_, [u8]> {
|
||||
Cow::Borrowed(&[])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct SslTlv {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
// #[test]
|
||||
// #[should_panic]
|
||||
// fn tlv_zero_len() {
|
||||
// Tlv::new(0x00, vec![]);
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn tlv_as_crc32c() {
|
||||
// noop
|
||||
assert_eq!(Crc32c::try_from_parts(0x04, &[0x00]), None);
|
||||
|
||||
assert_eq!(
|
||||
Crc32c::try_from_parts(0x03, &[0x08, 0x70, 0x17, 0x7b]),
|
||||
Some(Crc32c {
|
||||
checksum: 141563771
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
148
actix-proxy-protocol/src/v1.rs
Normal file
148
actix-proxy-protocol/src/v1.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use std::{fmt, io, net::SocketAddr};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use nom::{IResult, Parser as _};
|
||||
use tokio::io::{AsyncWrite, AsyncWriteExt as _};
|
||||
|
||||
use crate::AddressFamily;
|
||||
|
||||
pub const SIGNATURE: &str = "PROXY";
|
||||
pub const MAX_HEADER_SIZE: usize = 107;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Header {
|
||||
/// Address family.
|
||||
af: AddressFamily,
|
||||
|
||||
/// Source address.
|
||||
src: SocketAddr,
|
||||
|
||||
/// Destination address.
|
||||
dst: SocketAddr,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub const fn new(af: AddressFamily, src: SocketAddr, dst: SocketAddr) -> Self {
|
||||
Self { af, src, dst }
|
||||
}
|
||||
|
||||
pub const fn new_inet(src: SocketAddr, dst: SocketAddr) -> Self {
|
||||
Self::new(AddressFamily::Inet, src, dst)
|
||||
}
|
||||
|
||||
pub const fn new_inet6(src: SocketAddr, dst: SocketAddr) -> Self {
|
||||
Self::new(AddressFamily::Inet6, src, dst)
|
||||
}
|
||||
|
||||
pub fn write_to(&self, wrt: &mut impl io::Write) -> io::Result<()> {
|
||||
write!(wrt, "{self}")
|
||||
}
|
||||
|
||||
pub async fn write_to_tokio(&self, wrt: &mut (impl AsyncWrite + Unpin)) -> io::Result<()> {
|
||||
// max length of a V1 header is 107 bytes
|
||||
let mut buf = ArrayVec::<_, MAX_HEADER_SIZE>::new();
|
||||
self.write_to(&mut buf)?;
|
||||
wrt.write_all(&buf).await
|
||||
}
|
||||
|
||||
pub fn try_from_bytes(slice: &[u8]) -> IResult<&[u8], Self> {
|
||||
parsing::parse(slice)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Header {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{proto_sig} {af} {src_ip} {dst_ip} {src_port} {dst_port}\r\n",
|
||||
proto_sig = SIGNATURE,
|
||||
af = self.af.v1_str(),
|
||||
src_ip = self.src.ip(),
|
||||
dst_ip = self.dst.ip(),
|
||||
src_port = itoa::Buffer::new().format(self.src.port()),
|
||||
dst_port = itoa::Buffer::new().format(self.dst.port()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
mod parsing {
|
||||
use std::{
|
||||
net::{Ipv4Addr, SocketAddrV4},
|
||||
str::{self, FromStr},
|
||||
};
|
||||
|
||||
use nom::{
|
||||
branch::alt,
|
||||
bytes::complete::{tag, take_while},
|
||||
character::complete::char,
|
||||
combinator::{map, map_res},
|
||||
IResult,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Parses a number from serialized representation (as bytes).
|
||||
fn parse_number<T: FromStr>(input: &[u8]) -> IResult<&[u8], T> {
|
||||
map_res(take_while(|c: u8| c.is_ascii_digit()), |s: &[u8]| {
|
||||
let s = str::from_utf8(s).map_err(|_| "utf8 error")?;
|
||||
let val = s.parse::<T>().map_err(|_| "u8 parse error")?;
|
||||
Ok::<_, Box<dyn std::error::Error>>(val)
|
||||
})
|
||||
.parse(input)
|
||||
}
|
||||
|
||||
/// Parses an address family.
|
||||
fn parse_address_family(input: &[u8]) -> IResult<&[u8], AddressFamily> {
|
||||
map_res(alt((tag("TCP4"), tag("TCP6"))), |af: &[u8]| match af {
|
||||
b"TCP4" => Ok(AddressFamily::Inet),
|
||||
b"TCP6" => Ok(AddressFamily::Inet6),
|
||||
_ => Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"invalid address family",
|
||||
)),
|
||||
})
|
||||
.parse(input)
|
||||
}
|
||||
|
||||
/// Parses an IPv4 address from serialized representation (as bytes).
|
||||
fn parse_ipv4(input: &[u8]) -> IResult<&[u8], Ipv4Addr> {
|
||||
map(
|
||||
(
|
||||
parse_number::<u8>,
|
||||
char('.'),
|
||||
parse_number::<u8>,
|
||||
char('.'),
|
||||
parse_number::<u8>,
|
||||
char('.'),
|
||||
parse_number::<u8>,
|
||||
),
|
||||
|(a, _, b, _, c, _, d)| Ipv4Addr::new(a, b, c, d),
|
||||
)
|
||||
.parse(input)
|
||||
}
|
||||
|
||||
/// Parses an IPv4 address from ASCII bytes.
|
||||
pub(super) fn parse(input: &[u8]) -> IResult<&[u8], Header> {
|
||||
map(
|
||||
(
|
||||
tag(SIGNATURE),
|
||||
char(' '),
|
||||
parse_address_family,
|
||||
char(' '),
|
||||
parse_ipv4,
|
||||
char(' '),
|
||||
parse_ipv4,
|
||||
char(' '),
|
||||
parse_number::<u16>,
|
||||
char(' '),
|
||||
parse_number::<u16>,
|
||||
),
|
||||
|(_, _, af, _, src_ip, _, dst_ip, _, src_port, _, dst_port)| Header {
|
||||
af,
|
||||
src: SocketAddr::V4(SocketAddrV4::new(src_ip, src_port)),
|
||||
dst: SocketAddr::V4(SocketAddrV4::new(dst_ip, dst_port)),
|
||||
},
|
||||
)
|
||||
.parse(input)
|
||||
}
|
||||
}
|
304
actix-proxy-protocol/src/v2.rs
Normal file
304
actix-proxy-protocol/src/v2.rs
Normal file
@@ -0,0 +1,304 @@
|
||||
use std::{
|
||||
io,
|
||||
net::{IpAddr, SocketAddr},
|
||||
};
|
||||
|
||||
use smallvec::{SmallVec, ToSmallVec as _};
|
||||
use tokio::io::{AsyncWrite, AsyncWriteExt as _};
|
||||
|
||||
use crate::{
|
||||
tlv::{Crc32c, Tlv},
|
||||
AddressFamily, Command, TransportProtocol, Version,
|
||||
};
|
||||
|
||||
pub const SIGNATURE: [u8; 12] = [
|
||||
0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A,
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Header {
|
||||
command: Command,
|
||||
transport_protocol: TransportProtocol,
|
||||
address_family: AddressFamily,
|
||||
src: SocketAddr,
|
||||
dst: SocketAddr,
|
||||
tlvs: SmallVec<[(u8, SmallVec<[u8; 16]>); 4]>,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn new(
|
||||
command: Command,
|
||||
transport_protocol: TransportProtocol,
|
||||
address_family: AddressFamily,
|
||||
src: impl Into<SocketAddr>,
|
||||
dst: impl Into<SocketAddr>,
|
||||
) -> Self {
|
||||
Self {
|
||||
command,
|
||||
transport_protocol,
|
||||
address_family,
|
||||
src: src.into(),
|
||||
dst: dst.into(),
|
||||
tlvs: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_tcp_ipv4_proxy(src: impl Into<SocketAddr>, dst: impl Into<SocketAddr>) -> Self {
|
||||
Self::new(
|
||||
Command::Proxy,
|
||||
TransportProtocol::Stream,
|
||||
AddressFamily::Inet,
|
||||
src,
|
||||
dst,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn add_tlv(&mut self, typ: u8, value: impl AsRef<[u8]>) {
|
||||
self.tlvs.push((typ, SmallVec::from_slice(value.as_ref())));
|
||||
}
|
||||
|
||||
pub fn add_typed_tlv<T: Tlv>(&mut self, tlv: T) {
|
||||
self.add_tlv(T::TYPE, tlv.value_bytes());
|
||||
}
|
||||
|
||||
fn v2_len(&self) -> u16 {
|
||||
let addr_len = if self.src.is_ipv4() {
|
||||
4 + 2 // 4b IPv4 + 2b port number
|
||||
} else {
|
||||
16 + 2 // 16b IPv6 + 2b port number
|
||||
};
|
||||
|
||||
(addr_len * 2)
|
||||
+ self
|
||||
.tlvs
|
||||
.iter()
|
||||
.map(|(_, value)| 1 + 2 + value.len() as u16)
|
||||
.sum::<u16>()
|
||||
}
|
||||
|
||||
pub fn write_to(&self, wrt: &mut impl io::Write) -> io::Result<()> {
|
||||
// PROXY v2 signature
|
||||
wrt.write_all(&SIGNATURE)?;
|
||||
|
||||
// version | command
|
||||
wrt.write_all(&[Version::V2.v2_hi() | self.command.v2_lo()])?;
|
||||
|
||||
// address family | transport protocol
|
||||
wrt.write_all(&[self.address_family.v2_hi() | self.transport_protocol.v2_lo()])?;
|
||||
|
||||
// rest-of-header length
|
||||
wrt.write_all(&self.v2_len().to_be_bytes())?;
|
||||
|
||||
tracing::debug!("proxy rest-of-header len: {}", self.v2_len());
|
||||
|
||||
fn write_ip_bytes_to(wrt: &mut impl io::Write, ip: IpAddr) -> io::Result<()> {
|
||||
match ip {
|
||||
IpAddr::V4(ip) => wrt.write_all(&ip.octets()),
|
||||
IpAddr::V6(ip) => wrt.write_all(&ip.octets()),
|
||||
}
|
||||
}
|
||||
|
||||
// L3 (IP) address
|
||||
write_ip_bytes_to(wrt, self.src.ip())?;
|
||||
write_ip_bytes_to(wrt, self.dst.ip())?;
|
||||
|
||||
// L4 ports
|
||||
wrt.write_all(&self.src.port().to_be_bytes())?;
|
||||
wrt.write_all(&self.dst.port().to_be_bytes())?;
|
||||
|
||||
// TLVs
|
||||
for (typ, value) in &self.tlvs {
|
||||
wrt.write_all(&[*typ])?;
|
||||
wrt.write_all(&(value.len() as u16).to_be_bytes())?;
|
||||
wrt.write_all(value)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn write_to_tokio(&self, wrt: &mut (impl AsyncWrite + Unpin)) -> io::Result<()> {
|
||||
let buf = self.to_vec();
|
||||
wrt.write_all(&buf).await
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Vec<u8> {
|
||||
// TODO: figure out cap
|
||||
let mut buf = Vec::with_capacity(64);
|
||||
self.write_to(&mut buf).unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn has_tlv<T: Tlv>(&self) -> bool {
|
||||
self.tlvs.iter().any(|&(typ, _)| typ == T::TYPE)
|
||||
}
|
||||
|
||||
/// Calculates and adds a crc32c TLV to the PROXY header.
|
||||
///
|
||||
/// Uses method defined in spec.
|
||||
///
|
||||
/// If this is not called last thing it will be wrong.
|
||||
pub fn add_crc23c_checksum(&mut self) {
|
||||
// don't add a checksum if it is already set
|
||||
if self.has_tlv::<Crc32c>() {
|
||||
return;
|
||||
}
|
||||
|
||||
// When the checksum is supported by the sender after constructing the header
|
||||
// the sender MUST:
|
||||
// - initialize the checksum field to '0's.
|
||||
// - calculate the CRC32c checksum of the PROXY header as described in RFC4960,
|
||||
// Appendix B [8].
|
||||
// - put the resultant value into the checksum field, and leave the rest of
|
||||
// the bits unchanged.
|
||||
|
||||
// add zeroed checksum field to TLVs
|
||||
self.add_typed_tlv(Crc32c::default());
|
||||
|
||||
// write PROXY header to buffer
|
||||
let mut buf = Vec::new();
|
||||
self.write_to(&mut buf).unwrap();
|
||||
|
||||
// calculate CRC on buffer and update CRC TLV
|
||||
let crc_calc = crc32fast::hash(&buf);
|
||||
self.tlvs.last_mut().unwrap().1 = crc_calc.to_be_bytes().to_smallvec();
|
||||
|
||||
tracing::debug!("checksum is {}", crc_calc);
|
||||
}
|
||||
|
||||
pub fn validate_crc32c_tlv(&self) -> Option<bool> {
|
||||
// extract crc32c TLV or exit early if none is present
|
||||
let crc_sent = self
|
||||
.tlvs
|
||||
.iter()
|
||||
.filter_map(|(typ, value)| Crc32c::try_from_parts(*typ, value))
|
||||
.next()?;
|
||||
|
||||
// If the checksum is provided as part of the PROXY header and the checksum
|
||||
// functionality is supported by the receiver, the receiver MUST:
|
||||
// - store the received CRC32c checksum value aside.
|
||||
// - replace the 32 bits of the checksum field in the received PROXY header with
|
||||
// all '0's and calculate a CRC32c checksum value of the whole PROXY header.
|
||||
// - verify that the calculated CRC32c checksum is the same as the received
|
||||
// CRC32c checksum. If it is not, the receiver MUST treat the TCP connection
|
||||
// providing the header as invalid.
|
||||
// The default procedure for handling an invalid TCP connection is to abort it.
|
||||
|
||||
let mut this = self.clone();
|
||||
for (typ, value) in this.tlvs.iter_mut() {
|
||||
if Crc32c::try_from_parts(*typ, value).is_some() {
|
||||
value.fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
let mut buf = Vec::new();
|
||||
this.write_to(&mut buf).unwrap();
|
||||
let crc_calc = crc32fast::hash(&buf);
|
||||
|
||||
Some(crc_sent.checksum == crc_calc)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::net::Ipv6Addr;
|
||||
|
||||
use const_str::hex;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn write_v2_no_tlvs() {
|
||||
let mut exp = Vec::new();
|
||||
exp.extend_from_slice(&SIGNATURE); // 0-11
|
||||
exp.extend_from_slice(&[0x21, 0x11]); // 12-13
|
||||
exp.extend_from_slice(&[0x00, 0x0C]); // 14-15
|
||||
exp.extend_from_slice(&[127, 0, 0, 1, 127, 0, 0, 2]); // 16-23
|
||||
exp.extend_from_slice(&[0x04, 0xd2, 0x00, 80]); // 24-27
|
||||
|
||||
let header = Header::new(
|
||||
Command::Proxy,
|
||||
TransportProtocol::Stream,
|
||||
AddressFamily::Inet,
|
||||
SocketAddr::from(([127, 0, 0, 1], 1234)),
|
||||
SocketAddr::from(([127, 0, 0, 2], 80)),
|
||||
);
|
||||
|
||||
assert_eq!(header.v2_len(), 12);
|
||||
assert_eq!(header.to_vec(), exp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_v2_ipv6_tlv_noop() {
|
||||
let mut exp = Vec::new();
|
||||
exp.extend_from_slice(&SIGNATURE); // 0-11
|
||||
exp.extend_from_slice(&[0x20, 0x11]); // 12-13
|
||||
exp.extend_from_slice(&[0x00, 0x28]); // 14-15
|
||||
exp.extend_from_slice(&hex!("00000000000000000000000000000001")); // 16-31
|
||||
exp.extend_from_slice(&hex!("000102030405060708090A0B0C0D0E0F")); // 32-45
|
||||
exp.extend_from_slice(&[0x00, 80, 0xff, 0xff]); // 45-49
|
||||
exp.extend_from_slice(&[0x04, 0x00, 0x01, 0x00]); // 50-53 NOOP TLV
|
||||
|
||||
let mut header = Header::new(
|
||||
Command::Local,
|
||||
TransportProtocol::Stream,
|
||||
AddressFamily::Inet,
|
||||
SocketAddr::from((Ipv6Addr::LOCALHOST, 80)),
|
||||
SocketAddr::from((
|
||||
Ipv6Addr::from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]),
|
||||
65535,
|
||||
)),
|
||||
);
|
||||
|
||||
header.add_tlv(0x04, [0]);
|
||||
|
||||
assert_eq!(header.v2_len(), 36 + 4);
|
||||
assert_eq!(header.to_vec(), exp);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_v2_tlv_c2c() {
|
||||
let mut exp = Vec::new();
|
||||
exp.extend_from_slice(&SIGNATURE); // 0-11
|
||||
exp.extend_from_slice(&[0x21, 0x11]); // 12-13
|
||||
exp.extend_from_slice(&[0x00, 0x13]); // 14-15
|
||||
exp.extend_from_slice(&[127, 0, 0, 1, 127, 0, 0, 1]); // 16-23
|
||||
exp.extend_from_slice(&[0x00, 80, 0x00, 80]); // 24-27
|
||||
exp.extend_from_slice(&[0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00]); // 28-35 TLV crc32c
|
||||
|
||||
assert_eq!(
|
||||
crc32fast::hash(&exp),
|
||||
// correct checksum calculated manually
|
||||
u32::from_be_bytes([0x08, 0x70, 0x17, 0x7b]),
|
||||
);
|
||||
|
||||
// re-assign actual checksum to last 4 bytes of expected byte array
|
||||
exp[31..35].copy_from_slice(&[0x08, 0x70, 0x17, 0x7b]);
|
||||
|
||||
let mut header = Header::new(
|
||||
Command::Proxy,
|
||||
TransportProtocol::Stream,
|
||||
AddressFamily::Inet,
|
||||
SocketAddr::from(([127, 0, 0, 1], 80)),
|
||||
SocketAddr::from(([127, 0, 0, 1], 80)),
|
||||
);
|
||||
|
||||
assert!(
|
||||
header.validate_crc32c_tlv().is_none(),
|
||||
"header doesn't have CRC TLV added yet"
|
||||
);
|
||||
|
||||
// add crc32c TLV to header
|
||||
header.add_crc23c_checksum();
|
||||
|
||||
assert_eq!(header.v2_len(), 12 + 7);
|
||||
assert_eq!(header.to_vec(), exp);
|
||||
|
||||
// struct can self-validate checksum
|
||||
assert_eq!(header.validate_crc32c_tlv().unwrap(), true);
|
||||
|
||||
// mangle crc32c TLV and assert that validate now fails
|
||||
*header.tlvs.last_mut().unwrap().1.last_mut().unwrap() = 0x00;
|
||||
assert_eq!(header.validate_crc32c_tlv().unwrap(), false);
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.74.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 2.10.0
|
||||
|
||||
|
@@ -115,7 +115,7 @@ impl Arbiter {
|
||||
let system_id = sys.id();
|
||||
let arb_id = COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let name = format!("actix-rt|system:{}|arbiter:{}", system_id, arb_id);
|
||||
let name = format!("actix-rt|system:{system_id}|arbiter:{arb_id}");
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
|
||||
let (ready_tx, ready_rx) = std::sync::mpsc::channel::<()>();
|
||||
|
@@ -187,7 +187,7 @@ impl SystemRunner {
|
||||
|
||||
match exit_code {
|
||||
0 => Ok(()),
|
||||
nonzero => Err(io::Error::other(format!("Non-zero exit code: {}", nonzero))),
|
||||
nonzero => Err(io::Error::other(format!("Non-zero exit code: {nonzero}"))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,7 +5,7 @@
|
||||
## 2.6.0
|
||||
|
||||
- Add `ServerBuilder::shutdown_signal()` method.
|
||||
- Minimum supported Rust version (MSRV) is now 1.74.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 2.5.1
|
||||
|
||||
|
@@ -29,7 +29,7 @@ actix-utils = "3"
|
||||
futures-core = { version = "0.3.17", default-features = false, features = ["alloc"] }
|
||||
futures-util = { version = "0.3.17", default-features = false, features = ["alloc"] }
|
||||
mio = { version = "1", features = ["os-poll", "net"] }
|
||||
socket2 = "0.5"
|
||||
socket2 = "0.6"
|
||||
tokio = { version = "1.44.2", features = ["sync"] }
|
||||
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
|
||||
|
||||
|
@@ -130,7 +130,7 @@ impl Accept {
|
||||
if let Err(err) = self.poll.poll(&mut events, self.timeout) {
|
||||
match err.kind() {
|
||||
io::ErrorKind::Interrupted => {}
|
||||
_ => panic!("Poll error: {}", err),
|
||||
_ => panic!("Poll error: {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,6 @@ impl Accept {
|
||||
// task is done. Take care not to take the guard again inside this loop.
|
||||
let mut guard = self.waker_queue.guard();
|
||||
|
||||
#[allow(clippy::significant_drop_in_scrutinee)]
|
||||
match guard.pop_front() {
|
||||
// Worker notified it became available.
|
||||
Some(WakerInterest::WorkerAvailable(idx)) => {
|
||||
@@ -455,8 +454,8 @@ impl Accept {
|
||||
/// All other errors will incur a timeout before next `accept()` call is attempted. The timeout is
|
||||
/// useful to handle resource exhaustion errors like `ENFILE` and `EMFILE`. Otherwise, it could
|
||||
/// enter into a temporary spin loop.
|
||||
fn connection_error(e: &io::Error) -> bool {
|
||||
e.kind() == io::ErrorKind::ConnectionRefused
|
||||
|| e.kind() == io::ErrorKind::ConnectionAborted
|
||||
|| e.kind() == io::ErrorKind::ConnectionReset
|
||||
fn connection_error(err: &io::Error) -> bool {
|
||||
err.kind() == io::ErrorKind::ConnectionRefused
|
||||
|| err.kind() == io::ErrorKind::ConnectionAborted
|
||||
|| err.kind() == io::ErrorKind::ConnectionReset
|
||||
}
|
||||
|
@@ -92,11 +92,9 @@ impl OsSignals {
|
||||
.filter_map(|(kind, sig)| {
|
||||
unix::signal(*kind)
|
||||
.map(|tokio_sig| (*sig, tokio_sig))
|
||||
.map_err(|e| {
|
||||
.map_err(|err| {
|
||||
tracing::error!(
|
||||
"can not initialize stream handler for {:?} err: {}",
|
||||
sig,
|
||||
e
|
||||
"can not initialize stream handler for {sig:?} err: {err}",
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
|
@@ -105,9 +105,9 @@ impl From<StdUnixListener> for MioListener {
|
||||
impl fmt::Debug for MioListener {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
MioListener::Tcp(ref lst) => write!(f, "{:?}", lst),
|
||||
MioListener::Tcp(ref lst) => write!(f, "{lst:?}"),
|
||||
#[cfg(unix)]
|
||||
MioListener::Uds(ref lst) => write!(f, "{:?}", lst),
|
||||
MioListener::Uds(ref lst) => write!(f, "{lst:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,9 +115,9 @@ impl fmt::Debug for MioListener {
|
||||
impl fmt::Display for MioListener {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
MioListener::Tcp(ref lst) => write!(f, "{:?}", lst),
|
||||
MioListener::Tcp(ref lst) => write!(f, "{lst:?}"),
|
||||
#[cfg(unix)]
|
||||
MioListener::Uds(ref lst) => write!(f, "{:?}", lst),
|
||||
MioListener::Uds(ref lst) => write!(f, "{lst:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -133,9 +133,9 @@ impl fmt::Display for SocketAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Self::Unknown => write!(f, "Unknown SocketAddr"),
|
||||
Self::Tcp(ref addr) => write!(f, "{}", addr),
|
||||
Self::Tcp(ref addr) => write!(f, "{addr}"),
|
||||
#[cfg(unix)]
|
||||
Self::Uds(ref addr) => write!(f, "{:?}", addr),
|
||||
Self::Uds(ref addr) => write!(f, "{addr:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,9 +144,9 @@ impl fmt::Debug for SocketAddr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Self::Unknown => write!(f, "Unknown SocketAddr"),
|
||||
Self::Tcp(ref addr) => write!(f, "{:?}", addr),
|
||||
Self::Tcp(ref addr) => write!(f, "{addr:?}"),
|
||||
#[cfg(unix)]
|
||||
Self::Uds(ref addr) => write!(f, "{:?}", addr),
|
||||
Self::Uds(ref addr) => write!(f, "{addr:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,14 +266,14 @@ mod tests {
|
||||
#[test]
|
||||
fn socket_addr() {
|
||||
let addr = SocketAddr::Tcp("127.0.0.1:8080".parse().unwrap());
|
||||
assert!(format!("{:?}", addr).contains("127.0.0.1:8080"));
|
||||
assert_eq!(format!("{}", addr), "127.0.0.1:8080");
|
||||
assert!(format!("{addr:?}").contains("127.0.0.1:8080"));
|
||||
assert_eq!(format!("{addr}"), "127.0.0.1:8080");
|
||||
|
||||
let addr: StdSocketAddr = "127.0.0.1:0".parse().unwrap();
|
||||
let lst = create_mio_tcp_listener(addr, 128, &MpTcp::Disabled).unwrap();
|
||||
let lst = MioListener::Tcp(lst);
|
||||
assert!(format!("{:?}", lst).contains("TcpListener"));
|
||||
assert!(format!("{}", lst).contains("127.0.0.1"));
|
||||
assert!(format!("{lst:?}").contains("TcpListener"));
|
||||
assert!(format!("{lst}").contains("127.0.0.1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -283,12 +283,12 @@ mod tests {
|
||||
if let Ok(socket) = MioUnixListener::bind("/tmp/sock.xxxxx") {
|
||||
let addr = socket.local_addr().expect("Couldn't get local address");
|
||||
let a = SocketAddr::Uds(addr);
|
||||
assert!(format!("{:?}", a).contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{}", a).contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{a:?}").contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{a}").contains("/tmp/sock.xxxxx"));
|
||||
|
||||
let lst = MioListener::Uds(socket);
|
||||
assert!(format!("{:?}", lst).contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{}", lst).contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{lst:?}").contains("/tmp/sock.xxxxx"));
|
||||
assert!(format!("{lst}").contains("/tmp/sock.xxxxx"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -52,7 +52,7 @@ impl WakerQueue {
|
||||
|
||||
waker
|
||||
.wake()
|
||||
.unwrap_or_else(|e| panic!("can not wake up Accept Poll: {}", e));
|
||||
.unwrap_or_else(|err| panic!("can not wake up Accept Poll: {err}"));
|
||||
}
|
||||
|
||||
/// Get a MutexGuard of the waker queue.
|
||||
@@ -62,7 +62,7 @@ impl WakerQueue {
|
||||
|
||||
/// Reset the waker queue so it does not grow infinitely.
|
||||
pub(crate) fn reset(queue: &mut VecDeque<WakerInterest>) {
|
||||
std::mem::swap(&mut VecDeque::<WakerInterest>::with_capacity(16), queue);
|
||||
*queue = VecDeque::<WakerInterest>::with_capacity(16);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -325,7 +325,7 @@ impl ServerWorker {
|
||||
// no actix system
|
||||
(None, Some(rt_handle)) => {
|
||||
std::thread::Builder::new()
|
||||
.name(format!("actix-server worker {}", idx))
|
||||
.name(format!("actix-server worker {idx}"))
|
||||
.spawn(move || {
|
||||
let (worker_stopped_tx, worker_stopped_rx) = oneshot::channel();
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.74.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 2.0.3
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.74.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 3.4.0
|
||||
|
||||
|
@@ -80,7 +80,7 @@ async fn main() -> io::Result<()> {
|
||||
// Set up TLS service factory
|
||||
tls_acceptor
|
||||
.clone()
|
||||
.map_err(|err| println!("Rustls error: {:?}", err))
|
||||
.map_err(|err| println!("Rustls error: {err:?}"))
|
||||
.and_then(move |stream: TlsStream<TcpStream>| {
|
||||
let num = count.fetch_add(1, Ordering::Relaxed);
|
||||
info!("[{}] Got TLS connection: {:?}", num, &*stream);
|
||||
|
@@ -81,9 +81,9 @@ where
|
||||
trace!("TLS handshake success: {:?}", stream.hostname());
|
||||
stream.replace_io(res).1
|
||||
})
|
||||
.map_err(|e| {
|
||||
trace!("TLS handshake error: {:?}", e);
|
||||
io::Error::new(io::ErrorKind::Other, format!("{}", e))
|
||||
.map_err(|err| {
|
||||
trace!("TLS handshake error: {err:?}");
|
||||
io::Error::other(format!("{err}"))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@@ -141,10 +141,7 @@ where
|
||||
}
|
||||
Err(err) => {
|
||||
trace!("TLS handshake error: {:?}", err);
|
||||
Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("{}", err),
|
||||
)))
|
||||
Poll::Ready(Err(io::Error::other(format!("{err}"))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -159,8 +159,7 @@ where
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
match self.get_mut() {
|
||||
Self::InvalidDns => Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
Self::InvalidDns => Poll::Ready(Err(io::Error::other(
|
||||
"Rustls v0.20 can only handle hostname-based connections. Enable the `rustls-0_21` \
|
||||
feature and use the Rustls v0.21 utilities to gain this feature.",
|
||||
))),
|
||||
|
@@ -126,7 +126,7 @@ async fn accepts_connections() {
|
||||
let tls_acceptor = Acceptor::new(openssl_acceptor);
|
||||
|
||||
tls_acceptor
|
||||
.map_err(|err| println!("OpenSSL error: {:?}", err))
|
||||
.map_err(|err| println!("OpenSSL error: {err:?}"))
|
||||
.and_then(move |_stream: TlsStream<TcpStream>| ok(()))
|
||||
}
|
||||
});
|
||||
|
@@ -87,7 +87,7 @@ async fn accepts_connections() {
|
||||
let tls_acceptor = Acceptor::new(rustls_server_config(cert.clone(), key.clone()));
|
||||
|
||||
tls_acceptor
|
||||
.map_err(|err| println!("Rustls error: {:?}", err))
|
||||
.map_err(|err| println!("Rustls error: {err:?}"))
|
||||
.and_then(move |_stream: TlsStream<TcpStream>| ok(()))
|
||||
}
|
||||
});
|
||||
|
@@ -26,7 +26,7 @@ async fn custom_resolver() {
|
||||
port: u16,
|
||||
) -> LocalBoxFuture<'a, Result<Vec<SocketAddr>, Box<dyn std::error::Error>>> {
|
||||
Box::pin(async move {
|
||||
let local = format!("127.0.0.1:{}", port).parse().unwrap();
|
||||
let local = format!("127.0.0.1:{port}").parse().unwrap();
|
||||
Ok(vec![local])
|
||||
})
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.74.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 0.1.0
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.74.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 3.0.1
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.74.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 1.4.0
|
||||
|
||||
|
2
justfile
2
justfile
@@ -23,6 +23,8 @@ fmt:
|
||||
# Downgrade dependencies necessary to run MSRV checks/tests.
|
||||
[private]
|
||||
downgrade-for-msrv:
|
||||
cargo {{ toolchain }} update -p=rayon --precise=1.10.0 # next ver: 1.80.0
|
||||
cargo {{ toolchain }} update -p=rayon-core --precise=1.12.1 # next ver: 1.80.0
|
||||
cargo {{ toolchain }} update -p=native-tls --precise=0.2.13 # next ver: 1.80.0
|
||||
cargo {{ toolchain }} update -p=idna_adapter --precise=1.2.0 # next ver: 1.82.0
|
||||
cargo {{ toolchain }} update -p=litemap --precise=0.7.4 # next ver: 1.81.0
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.74.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 0.1.5
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.74.
|
||||
- Minimum supported Rust version (MSRV) is now 1.75.
|
||||
|
||||
## 0.1.4
|
||||
|
||||
|
Reference in New Issue
Block a user