1use crate::NetError;
4
5pub fn decode_chunked(mut input: &[u8], cap: usize) -> Result<Vec<u8>, NetError> {
20 let mut decoded = Vec::new();
21 loop {
22 let line_end = find_crlf(input).ok_or(NetError::TruncatedChunk)?;
23 let size = parse_chunk_size(&input[..line_end])?;
24 input = &input[line_end + 2..];
25
26 if size == 0 {
27 validate_trailers(input)?;
28 return Ok(decoded);
29 }
30
31 let next_len = decoded
32 .len()
33 .checked_add(size)
34 .ok_or(NetError::OversizeBody(cap))?;
35 if next_len > cap {
36 return Err(NetError::OversizeBody(cap));
37 }
38
39 let needed = size.checked_add(2).ok_or(NetError::InvalidChunkSize(
40 "size overflows frame length".to_owned(),
41 ))?;
42 if input.len() < needed {
43 return Err(NetError::TruncatedChunk);
44 }
45 if &input[size..needed] != b"\r\n" {
46 return Err(NetError::InvalidChunkDelimiter);
47 }
48
49 decoded.extend_from_slice(&input[..size]);
50 input = &input[needed..];
51 }
52}
53
54fn find_crlf(input: &[u8]) -> Option<usize> {
55 input.windows(2).position(|window| window == b"\r\n")
56}
57
58fn parse_chunk_size(line: &[u8]) -> Result<usize, NetError> {
59 let text = std::str::from_utf8(line)
60 .map_err(|err| NetError::InvalidChunkSize(format!("size line is not UTF-8: {err}")))?;
61 let size_text = text.split(';').next().unwrap_or_default().trim();
62 if size_text.is_empty() {
63 return Err(NetError::InvalidChunkSize("size line is empty".to_owned()));
64 }
65 usize::from_str_radix(size_text, 16)
66 .map_err(|err| NetError::InvalidChunkSize(format!("invalid size: {err}")))
67}
68
69fn validate_trailers(mut input: &[u8]) -> Result<(), NetError> {
70 loop {
71 let line_end = find_crlf(input).ok_or(NetError::TruncatedChunk)?;
72 input = &input[line_end + 2..];
73 if line_end == 0 {
74 if input.is_empty() {
75 return Ok(());
76 }
77 return Err(NetError::InvalidChunkDelimiter);
78 }
79 }
80}