Skip to content

Commit 7211ec2

Browse files
authored
fix(http1): allow keep-alive for chunked requests with trailers (#4043)
When a chunked request body included trailers, poll_read_body incorrectly transitioned to Reading::Closed instead of Reading::KeepAlive. This prevented connection reuse for any request that sent trailers, even though trailers signal body completion just like a final data frame at EOF. Closes #4044
1 parent d51cb71 commit 7211ec2

File tree

2 files changed

+54
-1
lines changed

2 files changed

+54
-1
lines changed

src/proto/h1/conn.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,8 @@ where
393393
};
394394
(reading, Poll::Ready(maybe_frame))
395395
} else if frame.is_trailers() {
396-
(Reading::Closed, Poll::Ready(Some(Ok(frame))))
396+
debug!("incoming body completed with trailers");
397+
(Reading::KeepAlive, Poll::Ready(Some(Ok(frame))))
397398
} else {
398399
trace!("discarding unknown frame");
399400
(Reading::Closed, Poll::Ready(None))

tests/server.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2967,6 +2967,58 @@ fn http1_trailer_recv_fields() {
29672967
);
29682968
}
29692969

2970+
#[test]
2971+
fn http1_trailer_recv_keep_alive() {
2972+
let server = serve();
2973+
server.reply().header("content-length", "2").body(b"ok");
2974+
let mut req = connect(server.addr());
2975+
2976+
// First request: chunked POST with trailers
2977+
req.write_all(
2978+
b"\
2979+
POST / HTTP/1.1\r\n\
2980+
trailer: chunky-trailer\r\n\
2981+
host: example.domain\r\n\
2982+
transfer-encoding: chunked\r\n\
2983+
\r\n\
2984+
5\r\n\
2985+
hello\r\n\
2986+
0\r\n\
2987+
chunky-trailer: header data\r\n\
2988+
\r\n\
2989+
",
2990+
)
2991+
.expect("writing 1");
2992+
2993+
assert_eq!(server.body(), b"hello");
2994+
2995+
let trailers = server.trailers();
2996+
assert_eq!(
2997+
trailers.get("chunky-trailer"),
2998+
Some(&"header data".parse().unwrap())
2999+
);
3000+
3001+
read_until(&mut req, |buf| buf.ends_with(b"ok")).expect("reading 1");
3002+
3003+
// Second request: reuse the same connection to verify keep-alive
3004+
let quux = b"zar quux";
3005+
server
3006+
.reply()
3007+
.header("content-length", quux.len().to_string())
3008+
.body(quux);
3009+
req.write_all(
3010+
b"\
3011+
GET /quux HTTP/1.1\r\n\
3012+
Host: example.domain\r\n\
3013+
Connection: close\r\n\
3014+
\r\n\
3015+
",
3016+
)
3017+
.expect("writing 2");
3018+
3019+
read_until(&mut req, |buf| buf.ends_with(quux)).expect("reading 2");
3020+
}
3021+
29703022
// -------------------------------------------------
29713023
// the Server that is used to run all the tests with
29723024
// -------------------------------------------------

0 commit comments

Comments
 (0)