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