1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-08-12 16:47:05 +02:00

Compare commits

..

14 Commits

Author SHA1 Message Date
Rob Ede
61b6e01b02 chore(bytestring): prepare release 1.3.1 2023-10-29 23:17:28 +00:00
Rob Ede
392e591820 chore(local-channel): prepare release 0.1.5 2023-10-29 23:16:53 +00:00
Rob Ede
87440e5734 chore(local-channel): prepare release 0.1.5 2023-10-29 23:15:19 +00:00
Rob Ede
665dec456f chore(local-waker): prepare release 0.1.4 2023-10-29 23:14:14 +00:00
Rob Ede
7d138f0c31 build: fix min version compat (#498) 2023-10-29 21:15:40 +00:00
Rob Ede
3cd5d8b07a doc: complete ServerBuilder::workers docs 2023-10-29 18:47:38 +00:00
Rob Ede
09548c96b0 doc: fix doc references 2023-10-29 18:45:12 +00:00
Rob Ede
17fd135349 docs: improve docs on ServerBuilder::{bind, workers} 2023-10-29 18:37:29 +00:00
dependabot[bot]
b9b628c47b Update criterion requirement from 0.4 to 0.5 (#495)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-29 05:08:55 +00:00
dependabot[bot]
580af3dec4 Bump actions/checkout from 3 to 4 (#494)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-29 04:17:06 +00:00
dependabot[bot]
db54639f0f Bump taiki-e/install-action from 2.18.9 to 2.21.2 (#497)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-29 03:30:20 +00:00
Rob Ede
6d9eb7e162 ci: specify full action versions 2023-10-29 01:55:34 +00:00
Rob Ede
69e50b5e66 add dependabot updater for actions and cargo 2023-10-29 01:35:12 +00:00
Rob Ede
17409cd203 docs: add bytestring readme 2023-09-17 20:33:36 +01:00
35 changed files with 228 additions and 1090 deletions

10
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
- package-ecosystem: cargo
directory: /
schedule:
interval: weekly

View File

@@ -36,7 +36,7 @@ jobs:
if: matrix.target.os == 'macos-latest'
run: sudo ifconfig lo0 alias 127.0.0.3
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Free Disk Space
if: matrix.target.os == 'ubuntu-latest'
@@ -52,12 +52,13 @@ jobs:
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Install Rust (${{ matrix.version }})
uses: actions-rust-lang/setup-rust-toolchain@v1
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
with:
toolchain: ${{ matrix.version }}
- uses: taiki-e/install-action@v2
with: { tool: cargo-hack }
- uses: taiki-e/install-action@v2.21.2
with:
tool: cargo-hack
- name: check lib
if: >
@@ -107,15 +108,15 @@ jobs:
name: minimal versions
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust (nightly)
uses: actions-rust-lang/setup-rust-toolchain@v1
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
with:
toolchain: nightly
- name: Install cargo-hack & cargo-minimal-versions
uses: taiki-e/install-action@v1
uses: taiki-e/install-action@v2.21.2
with:
tool: cargo-hack,cargo-minimal-versions
@@ -126,14 +127,15 @@ jobs:
name: nextest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
- name: Install cargo-nextest
uses: taiki-e/install-action@v1
with: { tool: cargo-nextest }
uses: taiki-e/install-action@v2.21.2
with:
tool: cargo-nextest
- name: Test with cargo-nextest
run: cargo nextest run

View File

@@ -24,10 +24,10 @@ jobs:
- { name: Windows (MinGW), os: windows-latest, triple: x86_64-pc-windows-gnu }
- { name: Windows (32-bit), os: windows-latest, triple: i686-pc-windows-msvc }
version:
- 1.65.0 # MSRV
- stable
- { name: msrv, version: 1.65.0 }
- { name: stable, version: stable }
name: ${{ matrix.target.name }} / ${{ matrix.version }}
name: ${{ matrix.target.name }} / ${{ matrix.version.name }}
runs-on: ${{ matrix.target.os }}
env: {}
@@ -37,7 +37,7 @@ jobs:
if: matrix.target.os == 'macos-latest'
run: sudo ifconfig lo0 alias 127.0.0.3
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Free Disk Space
if: matrix.target.os == 'ubuntu-latest'
@@ -52,21 +52,25 @@ jobs:
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL-Win64' | Out-File -FilePath $env:GITHUB_ENV -Append
echo 'OPENSSL_DIR=C:\Program Files\OpenSSL' | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Install Rust (${{ matrix.version }})
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install Rust (${{ matrix.version.name }})
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
with:
toolchain: ${{ matrix.version }}
toolchain: ${{ matrix.version.version }}
- uses: taiki-e/install-action@v1
with: { tool: cargo-hack }
- uses: taiki-e/install-action@v2.21.2
with:
tool: cargo-hack
- name: Generate Cargo.lock
run: cargo generate-lockfile
- name: workaround MSRV issues
if: matrix.version != 'stable'
if: matrix.version.name == 'msrv'
run: |
cargo update -p=time --precise=0.3.16 # time is only a dev dep so shouldn't affect msrv
cargo update -p=time --precise=0.3.16
cargo update -p=clap --precise=4.3.24
cargo update -p=clap_lex --precise=0.5.0
cargo update -p=anstyle --precise=1.0.2
- name: check lib
if: >
@@ -105,9 +109,9 @@ jobs:
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
&& RUSTUP_TOOLCHAIN=${{ matrix.version.version }} cargo ci-test-rustls-020
&& RUSTUP_TOOLCHAIN=${{ matrix.version.version }} cargo ci-test-rustls-021
&& RUSTUP_TOOLCHAIN=${{ matrix.version.version }} cargo ci-test-linux
"
- name: Clear the cargo caches
@@ -120,11 +124,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Rust (nightly)
uses: actions-rust-lang/setup-rust-toolchain@v1
with: { toolchain: nightly }
uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
with:
toolchain: nightly
- name: doc tests io-uring
run: |

View File

@@ -14,9 +14,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
- uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
with:
toolchain: nightly
components: rustfmt
@@ -31,13 +31,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
- uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
with: { components: clippy }
- uses: giraffate/clippy-action@v1
- 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
clippy_flags: --workspace --all-features --tests --examples --bins -- -Dclippy::todo -Aunknown_lints

View File

@@ -23,7 +23,7 @@ jobs:
components: llvm-tools-preview
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@v2.18.9
uses: taiki-e/install-action@v2.21.2
with:
tool: cargo-llvm-cov

View File

@@ -18,9 +18,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: actions-rust-lang/setup-rust-toolchain@v1
- uses: actions-rust-lang/setup-rust-toolchain@v1.5.0
with: { toolchain: nightly }
- name: Build Docs
@@ -30,6 +30,7 @@ jobs:
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
uses: JamesIves/github-pages-deploy-action@v4.4.3
with:
folder: target/doc
single-commit: true

View File

@@ -2,7 +2,6 @@
members = [
"actix-codec",
"actix-macros",
"actix-proxy-protocol",
"actix-rt",
"actix-server",
"actix-service",
@@ -23,7 +22,6 @@ rust-version = "1.65"
[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" }

View File

@@ -25,7 +25,7 @@ tokio-util = { version = "0.7", features = ["codec", "io"] }
tracing = { version = "0.1.30", default-features = false, features = ["log"] }
[dev-dependencies]
criterion = { version = "0.4", features = ["html_reports"] }
criterion = { version = "0.5", features = ["html_reports"] }
tokio-test = "0.4.2"
[[bench]]

View File

@@ -26,6 +26,7 @@ quote = "1"
syn = { version = "2", features = ["full"] }
# minimal versions compat
[target.'cfg(any())'.dependencies]
proc-macro2 = "1.0.60"
[dev-dependencies]

View File

@@ -1,7 +0,0 @@
# Changes
## Unreleased - 2022-xx-xx
## 0.0.1 - 2022-xx-xx
- delete me

View File

@@ -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"] }

View File

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

View File

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

View File

@@ -1,17 +0,0 @@
# actix-proxy-protocol
> Implementation of the [PROXY protocol].
[![crates.io](https://img.shields.io/crates/v/actix-proxy-protocol?label=latest)](https://crates.io/crates/actix-proxy-protocol)
[![Documentation](https://docs.rs/actix-proxy-protocol/badge.svg?version=0.1.0)](https://docs.rs/actix-proxy-protocol/0.1.0)
[![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
![License](https://img.shields.io/crates/l/actix-proxy-protocol.svg)
[![Dependency Status](https://deps.rs/crate/actix-proxy-protocol/0.1.0/status.svg)](https://deps.rs/crate/actix-proxy-protocol/0.1.0)
![Downloads](https://img.shields.io/crates/d/actix-proxy-protocol.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
## Resources
- [Examples](./examples)
[proxy protocol]: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt

View File

@@ -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(())
}

View File

@@ -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),
}

View File

@@ -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
})
);
}
}

View File

@@ -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()),
)
}
}

View File

@@ -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);
}
}

View File

@@ -203,16 +203,20 @@ impl SystemRunner {
.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.
/// This method provides access to the runtime, allowing direct interaction with its features.
/// The Actix runtime is responsible for managing the event loop for an Actix system and
/// executing asynchronous tasks. This method provides access to the runtime, allowing direct
/// interaction with its features.
///
/// In a typical use case, you might need to share the same runtime between different
/// parts of your project. For example, some components might require a [`actix_rt::Runtime`] to spawn tasks on
/// the same runtime.
/// parts of your project. For example, some components might require a [`Runtime`] to spawn
/// tasks on the same runtime.
///
/// # Example
/// Read more in the documentation for [`Runtime`].
///
/// # Examples
///
/// ```
/// let system_runner = actix_rt::System::new();
@@ -221,19 +225,14 @@ impl SystemRunner {
/// // Use the runtime to spawn an async task or perform other operations
/// ```
///
/// Read more in the documentation for [`actix_rt::Runtime`]
///
/// # Returns
///
/// An immutable reference to the [`actix_rt::Runtime`] instance associated with this
/// [`actix_rt::SystemRunner`] instance.
///
/// # Note
///
/// While this method provides an immutable reference to the Actix runtime, which is safe to share across threads,
/// be aware that spawning blocking tasks on the Actix runtime could potentially impact system performance.
/// This is because the Actix runtime is responsible for driving the system,
/// and blocking tasks could delay other tasks in the run loop.
/// While this method provides an immutable reference to the Actix runtime, which is safe to
/// share across threads, be aware that spawning blocking tasks on the Actix runtime could
/// potentially impact system performance. This is because the Actix runtime is responsible for
/// driving the system, and blocking tasks could delay other tasks in the run loop.
///
/// [`Runtime`]: crate::Runtime
pub fn runtime(&self) -> &crate::runtime::Runtime {
&self.rt
}

View File

@@ -2,14 +2,20 @@
> General purpose TCP server built for the Actix ecosystem.
<!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/actix-server?label=latest)](https://crates.io/crates/actix-server)
[![Documentation](https://docs.rs/actix-server/badge.svg?version=2.3.0)](https://docs.rs/actix-server/2.3.0)
[![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
![License](https://img.shields.io/crates/l/actix-server.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-server.svg)
<br />
[![Dependency Status](https://deps.rs/crate/actix-server/2.3.0/status.svg)](https://deps.rs/crate/actix-server/2.3.0)
![Download](https://img.shields.io/crates/d/actix-server.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
<!-- prettier-ignore-end -->
## Resources
- [Library Documentation](https://docs.rs/actix-server)
- [Examples](/actix-server/examples)

View File

@@ -2,7 +2,6 @@ use std::{io, num::NonZeroUsize, time::Duration};
use actix_rt::net::TcpStream;
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
use tracing::{info, trace};
use crate::{
server::ServerCommand,
@@ -14,7 +13,9 @@ use crate::{
/// Multipath TCP (MPTCP) preference.
///
/// Also see [`ServerBuilder::mptcp()`].
/// Currently only useful on Linux.
///
#[cfg_attr(target_os = "linux", doc = "Also see [`ServerBuilder::mptcp()`].")]
#[derive(Debug, Clone)]
pub enum MpTcp {
/// MPTCP will not be used when binding sockets.
@@ -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.
///
/// The default worker count is the number of physical CPU cores available. If your benchmark
/// testing indicates that simultaneous multi-threading is beneficial to your app, you can use
/// the [`num_cpus`] crate to acquire the _logical_ core count instead.
///
/// # Panics
///
/// Panics if `num` is 0.
///
/// [`num_cpus`]: https://docs.rs/num_cpus
pub fn workers(mut self, num: usize) -> Self {
assert_ne!(num, 0, "workers must be greater than 0");
self.threads = num;
@@ -155,13 +156,15 @@ impl ServerBuilder {
self.max_concurrent_connections(num)
}
/// Stop Actix `System` after server shutdown.
/// Sets flag to stop Actix `System` after server shutdown.
///
/// This has no effect when server is running in a Tokio-only runtime.
pub fn system_exit(mut self) -> Self {
self.exit = true;
self
}
/// Disable OS signal handling.
/// Disables OS signal handling.
pub fn disable_signals(mut self) -> Self {
self.listen_os_signals = false;
self
@@ -179,25 +182,49 @@ impl ServerBuilder {
self
}
/// Add new service to the server.
pub fn bind<F, U, N>(mut self, name: N, addr: U, factory: F) -> io::Result<Self>
/// Adds new service to the server.
///
/// Note that, if a DNS lookup is required, resolving hostnames is a blocking operation.
///
/// # Worker Count
///
/// The `factory` will be instantiated multiple times in most scenarios. The number of
/// instantiations is number of [`workers`](Self::workers()) × number of sockets resolved by
/// `addrs`.
///
/// For example, if you've manually set [`workers`](Self::workers()) to 2, and use `127.0.0.1`
/// as the bind `addrs`, then `factory` will be instantiated twice. However, using `localhost`
/// as the bind `addrs` can often resolve to both `127.0.0.1` (IPv4) _and_ `::1` (IPv6), causing
/// the `factory` to be instantiated 4 times (2 workers × 2 bind addresses).
///
/// Using a bind address of `0.0.0.0`, which signals to use all interfaces, may also multiple
/// the number of instantiations in a similar way.
///
/// # Errors
///
/// Returns an `io::Error` if:
/// - `addrs` cannot be resolved into one or more socket addresses;
/// - all the resolved socket addresses are already bound.
pub fn bind<F, U, N>(mut self, name: N, addrs: U, factory: F) -> io::Result<Self>
where
F: ServerServiceFactory<TcpStream>,
U: ToSocketAddrs,
N: AsRef<str>,
{
let sockets = bind_addr(addr, self.backlog, &self.mptcp)?;
let sockets = bind_addr(addrs, self.backlog, &self.mptcp)?;
trace!("binding server to: {:?}", &sockets);
tracing::trace!("binding server to: {sockets:?}");
for lst in sockets {
let token = self.next_token();
self.factories.push(StreamNewService::create(
name.as_ref().to_string(),
token,
factory.clone(),
lst.local_addr()?,
));
self.sockets
.push((token, name.as_ref().to_string(), MioListener::Tcp(lst)));
}
@@ -205,7 +232,12 @@ impl ServerBuilder {
Ok(self)
}
/// Add new service to the server.
/// Adds service to the server using a socket listener already bound.
///
/// # Worker Count
///
/// The `factory` will be instantiated multiple times in most scenarios. The number of
/// instantiations is: number of [`workers`](Self::workers()).
pub fn listen<F, N: AsRef<str>>(
mut self,
name: N,
@@ -237,7 +269,7 @@ impl ServerBuilder {
if self.sockets.is_empty() {
panic!("Server should have at least one bound socket");
} else {
info!("starting {} workers", self.threads);
tracing::info!("starting {} workers", self.threads);
Server::new(self)
}
}
@@ -251,7 +283,12 @@ impl ServerBuilder {
#[cfg(unix)]
impl ServerBuilder {
/// Add new unix domain service to the server.
/// Adds new service to the server using a UDS (unix domain socket) address.
///
/// # Worker Count
///
/// The `factory` will be instantiated multiple times in most scenarios. The number of
/// instantiations is: number of [`workers`](Self::workers()).
pub fn bind_uds<F, U, N>(self, name: N, addr: U, factory: F) -> io::Result<Self>
where
F: ServerServiceFactory<actix_rt::net::UnixStream>,
@@ -271,9 +308,14 @@ impl ServerBuilder {
self.listen_uds(name, lst, factory)
}
/// Add new unix domain service to the server.
/// Adds new service to the server using a UDS (unix domain socket) listener already bound.
///
/// Useful when running as a systemd service and a socket FD is acquired externally.
///
/// # Worker Count
///
/// The `factory` will be instantiated multiple times in most scenarios. The number of
/// instantiations is: number of [`workers`](Self::workers()).
pub fn listen_uds<F, N: AsRef<str>>(
mut self,
name: N,
@@ -284,17 +326,22 @@ impl ServerBuilder {
F: ServerServiceFactory<actix_rt::net::UnixStream>,
{
use std::net::{IpAddr, Ipv4Addr};
lst.set_nonblocking(true)?;
let token = self.next_token();
let addr = crate::socket::StdSocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
self.factories.push(StreamNewService::create(
name.as_ref().to_string(),
token,
factory,
addr,
));
self.sockets
.push((token, name.as_ref().to_string(), MioListener::from(lst)));
Ok(self)
}
}

View File

@@ -5,7 +5,7 @@
[![crates.io](https://img.shields.io/crates/v/actix-service?label=latest)](https://crates.io/crates/actix-service)
[![Documentation](https://docs.rs/actix-service/badge.svg?version=2.0.2)](https://docs.rs/actix-service/2.0.2)
[![Version](https://img.shields.io/badge/rustc-1.46+-ab6000.svg)](https://blog.rust-lang.org/2020/03/12/Rust-1.46.html)
![License](https://img.shields.io/crates/l/actix-service.svg)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/actix-service.svg)
[![Dependency Status](https://deps.rs/crate/actix-service/2.0.2/status.svg)](https://deps.rs/crate/actix-service/2.0.2)
![Download](https://img.shields.io/crates/d/actix-service.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)

View File

@@ -2,6 +2,10 @@
## Unreleased
## 1.3.1
- No significant changes since `1.3.0`.
## 1.3.0
- Implement `AsRef<ByteString>` for `ByteString`.

View File

@@ -1,11 +1,11 @@
[package]
name = "bytestring"
version = "1.3.0"
version = "1.3.1"
description = "A UTF-8 encoded read-only string using `Bytes` as storage"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
]
description = "An immutable UTF-8 encoded string using Bytes as storage"
keywords = ["string", "bytes", "utf8", "web", "actix"]
categories = ["no-std", "web-programming"]
homepage = "https://actix.rs"

16
bytestring/README.md Normal file
View File

@@ -0,0 +1,16 @@
# `bytestring`
> A UTF-8 encoded read-only string using `Bytes` as storage.
<!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/bytestring?label=latest)](https://crates.io/crates/bytestring)
[![Documentation](https://docs.rs/bytestring/badge.svg?version=1.3.1)](https://docs.rs/bytestring/1.3.1)
[![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/bytestring.svg)
<br />
[![Dependency Status](https://deps.rs/crate/bytestring/1.3.1/status.svg)](https://deps.rs/crate/bytestring/1.3.1)
![Download](https://img.shields.io/crates/d/bytestring.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
<!-- prettier-ignore-end -->

View File

@@ -1,4 +1,6 @@
//! 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]
#![deny(rust_2018_idioms, nonstandard_style)]

11
justfile Normal file
View File

@@ -0,0 +1,11 @@
_list:
@just --list
# Document crates in workspace.
doc:
RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls,openssl
# Document crates in workspace and watch for changes.
doc-watch:
RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls,openssl --open
cargo watch -- RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc --no-deps --workspace --features=rustls,openssl

View File

@@ -2,6 +2,10 @@
## Unreleased
## 0.1.5
- No significant changes since `0.1.4`.
## 0.1.4
- Minimum supported Rust version (MSRV) is now 1.65.

View File

@@ -1,6 +1,6 @@
[package]
name = "local-channel"
version = "0.1.4"
version = "0.1.5"
description = "A non-threadsafe multi-producer, single-consumer, futures-aware, FIFO queue"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",

16
local-channel/README.md Normal file
View File

@@ -0,0 +1,16 @@
# `local-channel`
> A non-threadsafe multi-producer, single-consumer, futures-aware, FIFO queue.
<!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/local-channel?label=latest)](https://crates.io/crates/local-channel)
[![Documentation](https://docs.rs/local-channel/badge.svg?version=0.1.5)](https://docs.rs/local-channel/0.1.5)
[![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/local-channel.svg)
<br />
[![Dependency Status](https://deps.rs/crate/local-channel/0.1.5/status.svg)](https://deps.rs/crate/local-channel/0.1.5)
![Download](https://img.shields.io/crates/d/local-channel.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
<!-- prettier-ignore-end -->

View File

@@ -1,4 +1,6 @@
//! Non-thread-safe channels.
//!
//! See docs for [`mpsc::channel()`].
#![deny(rust_2018_idioms, nonstandard_style)]
#![warn(future_incompatible, missing_docs)]

View File

@@ -2,6 +2,8 @@
## Unreleased
## 0.1.4
- Minimum supported Rust version (MSRV) is now 1.65.
## 0.1.3

View File

@@ -1,15 +1,15 @@
[package]
name = "local-waker"
version = "0.1.3"
version = "0.1.4"
description = "A synchronization primitive for thread-local task wakeup"
authors = [
"Nikolay Kim <fafhrd91@gmail.com>",
"Rob Ede <robjtede@icloud.com>",
]
repository = "https://github.com/actix/actix-net.git"
repository = "https://github.com/actix/actix-net"
keywords = ["waker", "local", "futures", "no-std"]
categories = ["asynchronous", "no-std"]
license = "MIT OR Apache-2.0"
license.workspace = true
edition.workspace = true
rust-version.workspace = true

16
local-waker/README.md Normal file
View File

@@ -0,0 +1,16 @@
# `local-waker`
> A synchronization primitive for thread-local task wakeup.
<!-- prettier-ignore-start -->
[![crates.io](https://img.shields.io/crates/v/local-waker?label=latest)](https://crates.io/crates/local-waker)
[![Documentation](https://docs.rs/local-waker/badge.svg?version=0.1.4)](https://docs.rs/local-waker/0.1.4)
[![Version](https://img.shields.io/badge/rustc-1.52+-ab6000.svg)](https://blog.rust-lang.org/2021/05/06/Rust-1.52.0.html)
![MIT or Apache 2.0 licensed](https://img.shields.io/crates/l/local-waker.svg)
<br />
[![Dependency Status](https://deps.rs/crate/local-waker/0.1.4/status.svg)](https://deps.rs/crate/local-waker/0.1.4)
![Download](https://img.shields.io/crates/d/local-waker.svg)
[![Chat on Discord](https://img.shields.io/discord/771444961383153695?label=chat&logo=discord)](https://discord.gg/NWpN5mmg3x)
<!-- prettier-ignore-end -->