Skip to main content

sozu_command_lib/
channel.rs

1//! Bidirectional length-delimited unix-socket channel.
2//!
3//! Implements the master ↔ worker / master ↔ CLI message channel: each
4//! payload is preceded by a native `usize` length prefix (NOT a NUL
5//! separator — that scheme belongs to the state-file save format written
6//! by `ConfigState::write_requests_to_file` in `command/src/state.rs`).
7//! Bounded by the per-channel `Channel::max_buffer_size`, checked against
8//! the declared length in `try_read_delimited_message` before any payload
9//! byte is allocated.
10
11use std::{
12    cmp::min,
13    fmt::Debug,
14    io::{self, ErrorKind, Read, Write},
15    marker::PhantomData,
16    mem,
17    ops::Deref,
18    os::unix::{
19        io::{AsRawFd, FromRawFd, IntoRawFd, RawFd},
20        net::UnixStream as StdUnixStream,
21    },
22    time::Duration,
23};
24
25use mio::{event::Source, net::UnixStream as MioUnixStream};
26use prost::{DecodeError, Message as ProstMessage};
27use zeroize::{Zeroize, Zeroizing};
28
29use crate::{buffer::growable::Buffer, ready::Ready};
30
31/// High watermark threshold: log a warning when buffer usage exceeds 80% of max
32const HIGH_WATERMARK_RATIO: f64 = 0.8;
33const CHANNEL_SNAPSHOT_VERSION: u16 = 1;
34const CHANNEL_READY_MASK: u16 =
35    Ready::READABLE.0 | Ready::WRITABLE.0 | Ready::ERROR.0 | Ready::HUP.0 | Ready::WRITE_CLOSED.0;
36
37#[derive(thiserror::Error, Debug)]
38pub enum ChannelError {
39    #[error("io read error")]
40    Read(std::io::Error),
41    #[error("no byte written on the channel")]
42    NoByteWritten,
43    #[error("no byte left to read on the channel")]
44    NoByteToRead,
45    #[error(
46        "message ({message_len} bytes) too large for back buffer capacity ({capacity} bytes, max {max} bytes)"
47    )]
48    MessageTooLarge {
49        message_len: usize,
50        capacity: usize,
51        max: usize,
52    },
53    #[error(
54        "declared message length ({message_len} bytes) is shorter than the {delimiter_size}-byte length prefix"
55    )]
56    MessageLengthUnderDelimiter {
57        message_len: usize,
58        delimiter_size: usize,
59    },
60    #[error("io write error")]
61    Write(std::io::Error),
62    #[error("channel buffer is full ({capacity} bytes, max {max} bytes), cannot grow more")]
63    BufferFull { capacity: usize, max: usize },
64    #[error("Timeout is reached: {0:?}")]
65    TimeoutReached(Duration),
66    #[error("Could not read anything on the channel")]
67    NothingRead,
68    #[error("invalid char set in command message, ignoring: {0}")]
69    InvalidCharSet(String),
70    #[error("could not set the timeout of the unix stream with file descriptor {fd}: {error}")]
71    SetTimeout { fd: i32, error: String },
72    #[error(
73        "Could not change the blocking status ef the unix stream with file descriptor {fd}: {error}"
74    )]
75    BlockingStatus { fd: i32, error: String },
76    #[error("Connection error: {0:?}")]
77    Connection(Option<std::io::Error>),
78    #[error("Invalid protobuf message: {0}")]
79    InvalidProtobufMessage(DecodeError),
80    #[error("This should never happen (index out of bound on a tested buffer)")]
81    MismatchBufferSize,
82}
83
84/// Serializable userspace state of a [`Channel`].
85///
86/// The Hub supplies the effective descriptor after fork/exec inheritance or
87/// explicit duplication. Only bytes that have not been consumed are captured;
88/// buffer offsets are normalized on restore while their capacities and
89/// backpressure state are preserved.
90#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
91pub struct ChannelSnapshot {
92    version: u16,
93    front: SnapshotBytes,
94    back: SnapshotBytes,
95    initial_buffer_size: usize,
96    max_buffer_size: usize,
97    front_buffer_capacity: usize,
98    back_buffer_capacity: usize,
99    readiness: u16,
100    interest: u16,
101    blocking: bool,
102    front_high_watermark_logged: bool,
103    back_high_watermark_logged: bool,
104}
105
106/// Owner of the unconsumed bytes a [`ChannelSnapshot`] carries.
107///
108/// Those bytes can be frames holding protected values, and a plain `Vec<u8>`
109/// leaves copies of them behind: serde grows it by `push`, so every
110/// reallocation frees the previous block unwiped, and a deserialization that
111/// fails after receiving some elements drops a half-built vector. This type
112/// wipes on drop (hence also the clone's own copy), wipes the previous block
113/// before a growth step releases it, and prints no bytes.
114///
115/// The wire form is the one `Vec<u8>` has, a sequence of unsigned integers, so
116/// serialized snapshots, and the partial frames they may carry, are unchanged.
117/// Only what lives in this process's memory is wiped: whatever buffer a
118/// serde format reads from or writes to is the caller's.
119#[derive(Clone, Eq, PartialEq)]
120struct SnapshotBytes(Vec<u8>);
121
122/// Capacity a deserialization starts from before doubling.
123const SNAPSHOT_BYTES_FLOOR: usize = 64;
124
125impl SnapshotBytes {
126    fn copy_of(bytes: &[u8]) -> Self {
127        SnapshotBytes(bytes.to_owned())
128    }
129
130    /// Append `byte`, widening into a fresh allocation and wiping the previous
131    /// one first when the current one is full.
132    fn push(&mut self, byte: u8) {
133        if self.0.len() == self.0.capacity() {
134            let wider = Vec::with_capacity(
135                self.0
136                    .capacity()
137                    .saturating_mul(2)
138                    .max(SNAPSHOT_BYTES_FLOOR),
139            );
140            let mut previous = mem::replace(&mut self.0, wider);
141            self.0.extend_from_slice(&previous);
142            previous.zeroize();
143        }
144        self.0.push(byte);
145    }
146}
147
148impl Deref for SnapshotBytes {
149    type Target = [u8];
150
151    fn deref(&self) -> &[u8] {
152        &self.0
153    }
154}
155
156impl Drop for SnapshotBytes {
157    fn drop(&mut self) {
158        self.0.zeroize();
159    }
160}
161
162impl Debug for SnapshotBytes {
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        write!(f, "<{} bytes redacted>", self.0.len())
165    }
166}
167
168impl serde::Serialize for SnapshotBytes {
169    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
170        serializer.collect_seq(&self.0)
171    }
172}
173
174impl<'de> serde::Deserialize<'de> for SnapshotBytes {
175    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
176        struct BytesVisitor;
177
178        impl<'de> serde::de::Visitor<'de> for BytesVisitor {
179            type Value = SnapshotBytes;
180
181            fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182                formatter.write_str("a sequence")
183            }
184
185            fn visit_seq<A: serde::de::SeqAccess<'de>>(
186                self,
187                mut seq: A,
188            ) -> Result<SnapshotBytes, A::Error> {
189                // An early return drops `bytes`, which wipes what it received.
190                let mut bytes = SnapshotBytes(Vec::new());
191                while let Some(byte) = seq.next_element::<u8>()? {
192                    bytes.push(byte);
193                }
194                Ok(bytes)
195            }
196        }
197
198        deserializer.deserialize_seq(BytesVisitor)
199    }
200}
201
202#[derive(thiserror::Error, Debug, Eq, PartialEq)]
203pub enum ChannelSnapshotError {
204    #[error("unsupported channel snapshot version {version}; supported version is {supported}")]
205    UnsupportedVersion { version: u16, supported: u16 },
206    #[error(
207        "invalid channel snapshot buffer bounds: initial capacity {initial} exceeds maximum {max}"
208    )]
209    InvalidBufferBounds { initial: usize, max: usize },
210    #[error(
211        "invalid {buffer} buffer capacity {capacity}; expected a value between initial {initial} and maximum {max}"
212    )]
213    InvalidBufferCapacity {
214        buffer: &'static str,
215        capacity: usize,
216        initial: usize,
217        max: usize,
218    },
219    #[error(
220        "{buffer} buffer carries {data_len} bytes but its restored capacity is only {capacity}"
221    )]
222    BufferedDataExceedsCapacity {
223        buffer: &'static str,
224        data_len: usize,
225        capacity: usize,
226    },
227    #[error("invalid channel snapshot {field} bits: {bits:#06x}")]
228    InvalidReadyBits { field: &'static str, bits: u16 },
229    #[error(
230        "channel snapshot buffer limits ({snapshot_initial}, {snapshot_max}) do not match the receiver's configured limits ({expected_initial}, {expected_max})"
231    )]
232    BufferLimitsMismatch {
233        snapshot_initial: usize,
234        snapshot_max: usize,
235        expected_initial: usize,
236        expected_max: usize,
237    },
238    #[error("could not read blocking mode of channel descriptor {fd}: {error}")]
239    BlockingModeQuery { fd: RawFd, error: String },
240    #[error(
241        "channel snapshot blocking mode ({snapshot_blocking}) disagrees with descriptor blocking mode ({fd_blocking})"
242    )]
243    BlockingModeMismatch {
244        snapshot_blocking: bool,
245        fd_blocking: bool,
246    },
247}
248
249impl ChannelSnapshot {
250    /// Whether restoring this channel leaves input already available in
251    /// userspace, independently of kernel readiness notifications.
252    pub fn has_buffered_input(&self) -> bool {
253        !self.front.is_empty()
254    }
255
256    /// Whether restoring this channel leaves output queued in userspace,
257    /// independently of kernel readiness notifications.
258    pub fn has_buffered_output(&self) -> bool {
259        !self.back.is_empty()
260    }
261
262    /// Total userspace bytes that the kernel cannot report through readiness.
263    pub fn buffered_bytes(&self) -> usize {
264        self.front.len().saturating_add(self.back.len())
265    }
266
267    fn validate(&self) -> Result<(), ChannelSnapshotError> {
268        if self.version != CHANNEL_SNAPSHOT_VERSION {
269            return Err(ChannelSnapshotError::UnsupportedVersion {
270                version: self.version,
271                supported: CHANNEL_SNAPSHOT_VERSION,
272            });
273        }
274        if self.initial_buffer_size > self.max_buffer_size {
275            return Err(ChannelSnapshotError::InvalidBufferBounds {
276                initial: self.initial_buffer_size,
277                max: self.max_buffer_size,
278            });
279        }
280
281        for (buffer, capacity) in [
282            ("front", self.front_buffer_capacity),
283            ("back", self.back_buffer_capacity),
284        ] {
285            if capacity < self.initial_buffer_size || capacity > self.max_buffer_size {
286                return Err(ChannelSnapshotError::InvalidBufferCapacity {
287                    buffer,
288                    capacity,
289                    initial: self.initial_buffer_size,
290                    max: self.max_buffer_size,
291                });
292            }
293        }
294
295        for (buffer, data_len, capacity) in [
296            ("front", self.front.len(), self.front_buffer_capacity),
297            ("back", self.back.len(), self.back_buffer_capacity),
298        ] {
299            if data_len > capacity {
300                return Err(ChannelSnapshotError::BufferedDataExceedsCapacity {
301                    buffer,
302                    data_len,
303                    capacity,
304                });
305            }
306        }
307
308        for (field, bits) in [("readiness", self.readiness), ("interest", self.interest)] {
309            if bits & !CHANNEL_READY_MASK != 0 {
310                return Err(ChannelSnapshotError::InvalidReadyBits { field, bits });
311            }
312        }
313
314        Ok(())
315    }
316}
317
318/// Does this read failure leave a channel that could parse the peer's next
319/// frame, if only it were allowed to refill from the socket?
320///
321/// `readable()` drops `Ready::READABLE` from `interest` whenever it fills
322/// `front_buf` to a capacity it may not grow past, and then refuses to run at
323/// all while the bit is missing (`if !(self.interest & self.readiness)
324/// .is_readable()`). That is correct backpressure, and the parse that frees
325/// room is what undoes it -- but until sozu-proxy/sozu#1445 only ONE parse
326/// outcome did: `read_message_nonblocking` re-armed on its `NothingRead` path
327/// and propagated every error through `?` before reaching it. A frame the
328/// parser rejected and then re-synchronised past therefore re-aligned a buffer
329/// it could no longer refill, and the bytes still in the socket were stranded
330/// on a session `wants_to_tick` (`bin/src/command/sessions.rs`) does not
331/// re-schedule for being merely readable.
332///
333/// The answer is per-variant, not a blanket re-arm before the `?`: a socket
334/// that just failed a `read(2)`, or a peer this end has decided to drop, must
335/// not have its readability re-asserted. The match below is exhaustive with no
336/// wildcard arm ON PURPOSE -- a variant added to `ChannelError` must fail to
337/// compile here rather than inherit someone else's disposition.
338///
339/// Re-arm (the channel retired bytes and is framed on a boundary again; the
340/// only thing missing is more bytes):
341///
342/// - `NothingRead` -- the frame is simply incomplete. The pre-existing case,
343///   and the reason the bit is ever re-armed at all.
344/// - `MessageLengthUnderDelimiter` -- the parser consumed exactly
345///   `delimiter_size()` bytes carrying a declared length no writer can emit for
346///   any payload, so those bytes are provably not a header and skipping them
347///   re-aligns the stream. Re-syncing a buffer that may never refill only
348///   re-syncs the bytes already in hand, which is the case #1445 was filed on.
349/// - `InvalidProtobufMessage` -- since sozu-proxy/sozu#1428 the whole frame is
350///   consumed before the decode failure is returned, so the buffer is left
351///   exactly as the decode-success path leaves it. The frame is lost; the
352///   channel is not.
353///
354/// Do not re-arm:
355///
356/// - `MessageTooLarge` -- twice not, and the first reason does not depend on
357///   the second. It consumes NOTHING: unlike its two siblings above it cannot,
358///   because the bytes behind the header may be payload rather than a fresh
359///   delimiter and re-framing on them would decode peer-chosen bytes as a
360///   control-plane request. So the buffer is not on a boundary and refilling
361///   only re-parses the same header -- a busy loop, not a recovery, and the
362///   frame could not complete anyway, a length above `max_buffer_size` not
363///   fitting a buffer bounded by `max_buffer_size` however much more is read.
364///   On top of that the branch has marked `Ready::ERROR` since #1428, so
365///   re-arming would assert readability on a channel this end has already
366///   decided to close.
367/// - `BufferFull` -- past #1436's compaction this arm means a compacted buffer
368///   at the ceiling whose whole capacity cannot hold a length prefix, so
369///   `available_space()` is zero and cannot grow. `readable()` would read zero
370///   bytes and immediately remove the bit again; refilling cannot change the
371///   outcome.
372/// - `MismatchBufferSize` -- "this should never happen": a `try_into` on a
373///   slice whose length was just checked against `delimiter_size()`. It is an
374///   internal invariant violation, and the same slice fails identically on
375///   every retry, so re-arming would spin instead of recovering.
376/// - `NoByteToRead` -- `read(2)` returned zero, the peer is gone.
377///   `readable()` has already set `interest = Ready::EMPTY` and raised HUP;
378///   re-arming would contradict the hangup it just recorded.
379/// - `Read` -- a hard socket failure, after which `readable()` has already
380///   cleared `interest` and raised HUP and ERROR (sozu-proxy/sozu#1560).
381/// - `Connection` -- either a failed `connect(2)` or `readable()`/`writable()`
382///   rejecting the call because the interest gate is shut. Restoring from here
383///   the very bit that gate just refused is the loop this function exists to
384///   avoid.
385/// - `NoByteWritten`, `Write` -- back-buffer/socket write failures, raised on
386///   the write path and terminal there (`NoByteWritten` raises HUP, a
387///   `writable()` socket error raises HUP and ERROR); nothing about them says
388///   the read side may refill.
389/// - `TimeoutReached` -- the blocking read path's deadline
390///   (`read_message_blocking_timeout`), an operator-visible bound rather than a
391///   framing state.
392/// - `SetTimeout`, `BlockingStatus` -- `setsockopt`/`fcntl` failures on the
393///   underlying stream, i.e. blocking-mode plumbing.
394/// - `InvalidCharSet` -- declared here but constructed nowhere; the live
395///   variant of that name is `ScmSocketError::InvalidCharSet`
396///   (`command/src/scm_socket.rs`). It is listed so the match stays exhaustive,
397///   and it is not a framing state either way.
398///
399/// Exactly six of the fifteen reach this function today.
400/// `try_read_delimited_message` raises five of them -- `MessageTooLarge`,
401/// `MessageLengthUnderDelimiter`, `InvalidProtobufMessage`, `BufferFull` and
402/// `MismatchBufferSize` -- and `read_message_nonblocking` mints the sixth,
403/// `NothingRead`, from its `Ok(None)`. The other nine (`NoByteToRead`, `Read`,
404/// `Connection`, `NoByteWritten`, `Write`, `TimeoutReached`, `SetTimeout`,
405/// `BlockingStatus`, `InvalidCharSet`) are dispositioned anyway rather than
406/// left to a wildcard, because a future call site is exactly how a wildcard
407/// becomes a wrong answer nobody wrote down.
408fn rearms_readable(error: &ChannelError) -> bool {
409    match error {
410        ChannelError::NothingRead
411        | ChannelError::MessageLengthUnderDelimiter { .. }
412        | ChannelError::InvalidProtobufMessage(_) => true,
413
414        ChannelError::MessageTooLarge { .. }
415        | ChannelError::BufferFull { .. }
416        | ChannelError::MismatchBufferSize
417        | ChannelError::NoByteToRead
418        | ChannelError::Read(_)
419        | ChannelError::Connection(_)
420        | ChannelError::NoByteWritten
421        | ChannelError::Write(_)
422        | ChannelError::TimeoutReached(_)
423        | ChannelError::SetTimeout { .. }
424        | ChannelError::BlockingStatus { .. }
425        | ChannelError::InvalidCharSet(_) => false,
426    }
427}
428
429/// Channel meant for communication between Sōzu processes over a UNIX socket.
430/// It wraps a unix socket using the mio crate, and transmit prost messages
431/// by serializing them in a binary format, with a fix-sized delimiter.
432/// To function, channels must come in pairs, one for each agent.
433/// They can function in a blocking or non-blocking way.
434pub struct Channel<Tx, Rx> {
435    pub sock: MioUnixStream,
436    pub front_buf: Buffer,
437    pub back_buf: Buffer,
438    initial_buffer_size: usize,
439    max_buffer_size: usize,
440    pub readiness: Ready,
441    pub interest: Ready,
442    blocking: bool,
443    /// true if a high watermark warning has been logged for the front buffer
444    front_high_watermark_logged: bool,
445    /// true if a high watermark warning has been logged for the back buffer
446    back_high_watermark_logged: bool,
447    phantom_tx: PhantomData<Tx>,
448    phantom_rx: PhantomData<Rx>,
449}
450
451/// A restored channel that may be registered with mio but cannot perform I/O.
452///
453/// Keeping the active [`Channel`] private prevents the receiving process from
454/// reading or writing a shared socket before the upgrade handoff commits.
455pub struct PausedChannel<Tx, Rx> {
456    channel: Channel<Tx, Rx>,
457}
458
459impl<Tx, Rx> PausedChannel<Tx, Rx> {
460    /// Returns the received descriptor for handoff bookkeeping.
461    pub fn fd(&self) -> RawFd {
462        self.channel.sock.as_raw_fd()
463    }
464
465    /// Validate mio registration during the prepared handoff phase.
466    ///
467    /// Registration changes neither the socket's blocking mode nor its byte
468    /// streams. No readiness is handled until [`PausedChannel::resume`]
469    /// returns the active channel after commit.
470    pub fn register(
471        &mut self,
472        registry: &Registry,
473        token: Token,
474        interests: Interest,
475    ) -> io::Result<()> {
476        self.channel.sock.register(registry, token, interests)
477    }
478
479    /// Activate the restored channel after the upgrade handoff commits.
480    pub fn resume(self) -> Channel<Tx, Rx> {
481        self.channel
482    }
483}
484
485impl<Tx, Rx> std::fmt::Debug for Channel<Tx, Rx> {
486    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487        f.debug_struct(&format!(
488            "Channel<{}, {}>",
489            std::any::type_name::<Tx>(),
490            std::any::type_name::<Rx>()
491        ))
492        .field("sock", &self.sock.as_raw_fd())
493        // .field("front_buf", &self.front_buf)
494        // .field("back_buf", &self.back_buf)
495        // .field("max_buffer_size", &self.max_buffer_size)
496        .field("readiness", &self.readiness)
497        .field("interest", &self.interest)
498        .field("blocking", &self.blocking)
499        .finish()
500    }
501}
502
503impl<Tx: Debug + ProstMessage + Default, Rx: Debug + ProstMessage + Default> Channel<Tx, Rx> {
504    /// Creates a nonblocking channel on a given socket path
505    pub fn from_path(
506        path: &str,
507        buffer_size: u64,
508        max_buffer_size: u64,
509    ) -> Result<Channel<Tx, Rx>, ChannelError> {
510        let unix_stream = MioUnixStream::connect(path)
511            .map_err(|io_error| ChannelError::Connection(Some(io_error)))?;
512        Ok(Channel::new(unix_stream, buffer_size, max_buffer_size))
513    }
514
515    /// Creates a nonblocking channel, using a unix stream
516    ///
517    /// `buffer_size` is clamped to `max_buffer_size` (sozu-proxy/sozu#1416):
518    /// every growth/shrink path in this file — `grow_size`, `try_read_delimited_message`,
519    /// `try_shrink_front_buf`/`try_shrink_back_buf` — reasons about `front_buf`/`back_buf`
520    /// capacity never exceeding `max_buffer_size`; a caller-supplied `buffer_size` above
521    /// that ceiling would start the channel already violating the invariant those paths
522    /// assert, before a single byte is read.
523    ///
524    /// A clamp, not a fallible constructor, because `max_buffer_size` and
525    /// `initial_buffer_size` are private fields with no external struct-literal path
526    /// around this function: `Channel::new` is the *only* place a `Channel` is built, so
527    /// unlike `H2FloodConfig::new` (sozu-proxy/sozu#1418, whose clamp is bypassed in
528    /// production by a raw struct literal in `get_h2_flood_config`) this clamp cannot be
529    /// routed around — it runs on every construction, including the dozen-plus call sites
530    /// across `bin/`, `e2e/`, `lib/examples/` and this module's own tests. Config load
531    /// (`ConfigBuilder::into_config`) is the operator-facing gate and rejects the
532    /// misconfiguration with a named error before a `Channel` is ever built; this clamp is
533    /// the structural backstop for any other direct caller that bypasses config
534    /// validation (tests, examples, a future call site), so it logs instead of silently
535    /// doing nothing observable.
536    pub fn new(sock: MioUnixStream, buffer_size: u64, max_buffer_size: u64) -> Channel<Tx, Rx> {
537        let buffer_size = buffer_size as usize;
538        let max_buffer_size = max_buffer_size as usize;
539        let initial_buffer_size = min(buffer_size, max_buffer_size);
540        if initial_buffer_size < buffer_size {
541            warn!(
542                "channel buffer_size ({}) exceeds max_buffer_size ({}); clamping initial \
543                 buffer capacity to {}",
544                buffer_size, max_buffer_size, initial_buffer_size
545            );
546        }
547        // Postcondition: the invariant every growth/shrink path in this file assumes.
548        debug_assert!(
549            initial_buffer_size <= max_buffer_size,
550            "initial buffer capacity must never exceed max_buffer_size"
551        );
552        Channel {
553            sock,
554            front_buf: Buffer::with_capacity(initial_buffer_size),
555            back_buf: Buffer::with_capacity(initial_buffer_size),
556            initial_buffer_size,
557            max_buffer_size,
558            readiness: Ready::EMPTY,
559            interest: Ready::READABLE,
560            blocking: false,
561            front_high_watermark_logged: false,
562            back_high_watermark_logged: false,
563            phantom_tx: PhantomData,
564            phantom_rx: PhantomData,
565        }
566    }
567
568    /// Capture every userspace byte and scheduling bit needed to continue this
569    /// channel after a main-process upgrade.
570    pub fn snapshot(&self) -> ChannelSnapshot {
571        debug_assert!(self.initial_buffer_size <= self.max_buffer_size);
572        debug_assert!(self.front_buf.capacity() <= self.max_buffer_size);
573        debug_assert!(self.back_buf.capacity() <= self.max_buffer_size);
574
575        ChannelSnapshot {
576            version: CHANNEL_SNAPSHOT_VERSION,
577            front: SnapshotBytes::copy_of(self.front_buf.data()),
578            back: SnapshotBytes::copy_of(self.back_buf.data()),
579            initial_buffer_size: self.initial_buffer_size,
580            max_buffer_size: self.max_buffer_size,
581            front_buffer_capacity: self.front_buf.capacity(),
582            back_buffer_capacity: self.back_buf.capacity(),
583            readiness: self.readiness.0,
584            interest: self.interest.0,
585            blocking: self.blocking,
586            front_high_watermark_logged: self.front_high_watermark_logged,
587            back_high_watermark_logged: self.back_high_watermark_logged,
588        }
589    }
590
591    /// Rebuild a channel around a received socket without reading, writing, or
592    /// changing flags on the shared open file description.
593    ///
594    /// The receiver supplies the configured buffer limits independently. A
595    /// snapshot cannot use its own size fields to authorize an allocation.
596    /// The descriptor's actual `O_NONBLOCK` flag must likewise agree with the
597    /// captured mode; neither validation changes that shared flag.
598    ///
599    /// The returned wrapper permits mio registration during PREPARED, but only
600    /// [`PausedChannel::resume`] exposes the active channel after COMMIT.
601    pub fn restore_paused(
602        sock: MioUnixStream,
603        snapshot: ChannelSnapshot,
604        expected_initial_buffer_size: usize,
605        expected_max_buffer_size: usize,
606    ) -> Result<PausedChannel<Tx, Rx>, ChannelSnapshotError> {
607        snapshot.validate()?;
608
609        if snapshot.initial_buffer_size != expected_initial_buffer_size
610            || snapshot.max_buffer_size != expected_max_buffer_size
611        {
612            return Err(ChannelSnapshotError::BufferLimitsMismatch {
613                snapshot_initial: snapshot.initial_buffer_size,
614                snapshot_max: snapshot.max_buffer_size,
615                expected_initial: expected_initial_buffer_size,
616                expected_max: expected_max_buffer_size,
617            });
618        }
619
620        let fd = sock.as_raw_fd();
621        // SAFETY: `sock` owns a live descriptor for this call. F_GETFL only
622        // reads flags from its open file description and cannot change the
623        // descriptor shared with the old main process during PREPARED.
624        let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };
625        if flags < 0 {
626            return Err(ChannelSnapshotError::BlockingModeQuery {
627                fd,
628                error: io::Error::last_os_error().to_string(),
629            });
630        }
631        let fd_blocking = flags & libc::O_NONBLOCK == 0;
632        if snapshot.blocking != fd_blocking {
633            return Err(ChannelSnapshotError::BlockingModeMismatch {
634                snapshot_blocking: snapshot.blocking,
635                fd_blocking,
636            });
637        }
638
639        let mut front_buf = Buffer::from_slice(&snapshot.front);
640        front_buf.grow(snapshot.front_buffer_capacity);
641        let mut back_buf = Buffer::from_slice(&snapshot.back);
642        back_buf.grow(snapshot.back_buffer_capacity);
643
644        debug_assert_eq!(front_buf.capacity(), snapshot.front_buffer_capacity);
645        debug_assert_eq!(back_buf.capacity(), snapshot.back_buffer_capacity);
646        // Not `debug_assert_eq!`: a failure would print both operands, and
647        // these bytes can be protected.
648        debug_assert!(
649            front_buf.data() == &*snapshot.front,
650            "restored front bytes must equal the snapshot's"
651        );
652        debug_assert!(
653            back_buf.data() == &*snapshot.back,
654            "restored back bytes must equal the snapshot's"
655        );
656
657        Ok(PausedChannel {
658            channel: Channel {
659                sock,
660                front_buf,
661                back_buf,
662                initial_buffer_size: snapshot.initial_buffer_size,
663                max_buffer_size: snapshot.max_buffer_size,
664                readiness: Ready(snapshot.readiness),
665                interest: Ready(snapshot.interest),
666                blocking: snapshot.blocking,
667                front_high_watermark_logged: snapshot.front_high_watermark_logged,
668                back_high_watermark_logged: snapshot.back_high_watermark_logged,
669                phantom_tx: PhantomData,
670                phantom_rx: PhantomData,
671            },
672        })
673    }
674
675    pub fn into<Tx2: Debug + ProstMessage + Default, Rx2: Debug + ProstMessage + Default>(
676        self,
677    ) -> Channel<Tx2, Rx2> {
678        Channel {
679            sock: self.sock,
680            front_buf: self.front_buf,
681            back_buf: self.back_buf,
682            initial_buffer_size: self.initial_buffer_size,
683            max_buffer_size: self.max_buffer_size,
684            readiness: self.readiness,
685            interest: self.interest,
686            blocking: self.blocking,
687            front_high_watermark_logged: self.front_high_watermark_logged,
688            back_high_watermark_logged: self.back_high_watermark_logged,
689            phantom_tx: PhantomData,
690            phantom_rx: PhantomData,
691        }
692    }
693
694    // Since MioUnixStream does not have a set_nonblocking method, we have to use the standard library.
695    // We get the file descriptor of the MioUnixStream socket, create a standard library UnixStream,
696    // set it to nonblocking, let go of the file descriptor
697    fn set_nonblocking(&mut self, nonblocking: bool) -> Result<(), ChannelError> {
698        // SAFETY: `fd` is borrowed from `self.sock` for the duration of this
699        // block. We wrap it in a `StdUnixStream` to call `set_nonblocking`,
700        // then immediately release ownership again with `into_raw_fd` so the
701        // descriptor is not closed by `Drop`. `self.sock` retains the
702        // original ownership.
703        unsafe {
704            let fd = self.sock.as_raw_fd();
705            let stream = StdUnixStream::from_raw_fd(fd);
706            stream
707                .set_nonblocking(nonblocking)
708                .map_err(|error| ChannelError::BlockingStatus {
709                    fd,
710                    error: error.to_string(),
711                })?;
712            let _fd = stream.into_raw_fd();
713        }
714        self.blocking = !nonblocking;
715        Ok(())
716    }
717
718    /// set the read_timeout of the unix stream. This works only temporary, be sure to set the timeout to None afterwards.
719    fn set_timeout(&mut self, timeout: Option<Duration>) -> Result<(), ChannelError> {
720        // SAFETY: `fd` is borrowed from `self.sock` for the duration of this
721        // block. We wrap it in a `StdUnixStream` to call `set_read_timeout`,
722        // then immediately release ownership again with `into_raw_fd` so the
723        // descriptor is not closed by `Drop`. `self.sock` retains the
724        // original ownership.
725        unsafe {
726            let fd = self.sock.as_raw_fd();
727            let stream = StdUnixStream::from_raw_fd(fd);
728            stream
729                .set_read_timeout(timeout)
730                .map_err(|error| ChannelError::SetTimeout {
731                    fd,
732                    error: error.to_string(),
733                })?;
734            let _fd = stream.into_raw_fd();
735        }
736        Ok(())
737    }
738
739    /// set the channel to be blocking
740    pub fn blocking(&mut self) -> Result<(), ChannelError> {
741        self.set_nonblocking(false)
742    }
743
744    /// set the channel to be nonblocking
745    pub fn nonblocking(&mut self) -> Result<(), ChannelError> {
746        self.set_nonblocking(true)
747    }
748
749    pub fn is_blocking(&self) -> bool {
750        self.blocking
751    }
752
753    /// Get the raw file descriptor of the UNIX socket
754    pub fn fd(&self) -> RawFd {
755        self.sock.as_raw_fd()
756    }
757
758    pub fn handle_events(&mut self, events: Ready) {
759        self.readiness |= events;
760    }
761
762    pub fn readiness(&self) -> Ready {
763        self.readiness & self.interest
764    }
765
766    /// Compute the next buffer size using a doubling strategy, capped at max_buffer_size.
767    /// Returns None if the buffer is already at max capacity.
768    fn grow_size(&self, current_capacity: usize) -> Option<usize> {
769        if current_capacity >= self.max_buffer_size {
770            return None;
771        }
772        // Precondition: we only reach here below the ceiling, so a strictly
773        // larger size always exists within [current+1, max_buffer_size].
774        debug_assert!(
775            current_capacity < self.max_buffer_size,
776            "grow_size must only grow buffers that are strictly below the max ceiling"
777        );
778        // double the capacity, but don't exceed max
779        let new_size = min(current_capacity.saturating_mul(2), self.max_buffer_size);
780        // ensure we grow by at least something (in case current_capacity is 0)
781        let new_size = new_size.max(current_capacity + 1);
782        let new_size = min(new_size, self.max_buffer_size);
783        // Postconditions: the new capacity strictly grows (forward progress,
784        // no spin) and never overshoots the configured ceiling.
785        debug_assert!(
786            new_size > current_capacity,
787            "grow_size must make forward progress (new capacity strictly larger)"
788        );
789        debug_assert!(
790            new_size <= self.max_buffer_size,
791            "grow_size must never exceed the configured max_buffer_size ceiling"
792        );
793        Some(new_size)
794    }
795
796    /// Check if a buffer has exceeded the high watermark and log a warning once
797    fn check_high_watermark(
798        buffer_name: &str,
799        capacity: usize,
800        max: usize,
801        already_logged: &mut bool,
802    ) {
803        if *already_logged {
804            return;
805        }
806        let threshold = (max as f64 * HIGH_WATERMARK_RATIO) as usize;
807        if capacity >= threshold {
808            warn!(
809                "channel {} buffer reached high watermark: {} bytes ({:.0}% of {} max)",
810                buffer_name,
811                capacity,
812                (capacity as f64 / max as f64) * 100.0,
813                max,
814            );
815            *already_logged = true;
816        }
817    }
818
819    /// Check wether we want and can read or write, and calls the appropriate handler.
820    pub fn run(&mut self) -> Result<(), ChannelError> {
821        let interest = self.interest & self.readiness;
822
823        if interest.is_readable() {
824            let _ = self.readable()?;
825        }
826
827        if interest.is_writable() {
828            let _ = self.writable()?;
829        }
830        Ok(())
831    }
832
833    /// Handle readability by filling the front buffer with the socket data.
834    /// Grows the front buffer when full using a doubling strategy, up to max_buffer_size.
835    pub fn readable(&mut self) -> Result<usize, ChannelError> {
836        if !(self.interest & self.readiness).is_readable() {
837            return Err(ChannelError::Connection(None));
838        }
839
840        let mut count = 0usize;
841        loop {
842            let size = self.front_buf.available_space();
843            trace!("channel available space: {}", size);
844            if size == 0 {
845                // try to grow the buffer before giving up
846                if let Some(new_size) = self.grow_size(self.front_buf.capacity()) {
847                    Self::check_high_watermark(
848                        "front",
849                        new_size,
850                        self.max_buffer_size,
851                        &mut self.front_high_watermark_logged,
852                    );
853                    self.front_buf.grow(new_size);
854                    // The read buffer must never grow past the configured ceiling.
855                    debug_assert!(
856                        self.front_buf.capacity() <= self.max_buffer_size,
857                        "front buffer capacity must stay within the max_buffer_size ceiling"
858                    );
859                } else {
860                    self.interest.remove(Ready::READABLE);
861                    break;
862                }
863            }
864
865            // The slice handed to `read` is exactly the free tail; the kernel
866            // can never write more than that, so a successful read of N bytes
867            // is bounded by the space we just measured (post-grow).
868            let space_before = self.front_buf.available_space();
869            debug_assert!(
870                space_before > 0,
871                "readable must only call read() with non-empty space (zero-space path grows or breaks)"
872            );
873            let data_before = self.front_buf.available_data();
874            match self.sock.read(self.front_buf.space()) {
875                Ok(0) => {
876                    self.interest = Ready::EMPTY;
877                    self.readiness.remove(Ready::READABLE);
878                    self.readiness.insert(Ready::HUP);
879                    return Err(ChannelError::NoByteToRead);
880                }
881                Err(read_error) => match read_error.kind() {
882                    ErrorKind::WouldBlock => {
883                        self.readiness.remove(Ready::READABLE);
884                        break;
885                    }
886                    _ => {
887                        // Mark the channel for closing, as the `Ok(0)` arm
888                        // does: the owner closes on HUP or ERROR, and an
889                        // edge-triggered poller will not report them again
890                        // (sozu-proxy/sozu#1560). Assignment, not `insert`:
891                        // the socket has failed, so no other bit may stay
892                        // armed.
893                        self.interest = Ready::EMPTY;
894                        self.readiness = Ready::HUP | Ready::ERROR;
895                        return Err(ChannelError::Read(read_error));
896                    }
897                },
898                Ok(bytes_read) => {
899                    // A read can never deliver more bytes than the free space
900                    // it was handed; otherwise `fill` would corrupt offsets.
901                    debug_assert!(
902                        bytes_read <= space_before,
903                        "read delivered more bytes than the buffer space it was given"
904                    );
905                    count += bytes_read;
906                    self.front_buf.fill(bytes_read);
907                    // `fill` advances `end` by exactly the bytes read, so the
908                    // available data grows by that delta — pair-assert the
909                    // offset mutation.
910                    debug_assert_eq!(
911                        self.front_buf.available_data(),
912                        data_before + bytes_read,
913                        "front buffer available_data must increase by exactly bytes_read"
914                    );
915                }
916            };
917        }
918
919        Ok(count)
920    }
921
922    /// Handle writability by writing the content of the back buffer onto the socket.
923    /// Shrinks the back buffer back toward initial size once fully drained.
924    pub fn writable(&mut self) -> Result<usize, ChannelError> {
925        if !(self.interest & self.readiness).is_writable() {
926            return Err(ChannelError::Connection(None));
927        }
928
929        let mut count = 0usize;
930        loop {
931            let size = self.back_buf.available_data();
932            if size == 0 {
933                self.interest.remove(Ready::WRITABLE);
934                self.try_shrink_back_buf();
935                break;
936            }
937
938            let data_before = self.back_buf.available_data();
939            match self.sock.write(self.back_buf.data()) {
940                Ok(0) => {
941                    self.interest = Ready::EMPTY;
942                    self.readiness.insert(Ready::HUP);
943                    return Err(ChannelError::NoByteWritten);
944                }
945                Ok(bytes_written) => {
946                    // The kernel cannot accept more than the slice (`data()`)
947                    // it was handed; otherwise `consume` would underflow.
948                    debug_assert!(
949                        bytes_written <= data_before,
950                        "write reported more bytes than the buffer data it was given"
951                    );
952                    count += bytes_written;
953                    let consumed = self.back_buf.consume(bytes_written);
954                    // `consume` is saturating but the bound above guarantees an
955                    // exact consume here; pair-assert the offset mutation.
956                    debug_assert_eq!(
957                        consumed, bytes_written,
958                        "back buffer must consume exactly the bytes written to the socket"
959                    );
960                    debug_assert_eq!(
961                        self.back_buf.available_data(),
962                        data_before - bytes_written,
963                        "back buffer available_data must shrink by exactly bytes_written"
964                    );
965                }
966                Err(write_error) => match write_error.kind() {
967                    ErrorKind::WouldBlock => {
968                        self.readiness.remove(Ready::WRITABLE);
969                        break;
970                    }
971                    _ => {
972                        // Same as the read side: keep the channel marked for
973                        // closing instead of wiping HUP and ERROR. WRITABLE
974                        // must go: the worker's `Server::send_queue` keeps
975                        // looping while it is set and data is pending.
976                        self.interest = Ready::EMPTY;
977                        self.readiness = Ready::HUP | Ready::ERROR;
978                        return Err(ChannelError::Write(write_error));
979                    }
980                },
981            }
982        }
983
984        Ok(count)
985    }
986
987    /// Depending on the blocking status:
988    ///
989    /// Blocking: wait for the front buffer to be filled, and parse a message from it
990    ///
991    /// Nonblocking: parse a message from the front buffer, without waiting.
992    /// Prefer using `channel.readable()` before
993    pub fn read_message(&mut self) -> Result<Rx, ChannelError> {
994        if self.blocking {
995            self.read_message_blocking()
996        } else {
997            self.read_message_nonblocking()
998        }
999    }
1000
1001    fn read_message_blocking(&mut self) -> Result<Rx, ChannelError> {
1002        self.read_message_blocking_timeout(None)
1003    }
1004
1005    /// Parse a message from the front buffer, without waiting
1006    fn read_message_nonblocking(&mut self) -> Result<Rx, ChannelError> {
1007        // `NothingRead` is not a special case, it is one row of
1008        // [`rearms_readable`]'s table: an incomplete frame and a frame the
1009        // parser rejected and re-synchronised past are the same situation from
1010        // `readable()`'s point of view, and propagating the second through `?`
1011        // before the re-arm is what stranded the socket bytes behind it
1012        // (sozu-proxy/sozu#1445).
1013        let error = match self.try_read_delimited_message() {
1014            Ok(Some(message)) => {
1015                self.try_shrink_front_buf();
1016                return Ok(message);
1017            }
1018            Ok(None) => ChannelError::NothingRead,
1019            Err(error) => error,
1020        };
1021
1022        if rearms_readable(&error) {
1023            self.interest.insert(Ready::READABLE);
1024        }
1025        Err(error)
1026    }
1027
1028    /// Wait for the front buffer to be filled, and parses a message from it.
1029    ///
1030    /// The wait ends with a message, a read error, or once `timeout` has
1031    /// elapsed since the call. A read interrupted by a signal (`EINTR`) is
1032    /// retried against that same deadline, never restarting it.
1033    pub fn read_message_blocking_timeout(
1034        &mut self,
1035        timeout: Option<Duration>,
1036    ) -> Result<Rx, ChannelError> {
1037        let now = std::time::Instant::now();
1038
1039        // 10 ms = 100 syscalls/sec on idle WouldBlock, pinning a CPU on
1040        // long blocking waits with no payload. 100 ms is
1041        // a usability-acceptable resolution for the outer `timeout`
1042        // deadline check (the wait is bounded by `timeout`, not by this
1043        // value) and drops the steady-state read syscall rate to 10/sec.
1044        self.set_timeout(Some(Duration::from_millis(100)))?;
1045
1046        let status = loop {
1047            if let Some(timeout) = timeout
1048                && now.elapsed() >= timeout
1049            {
1050                break Err(ChannelError::TimeoutReached(timeout));
1051            }
1052
1053            if let Some(message) = self.try_read_delimited_message()? {
1054                self.try_shrink_front_buf();
1055                return Ok(message);
1056            }
1057
1058            match self.sock.read(self.front_buf.space()) {
1059                Ok(0) => return Err(ChannelError::NoByteToRead),
1060                Ok(bytes_read) => self.front_buf.fill(bytes_read),
1061                Err(io_error) => match io_error.kind() {
1062                    // The 100 ms `SO_RCVTIMEO` slice elapsed with no byte.
1063                    ErrorKind::WouldBlock => continue,
1064                    // signal(7): a read with `SO_RCVTIMEO` is never restarted,
1065                    // even under `SA_RESTART`, so any handled signal (the main
1066                    // process's `SIGTERM`) lands here. Retry: the deadline
1067                    // check at the top of the loop still bounds the wait.
1068                    ErrorKind::Interrupted => continue,
1069                    _ => break Err(ChannelError::Read(io_error)),
1070                },
1071            };
1072        };
1073
1074        self.set_timeout(None)?;
1075
1076        status
1077    }
1078
1079    /// parse a prost message from the front buffer, grow it if necessary
1080    fn try_read_delimited_message(&mut self) -> Result<Option<Rx>, ChannelError> {
1081        // Invariant guarding all the slice indexing below: the front buffer can
1082        // never have grown past the configured ceiling. Every grow site routes
1083        // through `grow_size`/`max_buffer_size`; if this ever fired the length
1084        // checks would be reasoning against a stale bound.
1085        debug_assert!(
1086            self.front_buf.capacity() <= self.max_buffer_size,
1087            "front buffer capacity must never exceed max_buffer_size"
1088        );
1089        let buffer = self.front_buf.data();
1090        // `data()` returns `memory[position..end]`, so its length is exactly the
1091        // available data and can never exceed the buffer capacity.
1092        debug_assert!(
1093            buffer.len() <= self.front_buf.capacity(),
1094            "available data slice cannot exceed buffer capacity"
1095        );
1096        if buffer.len() >= delimiter_size() {
1097            let delimiter = buffer[..delimiter_size()]
1098                .try_into()
1099                .map_err(|_| ChannelError::MismatchBufferSize)?;
1100            let message_len = usize::from_le_bytes(delimiter);
1101
1102            // Defense in depth: bound the parser-side length up-front.
1103            // Without this an attacker who controls the
1104            // first 8 bytes of a frame can declare an arbitrarily large
1105            // message and drive `Buffer::grow` toward the
1106            // `max_buffer_size` ceiling before any byte of payload has
1107            // been read. Reject as `MessageTooLarge` before the doubling
1108            // growth strategy ever runs on attacker-supplied numbers.
1109            //
1110            // Unlike the `MessageLengthUnderDelimiter` case below, this one
1111            // must NOT consume and re-sync. That one can: a declared length
1112            // under `delimiter_size()` is a value no writer can produce for
1113            // any payload, so those bytes are provably not a real header and
1114            // skipping exactly them re-aligns the stream. Here the declared
1115            // length may be honest, in which case the bytes behind the header
1116            // are payload -- dropping the header and re-framing on them would
1117            // decode peer-chosen bytes as a fresh control-plane request. And
1118            // the frame can never complete either way: a length above
1119            // `max_buffer_size` does not fit a buffer bounded by
1120            // `max_buffer_size`, however much more is read.
1121            //
1122            // So it is fatal for that peer, which is already the stance taken
1123            // for the mirror condition on the write side
1124            // (`is_transient_overflow`, `bin/src/command/sessions.rs`: "a
1125            // frame bigger than the ceiling itself, which no amount of
1126            // draining will ever admit"). That is a statement about this end,
1127            // NOT about the peer's good faith: the two ends size their
1128            // channels from their own configuration, so a peer built with a
1129            // larger `max_command_buffer_size` emits frames this end refuses
1130            // while conforming perfectly -- `write_delimited_message` below
1131            // bounds an outgoing frame against the *writer's* ceiling, and the
1132            // two configurations shipped here already disagree tenfold
1133            // (`bin/config.toml` 163_840, `os-build/config.toml` 1_638_400).
1134            // A `sozu ctl --config` pointed at one while the supervisor was
1135            // started from the other, or two binaries built with different
1136            // `SOZU_CONFIG` defaults (`bin/build.rs`, `bin/src/util.rs`), is
1137            // all it takes. Closing is still the only correct exit for this
1138            // end, which can neither complete nor
1139            // re-sync the frame; the log line says what to reconcile instead
1140            // of implying an attack. Mark the channel errored -- the single
1141            // signal `ClientSession::ready`, `WorkerSession::ready` and
1142            // `wants_to_tick` all key on -- so the supervisor drops the
1143            // session. Without it the header is re-parsed forever,
1144            // `extract_messages` swallows the error with a bare `Err(_)`, and
1145            // the session wedges while pinning its file descriptor and buffer
1146            // (sozu-proxy/sozu#1428).
1147            //
1148            // `insert`, never assignment: on the worker's own channel
1149            // (`lib/src/server.rs`) `interest` carries only READABLE and
1150            // WRITABLE, so `readiness()` masks ERROR away and nothing there
1151            // reads `is_error()`. An assignment's only effect on that side
1152            // would be clearing WRITABLE, and `Server::send_queue` gates on
1153            // `channel.readiness.is_writable()`, so already-queued worker
1154            // responses would stall until the next edge-triggered writability
1155            // event.
1156            if message_len > self.max_buffer_size {
1157                error!(
1158                    "peer declared a {}-byte frame, above this end's {}-byte ceiling; closing \
1159                     the connection. The two ends size their channels independently, so this is \
1160                     more often a configuration mismatch than a hostile peer: reconcile \
1161                     `max_command_buffer_size` (and `command_buffer_size`, which must stay below \
1162                     it) between this end and the peer's",
1163                    message_len, self.max_buffer_size
1164                );
1165                self.readiness.insert(Ready::ERROR);
1166                return Err(ChannelError::MessageTooLarge {
1167                    message_len,
1168                    capacity: self.front_buf.capacity(),
1169                    max: self.max_buffer_size,
1170                });
1171            }
1172
1173            // A length-delimited frame is `[delimiter][payload]`. The declared
1174            // `message_len` is the total frame size and MUST therefore be at
1175            // least `delimiter_size()`. A peer-controlled value below that
1176            // ceiling makes `&buffer[delimiter_size()..message_len]` slice
1177            // backwards and panic; reject it the same way as oversized frames.
1178            //
1179            // Drop the bogus delimiter bytes before returning. Without this,
1180            // every subsequent `read_message()` re-reads the same bad header
1181            // from the front buffer and the worker burns CPU on the same error
1182            // until the peer disconnects.
1183            //
1184            // This re-aligns what is already in `front_buf`, and nothing more;
1185            // `rearms_readable` above carries the refill half.
1186            if message_len < delimiter_size() {
1187                self.front_buf.consume(delimiter_size());
1188                return Err(ChannelError::MessageLengthUnderDelimiter {
1189                    message_len,
1190                    delimiter_size: delimiter_size(),
1191                });
1192            }
1193
1194            if buffer.len() >= message_len {
1195                // By the time we slice, the two guards above have proven the
1196                // length prefix is well-formed: it is at least the delimiter
1197                // (so `delimiter_size()..message_len` runs forward) and at most
1198                // the configured ceiling (so it cannot drive growth). The
1199                // `buffer.len() >= message_len` branch then guarantees the whole
1200                // frame is in the buffer. These are the exact invariants that
1201                // keep the slice and the `consume` in bounds — never reachable
1202                // from a malformed length, which already returned an error.
1203                debug_assert!(
1204                    message_len >= delimiter_size(),
1205                    "decode path requires a frame at least as large as its delimiter"
1206                );
1207                debug_assert!(
1208                    message_len <= self.max_buffer_size,
1209                    "decode path requires the declared length within the max ceiling"
1210                );
1211                debug_assert!(
1212                    message_len <= buffer.len(),
1213                    "decode path requires the full frame to be buffered before slicing"
1214                );
1215                let available_before = self.front_buf.available_data();
1216                debug_assert_eq!(
1217                    available_before,
1218                    buffer.len(),
1219                    "available_data must equal the data slice length we validated against"
1220                );
1221                // Decode first, consume unconditionally, propagate a decode
1222                // failure only afterwards. Returning before the consume (as
1223                // this did until sozu-proxy/sozu#1428) left the whole frame at
1224                // the head of the buffer and moved no readiness bit, and
1225                // `extract_messages` (`bin/src/command/sessions.rs`) swallows
1226                // the error with its bare `Err(_)` arm, so the session
1227                // re-decoded the same bytes on every later read and wedged.
1228                //
1229                // Note what does NOT justify this. `message_len` is only
1230                // range-checked above -- at least `delimiter_size()`, at most
1231                // `max_buffer_size`, no larger than what is buffered. It is
1232                // not proven to be a real frame boundary, so on a
1233                // desynchronised stream consuming it re-frames on bytes the
1234                // sender never framed, which is the very hazard that argues
1235                // against consuming in the `MessageTooLarge` branch above.
1236                // Nor is this the `MessageLengthUnderDelimiter` stance: that
1237                // one consumes exactly `delimiter_size()` on a value no writer
1238                // can emit for any payload, an impossibility proof this branch
1239                // does not have.
1240                //
1241                // What justifies it is coherence with the success path four
1242                // lines down, which already consumes this same peer-supplied
1243                // `message_len` and re-frames on whatever follows. The
1244                // desynchronisation hazard is therefore identical whether the
1245                // payload decodes or not, and it predates this change.
1246                // Closing the peer only on the failing half would punish the
1247                // case more likely to be honest protobuf skew while silently
1248                // accepting the case that is not.
1249                let decoded = Rx::decode(&buffer[delimiter_size()..message_len]);
1250                let consumed = self.front_buf.consume(message_len);
1251                // The whole frame (delimiter + payload) is consumed exactly:
1252                // pair-assert that consume advanced by message_len and the data
1253                // pointer moved forward by the same amount.
1254                debug_assert_eq!(
1255                    consumed, message_len,
1256                    "must consume exactly the validated frame length"
1257                );
1258                debug_assert_eq!(
1259                    self.front_buf.available_data(),
1260                    available_before - message_len,
1261                    "available_data must drop by exactly the consumed frame length"
1262                );
1263                let message = decoded.map_err(|decode_error| {
1264                    error!(
1265                        "could not decode a {}-byte frame from the peer; that frame is dropped \
1266                         and the channel re-syncs on the next one: {}",
1267                        message_len, decode_error
1268                    );
1269                    ChannelError::InvalidProtobufMessage(decode_error)
1270                })?;
1271                return Ok(Some(message));
1272            }
1273        }
1274
1275        if self.front_buf.available_space() == 0 {
1276            // Compact before concluding anything about the space (fixes
1277            // sozu-proxy/sozu#1436). `available_space()` is `capacity - end`,
1278            // so it measures the free TAIL, not the free room: a buffer whose
1279            // `position` sits mid-way reports zero space while holding
1280            // `position` reusable bytes at its head. `Buffer::consume`
1281            // (`command/src/buffer/growable.rs`) only compacts past
1282            // `capacity / 2`, so decoding one frame of at most half the
1283            // capacity out of a buffer filled to capacity leaves exactly that
1284            // layout, and `Buffer::shift` hands the room straight back.
1285            //
1286            // Without it this arm returned `BufferFull` without consuming and
1287            // without moving a readiness bit -- the shape
1288            // sozu-proxy/sozu#1428 removed from the two branches above -- and
1289            // no later call could recover: `readable()` sees
1290            // `available_space() == 0`, gets `None` from `grow_size` at the
1291            // ceiling and executes `interest.remove(Ready::READABLE)`, so
1292            // every later `readable()` returns `Err(Connection(None))` and
1293            // `fill` is out of reach; `consume` needs a decoded frame or an
1294            // under-delimiter length, which this return path never reaches;
1295            // and `try_shrink_front_buf` is never reached, because both
1296            // `read_message` paths call it only after a frame decoded, and
1297            // both of its own early returns would stop it anyway --
1298            // `capacity <= initial_buffer_size` whenever the channel was
1299            // configured with `command_buffer_size == max_command_buffer_size`,
1300            // and `available_data() * 4 < initial_buffer_size`, which is false
1301            // with that much pending. Not because it shrinks rather than
1302            // shifts: `Buffer::shrink` calls `shift` unconditionally before its
1303            // own size bail, so reaching it WOULD have compacted. The session
1304            // then wedged permanently, holding its file descriptor and a full
1305            // buffer, on a peer that had done nothing wrong.
1306            //
1307            // Compacting here also changes the sub-ceiling path: a front buffer
1308            // still below `max_buffer_size` now compacts on its first zero-space
1309            // parse and grows on the next one, rather than doubling straight
1310            // away. The frame still completes -- the compacted buffer refills to
1311            // `end == capacity` at `position == 0`, where the shift is a no-op
1312            // and the grow below runs -- one read-parse round later, against one
1313            // fewer doubling.
1314            //
1315            // Compaction is the whole recovery, NOT a close: the frame is
1316            // under this end's ceiling and its buffered bytes are intact, so
1317            // only the buffer's internal offsets stand in the way. It cannot
1318            // mask an unsatisfiable frame either -- a declared length above
1319            // `max_buffer_size` returns `MessageTooLarge` at the guard above,
1320            // before control ever arrives here, so the ordering of the two is
1321            // what keeps "too large for the ceiling" and "too large for the
1322            // current layout" apart. The write side already compacts on the
1323            // same reasoning before it considers growing
1324            // (`write_delimited_message` below).
1325            //
1326            // Compacting makes the channel recoverable; it does not by itself
1327            // make a session recover. Freeing space without changing capacity
1328            // ended `extract_messages`' drain loop (`bin/src/command/sessions.rs`)
1329            // on the very parse that made the room, one read short of completing
1330            // the frame, and nothing re-schedules a merely-readable session. That
1331            // loop's termination anchor was corrected in the same changeset; a
1332            // change to what this arm does to the buffer without changing
1333            // capacity has to be checked against it.
1334            self.front_buf.shift();
1335        }
1336
1337        if self.front_buf.available_space() == 0 {
1338            if self.front_buf.capacity() >= self.max_buffer_size {
1339                // Past the compaction above, zero space means `position == 0`
1340                // and `end == capacity`, so the whole capacity is pending data;
1341                // with `capacity == max_buffer_size` any in-range declared
1342                // length would have decoded already. Only a capacity too small
1343                // to hold a length prefix at all reaches this -- a
1344                // configuration that can never frame a message, not a buffer
1345                // that has run out of room.
1346                debug_assert!(
1347                    self.front_buf.available_data() < delimiter_size(),
1348                    "a compacted, full buffer at the ceiling can only fail to parse when its capacity cannot hold a length prefix"
1349                );
1350                return Err(ChannelError::BufferFull {
1351                    capacity: self.front_buf.capacity(),
1352                    max: self.max_buffer_size,
1353                });
1354            }
1355            let new_size = self
1356                .grow_size(self.front_buf.capacity())
1357                .unwrap_or(self.max_buffer_size);
1358            Self::check_high_watermark(
1359                "front",
1360                new_size,
1361                self.max_buffer_size,
1362                &mut self.front_high_watermark_logged,
1363            );
1364            self.front_buf.grow(new_size);
1365        }
1366        Ok(None)
1367    }
1368
1369    /// Checks whether the channel is blocking or nonblocking, writes the message.
1370    ///
1371    /// If the channel is nonblocking, you have to flush using `channel.run()` afterwards
1372    pub fn write_message(&mut self, message: &Tx) -> Result<(), ChannelError> {
1373        if self.blocking {
1374            self.write_message_blocking(message)
1375        } else {
1376            self.write_message_nonblocking(message)
1377        }
1378    }
1379
1380    /// Writes the message in the buffer, but NOT on the socket.
1381    /// you have to call channel.run() afterwards
1382    fn write_message_nonblocking(&mut self, message: &Tx) -> Result<(), ChannelError> {
1383        self.write_delimited_message(message)?;
1384
1385        self.interest.insert(Ready::WRITABLE);
1386
1387        Ok(())
1388    }
1389
1390    /// fills the back buffer with data AND writes on the socket
1391    ///
1392    /// Returns `Ok(())` only once the whole back buffer reached the socket.
1393    /// A `write(2)` interrupted by a signal is retried; every other error is
1394    /// returned as `ChannelError::Write`, and the bytes not yet written stay in
1395    /// `back_buf` (sozu-proxy/sozu#1563).
1396    ///
1397    /// `WouldBlock` is not retried. On a blocking socket it only follows a send
1398    /// timeout (`SO_SNDTIMEO`), and this channel never arms one: `set_timeout`
1399    /// sets the read timeout alone. Retrying would spin on a descriptor someone
1400    /// else made nonblocking, and success would claim a delivery that did not
1401    /// happen, so it is reported like any other write failure.
1402    fn write_message_blocking(&mut self, message: &Tx) -> Result<(), ChannelError> {
1403        self.write_delimited_message(message)?;
1404
1405        loop {
1406            let size = self.back_buf.available_data();
1407            if size == 0 {
1408                break;
1409            }
1410
1411            match self.sock.write(self.back_buf.data()) {
1412                Ok(0) => return Err(ChannelError::NoByteWritten),
1413                Ok(bytes_written) => {
1414                    self.back_buf.consume(bytes_written);
1415                }
1416                Err(error) if error.kind() == ErrorKind::Interrupted => continue,
1417                Err(error) => return Err(ChannelError::Write(error)),
1418            }
1419        }
1420        Ok(())
1421    }
1422
1423    /// write a message on the back buffer, using our own delimiter (the delimiter of prost
1424    /// is not trustworthy since its size may change)
1425    pub fn write_delimited_message(&mut self, message: &Tx) -> Result<(), ChannelError> {
1426        // The encoded frame can hold protected values: wipe it on every exit,
1427        // including the `MessageTooLarge` refusal below. `encode_to_vec`
1428        // allocates `encoded_len()` bytes once, so no partial copy is left by a
1429        // regrowth.
1430        let payload = Zeroizing::new(message.encode_to_vec());
1431
1432        let payload_len = payload.len() + delimiter_size();
1433
1434        // The framed length is the payload plus a fixed delimiter, so it is by
1435        // construction at least one delimiter wide — the mirror of the
1436        // `MessageLengthUnderDelimiter` invariant the reader enforces.
1437        debug_assert!(
1438            payload_len >= delimiter_size(),
1439            "framed length must include the fixed-size delimiter prefix"
1440        );
1441
1442        let delimiter = payload_len.to_le_bytes();
1443
1444        if payload_len > self.back_buf.available_space() {
1445            self.back_buf.shift();
1446        }
1447
1448        let data_before = self.back_buf.available_data();
1449        if payload_len > self.back_buf.available_space() {
1450            let needed = payload_len - self.back_buf.available_space() + self.back_buf.capacity();
1451            if needed > self.max_buffer_size {
1452                return Err(ChannelError::MessageTooLarge {
1453                    message_len: payload_len,
1454                    capacity: self.back_buf.capacity(),
1455                    max: self.max_buffer_size,
1456                });
1457            }
1458            // Past the ceiling check, the required size is within the ceiling
1459            // and at least the current capacity (we only enter on shortfall).
1460            debug_assert!(
1461                needed <= self.max_buffer_size,
1462                "grow target must be within the max ceiling once the cap check passed"
1463            );
1464
1465            let capacity_before = self.back_buf.capacity();
1466            // use doubling strategy to reach at least `needed`, amortizing future writes
1467            let mut new_length = self.back_buf.capacity();
1468            while new_length < needed {
1469                new_length = new_length.saturating_mul(2).max(new_length + 1);
1470            }
1471            new_length = min(new_length, self.max_buffer_size);
1472            // Post-grow target: large enough to fit the frame yet capped at the
1473            // configured ceiling and never below where we started.
1474            debug_assert!(
1475                new_length >= needed,
1476                "doubling growth must reach at least the needed capacity"
1477            );
1478            debug_assert!(
1479                new_length <= self.max_buffer_size,
1480                "grown back buffer must stay within the max_buffer_size ceiling"
1481            );
1482            debug_assert!(
1483                new_length >= capacity_before,
1484                "growth must never shrink the back buffer"
1485            );
1486            Self::check_high_watermark(
1487                "back",
1488                new_length,
1489                self.max_buffer_size,
1490                &mut self.back_high_watermark_logged,
1491            );
1492            self.back_buf.grow(new_length);
1493            // After the grow the frame must fit in the now-available space.
1494            debug_assert!(
1495                payload_len <= self.back_buf.available_space(),
1496                "back buffer must have room for the full frame after growth"
1497            );
1498        }
1499
1500        self.back_buf
1501            .write_all(&delimiter)
1502            .map_err(ChannelError::Write)?;
1503        self.back_buf
1504            .write_all(&payload)
1505            .map_err(ChannelError::Write)?;
1506
1507        // The two writes appended exactly `payload_len` bytes (delimiter +
1508        // payload) to the back buffer's pending data.
1509        debug_assert_eq!(
1510            self.back_buf.available_data(),
1511            data_before + payload_len,
1512            "back buffer pending data must grow by exactly the framed length"
1513        );
1514        debug_assert!(
1515            self.back_buf.capacity() <= self.max_buffer_size,
1516            "back buffer capacity must never exceed the max_buffer_size ceiling"
1517        );
1518
1519        Ok(())
1520    }
1521
1522    /// Shrink the front buffer back toward initial_buffer_size when it is
1523    /// mostly empty (data consumed) and was previously grown.
1524    fn try_shrink_front_buf(&mut self) {
1525        let capacity = self.front_buf.capacity();
1526        if capacity <= self.initial_buffer_size {
1527            return;
1528        }
1529        // Past the early return, we are strictly above the floor, so a shrink
1530        // back to `initial_buffer_size` is a genuine reduction.
1531        debug_assert!(
1532            capacity > self.initial_buffer_size,
1533            "shrink path only runs when capacity is above the initial floor"
1534        );
1535        // only shrink when the buffer has little pending data
1536        if self.front_buf.available_data() * 4 < self.initial_buffer_size {
1537            let data_before = self.front_buf.available_data();
1538            self.front_buf.shrink(self.initial_buffer_size);
1539            self.front_high_watermark_logged = false;
1540            // Shrink preserves pending data and never drops below the floor.
1541            debug_assert!(
1542                self.front_buf.capacity() >= self.initial_buffer_size,
1543                "front buffer must never shrink below the initial buffer size floor"
1544            );
1545            debug_assert_eq!(
1546                self.front_buf.available_data(),
1547                data_before,
1548                "shrink must preserve all pending front-buffer data"
1549            );
1550            trace!(
1551                "front buffer shrunk from {} to {} bytes",
1552                capacity, self.initial_buffer_size
1553            );
1554        }
1555    }
1556
1557    /// Shrink the back buffer back toward initial_buffer_size when fully drained.
1558    fn try_shrink_back_buf(&mut self) {
1559        let capacity = self.back_buf.capacity();
1560        if capacity <= self.initial_buffer_size {
1561            return;
1562        }
1563        debug_assert!(
1564            capacity > self.initial_buffer_size,
1565            "shrink path only runs when capacity is above the initial floor"
1566        );
1567        if self.back_buf.available_data() == 0 {
1568            self.back_buf.shrink(self.initial_buffer_size);
1569            self.back_high_watermark_logged = false;
1570            // The back buffer is only shrunk once fully drained; it must end at
1571            // the floor with no pending data resurrected.
1572            debug_assert!(
1573                self.back_buf.capacity() >= self.initial_buffer_size,
1574                "back buffer must never shrink below the initial buffer size floor"
1575            );
1576            debug_assert_eq!(
1577                self.back_buf.available_data(),
1578                0,
1579                "back buffer must stay empty across a drained shrink"
1580            );
1581            trace!(
1582                "back buffer shrunk from {} to {} bytes",
1583                capacity, self.initial_buffer_size
1584            );
1585        }
1586    }
1587}
1588
1589/// the payload is prefixed with a delimiter of sizeof(usize) bytes
1590pub const fn delimiter_size() -> usize {
1591    std::mem::size_of::<usize>()
1592}
1593
1594type ChannelResult<Tx, Rx> = Result<(Channel<Tx, Rx>, Channel<Rx, Tx>), ChannelError>;
1595
1596impl<Tx: Debug + ProstMessage + Default, Rx: Debug + ProstMessage + Default> Channel<Tx, Rx> {
1597    /// creates a channel pair: `(blocking_channel, nonblocking_channel)`
1598    pub fn generate(buffer_size: u64, max_buffer_size: u64) -> ChannelResult<Tx, Rx> {
1599        let (command, proxy) = MioUnixStream::pair().map_err(ChannelError::Read)?;
1600        let proxy_channel = Channel::new(proxy, buffer_size, max_buffer_size);
1601        let mut command_channel = Channel::new(command, buffer_size, max_buffer_size);
1602        command_channel.blocking()?;
1603        Ok((command_channel, proxy_channel))
1604    }
1605
1606    /// creates a pair of nonblocking channels
1607    pub fn generate_nonblocking(buffer_size: u64, max_buffer_size: u64) -> ChannelResult<Tx, Rx> {
1608        let (command, proxy) = MioUnixStream::pair().map_err(ChannelError::Read)?;
1609        let proxy_channel = Channel::new(proxy, buffer_size, max_buffer_size);
1610        let command_channel = Channel::new(command, buffer_size, max_buffer_size);
1611        Ok((command_channel, proxy_channel))
1612    }
1613}
1614
1615impl<Tx: Debug + ProstMessage + Default, Rx: Debug + ProstMessage + Default> Iterator
1616    for Channel<Tx, Rx>
1617{
1618    type Item = Rx;
1619    fn next(&mut self) -> Option<Self::Item> {
1620        self.read_message().ok()
1621    }
1622}
1623
1624use mio::{Interest, Registry, Token};
1625impl<Tx, Rx> Source for Channel<Tx, Rx> {
1626    fn register(
1627        &mut self,
1628        registry: &Registry,
1629        token: Token,
1630        interests: Interest,
1631    ) -> io::Result<()> {
1632        self.sock.register(registry, token, interests)
1633    }
1634
1635    fn reregister(
1636        &mut self,
1637        registry: &Registry,
1638        token: Token,
1639        interests: Interest,
1640    ) -> io::Result<()> {
1641        self.sock.reregister(registry, token, interests)
1642    }
1643
1644    fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
1645        self.sock.deregister(registry)
1646    }
1647}
1648
1649#[cfg(test)]
1650mod tests {
1651    use std::{
1652        io::Write,
1653        os::fd::{AsRawFd, FromRawFd, OwnedFd},
1654        thread,
1655        time::Duration,
1656    };
1657
1658    use super::*;
1659    use mio::{Interest, Poll, Token};
1660
1661    #[derive(Clone, PartialEq, prost::Message)]
1662    pub struct ProtobufMessage {
1663        #[prost(uint32, required, tag = "1")]
1664        inner: u32,
1665    }
1666
1667    fn test_channels() -> (
1668        Channel<ProtobufMessage, ProtobufMessage>,
1669        Channel<ProtobufMessage, ProtobufMessage>,
1670    ) {
1671        Channel::generate(1000, 10000).expect("could not generate blocking channels for testing")
1672    }
1673
1674    #[test]
1675    fn channel_snapshot_round_trips_unconsumed_offsets_capacities_bits_and_mode() {
1676        let (mut channel, _peer): (
1677            Channel<ProtobufMessage, ProtobufMessage>,
1678            Channel<ProtobufMessage, ProtobufMessage>,
1679        ) = Channel::generate_nonblocking(8, 64).expect("could not generate channels");
1680
1681        channel.front_buf.grow(32);
1682        channel
1683            .front_buf
1684            .write_all(b"retired-front-pending")
1685            .expect("could not seed front buffer");
1686        channel.front_buf.consume(b"retired-".len());
1687        channel.back_buf.grow(48);
1688        channel
1689            .back_buf
1690            .write_all(b"sent-back-pending")
1691            .expect("could not seed back buffer");
1692        channel.back_buf.consume(b"sent-".len());
1693        channel.readiness =
1694            Ready::READABLE | Ready::WRITABLE | Ready::ERROR | Ready::HUP | Ready::WRITE_CLOSED;
1695        channel.interest = Ready::READABLE | Ready::WRITABLE;
1696        channel.front_high_watermark_logged = true;
1697
1698        let snapshot_json =
1699            serde_json::to_vec(&channel.snapshot()).expect("channel snapshot should serialize");
1700        let snapshot: ChannelSnapshot =
1701            serde_json::from_slice(&snapshot_json).expect("channel snapshot should deserialize");
1702        assert!(snapshot.has_buffered_input());
1703        assert!(snapshot.has_buffered_output());
1704        let original_fd = channel.fd();
1705        let paused: PausedChannel<ProtobufMessage, ProtobufMessage> =
1706            Channel::restore_paused(channel.sock, snapshot, 8, 64)
1707                .expect("valid channel snapshot should restore");
1708
1709        assert_eq!(paused.fd(), original_fd);
1710        let restored = paused.resume();
1711        assert_eq!(restored.front_buf.data(), b"front-pending");
1712        assert_eq!(restored.back_buf.data(), b"back-pending");
1713        assert_eq!(restored.front_buf.capacity(), 32);
1714        assert_eq!(restored.back_buf.capacity(), 48);
1715        assert_eq!(restored.initial_buffer_size, 8);
1716        assert_eq!(restored.max_buffer_size, 64);
1717        assert_eq!(
1718            restored.readiness,
1719            Ready::READABLE | Ready::WRITABLE | Ready::ERROR | Ready::HUP | Ready::WRITE_CLOSED
1720        );
1721        assert_eq!(restored.interest, Ready::READABLE | Ready::WRITABLE);
1722        assert!(!restored.is_blocking());
1723        assert!(restored.front_high_watermark_logged);
1724        assert!(!restored.back_high_watermark_logged);
1725    }
1726
1727    #[test]
1728    fn channel_snapshot_paused_registration_does_not_flush_before_resume() {
1729        let (mut channel, mut peer): (
1730            Channel<ProtobufMessage, ProtobufMessage>,
1731            Channel<ProtobufMessage, ProtobufMessage>,
1732        ) = Channel::generate_nonblocking(32, 128).expect("could not generate channels");
1733        channel
1734            .write_message(&ProtobufMessage { inner: 42 })
1735            .expect("could not queue message");
1736        channel.handle_events(Ready::WRITABLE);
1737
1738        let snapshot = channel.snapshot();
1739        let mut paused: PausedChannel<ProtobufMessage, ProtobufMessage> =
1740            Channel::restore_paused(channel.sock, snapshot, 32, 128)
1741                .expect("valid channel snapshot should restore");
1742        let poll = Poll::new().expect("could not create poll registry");
1743        paused
1744            .register(
1745                poll.registry(),
1746                Token(7),
1747                Interest::READABLE | Interest::WRITABLE,
1748            )
1749            .expect("paused channel should register");
1750
1751        peer.handle_events(Ready::READABLE);
1752        assert_eq!(peer.readable().expect("peer read probe should not fail"), 0);
1753        assert!(matches!(
1754            peer.read_message(),
1755            Err(ChannelError::NothingRead)
1756        ));
1757
1758        let mut restored = paused.resume();
1759        restored.run().expect("resumed channel should flush");
1760        peer.handle_events(Ready::READABLE);
1761        peer.readable().expect("peer should receive resumed bytes");
1762        assert_eq!(
1763            peer.read_message()
1764                .expect("peer should decode queued message"),
1765            ProtobufMessage { inner: 42 }
1766        );
1767    }
1768
1769    #[test]
1770    fn channel_snapshot_rejects_invalid_version_capacities_bytes_and_ready_bits() {
1771        fn snapshot_and_socket() -> (
1772            ChannelSnapshot,
1773            MioUnixStream,
1774            Channel<ProtobufMessage, ProtobufMessage>,
1775        ) {
1776            let (channel, peer): (
1777                Channel<ProtobufMessage, ProtobufMessage>,
1778                Channel<ProtobufMessage, ProtobufMessage>,
1779            ) = Channel::generate_nonblocking(8, 64).expect("could not generate channels");
1780            (channel.snapshot(), channel.sock, peer)
1781        }
1782
1783        let (mut invalid_version, sock, _peer) = snapshot_and_socket();
1784        invalid_version.version += 1;
1785        assert!(matches!(
1786            Channel::<ProtobufMessage, ProtobufMessage>::restore_paused(
1787                sock,
1788                invalid_version,
1789                8,
1790                64,
1791            ),
1792            Err(ChannelSnapshotError::UnsupportedVersion { .. })
1793        ));
1794
1795        let (mut invalid_bounds, sock, _peer) = snapshot_and_socket();
1796        invalid_bounds.initial_buffer_size = invalid_bounds.max_buffer_size + 1;
1797        assert!(matches!(
1798            Channel::<ProtobufMessage, ProtobufMessage>::restore_paused(
1799                sock,
1800                invalid_bounds,
1801                8,
1802                64,
1803            ),
1804            Err(ChannelSnapshotError::InvalidBufferBounds { .. })
1805        ));
1806
1807        let (mut invalid_capacity, sock, _peer) = snapshot_and_socket();
1808        invalid_capacity.front_buffer_capacity = invalid_capacity.initial_buffer_size - 1;
1809        assert!(matches!(
1810            Channel::<ProtobufMessage, ProtobufMessage>::restore_paused(
1811                sock,
1812                invalid_capacity,
1813                8,
1814                64,
1815            ),
1816            Err(ChannelSnapshotError::InvalidBufferCapacity { .. })
1817        ));
1818
1819        let (mut invalid_bytes, sock, _peer) = snapshot_and_socket();
1820        let over_capacity = vec![0; invalid_bytes.front_buffer_capacity + 1];
1821        invalid_bytes.front = SnapshotBytes::copy_of(&over_capacity);
1822        assert!(matches!(
1823            Channel::<ProtobufMessage, ProtobufMessage>::restore_paused(sock, invalid_bytes, 8, 64,),
1824            Err(ChannelSnapshotError::BufferedDataExceedsCapacity { .. })
1825        ));
1826
1827        let (mut invalid_readiness, sock, _peer) = snapshot_and_socket();
1828        invalid_readiness.readiness = 1 << 5;
1829        assert!(matches!(
1830            Channel::<ProtobufMessage, ProtobufMessage>::restore_paused(
1831                sock,
1832                invalid_readiness,
1833                8,
1834                64,
1835            ),
1836            Err(ChannelSnapshotError::InvalidReadyBits {
1837                field: "readiness",
1838                ..
1839            })
1840        ));
1841
1842        let (mut invalid_interest, sock, _peer) = snapshot_and_socket();
1843        invalid_interest.interest = 1 << 5;
1844        assert!(matches!(
1845            Channel::<ProtobufMessage, ProtobufMessage>::restore_paused(
1846                sock,
1847                invalid_interest,
1848                8,
1849                64,
1850            ),
1851            Err(ChannelSnapshotError::InvalidReadyBits {
1852                field: "interest",
1853                ..
1854            })
1855        ));
1856    }
1857
1858    #[test]
1859    fn channel_snapshot_accepts_every_defined_ready_subset_in_both_fields() {
1860        let defined_bits = Ready::READABLE.0
1861            | Ready::WRITABLE.0
1862            | Ready::ERROR.0
1863            | Ready::HUP.0
1864            | Ready::WRITE_CLOSED.0;
1865
1866        for bits in 0..=defined_bits {
1867            for field in ["readiness", "interest"] {
1868                let (channel, _peer): (
1869                    Channel<ProtobufMessage, ProtobufMessage>,
1870                    Channel<ProtobufMessage, ProtobufMessage>,
1871                ) = Channel::generate_nonblocking(8, 64).expect("could not generate channels");
1872                let mut snapshot = channel.snapshot();
1873                if field == "readiness" {
1874                    snapshot.readiness = bits;
1875                } else {
1876                    snapshot.interest = bits;
1877                }
1878
1879                let restored = Channel::<ProtobufMessage, ProtobufMessage>::restore_paused(
1880                    channel.sock,
1881                    snapshot,
1882                    8,
1883                    64,
1884                )
1885                .unwrap_or_else(|error| {
1886                    panic!("{field} rejected defined bits {bits:#04x}: {error}")
1887                })
1888                .resume();
1889                let restored_bits = if field == "readiness" {
1890                    restored.readiness.0
1891                } else {
1892                    restored.interest.0
1893                };
1894                assert_eq!(restored_bits, bits, "{field} changed bits {bits:#04x}");
1895            }
1896        }
1897    }
1898
1899    #[test]
1900    fn channel_snapshot_rejects_blocking_mode_that_disagrees_with_received_socket() {
1901        let (channel, _peer): (
1902            Channel<ProtobufMessage, ProtobufMessage>,
1903            Channel<ProtobufMessage, ProtobufMessage>,
1904        ) = Channel::generate_nonblocking(8, 64).expect("could not generate channels");
1905        let mut snapshot = channel.snapshot();
1906        snapshot.blocking = true;
1907
1908        // SAFETY: `channel.fd()` is live for this call, and a non-negative
1909        // result is immediately owned so every path closes the duplicate.
1910        let duplicate_fd = unsafe { libc::dup(channel.fd()) };
1911        assert!(duplicate_fd >= 0, "could not duplicate channel descriptor");
1912        // SAFETY: `dup` returned a fresh descriptor owned by this test.
1913        let duplicate = unsafe { OwnedFd::from_raw_fd(duplicate_fd) };
1914        // SAFETY: `duplicate` owns a live descriptor and F_GETFL only reads its
1915        // shared open-file-description flags.
1916        let flags_before = unsafe { libc::fcntl(duplicate.as_raw_fd(), libc::F_GETFL) };
1917        assert!(flags_before >= 0, "could not read channel status flags");
1918        assert_ne!(flags_before & libc::O_NONBLOCK, 0);
1919
1920        let restored = Channel::<ProtobufMessage, ProtobufMessage>::restore_paused(
1921            channel.sock,
1922            snapshot,
1923            8,
1924            64,
1925        );
1926
1927        // SAFETY: the duplicate remains live even when restoration rejects
1928        // and closes the separately owned descriptor supplied above.
1929        let flags_after = unsafe { libc::fcntl(duplicate.as_raw_fd(), libc::F_GETFL) };
1930        assert_eq!(
1931            flags_after, flags_before,
1932            "restore must not change fd flags"
1933        );
1934        assert!(matches!(
1935            restored,
1936            Err(ChannelSnapshotError::BlockingModeMismatch {
1937                snapshot_blocking: true,
1938                fd_blocking: false,
1939            })
1940        ));
1941    }
1942
1943    #[test]
1944    fn channel_snapshot_rejects_untrusted_limits_before_allocation() {
1945        let (channel, _peer): (
1946            Channel<ProtobufMessage, ProtobufMessage>,
1947            Channel<ProtobufMessage, ProtobufMessage>,
1948        ) = Channel::generate_nonblocking(8, 64).expect("could not generate channels");
1949        let mut snapshot = channel.snapshot();
1950        snapshot.max_buffer_size = usize::MAX;
1951        snapshot.front_buffer_capacity = usize::MAX;
1952        snapshot.back_buffer_capacity = usize::MAX;
1953
1954        assert!(matches!(
1955            Channel::<ProtobufMessage, ProtobufMessage>::restore_paused(
1956                channel.sock,
1957                snapshot,
1958                8,
1959                64,
1960            ),
1961            Err(ChannelSnapshotError::BufferLimitsMismatch {
1962                snapshot_initial: 8,
1963                snapshot_max: usize::MAX,
1964                expected_initial: 8,
1965                expected_max: 64,
1966            })
1967        ));
1968    }
1969
1970    #[test]
1971    fn channel_snapshot_restores_full_front_buffer_backpressure_and_rearms_after_drain() {
1972        let (mut frame_source, _frame_peer): (
1973            Channel<ProtobufMessage, ProtobufMessage>,
1974            Channel<ProtobufMessage, ProtobufMessage>,
1975        ) = Channel::generate_nonblocking(32, 32).expect("could not generate channels");
1976        frame_source
1977            .write_delimited_message(&ProtobufMessage { inner: 7 })
1978            .expect("could not frame message");
1979        let frame = frame_source.back_buf.data().to_owned();
1980        let (mut reader, _reader_peer): (
1981            Channel<ProtobufMessage, ProtobufMessage>,
1982            Channel<ProtobufMessage, ProtobufMessage>,
1983        ) = Channel::generate_nonblocking(frame.len() as u64, frame.len() as u64)
1984            .expect("could not generate capacity-bound reader");
1985        reader
1986            .front_buf
1987            .write_all(&frame)
1988            .expect("frame should fit");
1989        reader.handle_events(Ready::READABLE);
1990        assert_eq!(
1991            reader.readable().expect("full buffer should backpressure"),
1992            0
1993        );
1994        assert!(!reader.interest.is_readable());
1995
1996        let snapshot = reader.snapshot();
1997        let paused: PausedChannel<ProtobufMessage, ProtobufMessage> =
1998            Channel::restore_paused(reader.sock, snapshot, frame.len(), frame.len())
1999                .expect("backpressured channel should restore");
2000        let mut restored = paused.resume();
2001
2002        assert_eq!(restored.front_buf.data(), frame);
2003        assert!(!restored.interest.is_readable());
2004        assert!(restored.readiness.is_readable());
2005        assert_eq!(
2006            restored
2007                .read_message()
2008                .expect("buffered frame should decode"),
2009            ProtobufMessage { inner: 7 }
2010        );
2011        assert!(matches!(
2012            restored.read_message(),
2013            Err(ChannelError::NothingRead)
2014        ));
2015        assert!(restored.interest.is_readable());
2016        assert_eq!(restored.front_buf.available_data(), 0);
2017    }
2018
2019    #[test]
2020    fn unblock_a_channel() {
2021        let (mut blocking, _nonblocking) = test_channels();
2022        assert!(blocking.nonblocking().is_ok())
2023    }
2024
2025    #[test]
2026    fn generate_blocking_and_nonblocking_channels() {
2027        let (blocking_channel, nonblocking_channel) = test_channels();
2028
2029        assert!(blocking_channel.is_blocking());
2030        assert!(!nonblocking_channel.is_blocking());
2031
2032        let (nonblocking_channel_1, nonblocking_channel_2): (
2033            Channel<ProtobufMessage, ProtobufMessage>,
2034            Channel<ProtobufMessage, ProtobufMessage>,
2035        ) = Channel::generate_nonblocking(1000, 10000)
2036            .expect("could not generatie nonblocking channels");
2037
2038        assert!(!nonblocking_channel_1.is_blocking());
2039        assert!(!nonblocking_channel_2.is_blocking());
2040    }
2041
2042    #[test]
2043    fn write_and_read_message_blocking() {
2044        let (mut blocking_channel, mut nonblocking_channel) = test_channels();
2045
2046        let message_to_send = ProtobufMessage { inner: 42 };
2047
2048        nonblocking_channel
2049            .blocking()
2050            .expect("Could not block channel");
2051        nonblocking_channel
2052            .write_message(&message_to_send)
2053            .expect("Could not write message on channel");
2054
2055        trace!("we wrote a message!");
2056
2057        trace!("reading message..");
2058        // blocking_channel.readable();
2059        let message = blocking_channel
2060            .read_message()
2061            .expect("Could not read message on channel");
2062        trace!("read message!");
2063
2064        assert_eq!(message, ProtobufMessage { inner: 42 });
2065    }
2066
2067    #[test]
2068    fn read_message_blocking_with_timeout_fails() {
2069        let (mut reading_channel, mut writing_channel) = test_channels();
2070        writing_channel.blocking().expect("Could not block channel");
2071
2072        trace!("reading message in a detached thread, with a timeout of 100 milliseconds...");
2073        // Hand the channel back with the result: dropping it when the thread
2074        // ends would close the peer, and the late write below would then fail
2075        // with `EPIPE` instead of arriving too late (sozu-proxy/sozu#1563).
2076        let awaiting_with_timeout = thread::spawn(move || {
2077            let message =
2078                reading_channel.read_message_blocking_timeout(Some(Duration::from_millis(100)));
2079            trace!("read message!");
2080            (message, reading_channel)
2081        });
2082
2083        trace!("Waiting 200 milliseconds…");
2084        thread::sleep(std::time::Duration::from_millis(200));
2085
2086        writing_channel
2087            .write_message(&ProtobufMessage { inner: 200 })
2088            .expect("Could not write message on channel");
2089        trace!("we wrote a message that should arrive too late!");
2090
2091        let (arrived_too_late, _reading_channel) = awaiting_with_timeout
2092            .join()
2093            .expect("error with receiving message from awaiting thread");
2094
2095        assert!(arrived_too_late.is_err());
2096    }
2097
2098    /// Install a no-op `SIGUSR1` handler with `SA_RESTART`, as the main
2099    /// process does for `SIGTERM`. signal(7): a socket read with a receive
2100    /// timeout (`SO_RCVTIMEO`) is never restarted, it fails with `EINTR`.
2101    #[cfg(target_os = "linux")]
2102    fn install_restarting_sigusr1_handler() {
2103        extern "C" fn ignore(_: libc::c_int) {}
2104        // SAFETY: the handler is async-signal-safe (it does nothing), and the
2105        // `sigaction` struct is fully initialised before the call.
2106        unsafe {
2107            let mut action: libc::sigaction = std::mem::zeroed();
2108            action.sa_sigaction = ignore as extern "C" fn(libc::c_int) as libc::sighandler_t;
2109            action.sa_flags = libc::SA_RESTART;
2110            libc::sigemptyset(&mut action.sa_mask);
2111            assert_eq!(
2112                libc::sigaction(libc::SIGUSR1, &action, std::ptr::null_mut()),
2113                0
2114            );
2115        }
2116    }
2117
2118    /// Spawn a reader blocked in `read_message_blocking_timeout`, then
2119    /// interrupt its thread with `SIGUSR1` every 5 ms for `flood`, so at least
2120    /// one signal lands while the 100 ms `SO_RCVTIMEO` read is blocked.
2121    #[cfg(target_os = "linux")]
2122    fn read_blocking_under_signal_flood(
2123        timeout: Duration,
2124        flood: Duration,
2125        message: Option<ProtobufMessage>,
2126    ) -> (Result<ProtobufMessage, ChannelError>, Duration) {
2127        install_restarting_sigusr1_handler();
2128        let (mut reading_channel, mut writing_channel) = test_channels();
2129        writing_channel.blocking().expect("Could not block channel");
2130
2131        let (thread_sender, thread_receiver) = std::sync::mpsc::channel();
2132        let reader = thread::spawn(move || {
2133            // SAFETY: `pthread_self` has no preconditions.
2134            thread_sender
2135                .send(unsafe { libc::pthread_self() })
2136                .expect("send reader thread id");
2137            let started = std::time::Instant::now();
2138            let message = reading_channel.read_message_blocking_timeout(Some(timeout));
2139            (message, started.elapsed(), reading_channel)
2140        });
2141        let reader_thread = thread_receiver.recv().expect("reader thread id");
2142
2143        let flood_end = std::time::Instant::now() + flood;
2144        while std::time::Instant::now() < flood_end {
2145            thread::sleep(Duration::from_millis(5));
2146            // SAFETY: `reader` is joined below, so `reader_thread` names a
2147            // thread that is running or exited but not yet joined.
2148            unsafe { libc::pthread_kill(reader_thread, libc::SIGUSR1) };
2149        }
2150        if let Some(message) = message {
2151            writing_channel
2152                .write_message(&message)
2153                .expect("Could not write message on channel");
2154        }
2155
2156        let (result, elapsed, _reading_channel) = reader.join().expect("reader thread");
2157        (result, elapsed)
2158    }
2159
2160    /// A signal delivered while a blocking read waits (a whole-cgroup
2161    /// `SIGTERM` reaching a replacement main that waits for COMMIT) must not
2162    /// abort the wait: the message sent afterwards is still received.
2163    #[cfg(target_os = "linux")]
2164    #[test]
2165    fn read_message_blocking_with_timeout_survives_interrupting_signals() {
2166        let (result, _) = read_blocking_under_signal_flood(
2167            Duration::from_secs(5),
2168            Duration::from_millis(300),
2169            Some(ProtobufMessage { inner: 7 }),
2170        );
2171        assert_eq!(
2172            result.expect("an interrupted read must keep waiting"),
2173            ProtobufMessage { inner: 7 }
2174        );
2175    }
2176
2177    /// Retrying an interrupted read keeps the caller's deadline: signals
2178    /// arriving for longer than the timeout neither abort nor extend it.
2179    #[cfg(target_os = "linux")]
2180    #[test]
2181    fn read_message_blocking_with_timeout_keeps_its_deadline_under_signals() {
2182        let timeout = Duration::from_millis(300);
2183        let (result, elapsed) =
2184            read_blocking_under_signal_flood(timeout, Duration::from_millis(600), None);
2185        assert!(
2186            matches!(result, Err(ChannelError::TimeoutReached(reached)) if reached == timeout),
2187            "expected the deadline to be reached, got {result:?}"
2188        );
2189        assert!(
2190            elapsed >= timeout,
2191            "returned before the deadline: {elapsed:?}"
2192        );
2193        // One 100 ms `SO_RCVTIMEO` slice of overshoot, plus scheduling slack.
2194        assert!(
2195            elapsed < timeout + Duration::from_millis(400),
2196            "the deadline was extended: {elapsed:?}"
2197        );
2198    }
2199
2200    #[test]
2201    fn read_message_blocking_with_timeout_succeeds() {
2202        let (mut reading_channel, mut writing_channel) = test_channels();
2203        writing_channel.blocking().expect("Could not block channel");
2204
2205        trace!("reading message in a detached thread, with a timeout of 200 milliseconds...");
2206        let awaiting_with_timeout = thread::spawn(move || {
2207            let message = reading_channel
2208                .read_message_blocking_timeout(Some(Duration::from_millis(200)))
2209                .expect("Could not read message with timeout on blocking channel");
2210            trace!("read message!");
2211            message
2212        });
2213
2214        trace!("Waiting 100 milliseconds…");
2215        thread::sleep(std::time::Duration::from_millis(100));
2216
2217        writing_channel
2218            .write_message(&ProtobufMessage { inner: 100 })
2219            .expect("Could not write message on channel");
2220        trace!("we wrote a message that should arrive on time!");
2221
2222        let arrived_on_time = awaiting_with_timeout
2223            .join()
2224            .expect("error with receiving message from awaiting thread");
2225
2226        assert_eq!(arrived_on_time, ProtobufMessage { inner: 100 });
2227    }
2228
2229    #[test]
2230    fn exhaustive_use_of_nonblocking_channels() {
2231        // - two nonblocking channels A and B, identical
2232        let (mut channel_a, mut channel_b) = test_channels();
2233        channel_a.nonblocking().expect("Could not block channel");
2234
2235        // write on A
2236        channel_a
2237            .write_message(&ProtobufMessage { inner: 1 })
2238            .expect("Could not write message on channel");
2239
2240        // set B as readable, normally mio tells when to, by giving events
2241        channel_b.handle_events(Ready::READABLE);
2242
2243        // read on B
2244        let should_err = channel_b.read_message();
2245        assert!(should_err.is_err());
2246
2247        // write another message on A
2248        channel_a
2249            .write_message(&ProtobufMessage { inner: 2 })
2250            .expect("Could not write message on channel");
2251
2252        // insert a handle_events Ready::writable on A
2253        channel_a.handle_events(Ready::WRITABLE);
2254
2255        // flush A with run()
2256        channel_a.run().expect("Failed to run the channel");
2257
2258        // maybe a thread sleep
2259        thread::sleep(std::time::Duration::from_millis(100));
2260
2261        // receive with B using run()
2262        channel_b.run().expect("Failed to run the channel");
2263
2264        // use read_message() twice on B, check them
2265        let message_1 = channel_b
2266            .read_message()
2267            .expect("Could not read message on channel");
2268        assert_eq!(message_1, ProtobufMessage { inner: 1 });
2269
2270        let message_2 = channel_b
2271            .read_message()
2272            .expect("Could not read message on channel");
2273        assert_eq!(message_2, ProtobufMessage { inner: 2 });
2274    }
2275
2276    #[test]
2277    fn buffer_grows_with_doubling_strategy() {
2278        let (writing_channel, _reading_channel): (
2279            Channel<ProtobufMessage, ProtobufMessage>,
2280            Channel<ProtobufMessage, ProtobufMessage>,
2281        ) = Channel::generate(100, 10000).expect("could not generate channels");
2282
2283        assert_eq!(writing_channel.back_buf.capacity(), 100);
2284
2285        assert_eq!(writing_channel.grow_size(100), Some(200));
2286        assert_eq!(writing_channel.grow_size(200), Some(400));
2287        assert_eq!(writing_channel.grow_size(5000), Some(10000));
2288        assert_eq!(writing_channel.grow_size(10000), None);
2289    }
2290
2291    #[test]
2292    fn buffer_cap_returns_error() {
2293        let (mut writing_channel, _reading_channel): (
2294            Channel<ProtobufMessage, ProtobufMessage>,
2295            Channel<ProtobufMessage, ProtobufMessage>,
2296        ) = Channel::generate(50, 50).expect("could not generate channels");
2297
2298        writing_channel.blocking().expect("Could not block channel");
2299
2300        let mut i = 0u32;
2301        let result = loop {
2302            let msg = ProtobufMessage { inner: i };
2303            match writing_channel.write_delimited_message(&msg) {
2304                Ok(()) => i += 1,
2305                Err(e) => break Err(e),
2306            }
2307            if i > 10000 {
2308                break Ok(());
2309            }
2310        };
2311
2312        assert!(result.is_err());
2313        let err = result.unwrap_err();
2314        let err_msg = format!("{err}");
2315        assert!(
2316            err_msg.contains("too large") || err_msg.contains("cannot grow"),
2317            "unexpected error: {err_msg}"
2318        );
2319    }
2320
2321    #[test]
2322    fn back_buffer_shrinks_after_drain() {
2323        let (mut channel, _other): (
2324            Channel<ProtobufMessage, ProtobufMessage>,
2325            Channel<ProtobufMessage, ProtobufMessage>,
2326        ) = Channel::generate(100, 10000).expect("could not generate channels");
2327
2328        // Write directly to the back buffer (without draining to socket)
2329        // to force growth. Each message is ~10 bytes (delimiter + varint).
2330        for i in 0..20 {
2331            channel
2332                .write_delimited_message(&ProtobufMessage { inner: i })
2333                .expect("Could not write message");
2334        }
2335
2336        let grown_capacity = channel.back_buf.capacity();
2337        assert!(
2338            grown_capacity > 100,
2339            "expected buffer growth, got capacity {grown_capacity}"
2340        );
2341
2342        // Simulate full drain by consuming all data
2343        let data_len = channel.back_buf.available_data();
2344        channel.back_buf.consume(data_len);
2345        assert_eq!(channel.back_buf.available_data(), 0);
2346
2347        channel.try_shrink_back_buf();
2348        assert_eq!(
2349            channel.back_buf.capacity(),
2350            100,
2351            "back buffer should shrink to initial size after drain"
2352        );
2353    }
2354
2355    #[test]
2356    fn back_buffer_grows_with_doubling_on_write() {
2357        let (mut channel, _other): (
2358            Channel<ProtobufMessage, ProtobufMessage>,
2359            Channel<ProtobufMessage, ProtobufMessage>,
2360        ) = Channel::generate(32, 10000).expect("could not generate channels");
2361
2362        assert_eq!(channel.back_buf.capacity(), 32);
2363
2364        // Write enough messages to force growth beyond initial capacity.
2365        // Each ProtobufMessage encodes to ~4 bytes + 8-byte delimiter = ~12 bytes.
2366        for i in 0..10 {
2367            channel
2368                .write_delimited_message(&ProtobufMessage { inner: i })
2369                .expect("Could not write message");
2370        }
2371
2372        let grown = channel.back_buf.capacity();
2373        assert!(grown > 32, "expected buffer growth beyond 32, got {grown}");
2374        // doubling from 32 should yield a power-of-two-like size (64, 128, 256, ...)
2375        // rather than the exact needed amount
2376        assert!(
2377            grown.is_power_of_two() || grown == 10000,
2378            "expected doubling growth pattern, got {grown}"
2379        );
2380    }
2381
2382    /// Regression: a peer that writes a length-delimited frame whose
2383    /// declared length is *less than* the delimiter itself must be
2384    /// rejected with `MessageLengthUnderDelimiter`, never panic the
2385    /// reader with `slice index starts at N but ends at M`.
2386    ///
2387    /// Without the bounds check, `&buffer[delimiter_size()..message_len]`
2388    /// at `try_read_delimited_message` panics for any peer-controlled
2389    /// `message_len < delimiter_size()` (= 8 on 64-bit) — a one-packet
2390    /// denial-of-service against the master command socket.
2391    #[test]
2392    fn rejects_declared_length_below_delimiter() {
2393        let (mut reader, mut writer): (
2394            Channel<ProtobufMessage, ProtobufMessage>,
2395            Channel<ProtobufMessage, ProtobufMessage>,
2396        ) = Channel::generate(1000, 10000).expect("could not generate channels");
2397        writer.blocking().expect("writer to block");
2398        reader.blocking().expect("reader to block");
2399
2400        // Craft a delimiter that lies: message_len = 5 (< delimiter_size() = 8).
2401        // Send it as raw bytes, bypassing write_delimited_message.
2402        let bogus: usize = 5;
2403        let bytes = bogus.to_le_bytes();
2404        std::io::Write::write_all(&mut writer.sock, &bytes).expect("raw write of bogus delimiter");
2405
2406        match reader.read_message() {
2407            Err(ChannelError::MessageLengthUnderDelimiter {
2408                message_len,
2409                delimiter_size,
2410            }) => {
2411                assert_eq!(message_len, 5);
2412                assert_eq!(delimiter_size, std::mem::size_of::<usize>());
2413            }
2414            other => panic!(
2415                "expected MessageLengthUnderDelimiter, got {other:?}\n\
2416                 NOTE: a panic here means the slice-OOB hardening was reverted",
2417            ),
2418        }
2419    }
2420
2421    /// Regression for sozu-proxy/sozu#1416: `Channel::new`/`generate_nonblocking` took
2422    /// `buffer_size` and `max_buffer_size` without ever comparing them, so a caller
2423    /// passing `buffer_size > max_buffer_size` built a `front_buf` already larger than
2424    /// the ceiling it must respect. `45, 32` is the exact pair the fuzz target's
2425    /// construction generator produced before it was clamped to work around this defect.
2426    /// `try_read_delimited_message` then tripped its own
2427    /// "front buffer capacity must never exceed max_buffer_size" `debug_assert!` on the
2428    /// very first parse, before any wire byte was examined.
2429    #[test]
2430    fn channel_new_clamps_buffer_size_above_max() {
2431        let (mut reader, _writer): (
2432            Channel<ProtobufMessage, ProtobufMessage>,
2433            Channel<ProtobufMessage, ProtobufMessage>,
2434        ) = Channel::generate_nonblocking(45, 32).expect("could not generate channels");
2435
2436        // Parsing an empty buffer must not trip the capacity invariant assertion
2437        // (pre-fix: `try_read_delimited_message`'s own "front buffer capacity must
2438        // never exceed max_buffer_size" `debug_assert!` fires right here, before any
2439        // wire byte is examined).
2440        assert_eq!(
2441            reader
2442                .try_read_delimited_message()
2443                .expect("parsing an empty buffer must not error"),
2444            None
2445        );
2446
2447        assert!(
2448            reader.front_buf.capacity() <= 32,
2449            "front buffer capacity {} must never exceed max_buffer_size 32",
2450            reader.front_buf.capacity()
2451        );
2452    }
2453
2454    /// Regression for sozu-proxy/sozu#1428: a peer that declares a frame
2455    /// length above `max_buffer_size` must mark the channel for closing.
2456    ///
2457    /// The oversize guard rejects the frame but cannot re-sync past it. The
2458    /// declared length is *unsatisfiable* — the frame can never fit a buffer
2459    /// capped at `max_buffer_size`, so no amount of further reading completes
2460    /// it — and the bytes behind the delimiter are of unknown provenance:
2461    /// dropping the eight header bytes and re-framing would decode
2462    /// peer-chosen payload bytes as a fresh control-plane command. That is
2463    /// why this branch must NOT consume, while its sibling
2464    /// `MessageLengthUnderDelimiter` must: a declared length below
2465    /// `delimiter_size()` is one no writer can emit for any payload, so those
2466    /// eight bytes are provably not a real header and skipping exactly them
2467    /// re-aligns the stream.
2468    ///
2469    /// Left unconsumed AND unsignalled, the delimiter is re-parsed on every
2470    /// later read forever: `read_message_nonblocking` propagates the error
2471    /// with no log, `extract_messages` (`bin/src/command/sessions.rs`)
2472    /// discards it with a bare `Err(_)`, and neither `ClientSession::ready`
2473    /// nor `WorkerSession::ready` closes the session because both key on
2474    /// `readiness.is_error() || is_hup()`, which no parse error ever sets.
2475    /// The session then wedges permanently, pinning its file descriptor and
2476    /// up to `max_buffer_size` of buffer, and `doc/configure_admin_ops.md`
2477    /// §5.2's "the supervisor logs and drops the offending peer's session"
2478    /// never happens.
2479    #[test]
2480    fn oversized_declared_length_marks_the_channel_for_closing() {
2481        let (mut reader, mut writer): (
2482            Channel<ProtobufMessage, ProtobufMessage>,
2483            Channel<ProtobufMessage, ProtobufMessage>,
2484        ) = Channel::generate_nonblocking(1000, 10000)
2485            .expect("could not generate nonblocking channels");
2486
2487        // One byte past this reader's `max_buffer_size`. Written raw only
2488        // because both ends of this pair share one ceiling, so the writer's own
2489        // `write_delimited_message` would refuse it. That is not true of a real
2490        // deployment: the two ends size their channels from their own
2491        // `max_command_buffer_size` (`bin/config.toml` ships 163_840,
2492        // `os-build/config.toml` 1_638_400, the compiled default is 2_000_000),
2493        // so a peer configured higher puts this frame on the wire through the
2494        // ordinary write path while conforming perfectly. What follows is
2495        // therefore about what this end can do with the frame, not about the
2496        // peer's good faith.
2497        let oversized: usize = 10_001;
2498        std::io::Write::write_all(&mut writer.sock, &oversized.to_le_bytes())
2499            .expect("raw write of the oversized delimiter");
2500
2501        reader.handle_events(Ready::READABLE);
2502        reader
2503            .readable()
2504            .expect("reader must buffer the delimiter bytes");
2505
2506        let available_before = reader.front_buf.available_data();
2507        assert_eq!(
2508            available_before,
2509            delimiter_size(),
2510            "the delimiter must be buffered before the parse under test"
2511        );
2512
2513        // Arm writability before the parse that marks the channel, so the
2514        // assertion at the end of this test can tell an `insert` from an
2515        // assignment.
2516        reader.handle_events(Ready::WRITABLE);
2517
2518        match reader.read_message() {
2519            Err(ChannelError::MessageTooLarge {
2520                message_len, max, ..
2521            }) => {
2522                assert_eq!(message_len, oversized);
2523                assert_eq!(max, 10000);
2524            }
2525            other => panic!("expected MessageTooLarge, got {other:?}"),
2526        }
2527
2528        // The bytes stay put -- deliberately, see the doc comment above.
2529        assert_eq!(
2530            reader.front_buf.available_data(),
2531            available_before,
2532            "an unsatisfiable declared length must not be consumed: re-framing \
2533             on the bytes behind it would decode peer-chosen bytes as a command"
2534        );
2535
2536        // So a second parse returns the identical error against the identical
2537        // bytes. The parser alone can never make progress on this stream.
2538        match reader.read_message() {
2539            Err(ChannelError::MessageTooLarge { message_len, .. }) => {
2540                assert_eq!(message_len, oversized)
2541            }
2542            other => panic!("expected the identical MessageTooLarge, got {other:?}"),
2543        }
2544        assert_eq!(reader.front_buf.available_data(), available_before);
2545
2546        // Which leaves closing the peer as the only correct exit, and the channel
2547        // must say so through the one signal `ClientSession::ready`,
2548        // `WorkerSession::ready` and `wants_to_tick` all key on
2549        // (`bin/src/command/sessions.rs`).
2550        assert!(
2551            reader.readiness.is_error(),
2552            "an oversized declaration must mark the channel errored so the \
2553             supervisor drops the session; without it the same delimiter is \
2554             re-parsed forever, the error is swallowed by `extract_messages`'s \
2555             `Err(_)` arm, and the session wedges holding its fd and buffer"
2556        );
2557        // And it must INSERT that bit rather than assign it. On the worker's
2558        // own channel (`lib/src/server.rs`) `interest` carries only READABLE
2559        // and WRITABLE, so `readiness()` masks ERROR away and nothing there
2560        // reads `is_error()`: an assignment's only effect on that side is
2561        // clearing WRITABLE, and `Server::send_queue` gates on
2562        // `channel.readiness.is_writable()`, so responses already queued for
2563        // the supervisor would stall until the next edge-triggered writability
2564        // event.
2565        assert!(
2566            reader.readiness.is_writable(),
2567            "marking the channel errored must not clear the other readiness \
2568             bits; wiping WRITABLE stalls `Server::send_queue` until the next \
2569             writability event"
2570        );
2571    }
2572
2573    /// Regression for the second shape of sozu-proxy/sozu#1428: a frame whose
2574    /// declared length is valid but whose payload does not decode must be
2575    /// consumed, so the channel re-syncs on the next frame instead of
2576    /// re-decoding the same bytes for the life of the session.
2577    ///
2578    /// `try_read_delimited_message` propagated `InvalidProtobufMessage` from
2579    /// the `?` on `Rx::decode`, one line *before* the `consume`. The malformed
2580    /// frame therefore stayed at the head of `front_buf`, no readiness bit
2581    /// moved, `extract_messages` (`bin/src/command/sessions.rs`) discarded the
2582    /// error with its bare `Err(_)` arm, and the session wedged exactly as the
2583    /// oversize branch above did. Eight valid length bytes followed by garbage
2584    /// reach it, and so does protobuf skew between two builds.
2585    ///
2586    /// Consuming is coherent with the success path rather than provably safe:
2587    /// `message_len` is only range-checked, but the success path already
2588    /// consumes that same peer-supplied length and re-frames on whatever
2589    /// follows, so the decode-failure path now re-frames on exactly the bytes
2590    /// the decode-success path already did. One undecodable payload therefore
2591    /// costs its frame rather than the peer.
2592    ///
2593    /// SCOPE: this asserts the channel contract, not a session one. Unlike the
2594    /// oversize branch above, this shape has no `ClientSession`-level test, so
2595    /// nothing here proves a session-level property. A frame pipelined behind
2596    /// the malformed one is delivered on the same tick; the session-level proof
2597    /// of that is
2598    /// `client_session_delivers_the_frame_behind_a_malformed_length_prefix`,
2599    /// built on the sibling `MessageLengthUnderDelimiter` shape.
2600    #[test]
2601    fn malformed_payload_is_consumed_so_the_channel_resyncs() {
2602        let (mut reader, mut writer): (
2603            Channel<ProtobufMessage, ProtobufMessage>,
2604            Channel<ProtobufMessage, ProtobufMessage>,
2605        ) = Channel::generate_nonblocking(1000, 10000)
2606            .expect("could not generate nonblocking channels");
2607
2608        // A well-formed frame whose payload is not a `ProtobufMessage`: a
2609        // leading zero byte is field number 0, which no protobuf decoder
2610        // accepts. The length prefix itself is entirely valid. Hand-crafted
2611        // rather than random so the failure is deterministic and independent
2612        // of how much arbitrary input prost happens to accept.
2613        let frame_len: usize = delimiter_size() + 8;
2614        let mut malformed = frame_len.to_le_bytes().to_vec();
2615        malformed.extend_from_slice(&[0x00_u8; 8]);
2616        std::io::Write::write_all(&mut writer.sock, &malformed)
2617            .expect("raw write of the malformed frame");
2618
2619        // ... immediately followed by a perfectly good one.
2620        writer
2621            .write_message(&ProtobufMessage { inner: 7 })
2622            .expect("could not queue the following message");
2623        writer.handle_events(Ready::WRITABLE);
2624        writer.run().expect("could not flush the following message");
2625
2626        reader.handle_events(Ready::READABLE);
2627        reader.readable().expect("reader must buffer both frames");
2628
2629        let available_before = reader.front_buf.available_data();
2630        assert!(
2631            available_before > frame_len,
2632            "both frames must be buffered before the parse under test"
2633        );
2634
2635        match reader.read_message() {
2636            Err(ChannelError::InvalidProtobufMessage(_)) => {}
2637            other => panic!("expected InvalidProtobufMessage, got {other:?}"),
2638        }
2639
2640        assert_eq!(
2641            reader.front_buf.available_data(),
2642            available_before - frame_len,
2643            "a malformed payload behind a valid length must be consumed: the \
2644             frame boundary is exact, so dropping exactly those bytes re-syncs \
2645             the stream"
2646        );
2647
2648        assert_eq!(
2649            reader.read_message().expect("the next frame must decode"),
2650            ProtobufMessage { inner: 7 },
2651            "the channel must make progress past a malformed frame instead of \
2652             re-decoding it on every later read"
2653        );
2654    }
2655
2656    /// Builds the exact front-buffer layout of sozu-proxy/sozu#1436 and returns
2657    /// the reader sitting in it, the writer still holding the pending frame's
2658    /// tail, and that tail.
2659    ///
2660    /// `Buffer::fill` (`command/src/buffer/growable.rs`) compacts whenever a
2661    /// read reaches the end of the buffer, so a front buffer filled to capacity
2662    /// always lands at `position == 0`. `Buffer::consume` compacts only past
2663    /// `capacity / 2`, so decoding one frame of at most half the capacity out of
2664    /// that full buffer advances `position` without reaching the threshold and
2665    /// leaves `available_space() == 0` with `capacity - first.len()` bytes
2666    /// pending. At `capacity == max_buffer_size` nothing can grow out of it
2667    /// either.
2668    ///
2669    /// `declared` is the length the second frame announces; the caller picks it
2670    /// on whichever side of the ceiling it wants to exercise.
2671    fn wedged_reader(
2672        capacity: usize,
2673        declared: usize,
2674    ) -> (
2675        Channel<ProtobufMessage, ProtobufMessage>,
2676        Channel<ProtobufMessage, ProtobufMessage>,
2677        Vec<u8>,
2678    ) {
2679        let (mut reader, mut writer): (
2680            Channel<ProtobufMessage, ProtobufMessage>,
2681            Channel<ProtobufMessage, ProtobufMessage>,
2682        ) = Channel::generate_nonblocking(capacity as u64, capacity as u64)
2683            .expect("could not generate nonblocking channels");
2684
2685        // A real frame, produced by the production write path.
2686        writer
2687            .write_delimited_message(&ProtobufMessage { inner: 1 })
2688            .expect("could not frame the first message");
2689        let first = writer.back_buf.data().to_vec();
2690        writer.back_buf.consume(first.len());
2691        assert!(
2692            first.len() <= capacity / 2,
2693            "the first frame ({} bytes) must stay at or under `Buffer::consume`'s \
2694             {}-byte shift threshold, or the layout under test never forms",
2695            first.len(),
2696            capacity / 2
2697        );
2698
2699        // The second frame. `[0x08, 0x2a]` is field 1 set to 42, repeated: a
2700        // non-repeated scalar takes its last occurrence, so the payload decodes
2701        // at any EVEN length -- callers pass an even `declared`, since an odd
2702        // one would truncate the last pair to a tag with no varint behind it.
2703        // Written raw because both ends of this pair share one ceiling, so
2704        // `write_delimited_message` cannot emit a frame this large -- a peer
2705        // configured with a larger `max_command_buffer_size` puts exactly these
2706        // bytes on the wire through the ordinary write path.
2707        let mut second = declared.to_le_bytes().to_vec();
2708        while second.len() < declared {
2709            second.extend_from_slice(&[0x08, 0x2a]);
2710        }
2711        debug_assert_eq!(
2712            declared % 2,
2713            0,
2714            "an odd declared length truncates the padding to a dangling tag"
2715        );
2716        second.truncate(declared.max(delimiter_size()));
2717
2718        // Fill the front buffer to the brim: the first frame, then as much of
2719        // the second as fits. The rest stays in the writer.
2720        let head = capacity - first.len();
2721        let head = head.min(second.len());
2722        let mut wire = first.clone();
2723        wire.extend_from_slice(&second[..head]);
2724        std::io::Write::write_all(&mut writer.sock, &wire)
2725            .expect("raw write of the first frame and the second frame's head");
2726
2727        reader.handle_events(Ready::READABLE);
2728        assert_eq!(
2729            reader
2730                .readable()
2731                .expect("the reader must fill its front buffer"),
2732            wire.len()
2733        );
2734        assert_eq!(
2735            reader.front_buf.available_space(),
2736            0,
2737            "the front buffer must be filled to the brim before the decode"
2738        );
2739
2740        assert_eq!(
2741            reader.read_message().expect("the first frame must decode"),
2742            ProtobufMessage { inner: 1 }
2743        );
2744        assert_eq!(reader.front_buf.available_data(), capacity - first.len());
2745        assert_eq!(
2746            reader.front_buf.available_space(),
2747            0,
2748            "the layout under test: data pending, zero space, capacity pinned at \
2749             the ceiling -- `Buffer::consume` did not reach its shift threshold"
2750        );
2751
2752        let tail = second[head..].to_vec();
2753        (reader, writer, tail)
2754    }
2755
2756    /// Regression for sozu-proxy/sozu#1436: a frame that fits the ceiling but
2757    /// not the front buffer's current *layout* must be rescued by compaction,
2758    /// not rejected.
2759    ///
2760    /// The `BufferFull` arm of `try_read_delimited_message` returned without
2761    /// consuming and without moving a readiness bit -- the shape
2762    /// sozu-proxy/sozu#1428 removed from its two siblings -- and nothing
2763    /// downstream compensated: `extract_messages` (`bin/src/command/sessions.rs`)
2764    /// discards the error with a bare `Err(_)`, and `ClientSession::ready` /
2765    /// `WorkerSession::ready` close only on `readiness.is_error() || is_hup()`.
2766    /// Worse, it was unrecoverable: the three `front_buf` mutators that can
2767    /// trigger a `Buffer::shift` are all out of reach afterwards. `fill` needs a
2768    /// successful read, but `readable()` finds `available_space() == 0`, gets
2769    /// `None` from `grow_size` at the ceiling and executes
2770    /// `interest.remove(Ready::READABLE)`, so every later `readable()` returns
2771    /// `Err(Connection(None))`; `consume` needs a decoded frame or an
2772    /// under-delimiter length, and the parse returns before either;
2773    /// `try_shrink_front_buf` is only called after a frame decoded, and both
2774    /// of its own early returns would stop it anyway -- `capacity <=
2775    /// initial_buffer_size`, which holds whenever the channel was configured
2776    /// with `command_buffer_size == max_command_buffer_size`, and
2777    /// `available_data() * 4 < initial_buffer_size`, which is false with that
2778    /// much pending. Not because it shrinks rather than shifts: `Buffer::shrink`
2779    /// calls `shift` unconditionally before its own size bail.
2780    ///
2781    /// SCOPE: this is the channel contract. It proves the parser makes room and
2782    /// the frame decodes once its tail is buffered; it proves nothing about the
2783    /// session, whose drain loop had its own reason to stop one read short --
2784    /// see `client_session_completes_a_compacted_frame_without_another_peer_write`
2785    /// in `bin/src/command/sessions.rs`.
2786    ///
2787    /// The recovery is NOT the oversize branch's close. That branch drops the
2788    /// peer because the declared length can never be satisfied by a buffer
2789    /// bounded at `max_buffer_size`. Here the frame is under the ceiling and its
2790    /// buffered bytes are intact: only the buffer's internal offsets are in the
2791    /// way, and a single `Buffer::shift` hands back exactly the space the
2792    /// consumed frame left behind. Closing would drop a conforming peer over an
2793    /// internal buffer-management detail.
2794    #[test]
2795    fn a_frame_within_the_ceiling_is_rescued_by_compaction() {
2796        let capacity = 64usize;
2797        // Exactly at the ceiling: the largest frame this end may legitimately
2798        // be asked to accept, and the boundary case of the guard above.
2799        let (mut reader, mut writer, tail) = wedged_reader(capacity, capacity);
2800        assert!(
2801            !tail.is_empty(),
2802            "the second frame must still be incomplete"
2803        );
2804
2805        std::io::Write::write_all(&mut writer.sock, &tail)
2806            .expect("raw write of the second frame's tail");
2807
2808        // The parse that used to wedge. It must make room and ask for more
2809        // bytes, not declare the buffer unusable.
2810        match reader.read_message() {
2811            Err(ChannelError::NothingRead) => {}
2812            other => panic!(
2813                "expected the parser to compact and wait for the rest of the \
2814                 frame, got {other:?}"
2815            ),
2816        }
2817        assert!(
2818            reader.front_buf.available_space() >= tail.len(),
2819            "compaction must hand back the space the consumed frame left behind: \
2820             {} bytes of room for a {}-byte tail",
2821            reader.front_buf.available_space(),
2822            tail.len()
2823        );
2824        assert!(
2825            !reader.readiness.is_error(),
2826            "a frame within the ceiling must not mark the channel for closing"
2827        );
2828
2829        // And the session is alive: it reads again, and the frame completes.
2830        reader.handle_events(Ready::READABLE);
2831        assert_eq!(
2832            reader
2833                .readable()
2834                .expect("the reader must still accept the frame's tail"),
2835            tail.len()
2836        );
2837        assert_eq!(
2838            reader
2839                .read_message()
2840                .expect("the second frame must decode once its tail is buffered"),
2841            ProtobufMessage { inner: 42 },
2842            "a conforming peer's frame must complete rather than wedge the session"
2843        );
2844    }
2845
2846    /// The other side of sozu-proxy/sozu#1436's boundary: compaction must not
2847    /// rescue a frame that is genuinely unsatisfiable.
2848    ///
2849    /// A declared length above `max_buffer_size` does not fit a buffer bounded
2850    /// at `max_buffer_size` however it is compacted, so it must keep taking the
2851    /// sozu-proxy/sozu#1428 path -- `MessageTooLarge`, logged, channel marked
2852    /// errored -- from this layout exactly as from an empty buffer. The ceiling
2853    /// check runs before the zero-space arm precisely so that the two cases
2854    /// cannot be confused.
2855    #[test]
2856    fn a_frame_above_the_ceiling_still_closes_from_the_same_layout() {
2857        let capacity = 64usize;
2858        // Past the ceiling, in the very layout the compaction path rescues at
2859        // `capacity`. Even, because `wedged_reader` pads with two-byte protobuf
2860        // pairs and an odd length would leave a dangling tag; `capacity + 1` is
2861        // rejected by the identical guard, one byte earlier.
2862        let (mut reader, _writer, _tail) = wedged_reader(capacity, capacity + 2);
2863
2864        let pending = reader.front_buf.available_data();
2865        match reader.read_message() {
2866            Err(ChannelError::MessageTooLarge {
2867                message_len, max, ..
2868            }) => {
2869                assert_eq!(message_len, capacity + 2);
2870                assert_eq!(max, capacity);
2871            }
2872            other => panic!(
2873                "expected MessageTooLarge: compaction must not rescue a frame the \
2874                 ceiling can never admit, got {other:?}"
2875            ),
2876        }
2877        assert_eq!(
2878            reader.front_buf.available_data(),
2879            pending,
2880            "an unsatisfiable declared length must still not be consumed"
2881        );
2882        assert!(
2883            reader.readiness.is_error(),
2884            "an unsatisfiable declared length must still mark the channel for \
2885             closing, whatever the buffer layout it was parsed from"
2886        );
2887    }
2888
2889    /// A peer that dies with bytes still queued towards it makes the next
2890    /// `read(2)` fail with `ECONNRESET` rather than return EOF. That failure
2891    /// must leave the channel marked for closing exactly as EOF does: the
2892    /// supervisor's `WorkerSession::ready` closes a session on HUP or ERROR
2893    /// only, and an edge-triggered poller reports the hangup once, so a read
2894    /// error that wiped the readiness left a dead worker's session open for
2895    /// good (sozu-proxy/sozu#1560).
2896    #[test]
2897    fn a_read_error_keeps_the_channel_marked_for_closing() {
2898        for initial in [
2899            Ready::READABLE | Ready::WRITABLE | Ready::HUP | Ready::ERROR,
2900            Ready::READABLE | Ready::WRITABLE,
2901        ] {
2902            let (mut local, peer) =
2903                Channel::<ProtobufMessage, ProtobufMessage>::generate_nonblocking(1000, 10000)
2904                    .expect("could not generate nonblocking channels");
2905            // Leave a byte unread in the peer's receive queue, then close the
2906            // peer: the kernel resets the connection instead of sending EOF.
2907            assert_eq!(local.sock.write(b"x").expect("write to the live peer"), 1);
2908            drop(peer);
2909
2910            local.readiness = initial;
2911            match local.readable() {
2912                Err(ChannelError::Read(error)) => {
2913                    assert_eq!(error.kind(), ErrorKind::ConnectionReset)
2914                }
2915                other => panic!("expected a connection reset, got {other:?}"),
2916            }
2917            assert!(
2918                local.readiness.is_hup() && local.readiness.is_error(),
2919                "a read error must leave HUP and ERROR set (from {initial:?}), got {:?}",
2920                local.readiness
2921            );
2922            assert!(!local.readiness.is_readable() && !local.readiness.is_writable());
2923            assert_eq!(local.interest, Ready::EMPTY);
2924        }
2925    }
2926
2927    /// Same contract on the write side: `EPIPE` towards a closed peer must
2928    /// mark the channel for closing, as `Ok(0)` already does, and is reported
2929    /// as `ChannelError::Write`, not `Read` (sozu-proxy/sozu#1563).
2930    #[test]
2931    fn a_write_error_keeps_the_channel_marked_for_closing() {
2932        for initial in [
2933            Ready::READABLE | Ready::WRITABLE | Ready::HUP | Ready::ERROR,
2934            Ready::READABLE | Ready::WRITABLE,
2935        ] {
2936            let (mut local, peer) =
2937                Channel::<ProtobufMessage, ProtobufMessage>::generate_nonblocking(1000, 10000)
2938                    .expect("could not generate nonblocking channels");
2939            drop(peer);
2940
2941            local
2942                .write_delimited_message(&ProtobufMessage { inner: 7 })
2943                .expect("queue a message in the back buffer");
2944            local.interest.insert(Ready::WRITABLE);
2945            local.readiness = initial;
2946            match local.writable() {
2947                Err(ChannelError::Write(error)) => {
2948                    assert_eq!(error.kind(), ErrorKind::BrokenPipe)
2949                }
2950                other => panic!("expected a broken pipe reported as a write error, got {other:?}"),
2951            }
2952            assert!(
2953                local.readiness.is_hup() && local.readiness.is_error(),
2954                "a write error must leave HUP and ERROR set (from {initial:?}), got {:?}",
2955                local.readiness
2956            );
2957            assert!(!local.readiness.is_readable() && !local.readiness.is_writable());
2958            assert_eq!(local.interest, Ready::EMPTY);
2959        }
2960    }
2961
2962    /// A blocking write towards a closed peer must fail with the socket error.
2963    /// It used to return `Ok(())` for any `write(2)` error, leaving the frame
2964    /// in `back_buf` while the caller believed it delivered
2965    /// (sozu-proxy/sozu#1563).
2966    #[test]
2967    fn a_blocking_write_to_a_closed_peer_is_an_error() {
2968        let (mut blocking, peer) = test_channels();
2969        assert!(blocking.is_blocking());
2970        drop(peer);
2971
2972        match blocking.write_message(&ProtobufMessage { inner: 9 }) {
2973            Err(ChannelError::Write(error)) => assert_eq!(error.kind(), ErrorKind::BrokenPipe),
2974            other => panic!("expected a broken pipe reported as a write error, got {other:?}"),
2975        }
2976        assert!(
2977            blocking.back_buf.available_data() > 0,
2978            "the unsent frame must stay in the back buffer"
2979        );
2980    }
2981}