1use std::{io, task::Poll};
2
3use bytes::{Buf as _, Bytes, BytesMut};
4use tracing::{debug, trace};
5
6macro_rules! byte (
7 ($rdr:ident) => ({
8 if $rdr.len() > 0 {
9 let b = $rdr[0];
10 $rdr.advance(1);
11 b
12 } else {
13 return Poll::Pending
14 }
15 })
16);
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub(super) enum ChunkedState {
20 Size,
21 SizeLws,
22 Extension,
23 SizeLf,
24 Body,
25 BodyCr,
26 BodyLf,
27 EndCr,
28 EndLf,
29 End,
30}
31
32impl ChunkedState {
33 pub(super) fn step(
34 &self,
35 body: &mut BytesMut,
36 size: &mut u64,
37 buf: &mut Option<Bytes>,
38 ) -> Poll<Result<ChunkedState, io::Error>> {
39 use self::ChunkedState::*;
40 match *self {
41 Size => ChunkedState::read_size(body, size),
42 SizeLws => ChunkedState::read_size_lws(body),
43 Extension => ChunkedState::read_extension(body),
44 SizeLf => ChunkedState::read_size_lf(body, *size),
45 Body => ChunkedState::read_body(body, size, buf),
46 BodyCr => ChunkedState::read_body_cr(body),
47 BodyLf => ChunkedState::read_body_lf(body),
48 EndCr => ChunkedState::read_end_cr(body),
49 EndLf => ChunkedState::read_end_lf(body),
50 End => Poll::Ready(Ok(ChunkedState::End)),
51 }
52 }
53
54 fn read_size(rdr: &mut BytesMut, size: &mut u64) -> Poll<Result<ChunkedState, io::Error>> {
55 let radix = 16;
56
57 let rem = match byte!(rdr) {
58 b @ b'0'..=b'9' => b - b'0',
59 b @ b'a'..=b'f' => b + 10 - b'a',
60 b @ b'A'..=b'F' => b + 10 - b'A',
61 b'\t' | b' ' => return Poll::Ready(Ok(ChunkedState::SizeLws)),
62 b';' => return Poll::Ready(Ok(ChunkedState::Extension)),
63 b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)),
64 _ => {
65 return Poll::Ready(Err(io::Error::new(
66 io::ErrorKind::InvalidInput,
67 "Invalid chunk size line: Invalid Size",
68 )));
69 }
70 };
71
72 match size.checked_mul(radix) {
73 Some(n) => {
74 *size = n;
75 *size += rem as u64;
76
77 Poll::Ready(Ok(ChunkedState::Size))
78 }
79 None => {
80 debug!("chunk size would overflow u64");
81 Poll::Ready(Err(io::Error::new(
82 io::ErrorKind::InvalidInput,
83 "Invalid chunk size line: Size is too big",
84 )))
85 }
86 }
87 }
88
89 fn read_size_lws(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
90 match byte!(rdr) {
91 b'\t' | b' ' => Poll::Ready(Ok(ChunkedState::SizeLws)),
93 b';' => Poll::Ready(Ok(ChunkedState::Extension)),
94 b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
95 _ => Poll::Ready(Err(io::Error::new(
96 io::ErrorKind::InvalidInput,
97 "Invalid chunk size linear white space",
98 ))),
99 }
100 }
101 fn read_extension(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
102 match byte!(rdr) {
103 b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
104 0x00..=0x08 | 0x0a..=0x1f | 0x7f => Poll::Ready(Err(io::Error::new(
106 io::ErrorKind::InvalidInput,
107 "Invalid character in chunk extension",
108 ))),
109 _ => Poll::Ready(Ok(ChunkedState::Extension)), }
111 }
112 fn read_size_lf(rdr: &mut BytesMut, size: u64) -> Poll<Result<ChunkedState, io::Error>> {
113 match byte!(rdr) {
114 b'\n' if size > 0 => Poll::Ready(Ok(ChunkedState::Body)),
115 b'\n' if size == 0 => Poll::Ready(Ok(ChunkedState::EndCr)),
116 _ => Poll::Ready(Err(io::Error::new(
117 io::ErrorKind::InvalidInput,
118 "Invalid chunk size LF",
119 ))),
120 }
121 }
122
123 fn read_body(
124 rdr: &mut BytesMut,
125 rem: &mut u64,
126 buf: &mut Option<Bytes>,
127 ) -> Poll<Result<ChunkedState, io::Error>> {
128 trace!("Chunked read, remaining={:?}", rem);
129
130 let len = rdr.len() as u64;
131 if len == 0 {
132 Poll::Ready(Ok(ChunkedState::Body))
133 } else {
134 let slice;
135 if *rem > len {
136 slice = rdr.split().freeze();
137 *rem -= len;
138 } else {
139 slice = rdr.split_to(*rem as usize).freeze();
140 *rem = 0;
141 }
142 *buf = Some(slice);
143 if *rem > 0 {
144 Poll::Ready(Ok(ChunkedState::Body))
145 } else {
146 Poll::Ready(Ok(ChunkedState::BodyCr))
147 }
148 }
149 }
150
151 fn read_body_cr(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
152 match byte!(rdr) {
153 b'\r' => Poll::Ready(Ok(ChunkedState::BodyLf)),
154 _ => Poll::Ready(Err(io::Error::new(
155 io::ErrorKind::InvalidInput,
156 "Invalid chunk body CR",
157 ))),
158 }
159 }
160 fn read_body_lf(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
161 match byte!(rdr) {
162 b'\n' => Poll::Ready(Ok(ChunkedState::Size)),
163 _ => Poll::Ready(Err(io::Error::new(
164 io::ErrorKind::InvalidInput,
165 "Invalid chunk body LF",
166 ))),
167 }
168 }
169 fn read_end_cr(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
170 match byte!(rdr) {
171 b'\r' => Poll::Ready(Ok(ChunkedState::EndLf)),
172 _ => Poll::Ready(Err(io::Error::new(
173 io::ErrorKind::InvalidInput,
174 "Invalid chunk end CR",
175 ))),
176 }
177 }
178 fn read_end_lf(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
179 match byte!(rdr) {
180 b'\n' => Poll::Ready(Ok(ChunkedState::End)),
181 _ => Poll::Ready(Err(io::Error::new(
182 io::ErrorKind::InvalidInput,
183 "Invalid chunk end LF",
184 ))),
185 }
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use actix_codec::Decoder as _;
192 use bytes::{Bytes, BytesMut};
193 use http::Method;
194
195 use crate::{
196 error::ParseError,
197 h1::decoder::{MessageDecoder, PayloadItem},
198 HttpMessage as _, Request,
199 };
200
201 macro_rules! parse_ready {
202 ($e:expr) => {{
203 match MessageDecoder::<Request>::default().decode($e) {
204 Ok(Some((msg, _))) => msg,
205 Ok(_) => unreachable!("Eof during parsing http request"),
206 Err(err) => unreachable!("Error during parsing http request: {:?}", err),
207 }
208 }};
209 }
210
211 macro_rules! expect_parse_err {
212 ($e:expr) => {{
213 match MessageDecoder::<Request>::default().decode($e) {
214 Err(err) => match err {
215 ParseError::Io(_) => unreachable!("Parse error expected"),
216 _ => {}
217 },
218 _ => unreachable!("Error expected"),
219 }
220 }};
221 }
222
223 #[test]
224 fn test_parse_chunked_payload_chunk_extension() {
225 let mut buf = BytesMut::from(
226 "GET /test HTTP/1.1\r\n\
227 transfer-encoding: chunked\r\n\
228 \r\n",
229 );
230
231 let mut reader = MessageDecoder::<Request>::default();
232 let (msg, pl) = reader.decode(&mut buf).unwrap().unwrap();
233 let mut pl = pl.unwrap();
234 assert!(msg.chunked().unwrap());
235
236 buf.extend(b"4;test\r\ndata\r\n4\r\nline\r\n0\r\n\r\n"); let chunk = pl.decode(&mut buf).unwrap().unwrap().chunk();
238 assert_eq!(chunk, Bytes::from_static(b"data"));
239 let chunk = pl.decode(&mut buf).unwrap().unwrap().chunk();
240 assert_eq!(chunk, Bytes::from_static(b"line"));
241 let msg = pl.decode(&mut buf).unwrap().unwrap();
242 assert!(msg.eof());
243 }
244
245 #[test]
246 fn test_request_chunked() {
247 let mut buf = BytesMut::from(
248 "GET /test HTTP/1.1\r\n\
249 transfer-encoding: chunked\r\n\r\n",
250 );
251 let req = parse_ready!(&mut buf);
252
253 if let Ok(val) = req.chunked() {
254 assert!(val);
255 } else {
256 unreachable!("Error");
257 }
258
259 let mut buf = BytesMut::from(
261 "GET /test HTTP/1.1\r\n\
262 transfer-encoding: chnked\r\n\r\n",
263 );
264 expect_parse_err!(&mut buf);
265 }
266
267 #[test]
268 fn test_http_request_chunked_payload() {
269 let mut buf = BytesMut::from(
270 "GET /test HTTP/1.1\r\n\
271 transfer-encoding: chunked\r\n\r\n",
272 );
273 let mut reader = MessageDecoder::<Request>::default();
274 let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
275 let mut pl = pl.unwrap();
276 assert!(req.chunked().unwrap());
277
278 buf.extend(b"4\r\ndata\r\n4\r\nline\r\n0\r\n\r\n");
279 assert_eq!(
280 pl.decode(&mut buf).unwrap().unwrap().chunk().as_ref(),
281 b"data"
282 );
283 assert_eq!(
284 pl.decode(&mut buf).unwrap().unwrap().chunk().as_ref(),
285 b"line"
286 );
287 assert!(pl.decode(&mut buf).unwrap().unwrap().eof());
288 }
289
290 #[test]
291 fn test_http_request_chunked_payload_and_next_message() {
292 let mut buf = BytesMut::from(
293 "GET /test HTTP/1.1\r\n\
294 transfer-encoding: chunked\r\n\r\n",
295 );
296 let mut reader = MessageDecoder::<Request>::default();
297 let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
298 let mut pl = pl.unwrap();
299 assert!(req.chunked().unwrap());
300
301 buf.extend(
302 b"4\r\ndata\r\n4\r\nline\r\n0\r\n\r\n\
303 POST /test2 HTTP/1.1\r\n\
304 transfer-encoding: chunked\r\n\r\n"
305 .iter(),
306 );
307 let msg = pl.decode(&mut buf).unwrap().unwrap();
308 assert_eq!(msg.chunk().as_ref(), b"data");
309 let msg = pl.decode(&mut buf).unwrap().unwrap();
310 assert_eq!(msg.chunk().as_ref(), b"line");
311 let msg = pl.decode(&mut buf).unwrap().unwrap();
312 assert!(msg.eof());
313
314 let (req, _) = reader.decode(&mut buf).unwrap().unwrap();
315 assert!(req.chunked().unwrap());
316 assert_eq!(*req.method(), Method::POST);
317 assert!(req.chunked().unwrap());
318 }
319
320 #[test]
321 fn test_http_request_chunked_payload_chunks() {
322 let mut buf = BytesMut::from(
323 "GET /test HTTP/1.1\r\n\
324 transfer-encoding: chunked\r\n\r\n",
325 );
326
327 let mut reader = MessageDecoder::<Request>::default();
328 let (req, pl) = reader.decode(&mut buf).unwrap().unwrap();
329 let mut pl = pl.unwrap();
330 assert!(req.chunked().unwrap());
331
332 buf.extend(b"4\r\n1111\r\n");
333 let msg = pl.decode(&mut buf).unwrap().unwrap();
334 assert_eq!(msg.chunk().as_ref(), b"1111");
335
336 buf.extend(b"4\r\ndata\r");
337 let msg = pl.decode(&mut buf).unwrap().unwrap();
338 assert_eq!(msg.chunk().as_ref(), b"data");
339
340 buf.extend(b"\n4");
341 assert!(pl.decode(&mut buf).unwrap().is_none());
342
343 buf.extend(b"\r");
344 assert!(pl.decode(&mut buf).unwrap().is_none());
345 buf.extend(b"\n");
346 assert!(pl.decode(&mut buf).unwrap().is_none());
347
348 buf.extend(b"li");
349 let msg = pl.decode(&mut buf).unwrap().unwrap();
350 assert_eq!(msg.chunk().as_ref(), b"li");
351
352 buf.extend(b"ne\r\n0\r\n");
357 let msg = pl.decode(&mut buf).unwrap().unwrap();
358 assert_eq!(msg.chunk().as_ref(), b"ne");
359 assert!(pl.decode(&mut buf).unwrap().is_none());
360
361 buf.extend(b"\r\n");
362 assert!(pl.decode(&mut buf).unwrap().unwrap().eof());
363 }
364
365 #[test]
366 fn chunk_extension_quoted() {
367 let mut buf = BytesMut::from(
368 "GET /test HTTP/1.1\r\n\
369 Host: localhost:8080\r\n\
370 Transfer-Encoding: chunked\r\n\
371 \r\n\
372 2;hello=b;one=\"1 2 3\"\r\n\
373 xx",
374 );
375
376 let mut reader = MessageDecoder::<Request>::default();
377 let (_msg, pl) = reader.decode(&mut buf).unwrap().unwrap();
378 let mut pl = pl.unwrap();
379
380 let chunk = pl.decode(&mut buf).unwrap().unwrap();
381 assert_eq!(chunk, PayloadItem::Chunk(Bytes::from_static(b"xx")));
382 }
383
384 #[test]
385 fn hrs_chunk_extension_invalid() {
386 let mut buf = BytesMut::from(
387 "GET / HTTP/1.1\r\n\
388 Host: localhost:8080\r\n\
389 Transfer-Encoding: chunked\r\n\
390 \r\n\
391 2;x\nx\r\n\
392 4c\r\n\
393 0\r\n",
394 );
395
396 let mut reader = MessageDecoder::<Request>::default();
397 let (_msg, pl) = reader.decode(&mut buf).unwrap().unwrap();
398 let mut pl = pl.unwrap();
399
400 let err = pl.decode(&mut buf).unwrap_err();
401 assert!(err
402 .to_string()
403 .contains("Invalid character in chunk extension"));
404 }
405
406 #[test]
407 fn hrs_chunk_size_overflow() {
408 let mut buf = BytesMut::from(
409 "GET / HTTP/1.1\r\n\
410 Host: example.com\r\n\
411 Transfer-Encoding: chunked\r\n\
412 \r\n\
413 f0000000000000003\r\n\
414 abc\r\n\
415 0\r\n",
416 );
417
418 let mut reader = MessageDecoder::<Request>::default();
419 let (_msg, pl) = reader.decode(&mut buf).unwrap().unwrap();
420 let mut pl = pl.unwrap();
421
422 let err = pl.decode(&mut buf).unwrap_err();
423 assert!(err
424 .to_string()
425 .contains("Invalid chunk size line: Size is too big"));
426 }
427}