2019-10-22 10:13:49 -07:00
|
|
|
use crate::codec::decoder::Decoder;
|
|
|
|
|
use crate::codec::encoder::Encoder;
|
|
|
|
|
|
2019-11-20 14:27:49 -08:00
|
|
|
use bytes::{Buf, BufMut, BytesMut};
|
2024-05-18 10:09:37 +02:00
|
|
|
use std::{cmp, fmt, io, str};
|
2018-06-04 22:36:06 -05:00
|
|
|
|
2020-02-01 23:04:58 +01:00
|
|
|
/// A simple [`Decoder`] and [`Encoder`] implementation that splits up data into lines.
|
|
|
|
|
///
|
2023-09-08 21:59:48 +08:00
|
|
|
/// This uses the `\n` character as the line ending on all platforms.
|
|
|
|
|
///
|
2020-02-01 23:04:58 +01:00
|
|
|
/// [`Decoder`]: crate::codec::Decoder
|
|
|
|
|
/// [`Encoder`]: crate::codec::Encoder
|
2018-06-04 22:36:06 -05:00
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
|
|
|
|
pub struct LinesCodec {
|
|
|
|
|
// Stored index of the next index to examine for a `\n` character.
|
|
|
|
|
// This is used to optimize searching.
|
|
|
|
|
// For example, if `decode` was called with `abc`, it would hold `3`,
|
|
|
|
|
// because that is the next index to examine.
|
|
|
|
|
// The next time `decode` is called with `abcde\n`, the method will
|
|
|
|
|
// only look at `de\n` before returning.
|
|
|
|
|
next_index: usize,
|
2018-09-20 17:08:00 -07:00
|
|
|
|
|
|
|
|
/// The maximum length for a given line. If `usize::MAX`, lines will be
|
|
|
|
|
/// read until a `\n` character is reached.
|
|
|
|
|
max_length: usize,
|
|
|
|
|
|
|
|
|
|
/// Are we currently discarding the remainder of a line which was over
|
|
|
|
|
/// the length limit?
|
|
|
|
|
is_discarding: bool,
|
2018-06-04 22:36:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl LinesCodec {
|
|
|
|
|
/// Returns a `LinesCodec` for splitting up data into lines.
|
2018-09-20 17:08:00 -07:00
|
|
|
///
|
|
|
|
|
/// # Note
|
|
|
|
|
///
|
|
|
|
|
/// The returned `LinesCodec` will not have an upper bound on the length
|
|
|
|
|
/// of a buffered line. See the documentation for [`new_with_max_length`]
|
|
|
|
|
/// for information on why this could be a potential security risk.
|
|
|
|
|
///
|
2020-02-01 23:04:58 +01:00
|
|
|
/// [`new_with_max_length`]: crate::codec::LinesCodec::new_with_max_length()
|
2018-06-04 22:36:06 -05:00
|
|
|
pub fn new() -> LinesCodec {
|
2018-09-20 17:08:00 -07:00
|
|
|
LinesCodec {
|
|
|
|
|
next_index: 0,
|
|
|
|
|
max_length: usize::MAX,
|
|
|
|
|
is_discarding: false,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns a `LinesCodec` with a maximum line length limit.
|
|
|
|
|
///
|
|
|
|
|
/// If this is set, calls to `LinesCodec::decode` will return a
|
2020-02-01 23:04:58 +01:00
|
|
|
/// [`LinesCodecError`] when a line exceeds the length limit. Subsequent calls
|
2018-09-20 17:08:00 -07:00
|
|
|
/// will discard up to `limit` bytes from that line until a newline
|
|
|
|
|
/// character is reached, returning `None` until the line over the limit
|
|
|
|
|
/// has been fully discarded. After that point, calls to `decode` will
|
|
|
|
|
/// function as normal.
|
|
|
|
|
///
|
|
|
|
|
/// # Note
|
|
|
|
|
///
|
|
|
|
|
/// Setting a length limit is highly recommended for any `LinesCodec` which
|
|
|
|
|
/// will be exposed to untrusted input. Otherwise, the size of the buffer
|
|
|
|
|
/// that holds the line currently being read is unbounded. An attacker could
|
|
|
|
|
/// exploit this unbounded buffer by sending an unbounded amount of input
|
|
|
|
|
/// without any `\n` characters, causing unbounded memory consumption.
|
|
|
|
|
///
|
2020-02-01 23:04:58 +01:00
|
|
|
/// [`LinesCodecError`]: crate::codec::LinesCodecError
|
2018-09-20 17:08:00 -07:00
|
|
|
pub fn new_with_max_length(max_length: usize) -> Self {
|
|
|
|
|
LinesCodec {
|
|
|
|
|
max_length,
|
|
|
|
|
..LinesCodec::new()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the maximum line length when decoding.
|
|
|
|
|
///
|
|
|
|
|
/// ```
|
|
|
|
|
/// use std::usize;
|
2019-10-22 10:13:49 -07:00
|
|
|
/// use tokio_util::codec::LinesCodec;
|
2018-09-20 17:08:00 -07:00
|
|
|
///
|
|
|
|
|
/// let codec = LinesCodec::new();
|
|
|
|
|
/// assert_eq!(codec.max_length(), usize::MAX);
|
|
|
|
|
/// ```
|
|
|
|
|
/// ```
|
2019-10-22 10:13:49 -07:00
|
|
|
/// use tokio_util::codec::LinesCodec;
|
2018-09-20 17:08:00 -07:00
|
|
|
///
|
|
|
|
|
/// let codec = LinesCodec::new_with_max_length(256);
|
|
|
|
|
/// assert_eq!(codec.max_length(), 256);
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn max_length(&self) -> usize {
|
|
|
|
|
self.max_length
|
|
|
|
|
}
|
2018-06-04 22:36:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn utf8(buf: &[u8]) -> Result<&str, io::Error> {
|
2019-02-21 11:56:15 -08:00
|
|
|
str::from_utf8(buf)
|
|
|
|
|
.map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Unable to decode input as UTF8"))
|
2018-06-04 22:36:06 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn without_carriage_return(s: &[u8]) -> &[u8] {
|
|
|
|
|
if let Some(&b'\r') = s.last() {
|
|
|
|
|
&s[..s.len() - 1]
|
|
|
|
|
} else {
|
|
|
|
|
s
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Decoder for LinesCodec {
|
|
|
|
|
type Item = String;
|
2019-06-27 18:10:29 +01:00
|
|
|
type Error = LinesCodecError;
|
2018-06-04 22:36:06 -05:00
|
|
|
|
2019-06-27 18:10:29 +01:00
|
|
|
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<String>, LinesCodecError> {
|
2018-09-20 17:08:00 -07:00
|
|
|
loop {
|
|
|
|
|
// Determine how far into the buffer we'll search for a newline. If
|
|
|
|
|
// there's no max_length set, we'll read to the end of the buffer.
|
|
|
|
|
let read_to = cmp::min(self.max_length.saturating_add(1), buf.len());
|
|
|
|
|
|
|
|
|
|
let newline_offset = buf[self.next_index..read_to]
|
|
|
|
|
.iter()
|
|
|
|
|
.position(|b| *b == b'\n');
|
|
|
|
|
|
2019-08-26 16:38:52 -04:00
|
|
|
match (self.is_discarding, newline_offset) {
|
|
|
|
|
(true, Some(offset)) => {
|
|
|
|
|
// If we found a newline, discard up to that offset and
|
|
|
|
|
// then stop discarding. On the next iteration, we'll try
|
|
|
|
|
// to read a line normally.
|
|
|
|
|
buf.advance(offset + self.next_index + 1);
|
|
|
|
|
self.is_discarding = false;
|
|
|
|
|
self.next_index = 0;
|
|
|
|
|
}
|
|
|
|
|
(true, None) => {
|
|
|
|
|
// Otherwise, we didn't find a newline, so we'll discard
|
|
|
|
|
// everything we read. On the next iteration, we'll continue
|
|
|
|
|
// discarding up to max_len bytes unless we find a newline.
|
|
|
|
|
buf.advance(read_to);
|
|
|
|
|
self.next_index = 0;
|
|
|
|
|
if buf.is_empty() {
|
2021-02-26 00:21:59 +02:00
|
|
|
return Ok(None);
|
2019-08-26 16:38:52 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
(false, Some(offset)) => {
|
2018-09-20 17:08:00 -07:00
|
|
|
// Found a line!
|
|
|
|
|
let newline_index = offset + self.next_index;
|
|
|
|
|
self.next_index = 0;
|
|
|
|
|
let line = buf.split_to(newline_index + 1);
|
|
|
|
|
let line = &line[..line.len() - 1];
|
|
|
|
|
let line = without_carriage_return(line);
|
|
|
|
|
let line = utf8(line)?;
|
2019-08-26 16:38:52 -04:00
|
|
|
return Ok(Some(line.to_string()));
|
|
|
|
|
}
|
|
|
|
|
(false, None) if buf.len() > self.max_length => {
|
2018-09-20 17:08:00 -07:00
|
|
|
// Reached the maximum length without finding a
|
|
|
|
|
// newline, return an error and start discarding on the
|
|
|
|
|
// next call.
|
|
|
|
|
self.is_discarding = true;
|
2019-08-26 16:38:52 -04:00
|
|
|
return Err(LinesCodecError::MaxLineLengthExceeded);
|
|
|
|
|
}
|
|
|
|
|
(false, None) => {
|
2018-09-20 17:08:00 -07:00
|
|
|
// We didn't find a line or reach the length limit, so the next
|
|
|
|
|
// call will resume searching at the current offset.
|
|
|
|
|
self.next_index = read_to;
|
2019-08-26 16:38:52 -04:00
|
|
|
return Ok(None);
|
|
|
|
|
}
|
2018-09-20 17:08:00 -07:00
|
|
|
}
|
2018-06-04 22:36:06 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-27 18:10:29 +01:00
|
|
|
fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<String>, LinesCodecError> {
|
2018-06-04 22:36:06 -05:00
|
|
|
Ok(match self.decode(buf)? {
|
|
|
|
|
Some(frame) => Some(frame),
|
|
|
|
|
None => {
|
|
|
|
|
// No terminating newline - return remaining data, if any
|
|
|
|
|
if buf.is_empty() || buf == &b"\r"[..] {
|
|
|
|
|
None
|
|
|
|
|
} else {
|
2019-11-20 14:27:49 -08:00
|
|
|
let line = buf.split_to(buf.len());
|
2018-06-04 22:36:06 -05:00
|
|
|
let line = without_carriage_return(&line);
|
|
|
|
|
let line = utf8(line)?;
|
|
|
|
|
self.next_index = 0;
|
|
|
|
|
Some(line.to_string())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-04 15:54:41 -05:00
|
|
|
impl<T> Encoder<T> for LinesCodec
|
|
|
|
|
where
|
|
|
|
|
T: AsRef<str>,
|
|
|
|
|
{
|
2019-06-27 18:10:29 +01:00
|
|
|
type Error = LinesCodecError;
|
2018-06-04 22:36:06 -05:00
|
|
|
|
2020-03-04 15:54:41 -05:00
|
|
|
fn encode(&mut self, line: T, buf: &mut BytesMut) -> Result<(), LinesCodecError> {
|
|
|
|
|
let line = line.as_ref();
|
2018-06-04 22:36:06 -05:00
|
|
|
buf.reserve(line.len() + 1);
|
2019-11-20 14:27:49 -08:00
|
|
|
buf.put(line.as_bytes());
|
2018-06-04 22:36:06 -05:00
|
|
|
buf.put_u8(b'\n');
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-06-27 18:10:29 +01:00
|
|
|
|
2019-07-26 03:47:14 +09:00
|
|
|
impl Default for LinesCodec {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self::new()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-01 02:06:56 +09:00
|
|
|
/// An error occurred while encoding or decoding a line.
|
2019-06-27 18:10:29 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum LinesCodecError {
|
2019-07-24 15:26:41 -04:00
|
|
|
/// The maximum line length was exceeded.
|
2019-06-27 18:10:29 +01:00
|
|
|
MaxLineLengthExceeded,
|
2021-07-01 02:06:56 +09:00
|
|
|
/// An IO error occurred.
|
2019-06-27 18:10:29 +01:00
|
|
|
Io(io::Error),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for LinesCodecError {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
match self {
|
|
|
|
|
LinesCodecError::MaxLineLengthExceeded => write!(f, "max line length exceeded"),
|
|
|
|
|
LinesCodecError::Io(e) => write!(f, "{}", e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl From<io::Error> for LinesCodecError {
|
|
|
|
|
fn from(e: io::Error) -> LinesCodecError {
|
|
|
|
|
LinesCodecError::Io(e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::error::Error for LinesCodecError {}
|