Skip to main content

Module line_reader

Module line_reader 

Source
Expand description

One bounded, \n-terminated read, shared by the line-oriented text protocols.

tokio::io::AsyncBufReadExt::read_line grows its String until it finds a newline and caps nothing. A peer that connects and streams printable bytes without ever sending \n therefore makes the server allocate without bound — a one-connection out-of-memory, from an unauthenticated socket, before any model call. That defect was found in ftp, irc and smtp and fixed three separate times, then found again in imap, nntp and pop3.

Three copies of a fill_buf/consume loop is how the control-character filters ended up with six different semantics before utils::sanitize existed, so this is one implementation rather than a sixth. Each protocol still owns its own MAX_* constant and its own refusal wording — the bound is a protocol decision, the reading is not.

The cap is on what is buffered, not on what is consumed. A line longer than the cap is reported as BoundedLine::TooLong with the offending bytes left unconsumed, because there is no resynchronisation point: the peer is mid-line and the next byte is not the start of a command. Every caller answers in its own vocabulary and closes.

Decoding is lossy. read_line returns ErrorKind::InvalidData for any non-UTF-8 byte and kills the session on it, which is wrong for protocols that advertise 8-bit transports (smtp’s 8BITMIME, imap’s literals). These bytes become an event payload and a log line; nothing here is re-emitted on the wire, so a lossy decode loses nothing a peer can observe.

Enums§

BoundedLine
The outcome of one bounded line read.

Functions§

read_bounded_delimited
Read one complete byte-delimited frame, preserving binary payloads and enforcing the cap before allocation. EOF with a fragment is an error. Keep this future alive across unrelated commands: like read_until, cancellation discards its partial frame.
read_bounded_line
Read one \n-terminated line, buffering at most max_len bytes.