sozu_lib/socket.rs
1//! Socket I/O wrappers and TCP option helpers.
2//!
3//! Hosts the `SocketHandler` trait, the `FrontRustls` wrapper that drives
4//! a rustls `ServerConnection` over a `TcpStream`, plus the ancillary
5//! `getsockopt(TCP_INFO)` / TCP-keepalive helpers. The
6//! `FrontRustls::socket_write` / `socket_write_vectored` pair is a known
7//! truncation hot spot — keep the two paths structurally symmetric (see
8//! the per-method `///` invariants).
9
10use std::{
11 io::{ErrorKind, Read, Write},
12 net::SocketAddr,
13};
14
15use mio::net::{TcpListener, TcpStream, UdpSocket};
16use rustls::{ProtocolVersion, ServerConnection};
17use rusty_ulid::Ulid;
18use socket2::{Domain, Protocol, Socket, Type};
19use sozu_command::{config::MAX_LOOP_ITERATIONS, logging::ansi_palette, ready::Ready};
20
21use crate::metrics::names;
22
23#[derive(thiserror::Error, Debug)]
24pub enum ServerBindError {
25 #[error("could not set bind to socket: {0}")]
26 BindError(std::io::Error),
27 #[error("could not listen on socket: {0}")]
28 Listen(std::io::Error),
29 #[error("could not set socket to nonblocking: {0}")]
30 SetNonBlocking(std::io::Error),
31 #[error("could not set reuse address: {0}")]
32 SetReuseAddress(std::io::Error),
33 #[error("could not set reuse address: {0}")]
34 SetReusePort(std::io::Error),
35 #[error("Could not create socket: {0}")]
36 SocketCreationError(std::io::Error),
37 #[error("Invalid socket address '{address}': {error}")]
38 InvalidSocketAddress { address: String, error: String },
39 #[error(
40 "could not bind the listener {address} to the network interface '{interface}' \
41 (SO_BINDTODEVICE): {error}. The interface must exist when the listener is activated, \
42 and on Linux kernels before 5.7 binding a socket to an interface requires the \
43 CAP_NET_RAW capability"
44 )]
45 BindToDevice {
46 address: SocketAddr,
47 interface: String,
48 error: std::io::Error,
49 },
50 #[error(
51 "the listener {address} names the network interface '{interface}', but binding a \
52 socket to an interface (SO_BINDTODEVICE) is only supported on Linux"
53 )]
54 BindToDeviceUnsupported {
55 address: SocketAddr,
56 interface: String,
57 },
58}
59
60#[derive(Debug, PartialEq, Eq, Copy, Clone)]
61pub enum SocketResult {
62 Continue,
63 Closed,
64 WouldBlock,
65 Error,
66}
67
68#[derive(Debug, PartialEq, Eq, Copy, Clone)]
69pub enum TransportProtocol {
70 Tcp,
71 Ssl2,
72 Ssl3,
73 Tls1_0,
74 Tls1_1,
75 Tls1_2,
76 Tls1_3,
77}
78
79pub trait SocketHandler {
80 fn socket_read(&mut self, buf: &mut [u8]) -> (usize, SocketResult);
81 fn socket_write(&mut self, buf: &[u8]) -> (usize, SocketResult);
82 fn socket_write_vectored(&mut self, _buf: &[std::io::IoSlice]) -> (usize, SocketResult);
83 fn socket_wants_write(&self) -> bool {
84 false
85 }
86 fn socket_close(&mut self) {}
87 /// Write `buf`, then begin the close [`Self::socket_close`] begins, and
88 /// report what [`Self::socket_write`] would for `buf`.
89 ///
90 /// The close is begun only when the whole of `buf` was accepted, so a
91 /// caller that gets back less resumes with the rest and the close still
92 /// follows its last byte. A handler without a close of its own does
93 /// exactly the two calls; [`FrontRustls`] puts the plaintext and its
94 /// `close_notify` behind one flush, so the last application bytes and
95 /// the alert leave in one `writev(2)` instead of two.
96 fn socket_write_then_close(&mut self, buf: &[u8]) -> (usize, SocketResult) {
97 let (size, status) = self.socket_write(buf);
98 if size == buf.len() {
99 self.socket_close();
100 }
101 (size, status)
102 }
103 fn socket_ref(&self) -> &TcpStream;
104 fn socket_mut(&mut self) -> &mut TcpStream;
105 /// Peer address of this connection, preferring a snapshot taken when the
106 /// handler was built over a live `getpeername(2)`.
107 ///
108 /// `getpeername(2)` fails with `ENOTCONN` the moment the peer resets, so a
109 /// live lookup renders `peer=None` on exactly the error lines an operator
110 /// reads during an incident. It also reports the transport peer, which on a
111 /// PROXY-protocol frontend is the load balancer rather than the client the
112 /// rest of the session is logged against.
113 ///
114 /// Implementations that cache an address ([`SessionTcpStream`],
115 /// [`FrontRustls`]) return it and fall back to the live lookup only when
116 /// they have none; the raw [`TcpStream`] impl carries no context and has
117 /// nothing but the live lookup. Deliberately has no default body: a new
118 /// handler must state which of the two it is.
119 fn peer_addr(&self) -> Option<SocketAddr>;
120 fn protocol(&self) -> TransportProtocol;
121 fn read_error(&self);
122 fn write_error(&self);
123 /// Returns the owning connection's session ULID when known. Used by
124 /// `log_socket_context!` to render the `[<session_ulid> - - -]` segment
125 /// of the socket-layer log prefix, matching the format used by the
126 /// rest of the mux stack. Returns `None` for contextless implementations
127 /// (e.g. raw `mio::TcpStream`); the macro renders `-` in the ULID slot.
128 fn session_ulid(&self) -> Option<Ulid> {
129 None
130 }
131}
132
133/// Format the socket-layer log prefix `[<session_ulid_or_->]\tSOCKET\tSession(
134/// peer=..., local=..., rtt=..., state=..., protocol=...)\t >>>` for a
135/// [`SocketHandler`] impl that has `self` in scope. When `$self.session_ulid()`
136/// returns `None` (e.g. the raw [`TcpStream`] impl that carries no session
137/// context) the ULID slot is rendered as `-` so the column layout stays
138/// stable across sessionless plumbing. The `[ulid - - -]` context comes first
139/// to stay aligned with `MUX-*`, `PIPE` and `RUSTLS` log lines. Colour scheme
140/// comes from [`sozu_command::logging::ansi_palette`] — single source of
141/// truth for every `log_*_context!` macro in the proxy.
142///
143/// `peer` is [`SocketHandler::peer_addr`] — the address the handler cached,
144/// falling back to a live `getpeername(2)` only where it has none. Same
145/// preference order, and now the same single source, as
146/// [`log_socket_module_prefix`] and the `MUX-*` contexts, so every rendering
147/// of one connection names one host.
148///
149/// Spelled as a fully-qualified trait call to foreclose a latent hazard, not
150/// to fix a present one: `$self.peer_addr()` compiles clean today and passes
151/// every test, because no current expansion sits on a raw [`TcpStream`] — all
152/// of them are in `impl SocketHandler for FrontRustls`, which has no inherent
153/// `peer_addr` for method resolution to prefer. Add one on a bare `mio`
154/// [`TcpStream`] and the unqualified form silently picks mio's inherent
155/// method instead of this trait's: measured, that slot renders
156/// `peer=Ok(127.0.0.1:33587)` beside `local=Some(..)` — an `io::Result` where
157/// every other expansion renders an `Option`. The qualification makes that
158/// impossible rather than merely unlikely.
159///
160/// `local`, `rtt`, `state` render per [`log_socket_module_prefix`]'s
161/// description; `local` stays a live `getsockname(2)`, which survives what
162/// `getpeername(2)` does not.
163macro_rules! log_socket_context {
164 ($self:expr) => {{
165 let (open, reset, grey, gray, white) = ansi_palette();
166 let ulid = match $self.session_ulid() {
167 Some(ulid) => ulid.to_string(),
168 None => "-".to_string(),
169 };
170 let snapshot = crate::socket::stats::socket_snapshot($self.socket_ref());
171 let rtt = snapshot.as_ref().map(|s| s.rtt);
172 let state = snapshot.as_ref().map(|s| s.state);
173 format!(
174 "[{ulid} - - -]\t{open}SOCKET{reset}\t{grey}Session{reset}({gray}peer{reset}={white}{peer:?}{reset}, {gray}local{reset}={white}{local:?}{reset}, {gray}rtt{reset}={white}{rtt:?}{reset}, {gray}state{reset}={white}{state:?}{reset}, {gray}protocol{reset}={white}{protocol:?}{reset})\t >>>",
175 open = open,
176 reset = reset,
177 grey = grey,
178 gray = gray,
179 white = white,
180 ulid = ulid,
181 peer = crate::socket::SocketHandler::peer_addr($self),
182 local = $self.socket_ref().local_addr().ok(),
183 rtt = rtt,
184 state = state,
185 protocol = $self.protocol(),
186 )
187 }};
188}
189
190/// Module-level socket log prefix used from free functions (e.g. the shared
191/// `tcp_socket_*` helpers) where `self` is not in scope but the caller can
192/// still thread a session `Ulid`, a cached peer address, and the underlying
193/// [`TcpStream`] through as parameters. Renders the same
194/// `[<ulid> - - -]\tSOCKET\tSession(peer=..., local=..., rtt=..., state=..., protocol=Tcp)\t >>>`
195/// prefix as [`log_socket_context!`]; colour scheme via
196/// [`sozu_command::logging::ansi_palette`].
197///
198/// Per-slot semantics:
199///
200/// - `peer` — prefers the caller-supplied `configured_peer` (cached at
201/// [`SessionTcpStream`] construction, immune to ENOTCONN on a socket that
202/// failed an asynchronous `connect()`) and falls back to a live
203/// `getpeername(2)` lookup when no cache was provided.
204/// - `local` — `getsockname(2)`, stays valid across failed connects.
205/// - `rtt` / `state` — a single `getsockopt(TCP_INFO)` call via
206/// [`stats::socket_snapshot`]; both render as `None` on an FSM state
207/// where the kernel rejects the call. `state="SYN_SENT"` is the
208/// clearest signal for a failed outbound `connect()`.
209/// - `protocol` — hardcoded to `Tcp` (raw-TCP helpers only).
210fn log_socket_module_prefix(
211 stream: &TcpStream,
212 session_ulid: Option<Ulid>,
213 configured_peer: Option<SocketAddr>,
214) -> String {
215 let (open, reset, grey, gray, white) = ansi_palette();
216 let ulid = match session_ulid {
217 Some(ulid) => ulid.to_string(),
218 None => "-".to_string(),
219 };
220 let snapshot = crate::socket::stats::socket_snapshot(stream);
221 let rtt = snapshot.as_ref().map(|s| s.rtt);
222 let state = snapshot.as_ref().map(|s| s.state);
223 format!(
224 "[{ulid} - - -]\t{open}SOCKET{reset}\t{grey}Session{reset}({gray}peer{reset}={white}{peer:?}{reset}, {gray}local{reset}={white}{local:?}{reset}, {gray}rtt{reset}={white}{rtt:?}{reset}, {gray}state{reset}={white}{state:?}{reset}, {gray}protocol{reset}={white}Tcp{reset})\t >>>",
225 peer = configured_peer.or_else(|| stream.peer_addr().ok()),
226 local = stream.local_addr().ok(),
227 )
228}
229
230/// Body of [`tcp_socket_read`], generic over the transport so tests can count
231/// every read a call issues against it.
232///
233/// One `read` per call. A stream socket that answers fewer bytes than it was
234/// offered has emptied its receive queue: `man 7 epoll` (man-pages 6.13, the
235/// EPOLLET question) — "if you call read(2) by asking to read a certain
236/// amount of data and read(2) returns a lower number of bytes, you can be sure
237/// of having exhausted the read I/O space for the file descriptor". A second
238/// `recv(2)` could only answer EAGAIN, so the call stops there, as HAProxy
239/// (`src/raw_sock.c`, `fd_cant_recv` when `ret < try`) and tokio
240/// (`tokio/src/io/poll_evented.rs`, `clear_readiness` when `0 < n < len`) do.
241///
242/// One exception to that guarantee: TCP urgent data. `recv` stops before an
243/// urgent (out-of-band) mark even when bytes are queued behind it, so a short
244/// read does NOT prove the queue empty while such a mark is pending. Measured
245/// on a loopback socket: `AAAA`, `send(B, MSG_OOB)`, `CCCC` raise one edge,
246/// this read answers `(4, WouldBlock)` with `AAAA`, no further event comes,
247/// and `CCCC` stays in the kernel until the peer sends again. A stream that
248/// carries OOB data (telnet, rlogin, FTP `ABOR`) can therefore stall until the
249/// peer's next send. HAProxy and tokio behave the same, and sozu never read
250/// out-of-band data before either.
251///
252/// Readiness contract:
253///
254/// - a short read answers `(n, WouldBlock)`, exactly what the previous loop
255/// answered one EAGAIN later; the caller drops READABLE, and mio registers
256/// every socket edge-triggered (`mio/src/sys/unix/selector/epoll.rs`,
257/// `EPOLLET`), where "an event will be generated upon each receipt of a
258/// chunk of data" (`man 7 epoll`), so bytes arriving after this `recv`
259/// raise the next event;
260/// - a read that fills `buf` is not a short read: it answers `(n, Continue)`
261/// and READABLE stays, because more may be queued;
262/// - EOF behind the data is left for the next call. A FIN that was already
263/// signalled comes with no further edge, so a caller that saw HUP must keep
264/// READABLE to read it: `update_readiness_after_read` in the mux does.
265///
266/// A read the kernel interrupted (`EINTR`) is retried at once, as
267/// [`flush_tls`] does for writes: it says nothing about the socket.
268///
269/// The returned error, when there is one, is a transport failure the caller
270/// logs with its socket context; the result is then [`SocketResult::Error`].
271fn plain_socket_read<R: Read>(
272 stream: &mut R,
273 buf: &mut [u8],
274) -> (usize, SocketResult, Option<std::io::Error>) {
275 // An empty slice reads nothing and says nothing about the socket; callers
276 // guard against it, and `(0, Continue)` is what they expect back.
277 if buf.is_empty() {
278 return (0, SocketResult::Continue, None);
279 }
280 let read = loop {
281 match stream.read(buf) {
282 Err(e) if e.kind() == ErrorKind::Interrupted => continue,
283 read => break read,
284 }
285 };
286 match read {
287 Ok(0) => (0, SocketResult::Closed, None),
288 Ok(sz) => {
289 // `read` cannot report more bytes than the slice it was given.
290 debug_assert!(
291 sz <= buf.len(),
292 "read reported {sz} bytes into a {}-byte slice",
293 buf.len()
294 );
295 if sz == buf.len() {
296 (sz, SocketResult::Continue, None)
297 } else {
298 (sz, SocketResult::WouldBlock, None)
299 }
300 }
301 Err(e) => match e.kind() {
302 ErrorKind::WouldBlock => (0, SocketResult::WouldBlock, None),
303 // Treat `ConnectionRefused` as a closed socket, mirroring the
304 // write path. On Linux a failed asynchronous `connect()`
305 // surfaces as `ECONNREFUSED` on the first read; it is
306 // operationally identical to any other benign peer-initiated
307 // close and does not warrant a log line on every backend
308 // that happens to be down.
309 //
310 // A reset is still a close, but it is counted: clients that abort
311 // instead of finishing cleanly are an operational signal, not an
312 // error (sozu-proxy/sozu#434). The kernel reports `ECONNRESET` to
313 // the first syscall after the RST only; later reads see EOF and
314 // later writes `EPIPE`, so each reset is counted once.
315 ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted => {
316 incr!(names::tcp::READ_RESET);
317 (0, SocketResult::Closed, None)
318 }
319 ErrorKind::BrokenPipe | ErrorKind::ConnectionRefused => (0, SocketResult::Closed, None),
320 _ => (0, SocketResult::Error, Some(e)),
321 },
322 }
323}
324
325/// Shared read/write/vectored-write logic used by both
326/// [`impl SocketHandler for TcpStream`] and
327/// [`impl SocketHandler for SessionTcpStream`]. Free-function entry point:
328/// `self` is out of scope here, so error logs use [`log_socket_module_prefix`]
329/// which renders the same `Session(peer, rtt, protocol)` context as
330/// [`log_socket_context!`] by reading from the `stream` + `session_ulid`
331/// parameters threaded through each helper. The read itself is
332/// [`plain_socket_read`].
333fn tcp_socket_read(
334 stream: &mut TcpStream,
335 buf: &mut [u8],
336 session_ulid: Option<Ulid>,
337 configured_peer: Option<SocketAddr>,
338) -> (usize, SocketResult) {
339 let (size, result, fault) = plain_socket_read(stream, buf);
340 if let Some(e) = fault {
341 match e.kind() {
342 // Noisy-expected transport failures: backend unreachable,
343 // TCP_USER_TIMEOUT expiry, post-close reads. Keep a log line
344 // so operators can still trend the rate, but `warn!` — this
345 // is reality-at-scale, not a sozu invariant break.
346 ErrorKind::HostUnreachable
347 | ErrorKind::NetworkUnreachable
348 | ErrorKind::TimedOut
349 | ErrorKind::NotConnected => warn!(
350 "{} socket_read error={:?}",
351 log_socket_module_prefix(stream, session_ulid, configured_peer),
352 e
353 ),
354 // Genuinely loud variants (`PermissionDenied`, `AddrNotAvailable`,
355 // `InvalidInput`/`Data`, …) and the unknown catch-all stay at
356 // `error!` so operators keep paging on real misconfig.
357 _ => error!(
358 "{} socket_read error={:?}",
359 log_socket_module_prefix(stream, session_ulid, configured_peer),
360 e
361 ),
362 }
363 }
364 (size, result)
365}
366
367/// A failure [`plain_socket_write`] or [`plain_socket_write_vectored`] hit,
368/// handed back rather than logged so [`tcp_socket_write`] and
369/// [`tcp_socket_write_vectored`] render it with their socket context, as
370/// [`tcp_socket_read`] does for [`plain_socket_read`]. Its counters are
371/// already bumped.
372#[derive(Debug)]
373enum PlainWriteFault {
374 /// The write loop ran [`MAX_LOOP_ITERATIONS`] times without settling.
375 LoopLimit,
376 /// The socket failed with something other than `WouldBlock`, a reset, a
377 /// broken pipe, a refused connection or an interruption.
378 Io(std::io::Error),
379}
380
381/// Map a failed plain TCP write to its result, bumping its counters. A peer
382/// reset (see [`plain_socket_read`]) keeps counting under `tcp.write.error`
383/// as well, as it did before `tcp.write.reset`.
384fn plain_write_error(e: std::io::Error) -> (SocketResult, Option<PlainWriteFault>) {
385 match e.kind() {
386 ErrorKind::WouldBlock => (SocketResult::WouldBlock, None),
387 ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted => {
388 incr!(names::tcp::WRITE_RESET);
389 incr!(names::tcp::WRITE_ERROR);
390 (SocketResult::Closed, None)
391 }
392 ErrorKind::BrokenPipe | ErrorKind::ConnectionRefused => {
393 incr!(names::tcp::WRITE_ERROR);
394 (SocketResult::Closed, None)
395 }
396 _ => {
397 incr!(names::tcp::WRITE_ERROR);
398 (SocketResult::Error, Some(PlainWriteFault::Io(e)))
399 }
400 }
401}
402
403/// Body of [`tcp_socket_write`], generic over the transport so tests can
404/// script what each write answers.
405///
406/// A write the kernel interrupted (`EINTR`) is retried at once, as
407/// [`flush_tls`] and std's `write_all` do: it says nothing about the socket,
408/// so it must neither end the write as `WouldBlock`, which no edge would
409/// follow, nor as `Error`, which closes the session. The retry counts against
410/// [`MAX_LOOP_ITERATIONS`] like every other pass of the loop.
411fn plain_socket_write<W: Write>(
412 stream: &mut W,
413 buf: &[u8],
414) -> (usize, SocketResult, Option<PlainWriteFault>) {
415 let mut size = 0usize;
416 let mut counter = 0;
417 loop {
418 counter += 1;
419 if counter > MAX_LOOP_ITERATIONS {
420 incr!(names::socket::WRITE_INFINITE_LOOP_ERROR);
421 return (size, SocketResult::Error, Some(PlainWriteFault::LoopLimit));
422 }
423 // Loop invariant: the cursor never overshoots the buffer, so the
424 // `&buf[size..]` slice below can never panic on a bad offset.
425 debug_assert!(
426 size <= buf.len(),
427 "write cursor {size} overran buffer len {} (would slice out of bounds)",
428 buf.len()
429 );
430 if size == buf.len() {
431 return (size, SocketResult::Continue, None);
432 }
433 match stream.write(&buf[size..]) {
434 Ok(0) => return (size, SocketResult::Continue, None),
435 Ok(sz) => {
436 // `write` cannot report more bytes than the slice it was given.
437 debug_assert!(
438 sz <= buf.len() - size,
439 "write reported {sz} bytes from a {}-byte remaining slice",
440 buf.len() - size
441 );
442 size += sz;
443 }
444 Err(e) if e.kind() == ErrorKind::Interrupted => {}
445 Err(e) => {
446 let (result, fault) = plain_write_error(e);
447 return (size, result, fault);
448 }
449 }
450 }
451}
452
453/// Body of [`tcp_socket_write_vectored`], generic over the transport so tests
454/// can script what each write answers. One `writev(2)`, retried at once when
455/// the kernel interrupts it (`EINTR`), as [`plain_socket_write`] retries.
456fn plain_socket_write_vectored<W: Write>(
457 stream: &mut W,
458 bufs: &[std::io::IoSlice],
459) -> (usize, SocketResult, Option<PlainWriteFault>) {
460 let written = loop {
461 match stream.write_vectored(bufs) {
462 Err(e) if e.kind() == ErrorKind::Interrupted => continue,
463 written => break written,
464 }
465 };
466 match written {
467 Ok(sz) => {
468 // `write_vectored` cannot report more bytes than the slices held.
469 debug_assert!(
470 sz <= bufs.iter().map(|b| b.len()).sum::<usize>(),
471 "write_vectored reported {sz} bytes from {}-byte slices",
472 bufs.iter().map(|b| b.len()).sum::<usize>()
473 );
474 (sz, SocketResult::Continue, None)
475 }
476 Err(e) => {
477 let (result, fault) = plain_write_error(e);
478 (0, result, fault)
479 }
480 }
481}
482
483/// Log a [`PlainWriteFault`] with the socket context of [`tcp_socket_read`].
484fn log_plain_write_fault(
485 stream: &TcpStream,
486 session_ulid: Option<Ulid>,
487 configured_peer: Option<SocketAddr>,
488 fault: PlainWriteFault,
489) {
490 match fault {
491 PlainWriteFault::LoopLimit => error!(
492 "{} MAX_LOOP_ITERATION reached in TcpStream::socket_write",
493 log_socket_module_prefix(stream, session_ulid, configured_peer)
494 ),
495 PlainWriteFault::Io(e) => match e.kind() {
496 // Noisy-expected transport failures (see `tcp_socket_read` for
497 // rationale): `warn!`, the `tcp.write.error` counter still bumped
498 // so rate-based dashboards stay accurate.
499 ErrorKind::HostUnreachable
500 | ErrorKind::NetworkUnreachable
501 | ErrorKind::TimedOut
502 | ErrorKind::NotConnected => warn!(
503 "{} socket_write error={:?}",
504 log_socket_module_prefix(stream, session_ulid, configured_peer),
505 e
506 ),
507 //FIXME: timeout and other common errors should be sent up
508 _ => error!(
509 "{} socket_write error={:?}",
510 log_socket_module_prefix(stream, session_ulid, configured_peer),
511 e
512 ),
513 },
514 }
515}
516
517fn tcp_socket_write(
518 stream: &mut TcpStream,
519 buf: &[u8],
520 session_ulid: Option<Ulid>,
521 configured_peer: Option<SocketAddr>,
522) -> (usize, SocketResult) {
523 let (size, result, fault) = plain_socket_write(stream, buf);
524 if let Some(fault) = fault {
525 log_plain_write_fault(stream, session_ulid, configured_peer, fault);
526 }
527 (size, result)
528}
529
530fn tcp_socket_write_vectored(
531 stream: &mut TcpStream,
532 bufs: &[std::io::IoSlice],
533 session_ulid: Option<Ulid>,
534 configured_peer: Option<SocketAddr>,
535) -> (usize, SocketResult) {
536 let (size, result, fault) = plain_socket_write_vectored(stream, bufs);
537 if let Some(fault) = fault {
538 log_plain_write_fault(stream, session_ulid, configured_peer, fault);
539 }
540 (size, result)
541}
542
543impl SocketHandler for TcpStream {
544 fn socket_read(&mut self, buf: &mut [u8]) -> (usize, SocketResult) {
545 tcp_socket_read(self, buf, None, None)
546 }
547
548 fn socket_write(&mut self, buf: &[u8]) -> (usize, SocketResult) {
549 tcp_socket_write(self, buf, None, None)
550 }
551
552 fn socket_write_vectored(&mut self, bufs: &[std::io::IoSlice]) -> (usize, SocketResult) {
553 tcp_socket_write_vectored(self, bufs, None, None)
554 }
555
556 fn socket_ref(&self) -> &TcpStream {
557 self
558 }
559
560 fn socket_mut(&mut self) -> &mut TcpStream {
561 self
562 }
563
564 /// No context to cache: a bare stream is all there is, so this is the live
565 /// lookup and renders `None` once the peer has reset. Call sites that need
566 /// a surviving address use [`SessionTcpStream`] or [`FrontRustls`].
567 /// Fully-qualified so it reads as the inherent `mio` method, not a
568 /// recursive call into the trait method being defined.
569 fn peer_addr(&self) -> Option<SocketAddr> {
570 TcpStream::peer_addr(self).ok()
571 }
572
573 fn protocol(&self) -> TransportProtocol {
574 TransportProtocol::Tcp
575 }
576
577 fn read_error(&self) {
578 incr!(names::tcp::READ_ERROR);
579 }
580
581 fn write_error(&self) {
582 incr!(names::tcp::WRITE_ERROR);
583 }
584}
585
586/// [`TcpStream`] wrapped with the owning session's ULID. Exists so plain-TCP
587/// frontends and backends inside the mux stack can prefix SOCKET-layer error
588/// logs with `[<session_ulid> - - -]`, matching what TLS-wrapped frontends
589/// already do via [`FrontRustls::session_ulid`].
590///
591/// The inner [`TcpStream`] is exposed directly so mio registration sites can
592/// borrow it as-is; the outer type only participates in the [`SocketHandler`]
593/// trait dispatch.
594#[derive(Debug)]
595pub struct SessionTcpStream {
596 pub stream: TcpStream,
597 pub session_ulid: Ulid,
598 /// Peer address cached at construction. For backend-facing sockets
599 /// (created from a nonblocking `connect()` in `Mux::dial_backend`) this is
600 /// the cluster-configured backend address — reliable across ENOTCONN
601 /// after a failed handshake, which is the sharp case that motivates the
602 /// cache. For frontend-facing sockets constructed from an accepted
603 /// `TcpStream`, this is the client's peer address — identical to what a
604 /// live `getpeername(2)` would return, but threaded through the same
605 /// plumbing for uniformity. Used as the preferred source of truth for
606 /// the `peer=` slot in `log_socket_module_prefix`, falling back to a
607 /// live lookup when `None`.
608 pub configured_peer: Option<SocketAddr>,
609}
610
611impl SessionTcpStream {
612 pub fn new(stream: TcpStream, session_ulid: Ulid, configured_peer: Option<SocketAddr>) -> Self {
613 Self {
614 stream,
615 session_ulid,
616 configured_peer,
617 }
618 }
619}
620
621impl SocketHandler for SessionTcpStream {
622 fn socket_read(&mut self, buf: &mut [u8]) -> (usize, SocketResult) {
623 tcp_socket_read(
624 &mut self.stream,
625 buf,
626 Some(self.session_ulid),
627 self.configured_peer,
628 )
629 }
630
631 fn socket_write(&mut self, buf: &[u8]) -> (usize, SocketResult) {
632 tcp_socket_write(
633 &mut self.stream,
634 buf,
635 Some(self.session_ulid),
636 self.configured_peer,
637 )
638 }
639
640 fn socket_write_vectored(&mut self, bufs: &[std::io::IoSlice]) -> (usize, SocketResult) {
641 tcp_socket_write_vectored(
642 &mut self.stream,
643 bufs,
644 Some(self.session_ulid),
645 self.configured_peer,
646 )
647 }
648
649 fn socket_ref(&self) -> &TcpStream {
650 &self.stream
651 }
652
653 fn socket_mut(&mut self) -> &mut TcpStream {
654 &mut self.stream
655 }
656
657 /// Same preference order as `log_socket_module_prefix` (private, so not
658 /// linked), which this type's
659 /// I/O helpers already feed `configured_peer` into — one address for both
660 /// the `SOCKET` and `MUX-*` renderings of the same connection.
661 fn peer_addr(&self) -> Option<SocketAddr> {
662 self.configured_peer
663 .or_else(|| self.stream.peer_addr().ok())
664 }
665
666 fn protocol(&self) -> TransportProtocol {
667 TransportProtocol::Tcp
668 }
669
670 fn read_error(&self) {
671 incr!(names::tcp::READ_ERROR);
672 }
673
674 fn write_error(&self) {
675 incr!(names::tcp::WRITE_ERROR);
676 }
677
678 fn session_ulid(&self) -> Option<Ulid> {
679 Some(self.session_ulid)
680 }
681}
682
683pub struct FrontRustls {
684 pub stream: TcpStream,
685 pub session: ServerConnection,
686 /// Peer sent a graceful FIN on the read side (`read()` returned `Ok(0)`).
687 /// We can no longer receive plaintext, but may still have rustls-buffered
688 /// records to flush on the write side — do NOT abort pending writes.
689 pub peer_disconnected: bool,
690 /// The TCP channel is dead: the peer reset the connection
691 /// (RST/ConnectionAborted/BrokenPipe), or a write answered
692 /// [`SocketResult::Error`] — a socket error other than `WouldBlock`
693 /// (an `Interrupted` write is retried in `flush_tls` and never gets
694 /// here), a rustls writer failure, or the write loop's budget. Further
695 /// writes are pointless and short-circuit, and
696 /// [`SocketHandler::socket_wants_write`] stops reporting the records
697 /// rustls still holds: no write can deliver them, so they must neither
698 /// keep the session open nor wait for a WRITABLE edge that will not come.
699 pub peer_reset: bool,
700 /// `process_new_packets` failed on this connection: a corrupt record, a
701 /// fatal alert, a protocol violation. rustls keeps that error for good,
702 /// so from then on [`SocketHandler::socket_read`] answers `(0, Error)`
703 /// at once, without serving plaintext rustls decrypted before the
704 /// failure. See `rustls_socket_read`.
705 pub tls_fatal: bool,
706 /// Connection/session ULID propagated from the enclosing mux session.
707 /// Rendered into SOCKET-layer error logs via [`Self::session_ulid`].
708 pub session_ulid: Ulid,
709 /// Peer address snapshot, same role as [`SessionTcpStream::configured_peer`]
710 /// and same name so the two handlers read alike. Seeded from
711 /// `HttpsSession::peer_address`, which is the PROXY-advertised source on an
712 /// expect-proxy frontend and the accepted socket's peer otherwise — NOT
713 /// from `stream.peer_addr()`, which would be the load balancer in the first
714 /// case and would die with `ENOTCONN` in both. Read through
715 /// [`SocketHandler::peer_addr`].
716 pub configured_peer: Option<SocketAddr>,
717 /// What the read path may assume about `stream`'s receive queue from one
718 /// [`SocketHandler::socket_read`] call to the next. See [`RecvMemory`].
719 pub recv_memory: RecvMemory,
720}
721
722impl FrontRustls {
723 /// Record that the event loop delivered `events` for `stream`: whatever a
724 /// `recv` proved before, bytes may have arrived since. The HTTPS session
725 /// calls it for every event on its frontend token, whatever its state
726 /// (`HttpsSession::update_readiness`, `lib/src/https.rs`), which is the
727 /// only way events reach a session (`Server::ready`, `lib/src/server.rs`).
728 pub fn readiness_delivered(&mut self, events: Ready) {
729 self.recv_memory.event_delivered(events);
730 }
731}
732
733/// What `rustls_socket_read` may assume about the receive queue between two
734/// calls.
735///
736/// The H2 read path makes one call per frame header and one per payload
737/// (`lib/src/protocol/mux/h2.rs`, `readable_inner`), so the frames of one
738/// record are served by several calls: the first one's `recv` is short, the
739/// rest come from rustls's plaintext buffer. Without a memory, the call that
740/// finds that buffer empty issued one more `recv`, which could only answer
741/// EAGAIN — once per request on a multiplexed connection (#1609).
742///
743/// `drained` carries the proof across calls, and is safe for as long as no
744/// readiness event has been delivered since it was set. mio registers every
745/// socket edge-triggered (`mio/src/sys/unix/selector/epoll.rs`, `EPOLLET`),
746/// and "an event will be generated upon each receipt of a chunk of data"
747/// (`man 7 epoll`), so a byte that arrives after the proof either raises an
748/// event still pending in epoll — the next `epoll_wait` delivers it and
749/// re-arms READABLE — or one already delivered, which cleared `drained`. A
750/// delivery that lands while the mux still holds READABLE changes nothing the
751/// mux can see, which is why the memory is cleared at delivery rather than
752/// inferred from READABLE.
753///
754/// `hup_seen` turns the memory off for good once HUP or ERROR is delivered.
755/// A FIN that arrived with the data raised its edge already, and no later
756/// event will announce the EOF behind a short read; the mux keeps READABLE
757/// for it (`update_readiness_after_read`), and the next call must read it.
758///
759/// The TCP urgent data reserve of `plain_socket_read` applies unchanged: a
760/// `recv` stopped short by an urgent mark proves nothing, and the stream
761/// stalls until the peer's next send, as it does within a call since #1606.
762#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
763pub struct RecvMemory {
764 /// A `recv` answered short or EAGAIN, and no event has been delivered
765 /// since.
766 drained: bool,
767 /// HUP or ERROR has been delivered.
768 hup_seen: bool,
769}
770
771impl RecvMemory {
772 /// The memory a session starts with once its handshake is over. `drained`
773 /// is what the handshake proved: a `recv` that answered short or EAGAIN
774 /// dropped READABLE from its event, and only a delivered event with
775 /// READABLE put it back (`handshake_read`, `lib/src/protocol/rustls.rs`).
776 /// A handshake closes on HUP or ERROR, so none was seen.
777 pub fn after_handshake(drained: bool) -> Self {
778 RecvMemory {
779 drained,
780 hup_seen: false,
781 }
782 }
783
784 fn event_delivered(&mut self, events: Ready) {
785 self.drained = false;
786 if events.is_hup() || events.is_error() {
787 self.hup_seen = true;
788 }
789 }
790}
791
792/// One `write_tls` of `session` into `stream`: at most one `writev(2)`, and
793/// none when rustls holds no record (`ChunkVecBuffer::write_to` returns
794/// `Ok(0)` before touching the socket, `rustls-0.23.45/src/vecbuf.rs`).
795///
796/// Every `FrontRustls` write goes through here so tests can count the writes
797/// that reach the socket ([`tls_writes`]).
798///
799/// It is also the one place a TLS frontend's write can observe a peer reset,
800/// so `rustls.write.reset` is counted here rather than at each caller's
801/// error arm (sozu-proxy/sozu#434); the callers' `peer_reset` then stops
802/// every later write before it reaches the socket, so a reset counts once.
803/// The callers still count it under `rustls.write.error` as well.
804///
805/// A write the kernel interrupted (`EINTR`) is retried at once, as the
806/// relay (`lib/src/protocol/proxy_protocol/relay.rs`), the UDP path
807/// (`lib/src/udp.rs`) and the plain TCP paths ([`plain_socket_read`],
808/// [`plain_socket_write`], [`plain_socket_write_vectored`]) do: it says
809/// nothing about the socket, so it must
810/// neither end the write as `WouldBlock`, which no edge would follow, nor
811/// as `Error`, which marks the channel dead.
812fn flush_tls(
813 session: &mut ServerConnection,
814 stream: &mut impl std::io::Write,
815) -> std::io::Result<usize> {
816 #[cfg(test)]
817 if session.wants_write() {
818 TLS_WRITES.with(|writes| writes.set(writes.get() + 1));
819 }
820 let written = loop {
821 match session.write_tls(stream) {
822 Err(e) if e.kind() == ErrorKind::Interrupted => continue,
823 written => break written,
824 }
825 };
826 if let Err(e) = &written
827 && matches!(
828 e.kind(),
829 ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted
830 )
831 {
832 incr!(names::rustls::WRITE_RESET);
833 }
834 written
835}
836
837#[cfg(test)]
838thread_local! {
839 static TLS_WRITES: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
840}
841
842/// How many `write_tls` calls holding at least one record the calling thread
843/// has issued through [`flush_tls`] so far.
844#[cfg(test)]
845pub(crate) fn tls_writes() -> usize {
846 TLS_WRITES.with(std::cell::Cell::get)
847}
848
849impl std::fmt::Debug for FrontRustls {
850 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
851 f.debug_struct("FrontRustls")
852 .field("stream", &self.stream)
853 .finish_non_exhaustive()
854 }
855}
856
857/// A failure [`rustls_socket_read`] hit, handed back rather than logged so the
858/// caller can render it with its full socket context: the helper borrows the
859/// stream mutably for the whole pass, which is exactly what
860/// `log_socket_context!` needs to read.
861#[derive(Debug)]
862enum RustlsReadFault {
863 /// The pass ran [`MAX_LOOP_ITERATIONS`] times without settling.
864 LoopLimit,
865 /// `read_tls` failed with something other than `WouldBlock`, a reset, an
866 /// interruption (retried), or rustls's own "plaintext buffer full".
867 ReadTls(std::io::Error),
868 /// `process_new_packets` rejected what `read_tls` delivered.
869 ProcessPackets(rustls::Error),
870 /// The plaintext reader failed with something other than `WouldBlock` or
871 /// a reset.
872 Plaintext(std::io::Error),
873}
874
875/// A [`Read`] adapter that records whether its last read returned fewer bytes
876/// than it was offered.
877///
878/// `ServerConnection::read_tls` issues exactly one `read` into its deframer
879/// buffer (`rustls-0.23.45/src/msgs/deframer/buffers.rs:208`) and returns only
880/// the byte count, not the length it offered, so a short read is invisible
881/// from its return value. An `Ok(0)` is EOF, not a short read, and an error is
882/// neither.
883///
884/// Two callers wrap `read_tls` in it: [`rustls_socket_read`] once the session
885/// is established, and `handshake_read` (`lib/src/protocol/rustls.rs`) while
886/// the handshake runs.
887pub(crate) struct ShortReadProbe<'a, R> {
888 inner: &'a mut R,
889 short: bool,
890}
891
892impl<'a, R> ShortReadProbe<'a, R> {
893 pub(crate) fn new(inner: &'a mut R) -> Self {
894 ShortReadProbe {
895 inner,
896 short: false,
897 }
898 }
899
900 /// Whether the last read answered fewer bytes than it was offered, which
901 /// on a stream socket proves its receive queue empty (see
902 /// [`plain_socket_read`] for the one exception, TCP urgent data).
903 pub(crate) fn was_short(&self) -> bool {
904 self.short
905 }
906}
907
908impl<R: Read> Read for ShortReadProbe<'_, R> {
909 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
910 let read = self.inner.read(buf)?;
911 self.short = 0 < read && read < buf.len();
912 Ok(read)
913 }
914}
915
916/// Body of [`FrontRustls::socket_read`], generic over the transport so tests
917/// can count every read the pass issues against it.
918///
919/// Each pass drains the plaintext rustls already decrypted, and only then —
920/// if the caller's buffer is still not full — calls `read_tls` (one `recv(2)`)
921/// and `process_new_packets`. That order is what tokio-rustls follows
922/// (`tokio-rustls-0.26.4/src/common/mod.rs:198-212`: `read_tls` only while
923/// `wants_read()`, then the plaintext), and what OpenSSL gives HAProxy for
924/// free: `SSL_read` serves an already-decrypted record without touching the
925/// socket.
926///
927/// Readiness contract, unchanged by that order:
928///
929/// - every `recv` that answers EAGAIN ends the call with
930/// [`SocketResult::WouldBlock`], because the drain ran first and so the
931/// buffer is not full; the mux then drops READABLE until the next mio
932/// event (`update_readiness`), which is the edge-triggered re-arm;
933/// - so does a `read_tls` whose `recv` answered fewer bytes than rustls
934/// offered (4096, `rustls-0.23.45/src/msgs/deframer/buffers.rs:220`): the
935/// receive queue is empty, as for [`plain_socket_read`], so the pass makes
936/// no second `recv` that could only answer EAGAIN. It still processes what
937/// arrived and drains the plaintext, and answers `WouldBlock` only when
938/// the buffer is still not full, and `Closed` when that arrival carried
939/// `close_notify`. [`ShortReadProbe`] sees the offered length, which
940/// `read_tls` does not return;
941/// - [`SocketResult::Continue`] with a full buffer means "more may be
942/// buffered", so READABLE stays and the next call drains before it reads;
943/// - a `read_tls` that fills what rustls offered but completes no record
944/// produces no plaintext, so the pass loops and reads again until a short
945/// read, EAGAIN, EOF or a full buffer;
946/// - one record can hold several H2 frames: the later calls are served from
947/// the plaintext buffer without a `recv`, and the call that empties that
948/// buffer makes no `recv` either when an earlier call's `recv` answered
949/// short or EAGAIN and no readiness event was delivered since: `memory`
950/// carries that proof ([`RecvMemory`]), so the call answers `WouldBlock`
951/// without touching the socket. Once HUP or ERROR has been delivered the
952/// memory is off, and a call that finds the plaintext empty reads.
953///
954/// [`SocketResult::Error`] from `process_new_packets` is sticky: the failing
955/// pass still reports the plaintext it had already copied into `buf` before
956/// reaching `read_tls` (a `(n, Error)` return), but it sets `tls_fatal`, and
957/// every later pass answers `(0, Error)` without draining anything rustls
958/// decrypted before the failure.
959///
960/// EOF and `close_notify` are reported as [`SocketResult::Closed`] by the
961/// first call that finds no plaintext left to deliver, never behind it: a
962/// peer's last frames are handed over before the close. A call the buffered
963/// plaintext can satisfy completely returns `Continue` without looking at
964/// the socket, so a FIN that is already queued is seen one call later than
965/// when every call began with `read_tls`. A TCP FIN queued behind a short
966/// read is likewise seen by the next call, which the mux makes once it has
967/// seen HUP (`update_readiness_after_read`), and which reads because that HUP
968/// turned the memory off.
969fn rustls_socket_read<R: Read>(
970 session: &mut ServerConnection,
971 stream: &mut R,
972 buf: &mut [u8],
973 peer_disconnected: &mut bool,
974 peer_reset: &mut bool,
975 tls_fatal: &mut bool,
976 memory: &mut RecvMemory,
977) -> (usize, SocketResult, Option<RustlsReadFault>) {
978 // `Error` is sticky. Once `process_new_packets` has failed, plaintext it
979 // appended from records that preceded the bad one may still sit in the
980 // reader; serving it would hand the mux frames from a session that is
981 // already dead, and the mux only drops READABLE on `Error`, so the next
982 // peer bytes would be parsed. The failure was logged on the pass that
983 // hit it; later passes answer silently.
984 if *tls_fatal {
985 return (0, SocketResult::Error, None);
986 }
987 let mut size = 0usize;
988 let mut can_read = true;
989 // Seeded from the memory: a `recv` in an earlier call may already have
990 // proved the queue empty, with no event delivered since.
991 let mut drained = memory.drained && !memory.hup_seen;
992 let mut is_error = false;
993 let mut is_closed = false;
994 let mut fault = None;
995
996 let mut counter = 0;
997 loop {
998 counter += 1;
999 if counter > MAX_LOOP_ITERATIONS {
1000 fault = Some(RustlsReadFault::LoopLimit);
1001 is_error = true;
1002 break;
1003 }
1004
1005 // Plaintext first: rustls may already hold decrypted bytes from a
1006 // record an earlier call read — under H2, which asks for one 9-byte
1007 // frame header and then one payload at a time, the rest of that
1008 // record's frames. Serving them before `read_tls` is what keeps a
1009 // call the buffer can satisfy from issuing a `recv(2)` that can only
1010 // answer EAGAIN. `wants_read()` is false exactly while plaintext is
1011 // pending or `close_notify` has been received
1012 // (rustls-0.23.45/src/common_state.rs:674-684); in the second case the
1013 // reader answers `Ok(0)` and the `read_tls` below reports it.
1014 while !session.wants_read() {
1015 match session.reader().read(&mut buf[size..]) {
1016 Ok(0) => break,
1017 Ok(sz) => {
1018 // The rustls reader cannot return more plaintext than
1019 // the remaining slice it was handed.
1020 debug_assert!(
1021 sz <= buf.len() - size,
1022 "rustls reader returned {sz} bytes into a {}-byte remaining slice",
1023 buf.len() - size
1024 );
1025 size += sz;
1026 }
1027 Err(e) => match e.kind() {
1028 ErrorKind::WouldBlock => {
1029 break;
1030 }
1031 ErrorKind::ConnectionReset
1032 | ErrorKind::ConnectionAborted
1033 | ErrorKind::BrokenPipe => {
1034 is_closed = true;
1035 break;
1036 }
1037 _ => {
1038 fault = Some(RustlsReadFault::Plaintext(e));
1039 is_error = true;
1040 break;
1041 }
1042 },
1043 }
1044 }
1045
1046 // Loop invariant: the plaintext cursor never overshoots the caller's
1047 // buffer, so the next pass's `&mut buf[size..]` is a valid slice.
1048 debug_assert!(
1049 size <= buf.len(),
1050 "rustls read cursor {size} overran buffer len {} (would slice out of bounds)",
1051 buf.len()
1052 );
1053 if size == buf.len() {
1054 break;
1055 }
1056
1057 if !can_read | is_error | is_closed {
1058 break;
1059 }
1060
1061 // A short or EAGAIN `recv` earlier in this pass — or in an earlier
1062 // call, with no event delivered since (`RecvMemory`) — emptied the
1063 // receive queue, so a `read_tls` that would `recv` again can only
1064 // answer EAGAIN. One that
1065 // would not — `close_notify` received, which is exactly when
1066 // `wants_read()` is false with the plaintext drained — still runs:
1067 // it answers `Ok(0)` without touching the socket
1068 // (rustls-0.23.45/src/conn.rs:768-770) and reports the close.
1069 if drained && session.wants_read() {
1070 can_read = false;
1071 break;
1072 }
1073
1074 // rustls hands an interrupted `recv` (`EINTR`) back from `read_tls`
1075 // (only its `complete_io` retries it), and it says nothing about the
1076 // socket, so it is retried at once, as [`plain_socket_read`] does. The
1077 // probe only records a successful read, so the retry leaves
1078 // `was_short` exact.
1079 let mut probe = ShortReadProbe::new(&mut *stream);
1080 let read = loop {
1081 match session.read_tls(&mut probe) {
1082 Err(e) if e.kind() == ErrorKind::Interrupted => continue,
1083 read => break read,
1084 }
1085 };
1086 drained |= probe.was_short();
1087 match read {
1088 Ok(0) => {
1089 // Graceful FIN on the read side: peer closed its write
1090 // half. Keep `peer_reset` unset so outbound writes can
1091 // still flush rustls's buffered records (half-close).
1092 can_read = false;
1093 is_closed = true;
1094 *peer_disconnected = true;
1095 }
1096 Ok(_sz) => {}
1097 Err(e) => match e.kind() {
1098 ErrorKind::WouldBlock => {
1099 can_read = false;
1100 drained = true;
1101 }
1102 ErrorKind::ConnectionReset
1103 | ErrorKind::ConnectionAborted
1104 | ErrorKind::BrokenPipe => {
1105 // Full RST/abort: the TCP channel is dead. Mark
1106 // `peer_reset` so writes short-circuit (nothing can
1107 // reach the peer anymore) but still set
1108 // `peer_disconnected` for back-compatible read-side
1109 // logic.
1110 //
1111 // The reset is counted here, the only arm that reads the
1112 // socket, and once: `peer_reset` stops every later write
1113 // before it reaches the socket (sozu-proxy/sozu#434).
1114 // `EPIPE` is not a reset this `recv` observed.
1115 if e.kind() != ErrorKind::BrokenPipe {
1116 incr!(names::rustls::READ_RESET);
1117 }
1118 is_closed = true;
1119 *peer_disconnected = true;
1120 *peer_reset = true;
1121 }
1122 // rustls-0.23.45/src/conn.rs:761-766: rustls's 16 KB
1123 // received_plaintext buffer is full. The drain above empties
1124 // it before every `read_tls` unless the caller's buffer
1125 // filled first, which breaks out before reaching here, so
1126 // this arm is defensive; the next pass drains again.
1127 ErrorKind::Other => {}
1128 _ => {
1129 fault = Some(RustlsReadFault::ReadTls(e));
1130 is_error = true;
1131 break;
1132 }
1133 },
1134 }
1135
1136 if let Err(e) = session.process_new_packets() {
1137 fault = Some(RustlsReadFault::ProcessPackets(e));
1138 *tls_fatal = true;
1139 is_error = true;
1140 break;
1141 }
1142 }
1143
1144 // Post-condition: we never report more plaintext than the caller asked
1145 // for, and Error/Closed are mutually exclusive (the loop `break`s on the
1146 // first one set, so both can never be true on the same pass).
1147 debug_assert!(
1148 size <= buf.len(),
1149 "rustls socket_read returned {size} bytes for a {}-byte buffer",
1150 buf.len()
1151 );
1152 debug_assert!(
1153 !(is_error && is_closed),
1154 "rustls socket_read cannot be both Error and Closed"
1155 );
1156 // Carried to the next call; `FrontRustls::readiness_delivered` clears it.
1157 memory.drained = drained;
1158 let result = if is_error {
1159 SocketResult::Error
1160 } else if is_closed {
1161 SocketResult::Closed
1162 } else if size == buf.len() {
1163 // The full requested amount was read (possibly from the rustls
1164 // plaintext buffer). Report Continue so the caller keeps
1165 // READABLE in the readiness set — there may be more decrypted
1166 // data available without a new mio event.
1167 SocketResult::Continue
1168 } else if !can_read {
1169 SocketResult::WouldBlock
1170 } else {
1171 SocketResult::Continue
1172 };
1173 (size, result, fault)
1174}
1175
1176impl SocketHandler for FrontRustls {
1177 fn socket_read(&mut self, buf: &mut [u8]) -> (usize, SocketResult) {
1178 let (size, result, fault) = rustls_socket_read(
1179 &mut self.session,
1180 &mut self.stream,
1181 buf,
1182 &mut self.peer_disconnected,
1183 &mut self.peer_reset,
1184 &mut self.tls_fatal,
1185 &mut self.recv_memory,
1186 );
1187 match fault {
1188 None => {}
1189 Some(RustlsReadFault::LoopLimit) => {
1190 error!(
1191 "{} MAX_LOOP_ITERATION reached in FrontRustls::socket_read",
1192 log_socket_context!(self)
1193 );
1194 incr!(names::rustls::READ_INFINITE_LOOP_ERROR);
1195 }
1196 Some(RustlsReadFault::ReadTls(e)) => error!(
1197 "{} could not read TLS stream from socket: {:?}",
1198 log_socket_context!(self),
1199 e
1200 ),
1201 Some(RustlsReadFault::ProcessPackets(e)) => error!(
1202 "{} could not process read TLS packets: {:?}",
1203 log_socket_context!(self),
1204 e
1205 ),
1206 Some(RustlsReadFault::Plaintext(e)) => error!(
1207 "{} could not read data from TLS stream: {:?}",
1208 log_socket_context!(self),
1209 e
1210 ),
1211 }
1212 (size, result)
1213 }
1214
1215 /// Keep these two functions structurally symmetric — a divergence
1216 /// caused the 4.5 MB H2 truncation bug. Tests
1217 /// `e2e::tests::h2_correctness_tests::*` and
1218 /// `e2e::tests::h2_tests::test_h2_large_*` are the regression guard.
1219 fn socket_write(&mut self, buf: &[u8]) -> (usize, SocketResult) {
1220 // Abort only on a true RST — a FIN on the read side still permits
1221 // flushing rustls's plaintext buffer (TLS half-close).
1222 if self.peer_reset {
1223 return (0, SocketResult::Closed);
1224 }
1225
1226 let mut buffered_size = 0usize;
1227 let mut can_write = true;
1228 let mut is_error = false;
1229 let mut is_closed = false;
1230
1231 let mut counter = 0;
1232 loop {
1233 counter += 1;
1234 if counter > MAX_LOOP_ITERATIONS {
1235 error!(
1236 "{} MAX_LOOP_ITERATION reached in FrontRustls::socket_write",
1237 log_socket_context!(self)
1238 );
1239 incr!(names::rustls::WRITE_INFINITE_LOOP_ERROR);
1240 is_error = true;
1241 break;
1242 }
1243 // Loop invariant: the absorbed-plaintext cursor never overshoots the
1244 // caller's buffer, so `&buf[buffered_size..]` is always a valid slice.
1245 debug_assert!(
1246 buffered_size <= buf.len(),
1247 "rustls write cursor {buffered_size} overran buffer len {} (would slice out of bounds)",
1248 buf.len()
1249 );
1250 if buffered_size == buf.len() {
1251 break;
1252 }
1253
1254 if !can_write | is_error | is_closed {
1255 break;
1256 }
1257
1258 match self.session.writer().write(&buf[buffered_size..]) {
1259 Ok(0) => {} // zero byte written means that the Rustls buffers are full, we will try to write on the socket and try again
1260 Ok(sz) => {
1261 // rustls cannot absorb more plaintext than the remaining slice.
1262 debug_assert!(
1263 sz <= buf.len() - buffered_size,
1264 "rustls writer absorbed {sz} bytes from a {}-byte remaining slice",
1265 buf.len() - buffered_size
1266 );
1267 buffered_size += sz;
1268 }
1269 Err(e) => match e.kind() {
1270 ErrorKind::WouldBlock => {
1271 // we don't need to do anything, the session will return false in wants_write?
1272 //error!("rustls socket_write wouldblock");
1273 }
1274 ErrorKind::ConnectionReset
1275 | ErrorKind::ConnectionAborted
1276 | ErrorKind::BrokenPipe => {
1277 //FIXME: this should probably not happen here
1278 incr!(names::rustls::WRITE_ERROR);
1279 is_closed = true;
1280 self.peer_reset = true;
1281 break;
1282 }
1283 _ => {
1284 error!(
1285 "{} could not write data to TLS stream: {:?}",
1286 log_socket_context!(self),
1287 e
1288 );
1289 incr!(names::rustls::WRITE_ERROR);
1290 is_error = true;
1291 break;
1292 }
1293 },
1294 }
1295
1296 loop {
1297 match flush_tls(&mut self.session, &mut self.stream) {
1298 Ok(0) => {
1299 //can_write = false;
1300 break;
1301 }
1302 Ok(_sz) => {}
1303 Err(e) => match e.kind() {
1304 ErrorKind::WouldBlock => {
1305 can_write = false;
1306 break;
1307 }
1308 ErrorKind::ConnectionReset
1309 | ErrorKind::ConnectionAborted
1310 | ErrorKind::BrokenPipe => {
1311 incr!(names::rustls::WRITE_ERROR);
1312 is_closed = true;
1313 self.peer_reset = true;
1314 break;
1315 }
1316 _ => {
1317 error!(
1318 "{} could not write TLS stream to socket: {:?}",
1319 log_socket_context!(self),
1320 e
1321 );
1322 incr!(names::rustls::WRITE_ERROR);
1323 is_error = true;
1324 break;
1325 }
1326 },
1327 }
1328 }
1329 }
1330
1331 // Flush any pending TLS records even if no application data was written.
1332 // This handles the case where h2.rs calls socket_write(&[]) to flush
1333 // buffered TLS data (e.g. NewSessionTicket, key updates). Without this,
1334 // the main loop above exits immediately for empty buffers and write_tls
1335 // is never called.
1336 if !is_error && !is_closed && can_write && self.session.wants_write() {
1337 loop {
1338 match flush_tls(&mut self.session, &mut self.stream) {
1339 Ok(0) => break,
1340 Ok(_) => {}
1341 Err(e) => match e.kind() {
1342 ErrorKind::WouldBlock => {
1343 can_write = false;
1344 break;
1345 }
1346 ErrorKind::ConnectionReset
1347 | ErrorKind::ConnectionAborted
1348 | ErrorKind::BrokenPipe => {
1349 incr!(names::rustls::WRITE_ERROR);
1350 is_closed = true;
1351 self.peer_reset = true;
1352 break;
1353 }
1354 _ => {
1355 error!(
1356 "{} could not flush TLS stream to socket: {:?}",
1357 log_socket_context!(self),
1358 e
1359 );
1360 incr!(names::rustls::WRITE_ERROR);
1361 is_error = true;
1362 break;
1363 }
1364 },
1365 }
1366 }
1367 }
1368
1369 // Post-condition: we never report absorbing more plaintext than the
1370 // caller handed us — over-reporting is exactly the truncation-class bug
1371 // these two symmetric paths exist to avoid.
1372 debug_assert!(
1373 buffered_size <= buf.len(),
1374 "rustls socket_write reported {buffered_size} bytes for a {}-byte buffer",
1375 buf.len()
1376 );
1377 debug_assert!(
1378 !(is_error && is_closed),
1379 "rustls socket_write cannot be both Error and Closed"
1380 );
1381 if is_error {
1382 // Whatever failed — the socket, rustls, or the loop budget — no
1383 // later write can deliver what rustls holds: see `peer_reset`.
1384 self.peer_reset = true;
1385 (buffered_size, SocketResult::Error)
1386 } else if is_closed {
1387 (buffered_size, SocketResult::Closed)
1388 } else if !can_write {
1389 (buffered_size, SocketResult::WouldBlock)
1390 } else {
1391 (buffered_size, SocketResult::Continue)
1392 }
1393 }
1394
1395 /// Write a list of plaintext slices through the rustls session.
1396 ///
1397 /// Empty-buffer invariant: callers may legitimately pass `bufs.is_empty()`
1398 /// or an all-empty slice to request a pure flush pass. In that case
1399 /// `total_len == 0`, the top-of-loop `buffered_size == total_len` guard
1400 /// fires immediately after `write_tls` drains any pending TLS records the
1401 /// session still has buffered (e.g. the remainder of a record split by
1402 /// the previous call, or `close_notify` output). This mirrors
1403 /// [`Self::socket_write`]: both entry points must stay structurally
1404 /// symmetric so that a zero-byte flush never early-returns without giving
1405 /// rustls a chance to emit bytes.
1406 ///
1407 /// Keep these two functions structurally symmetric — a divergence
1408 /// caused the 4.5 MB H2 truncation bug. Tests
1409 /// `e2e::tests::h2_correctness_tests::*` and
1410 /// `e2e::tests::h2_tests::test_h2_large_*` are the regression guard.
1411 fn socket_write_vectored(&mut self, bufs: &[std::io::IoSlice]) -> (usize, SocketResult) {
1412 if self.peer_reset {
1413 return (0, SocketResult::Closed);
1414 }
1415
1416 let total_len: usize = bufs.iter().map(|b| b.len()).sum();
1417 let mut buffered_size = 0usize;
1418 let mut can_write = true;
1419 let mut is_error = false;
1420 let mut is_closed = false;
1421
1422 let mut counter = 0;
1423 loop {
1424 counter += 1;
1425 if counter > MAX_LOOP_ITERATIONS {
1426 error!(
1427 "{} MAX_LOOP_ITERATION reached in FrontRustls::socket_write_vectored",
1428 log_socket_context!(self)
1429 );
1430 incr!(names::rustls::WRITE_INFINITE_LOOP_ERROR);
1431 is_error = true;
1432 break;
1433 }
1434 // Loop invariant: the absorbed-plaintext cursor never overshoots the
1435 // summed slice length we computed up front (mirrors the scalar path).
1436 debug_assert!(
1437 buffered_size <= total_len,
1438 "rustls vectored write cursor {buffered_size} overran total slice len {total_len}"
1439 );
1440 if buffered_size == total_len {
1441 break;
1442 }
1443
1444 if !can_write | is_error | is_closed {
1445 break;
1446 }
1447
1448 // rustls's Writer does not expose a "write from offset across slices"
1449 // helper, so we push plaintext once and then drain via write_tls.
1450 // If rustls only partially absorbs the slices, we break and return
1451 // the partial count so the caller can advance its buffers and retry.
1452 if buffered_size == 0 {
1453 match self.session.writer().write_vectored(bufs) {
1454 Ok(0) => {}
1455 Ok(sz) => {
1456 // rustls cannot absorb more plaintext than the slices held.
1457 debug_assert!(
1458 sz <= total_len,
1459 "rustls writer absorbed {sz} bytes from {total_len}-byte slices"
1460 );
1461 buffered_size += sz;
1462 }
1463 Err(e) => match e.kind() {
1464 ErrorKind::WouldBlock => {}
1465 ErrorKind::ConnectionReset
1466 | ErrorKind::ConnectionAborted
1467 | ErrorKind::BrokenPipe => {
1468 incr!(names::rustls::WRITE_ERROR);
1469 is_closed = true;
1470 self.peer_reset = true;
1471 break;
1472 }
1473 _ => {
1474 error!(
1475 "{} could not write data to TLS stream: {:?}",
1476 log_socket_context!(self),
1477 e
1478 );
1479 incr!(names::rustls::WRITE_ERROR);
1480 is_error = true;
1481 break;
1482 }
1483 },
1484 }
1485 }
1486
1487 // Plaintext was partially absorbed — we cannot re-call write_vectored
1488 // because the IoSlice pointers have not been advanced. Drain whatever
1489 // rustls buffered to the socket, then return the partial count so the
1490 // caller can consume and retry with adjusted slices.
1491 if buffered_size > 0 && buffered_size < total_len {
1492 loop {
1493 match flush_tls(&mut self.session, &mut self.stream) {
1494 Ok(0) => break,
1495 Ok(_) => {}
1496 Err(e) => match e.kind() {
1497 ErrorKind::WouldBlock => {
1498 can_write = false;
1499 break;
1500 }
1501 ErrorKind::ConnectionReset
1502 | ErrorKind::ConnectionAborted
1503 | ErrorKind::BrokenPipe => {
1504 incr!(names::rustls::WRITE_ERROR);
1505 is_closed = true;
1506 self.peer_reset = true;
1507 break;
1508 }
1509 _ => {
1510 error!(
1511 "{} could not write TLS stream to socket: {:?}",
1512 log_socket_context!(self),
1513 e
1514 );
1515 incr!(names::rustls::WRITE_ERROR);
1516 is_error = true;
1517 break;
1518 }
1519 },
1520 }
1521 }
1522 break;
1523 }
1524
1525 loop {
1526 match flush_tls(&mut self.session, &mut self.stream) {
1527 Ok(0) => {
1528 break;
1529 }
1530 Ok(_sz) => {}
1531 Err(e) => match e.kind() {
1532 ErrorKind::WouldBlock => {
1533 can_write = false;
1534 break;
1535 }
1536 ErrorKind::ConnectionReset
1537 | ErrorKind::ConnectionAborted
1538 | ErrorKind::BrokenPipe => {
1539 incr!(names::rustls::WRITE_ERROR);
1540 is_closed = true;
1541 self.peer_reset = true;
1542 break;
1543 }
1544 _ => {
1545 error!(
1546 "{} could not write TLS stream to socket: {:?}",
1547 log_socket_context!(self),
1548 e
1549 );
1550 incr!(names::rustls::WRITE_ERROR);
1551 is_error = true;
1552 break;
1553 }
1554 },
1555 }
1556 }
1557 }
1558
1559 if !is_error && !is_closed && can_write && self.session.wants_write() {
1560 loop {
1561 match flush_tls(&mut self.session, &mut self.stream) {
1562 Ok(0) => break,
1563 Ok(_) => {}
1564 Err(e) => match e.kind() {
1565 ErrorKind::WouldBlock => {
1566 can_write = false;
1567 break;
1568 }
1569 ErrorKind::ConnectionReset
1570 | ErrorKind::ConnectionAborted
1571 | ErrorKind::BrokenPipe => {
1572 incr!(names::rustls::WRITE_ERROR);
1573 is_closed = true;
1574 self.peer_reset = true;
1575 break;
1576 }
1577 _ => {
1578 error!(
1579 "{} could not flush TLS stream to socket: {:?}",
1580 log_socket_context!(self),
1581 e
1582 );
1583 incr!(names::rustls::WRITE_ERROR);
1584 is_error = true;
1585 break;
1586 }
1587 },
1588 }
1589 }
1590 }
1591
1592 // Post-condition: report no more than the summed slice length, and keep
1593 // Error/Closed mutually exclusive — must stay structurally symmetric with
1594 // `socket_write` (divergence here is the 4.5 MB truncation-class bug).
1595 debug_assert!(
1596 buffered_size <= total_len,
1597 "rustls socket_write_vectored reported {buffered_size} bytes for {total_len}-byte slices"
1598 );
1599 debug_assert!(
1600 !(is_error && is_closed),
1601 "rustls socket_write_vectored cannot be both Error and Closed"
1602 );
1603 if is_error {
1604 // Same as `socket_write`: an `Error` always marks the channel dead.
1605 self.peer_reset = true;
1606 (buffered_size, SocketResult::Error)
1607 } else if is_closed {
1608 (buffered_size, SocketResult::Closed)
1609 } else if !can_write {
1610 (buffered_size, SocketResult::WouldBlock)
1611 } else {
1612 (buffered_size, SocketResult::Continue)
1613 }
1614 }
1615
1616 fn socket_close(&mut self) {
1617 self.session.send_close_notify();
1618 }
1619
1620 /// Queue `buf` and the `close_notify` behind it, then flush both with one
1621 /// `write_tls`, which hands every pending record to one `write_vectored`
1622 /// (`rustls-0.23.45/src/vecbuf.rs`, `ChunkVecBuffer::write_to`).
1623 ///
1624 /// The plaintext must be queued first: rustls encrypts it into the send
1625 /// buffer at once (the handshake is over on every connection the mux
1626 /// drives), and the alert queued after it is the next record on the wire,
1627 /// so the peer reads the bytes and then the close. The other order would
1628 /// end the peer's reading before the bytes.
1629 ///
1630 /// When rustls does not take all of `buf` (its send buffer is full) the
1631 /// alert is not queued, as the trait requires, and the caller's next call
1632 /// carries the rest. A write error falls back to [`Self::socket_write`],
1633 /// which reports it with the socket context.
1634 fn socket_write_then_close(&mut self, buf: &[u8]) -> (usize, SocketResult) {
1635 if self.peer_reset {
1636 return (0, SocketResult::Closed);
1637 }
1638 match self.session.writer().write(buf) {
1639 Ok(absorbed) => {
1640 if absorbed == buf.len() {
1641 self.session.send_close_notify();
1642 }
1643 let (_flushed, status) = self.socket_write(&[]);
1644 (absorbed, status)
1645 }
1646 Err(_) => self.socket_write(buf),
1647 }
1648 }
1649
1650 fn socket_wants_write(&self) -> bool {
1651 // Only a dead channel (a RST, or a failed socket write) stops us
1652 // wanting to write — a peer FIN still allows flushing TLS plaintext
1653 // buffered in rustls (half-close).
1654 !self.peer_reset && self.session.wants_write()
1655 }
1656
1657 fn socket_ref(&self) -> &TcpStream {
1658 &self.stream
1659 }
1660
1661 fn socket_mut(&mut self) -> &mut TcpStream {
1662 &mut self.stream
1663 }
1664
1665 /// No production route reaches the fallback any more. The expect-proxy
1666 /// route gets there only through `upgrade_expect`, which returns `None`
1667 /// unless both PROXY addresses parse, so `peer_address` is `Some` by the
1668 /// time a handshake exists; the direct route seeds it in
1669 /// `HttpsSession::new` (`https.rs`) with the address `accept(2)` returned
1670 /// with the socket, which cannot fail the way `getpeername(2)` can. The
1671 /// arm stays as defence in depth: a handler built without a peer would
1672 /// otherwise blank the `peer=` slot of a healthy connection. Do not
1673 /// delete it — `https::tests::front_rustls_peer_addr_falls_back_to_the_live_lookup`
1674 /// builds such a handler by hand and fails without it.
1675 fn peer_addr(&self) -> Option<SocketAddr> {
1676 self.configured_peer
1677 .or_else(|| self.stream.peer_addr().ok())
1678 }
1679
1680 fn protocol(&self) -> TransportProtocol {
1681 self.session
1682 .protocol_version()
1683 .map(|version| match version {
1684 ProtocolVersion::SSLv2 => TransportProtocol::Ssl2,
1685 ProtocolVersion::SSLv3 => TransportProtocol::Ssl3,
1686 ProtocolVersion::TLSv1_0 => TransportProtocol::Tls1_0,
1687 ProtocolVersion::TLSv1_1 => TransportProtocol::Tls1_1,
1688 ProtocolVersion::TLSv1_2 => TransportProtocol::Tls1_2,
1689 ProtocolVersion::TLSv1_3 => TransportProtocol::Tls1_3,
1690 _ => TransportProtocol::Tls1_3,
1691 })
1692 .unwrap_or(TransportProtocol::Tcp)
1693 }
1694
1695 fn read_error(&self) {
1696 incr!(names::rustls::READ_ERROR);
1697 }
1698
1699 fn write_error(&self) {
1700 incr!(names::rustls::WRITE_ERROR);
1701 }
1702
1703 fn session_ulid(&self) -> Option<Ulid> {
1704 Some(self.session_ulid)
1705 }
1706}
1707
1708/// Bind `sock`, the listening socket for `addr`, to the network `interface`
1709/// (`SO_BINDTODEVICE`), before `bind(2)`: the listener then accepts only what
1710/// arrives through that interface, whatever address it holds, which is what
1711/// lets a listener follow an interface whose address is not known ahead (a
1712/// WireGuard `wg0`). `None` leaves the socket unbound, as before interfaces
1713/// existed.
1714///
1715/// The option is set before `bind(2)` so that two listeners on the same
1716/// address, bound to different interfaces, can both bind under
1717/// `SO_REUSEADDR`/`SO_REUSEPORT`. It survives the SCM hand-off of an upgrade:
1718/// it is a property of the socket, not of the process.
1719fn bind_to_device(
1720 sock: &Socket,
1721 addr: SocketAddr,
1722 interface: Option<&str>,
1723) -> Result<(), ServerBindError> {
1724 let Some(interface) = interface else {
1725 return Ok(());
1726 };
1727 #[cfg(target_os = "linux")]
1728 {
1729 sock.bind_device(Some(interface.as_bytes()))
1730 .map_err(|error| ServerBindError::BindToDevice {
1731 address: addr,
1732 interface: interface.to_owned(),
1733 error,
1734 })?;
1735 debug_assert!(
1736 sock.device().map_or(true, |device| device.as_deref()
1737 == Some(interface.as_bytes())),
1738 "a socket bound to an interface must report that interface"
1739 );
1740 Ok(())
1741 }
1742 #[cfg(not(target_os = "linux"))]
1743 {
1744 let _ = sock;
1745 Err(ServerBindError::BindToDeviceUnsupported {
1746 address: addr,
1747 interface: interface.to_owned(),
1748 })
1749 }
1750}
1751
1752pub fn server_bind(
1753 addr: SocketAddr,
1754 interface: Option<&str>,
1755) -> Result<TcpListener, ServerBindError> {
1756 let sock = Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))
1757 .map_err(ServerBindError::SocketCreationError)?;
1758
1759 bind_to_device(&sock, addr, interface)?;
1760
1761 // set so_reuseaddr, but only on unix (mirrors what libstd does)
1762 if cfg!(unix) {
1763 sock.set_reuse_address(true)
1764 .map_err(ServerBindError::SetReuseAddress)?;
1765 }
1766
1767 sock.set_reuse_port(true)
1768 .map_err(ServerBindError::SetReusePort)?;
1769
1770 sock.bind(&addr.into())
1771 .map_err(ServerBindError::BindError)?;
1772
1773 sock.set_nonblocking(true)
1774 .map_err(ServerBindError::SetNonBlocking)?;
1775
1776 // listen
1777 // FIXME: make the backlog configurable?
1778 sock.listen(1024).map_err(ServerBindError::Listen)?;
1779
1780 // Post-conditions (invariant violations only — every fallible syscall above
1781 // already returns an error; these `debug_assert!`s catch a flag we *set*
1782 // silently not sticking, which would be our own logic bug, not a syscall
1783 // failure on network input). The getters return `io::Result`; we only
1784 // assert when the kernel answers, degrading to a no-op on the rare query
1785 // failure so we never panic on a dying fd.
1786 if let Ok(nonblocking) = sock.nonblocking() {
1787 debug_assert!(
1788 nonblocking,
1789 "server_bind must return a non-blocking socket (the worker event loop is edge-triggered)"
1790 );
1791 }
1792 // `SO_REUSEPORT` is set on every platform; assert it stuck so a SCM hand-off
1793 // across a hot-upgrade can re-bind the same address.
1794 #[cfg(unix)]
1795 if let Ok(reuse_port) = sock.reuse_port() {
1796 debug_assert!(
1797 reuse_port,
1798 "server_bind must set SO_REUSEPORT so the listener survives a hot-upgrade re-bind"
1799 );
1800 }
1801 // `SO_REUSEADDR` is unix-only here (mirrors libstd).
1802 #[cfg(unix)]
1803 if let Ok(reuse_address) = sock.reuse_address() {
1804 debug_assert!(
1805 reuse_address,
1806 "server_bind must set SO_REUSEADDR on unix (mirrors libstd)"
1807 );
1808 }
1809 // A bound STREAM socket carries a local address in the requested family.
1810 if let Ok(local) = sock.local_addr() {
1811 debug_assert_eq!(
1812 local.is_ipv4(),
1813 addr.is_ipv4(),
1814 "bound socket family must match the requested address family"
1815 );
1816 debug_assert_eq!(
1817 local.is_ipv6(),
1818 addr.is_ipv6(),
1819 "bound socket family must match the requested address family"
1820 );
1821 }
1822
1823 Ok(TcpListener::from_std(sock.into()))
1824}
1825
1826/// Bind a non-blocking UDP listener socket on `addr`.
1827///
1828/// Mirrors [`server_bind`] but for DGRAM: `SO_REUSEADDR` (unix) + `SO_REUSEPORT`
1829/// so the socket can be SCM-passed and re-bound across a hot-upgrade, then
1830/// `bind` + non-blocking. Unlike TCP there is **no `listen()`** — a UDP socket
1831/// receives datagrams directly. The returned `mio::net::UdpSocket` is the one
1832/// listener socket the UDP datapath demuxes many flows over (one-socket-many-
1833/// flows; per-flow return sockets are created by [`udp_connect`]).
1834pub fn udp_bind(addr: SocketAddr, interface: Option<&str>) -> Result<UdpSocket, ServerBindError> {
1835 let sock = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))
1836 .map_err(ServerBindError::SocketCreationError)?;
1837
1838 bind_to_device(&sock, addr, interface)?;
1839
1840 // set so_reuseaddr, but only on unix (mirrors what libstd does)
1841 if cfg!(unix) {
1842 sock.set_reuse_address(true)
1843 .map_err(ServerBindError::SetReuseAddress)?;
1844 }
1845
1846 sock.set_reuse_port(true)
1847 .map_err(ServerBindError::SetReusePort)?;
1848
1849 sock.bind(&addr.into())
1850 .map_err(ServerBindError::BindError)?;
1851
1852 sock.set_nonblocking(true)
1853 .map_err(ServerBindError::SetNonBlocking)?;
1854
1855 // No `listen()` for DGRAM sockets.
1856
1857 // Post-conditions — same rationale as `server_bind`: assert the flags we set
1858 // stuck (logic bug if not), degrading to a no-op when the kernel refuses the
1859 // query so a dying fd never panics. There is deliberately no `listen()`
1860 // check here: DGRAM sockets are never listened on.
1861 if let Ok(nonblocking) = sock.nonblocking() {
1862 debug_assert!(
1863 nonblocking,
1864 "udp_bind must return a non-blocking socket (the worker event loop is edge-triggered)"
1865 );
1866 }
1867 #[cfg(unix)]
1868 if let Ok(reuse_port) = sock.reuse_port() {
1869 debug_assert!(
1870 reuse_port,
1871 "udp_bind must set SO_REUSEPORT so the listener survives a hot-upgrade re-bind"
1872 );
1873 }
1874 #[cfg(unix)]
1875 if let Ok(reuse_address) = sock.reuse_address() {
1876 debug_assert!(
1877 reuse_address,
1878 "udp_bind must set SO_REUSEADDR on unix (mirrors libstd / server_bind)"
1879 );
1880 }
1881 if let Ok(local) = sock.local_addr() {
1882 debug_assert_eq!(
1883 local.is_ipv4(),
1884 addr.is_ipv4(),
1885 "bound UDP socket family must match the requested address family"
1886 );
1887 debug_assert_eq!(
1888 local.is_ipv6(),
1889 addr.is_ipv6(),
1890 "bound UDP socket family must match the requested address family"
1891 );
1892 }
1893
1894 Ok(UdpSocket::from_std(sock.into()))
1895}
1896
1897/// Create a non-blocking **connected** per-flow upstream UDP socket toward
1898/// `backend`.
1899///
1900/// The socket is bound to an ephemeral local port (family matched to the
1901/// backend) and `connect`-ed to the backend address. A connected UDP socket
1902/// "only receives from the connected address" (`connect(2)`), so its fd is the
1903/// symmetric-NAT return-demux key for one flow: the shell registers
1904/// `upstream_token -> FlowId` and feeds anything that arrives on it back into
1905/// the manager as a `BackendDatagram`. `send` (not `send_to`) is then used for
1906/// the forward path. Errors (`EMFILE`/`ENFILE`/connect refusal) bubble up so
1907/// the caller can shed the flow rather than panic.
1908///
1909/// On Linux the socket is created non-blocking and close-on-exec in the one
1910/// `socket(2)` call (`SOCK_NONBLOCK | SOCK_CLOEXEC`; `Socket::new` adds the
1911/// latter), so a new flow costs `socket`, `bind`, `connect` and no `fcntl(2)`.
1912/// Other platforms keep the `set_nonblocking` round trip. The post-condition
1913/// queries (`fcntl(F_GETFL)`, `getsockname(2)`, `getpeername(2)`) only feed
1914/// `debug_assert!`s, so they are compiled out of release builds.
1915pub fn udp_connect(backend: SocketAddr) -> Result<UdpSocket, ServerBindError> {
1916 let unspecified: SocketAddr = match backend {
1917 SocketAddr::V4(_) => (std::net::Ipv4Addr::UNSPECIFIED, 0).into(),
1918 SocketAddr::V6(_) => (std::net::Ipv6Addr::UNSPECIFIED, 0).into(),
1919 };
1920 // The ephemeral bind address must be in the backend's family with port 0,
1921 // or the subsequent `connect` would mix families / pin a wrong local port.
1922 debug_assert_eq!(
1923 unspecified.is_ipv4(),
1924 backend.is_ipv4(),
1925 "ephemeral bind family must match the backend family"
1926 );
1927 debug_assert_eq!(
1928 unspecified.port(),
1929 0,
1930 "ephemeral bind must use port 0 so the kernel picks the source port"
1931 );
1932 // Linux takes `O_NONBLOCK` at creation, which saves the `F_GETFL`/`F_SETFL`
1933 // pair of `set_nonblocking` on every new flow.
1934 #[cfg(target_os = "linux")]
1935 let ty = Type::DGRAM.nonblocking();
1936 #[cfg(not(target_os = "linux"))]
1937 let ty = Type::DGRAM;
1938 let sock = Socket::new(Domain::for_address(backend), ty, Some(Protocol::UDP))
1939 .map_err(ServerBindError::SocketCreationError)?;
1940
1941 sock.bind(&unspecified.into())
1942 .map_err(ServerBindError::BindError)?;
1943 #[cfg(not(target_os = "linux"))]
1944 sock.set_nonblocking(true)
1945 .map_err(ServerBindError::SetNonBlocking)?;
1946 // `connect` on a DGRAM socket pins the return 4-tuple; a non-blocking
1947 // connect on UDP completes immediately (no handshake).
1948 sock.connect(&backend.into())
1949 .map_err(ServerBindError::BindError)?;
1950
1951 // Post-conditions — assert the flag/connect state stuck (logic bug if not),
1952 // degrading to a no-op when the kernel refuses the query so a dying fd never
1953 // panics on this network-facing path. Each query is a syscall that only
1954 // feeds a `debug_assert!`, so release builds skip them on this per-flow path.
1955 #[cfg(debug_assertions)]
1956 {
1957 if let Ok(nonblocking) = sock.nonblocking() {
1958 debug_assert!(
1959 nonblocking,
1960 "udp_connect must return a non-blocking socket (the worker event loop is edge-triggered)"
1961 );
1962 }
1963 // The connected return socket's local addr family must match the backend,
1964 // and the kernel must have assigned a concrete source port (no longer 0).
1965 if let Ok(local) = sock.local_addr() {
1966 debug_assert_eq!(
1967 local.is_ipv4(),
1968 backend.is_ipv4(),
1969 "connected UDP socket family must match the backend family"
1970 );
1971 if let Some(local) = local.as_socket() {
1972 debug_assert_ne!(
1973 local.port(),
1974 0,
1975 "connect must bind a concrete ephemeral source port (the return-demux key)"
1976 );
1977 }
1978 }
1979 // `connect` pinned the peer 4-tuple — `getpeername(2)` must echo the backend.
1980 if let Ok(peer) = sock.peer_addr()
1981 && let Some(peer) = peer.as_socket()
1982 {
1983 debug_assert_eq!(
1984 peer, backend,
1985 "connect must pin the peer to the requested backend (symmetric-NAT return-demux key)"
1986 );
1987 }
1988 }
1989
1990 Ok(UdpSocket::from_std(sock.into()))
1991}
1992
1993/// Socket statistics
1994pub mod stats {
1995 use std::{os::fd::AsRawFd, time::Duration};
1996
1997 use internal::{OPT_LEVEL, OPT_NAME, TcpInfo};
1998
1999 /// Point-in-time snapshot of kernel TCP bookkeeping for a socket. Populated
2000 /// from a single `getsockopt(TCP_INFO)` syscall so callers that want both
2001 /// the smoothed RTT and the FSM state don't pay for two trips into the
2002 /// kernel. Field set is deliberately narrow — extend with more `tcp_info`
2003 /// members if the log prefix grows.
2004 #[derive(Clone, Debug)]
2005 pub struct TcpSnapshot {
2006 pub rtt: Duration,
2007 pub state: &'static str,
2008 }
2009
2010 /// Round trip time for a TCP socket. Kept for existing metric callers;
2011 /// log-prefix callers should prefer [`socket_snapshot`] which returns the
2012 /// RTT **and** the TCP FSM state from a single syscall.
2013 pub fn socket_rtt<A: AsRawFd>(socket: &A) -> Option<Duration> {
2014 socket_info(socket.as_raw_fd()).map(|info| Duration::from_micros(info.rtt() as u64))
2015 }
2016
2017 /// Smoothed RTT + human-readable TCP state (`"ESTABLISHED"`, `"SYN_SENT"`,
2018 /// `"CLOSE_WAIT"`, …) pulled from a single `getsockopt(TCP_INFO)` call.
2019 /// Returns `None` when the kernel refuses the call — e.g. the socket has
2020 /// been closed, or the FSM is in a state where `TCP_INFO` is not usable.
2021 /// Safe on dying/refused sockets: the inner syscall's `status != 0`
2022 /// branch is the only failure mode and it degrades to `None`.
2023 pub fn socket_snapshot<A: AsRawFd>(socket: &A) -> Option<TcpSnapshot> {
2024 socket_info(socket.as_raw_fd()).map(|info| TcpSnapshot {
2025 rtt: Duration::from_micros(info.rtt() as u64),
2026 state: info.state(),
2027 })
2028 }
2029
2030 #[cfg(unix)]
2031 pub fn socket_info(fd: libc::c_int) -> Option<TcpInfo> {
2032 // SAFETY: `TcpInfo` is a C POD whose every byte pattern is a legal
2033 // representation; zero-init satisfies `assume_init`'s invariant
2034 // (and `std::mem::zeroed` is the canonical idiom for that).
2035 let mut tcp_info: TcpInfo = unsafe { std::mem::zeroed() };
2036 let struct_len = std::mem::size_of::<TcpInfo>() as libc::socklen_t;
2037 let mut len = struct_len;
2038 // SAFETY: `tcp_info` and `len` are fully initialised above; libc
2039 // reads only `len` bytes through the pointer and writes back the
2040 // resulting length. We check the return value (`status != 0`) to
2041 // distinguish success from validation failure.
2042 let status = unsafe {
2043 libc::getsockopt(
2044 fd,
2045 OPT_LEVEL,
2046 OPT_NAME,
2047 &mut tcp_info as *mut _ as *mut _,
2048 &mut len,
2049 )
2050 };
2051 if status != 0 {
2052 None
2053 } else {
2054 // The kernel writes back the number of bytes it populated. It must
2055 // never claim to have written more than the buffer we handed it —
2056 // that would mean it overran `tcp_info`, an out-of-bounds write we
2057 // could not have detected by the return code alone.
2058 debug_assert!(
2059 len <= struct_len,
2060 "getsockopt(TCP_INFO) wrote back len {len} > struct size {struct_len} (buffer overrun)"
2061 );
2062 Some(tcp_info)
2063 }
2064 }
2065 #[cfg(not(unix))]
2066 pub fn socketinfo(fd: libc::c_int) -> Option<TcpInfo> {
2067 None
2068 }
2069
2070 #[cfg(unix)]
2071 #[cfg(not(any(target_os = "macos", target_os = "ios")))]
2072 mod internal {
2073 #[cfg(target_os = "linux")]
2074 pub const OPT_LEVEL: libc::c_int = libc::SOL_TCP;
2075
2076 #[cfg(any(
2077 target_os = "freebsd",
2078 target_os = "dragonfly",
2079 target_os = "openbsd",
2080 target_os = "netbsd"
2081 ))]
2082 pub const OPT_LEVEL: libc::c_int = libc::IPPROTO_TCP;
2083
2084 pub const OPT_NAME: libc::c_int = libc::TCP_INFO;
2085
2086 #[derive(Clone, Debug)]
2087 #[repr(C)]
2088 pub struct TcpInfo {
2089 // State
2090 tcpi_state: u8,
2091 tcpi_ca_state: u8,
2092 tcpi_retransmits: u8,
2093 tcpi_probes: u8,
2094 tcpi_backoff: u8,
2095 tcpi_options: u8,
2096 tcpi_snd_rcv_wscale: u8, // 4bits|4bits
2097
2098 tcpi_rto: u32,
2099 tcpi_ato: u32,
2100 tcpi_snd_mss: u32,
2101 tcpi_rcv_mss: u32,
2102
2103 tcpi_unacked: u32,
2104 tcpi_sacked: u32,
2105 tcpi_lost: u32,
2106 tcpi_retrans: u32,
2107 tcpi_fackets: u32,
2108
2109 // Times
2110 tcpi_last_data_sent: u32,
2111 tcpi_last_ack_sent: u32, // Not remembered
2112 tcpi_last_data_recv: u32,
2113 tcpi_last_ack_recv: u32,
2114
2115 // Metrics
2116 tcpi_pmtu: u32,
2117 tcpi_rcv_ssthresh: u32,
2118 tcpi_rtt: u32,
2119 tcpi_rttvar: u32,
2120 tcpi_snd_ssthresh: u32,
2121 tcpi_snd_cwnd: u32,
2122 tcpi_advmss: u32,
2123 tcpi_reordering: u32,
2124 }
2125 impl TcpInfo {
2126 pub fn rtt(&self) -> u32 {
2127 self.tcpi_rtt
2128 }
2129
2130 /// Human-readable Linux TCP FSM state. Values follow
2131 /// `include/net/tcp_states.h` (`TCP_ESTABLISHED = 1`,
2132 /// `TCP_SYN_SENT = 2`, …). Anything unexpected falls back to
2133 /// `"UNKNOWN"` rather than panicking — the log prefix is a
2134 /// best-effort diagnostic and must not add failure modes.
2135 pub fn state(&self) -> &'static str {
2136 match self.tcpi_state {
2137 1 => "ESTABLISHED",
2138 2 => "SYN_SENT",
2139 3 => "SYN_RECV",
2140 4 => "FIN_WAIT1",
2141 5 => "FIN_WAIT2",
2142 6 => "TIME_WAIT",
2143 7 => "CLOSE",
2144 8 => "CLOSE_WAIT",
2145 9 => "LAST_ACK",
2146 10 => "LISTEN",
2147 11 => "CLOSING",
2148 12 => "NEW_SYN_RECV",
2149 _ => "UNKNOWN",
2150 }
2151 }
2152 }
2153 }
2154
2155 #[cfg(unix)]
2156 #[cfg(any(target_os = "macos", target_os = "ios"))]
2157 mod internal {
2158 pub const OPT_LEVEL: libc::c_int = libc::IPPROTO_TCP;
2159 pub const OPT_NAME: libc::c_int = 0x106;
2160
2161 #[derive(Clone, Debug)]
2162 #[repr(C)]
2163 pub struct TcpInfo {
2164 tcpi_state: u8,
2165 tcpi_snd_wscale: u8,
2166 tcpi_rcv_wscale: u8,
2167 __pad1: u8,
2168 tcpi_options: u32,
2169 tcpi_flags: u32,
2170 tcpi_rto: u32,
2171 tcpi_maxseg: u32,
2172 tcpi_snd_ssthresh: u32,
2173 tcpi_snd_cwnd: u32,
2174 tcpi_snd_wnd: u32,
2175 tcpi_snd_sbbytes: u32,
2176 tcpi_rcv_wnd: u32,
2177 tcpi_rttcur: u32,
2178 tcpi_srtt: u32,
2179 tcpi_rttvar: u32,
2180 tcpi_tfo: u32,
2181 tcpi_txpackets: u64,
2182 tcpi_txbytes: u64,
2183 tcpi_txretransmitbytes: u64,
2184 tcpi_rxpackets: u64,
2185 tcpi_rxbytes: u64,
2186 tcpi_rxoutoforderbytes: u64,
2187 tcpi_txretransmitpackets: u64,
2188 }
2189 impl TcpInfo {
2190 pub fn rtt(&self) -> u32 {
2191 // tcpi_srtt is in milliseconds not microseconds
2192 self.tcpi_srtt * 1000
2193 }
2194
2195 /// Human-readable Darwin TCP FSM state. Values follow
2196 /// `netinet/tcp_fsm.h` (`TCPS_CLOSED = 0`, `TCPS_LISTEN = 1`,
2197 /// `TCPS_SYN_SENT = 2`, …). Differs from Linux numbering —
2198 /// macOS counts from 0, Linux from 1.
2199 pub fn state(&self) -> &'static str {
2200 match self.tcpi_state {
2201 0 => "CLOSED",
2202 1 => "LISTEN",
2203 2 => "SYN_SENT",
2204 3 => "SYN_RECEIVED",
2205 4 => "ESTABLISHED",
2206 5 => "CLOSE_WAIT",
2207 6 => "FIN_WAIT_1",
2208 7 => "CLOSING",
2209 8 => "LAST_ACK",
2210 9 => "FIN_WAIT_2",
2211 10 => "TIME_WAIT",
2212 _ => "UNKNOWN",
2213 }
2214 }
2215 }
2216 }
2217
2218 #[cfg(not(unix))]
2219 #[derive(Clone, Debug)]
2220 struct TcpInfo {}
2221
2222 #[test]
2223 #[serial_test::serial]
2224 fn test_rtt() {
2225 let sock = std::net::TcpStream::connect("google.com:80").unwrap();
2226 let fd = sock.as_raw_fd();
2227 let info = socket_info(fd);
2228 assert!(info.is_some());
2229 println!("{info:#?}");
2230 println!(
2231 "rtt: {}",
2232 sozu_command::logging::LogDuration(socket_rtt(&sock))
2233 );
2234 }
2235}
2236
2237#[cfg(test)]
2238pub(crate) mod tests {
2239 use super::*;
2240
2241 /// Binding a listener to an interface needs `CAP_NET_RAW` on kernels
2242 /// before 5.7; an environment without it cannot run the positive test.
2243 #[cfg(target_os = "linux")]
2244 fn lacks_bind_to_device_permission(error: &ServerBindError) -> bool {
2245 matches!(
2246 error,
2247 ServerBindError::BindToDevice { error, .. }
2248 if error.kind() == std::io::ErrorKind::PermissionDenied
2249 )
2250 }
2251
2252 /// `server_bind` and `udp_bind` bind a listener with an `interface` to
2253 /// that interface (`SO_BINDTODEVICE`, read back with `getsockopt`), and a
2254 /// listener without one to none — today's behaviour.
2255 #[cfg(target_os = "linux")]
2256 #[test]
2257 fn bind_sets_so_bindtodevice_to_the_listener_interface() {
2258 use socket2::SockRef;
2259
2260 let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
2261
2262 let tcp = match server_bind(addr, Some("lo")) {
2263 Ok(listener) => listener,
2264 Err(error) if lacks_bind_to_device_permission(&error) => {
2265 eprintln!(
2266 "SKIPPED bind_sets_so_bindtodevice_to_the_listener_interface: this \
2267 environment may not bind a socket to an interface (needs CAP_NET_RAW \
2268 before Linux 5.7): {error}"
2269 );
2270 return;
2271 }
2272 Err(error) => panic!("binding a TCP listener to lo: {error}"),
2273 };
2274 assert_eq!(
2275 SockRef::from(&tcp)
2276 .device()
2277 .expect("getsockopt(SO_BINDTODEVICE)"),
2278 Some(b"lo".to_vec()),
2279 "the TCP listener must be bound to lo"
2280 );
2281
2282 let udp = udp_bind(addr, Some("lo")).expect("binding a UDP listener to lo");
2283 assert_eq!(
2284 SockRef::from(&udp)
2285 .device()
2286 .expect("getsockopt(SO_BINDTODEVICE)"),
2287 Some(b"lo".to_vec()),
2288 "the UDP listener must be bound to lo"
2289 );
2290
2291 let plain = server_bind(addr, None).expect("binding a TCP listener");
2292 assert_eq!(
2293 SockRef::from(&plain)
2294 .device()
2295 .expect("getsockopt(SO_BINDTODEVICE)"),
2296 None,
2297 "a listener without interface stays bound to no interface"
2298 );
2299 }
2300
2301 /// An interface that does not exist fails the bind with an explicit error
2302 /// naming the listener address and the interface, and pointing at the two
2303 /// usual causes: the interface must exist, and pre-5.7 kernels require
2304 /// `CAP_NET_RAW`.
2305 #[cfg(target_os = "linux")]
2306 #[test]
2307 fn bind_to_a_missing_interface_names_the_interface_and_the_capability() {
2308 let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
2309 for result in [
2310 server_bind(addr, Some("sozu-nosuch0")).map(|_| ()),
2311 udp_bind(addr, Some("sozu-nosuch0")).map(|_| ()),
2312 ] {
2313 let error = result.expect_err("binding to a missing interface must fail");
2314 assert!(
2315 matches!(&error, ServerBindError::BindToDevice { interface, .. } if interface == "sozu-nosuch0"),
2316 "{error:?}"
2317 );
2318 let message = error.to_string();
2319 for needle in [
2320 "127.0.0.1:0",
2321 "'sozu-nosuch0'",
2322 "SO_BINDTODEVICE",
2323 "must exist",
2324 "CAP_NET_RAW",
2325 "5.7",
2326 ] {
2327 assert!(
2328 message.contains(needle),
2329 "{needle:?} missing from: {message}"
2330 );
2331 }
2332 }
2333 }
2334
2335 /// Address a handler caches. Deliberately non-loopback so it can never
2336 /// collide with the ephemeral address a live `getpeername(2)` reports for
2337 /// the test connection.
2338 const CACHED_PEER: &str = "10.0.0.42:12345";
2339
2340 /// A live, established loopback connection plus the address
2341 /// `getpeername(2)` reports for it. The listener is returned so the
2342 /// connection stays up for the whole test: the property under test is that
2343 /// the snapshot wins even while the live lookup is perfectly healthy, and a
2344 /// half-dead socket would weaken that rather than strengthen it.
2345 ///
2346 /// Using a genuinely connected socket instead of an aborted `connect()` is
2347 /// also what makes these tests deterministic: whether a nonblocking connect
2348 /// to a closed port has already collected its RST — and therefore whether
2349 /// `getpeername(2)` has started answering `ENOTCONN` — is a race against the
2350 /// kernel, not something a test may assert on.
2351 fn connected_loopback_stream() -> (std::net::TcpListener, TcpStream, SocketAddr) {
2352 let listener = std::net::TcpListener::bind("127.0.0.1:0")
2353 .expect("test listener must bind to a loopback port");
2354 let live_peer = listener
2355 .local_addr()
2356 .expect("test listener must report its local address");
2357 let stream =
2358 std::net::TcpStream::connect(live_peer).expect("loopback connect must complete");
2359 stream
2360 .set_nonblocking(true)
2361 .expect("mio requires a nonblocking stream");
2362 (listener, TcpStream::from_std(stream), live_peer)
2363 }
2364
2365 /// The cache is the whole point of the snapshot: on a PROXY-protocol
2366 /// frontend it holds the advertised client while the socket's own peer is
2367 /// the load balancer, and on a backend socket it holds the configured
2368 /// backend while `getpeername(2)` answers `ENOTCONN` after a failed
2369 /// `connect()`. A handler that consults the kernel first has neither.
2370 ///
2371 /// To SEE THIS RED: in `impl SocketHandler for SessionTcpStream`, replace
2372 /// the body of `peer_addr` with `self.stream.peer_addr().ok()`. The cached
2373 /// address disappears and the loopback address takes its place.
2374 #[test]
2375 fn session_tcp_stream_peer_addr_prefers_the_cached_address() {
2376 let (_listener, stream, live_peer) = connected_loopback_stream();
2377 let cached: SocketAddr = CACHED_PEER
2378 .parse()
2379 .expect("the cached peer literal must parse");
2380 let handler = SessionTcpStream::new(stream, Ulid::generate(), Some(cached));
2381
2382 // Premise: the live lookup is healthy and disagrees with the cache, so
2383 // the assertion below cannot pass for the wrong reason.
2384 assert_eq!(
2385 handler.socket_ref().peer_addr().ok(),
2386 Some(live_peer),
2387 "the test socket must be genuinely connected, so a live lookup succeeds"
2388 );
2389 assert_ne!(
2390 cached, live_peer,
2391 "the cached and live addresses must differ for this test to discriminate"
2392 );
2393
2394 assert_eq!(
2395 handler.peer_addr(),
2396 Some(cached),
2397 "a cached peer address must win over a live getpeername(2)"
2398 );
2399 }
2400
2401 /// The fallback arm. Without it a handler built with no snapshot would
2402 /// render `peer=None` on every line rather than degrading to the live
2403 /// lookup that was there before the cache existed.
2404 ///
2405 /// To SEE THIS RED: in `impl SocketHandler for SessionTcpStream`, drop the
2406 /// `.or_else(|| self.stream.peer_addr().ok())` from `peer_addr`. The
2407 /// handler then answers `None` for a perfectly healthy connection.
2408 #[test]
2409 fn session_tcp_stream_peer_addr_falls_back_to_the_live_lookup() {
2410 let (_listener, stream, live_peer) = connected_loopback_stream();
2411 let handler = SessionTcpStream::new(stream, Ulid::generate(), None);
2412
2413 assert_eq!(
2414 handler.peer_addr(),
2415 Some(live_peer),
2416 "with nothing cached, peer_addr must degrade to getpeername(2)"
2417 );
2418 }
2419
2420 /// A bare `TcpStream` carries no session context and therefore nothing to
2421 /// cache; its `peer_addr` is the live lookup and nothing else. Pinned so
2422 /// that a later attempt to give `SocketHandler::peer_addr` a default body
2423 /// has to confront this impl explicitly.
2424 ///
2425 /// To SEE THIS RED: in `impl SocketHandler for TcpStream`, replace the body
2426 /// of `peer_addr` with `None`.
2427 #[test]
2428 fn raw_tcp_stream_peer_addr_is_the_live_lookup() {
2429 let (_listener, stream, live_peer) = connected_loopback_stream();
2430
2431 assert_eq!(
2432 SocketHandler::peer_addr(&stream),
2433 Some(live_peer),
2434 "the contextless handler must report the kernel's answer"
2435 );
2436 }
2437
2438 /// The `peer=` slot of a `SOCKET` line is the handler's cached address,
2439 /// the same one `MUX-H2` and `log_socket_module_prefix` render — not a
2440 /// live `getpeername(2)`.
2441 ///
2442 /// Renders through [`log_socket_context!`] itself rather than through
2443 /// [`SocketHandler::peer_addr`], because the accessor was already correct
2444 /// for every impl while the macro consulted the kernel behind its back.
2445 /// The three `peer_addr` tests above therefore all passed while a `SOCKET`
2446 /// line still named the load balancer on a PROXY-protocol TLS frontend.
2447 ///
2448 /// Expanded against [`SessionTcpStream`] and not [`FrontRustls`], which is
2449 /// the macro's only production caller, because a `FrontRustls` needs a
2450 /// completed rustls handshake and this module builds no TLS. The macro is
2451 /// generic over [`SocketHandler`], so what this pins is the slot's
2452 /// resolution for every impl; that the TLS frontend really renders the
2453 /// PROXY-advertised client on the wire is pinned end to end by
2454 /// `e2e::tests::socket_log_context_tests:: /// test_tls_socket_log_peer_is_the_advertised_client`.
2455 ///
2456 /// The palette is empty here — `LOGGER_COLORED` defaults to `false` and
2457 /// this test installs no logger — so the slot is matched as plain text. A
2458 /// coloured palette would split `peer=` with an escape sequence and redden
2459 /// the test rather than silently passing it.
2460 ///
2461 /// To SEE THIS RED: in [`log_socket_context!`], replace
2462 /// `peer = crate::socket::SocketHandler::peer_addr($self),` with the
2463 /// pre-fix `peer = $self.socket_ref().peer_addr().ok(),`.
2464 #[test]
2465 fn log_socket_context_renders_the_cached_peer_not_a_live_lookup() {
2466 let (_listener, stream, live_peer) = connected_loopback_stream();
2467 let cached: SocketAddr = CACHED_PEER
2468 .parse()
2469 .expect("the cached peer literal must parse");
2470 let handler = SessionTcpStream::new(stream, Ulid::generate(), Some(cached));
2471
2472 // Premise: the live lookup is healthy and disagrees with the cache, so
2473 // neither assertion below can pass for the wrong reason.
2474 assert_eq!(
2475 handler.socket_ref().peer_addr().ok(),
2476 Some(live_peer),
2477 "the test socket must be genuinely connected, so a live lookup succeeds"
2478 );
2479 assert_ne!(
2480 cached, live_peer,
2481 "the cached and live addresses must differ for this test to discriminate"
2482 );
2483
2484 // `&handler`, not `handler`: the macro's `peer` slot is a
2485 // fully-qualified `SocketHandler::peer_addr` call, so the expansion
2486 // binds a shared reference. Production call sites pass `self` from a
2487 // `&mut self` method and reach the same place by reborrow.
2488 let rendered = log_socket_context!(&handler);
2489
2490 assert!(
2491 rendered.contains(&format!("peer=Some({cached})")),
2492 "the SOCKET peer= slot must render the cached address; rendered: {rendered}"
2493 );
2494 assert!(
2495 !rendered.contains(&format!("peer=Some({live_peer})")),
2496 "the SOCKET peer= slot must not render a live getpeername(2); rendered: {rendered}"
2497 );
2498 // Scope guard: this change touches `peer` and nothing else. `local` is
2499 // still `getsockname(2)` on the same socket, which is the stream's own
2500 // address and never the cached peer.
2501 assert!(
2502 rendered.contains(&format!(
2503 "local=Some({})",
2504 handler
2505 .socket_ref()
2506 .local_addr()
2507 .expect("a connected socket has a local address")
2508 )),
2509 "the SOCKET local= slot must stay getsockname(2); rendered: {rendered}"
2510 );
2511 }
2512
2513 /// A socket that was never connected, and therefore one whose
2514 /// `getpeername(2)` fails with `ENOTCONN` *deterministically*.
2515 ///
2516 /// This is the one reliable way to stage a failing live lookup in a test.
2517 /// The obvious alternative — connect to a closed port and wait for the RST
2518 /// — is the race `connected_loopback_stream`'s note already rules out:
2519 /// whether the kernel has collected the RST yet, and so whether
2520 /// `getpeername(2)` has started refusing, is not something a test may
2521 /// assert on. An unconnected socket is in the refusing state from birth.
2522 fn unconnected_stream() -> TcpStream {
2523 let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP))
2524 .expect("a test socket must be creatable");
2525 socket
2526 .set_nonblocking(true)
2527 .expect("mio requires a nonblocking stream");
2528 TcpStream::from_std(std::net::TcpStream::from(socket))
2529 }
2530
2531 /// The `peer=` slot survives a live lookup that FAILS, which is the arm an
2532 /// operator meets during an incident: `getpeername(2)` answers `ENOTCONN`
2533 /// once the peer has reset, so the pre-fix macro rendered `peer=None` on
2534 /// exactly the error lines being read at the time.
2535 ///
2536 /// Staged with a never-connected socket rather than a reset one, because
2537 /// only the former refuses deterministically — see
2538 /// [`unconnected_stream`]. A peer reset is one way to reach that state;
2539 /// what this pins is the rendering once the socket is in it, which is the
2540 /// part the macro owns.
2541 ///
2542 /// The companion
2543 /// [`log_socket_context_renders_the_cached_peer_not_a_live_lookup`] covers
2544 /// the other half, where the live lookup SUCCEEDS and disagrees. The two
2545 /// are separate tests because a single one cannot be red for both reasons,
2546 /// and the PROXY-protocol defect lives in the succeeding half.
2547 ///
2548 /// To SEE THIS RED: in [`log_socket_context!`], replace
2549 /// `peer = crate::socket::SocketHandler::peer_addr($self),` with the
2550 /// pre-fix `peer = $self.socket_ref().peer_addr().ok(),`. The slot
2551 /// collapses to `peer=None`.
2552 #[test]
2553 fn log_socket_context_renders_the_cached_peer_when_the_live_lookup_fails() {
2554 let stream = unconnected_stream();
2555 let cached: SocketAddr = CACHED_PEER
2556 .parse()
2557 .expect("the cached peer literal must parse");
2558
2559 // Premise: the live lookup really is refusing, so the assertion below
2560 // cannot pass for the wrong reason.
2561 assert_eq!(
2562 stream.peer_addr().ok(),
2563 None,
2564 "an unconnected socket must refuse getpeername(2)"
2565 );
2566
2567 let handler = SessionTcpStream::new(stream, Ulid::generate(), Some(cached));
2568 let rendered = log_socket_context!(&handler);
2569
2570 assert!(
2571 rendered.contains(&format!("peer=Some({cached})")),
2572 "the SOCKET peer= slot must survive a failed live lookup; rendered: {rendered}"
2573 );
2574 assert!(
2575 !rendered.contains("peer=None"),
2576 "the SOCKET peer= slot must not collapse to None while a cache exists; \
2577 rendered: {rendered}"
2578 );
2579 }
2580
2581 /// Counts, with a seccomp user-notification filter, the `fcntl(2)`,
2582 /// `getsockname(2)` and `getpeername(2)` calls one `udp_connect` makes,
2583 /// then checks the returned socket's flags and addresses.
2584 ///
2585 /// The filter is installed on a dedicated thread only (no `TSYNC`), so the
2586 /// rest of the test binary is untouched. Every matching call is reported to
2587 /// this thread, counted, and let through unchanged
2588 /// (`SECCOMP_USER_NOTIF_FLAG_CONTINUE`), so `udp_connect` sees the real
2589 /// kernel answers. Release builds must make none of the three calls: the
2590 /// socket is born non-blocking and the post-condition queries only feed
2591 /// `debug_assert!`s. Debug builds keep exactly those queries: one
2592 /// `F_GETFL`, one `getsockname`, one `getpeername`.
2593 #[cfg(all(target_os = "linux", target_pointer_width = "64"))]
2594 #[test]
2595 fn udp_connect_is_born_nonblocking_and_skips_release_queries() {
2596 use std::os::fd::{AsRawFd as _, FromRawFd as _, OwnedFd};
2597
2598 const WATCHED: [libc::c_long; 3] = [
2599 libc::SYS_fcntl,
2600 libc::SYS_getsockname,
2601 libc::SYS_getpeername,
2602 ];
2603
2604 let backend_sock =
2605 std::net::UdpSocket::bind("127.0.0.1:0").expect("the test backend must bind");
2606 let backend = backend_sock
2607 .local_addr()
2608 .expect("the test backend must report its address");
2609
2610 let (listener_tx, listener_rx) = std::sync::mpsc::channel::<OwnedFd>();
2611 let worker = std::thread::spawn(move || {
2612 let stmt = |code: u32, k: u32| libc::sock_filter {
2613 code: code as u16,
2614 jt: 0,
2615 jf: 0,
2616 k,
2617 };
2618 let jeq = |k: libc::c_long, jt: u8| libc::sock_filter {
2619 code: (libc::BPF_JMP | libc::BPF_JEQ | libc::BPF_K) as u16,
2620 jt,
2621 jf: 0,
2622 k: k as u32,
2623 };
2624 // `seccomp_data.nr` is the first field; any watched number jumps
2625 // to the USER_NOTIF return, everything else is allowed.
2626 let mut program = [
2627 stmt(libc::BPF_LD | libc::BPF_W | libc::BPF_ABS, 0),
2628 jeq(WATCHED[0], 3),
2629 jeq(WATCHED[1], 2),
2630 jeq(WATCHED[2], 1),
2631 stmt(libc::BPF_RET | libc::BPF_K, libc::SECCOMP_RET_ALLOW),
2632 stmt(libc::BPF_RET | libc::BPF_K, libc::SECCOMP_RET_USER_NOTIF),
2633 ];
2634 let fprog = libc::sock_fprog {
2635 len: program.len() as u16,
2636 filter: program.as_mut_ptr(),
2637 };
2638 // SAFETY: plain syscalls on this thread; `fprog` outlives them.
2639 let listener = unsafe {
2640 assert_eq!(
2641 libc::prctl(libc::PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0),
2642 0,
2643 "PR_SET_NO_NEW_PRIVS: {}",
2644 std::io::Error::last_os_error()
2645 );
2646 libc::syscall(
2647 libc::SYS_seccomp,
2648 libc::SECCOMP_SET_MODE_FILTER,
2649 libc::SECCOMP_FILTER_FLAG_NEW_LISTENER,
2650 &fprog as *const libc::sock_fprog,
2651 )
2652 };
2653 assert!(
2654 listener >= 0,
2655 "installing the seccomp filter failed: {}",
2656 std::io::Error::last_os_error()
2657 );
2658 // SAFETY: the kernel just returned this descriptor to us.
2659 let listener = unsafe { OwnedFd::from_raw_fd(listener as libc::c_int) };
2660 listener_tx
2661 .send(listener)
2662 .expect("the counting thread must receive the listener");
2663 udp_connect(backend)
2664 });
2665
2666 let listener = listener_rx
2667 .recv()
2668 .expect("the worker must hand over the seccomp listener");
2669 let mut counts = [0usize; 3];
2670 loop {
2671 let mut pollfd = libc::pollfd {
2672 fd: listener.as_raw_fd(),
2673 events: libc::POLLIN,
2674 revents: 0,
2675 };
2676 // SAFETY: one valid pollfd. The 10 s bound only turns a wedged
2677 // worker into a failure instead of a hung test run.
2678 let ready = unsafe { libc::poll(&mut pollfd, 1, 10_000) };
2679 assert!(ready > 0, "no seccomp notification or hang-up within 10 s");
2680 if pollfd.revents & libc::POLLIN == 0 {
2681 // POLLHUP: the filtered thread has exited.
2682 break;
2683 }
2684 // SAFETY: both structs are plain data; RECV requires a zeroed one.
2685 let mut notif: libc::seccomp_notif = unsafe { std::mem::zeroed() };
2686 if unsafe {
2687 libc::ioctl(
2688 listener.as_raw_fd(),
2689 libc::SECCOMP_IOCTL_NOTIF_RECV,
2690 &mut notif,
2691 )
2692 } != 0
2693 {
2694 let error = std::io::Error::last_os_error();
2695 // ENOENT: the notifying call was interrupted before we read it.
2696 assert_eq!(
2697 error.raw_os_error(),
2698 Some(libc::ENOENT),
2699 "SECCOMP_IOCTL_NOTIF_RECV: {error}"
2700 );
2701 continue;
2702 }
2703 let nr = libc::c_long::from(notif.data.nr);
2704 let slot = WATCHED
2705 .iter()
2706 .position(|watched| *watched == nr)
2707 .expect("the filter only reports watched syscalls");
2708 counts[slot] += 1;
2709 let mut resp: libc::seccomp_notif_resp = unsafe { std::mem::zeroed() };
2710 resp.id = notif.id;
2711 resp.flags = libc::SECCOMP_USER_NOTIF_FLAG_CONTINUE as u32;
2712 // SAFETY: `resp` is a valid response for the id just received.
2713 let sent = unsafe {
2714 libc::ioctl(
2715 listener.as_raw_fd(),
2716 libc::SECCOMP_IOCTL_NOTIF_SEND,
2717 &mut resp,
2718 )
2719 };
2720 assert_eq!(
2721 sent,
2722 0,
2723 "SECCOMP_IOCTL_NOTIF_SEND: {}",
2724 std::io::Error::last_os_error()
2725 );
2726 }
2727
2728 let sock = worker
2729 .join()
2730 .expect("the filtered thread must not panic")
2731 .expect("udp_connect must succeed toward a bound loopback backend");
2732
2733 let expected = if cfg!(debug_assertions) {
2734 [1, 1, 1]
2735 } else {
2736 [0, 0, 0]
2737 };
2738 assert_eq!(
2739 counts,
2740 expected,
2741 "udp_connect (fcntl, getsockname, getpeername) calls, debug_assertions = {}",
2742 cfg!(debug_assertions)
2743 );
2744
2745 let fd = sock.as_raw_fd();
2746 // SAFETY: fcntl queries on a descriptor we own.
2747 let (status, fd_flags) = unsafe {
2748 (
2749 libc::fcntl(fd, libc::F_GETFL),
2750 libc::fcntl(fd, libc::F_GETFD),
2751 )
2752 };
2753 assert!(status >= 0 && fd_flags >= 0, "fcntl on the returned socket");
2754 assert_ne!(
2755 status & libc::O_NONBLOCK,
2756 0,
2757 "the socket must be non-blocking"
2758 );
2759 assert_ne!(
2760 fd_flags & libc::FD_CLOEXEC,
2761 0,
2762 "the socket must be close-on-exec"
2763 );
2764 let local = sock
2765 .local_addr()
2766 .expect("the socket must report its local address");
2767 assert!(local.is_ipv4(), "the local family must match the backend's");
2768 assert_ne!(local.port(), 0, "connect must pick a concrete source port");
2769 assert_eq!(
2770 sock.peer_addr().expect("the socket must be connected"),
2771 backend,
2772 "connect must pin the peer to the backend"
2773 );
2774 }
2775
2776 // ---- peer resets (sozu-proxy/sozu#434) ------------------------------
2777
2778 /// The current value of one proxy-level counter on this test thread,
2779 /// absent counting as 0. `METRICS` is a `thread_local!` shared with every
2780 /// test libtest runs on the same thread, so callers assert a DELTA
2781 /// against a baseline, never an absolute value. Mirrors `proxy_metric`
2782 /// in `lib/src/protocol/mux/stream.rs`.
2783 pub(crate) fn proxy_count(name: &str) -> i64 {
2784 use sozu_command::proto::command::filtered_metrics::Inner;
2785 crate::metrics::METRICS.with(|metrics| {
2786 metrics
2787 .borrow_mut()
2788 .dump_local_proxy_metrics()
2789 .get(name)
2790 .and_then(|metric| match metric.inner {
2791 Some(Inner::Count(value)) => Some(value),
2792 _ => None,
2793 })
2794 .unwrap_or(0)
2795 })
2796 }
2797
2798 /// A nonblocking loopback stream whose peer reset it: the accepted side
2799 /// sets `SO_LINGER` to zero and closes, which makes the kernel send a
2800 /// RST instead of a FIN.
2801 ///
2802 /// Returns once `poll(2)` reports `POLLERR` on the stream, so the RST has
2803 /// been processed and the next syscall is the one that answers
2804 /// `ECONNRESET`. `poll` does not consume the pending socket error, a
2805 /// blocking read would. Bounded: a RST that never arrives fails the test
2806 /// instead of hanging it.
2807 pub(crate) fn reset_stream() -> TcpStream {
2808 let listener = std::net::TcpListener::bind("127.0.0.1:0")
2809 .expect("test listener must bind to a loopback port");
2810 let stream = std::net::TcpStream::connect(
2811 listener
2812 .local_addr()
2813 .expect("test listener must report its local address"),
2814 )
2815 .expect("loopback connect must complete");
2816 let (peer, _) = listener.accept().expect("loopback accept must complete");
2817 socket2::SockRef::from(&peer)
2818 .set_linger(Some(std::time::Duration::ZERO))
2819 .expect("SO_LINGER must accept a zero timeout");
2820 drop(peer);
2821
2822 let mut pollfd = libc::pollfd {
2823 fd: std::os::unix::io::AsRawFd::as_raw_fd(&stream),
2824 events: libc::POLLIN,
2825 revents: 0,
2826 };
2827 // SAFETY: one valid `pollfd` borrowed for the duration of the call.
2828 let ready = unsafe { libc::poll(&mut pollfd, 1, 5_000) };
2829 assert_eq!(ready, 1, "the peer's RST must arrive within 5 s");
2830 assert_ne!(
2831 pollfd.revents & libc::POLLERR,
2832 0,
2833 "premise: the peer must have reset the connection, not closed it"
2834 );
2835 stream
2836 .set_nonblocking(true)
2837 .expect("mio requires a nonblocking stream");
2838 TcpStream::from_std(stream)
2839 }
2840
2841 /// A read that meets the peer's RST answers `Closed`, as it always did,
2842 /// and counts `tcp.read.reset` once: the kernel reports `ECONNRESET` to
2843 /// that read only, and the next one is an EOF.
2844 ///
2845 /// To SEE THIS RED: drop `incr!(names::tcp::READ_RESET)` from
2846 /// `plain_socket_read`.
2847 #[test]
2848 fn a_plain_read_counts_a_peer_reset_once() {
2849 let mut stream = reset_stream();
2850 let before = proxy_count(names::tcp::READ_RESET);
2851 let mut buf = [0u8; 64];
2852
2853 let (size, result) = stream.socket_read(&mut buf);
2854 assert_eq!((size, result), (0, SocketResult::Closed));
2855 assert_eq!(proxy_count(names::tcp::READ_RESET) - before, 1);
2856
2857 let (size, result) = stream.socket_read(&mut buf);
2858 assert_eq!((size, result), (0, SocketResult::Closed));
2859 assert_eq!(
2860 proxy_count(names::tcp::READ_RESET) - before,
2861 1,
2862 "the EOF after the reset is not another reset"
2863 );
2864 }
2865
2866 /// A write that meets the peer's RST answers `Closed`, as it always did,
2867 /// still counts `tcp.write.error`, and counts `tcp.write.reset` once: the
2868 /// next write answers `EPIPE`, which is not a reset.
2869 ///
2870 /// To SEE THIS RED: drop `incr!(names::tcp::WRITE_RESET)` from
2871 /// `tcp_socket_write`.
2872 #[test]
2873 fn a_plain_write_counts_a_peer_reset_once() {
2874 let mut stream = reset_stream();
2875 let resets = proxy_count(names::tcp::WRITE_RESET);
2876 let errors = proxy_count(names::tcp::WRITE_ERROR);
2877
2878 let (size, result) = stream.socket_write(b"after the reset");
2879 assert_eq!((size, result), (0, SocketResult::Closed));
2880 assert_eq!(proxy_count(names::tcp::WRITE_RESET) - resets, 1);
2881 assert_eq!(proxy_count(names::tcp::WRITE_ERROR) - errors, 1);
2882
2883 let (size, result) = stream.socket_write(b"after the reset");
2884 assert_eq!((size, result), (0, SocketResult::Closed));
2885 assert_eq!(
2886 proxy_count(names::tcp::WRITE_RESET) - resets,
2887 1,
2888 "the EPIPE after the reset is not another reset"
2889 );
2890 }
2891
2892 /// The vectored write path counts a reset exactly as the scalar one.
2893 ///
2894 /// To SEE THIS RED: drop `incr!(names::tcp::WRITE_RESET)` from
2895 /// `tcp_socket_write_vectored`.
2896 #[test]
2897 fn a_plain_vectored_write_counts_a_peer_reset() {
2898 let mut stream = reset_stream();
2899 let before = proxy_count(names::tcp::WRITE_RESET);
2900
2901 let (size, result) =
2902 stream.socket_write_vectored(&[std::io::IoSlice::new(b"after the reset")]);
2903 assert_eq!((size, result), (0, SocketResult::Closed));
2904 assert_eq!(proxy_count(names::tcp::WRITE_RESET) - before, 1);
2905 }
2906}
2907
2908/// [`rustls_socket_read`] and [`plain_socket_read`] against an in-memory
2909/// transport that counts every read a call issues: each one is a `recv(2)` on
2910/// the real socket.
2911#[cfg(test)]
2912pub(crate) mod rustls_read_tests {
2913 use std::{collections::VecDeque, io::Write, sync::Arc};
2914
2915 use super::*;
2916
2917 /// SNI the test certificate is registered under.
2918 pub(crate) const TEST_SNI: &str = "lolcatho.st";
2919
2920 /// A frame header, then its payload: what the H2 read path asks for, one
2921 /// `socket_read` at a time (`lib/src/protocol/mux/h2.rs`, `readable_inner`).
2922 const FRAME_HEADER_LEN: usize = 9;
2923
2924 /// The frames of a client's first flight after the preface — SETTINGS,
2925 /// WINDOW_UPDATE, HEADERS, PING — as `(header, payload)` byte blocks. Only
2926 /// their lengths matter to the transport layer; the contents are
2927 /// distinct so a misordered or duplicated read cannot compare equal.
2928 fn client_frames() -> Vec<Vec<u8>> {
2929 [6usize, 4, 37, 8]
2930 .iter()
2931 .enumerate()
2932 .flat_map(|(index, &payload_len)| {
2933 let mut header = vec![0u8; FRAME_HEADER_LEN];
2934 header[..3].copy_from_slice(&(payload_len as u32).to_be_bytes()[1..]);
2935 header[3] = index as u8;
2936 let payload = (0..payload_len)
2937 .map(|byte| (index * 64 + byte) as u8)
2938 .collect();
2939 [header, payload]
2940 })
2941 .collect()
2942 }
2943
2944 /// A non-blocking transport: `wire` is what the kernel holds, served by at
2945 /// most one read, and `eof` is a received FIN once `wire` is drained.
2946 /// `eagains` counts the reads that found nothing, the ones a short read
2947 /// makes unnecessary. The first `interrupts` reads answer `EINTR`.
2948 #[derive(Default)]
2949 pub(crate) struct CountingTransport {
2950 pub(crate) wire: VecDeque<u8>,
2951 pub(crate) eof: bool,
2952 pub(crate) reset: bool,
2953 pub(crate) reads: usize,
2954 pub(crate) eagains: usize,
2955 pub(crate) interrupts: usize,
2956 }
2957
2958 impl Read for CountingTransport {
2959 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
2960 self.reads += 1;
2961 if self.interrupts > 0 {
2962 self.interrupts -= 1;
2963 return Err(ErrorKind::Interrupted.into());
2964 }
2965 if self.reset {
2966 return Err(ErrorKind::ConnectionReset.into());
2967 }
2968 if self.wire.is_empty() {
2969 return if self.eof {
2970 Ok(0)
2971 } else {
2972 self.eagains += 1;
2973 Err(ErrorKind::WouldBlock.into())
2974 };
2975 }
2976 let len = buf.len().min(self.wire.len());
2977 for (slot, byte) in buf.iter_mut().zip(self.wire.drain(..len)) {
2978 *slot = byte;
2979 }
2980 Ok(len)
2981 }
2982 }
2983
2984 /// Accepts whatever certificate the server presents: the client is the
2985 /// encryption oracle for the bytes under test, not a party whose trust
2986 /// decision is being checked. `lib/assets/certificate.pem` carries no
2987 /// subjectAltName, so rustls's web-PKI verifier would refuse it.
2988 #[derive(Debug)]
2989 struct AcceptAnyServerCertificate;
2990
2991 impl rustls::client::danger::ServerCertVerifier for AcceptAnyServerCertificate {
2992 fn verify_server_cert(
2993 &self,
2994 _end_entity: &rustls::pki_types::CertificateDer<'_>,
2995 _intermediates: &[rustls::pki_types::CertificateDer<'_>],
2996 _server_name: &rustls::pki_types::ServerName<'_>,
2997 _ocsp_response: &[u8],
2998 _now: rustls::pki_types::UnixTime,
2999 ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
3000 Ok(rustls::client::danger::ServerCertVerified::assertion())
3001 }
3002
3003 fn verify_tls12_signature(
3004 &self,
3005 _message: &[u8],
3006 _cert: &rustls::pki_types::CertificateDer<'_>,
3007 _dss: &rustls::DigitallySignedStruct,
3008 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
3009 Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
3010 }
3011
3012 fn verify_tls13_signature(
3013 &self,
3014 _message: &[u8],
3015 _cert: &rustls::pki_types::CertificateDer<'_>,
3016 _dss: &rustls::DigitallySignedStruct,
3017 ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
3018 Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
3019 }
3020
3021 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
3022 crate::crypto::default_provider()
3023 .signature_verification_algorithms
3024 .supported_schemes()
3025 }
3026 }
3027
3028 /// Move every pending TLS byte from `from` to `to`, in memory.
3029 fn pump_tls<A, B>(
3030 from: &mut rustls::ConnectionCommon<A>,
3031 to: &mut rustls::ConnectionCommon<B>,
3032 ) {
3033 let mut wire = Vec::new();
3034 while from.wants_write() {
3035 from.write_tls(&mut wire)
3036 .expect("a TLS flight must serialize into memory");
3037 }
3038 let mut cursor = std::io::Cursor::new(wire.as_slice());
3039 while (cursor.position() as usize) < wire.len() {
3040 to.read_tls(&mut cursor)
3041 .expect("a TLS flight must be readable from memory");
3042 to.process_new_packets()
3043 .expect("a TLS flight must process cleanly");
3044 }
3045 }
3046
3047 /// A TLS 1.3 server session serving the test certificate for
3048 /// [`TEST_SNI`], and a client that accepts it and offers `alpn`, neither
3049 /// started.
3050 pub(crate) fn fresh_pair(alpn: Vec<Vec<u8>>) -> (ServerConnection, rustls::ClientConnection) {
3051 let provider = Arc::new(crate::crypto::default_provider());
3052 let resolver = Arc::new(crate::tls::MutexCertificateResolver::default());
3053 resolver
3054 .0
3055 .lock()
3056 .expect("the test resolver lock must be available")
3057 .add_certificate(&sozu_command::proto::command::AddCertificate {
3058 address: sozu_command::proto::command::SocketAddress::new_v4(127, 0, 0, 1, 8443),
3059 certificate: sozu_command::proto::command::CertificateAndKey {
3060 certificate: include_str!("../assets/certificate.pem").to_owned(),
3061 key: include_str!("../assets/key.pem").to_owned(),
3062 names: vec![TEST_SNI.to_owned()],
3063 ..Default::default()
3064 },
3065 expired_at: None,
3066 })
3067 .expect("the test certificate must load into the resolver");
3068 let mut server_config = rustls::ServerConfig::builder_with_provider(provider.clone())
3069 .with_protocol_versions(&[&rustls::version::TLS13])
3070 .expect("the test provider must support TLS 1.3")
3071 .with_no_client_auth()
3072 .with_cert_resolver(resolver);
3073 server_config.send_tls13_tickets = 0;
3074 let client_config = rustls::ClientConfig::builder_with_provider(provider)
3075 .with_protocol_versions(&[&rustls::version::TLS13])
3076 .expect("the test provider must support TLS 1.3")
3077 .dangerous()
3078 .with_custom_certificate_verifier(Arc::new(AcceptAnyServerCertificate))
3079 .with_no_client_auth();
3080 let server = ServerConnection::new(Arc::new(server_config))
3081 .expect("the test server session must initialize");
3082 let client = rustls::ClientConnection::new_with_alpn(
3083 Arc::new(client_config),
3084 rustls::pki_types::ServerName::try_from(TEST_SNI)
3085 .expect("the test SNI must be a valid DNS name"),
3086 alpn,
3087 )
3088 .expect("the test client session must initialize");
3089 (server, client)
3090 }
3091
3092 /// A settled TLS 1.3 server session and the client that feeds it.
3093 fn handshaken_pair() -> (ServerConnection, rustls::ClientConnection) {
3094 let (mut server, mut client) = fresh_pair(Vec::new());
3095 for _ in 0..16 {
3096 if !client.is_handshaking() && !server.is_handshaking() && !client.wants_write() {
3097 break;
3098 }
3099 pump_tls(&mut client, &mut server);
3100 pump_tls(&mut server, &mut client);
3101 }
3102 assert!(
3103 !server.is_handshaking() && !client.is_handshaking(),
3104 "premise: the in-memory handshake must complete"
3105 );
3106 (server, client)
3107 }
3108
3109 /// Encrypt `plaintext` as ONE `write_all` + drain, so a flight smaller
3110 /// than a record's 16 KiB limit lands in a single TLS record.
3111 fn seal(client: &mut rustls::ClientConnection, plaintext: &[u8]) -> Vec<u8> {
3112 client
3113 .writer()
3114 .write_all(plaintext)
3115 .expect("the client must absorb the plaintext");
3116 let mut wire = Vec::new();
3117 while client.wants_write() {
3118 client
3119 .write_tls(&mut wire)
3120 .expect("the client record must serialize");
3121 }
3122 wire
3123 }
3124
3125 /// The server side under test: a session plus the flags `FrontRustls`
3126 /// threads through [`rustls_socket_read`].
3127 struct Front {
3128 session: ServerConnection,
3129 peer_disconnected: bool,
3130 peer_reset: bool,
3131 tls_fatal: bool,
3132 memory: RecvMemory,
3133 }
3134
3135 impl Front {
3136 fn read(
3137 &mut self,
3138 transport: &mut CountingTransport,
3139 len: usize,
3140 ) -> (Vec<u8>, SocketResult) {
3141 let mut buf = vec![0u8; len];
3142 let (size, result, fault) = rustls_socket_read(
3143 &mut self.session,
3144 transport,
3145 &mut buf,
3146 &mut self.peer_disconnected,
3147 &mut self.peer_reset,
3148 &mut self.tls_fatal,
3149 &mut self.memory,
3150 );
3151 assert!(fault.is_none(), "unexpected read fault: {fault:?}");
3152 buf.truncate(size);
3153 (buf, result)
3154 }
3155
3156 /// Same as [`Self::read`], for a pass that is expected to fault.
3157 fn read_faulting(
3158 &mut self,
3159 transport: &mut CountingTransport,
3160 len: usize,
3161 ) -> (Vec<u8>, SocketResult) {
3162 let mut buf = vec![0u8; len];
3163 let (size, result, _fault) = rustls_socket_read(
3164 &mut self.session,
3165 transport,
3166 &mut buf,
3167 &mut self.peer_disconnected,
3168 &mut self.peer_reset,
3169 &mut self.tls_fatal,
3170 &mut self.memory,
3171 );
3172 buf.truncate(size);
3173 (buf, result)
3174 }
3175
3176 /// What `FrontRustls::readiness_delivered` does when the event loop
3177 /// delivers `events` for the socket.
3178 fn deliver(&mut self, events: Ready) {
3179 self.memory.event_delivered(events);
3180 }
3181 }
3182
3183 fn front_and_client() -> (Front, rustls::ClientConnection) {
3184 let (session, client) = handshaken_pair();
3185 (
3186 Front {
3187 session,
3188 peer_disconnected: false,
3189 peer_reset: false,
3190 tls_fatal: false,
3191 memory: RecvMemory::default(),
3192 },
3193 client,
3194 )
3195 }
3196
3197 /// Several H2 frames in ONE TLS record, read the way the H2 path reads
3198 /// them: header, payload, header, payload. The record costs one `recv`;
3199 /// every later frame is already decrypted and must be served from
3200 /// rustls's plaintext buffer, and the call that finds that buffer empty
3201 /// answers `WouldBlock` without a `recv`: the first call's short `recv`
3202 /// proved the queue empty and no event was delivered since.
3203 ///
3204 /// Before #1606 every call started with `read_tls`, so each frame block
3205 /// after the first issued one `recv` that returned `EAGAIN`: 1 + 7 + 1.
3206 /// Before #1609 the memory did not outlive a call, and the last call still
3207 /// issued one: 1 + 1.
3208 #[test]
3209 fn frames_sharing_one_record_cost_one_recv() {
3210 let (mut front, mut client) = front_and_client();
3211 let frames = client_frames();
3212 let record = seal(&mut client, &frames.concat());
3213 let mut transport = CountingTransport {
3214 wire: record.into(),
3215 ..Default::default()
3216 };
3217
3218 for (index, block) in frames.iter().enumerate() {
3219 let (read, result) = front.read(&mut transport, block.len());
3220 assert_eq!(
3221 &read, block,
3222 "frame block {index} must arrive intact and in order"
3223 );
3224 assert_eq!(result, SocketResult::Continue, "frame block {index}");
3225 }
3226 let (read, result) = front.read(&mut transport, FRAME_HEADER_LEN);
3227 assert!(read.is_empty(), "nothing is left after the last frame");
3228 assert_eq!(result, SocketResult::WouldBlock);
3229
3230 assert_eq!(
3231 (transport.reads, transport.eagains),
3232 (1, 0),
3233 "one recv for the record and no EAGAIN once the plaintext is drained, \
3234 for {} frame blocks",
3235 frames.len()
3236 );
3237 assert!(!front.peer_disconnected && !front.peer_reset);
3238 }
3239
3240 /// An event delivered after the proof clears it: the bytes that raised it
3241 /// are read by the next call, with no EAGAIN on either side.
3242 #[test]
3243 fn an_event_after_a_short_read_lets_the_next_call_read() {
3244 let (mut front, mut client) = front_and_client();
3245 let frames = client_frames();
3246 let mut transport = CountingTransport {
3247 wire: seal(&mut client, &frames.concat()).into(),
3248 ..Default::default()
3249 };
3250 for block in &frames {
3251 assert_eq!(&front.read(&mut transport, block.len()).0, block);
3252 }
3253 assert_eq!(
3254 front.read(&mut transport, FRAME_HEADER_LEN),
3255 (Vec::new(), SocketResult::WouldBlock),
3256 "no event since the short read: nothing is read"
3257 );
3258
3259 transport.wire.extend(seal(&mut client, &frames[0]));
3260 front.deliver(Ready::READABLE);
3261 let (read, result) = front.read(&mut transport, frames[0].len());
3262 assert_eq!(read, frames[0], "the new arrival is read");
3263 assert_eq!(result, SocketResult::Continue);
3264 assert_eq!((transport.reads, transport.eagains), (2, 0));
3265 }
3266
3267 /// A FIN whose edge is delivered after the proof, as HUP: the memory is
3268 /// off from then on, and the next call reads the EOF.
3269 #[test]
3270 fn a_hup_delivered_after_a_short_read_lets_the_next_call_read_the_eof() {
3271 let (mut front, mut client) = front_and_client();
3272 let frames = client_frames();
3273 let mut transport = CountingTransport {
3274 wire: seal(&mut client, &frames.concat()).into(),
3275 ..Default::default()
3276 };
3277 for block in &frames {
3278 assert_eq!(&front.read(&mut transport, block.len()).0, block);
3279 }
3280
3281 transport.eof = true;
3282 front.deliver(Ready::HUP);
3283 assert_eq!(
3284 front.read(&mut transport, FRAME_HEADER_LEN),
3285 (Vec::new(), SocketResult::Closed)
3286 );
3287 assert!(front.peer_disconnected);
3288 assert_eq!((transport.reads, transport.eagains), (2, 0));
3289 }
3290
3291 /// A caller buffer larger than everything received (the H1 path) takes the
3292 /// whole record in one short `recv` and stops there: the queue is empty,
3293 /// so a second `recv` could only answer EAGAIN.
3294 ///
3295 /// Before the stop on a short read the pass called `read_tls` again and
3296 /// counted 2 reads, the second one EAGAIN.
3297 #[test]
3298 fn a_large_buffer_drains_the_record_in_one_short_recv() {
3299 let (mut front, mut client) = front_and_client();
3300 let plaintext = client_frames().concat();
3301 let mut transport = CountingTransport {
3302 wire: seal(&mut client, &plaintext).into(),
3303 ..Default::default()
3304 };
3305
3306 let (read, result) = front.read(&mut transport, 16 * 1024);
3307 assert_eq!(read, plaintext);
3308 assert_eq!(result, SocketResult::WouldBlock);
3309 assert_eq!(
3310 (transport.reads, transport.eagains),
3311 (1, 0),
3312 "one short recv for the record and no EAGAIN"
3313 );
3314 }
3315
3316 /// `close_notify` in the same arrival as the data, read with a large
3317 /// buffer: the pass stops reading the socket after the short `recv`, but
3318 /// must still report the close rustls already processed, in the same call.
3319 /// A pass that skipped every `read_tls` after a short read would answer
3320 /// `WouldBlock` here and leave the close unreported until a later event.
3321 #[test]
3322 fn close_notify_in_the_short_read_is_reported_in_the_same_call() {
3323 let (mut front, mut client) = front_and_client();
3324 let plaintext = client_frames().concat();
3325 let mut wire = seal(&mut client, &plaintext);
3326 client.send_close_notify();
3327 while client.wants_write() {
3328 client
3329 .write_tls(&mut wire)
3330 .expect("the close_notify alert must serialize");
3331 }
3332 let mut transport = CountingTransport {
3333 wire: wire.into(),
3334 ..Default::default()
3335 };
3336
3337 let (read, result) = front.read(&mut transport, 16 * 1024);
3338 assert_eq!(read, plaintext, "the data precedes the close");
3339 assert_eq!(result, SocketResult::Closed);
3340 assert!(front.peer_disconnected && !front.peer_reset);
3341 assert_eq!((transport.reads, transport.eagains), (1, 0));
3342 }
3343
3344 /// A TCP FIN behind the data, read with a large buffer: the short `recv`
3345 /// delivers the data and stops, and the FIN is read by the next call. The
3346 /// mux makes that call because it saw HUP
3347 /// (`update_readiness_after_read`).
3348 #[test]
3349 fn a_fin_behind_a_short_read_is_reported_by_the_next_call() {
3350 let (mut front, mut client) = front_and_client();
3351 let plaintext = client_frames().concat();
3352 let mut transport = CountingTransport {
3353 wire: seal(&mut client, &plaintext).into(),
3354 eof: true,
3355 ..Default::default()
3356 };
3357 // The FIN came with the data: one edge, delivered as HUP, which turns
3358 // the memory off so the next call reads the EOF.
3359 front.deliver(Ready::READABLE | Ready::HUP);
3360
3361 let (read, result) = front.read(&mut transport, 16 * 1024);
3362 assert_eq!(read, plaintext);
3363 assert_eq!(result, SocketResult::WouldBlock);
3364 assert!(!front.peer_disconnected, "the FIN has not been read yet");
3365
3366 let (read, result) = front.read(&mut transport, 16 * 1024);
3367 assert!(read.is_empty());
3368 assert_eq!(result, SocketResult::Closed);
3369 assert!(front.peer_disconnected && !front.peer_reset);
3370 assert_eq!((transport.reads, transport.eagains), (2, 0));
3371 }
3372
3373 /// A body spanning several records and many 4096-byte `read_tls` calls,
3374 /// read with the H1 buffer size: every byte arrives in order and the full
3375 /// reads keep the pass going.
3376 ///
3377 /// No read answers EAGAIN. The final short `recv` happens in a call whose
3378 /// buffer then fills from the plaintext it decrypted, so that call answers
3379 /// `Continue`; the next call drains the rest and answers `WouldBlock`
3380 /// without a `recv`, because no event was delivered since the short one
3381 /// ([`RecvMemory`]). Before #1609 that call issued one `recv` for an
3382 /// EAGAIN.
3383 #[test]
3384 fn a_large_body_over_tls_arrives_intact() {
3385 let (mut front, mut client) = front_and_client();
3386 let body: Vec<u8> = (0..(64 * 1024 + 123)).map(|i| (i % 251) as u8).collect();
3387 // One record per 16 KiB chunk: the client's outgoing buffer would
3388 // refuse the whole body in one `write_all`.
3389 let mut transport = CountingTransport {
3390 wire: body
3391 .chunks(16 * 1024)
3392 .flat_map(|chunk| seal(&mut client, chunk))
3393 .collect(),
3394 ..Default::default()
3395 };
3396
3397 let mut received = Vec::new();
3398 loop {
3399 let (read, result) = front.read(&mut transport, 16 * 1024);
3400 received.extend_from_slice(&read);
3401 match result {
3402 SocketResult::Continue => assert_eq!(read.len(), 16 * 1024),
3403 SocketResult::WouldBlock => break,
3404 other => panic!("unexpected {other:?} after {} bytes", received.len()),
3405 }
3406 }
3407 assert_eq!(received, body);
3408 assert!(transport.wire.is_empty());
3409 assert_eq!(
3410 transport.eagains, 0,
3411 "the call after the one that filled from the last short read reads nothing"
3412 );
3413 }
3414
3415 /// A record split across two arrivals: the first `read_tls` returns bytes
3416 /// but `process_new_packets` produces no plaintext. That `recv` was short,
3417 /// so the queue is empty and the pass reports `WouldBlock` with nothing
3418 /// delivered, without a second `recv`. The rest of the record then
3419 /// completes it.
3420 #[test]
3421 fn a_fragmented_record_is_read_again_until_it_completes() {
3422 let (mut front, mut client) = front_and_client();
3423 let frames = client_frames();
3424 let record = seal(&mut client, &frames.concat());
3425 let (head, tail) = record.split_at(record.len() / 2);
3426 let mut transport = CountingTransport {
3427 wire: head.iter().copied().collect(),
3428 ..Default::default()
3429 };
3430
3431 let (read, result) = front.read(&mut transport, FRAME_HEADER_LEN);
3432 assert!(read.is_empty(), "half a record decrypts to nothing");
3433 assert_eq!(result, SocketResult::WouldBlock);
3434 assert_eq!(
3435 (transport.reads, transport.eagains),
3436 (1, 0),
3437 "the partial record, in one short recv"
3438 );
3439
3440 transport.wire.extend(tail);
3441 // The tail's arrival raises the edge the event loop delivers.
3442 front.deliver(Ready::READABLE);
3443 for (index, block) in frames.iter().enumerate() {
3444 let (read, result) = front.read(&mut transport, block.len());
3445 assert_eq!(&read, block, "frame block {index}");
3446 assert_eq!(result, SocketResult::Continue, "frame block {index}");
3447 }
3448 assert_eq!(transport.reads, 2, "the rest of the record costs one recv");
3449 }
3450
3451 /// `close_notify` behind the data in the same flight: every frame is
3452 /// still delivered, then the pass reports `Closed` — from rustls's own
3453 /// `read_tls` early return, without another `recv`.
3454 #[test]
3455 fn close_notify_after_the_frames_reports_closed_once_they_are_read() {
3456 let (mut front, mut client) = front_and_client();
3457 let frames = client_frames();
3458 let mut wire = seal(&mut client, &frames.concat());
3459 client.send_close_notify();
3460 while client.wants_write() {
3461 client
3462 .write_tls(&mut wire)
3463 .expect("the close_notify alert must serialize");
3464 }
3465 let mut transport = CountingTransport {
3466 wire: wire.into(),
3467 ..Default::default()
3468 };
3469
3470 for (index, block) in frames.iter().enumerate() {
3471 let (read, result) = front.read(&mut transport, block.len());
3472 assert_eq!(&read, block, "frame block {index}");
3473 assert_eq!(result, SocketResult::Continue, "frame block {index}");
3474 }
3475 let (read, result) = front.read(&mut transport, FRAME_HEADER_LEN);
3476 assert!(read.is_empty());
3477 assert_eq!(
3478 result,
3479 SocketResult::Closed,
3480 "close_notify must close the read side"
3481 );
3482 assert!(
3483 front.peer_disconnected,
3484 "a clean close is a read-side disconnect"
3485 );
3486 assert!(!front.peer_reset, "a clean close is not a reset");
3487 assert_eq!(
3488 transport.reads, 1,
3489 "close_notify needs no recv beyond the flight"
3490 );
3491
3492 let (read, result) = front.read(&mut transport, FRAME_HEADER_LEN);
3493 assert!(read.is_empty());
3494 assert_eq!(
3495 result,
3496 SocketResult::Closed,
3497 "a closed session stays closed"
3498 );
3499 }
3500
3501 /// A TCP FIN without `close_notify`: buffered frames are delivered first,
3502 /// then `Closed` (never `Error`), and a later call is `Closed` again.
3503 #[test]
3504 fn tcp_eof_without_close_notify_reports_closed_after_the_buffered_frames() {
3505 let (mut front, mut client) = front_and_client();
3506 let frames = client_frames();
3507 let mut transport = CountingTransport {
3508 wire: seal(&mut client, &frames.concat()).into(),
3509 eof: true,
3510 ..Default::default()
3511 };
3512 // The FIN came with the data: one edge, delivered as HUP.
3513 front.deliver(Ready::READABLE | Ready::HUP);
3514
3515 for (index, block) in frames.iter().enumerate() {
3516 let (read, result) = front.read(&mut transport, block.len());
3517 assert_eq!(&read, block, "frame block {index}");
3518 assert_eq!(result, SocketResult::Continue, "frame block {index}");
3519 }
3520 let (read, result) = front.read(&mut transport, FRAME_HEADER_LEN);
3521 assert!(read.is_empty());
3522 assert_eq!(
3523 result,
3524 SocketResult::Closed,
3525 "a FIN must close the read side"
3526 );
3527 assert!(front.peer_disconnected && !front.peer_reset);
3528
3529 let (read, result) = front.read(&mut transport, FRAME_HEADER_LEN);
3530 assert!(read.is_empty());
3531 assert_eq!(result, SocketResult::Closed, "EOF stays EOF, not an error");
3532 }
3533
3534 /// A valid record of four frames followed, in the same arrival, by a
3535 /// corrupted one. `process_new_packets` appends the first record's
3536 /// plaintext and then fails on the second: the session is dead. Every
3537 /// later call must answer `Error` without serving that plaintext, or the
3538 /// mux (which only drops READABLE on `Error`) would parse frames from a
3539 /// TLS session that has already failed as soon as the peer sends again.
3540 #[test]
3541 fn a_fatal_tls_error_stays_an_error_and_serves_no_buffered_plaintext() {
3542 let (mut front, mut client) = front_and_client();
3543 let frames = client_frames();
3544 let mut wire = seal(&mut client, &frames.concat());
3545 let mut corrupted = seal(&mut client, b"after the valid record");
3546 let last = corrupted.len() - 1;
3547 corrupted[last] ^= 0xff;
3548 wire.extend(corrupted);
3549 let mut transport = CountingTransport {
3550 wire: wire.into(),
3551 ..Default::default()
3552 };
3553
3554 let (read, result) = front.read_faulting(&mut transport, FRAME_HEADER_LEN);
3555 assert!(read.is_empty(), "the failing pass delivers nothing");
3556 assert_eq!(result, SocketResult::Error);
3557
3558 for call in 0..2 * frames.len() {
3559 transport.wire.extend([0u8; 16]);
3560 let (read, result) = front.read_faulting(&mut transport, FRAME_HEADER_LEN);
3561 assert_eq!(
3562 (read.len(), result),
3563 (0, SocketResult::Error),
3564 "call {call} after a fatal TLS error must stay (0, Error)"
3565 );
3566 }
3567 }
3568
3569 /// The pass that hits the fatal error keeps what it had already copied:
3570 /// plaintext served from the buffer BEFORE `read_tls` met the corrupt
3571 /// record is returned with `Error` (`(n, Error)`), and only later passes
3572 /// are `(0, Error)`.
3573 #[test]
3574 fn plaintext_served_before_the_fatal_error_is_returned_with_it() {
3575 let (mut front, mut client) = front_and_client();
3576 let frames = client_frames();
3577 let plaintext = frames.concat();
3578 let mut transport = CountingTransport {
3579 wire: seal(&mut client, &plaintext).into(),
3580 ..Default::default()
3581 };
3582 let (read, result) = front.read(&mut transport, FRAME_HEADER_LEN);
3583 assert_eq!(read, plaintext[..FRAME_HEADER_LEN]);
3584 assert_eq!(result, SocketResult::Continue);
3585
3586 let mut corrupted = seal(&mut client, b"after the valid record");
3587 let last = corrupted.len() - 1;
3588 corrupted[last] ^= 0xff;
3589 transport.wire.extend(corrupted);
3590 front.deliver(Ready::READABLE);
3591
3592 let (read, result) = front.read_faulting(&mut transport, 16 * 1024);
3593 assert_eq!(
3594 read,
3595 plaintext[FRAME_HEADER_LEN..],
3596 "the rest of the valid record"
3597 );
3598 assert_eq!(result, SocketResult::Error);
3599 let (read, result) = front.read_faulting(&mut transport, 16 * 1024);
3600 assert_eq!((read.len(), result), (0, SocketResult::Error));
3601 }
3602
3603 /// A reset is `Closed` with `peer_reset` set, so writes short-circuit.
3604 #[test]
3605 fn a_reset_reports_closed_and_marks_the_peer_reset() {
3606 let (mut front, _client) = front_and_client();
3607 let mut transport = CountingTransport {
3608 reset: true,
3609 ..Default::default()
3610 };
3611
3612 let (read, result) = front.read(&mut transport, FRAME_HEADER_LEN);
3613 assert!(read.is_empty());
3614 assert_eq!(result, SocketResult::Closed);
3615 assert!(front.peer_disconnected && front.peer_reset);
3616 assert_eq!(transport.reads, 1);
3617 }
3618
3619 /// A settled TLS frontend over a real socket its peer reset
3620 /// (`SO_LINGER` zero, then close).
3621 fn reset_front() -> FrontRustls {
3622 let (session, _client) = handshaken_pair();
3623 FrontRustls {
3624 stream: super::tests::reset_stream(),
3625 session,
3626 peer_disconnected: false,
3627 peer_reset: false,
3628 tls_fatal: false,
3629 session_ulid: Ulid::generate(),
3630 configured_peer: None,
3631 recv_memory: RecvMemory::default(),
3632 }
3633 }
3634
3635 /// A TLS read that meets the peer's RST answers `Closed` and counts
3636 /// `rustls.read.reset` once. The write that follows short-circuits on
3637 /// `peer_reset` without reaching the socket, so it counts nothing, and
3638 /// the reset does not leak into the plain-TCP counter either.
3639 ///
3640 /// To SEE THIS RED: drop `incr!(names::rustls::READ_RESET)` from
3641 /// `rustls_socket_read`.
3642 #[test]
3643 fn a_tls_read_counts_a_peer_reset_once() {
3644 let mut front = reset_front();
3645 let reads = super::tests::proxy_count(names::rustls::READ_RESET);
3646 let writes = super::tests::proxy_count(names::rustls::WRITE_RESET);
3647 let plain = super::tests::proxy_count(names::tcp::READ_RESET);
3648
3649 let mut buf = [0u8; 64];
3650 let (size, result) = front.socket_read(&mut buf);
3651 assert_eq!((size, result), (0, SocketResult::Closed));
3652 assert!(front.peer_reset);
3653 assert_eq!(
3654 super::tests::proxy_count(names::rustls::READ_RESET) - reads,
3655 1
3656 );
3657
3658 let (_, result) = front.socket_write(b"after the reset");
3659 assert_eq!(result, SocketResult::Closed);
3660 assert_eq!(
3661 super::tests::proxy_count(names::rustls::WRITE_RESET) - writes,
3662 0,
3663 "a write after a reset read never reaches the socket"
3664 );
3665 assert_eq!(super::tests::proxy_count(names::tcp::READ_RESET) - plain, 0);
3666 }
3667
3668 /// A TLS write that meets the peer's RST answers `Closed`, still counts
3669 /// `rustls.write.error`, and counts `rustls.write.reset` once: the next
3670 /// write short-circuits on `peer_reset`.
3671 ///
3672 /// To SEE THIS RED: drop `incr!(names::rustls::WRITE_RESET)` from
3673 /// `flush_tls`.
3674 #[test]
3675 fn a_tls_write_counts_a_peer_reset_once() {
3676 let mut front = reset_front();
3677 let resets = super::tests::proxy_count(names::rustls::WRITE_RESET);
3678 let errors = super::tests::proxy_count(names::rustls::WRITE_ERROR);
3679
3680 let (_, result) = front.socket_write(b"after the reset");
3681 assert_eq!(result, SocketResult::Closed);
3682 assert!(front.peer_reset);
3683 assert_eq!(
3684 super::tests::proxy_count(names::rustls::WRITE_RESET) - resets,
3685 1
3686 );
3687 assert_eq!(
3688 super::tests::proxy_count(names::rustls::WRITE_ERROR) - errors,
3689 1
3690 );
3691
3692 let (_, result) = front.socket_write_vectored(&[std::io::IoSlice::new(b"after the reset")]);
3693 assert_eq!(result, SocketResult::Closed);
3694 assert_eq!(
3695 super::tests::proxy_count(names::rustls::WRITE_RESET) - resets,
3696 1,
3697 "the reset is counted once"
3698 );
3699 }
3700
3701 /// A settled TLS frontend over a socket whose connection the kernel
3702 /// refused: its first write answers `ECONNREFUSED`, which no arm but the
3703 /// generic one names.
3704 fn refused_front() -> FrontRustls {
3705 let address = std::net::TcpListener::bind("127.0.0.1:0")
3706 .expect("test listener must bind to a loopback port")
3707 .local_addr()
3708 .expect("test listener must report its local address");
3709 // The listener is gone: the connect below meets a RST.
3710 let socket =
3711 Socket::new(Domain::IPV4, Type::STREAM, None).expect("a TCP socket must be created");
3712 socket
3713 .set_nonblocking(true)
3714 .expect("mio requires a nonblocking stream");
3715 if let Err(e) = socket.connect(&address.into()) {
3716 assert_eq!(
3717 e.raw_os_error(),
3718 Some(libc::EINPROGRESS),
3719 "a nonblocking connect must be in progress, got {e}"
3720 );
3721 }
3722 let mut pollfd = libc::pollfd {
3723 fd: std::os::unix::io::AsRawFd::as_raw_fd(&socket),
3724 events: libc::POLLOUT,
3725 revents: 0,
3726 };
3727 // SAFETY: one valid `pollfd` borrowed for the duration of the call.
3728 let ready = unsafe { libc::poll(&mut pollfd, 1, 5_000) };
3729 assert_eq!(ready, 1, "the refusal must arrive within 5 s");
3730 assert_ne!(
3731 pollfd.revents & libc::POLLERR,
3732 0,
3733 "premise: the kernel must have refused the connection"
3734 );
3735 let (session, _client) = handshaken_pair();
3736 FrontRustls {
3737 stream: TcpStream::from_std(std::net::TcpStream::from(socket)),
3738 session,
3739 peer_disconnected: false,
3740 peer_reset: false,
3741 tls_fatal: false,
3742 session_ulid: Ulid::generate(),
3743 configured_peer: None,
3744 recv_memory: RecvMemory::default(),
3745 }
3746 }
3747
3748 /// A TLS write that meets a socket error no other arm names answers
3749 /// `Error` and marks the channel dead: the record rustls still holds can
3750 /// never be delivered, so `socket_wants_write` stops reporting it, and the
3751 /// next write short-circuits without reaching the socket. Otherwise the
3752 /// record kept the H1 session's flush pending, which delays its close on
3753 /// the hang-up, and a mux pass that re-raised WRITABLE for it retried the
3754 /// failing write (sozu-proxy/sozu#1780).
3755 ///
3756 /// To SEE THIS RED: drop `self.peer_reset = true;` from the generic arm
3757 /// of the `flush_tls` loop in `FrontRustls::socket_write`.
3758 #[test]
3759 fn a_tls_write_that_meets_a_socket_error_stops_wanting_to_write() {
3760 let mut front = refused_front();
3761 let errors = super::tests::proxy_count(names::rustls::WRITE_ERROR);
3762
3763 let (_, result) = front.socket_write(b"refused by the kernel");
3764 assert_eq!(result, SocketResult::Error);
3765 assert_eq!(
3766 super::tests::proxy_count(names::rustls::WRITE_ERROR) - errors,
3767 1
3768 );
3769 assert!(
3770 front.session.wants_write(),
3771 "premise: rustls must still hold the record the kernel refused"
3772 );
3773 assert!(
3774 !front.socket_wants_write(),
3775 "a record no write can deliver must not read as a pending write"
3776 );
3777
3778 let writes = tls_writes();
3779 let (_, result) = front.socket_write_vectored(&[std::io::IoSlice::new(b"after the error")]);
3780 assert_eq!(result, SocketResult::Closed);
3781 assert_eq!(
3782 tls_writes(),
3783 writes,
3784 "a write after the error must not reach the socket"
3785 );
3786 }
3787
3788 /// `refused_front` with records already queued in rustls and nothing
3789 /// written yet, for the empty-buffer flushes that only drain them.
3790 fn refused_front_holding_records() -> FrontRustls {
3791 let mut front = refused_front();
3792 front
3793 .session
3794 .writer()
3795 .write_all(b"queued before the flush")
3796 .expect("rustls must absorb the plaintext");
3797 assert!(
3798 front.session.wants_write(),
3799 "premise: rustls must hold the record the flush will try to write"
3800 );
3801 front
3802 }
3803
3804 /// The write that met the socket error answered `Error`, and the record
3805 /// rustls still holds no longer reads as a pending write.
3806 fn assert_error_marks_the_channel_dead(front: &FrontRustls, result: SocketResult) {
3807 assert_eq!(result, SocketResult::Error);
3808 assert!(
3809 front.session.wants_write(),
3810 "premise: rustls must still hold the record the kernel refused"
3811 );
3812 assert!(
3813 front.peer_reset && !front.socket_wants_write(),
3814 "an Error write must mark the channel dead, or the record keeps the \
3815 session waiting for a WRITABLE edge that never comes"
3816 );
3817 }
3818
3819 /// The empty-buffer flush of `socket_write` — the one `H2Shell` issues to
3820 /// push queued records — marks the channel dead on a socket error.
3821 ///
3822 /// TO SEE THIS RED: drop the `self.peer_reset = true;` of the `is_error`
3823 /// return in `FrontRustls::socket_write`.
3824 #[test]
3825 fn an_empty_tls_flush_that_meets_a_socket_error_marks_the_channel_dead() {
3826 let mut front = refused_front_holding_records();
3827 let (_, result) = front.socket_write(&[]);
3828 assert_error_marks_the_channel_dead(&front, result);
3829 }
3830
3831 /// A vectored write rustls absorbs whole, then flushes in the main loop.
3832 ///
3833 /// TO SEE THIS RED: drop the `self.peer_reset = true;` of the `is_error`
3834 /// return in `FrontRustls::socket_write_vectored`.
3835 #[test]
3836 fn a_vectored_tls_write_that_meets_a_socket_error_marks_the_channel_dead() {
3837 let mut front = refused_front();
3838 let (size, result) = front.socket_write_vectored(&[
3839 std::io::IoSlice::new(b"refused "),
3840 std::io::IoSlice::new(b"by the kernel"),
3841 ]);
3842 assert_eq!(size, 21, "premise: rustls must absorb the whole write");
3843 assert_error_marks_the_channel_dead(&front, result);
3844 }
3845
3846 /// A vectored write rustls absorbs only in part (its send buffer is
3847 /// capped), which takes the partial-write flush before returning.
3848 ///
3849 /// TO SEE THIS RED: the same edit as the whole-write test above.
3850 #[test]
3851 fn a_partial_vectored_tls_write_that_meets_a_socket_error_marks_the_channel_dead() {
3852 let mut front = refused_front();
3853 front.session.set_buffer_limit(Some(64));
3854 let payload = vec![b'x'; 4096];
3855 let (size, result) = front.socket_write_vectored(&[std::io::IoSlice::new(&payload)]);
3856 assert!(
3857 size > 0 && size < payload.len(),
3858 "premise: rustls must absorb part of the write, got {size}"
3859 );
3860 assert_error_marks_the_channel_dead(&front, result);
3861 }
3862
3863 /// The empty-buffer flush of `socket_write_vectored`.
3864 ///
3865 /// TO SEE THIS RED: the same edit as the whole-write test above.
3866 #[test]
3867 fn an_empty_vectored_tls_flush_that_meets_a_socket_error_marks_the_channel_dead() {
3868 let mut front = refused_front_holding_records();
3869 let (_, result) = front.socket_write_vectored(&[]);
3870 assert_error_marks_the_channel_dead(&front, result);
3871 }
3872
3873 /// A transport whose first write is interrupted (`EINTR`) and whose next
3874 /// one takes everything.
3875 #[derive(Default)]
3876 struct InterruptedOnce {
3877 calls: usize,
3878 written: Vec<u8>,
3879 }
3880
3881 impl std::io::Write for InterruptedOnce {
3882 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
3883 self.calls += 1;
3884 if self.calls == 1 {
3885 return Err(std::io::Error::from(ErrorKind::Interrupted));
3886 }
3887 self.written.extend_from_slice(buf);
3888 Ok(buf.len())
3889 }
3890
3891 fn flush(&mut self) -> std::io::Result<()> {
3892 Ok(())
3893 }
3894 }
3895
3896 /// An interrupted TLS write is retried, not reported: as `WouldBlock` no
3897 /// edge would resume it, and as an error it would mark the channel dead.
3898 ///
3899 /// TO SEE THIS RED: remove the `Interrupted => continue` arm of the retry
3900 /// loop in `flush_tls`.
3901 #[test]
3902 fn an_interrupted_tls_write_is_retried() {
3903 let (mut session, _client) = handshaken_pair();
3904 session
3905 .writer()
3906 .write_all(b"interrupted once")
3907 .expect("rustls must absorb the plaintext");
3908 let mut transport = InterruptedOnce::default();
3909
3910 let written = flush_tls(&mut session, &mut transport)
3911 .expect("an interrupted write must be retried, not reported");
3912
3913 assert!(written > 0 && transport.written.len() == written);
3914 assert_eq!(transport.calls, 2, "one interrupted write, one retry");
3915 assert!(!session.wants_write(), "the retry must deliver the record");
3916 }
3917
3918 /// An interrupted plain TCP write is retried, not reported: as an error it
3919 /// would close the session (the pipe path closes on a front write error).
3920 ///
3921 /// TO SEE THIS RED: remove the `Interrupted` arm of the write loop in
3922 /// `plain_socket_write`.
3923 #[test]
3924 fn an_interrupted_plain_write_is_retried() {
3925 let mut transport = InterruptedOnce::default();
3926
3927 let (size, result, fault) = plain_socket_write(&mut transport, b"interrupted once");
3928
3929 assert!(fault.is_none(), "unexpected write fault: {fault:?}");
3930 assert_eq!((size, result), (16, SocketResult::Continue));
3931 assert_eq!(transport.calls, 2, "one interrupted write, one retry");
3932 assert_eq!(transport.written, b"interrupted once");
3933 }
3934
3935 /// The vectored plain TCP write retries an interruption the same way.
3936 ///
3937 /// TO SEE THIS RED: remove the `Interrupted` arm of the retry loop in
3938 /// `plain_socket_write_vectored`.
3939 #[test]
3940 fn an_interrupted_plain_vectored_write_is_retried() {
3941 let mut transport = InterruptedOnce::default();
3942 let bufs = [std::io::IoSlice::new(b"interrupted once")];
3943
3944 let (size, result, fault) = plain_socket_write_vectored(&mut transport, &bufs);
3945
3946 assert!(fault.is_none(), "unexpected write fault: {fault:?}");
3947 assert_eq!((size, result), (16, SocketResult::Continue));
3948 assert_eq!(transport.calls, 2, "one interrupted write, one retry");
3949 assert_eq!(transport.written, b"interrupted once");
3950 }
3951
3952 /// An interrupted plain TCP read is retried, not reported as an error.
3953 ///
3954 /// TO SEE THIS RED: remove the `Interrupted` arm of the retry loop in
3955 /// `plain_socket_read`.
3956 #[test]
3957 fn an_interrupted_plain_read_is_retried() {
3958 let mut transport = CountingTransport {
3959 wire: b"interrupted once".iter().copied().collect(),
3960 interrupts: 1,
3961 ..Default::default()
3962 };
3963 let mut buf = [0u8; 32];
3964
3965 let (size, result, fault) = plain_socket_read(&mut transport, &mut buf);
3966
3967 assert!(fault.is_none(), "unexpected read fault: {fault:?}");
3968 assert_eq!((size, result), (16, SocketResult::WouldBlock));
3969 assert_eq!(&buf[..size], b"interrupted once");
3970 assert_eq!(transport.reads, 2, "one interrupted read, one retry");
3971 }
3972
3973 /// An interrupted `recv` under `read_tls` is retried, not reported as a
3974 /// `ReadTls` fault.
3975 ///
3976 /// TO SEE THIS RED: remove the `Interrupted` arm of the `read_tls` retry
3977 /// loop in `rustls_socket_read`.
3978 #[test]
3979 fn an_interrupted_tls_read_is_retried() {
3980 let (mut front, mut client) = front_and_client();
3981 let mut transport = CountingTransport {
3982 wire: seal(&mut client, b"interrupted once").into(),
3983 interrupts: 1,
3984 ..Default::default()
3985 };
3986
3987 let (read, result) = front.read(&mut transport, 32);
3988
3989 assert_eq!(read, b"interrupted once");
3990 assert_eq!(result, SocketResult::WouldBlock);
3991 assert_eq!(transport.reads, 2, "one interrupted read, one retry");
3992 }
3993
3994 /// Read everything `transport` holds through [`plain_socket_read`] with a
3995 /// `len`-byte buffer, one call at a time, until a call answers something
3996 /// other than `Continue`.
3997 fn plain_read_all(transport: &mut CountingTransport, len: usize) -> (Vec<u8>, SocketResult) {
3998 let mut received = Vec::new();
3999 loop {
4000 let mut buf = vec![0u8; len];
4001 let (size, result, fault) = plain_socket_read(transport, &mut buf);
4002 assert!(fault.is_none(), "unexpected read fault: {fault:?}");
4003 received.extend_from_slice(&buf[..size]);
4004 if result != SocketResult::Continue {
4005 return (received, result);
4006 }
4007 assert_eq!(size, len, "Continue means the buffer filled");
4008 }
4009 }
4010
4011 /// A request smaller than the buffer (the H1 path, one request per
4012 /// connection) costs one `recv`: it answers fewer bytes than offered, the
4013 /// queue is empty, and the call reports `WouldBlock` with the bytes.
4014 ///
4015 /// Before the stop on a short read `tcp_socket_read` looped and counted 2
4016 /// reads, the second one EAGAIN.
4017 #[test]
4018 fn a_short_plain_read_costs_one_recv_and_no_eagain() {
4019 let request = b"GET /index.html HTTP/1.1\r\nHost: lolcatho.st\r\n\r\n".to_vec();
4020 let mut transport = CountingTransport {
4021 wire: request.iter().copied().collect(),
4022 ..Default::default()
4023 };
4024
4025 let mut buf = vec![0u8; 16 * 1024];
4026 let (size, result, fault) = plain_socket_read(&mut transport, &mut buf);
4027 assert!(fault.is_none());
4028 assert_eq!(&buf[..size], &request[..]);
4029 assert_eq!(result, SocketResult::WouldBlock);
4030 assert_eq!(
4031 (transport.reads, transport.eagains),
4032 (1, 0),
4033 "one short recv and no EAGAIN"
4034 );
4035 }
4036
4037 /// A read that fills the buffer is not a short read: it answers
4038 /// `Continue`, READABLE stays, and the next call reads on.
4039 #[test]
4040 fn a_full_plain_buffer_is_not_a_short_read() {
4041 let mut transport = CountingTransport {
4042 wire: (0..250u8).collect(),
4043 ..Default::default()
4044 };
4045
4046 let mut buf = vec![0u8; 100];
4047 let (size, result, _) = plain_socket_read(&mut transport, &mut buf);
4048 assert_eq!((size, result), (100, SocketResult::Continue));
4049 let (size, result, _) = plain_socket_read(&mut transport, &mut buf);
4050 assert_eq!((size, result), (100, SocketResult::Continue));
4051 let (size, result, _) = plain_socket_read(&mut transport, &mut buf);
4052 assert_eq!((size, result), (50, SocketResult::WouldBlock));
4053 assert_eq!((transport.reads, transport.eagains), (3, 0));
4054 }
4055
4056 /// Data followed by a FIN: the short read delivers the data, and the next
4057 /// call reads the EOF as `Closed`. An empty queue with a FIN is `Closed`
4058 /// at once.
4059 #[test]
4060 fn a_fin_behind_a_short_plain_read_is_closed_on_the_next_call() {
4061 let mut transport = CountingTransport {
4062 wire: (0..50u8).collect(),
4063 eof: true,
4064 ..Default::default()
4065 };
4066
4067 let mut buf = vec![0u8; 16 * 1024];
4068 let (size, result, _) = plain_socket_read(&mut transport, &mut buf);
4069 assert_eq!((size, result), (50, SocketResult::WouldBlock));
4070 assert_eq!(buf[..size], (0..50u8).collect::<Vec<_>>()[..]);
4071 let (size, result, _) = plain_socket_read(&mut transport, &mut buf);
4072 assert_eq!((size, result), (0, SocketResult::Closed));
4073 let (size, result, _) = plain_socket_read(&mut transport, &mut buf);
4074 assert_eq!((size, result), (0, SocketResult::Closed), "EOF stays EOF");
4075 assert_eq!(transport.eagains, 0);
4076 }
4077
4078 /// A message arriving in fragments: each arrival costs one short `recv`
4079 /// and ends its call, and the bytes concatenate in order.
4080 #[test]
4081 fn a_fragmented_plain_message_costs_one_recv_per_arrival() {
4082 let message: Vec<u8> = (0..70u8).collect();
4083 let mut transport = CountingTransport::default();
4084 let mut received = Vec::new();
4085
4086 for chunk in message.chunks(30) {
4087 transport.wire.extend(chunk);
4088 let mut buf = vec![0u8; 16 * 1024];
4089 let (size, result, _) = plain_socket_read(&mut transport, &mut buf);
4090 assert_eq!((size, result), (chunk.len(), SocketResult::WouldBlock));
4091 received.extend_from_slice(&buf[..size]);
4092 }
4093 assert_eq!(received, message);
4094 assert_eq!((transport.reads, transport.eagains), (3, 0));
4095 }
4096
4097 /// A body much larger than the buffer: every full read answers `Continue`,
4098 /// the final short read answers `WouldBlock`, every byte arrives in order,
4099 /// and no read answers EAGAIN.
4100 #[test]
4101 fn a_large_plain_body_arrives_intact_without_eagain() {
4102 let body: Vec<u8> = (0..(1024 * 1024 + 123)).map(|i| (i % 251) as u8).collect();
4103 let mut transport = CountingTransport {
4104 wire: body.iter().copied().collect(),
4105 ..Default::default()
4106 };
4107
4108 let (received, result) = plain_read_all(&mut transport, 16 * 1024);
4109 assert_eq!(result, SocketResult::WouldBlock);
4110 assert_eq!(received, body);
4111 assert_eq!(
4112 (transport.reads, transport.eagains),
4113 (65, 0),
4114 "64 full reads and the short tail"
4115 );
4116 }
4117
4118 /// A body that is an exact multiple of the buffer ends on a full read, so
4119 /// the only way to learn the queue is empty is the EAGAIN that follows: a
4120 /// full buffer is never taken for a short read.
4121 #[test]
4122 fn a_plain_body_ending_on_a_full_buffer_still_needs_its_eagain() {
4123 let body: Vec<u8> = (0..(4 * 16 * 1024)).map(|i| (i % 251) as u8).collect();
4124 let mut transport = CountingTransport {
4125 wire: body.iter().copied().collect(),
4126 ..Default::default()
4127 };
4128
4129 let (received, result) = plain_read_all(&mut transport, 16 * 1024);
4130 assert_eq!(result, SocketResult::WouldBlock);
4131 assert_eq!(received, body);
4132 assert_eq!((transport.reads, transport.eagains), (5, 1));
4133 }
4134
4135 /// The kernel property the stop relies on, on a real socket registered
4136 /// edge-triggered with mio: after a short read, bytes that arrive later
4137 /// raise a new READABLE event, and a FIN raises one too. No event is
4138 /// pending in between, so nothing else would have woken the reader.
4139 #[test]
4140 fn after_a_short_read_new_bytes_raise_a_new_edge() {
4141 use std::io::Write as _;
4142
4143 let token = mio::Token(7);
4144 let mut poll = mio::Poll::new().expect("a poll instance must open");
4145 let mut events = mio::Events::with_capacity(8);
4146 let listener = std::net::TcpListener::bind("127.0.0.1:0")
4147 .expect("test listener must bind to a loopback port");
4148 let mut client = std::net::TcpStream::connect(
4149 listener
4150 .local_addr()
4151 .expect("test listener must report its local address"),
4152 )
4153 .expect("loopback connect must complete");
4154 let (accepted, _) = listener.accept().expect("the connection must be accepted");
4155 accepted
4156 .set_nonblocking(true)
4157 .expect("mio requires a nonblocking stream");
4158 let mut server = TcpStream::from_std(accepted);
4159 poll.registry()
4160 .register(&mut server, token, mio::Interest::READABLE)
4161 .expect("the stream must register");
4162 let wait_for_edge = |poll: &mut mio::Poll, events: &mut mio::Events| {
4163 poll.poll(events, Some(std::time::Duration::from_secs(5)))
4164 .expect("poll must succeed");
4165 events
4166 .iter()
4167 .any(|event| event.token() == token && event.is_readable())
4168 };
4169 let mut buf = vec![0u8; 16 * 1024];
4170
4171 client.write_all(b"first").expect("the client must write");
4172 assert!(wait_for_edge(&mut poll, &mut events), "the first arrival");
4173 assert_eq!(
4174 tcp_socket_read(&mut server, &mut buf, None, None),
4175 (5, SocketResult::WouldBlock)
4176 );
4177
4178 poll.poll(&mut events, Some(std::time::Duration::from_millis(50)))
4179 .expect("poll must succeed");
4180 assert!(events.is_empty(), "edge-triggered: nothing new, no event");
4181
4182 client.write_all(b"second").expect("the client must write");
4183 assert!(
4184 wait_for_edge(&mut poll, &mut events),
4185 "bytes arriving after a short read must raise a new edge"
4186 );
4187 assert_eq!(
4188 tcp_socket_read(&mut server, &mut buf, None, None),
4189 (6, SocketResult::WouldBlock)
4190 );
4191
4192 client
4193 .shutdown(std::net::Shutdown::Write)
4194 .expect("the client must half-close");
4195 assert!(wait_for_edge(&mut poll, &mut events), "the FIN");
4196 assert_eq!(
4197 tcp_socket_read(&mut server, &mut buf, None, None),
4198 (0, SocketResult::Closed)
4199 );
4200 }
4201}