net_io/lib.rs
1//! # net_io
2//!
3//! The network IO substrate for the `-net` and `-io` crates: the entrance and
4//! exit every service shares. It owns listeners and sockets, the life of each
5//! connection, the limits and timeouts that keep a peer from holding what it
6//! should not, graceful drain, and the seams a connection gate and a TLS engine
7//! plug into. It is protocol-free and has no runtime dependencies.
8//!
9//! ## Quick start
10//!
11//! ```no_run
12//! fn main() -> Result<(), net_io::NetError> {
13//! // An echo server: one handler call per connection on a bounded worker pool,
14//! // with every default limit (idle, read, write, minimum rate, caps) applied.
15//! let server = net_io::serve("127.0.0.1:7000", |conn| {
16//! let mut buf = [0u8; 4096];
17//! loop {
18//! let n = conn.recv(&mut buf)?;
19//! if n == 0 {
20//! return Ok(()); // the peer closed its side
21//! }
22//! conn.send_all(&buf[..n])?;
23//! }
24//! })?;
25//!
26//! // A client.
27//! let mut conn = net_io::connect("127.0.0.1:7000")?;
28//! conn.send_all(b"ping")?;
29//! let mut reply = [0u8; 4];
30//! conn.recv_exact(&mut reply)?;
31//! assert_eq!(&reply, b"ping");
32//!
33//! // Stop: close the door, drain open connections, join the workers.
34//! server.handle().request_stop();
35//! let report = server.wait_for_stop();
36//! println!("{} closed gracefully, {} killed", report.graceful, report.killed);
37//! Ok(())
38//! }
39//! ```
40//!
41//! Everything else (several listeners, a gate, events, the control line) goes
42//! through [`ServerBuilder`]. The full reference with every item, its errors,
43//! and its cost is `docs/API.md` in the
44//! [repository](https://github.com/jamesgober/net-io).
45//!
46//! ## Status
47//!
48//! `0.6.0`: the reactor on epoll, kqueue, and IOCP, and the blocking pool, on
49//! Linux, macOS, FreeBSD, and Windows. TCP, Unix domain sockets (AF_UNIX on
50//! Windows), Windows named pipes, and UDP with batches and sessions; zero-copy file
51//! sends; the PROXY protocol, the TLS seam, systemd and FastCGI inheritance, listener
52//! hand-off on Unix and Windows, and the public [`Driver`] interface.
53
54#![doc(html_root_url = "https://docs.rs/net-io")]
55#![cfg_attr(docsrs, feature(doc_cfg))]
56#![deny(unsafe_code)]
57#![deny(unsafe_op_in_unsafe_fn)]
58#![deny(missing_docs)]
59#![cfg_attr(
60 not(test),
61 deny(
62 clippy::unwrap_used,
63 clippy::expect_used,
64 clippy::panic,
65 clippy::todo,
66 clippy::unimplemented,
67 clippy::dbg_macro,
68 clippy::print_stdout,
69 clippy::print_stderr
70 )
71)]
72
73mod accept;
74mod addr;
75mod buf;
76mod completer;
77mod control;
78mod crc32c;
79mod driver;
80mod error;
81mod events;
82mod gate;
83mod handoff;
84mod id;
85mod info;
86mod inherit;
87mod limits;
88mod link;
89#[cfg(feature = "local")]
90mod local;
91#[cfg(all(windows, feature = "local"))]
92mod local_windows;
93mod opts;
94#[cfg(all(windows, feature = "local"))]
95mod pipe;
96mod proxy;
97#[cfg(feature = "proxy")]
98mod proxy_parse;
99mod rate;
100mod server;
101mod session;
102mod sys;
103mod tcp;
104#[cfg(test)]
105mod testing;
106mod time;
107mod tls;
108mod trusted;
109#[cfg(feature = "udp")]
110mod udp;
111#[cfg_attr(not(test), allow(dead_code))]
112mod wheel;
113
114/// Entry points for net-io's fuzz targets (`fuzz/`), compiled only under
115/// cargo-fuzz (`--cfg fuzzing`). Not part of the public API and not covered by semver.
116#[cfg(fuzzing)]
117#[doc(hidden)]
118pub mod __fuzz {
119 /// The hand-off manifest decoder.
120 pub fn manifest(data: &[u8]) {
121 crate::handoff::fuzz_manifest(data);
122 }
123
124 /// The systemd environment reader.
125 pub fn systemd_env(data: &[u8]) {
126 crate::inherit::fuzz_systemd_env(data);
127 }
128}
129
130/// Fault injection for net-io's own tests, compiled only with `--cfg net_io_shim`.
131/// Not part of the public API and not covered by semver.
132#[cfg(net_io_shim)]
133#[doc(hidden)]
134pub mod __shim {
135 pub use crate::sys::shim::{Fault, Op, clear, counted, program, start_counting, stop_counting};
136}
137
138use std::net::ToSocketAddrs;
139
140pub use addr::{AddrError, Cidr, PrefixKey, PrefixPolicy, normalize, normalize_socket};
141pub use buf::{Buffer, BufferPool, PoolStats};
142pub use completer::{Completer, PostError};
143pub use control::{ControlLine, ControlState, NoControl, SimpleControl};
144#[cfg(feature = "reactor")]
145pub use driver::new_reactor_driver;
146pub use driver::{
147 BorrowedSocket, Completion, Completions, Driver, DriverKind, DriverWaker, Operation,
148 OperationKind, Rejected, SendChunks, Token,
149};
150pub use error::{
151 CloseReason, ConfigReason, Direction, ErrorClass, KillReason, LimitKind, NetError,
152 OsErrorClass, RefusalCause, TimeoutKind,
153};
154pub use events::{CoalescedKind, Event, EventSink, NoopSink};
155pub use gate::{AdmitStage, Admitted, AllowAll, Denial, Gate, KillHandle, Refusal, WriteCheck};
156pub use handoff::{HandoffError, HandoffPeer, ListenerHandle};
157#[cfg(all(windows, feature = "local"))]
158pub use handoff::{SharedListeners, receive_shared, share_listeners};
159#[cfg(all(unix, feature = "local"))]
160pub use handoff::{recv_listeners, send_listeners};
161pub use id::{ConnectionId, ListenerId, WorkerId};
162pub use info::{
163 ConnectionInfo, Endpoint, GateTag, Lane, Origin, PeerCredentials, PipeName, Transport, UnixPath,
164};
165#[cfg(unix)]
166pub use inherit::fastcgi_listener;
167pub use inherit::{InheritError, InheritedListener, systemd, systemd_named};
168pub use limits::{Limits, LimitsBuilder, MinRate, Pressure, SizeClass};
169#[cfg(all(unix, feature = "local"))]
170pub use local::{Descriptors, UnixConnection, UnixListener};
171#[cfg(feature = "local")]
172pub use local::{LocalConnection, LocalListener};
173#[cfg(all(windows, feature = "local"))]
174pub use local_windows::{UnixConnection, UnixListener};
175pub use opts::{Keepalive, Linger, ListenOptions, LocalOptions, SocketOptions, TcpOptions};
176#[cfg(all(windows, feature = "local"))]
177pub use pipe::{PipeConnection, PipeListener, PipeOptions, PipeSecurity};
178pub use proxy::{ProxyError, ProxyFacts, ProxyMode, ProxyVersions};
179#[cfg(feature = "proxy")]
180pub use proxy_parse::{
181 ProxyAddress, ProxyCommand, ProxyHeader, ProxyParser, ProxyProgress, ProxySsl, ProxyTransport,
182 Tlv, Tlvs, encode_v1, encode_v2,
183};
184#[cfg(feature = "udp")]
185pub use server::UdpOnly;
186pub use server::{
187 AdoptSpec, BlockingHandler, DoorState, DrainReport, ListenerSpec, NoGate, NoWork, Placement,
188 Server, ServerBuilder, ServerHandle, Sessions, Stats, Workload,
189};
190pub use session::{
191 Input, Output, OutputFull, Phase, Session, SessionContext, SessionFactory, Step, SwapError,
192};
193pub use tcp::{ConnectOptions, ListenerInfo, TcpConnection, TcpListener};
194pub use time::{Clock, ManualClock, MonotonicClock, monotonic_micros};
195pub use tls::{
196 NoTls, NoTlsSession, TlsAcceptor, TlsCloseKind, TlsError, TlsEvent, TlsFailure, TlsInfo,
197 TlsProgress, TlsSession,
198};
199pub use trusted::TrustedProxies;
200#[cfg(feature = "udp")]
201pub use udp::{
202 Datagram, DatagramBuffer, MulticastInterface, OutgoingDatagram, UdpBudget, UdpContext,
203 UdpOptions, UdpSession, UdpSessionFactory, UdpSocket, UdpStats,
204};
205
206/// Crate version string, populated by Cargo at build time.
207///
208/// ```
209/// assert!(!net_io::VERSION.is_empty());
210/// ```
211pub const VERSION: &str = env!("CARGO_PKG_VERSION");
212
213/// Binds a TCP listener with safe defaults: backlog 1,024 (capped by the OS);
214/// `SO_REUSEADDR` on Unix; `SO_EXCLUSIVEADDRUSE` on Windows; IPv6-only for IPv6
215/// addresses; close-on-exec. The first resolved address that binds wins.
216///
217/// # Errors
218///
219/// `AddrInUse`, `PermissionDenied`, `AddrNotAvailable`, `NoAddresses`, `Bind`.
220///
221/// # Examples
222///
223/// ```
224/// let listener = net_io::listen("127.0.0.1:0")?;
225/// let addr = listener.local_addr()?;
226/// println!("listening on {addr}, backlog {:?}", listener.info().backlog);
227/// # Ok::<(), net_io::NetError>(())
228/// ```
229pub fn listen<A: ToSocketAddrs>(addr: A) -> Result<TcpListener, NetError> {
230 TcpListener::bind(addr)
231}
232
233/// Connects with a total timeout of 10 s across every resolved address,
234/// `TCP_NODELAY` on, and default limits (read and write timeouts 30 s).
235///
236/// # Errors
237///
238/// `ConnectTimeout`, `ConnectionRefused`, `HostUnreachable`, `NetworkUnreachable`, `NoAddresses`.
239///
240/// # Examples
241///
242/// ```no_run
243/// let mut conn = net_io::connect("example.com:80")?;
244/// conn.send_all(b"HEAD / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")?;
245/// let mut head = [0u8; 512];
246/// let n = conn.recv(&mut head)?;
247/// println!("{}", String::from_utf8_lossy(&head[..n]));
248/// # Ok::<(), net_io::NetError>(())
249/// ```
250pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<TcpConnection, NetError> {
251 TcpConnection::connect(addr)
252}
253
254/// Starts a server on the blocking driver that runs `handler` once per
255/// connection on a pool worker, with default limits and [`AllowAll`] as the
256/// gate; the door is open on return. `Ok(())` closes the connection
257/// gracefully; `Err` closes it abortively.
258///
259/// Keep the returned `Server`: dropping it stops the server. A server facing
260/// the internet should set a [`Gate`] through [`ServerBuilder`].
261///
262/// # Errors
263///
264/// As [`listen`]; `InvalidConfig` when the worker count exceeds the descriptor limit.
265///
266/// # Examples
267///
268/// ```no_run
269/// use std::io::{BufRead, BufReader, Write};
270///
271/// // Handlers can use std::io traits: TcpConnection implements Read and Write.
272/// let server = net_io::serve("127.0.0.1:7001", |conn| {
273/// let mut line = String::new();
274/// BufReader::new(&mut *conn).read_line(&mut line)?;
275/// conn.write_all(line.to_uppercase().as_bytes())?;
276/// Ok(())
277/// })?;
278/// server.wait_for_stop();
279/// # Ok::<(), net_io::NetError>(())
280/// ```
281pub fn serve<A, F>(addr: A, handler: F) -> Result<Server, NetError>
282where
283 A: ToSocketAddrs,
284 F: Fn(&mut TcpConnection) -> Result<(), NetError> + Send + Sync + 'static,
285{
286 let server = ServerBuilder::new()
287 .listener(ListenerSpec::tcp(addr))
288 .driver(DriverKind::Blocking)
289 .handler(handler)
290 .gate(AllowAll)
291 .build()?;
292 server.open_door()?;
293 Ok(server)
294}
295
296/// Starts a server that drives sans-IO [`Session`]s, with default limits and
297/// [`AllowAll`] as the gate; the door is open on return. Any
298/// `Fn(&ConnectionInfo) -> S` is a factory.
299///
300/// # Errors
301///
302/// As [`serve`].
303///
304/// # Examples
305///
306/// ```no_run
307/// use net_io::{Input, Session, SessionContext, Step};
308///
309/// struct Echo;
310/// impl Session for Echo {
311/// fn on_data(&mut self, cx: &mut SessionContext<'_>, input: &mut Input) -> Step {
312/// let taken = cx.output().push_partial(input.bytes());
313/// input.consume(taken);
314/// Step::Write
315/// }
316/// }
317///
318/// let server = net_io::serve_sessions("127.0.0.1:7002", |_info: &net_io::ConnectionInfo| Echo)?;
319/// server.wait_for_stop();
320/// # Ok::<(), net_io::NetError>(())
321/// ```
322pub fn serve_sessions<A, F>(addr: A, factory: F) -> Result<Server, NetError>
323where
324 A: ToSocketAddrs,
325 F: SessionFactory,
326{
327 let server = ServerBuilder::new()
328 .listener(ListenerSpec::tcp(addr))
329 .sessions(factory)
330 .gate(AllowAll)
331 .build()?;
332 server.open_door()?;
333 Ok(server)
334}
335
336/// Opens a local IPC listener: on Unix, a Unix domain socket at the path `name`
337/// with mode `0600` (only the owner may connect; [`LocalOptions`] and
338/// `UnixListener::bind_with` for more). On Windows, the named pipe
339/// `\\\\.\\pipe\\<name>`, owner only, remote clients rejected.
340///
341/// # Errors
342///
343/// `PathTooLong`, `StaleSocketInUse` (a live listener owns the path, or it is
344/// not a socket), `PermissionDenied`, `NotFound`, `Unsupported`, `Os`.
345///
346/// ```no_run
347/// let listener = net_io::listen_local("/run/myapp/control.sock")?;
348/// let (mut conn, info) = listener.accept()?;
349/// println!("peer process {:?}", info.peer_credentials.and_then(|c| c.pid));
350/// conn.send_all(b"ok\n")?;
351/// # Ok::<(), net_io::NetError>(())
352/// ```
353#[cfg(feature = "local")]
354pub fn listen_local(name: &str) -> Result<LocalListener, NetError> {
355 local::listen(name)
356}
357
358/// Connects to a local IPC listener (see [`listen_local`]); waits up to 10 s
359/// when its backlog is full.
360///
361/// # Errors
362///
363/// `NotFound` (no socket, or nobody listening), `PermissionDenied`,
364/// `TimedOut { kind: Connect }`, `PathTooLong`, `Unsupported`, `Os`.
365///
366/// ```no_run
367/// let mut conn = net_io::connect_local("/run/myapp/control.sock")?;
368/// conn.send_all(b"status\n")?;
369/// # Ok::<(), net_io::NetError>(())
370/// ```
371#[cfg(feature = "local")]
372pub fn connect_local(name: &str) -> Result<LocalConnection, NetError> {
373 local::connect(name)
374}
375
376#[cfg(test)]
377mod tests {
378 use super::VERSION;
379
380 #[test]
381 fn test_version_matches_manifest() {
382 assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
383 assert_eq!(VERSION.split('.').count(), 3, "expected MAJOR.MINOR.PATCH");
384 }
385}