Skip to content

Commit 540fff9

Browse files
authored
fix(http1): discard content-length header when received before transfer-encoding (#4124)
While hyper currently recognizes the correct message payload semantics when both content-length and transfer-encoding headers are sent, there are cases where it doesn't always strip the canceled content-length header. This fix now correctly removes the header if it was seen before transfer-encoding. It also now sets the connection to close at the end of the message, as recommended in the new RFC 9112. (To be clear, the semantics have always been correct. This reduces the possibility of forwarding confusing headers to a remote that doesn't know how to act correctly.) Closes #4123
1 parent ccc1e85 commit 540fff9

1 file changed

Lines changed: 53 additions & 34 deletions

File tree

src/proto/h1/role.rs

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ impl Http1Transaction for Server {
234234
let mut decoder = DecodedLength::ZERO;
235235
let mut expect_continue = false;
236236
let mut con_len = None;
237+
let mut is_cl = false;
237238
let mut is_te = false;
238239
let mut is_te_chunked = false;
239240
let mut wants_upgrade = subject.0 == Method::CONNECT;
@@ -272,6 +273,9 @@ impl Http1Transaction for Server {
272273
return Err(Parse::transfer_encoding_unexpected());
273274
}
274275
is_te = true;
276+
if is_cl && con_len.take().is_some() {
277+
headers.remove(header::CONTENT_LENGTH);
278+
}
275279
if headers::is_chunked_(&value) {
276280
is_te_chunked = true;
277281
decoder = DecodedLength::CHUNKED;
@@ -280,6 +284,7 @@ impl Http1Transaction for Server {
280284
}
281285
}
282286
header::CONTENT_LENGTH => {
287+
is_cl = true;
283288
if is_te {
284289
continue;
285290
}
@@ -340,6 +345,10 @@ impl Http1Transaction for Server {
340345
return Err(Parse::transfer_encoding_invalid());
341346
}
342347

348+
if is_te && is_cl {
349+
keep_alive = false;
350+
}
351+
343352
let mut extensions = http::Extensions::default();
344353

345354
if let Some(header_case_map) = header_case_map {
@@ -2048,45 +2057,55 @@ mod tests {
20482057
);
20492058

20502059
// transfer-encoding and content-length = chunked
2051-
assert_eq!(
2052-
parse(
2053-
"\
2054-
POST / HTTP/1.1\r\n\
2055-
content-length: 10\r\n\
2056-
transfer-encoding: chunked\r\n\
2057-
\r\n\
2058-
"
2059-
)
2060-
.decode,
2061-
DecodedLength::CHUNKED
2060+
let msg = parse(
2061+
"\
2062+
POST / HTTP/1.1\r\n\
2063+
content-length: 10\r\n\
2064+
transfer-encoding: chunked\r\n\
2065+
\r\n\
2066+
",
20622067
);
2068+
assert_eq!(msg.decode, DecodedLength::CHUNKED);
2069+
assert!(!msg.head.headers.contains_key(header::CONTENT_LENGTH));
2070+
assert!(!msg.keep_alive);
20632071

2064-
assert_eq!(
2065-
parse(
2066-
"\
2067-
POST / HTTP/1.1\r\n\
2068-
transfer-encoding: chunked\r\n\
2069-
content-length: 10\r\n\
2070-
\r\n\
2071-
"
2072-
)
2073-
.decode,
2074-
DecodedLength::CHUNKED
2072+
let msg = parse(
2073+
"\
2074+
POST / HTTP/1.1\r\n\
2075+
transfer-encoding: chunked\r\n\
2076+
content-length: 10\r\n\
2077+
\r\n\
2078+
",
20752079
);
2080+
assert_eq!(msg.decode, DecodedLength::CHUNKED);
2081+
assert!(!msg.head.headers.contains_key(header::CONTENT_LENGTH));
2082+
assert!(!msg.keep_alive);
20762083

2077-
assert_eq!(
2078-
parse(
2079-
"\
2080-
POST / HTTP/1.1\r\n\
2081-
transfer-encoding: gzip\r\n\
2082-
content-length: 10\r\n\
2083-
transfer-encoding: chunked\r\n\
2084-
\r\n\
2085-
"
2086-
)
2087-
.decode,
2088-
DecodedLength::CHUNKED
2084+
let msg = parse(
2085+
"\
2086+
POST / HTTP/1.1\r\n\
2087+
transfer-encoding: gzip\r\n\
2088+
content-length: 10\r\n\
2089+
transfer-encoding: chunked\r\n\
2090+
\r\n\
2091+
",
2092+
);
2093+
assert_eq!(msg.decode, DecodedLength::CHUNKED);
2094+
assert!(!msg.head.headers.contains_key(header::CONTENT_LENGTH));
2095+
assert!(!msg.keep_alive);
2096+
2097+
let msg = parse(
2098+
"\
2099+
POST / HTTP/1.1\r\n\
2100+
connection: keep-alive\r\n\
2101+
content-length: 10\r\n\
2102+
transfer-encoding: chunked\r\n\
2103+
\r\n\
2104+
",
20892105
);
2106+
assert_eq!(msg.decode, DecodedLength::CHUNKED);
2107+
assert!(!msg.head.headers.contains_key(header::CONTENT_LENGTH));
2108+
assert!(!msg.keep_alive);
20902109

20912110
// multiple content-lengths of same value are fine
20922111
assert_eq!(

0 commit comments

Comments
 (0)