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.3.0`: TCP and the blocking driver on Linux, macOS, and Windows. PROXY
49//! protocol, UDP, local sockets, the TLS server integration, inheritance, and
50//! hand-off arrive in `0.4.0`; the reactor in `0.5.0`.
51
52#![doc(html_root_url = "https://docs.rs/net-io")]
53#![cfg_attr(docsrs, feature(doc_cfg))]
54#![deny(unsafe_code)]
55#![deny(unsafe_op_in_unsafe_fn)]
56#![deny(missing_docs)]
57#![cfg_attr(
58 not(test),
59 deny(
60 clippy::unwrap_used,
61 clippy::expect_used,
62 clippy::panic,
63 clippy::todo,
64 clippy::unimplemented,
65 clippy::dbg_macro,
66 clippy::print_stdout,
67 clippy::print_stderr
68 )
69)]
70
71mod accept;
72mod addr;
73mod buf;
74mod completer;
75mod control;
76mod driver;
77mod error;
78mod events;
79mod gate;
80mod handoff;
81mod id;
82mod info;
83mod inherit;
84mod limits;
85mod link;
86mod opts;
87mod proxy;
88mod rate;
89mod server;
90mod session;
91mod sys;
92mod tcp;
93#[cfg(test)]
94mod testing;
95mod time;
96mod tls;
97mod trusted;
98
99/// Fault injection for net-io's own tests, compiled only with `--cfg net_io_shim`.
100/// Not part of the public API and not covered by semver.
101#[cfg(net_io_shim)]
102#[doc(hidden)]
103pub mod __shim {
104 pub use crate::sys::shim::{Fault, Op, clear, counted, program, start_counting, stop_counting};
105}
106
107use std::net::ToSocketAddrs;
108
109pub use addr::{AddrError, Cidr, PrefixKey, PrefixPolicy, normalize, normalize_socket};
110pub use buf::{Buffer, BufferPool, PoolStats};
111pub use completer::{Completer, PostError};
112pub use control::{ControlLine, ControlState, NoControl, SimpleControl};
113pub use driver::DriverKind;
114pub use error::{
115 CloseReason, ConfigReason, Direction, ErrorClass, KillReason, LimitKind, NetError,
116 OsErrorClass, RefusalCause, TimeoutKind,
117};
118pub use events::{CoalescedKind, Event, EventSink, NoopSink};
119pub use gate::{AdmitStage, Admitted, AllowAll, Denial, Gate, KillHandle, Refusal, WriteCheck};
120pub use handoff::HandoffError;
121pub use id::{ConnectionId, ListenerId, WorkerId};
122pub use info::{
123 ConnectionInfo, Endpoint, GateTag, Lane, Origin, PeerCredentials, PipeName, Transport, UnixPath,
124};
125pub use inherit::InheritError;
126pub use limits::{Limits, LimitsBuilder, MinRate, Pressure, SizeClass};
127pub use opts::{Keepalive, Linger, ListenOptions, SocketOptions, TcpOptions};
128pub use proxy::{ProxyError, ProxyFacts, ProxyMode, ProxyVersions};
129pub use server::{
130 BlockingHandler, DoorState, DrainReport, ListenerSpec, NoGate, NoWork, Server, ServerBuilder,
131 ServerHandle, Sessions, Stats, Workload,
132};
133pub use session::{
134 Input, Output, OutputFull, Phase, Session, SessionContext, SessionFactory, Step, SwapError,
135};
136pub use tcp::{ConnectOptions, ListenerInfo, TcpConnection, TcpListener};
137pub use time::{Clock, ManualClock, MonotonicClock};
138pub use tls::{
139 NoTls, NoTlsSession, TlsAcceptor, TlsCloseKind, TlsError, TlsEvent, TlsFailure, TlsInfo,
140 TlsProgress, TlsSession,
141};
142pub use trusted::TrustedProxies;
143
144/// Crate version string, populated by Cargo at build time.
145///
146/// ```
147/// assert!(!net_io::VERSION.is_empty());
148/// ```
149pub const VERSION: &str = env!("CARGO_PKG_VERSION");
150
151/// Binds a TCP listener with safe defaults: backlog 1,024 (capped by the OS);
152/// `SO_REUSEADDR` on Unix; `SO_EXCLUSIVEADDRUSE` on Windows; IPv6-only for IPv6
153/// addresses; close-on-exec. The first resolved address that binds wins.
154///
155/// # Errors
156///
157/// `AddrInUse`, `PermissionDenied`, `AddrNotAvailable`, `NoAddresses`, `Bind`.
158///
159/// # Examples
160///
161/// ```
162/// let listener = net_io::listen("127.0.0.1:0")?;
163/// let addr = listener.local_addr()?;
164/// println!("listening on {addr}, backlog {:?}", listener.info().backlog);
165/// # Ok::<(), net_io::NetError>(())
166/// ```
167pub fn listen<A: ToSocketAddrs>(addr: A) -> Result<TcpListener, NetError> {
168 TcpListener::bind(addr)
169}
170
171/// Connects with a total timeout of 10 s across every resolved address,
172/// `TCP_NODELAY` on, and default limits (read and write timeouts 30 s).
173///
174/// # Errors
175///
176/// `ConnectTimeout`, `ConnectionRefused`, `HostUnreachable`, `NetworkUnreachable`, `NoAddresses`.
177///
178/// # Examples
179///
180/// ```no_run
181/// let mut conn = net_io::connect("example.com:80")?;
182/// conn.send_all(b"HEAD / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")?;
183/// let mut head = [0u8; 512];
184/// let n = conn.recv(&mut head)?;
185/// println!("{}", String::from_utf8_lossy(&head[..n]));
186/// # Ok::<(), net_io::NetError>(())
187/// ```
188pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<TcpConnection, NetError> {
189 TcpConnection::connect(addr)
190}
191
192/// Starts a server on the blocking driver that runs `handler` once per
193/// connection on a pool worker, with default limits and [`AllowAll`] as the
194/// gate; the door is open on return. `Ok(())` closes the connection
195/// gracefully; `Err` closes it abortively.
196///
197/// Keep the returned `Server`: dropping it stops the server. A server facing
198/// the internet should set a [`Gate`] through [`ServerBuilder`].
199///
200/// # Errors
201///
202/// As [`listen`]; `InvalidConfig` when the worker count exceeds the descriptor limit.
203///
204/// # Examples
205///
206/// ```no_run
207/// use std::io::{BufRead, BufReader, Write};
208///
209/// // Handlers can use std::io traits: TcpConnection implements Read and Write.
210/// let server = net_io::serve("127.0.0.1:7001", |conn| {
211/// let mut line = String::new();
212/// BufReader::new(&mut *conn).read_line(&mut line)?;
213/// conn.write_all(line.to_uppercase().as_bytes())?;
214/// Ok(())
215/// })?;
216/// server.wait_for_stop();
217/// # Ok::<(), net_io::NetError>(())
218/// ```
219pub fn serve<A, F>(addr: A, handler: F) -> Result<Server, NetError>
220where
221 A: ToSocketAddrs,
222 F: Fn(&mut TcpConnection) -> Result<(), NetError> + Send + Sync + 'static,
223{
224 let server = ServerBuilder::new()
225 .listener(ListenerSpec::tcp(addr))
226 .driver(DriverKind::Blocking)
227 .handler(handler)
228 .gate(AllowAll)
229 .build()?;
230 server.open_door()?;
231 Ok(server)
232}
233
234/// Starts a server that drives sans-IO [`Session`]s, with default limits and
235/// [`AllowAll`] as the gate; the door is open on return. Any
236/// `Fn(&ConnectionInfo) -> S` is a factory.
237///
238/// # Errors
239///
240/// As [`serve`].
241///
242/// # Examples
243///
244/// ```no_run
245/// use net_io::{Input, Session, SessionContext, Step};
246///
247/// struct Echo;
248/// impl Session for Echo {
249/// fn on_data(&mut self, cx: &mut SessionContext<'_>, input: &mut Input) -> Step {
250/// let taken = cx.output().push_partial(input.bytes());
251/// input.consume(taken);
252/// Step::Write
253/// }
254/// }
255///
256/// let server = net_io::serve_sessions("127.0.0.1:7002", |_info: &net_io::ConnectionInfo| Echo)?;
257/// server.wait_for_stop();
258/// # Ok::<(), net_io::NetError>(())
259/// ```
260pub fn serve_sessions<A, F>(addr: A, factory: F) -> Result<Server, NetError>
261where
262 A: ToSocketAddrs,
263 F: SessionFactory,
264{
265 let server = ServerBuilder::new()
266 .listener(ListenerSpec::tcp(addr))
267 .sessions(factory)
268 .gate(AllowAll)
269 .build()?;
270 server.open_door()?;
271 Ok(server)
272}
273
274#[cfg(test)]
275mod tests {
276 use super::VERSION;
277
278 #[test]
279 fn test_version_matches_manifest() {
280 assert_eq!(VERSION, env!("CARGO_PKG_VERSION"));
281 assert_eq!(VERSION.split('.').count(), 3, "expected MAJOR.MINOR.PATCH");
282 }
283}