impl ChunkedState {
fn new() -> ChunkedState {
ChunkedState::Start
}
fn step<R: MemRead>(
&self,
cx: &mut Context<'_>,
body: &mut R,
StepArgs {
chunk_size,
chunk_buf,
extensions_cnt,
trailers_buf,
trailers_cnt,
max_headers_cnt,
max_headers_bytes,
}: StepArgs<'_>,
) -> Poll<Result<ChunkedState, io::Error>> {
use self::ChunkedState::*;
match *self {
Start => ChunkedState::read_start(cx, body, chunk_size),
Size => ChunkedState::read_size(cx, body, chunk_size),
SizeLws => ChunkedState::read_size_lws(cx, body),
Extension => ChunkedState::read_extension(cx, body, extensions_cnt),
SizeLf => ChunkedState::read_size_lf(cx, body, *chunk_size),
Body => ChunkedState::read_body(cx, body, chunk_size, chunk_buf),
BodyCr => ChunkedState::read_body_cr(cx, body),
BodyLf => ChunkedState::read_body_lf(cx, body),
Trailer => ChunkedState::read_trailer(cx, body, trailers_buf, max_headers_bytes),
TrailerLf => ChunkedState::read_trailer_lf(
cx,
body,
trailers_buf,
trailers_cnt,
max_headers_cnt,
max_headers_bytes,
),
EndCr => ChunkedState::read_end_cr(cx, body, trailers_buf, max_headers_bytes),
EndLf => ChunkedState::read_end_lf(cx, body, trailers_buf, max_headers_bytes),
End => Poll::Ready(Ok(ChunkedState::End)),
}
}
fn read_start<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
size: &mut u64,
) -> Poll<Result<ChunkedState, io::Error>> {
trace!("Read chunk start");
let radix = 16;
match byte!(rdr, cx) {
b @ b'0'..=b'9' => {
*size = or_overflow!(size.checked_mul(radix));
*size = or_overflow!(size.checked_add((b - b'0') as u64));
}
b @ b'a'..=b'f' => {
*size = or_overflow!(size.checked_mul(radix));
*size = or_overflow!(size.checked_add((b + 10 - b'a') as u64));
}
b @ b'A'..=b'F' => {
*size = or_overflow!(size.checked_mul(radix));
*size = or_overflow!(size.checked_add((b + 10 - b'A') as u64));
}
_ => {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk size line: missing size digit",
)));
}
}
Poll::Ready(Ok(ChunkedState::Size))
}
fn read_size<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
size: &mut u64,
) -> Poll<Result<ChunkedState, io::Error>> {
trace!("Read chunk hex size");
let radix = 16;
match byte!(rdr, cx) {
b @ b'0'..=b'9' => {
*size = or_overflow!(size.checked_mul(radix));
*size = or_overflow!(size.checked_add((b - b'0') as u64));
}
b @ b'a'..=b'f' => {
*size = or_overflow!(size.checked_mul(radix));
*size = or_overflow!(size.checked_add((b + 10 - b'a') as u64));
}
b @ b'A'..=b'F' => {
*size = or_overflow!(size.checked_mul(radix));
*size = or_overflow!(size.checked_add((b + 10 - b'A') as u64));
}
b'\t' | b' ' => return Poll::Ready(Ok(ChunkedState::SizeLws)),
b';' => return Poll::Ready(Ok(ChunkedState::Extension)),
b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)),
_ => {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk size line: Invalid Size",
)));
}
}
Poll::Ready(Ok(ChunkedState::Size))
}
fn read_size_lws<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
) -> Poll<Result<ChunkedState, io::Error>> {
trace!("read_size_lws");
match byte!(rdr, cx) {
// LWS can follow the chunk size, but no more digits can come
b'\t' | b' ' => Poll::Ready(Ok(ChunkedState::SizeLws)),
b';' => Poll::Ready(Ok(ChunkedState::Extension)),
b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
_ => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk size linear white space",
))),
}
}
fn read_extension<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
extensions_cnt: &mut u64,
) -> Poll<Result<ChunkedState, io::Error>> {
trace!("read_extension");
// We don't care about extensions really at all. Just ignore them.
// They "end" at the next CRLF.
//
// However, some implementations may not check for the CR, so to save
// them from themselves, we reject extensions containing plain LF as
// well.
match byte!(rdr, cx) {
b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
b'\n' => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidData,
"invalid chunk extension contains newline",
))),
_ => {
*extensions_cnt += 1;
if *extensions_cnt >= CHUNKED_EXTENSIONS_LIMIT {
Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidData,
"chunk extensions over limit",
)))
} else {
Poll::Ready(Ok(ChunkedState::Extension))
}
} // no supported extensions
}
}
fn read_size_lf<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
size: u64,
) -> Poll<Result<ChunkedState, io::Error>> {
trace!("Chunk size is {:?}", size);
match byte!(rdr, cx) {
b'\n' => {
if size == 0 {
Poll::Ready(Ok(ChunkedState::EndCr))
} else {
debug!("incoming chunked header: {0:#X} ({0} bytes)", size);
Poll::Ready(Ok(ChunkedState::Body))
}
}
_ => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk size LF",
))),
}
}
fn read_body<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
rem: &mut u64,
buf: &mut Option<Bytes>,
) -> Poll<Result<ChunkedState, io::Error>> {
trace!("Chunked read, remaining={:?}", rem);
// cap remaining bytes at the max capacity of usize
let rem_cap = match *rem {
r if r > usize::MAX as u64 => usize::MAX,
r => r as usize,
};
let to_read = rem_cap;
let slice = ready!(rdr.read_mem(cx, to_read))?;
let count = slice.len();
if count == 0 {
*rem = 0;
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
IncompleteBody,
)));
}
*buf = Some(slice);
*rem -= count as u64;
if *rem > 0 {
Poll::Ready(Ok(ChunkedState::Body))
} else {
Poll::Ready(Ok(ChunkedState::BodyCr))
}
}
fn read_body_cr<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
) -> Poll<Result<ChunkedState, io::Error>> {
match byte!(rdr, cx) {
b'\r' => Poll::Ready(Ok(ChunkedState::BodyLf)),
_ => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk body CR",
))),
}
}
fn read_body_lf<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
) -> Poll<Result<ChunkedState, io::Error>> {
match byte!(rdr, cx) {
b'\n' => Poll::Ready(Ok(ChunkedState::Start)),
_ => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk body LF",
))),
}
}
fn read_trailer<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
trailers_buf: &mut Option<BytesMut>,
h1_max_header_size: usize,
) -> Poll<Result<ChunkedState, io::Error>> {
trace!("read_trailer");
let byte = byte!(rdr, cx);
put_u8!(
trailers_buf.as_mut().expect("trailers_buf is None"),
byte,
h1_max_header_size
);
match byte {
b'\r' => Poll::Ready(Ok(ChunkedState::TrailerLf)),
_ => Poll::Ready(Ok(ChunkedState::Trailer)),
}
}
fn read_trailer_lf<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
trailers_buf: &mut Option<BytesMut>,
trailers_cnt: &mut usize,
h1_max_headers: usize,
h1_max_header_size: usize,
) -> Poll<Result<ChunkedState, io::Error>> {
let byte = byte!(rdr, cx);
match byte {
b'\n' => {
if *trailers_cnt >= h1_max_headers {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidData,
"chunk trailers count overflow",
)));
}
*trailers_cnt += 1;
put_u8!(
trailers_buf.as_mut().expect("trailers_buf is None"),
byte,
h1_max_header_size
);
Poll::Ready(Ok(ChunkedState::EndCr))
}
_ => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid trailer end LF",
))),
}
}
fn read_end_cr<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
trailers_buf: &mut Option<BytesMut>,
h1_max_header_size: usize,
) -> Poll<Result<ChunkedState, io::Error>> {
let byte = byte!(rdr, cx);
match byte {
b'\r' => {
if let Some(trailers_buf) = trailers_buf {
put_u8!(trailers_buf, byte, h1_max_header_size);
}
Poll::Ready(Ok(ChunkedState::EndLf))
}
byte => {
match trailers_buf {
None => {
// 64 will fit a single Expires header without reallocating
let mut buf = BytesMut::with_capacity(64);
buf.put_u8(byte);
*trailers_buf = Some(buf);
}
Some(ref mut trailers_buf) => {
put_u8!(trailers_buf, byte, h1_max_header_size);
}
}
Poll::Ready(Ok(ChunkedState::Trailer))
}
}
}
fn read_end_lf<R: MemRead>(
cx: &mut Context<'_>,
rdr: &mut R,
trailers_buf: &mut Option<BytesMut>,
h1_max_header_size: usize,
) -> Poll<Result<ChunkedState, io::Error>> {
let byte = byte!(rdr, cx);
match byte {
b'\n' => {
if let Some(trailers_buf) = trailers_buf {
put_u8!(trailers_buf, byte, h1_max_header_size);
}
Poll::Ready(Ok(ChunkedState::End))
}
_ => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk end LF",
))),
}
}
}