mod engine;
#[cfg(feature = "reactor")]
mod reactor;
mod source;
use std::any::TypeId;
use std::fmt;
use std::net::{SocketAddr, ToSocketAddrs};
use std::path::PathBuf;
use std::sync::Arc;
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
use crate::control::ControlLine;
use crate::driver::DriverKind;
use crate::error::{CloseReason, ConfigReason, KillReason, NetError, RefusalCause};
use crate::events::{EventSink, NoopSink};
use crate::gate::Gate;
use crate::handoff::ListenerHandle;
use crate::id::{ConnectionId, ListenerId, WorkerId};
use crate::limits::Limits;
use crate::opts::{ListenOptions, LocalOptions};
use crate::proxy::ProxyMode;
use crate::session::SessionFactory;
use crate::tcp::{ListenerInfo, TcpConnection};
use crate::time::Clock;
use crate::tls::ErasedAcceptor;
use crate::tls::{NoTls, TlsAcceptor};
use crate::trusted::TrustedProxies;
pub(crate) use engine::Shared;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DoorState {
Open,
Closed,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DrainReport {
pub graceful: u32,
pub killed: u32,
pub took: Duration,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Stats {
pub open: u64,
pub accepted: u64,
pub refused: u64,
pub closed: u64,
pub bytes_in: u64,
pub bytes_out: u64,
pub egress_denied: u64,
pub killed: u64,
pub slow_peers: u64,
pub timeouts: u64,
pub descriptor_pressure: u64,
pub stale_completions: u64,
pub session_panics: u64,
refused_by: [u64; RefusalCause::COUNT],
closed_by: [u64; CloseReason::COUNT],
}
impl Stats {
#[must_use]
pub fn refused_for(&self, cause: RefusalCause) -> u64 {
self.refused_by[cause.index()]
}
#[must_use]
pub fn closed_for(&self, reason: CloseReason) -> u64 {
self.closed_by[reason.index()]
}
pub fn closes(&self) -> impl Iterator<Item = (CloseReason, u64)> + '_ {
CloseReason::BY_INDEX
.iter()
.zip(self.closed_by.iter())
.filter(|(_, n)| **n > 0)
.map(|(r, n)| (*r, *n))
}
pub fn refusals(&self) -> impl Iterator<Item = (RefusalCause, u64)> + '_ {
RefusalCause::BY_INDEX
.iter()
.zip(self.refused_by.iter())
.filter(|(_, n)| **n > 0)
.map(|(r, n)| (*r, *n))
}
}
#[must_use]
#[derive(Debug)]
pub struct ListenerSpec {
pub(crate) addrs: Result<Vec<SocketAddr>, String>,
pub(crate) options: Option<ListenOptions>,
pub(crate) name: Option<String>,
pub(crate) limits: Limits,
pub(crate) proxy: ProxyMode,
pub(crate) trusted: Option<Arc<TrustedProxies>>,
pub(crate) tls: bool,
pub(crate) unix: Option<(PathBuf, LocalOptions)>,
#[cfg(all(windows, feature = "local"))]
pub(crate) pipe: Option<(String, crate::pipe::PipeOptions)>,
pub(crate) adopt: Option<ListenerHandle>,
}
impl ListenerSpec {
pub fn tcp<A: ToSocketAddrs>(addr: A) -> ListenerSpec {
ListenerSpec {
addrs: addr
.to_socket_addrs()
.map(|i| i.collect())
.map_err(|e| e.to_string()),
options: None,
name: None,
limits: Limits::default(),
proxy: ProxyMode::Off,
trusted: None,
tls: false,
unix: None,
#[cfg(all(windows, feature = "local"))]
pipe: None,
adopt: None,
}
}
pub fn tcp_with<A: ToSocketAddrs>(addr: A, options: ListenOptions) -> ListenerSpec {
ListenerSpec {
options: Some(options),
..ListenerSpec::tcp(addr)
}
}
#[cfg(all(windows, feature = "local"))]
pub fn pipe(name: &str, options: crate::pipe::PipeOptions) -> ListenerSpec {
ListenerSpec {
pipe: Some((name.to_owned(), options)),
..ListenerSpec::tcp(std::net::SocketAddr::from(([127, 0, 0, 1], 0)))
}
}
pub fn unix(path: impl Into<PathBuf>, options: LocalOptions) -> ListenerSpec {
ListenerSpec {
addrs: Ok(Vec::new()),
options: None,
name: None,
limits: Limits::default(),
proxy: ProxyMode::Off,
trusted: None,
tls: false,
unix: Some((path.into(), options)),
#[cfg(all(windows, feature = "local"))]
pipe: None,
adopt: None,
}
}
pub fn adopt(handle: ListenerHandle) -> ListenerSpec {
ListenerSpec {
addrs: Ok(Vec::new()),
options: None,
name: None,
limits: Limits::default(),
proxy: ProxyMode::Off,
trusted: None,
tls: false,
unix: None,
#[cfg(all(windows, feature = "local"))]
pipe: None,
adopt: Some(handle),
}
}
pub fn name(mut self, name: &str) -> ListenerSpec {
self.name = Some(name.to_owned());
self
}
pub fn limits(mut self, limits: Limits) -> ListenerSpec {
self.limits = limits;
self
}
pub fn proxy(mut self, mode: ProxyMode) -> ListenerSpec {
self.proxy = mode;
self
}
pub fn tls(mut self, on: bool) -> ListenerSpec {
self.tls = on;
self
}
pub fn trusted_proxies(mut self, proxies: TrustedProxies) -> ListenerSpec {
self.trusted = Some(Arc::new(proxies));
self
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct NoWork;
#[derive(Clone, Copy, Debug, Default)]
pub struct NoGate;
#[derive(Debug)]
pub struct Sessions<F> {
pub(crate) factory: F,
}
#[cfg(feature = "reactor")]
impl<F> Sessions<F> {
pub(crate) fn factory(&self) -> &F {
&self.factory
}
}
pub struct BlockingHandler<H> {
pub(crate) handler: H,
}
impl<H> fmt::Debug for BlockingHandler<H> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("BlockingHandler")
}
}
pub(crate) mod sealed {
pub trait Sealed {}
}
pub trait Workload: sealed::Sealed + engine::Serve + Send + Sync + 'static {}
impl<F: SessionFactory> sealed::Sealed for Sessions<F> {}
impl<F: SessionFactory> Workload for Sessions<F> {}
impl<H> sealed::Sealed for BlockingHandler<H> where
H: Fn(&mut TcpConnection) -> Result<(), NetError> + Send + Sync + 'static
{
}
impl<H> Workload for BlockingHandler<H> where
H: Fn(&mut TcpConnection) -> Result<(), NetError> + Send + Sync + 'static
{
}
#[must_use]
pub struct ServerBuilder<W = NoWork, G = NoGate, S = NoopSink, T = NoTls> {
listeners: Vec<ListenerSpec>,
driver: DriverKind,
workers: Option<u16>,
limits: Limits,
work: W,
gate: G,
sink: S,
tls: T,
control: Option<Arc<dyn ControlLine>>,
wipe: bool,
clock: Option<Arc<dyn Clock>>,
trusted: Option<Arc<TrustedProxies>>,
}
impl ServerBuilder {
pub fn new() -> ServerBuilder {
ServerBuilder {
listeners: Vec::new(),
driver: DriverKind::Auto,
workers: None,
limits: Limits::default(),
work: NoWork,
gate: NoGate,
sink: NoopSink,
tls: NoTls,
control: None,
wipe: true,
clock: None,
trusted: None,
}
}
}
impl Default for ServerBuilder {
fn default() -> ServerBuilder {
ServerBuilder::new()
}
}
impl<W, G, S, T> ServerBuilder<W, G, S, T> {
fn map<W2, G2, S2, T2>(
self,
f: impl FnOnce(W, G, S, T) -> (W2, G2, S2, T2),
) -> ServerBuilder<W2, G2, S2, T2> {
let ServerBuilder {
listeners,
driver,
workers,
limits,
work,
gate,
sink,
tls,
control,
wipe,
clock,
trusted,
} = self;
let (work, gate, sink, tls) = f(work, gate, sink, tls);
ServerBuilder {
listeners,
driver,
workers,
limits,
work,
gate,
sink,
tls,
control,
wipe,
clock,
trusted,
}
}
pub fn trusted_proxies(mut self, proxies: TrustedProxies) -> Self {
self.trusted = Some(Arc::new(proxies));
self
}
pub fn listener(mut self, spec: ListenerSpec) -> Self {
self.listeners.push(spec);
self
}
pub fn driver(mut self, kind: DriverKind) -> Self {
self.driver = kind;
self
}
pub fn workers(mut self, n: u16) -> Self {
self.workers = Some(n);
self
}
pub fn limits(mut self, limits: Limits) -> Self {
self.limits = limits;
self
}
pub fn sessions<F: SessionFactory>(self, factory: F) -> ServerBuilder<Sessions<F>, G, S, T> {
self.map(|_, g, s, t| (Sessions { factory }, g, s, t))
}
pub fn handler<H>(self, handler: H) -> ServerBuilder<BlockingHandler<H>, G, S, T>
where
H: Fn(&mut TcpConnection) -> Result<(), NetError> + Send + Sync + 'static,
{
self.map(|_, g, s, t| (BlockingHandler { handler }, g, s, t))
}
pub fn tls<T2: TlsAcceptor>(self, acceptor: T2) -> ServerBuilder<W, G, S, T2> {
self.map(|w, g, s, _| (w, g, s, acceptor))
}
pub fn gate<G2: Gate>(self, gate: G2) -> ServerBuilder<W, G2, S, T> {
self.map(|w, _, s, t| (w, gate, s, t))
}
pub fn events<S2: EventSink>(self, sink: S2) -> ServerBuilder<W, G, S2, T> {
self.map(|w, g, _, t| (w, g, sink, t))
}
pub fn control<C: ControlLine>(mut self, line: Arc<C>) -> Self {
self.control = Some(line);
self
}
pub fn wipe_buffers(mut self, on: bool) -> Self {
self.wipe = on;
self
}
pub fn clock<K: Clock>(mut self, clock: Arc<K>) -> Self {
self.clock = Some(clock);
self
}
}
impl<W, G, S, T> ServerBuilder<W, G, S, T>
where
W: Workload,
G: Gate,
S: EventSink,
T: TlsAcceptor,
{
pub fn build(self) -> Result<Server, NetError> {
let requested = match self.driver {
DriverKind::Auto if <W as engine::Serve>::BLOCKING_ONLY => DriverKind::Blocking,
other => other,
};
let driver = match requested.resolve() {
DriverKind::Reactor if <W as engine::Serve>::BLOCKING_ONLY => {
return Err(invalid("driver", ConfigReason::Inconsistent));
}
DriverKind::Reactor if !DriverKind::reactor_built() => {
return Err(NetError::Unsupported {
feature: "the reactor driver",
platform: std::env::consts::OS,
});
}
DriverKind::Uring => {
return Err(NetError::Unsupported {
feature: "the io_uring driver (after 1.0.0)",
platform: std::env::consts::OS,
});
}
kind => kind,
};
if self.listeners.is_empty() {
return Err(invalid("listener", ConfigReason::Missing));
}
if self.listeners.len() > 64 {
return Err(invalid("listener", ConfigReason::TooLarge { max: 64 }));
}
let workers = self.workers.unwrap_or_else(|| {
if driver == DriverKind::Reactor {
std::thread::available_parallelism()
.map_or(4, |n| u16::try_from(n.get()).unwrap_or(4096).min(4096))
} else {
128
}
});
if workers == 0 {
return Err(invalid("workers", ConfigReason::Zero));
}
if workers > 4096 {
return Err(invalid("workers", ConfigReason::TooLarge { max: 4096 }));
}
let mut listeners = self.listeners;
let has_tls = TypeId::of::<T>() != TypeId::of::<NoTls>();
let mut names = Vec::new();
for spec in &mut listeners {
check_spec(spec, self.trusted.as_ref(), has_tls, &names)?;
if let Some(n) = &spec.name {
names.push(n.clone());
}
}
let limits = if self.limits.max_connections().is_none() {
let cap = if driver == DriverKind::Reactor {
reactor_default_cap(workers)
} else {
u32::from(workers)
};
self.limits.to_builder().max_connections(cap).build()?
} else {
self.limits.clone()
};
limits.validate(workers, crate::sys::descriptor_limit())?;
for spec in &listeners {
limits.overlay(&spec.limits).validate(workers, None)?;
}
let tls: Option<Arc<dyn ErasedAcceptor>> = if has_tls {
Some(Arc::new(self.tls))
} else {
None
};
engine::start(engine::Config {
tls,
trusted: self.trusted,
listeners,
driver,
workers,
limits,
work: self.work,
gate: self.gate,
sink: self.sink,
control: self.control,
wipe: self.wipe,
clock: self.clock,
})
}
}
fn reactor_default_cap(workers: u16) -> u32 {
let wanted = 10_000u32;
match crate::sys::descriptor_limit() {
Some(have) => {
let spare = u64::from(workers).saturating_mul(2) + 64;
let room = have.saturating_sub(spare).clamp(16, u64::from(wanted));
u32::try_from(room).unwrap_or(wanted)
}
None => wanted,
}
}
fn invalid(field: &'static str, reason: ConfigReason) -> NetError {
NetError::InvalidConfig { field, reason }
}
#[must_use]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AdoptSpec {
listener: String,
lane: Option<crate::info::Lane>,
}
impl AdoptSpec {
pub fn new(listener: &str) -> AdoptSpec {
AdoptSpec {
listener: listener.to_owned(),
lane: None,
}
}
pub fn lane(mut self, lane: crate::info::Lane) -> AdoptSpec {
self.lane = Some(lane);
self
}
}
fn check_spec(
spec: &mut ListenerSpec,
server_trusted: Option<&Arc<TrustedProxies>>,
has_tls: bool,
taken: &[String],
) -> Result<(), NetError> {
if spec.name.is_none() {
spec.name = spec
.adopt
.as_ref()
.and_then(|h| h.name())
.map(str::to_owned);
}
let unix = spec.unix.is_some()
|| spec
.adopt
.as_ref()
.is_some_and(|h| h.kind() == crate::info::Transport::Unix);
if unix && !cfg!(feature = "local") {
return Err(NetError::Unsupported {
feature: "Unix domain sockets (the `local` feature)",
platform: std::env::consts::OS,
});
}
if spec.tls {
if !cfg!(feature = "tls") {
return Err(NetError::Unsupported {
feature: "TLS (the `tls` feature)",
platform: std::env::consts::OS,
});
}
if !has_tls {
return Err(invalid("tls", ConfigReason::Missing));
}
}
if spec.proxy != ProxyMode::Off {
if !cfg!(feature = "proxy") {
return Err(NetError::Unsupported {
feature: "PROXY protocol (the `proxy` feature)",
platform: std::env::consts::OS,
});
}
if !unix {
let trusted = spec.trusted.clone().or_else(|| server_trusted.cloned());
match trusted {
Some(t) if !t.is_empty() => spec.trusted = Some(t),
_ => return Err(invalid("trusted_proxies", ConfigReason::Missing)),
}
}
}
if let Some(n) = &spec.name {
if n.len() > 64 {
return Err(invalid("name", ConfigReason::TooLarge { max: 64 }));
}
if taken.contains(n) {
return Err(invalid("name", ConfigReason::Inconsistent));
}
}
Ok(())
}
#[must_use = "dropping a Server stops it; call wait_for_stop() to run until a stop is requested"]
pub struct Server {
pub(crate) shared: Arc<Shared>,
pub(crate) threads: Vec<JoinHandle<()>>,
pub(crate) listeners: Vec<ListenerInfo>,
pub(crate) stopped: bool,
}
impl Server {
pub fn open_door(&self) -> Result<(), NetError> {
self.shared.open_door()
}
pub fn close_door(&self) {
self.shared.close_door();
}
#[must_use]
pub fn door(&self) -> DoorState {
self.shared.door()
}
pub fn drain(&self, deadline: Instant) -> DrainReport {
self.shared.drain(deadline)
}
pub fn shutdown(mut self, deadline: Instant) -> DrainReport {
self.stop(deadline)
}
pub fn wait_for_stop(mut self) -> DrainReport {
let deadline = self.shared.wait_for_stop_request();
self.stop(deadline)
}
fn stop(&mut self, deadline: Instant) -> DrainReport {
if self.stopped {
return DrainReport {
graceful: 0,
killed: 0,
took: Duration::ZERO,
};
}
self.stopped = true;
let report = self.shared.drain(deadline);
self.shared.stop_threads();
for t in self.threads.drain(..) {
let _ = t.join();
}
self.shared.join_listeners();
report
}
pub fn adopt(&self, conn: TcpConnection, spec: AdoptSpec) -> Result<ConnectionId, NetError> {
let mut conn = conn;
let link = conn.take_link().ok_or(NetError::Closed)?;
let (Some(peer), Some(local)) =
(link.info.peer.socket_addr(), link.info.local.socket_addr())
else {
return Err(invalid("connection", ConfigReason::Inconsistent));
};
let stream = link
.into_stream(CloseReason::SessionClosed)
.and_then(crate::sys::Stream::into_tcp)
.ok_or(invalid("connection", ConfigReason::Inconsistent))?;
self.shared
.adopt(stream, peer, local, &spec.listener, spec.lane)
}
pub fn release_listeners(&self) -> Result<Vec<ListenerHandle>, NetError> {
self.shared.release_listeners()
}
pub fn add_listener(&mut self, spec: ListenerSpec) -> Result<ListenerId, NetError> {
let mut spec = spec;
if self.listeners.len() >= 64 {
return Err(invalid("listener", ConfigReason::TooLarge { max: 64 }));
}
let names: Vec<String> = self
.listeners
.iter()
.filter_map(|l| l.name.clone())
.collect();
check_spec(
&mut spec,
self.shared.trusted.as_ref(),
self.shared.tls.is_some(),
&names,
)?;
let workers = u16::try_from(self.shared.worker_count()).unwrap_or(u16::MAX);
self.shared
.base_limits
.overlay(&spec.limits)
.validate(workers, None)?;
let info = self.shared.add_listener(spec)?;
let id = info.id;
self.listeners.push(info);
Ok(id)
}
pub fn remove_listener(
&mut self,
id: ListenerId,
deadline: Instant,
) -> Result<DrainReport, NetError> {
let report = self.shared.remove_listener(id, deadline)?;
self.listeners.retain(|l| l.id != id);
Ok(report)
}
#[must_use]
pub fn handle(&self) -> ServerHandle {
ServerHandle {
shared: self.shared.clone(),
}
}
#[must_use]
pub fn stats(&self) -> Stats {
self.shared.stats()
}
#[must_use]
pub fn listeners(&self) -> &[ListenerInfo] {
&self.listeners
}
}
impl Drop for Server {
fn drop(&mut self) {
let deadline = Instant::now() + self.shared.drain_timeout;
self.stop(deadline);
}
}
impl fmt::Debug for Server {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Server")
.field("listeners", &self.listeners)
.field("door", &self.door())
.finish()
}
}
#[derive(Clone)]
pub struct ServerHandle {
pub(crate) shared: Arc<Shared>,
}
impl ServerHandle {
pub fn kill(&self, id: ConnectionId, reason: KillReason) -> bool {
self.shared.kill(id, reason)
}
pub fn close_door(&self) {
self.shared.close_door();
}
pub fn request_stop(&self) {
self.shared.request_stop(None);
}
pub fn wake_all(&self) {
self.shared.wake_all();
}
#[must_use]
pub fn completer(&self, worker: WorkerId) -> Option<crate::completer::Completer> {
self.shared.completer(worker)
}
#[must_use]
pub fn stats(&self) -> Stats {
self.shared.stats()
}
#[must_use]
pub fn door(&self) -> DoorState {
self.shared.door()
}
}
impl fmt::Debug for ServerHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ServerHandle")
.field("door", &self.door())
.finish()
}
}
impl Stats {
pub(crate) fn from_parts(
mut base: Stats,
refused_by: [u64; RefusalCause::COUNT],
closed_by: [u64; CloseReason::COUNT],
) -> Stats {
base.refused_by = refused_by;
base.closed_by = closed_by;
base
}
}