Skip to main content

actix_codec/
lines.rs

1use std::io;
2
3use bytes::{Buf, BufMut, Bytes, BytesMut};
4use memchr::memchr;
5
6use super::{Decoder, Encoder};
7
8/// Lines codec. Reads/writes line delimited strings.
9///
10/// Will split input up by LF or CRLF delimiters. Carriage return characters at the end of lines are
11/// not preserved.
12///
13/// # Security
14///
15/// When used with untrusted input, it is recommended to set a maximum line length with
16/// [`LinesCodec::new_with_max_length`]. Without a length limit, the internal read buffer can grow
17/// without bound if a peer sends an unbounded amount of data without a `\n`, potentially leading
18/// to memory exhaustion (DoS).
19#[derive(Debug, Copy, Clone)]
20#[non_exhaustive]
21pub struct LinesCodec {
22    max_length: usize,
23    // Next byte index to examine for `\n` after an incomplete decode.
24    next_index: usize,
25}
26
27impl LinesCodec {
28    /// Creates a new `LinesCodec` with no maximum line length.
29    ///
30    /// Consider using [`LinesCodec::new_with_max_length`] when working with untrusted input.
31    pub const fn new() -> Self {
32        Self {
33            max_length: usize::MAX,
34            next_index: 0,
35        }
36    }
37
38    /// Creates a new `LinesCodec` with a maximum line length, in bytes.
39    ///
40    /// The limit applies to the bytes before the line delimiter (`\n`). If present, a trailing
41    /// carriage return (`\r`) in `\r\n` sequences is not counted towards the limit.
42    ///
43    /// Using a length limit is recommended when working with untrusted input to avoid unbounded
44    /// buffering.
45    pub const fn new_with_max_length(max_length: usize) -> Self {
46        Self {
47            max_length,
48            next_index: 0,
49        }
50    }
51
52    /// Returns the maximum permitted line length, in bytes.
53    pub const fn max_length(&self) -> usize {
54        self.max_length
55    }
56}
57
58impl Default for LinesCodec {
59    fn default() -> Self {
60        Self::new()
61    }
62}
63
64impl<T: AsRef<str>> Encoder<T> for LinesCodec {
65    type Error = io::Error;
66
67    #[inline]
68    fn encode(&mut self, item: T, dst: &mut BytesMut) -> Result<(), Self::Error> {
69        let item = item.as_ref();
70        dst.reserve(item.len() + 1);
71        dst.put_slice(item.as_bytes());
72        dst.put_u8(b'\n');
73        Ok(())
74    }
75}
76
77impl Decoder for LinesCodec {
78    type Item = String;
79    type Error = io::Error;
80
81    fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
82        if src.is_empty() {
83            self.next_index = 0;
84            return Ok(None);
85        }
86
87        // Framed reads append to the same buffer after incomplete decodes. We do not currently
88        // expect callers to replace it, but fall back to a fresh scan if they do.
89        let start = if self.next_index < src.len() {
90            self.next_index
91        } else {
92            0
93        };
94
95        let len = match memchr(b'\n', &src[start..]) {
96            Some(n) => start + n,
97            None => {
98                let max = self.max_length;
99                if max != usize::MAX {
100                    // No delimiter yet; if current buffered data already exceeds the maximum line
101                    // length, abort to avoid unbounded memory growth.
102                    let max_cr = max.saturating_add(1);
103
104                    if src.len() > max && !(src.len() == max_cr && src.last() == Some(&b'\r')) {
105                        return Err(io::Error::new(
106                            io::ErrorKind::InvalidData,
107                            "max line length exceeded",
108                        ));
109                    }
110                }
111
112                self.next_index = src.len();
113                return Ok(None);
114            }
115        };
116
117        // Reject overly long lines before splitting/advancing buffers.
118        let max = self.max_length;
119        if max != usize::MAX {
120            let max_cr = max.saturating_add(1);
121
122            if len > max && !(len == max_cr && src.get(len - 1) == Some(&b'\r')) {
123                return Err(io::Error::new(
124                    io::ErrorKind::InvalidData,
125                    "max line length exceeded",
126                ));
127            }
128        }
129
130        self.next_index = 0;
131
132        // split up to new line char
133        let mut buf = src.split_to(len);
134        debug_assert_eq!(len, buf.len());
135
136        // remove new line char from source
137        src.advance(1);
138
139        match buf.last() {
140            // remove carriage returns at the end of buf
141            Some(b'\r') => buf.truncate(len - 1),
142
143            // line is empty
144            None => return Ok(Some(String::new())),
145
146            _ => {}
147        }
148
149        try_into_utf8(buf.freeze())
150    }
151
152    fn decode_eof(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
153        match self.decode(src)? {
154            Some(frame) => Ok(Some(frame)),
155            None if src.is_empty() => Ok(None),
156            None => {
157                self.next_index = 0;
158
159                let buf = match src.last() {
160                    // if last line ends in a CR then take everything up to it
161                    Some(b'\r') => src.split_to(src.len() - 1),
162
163                    // take all bytes from source
164                    _ => src.split(),
165                };
166
167                if buf.is_empty() {
168                    return Ok(None);
169                }
170
171                try_into_utf8(buf.freeze())
172            }
173        }
174    }
175}
176
177// Attempts to convert bytes into a `String`.
178fn try_into_utf8(buf: Bytes) -> io::Result<Option<String>> {
179    String::from_utf8(buf.to_vec())
180        .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
181        .map(Some)
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187
188    #[test]
189    fn lines_decoder() {
190        let mut codec = LinesCodec::default();
191        let mut buf = BytesMut::from("\nline 1\nline 2\r\nline 3\n\r\n\r");
192
193        assert_eq!("", codec.decode(&mut buf).unwrap().unwrap());
194        assert_eq!("line 1", codec.decode(&mut buf).unwrap().unwrap());
195        assert_eq!("line 2", codec.decode(&mut buf).unwrap().unwrap());
196        assert_eq!("line 3", codec.decode(&mut buf).unwrap().unwrap());
197        assert_eq!("", codec.decode(&mut buf).unwrap().unwrap());
198        assert!(codec.decode(&mut buf).unwrap().is_none());
199        assert!(codec.decode_eof(&mut buf).unwrap().is_none());
200
201        buf.put_slice(b"k");
202        assert!(codec.decode(&mut buf).unwrap().is_none());
203        assert_eq!("\rk", codec.decode_eof(&mut buf).unwrap().unwrap());
204
205        assert!(codec.decode(&mut buf).unwrap().is_none());
206        assert!(codec.decode_eof(&mut buf).unwrap().is_none());
207    }
208
209    #[test]
210    fn lines_encoder() {
211        let mut codec = LinesCodec::default();
212
213        let mut buf = BytesMut::new();
214
215        codec.encode("", &mut buf).unwrap();
216        assert_eq!(&buf[..], b"\n");
217
218        codec.encode("test", &mut buf).unwrap();
219        assert_eq!(&buf[..], b"\ntest\n");
220
221        codec.encode("a\nb", &mut buf).unwrap();
222        assert_eq!(&buf[..], b"\ntest\na\nb\n");
223    }
224
225    #[test]
226    fn lines_encoder_no_overflow() {
227        let mut codec = LinesCodec::default();
228
229        let mut buf = BytesMut::new();
230        codec.encode("1234567", &mut buf).unwrap();
231        assert_eq!(&buf[..], b"1234567\n");
232
233        let mut buf = BytesMut::new();
234        codec.encode("12345678", &mut buf).unwrap();
235        assert_eq!(&buf[..], b"12345678\n");
236
237        let mut buf = BytesMut::new();
238        codec.encode("123456789111213", &mut buf).unwrap();
239        assert_eq!(&buf[..], b"123456789111213\n");
240
241        let mut buf = BytesMut::new();
242        codec.encode("1234567891112131", &mut buf).unwrap();
243        assert_eq!(&buf[..], b"1234567891112131\n");
244    }
245
246    #[test]
247    fn lines_decoder_errors_on_overlong_line_without_delimiter() {
248        let mut codec = LinesCodec::new_with_max_length(4);
249        let mut buf = BytesMut::from(&b"aaaaa"[..]);
250
251        let err = codec.decode(&mut buf).unwrap_err();
252        assert_eq!(err.kind(), io::ErrorKind::InvalidData);
253    }
254
255    #[test]
256    fn lines_decoder_resumes_from_previous_search() {
257        let mut codec = LinesCodec::default();
258        let mut buf = BytesMut::from(&b"partial"[..]);
259
260        assert!(codec.decode(&mut buf).unwrap().is_none());
261
262        buf.put_slice(b" line\n");
263        assert_eq!("partial line", codec.decode(&mut buf).unwrap().unwrap());
264        assert!(codec.decode(&mut buf).unwrap().is_none());
265    }
266
267    #[test]
268    fn lines_decoder_resumes_across_multiple_partial_chunks() {
269        let mut codec = LinesCodec::default();
270        let mut buf = BytesMut::new();
271
272        buf.put_slice(b"partial");
273        assert!(codec.decode(&mut buf).unwrap().is_none());
274
275        buf.put_slice(b" line");
276        assert!(codec.decode(&mut buf).unwrap().is_none());
277
278        buf.put_slice(b" across chunks");
279        assert!(codec.decode(&mut buf).unwrap().is_none());
280
281        buf.put_slice(b"\n");
282        assert_eq!(
283            "partial line across chunks",
284            codec.decode(&mut buf).unwrap().unwrap()
285        );
286    }
287
288    #[test]
289    fn lines_decoder_resumes_with_max_length() {
290        let mut codec = LinesCodec::new_with_max_length(18);
291        let mut buf = BytesMut::new();
292
293        buf.put_slice(b"partial");
294        assert!(codec.decode(&mut buf).unwrap().is_none());
295
296        buf.put_slice(b" line");
297        assert!(codec.decode(&mut buf).unwrap().is_none());
298
299        buf.put_slice(b" ok\n");
300        assert_eq!("partial line ok", codec.decode(&mut buf).unwrap().unwrap());
301    }
302
303    #[test]
304    fn lines_decoder_errors_on_overlong_partial_line() {
305        let mut codec = LinesCodec::new_with_max_length(4);
306        let mut buf = BytesMut::from(&b"aa"[..]);
307
308        assert!(codec.decode(&mut buf).unwrap().is_none());
309
310        buf.put_slice(b"aaa");
311        let err = codec.decode(&mut buf).unwrap_err();
312        assert_eq!(err.kind(), io::ErrorKind::InvalidData);
313    }
314
315    #[test]
316    fn lines_decoder_resets_search_after_decode_eof() {
317        let mut codec = LinesCodec::default();
318        let mut buf = BytesMut::from(&b"partial"[..]);
319
320        assert!(codec.decode(&mut buf).unwrap().is_none());
321        assert_eq!("partial", codec.decode_eof(&mut buf).unwrap().unwrap());
322
323        buf.put_slice(b"next\n");
324        assert_eq!("next", codec.decode(&mut buf).unwrap().unwrap());
325    }
326}