1use crate::parse::check_trailers;
5use crate::{Error, Framing, Limits};
6use simoom_sys::scan::{find_byte, TCHAR};
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10pub enum BodyEvent<'a> {
11 Data(&'a [u8]),
13 End(&'a [u8]),
17 NeedMore,
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22enum St {
23 Length,
24 Close,
25 Done,
26 Size0,
27 Size,
28 ExtPreSemi,
29 ExtNameStart,
30 ExtName,
31 ExtNameEnd,
32 ExtValStart,
33 ExtTok,
34 ExtQuoted,
35 ExtQuotedEsc,
36 ExtAfterQuote,
37 SizeLf,
38 Data,
39 DataCr,
40 DataLf,
41 Trailers,
42}
43
44#[derive(Clone, Debug)]
51pub struct BodyDecoder {
52 state: St,
53 remaining: u64,
54 received: u64,
55 limit: u64,
56 line: u32,
57 max_line: u32,
58 scanned: u32,
59 max_trailers: u32,
60 max_headers: u32,
61 max_header_line: u32,
62}
63
64#[inline]
65fn hexval(b: u8) -> Option<u64> {
66 match b {
67 b'0'..=b'9' => Some(u64::from(b - b'0')),
68 b'a'..=b'f' => Some(u64::from(b - b'a' + 10)),
69 b'A'..=b'F' => Some(u64::from(b - b'A' + 10)),
70 _ => None,
71 }
72}
73
74#[inline]
75fn ws(b: u8) -> bool {
76 b == b' ' || b == b'\t'
77}
78
79fn clamp(n: usize) -> u32 {
80 n.min(u32::MAX as usize) as u32
81}
82
83impl BodyDecoder {
84 pub fn new(framing: Framing, lim: &Limits) -> Self {
87 let (state, remaining) = match framing {
88 Framing::None => (St::Done, 0),
89 Framing::Length(n) => (St::Length, n),
90 Framing::Chunked => (St::Size0, 0),
91 Framing::Close => (St::Close, 0),
92 };
93 BodyDecoder {
94 state,
95 remaining,
96 received: 0,
97 limit: lim.max_body,
98 line: 0,
99 max_line: clamp(lim.max_chunk_line),
100 scanned: 0,
101 max_trailers: clamp(lim.max_trailers),
102 max_headers: clamp(lim.max_headers),
103 max_header_line: clamp(lim.max_header_line),
104 }
105 }
106
107 pub fn is_done(&self) -> bool {
109 self.state == St::Done
110 }
111
112 pub fn eof(&mut self) -> Result<(), Error> {
114 match self.state {
115 St::Close | St::Done => {
116 self.state = St::Done;
117 Ok(())
118 }
119 _ => Err(Error::UnexpectedEof),
120 }
121 }
122
123 pub fn decode<'a>(&mut self, input: &'a [u8]) -> Result<(usize, BodyEvent<'a>), Error> {
125 match self.state {
126 St::Done => return Ok((0, BodyEvent::End(&[]))),
127 St::Length => {
128 if self.remaining > self.limit {
129 return Err(Error::BodyTooLarge);
130 }
131 if input.is_empty() {
132 return Ok((0, BodyEvent::NeedMore));
133 }
134 let n = self.remaining.min(input.len() as u64) as usize;
135 self.remaining -= n as u64;
136 if self.remaining == 0 {
137 self.state = St::Done;
138 }
139 return Ok((n, BodyEvent::Data(&input[..n])));
140 }
141 St::Close => {
142 if input.is_empty() {
143 return Ok((0, BodyEvent::NeedMore));
144 }
145 self.received += input.len() as u64;
146 if self.received > self.limit {
147 return Err(Error::BodyTooLarge);
148 }
149 return Ok((input.len(), BodyEvent::Data(input)));
150 }
151 _ => {}
152 }
153 let mut i = 0;
154 loop {
155 match self.state {
156 St::Data => {
157 if i == input.len() {
158 return Ok((i, BodyEvent::NeedMore));
159 }
160 let n = self.remaining.min((input.len() - i) as u64) as usize;
161 self.remaining -= n as u64;
162 if self.remaining == 0 {
163 self.state = St::DataCr;
164 }
165 return Ok((i + n, BodyEvent::Data(&input[i..i + n])));
166 }
167 St::Trailers => return self.trailers(input, i),
168 St::Done => return Ok((i, BodyEvent::End(&[]))),
169 _ => {}
170 }
171 let Some(&b) = input.get(i) else { return Ok((i, BodyEvent::NeedMore)) };
172 i += 1;
173 self.step(b)?;
174 }
175 }
176
177 fn step(&mut self, b: u8) -> Result<(), Error> {
178 use St::*;
179 if !matches!(self.state, DataCr | DataLf) {
180 self.line += 1;
181 if self.line > self.max_line {
182 return Err(Error::ChunkLineTooLong);
183 }
184 }
185 let ext = Error::InvalidChunkExtension;
186 let tchar = TCHAR.contains(b);
187 self.state = match self.state {
188 Size0 => {
189 self.remaining = hexval(b).ok_or(Error::InvalidChunkSize)?;
190 Size
191 }
192 Size => match (hexval(b), b) {
193 (Some(v), _) => {
194 if self.remaining >> 60 != 0 {
195 return Err(Error::ChunkSizeOverflow);
196 }
197 self.remaining = self.remaining << 4 | v;
198 Size
199 }
200 (_, b';') => ExtNameStart,
201 (_, b'\r') => SizeLf,
202 (_, b) if ws(b) => ExtPreSemi,
203 _ => return Err(Error::InvalidChunkSize),
204 },
205 ExtPreSemi => match b {
206 b';' => ExtNameStart,
207 b if ws(b) => ExtPreSemi,
208 _ => return Err(ext),
209 },
210 ExtNameStart => match b {
211 b if ws(b) => ExtNameStart,
212 _ if tchar => ExtName,
213 _ => return Err(ext),
214 },
215 ExtName => match b {
216 _ if tchar => ExtName,
217 b';' => ExtNameStart,
218 b'=' => ExtValStart,
219 b'\r' => SizeLf,
220 b if ws(b) => ExtNameEnd,
221 _ => return Err(ext),
222 },
223 ExtNameEnd => match b {
224 b'=' => ExtValStart,
225 b';' => ExtNameStart,
226 b if ws(b) => ExtNameEnd,
227 _ => return Err(ext),
228 },
229 ExtValStart => match b {
230 b'"' => ExtQuoted,
231 b if ws(b) => ExtValStart,
232 _ if tchar => ExtTok,
233 _ => return Err(ext),
234 },
235 ExtTok => match b {
236 _ if tchar => ExtTok,
237 b';' => ExtNameStart,
238 b'\r' => SizeLf,
239 b if ws(b) => ExtPreSemi,
240 _ => return Err(ext),
241 },
242 ExtQuoted => match b {
243 b'"' => ExtAfterQuote,
244 b'\\' => ExtQuotedEsc,
245 b'\t' | b' ' | 0x21 | 0x23..=0x5b | 0x5d..=0x7e | 0x80..=0xff => ExtQuoted,
246 _ => return Err(ext),
247 },
248 ExtQuotedEsc => match b {
249 b'\t' | 0x20..=0x7e | 0x80..=0xff => ExtQuoted,
250 _ => return Err(ext),
251 },
252 ExtAfterQuote => match b {
253 b';' => ExtNameStart,
254 b'\r' => SizeLf,
255 b if ws(b) => ExtPreSemi,
256 _ => return Err(ext),
257 },
258 SizeLf => {
259 if b != b'\n' {
260 return Err(Error::BareCr);
261 }
262 self.line = 0;
263 if self.remaining == 0 {
264 Trailers
265 } else {
266 self.received = self.received.saturating_add(self.remaining);
267 if self.received > self.limit {
268 return Err(Error::BodyTooLarge);
269 }
270 Data
271 }
272 }
273 DataCr if b == b'\r' => DataLf,
274 DataLf if b == b'\n' => Size0,
275 DataCr | DataLf => return Err(Error::MissingChunkCrlf),
276 Length | Close | Done | Data | Trailers => unreachable!("not a chunk-line state"),
277 };
278 Ok(())
279 }
280
281 fn trailers<'a>(&mut self, input: &'a [u8], i: usize) -> Result<(usize, BodyEvent<'a>), Error> {
282 let rest = &input[i..];
283 match rest {
284 [] | [b'\r'] => return Ok((i, BodyEvent::NeedMore)),
285 [b'\r', b'\n', ..] => {
286 self.state = St::Done;
287 return Ok((i + 2, BodyEvent::End(&[])));
288 }
289 [b'\r', ..] => return Err(Error::BareCr),
290 _ => {}
291 }
292 let mut k = (self.scanned as usize).saturating_sub(3);
293 while let Some(off) = find_byte(&rest[k..], b'\n') {
294 let at = k + off;
295 if at >= 3 && &rest[at - 3..=at] == b"\r\n\r\n" {
296 let section = &rest[..=at];
297 let lim = Limits {
298 max_headers: self.max_headers as usize,
299 max_header_line: self.max_header_line as usize,
300 max_trailers: self.max_trailers as usize,
301 ..Limits::default()
302 };
303 check_trailers(section, &lim)?;
304 self.state = St::Done;
305 return Ok((i + section.len(), BodyEvent::End(section)));
306 }
307 k = at + 1;
308 }
309 if rest.len() > self.max_trailers as usize {
310 return Err(Error::TrailersTooLarge);
311 }
312 self.scanned = clamp(rest.len());
313 Ok((i, BodyEvent::NeedMore))
314 }
315}