fix(http1): ignore chunked trailers (#2357)
Previously, hyper returned an "Invalid chunk end CR" error on chunked responses with trailers, as described in RFC 7230 Section 4.1.2. This commit adds code to ignore the trailers. Closes #2171
This commit is contained in:
@@ -55,6 +55,8 @@ enum ChunkedState {
|
||||
Body,
|
||||
BodyCr,
|
||||
BodyLf,
|
||||
Trailer,
|
||||
TrailerLf,
|
||||
EndCr,
|
||||
EndLf,
|
||||
End,
|
||||
@@ -196,6 +198,8 @@ impl ChunkedState {
|
||||
Body => ChunkedState::read_body(cx, body, size, buf),
|
||||
BodyCr => ChunkedState::read_body_cr(cx, body),
|
||||
BodyLf => ChunkedState::read_body_lf(cx, body),
|
||||
Trailer => ChunkedState::read_trailer(cx, body),
|
||||
TrailerLf => ChunkedState::read_trailer_lf(cx, body),
|
||||
EndCr => ChunkedState::read_end_cr(cx, body),
|
||||
EndLf => ChunkedState::read_end_lf(cx, body),
|
||||
End => Poll::Ready(Ok(ChunkedState::End)),
|
||||
@@ -340,16 +344,36 @@ impl ChunkedState {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_trailer<R: MemRead>(
|
||||
cx: &mut task::Context<'_>,
|
||||
rdr: &mut R,
|
||||
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||
trace!("read_trailer");
|
||||
match byte!(rdr, cx) {
|
||||
b'\r' => Poll::Ready(Ok(ChunkedState::TrailerLf)),
|
||||
_ => Poll::Ready(Ok(ChunkedState::Trailer)),
|
||||
}
|
||||
}
|
||||
fn read_trailer_lf<R: MemRead>(
|
||||
cx: &mut task::Context<'_>,
|
||||
rdr: &mut R,
|
||||
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||
match byte!(rdr, cx) {
|
||||
b'\n' => 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 task::Context<'_>,
|
||||
rdr: &mut R,
|
||||
) -> Poll<Result<ChunkedState, io::Error>> {
|
||||
match byte!(rdr, cx) {
|
||||
b'\r' => Poll::Ready(Ok(ChunkedState::EndLf)),
|
||||
_ => Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Invalid chunk end CR",
|
||||
))),
|
||||
_ => Poll::Ready(Ok(ChunkedState::Trailer)),
|
||||
}
|
||||
}
|
||||
fn read_end_lf<R: MemRead>(
|
||||
@@ -538,6 +562,15 @@ mod tests {
|
||||
assert_eq!("1234567890abcdef", &result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_read_chunked_trailer_with_missing_lf() {
|
||||
let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\nbad\r\r\n"[..];
|
||||
let mut decoder = Decoder::chunked();
|
||||
decoder.decode_fut(&mut mock_buf).await.expect("decode");
|
||||
let e = decoder.decode_fut(&mut mock_buf).await.unwrap_err();
|
||||
assert_eq!(e.kind(), io::ErrorKind::InvalidInput);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_read_chunked_after_eof() {
|
||||
let mut mock_buf = &b"10\r\n1234567890abcdef\r\n0\r\n\r\n"[..];
|
||||
|
||||
@@ -430,6 +430,69 @@ test! {
|
||||
body: None,
|
||||
}
|
||||
|
||||
test! {
|
||||
name: client_get_req_body_chunked_with_trailer,
|
||||
|
||||
server:
|
||||
expected: "\
|
||||
GET / HTTP/1.1\r\n\
|
||||
host: {addr}\r\n\
|
||||
\r\n\
|
||||
",
|
||||
reply: "\
|
||||
HTTP/1.1 200 OK\r\n\
|
||||
Transfer-Encoding: chunked\r\n\
|
||||
\r\n\
|
||||
5\r\n\
|
||||
hello\r\n\
|
||||
0\r\n\
|
||||
Trailer: value\r\n\
|
||||
\r\n\
|
||||
",
|
||||
|
||||
client:
|
||||
request: {
|
||||
method: GET,
|
||||
url: "http://{addr}/",
|
||||
},
|
||||
response:
|
||||
status: OK,
|
||||
headers: {},
|
||||
body: &b"hello"[..],
|
||||
}
|
||||
|
||||
test! {
|
||||
name: client_get_req_body_chunked_with_multiple_trailers,
|
||||
|
||||
server:
|
||||
expected: "\
|
||||
GET / HTTP/1.1\r\n\
|
||||
host: {addr}\r\n\
|
||||
\r\n\
|
||||
",
|
||||
reply: "\
|
||||
HTTP/1.1 200 OK\r\n\
|
||||
Transfer-Encoding: chunked\r\n\
|
||||
\r\n\
|
||||
5\r\n\
|
||||
hello\r\n\
|
||||
0\r\n\
|
||||
Trailer: value\r\n\
|
||||
another-trainer: another-value\r\n\
|
||||
\r\n\
|
||||
",
|
||||
|
||||
client:
|
||||
request: {
|
||||
method: GET,
|
||||
url: "http://{addr}/",
|
||||
},
|
||||
response:
|
||||
status: OK,
|
||||
headers: {},
|
||||
body: &b"hello"[..],
|
||||
}
|
||||
|
||||
test! {
|
||||
name: client_get_req_body_sized,
|
||||
|
||||
|
||||
Reference in New Issue
Block a user