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