Skip to content

Commit 74a3908

Browse files
authored
fix(http1): preserve hop-by-hop when setting close or keep-alive (#4196)
This adjusts #4110 to use `append` instead of `insert` when a nonzero number of "connection" headers are already present, and only if none of the existing headers already include the value being added. Fixes #4195
1 parent c6dca20 commit 74a3908

2 files changed

Lines changed: 53 additions & 11 deletions

File tree

src/proto/h1/conn.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::time::Duration;
1111
use crate::rt::{Read, Write};
1212
use bytes::{Buf, Bytes};
1313
use futures_core::ready;
14-
use http::header::{HeaderValue, CONNECTION};
14+
use http::header::{Entry, HeaderValue, CONNECTION};
1515
use http::{HeaderMap, Method, Version};
1616
use http_body::Frame;
1717
use httparse::ParserConfig;
@@ -664,10 +664,11 @@ where
664664

665665
// Fix keep-alive when Connection: keep-alive header is not present
666666
fn fix_keep_alive(&mut self, head: &mut MessageHead<T::Outgoing>) {
667-
let outgoing_is_keep_alive = head
668-
.headers
669-
.get(CONNECTION)
670-
.map_or(false, headers::connection_keep_alive);
667+
let connection_entry = head.headers.entry(CONNECTION);
668+
let outgoing_is_keep_alive = match &connection_entry {
669+
Entry::Occupied(entry) => entry.iter().any(headers::connection_keep_alive),
670+
Entry::Vacant(_) => false,
671+
};
671672

672673
if !outgoing_is_keep_alive {
673674
match head.version {
@@ -676,10 +677,14 @@ where
676677
Version::HTTP_10 => self.state.disable_keep_alive(),
677678
// If response is version 1.1 and keep-alive is wanted, add
678679
// Connection: keep-alive header when not present
679-
Version::HTTP_11 if self.state.wants_keep_alive() => {
680-
head.headers
681-
.insert(CONNECTION, HeaderValue::from_static("keep-alive"));
682-
}
680+
Version::HTTP_11 if self.state.wants_keep_alive() => match connection_entry {
681+
Entry::Occupied(mut entry) => {
682+
entry.append(HeaderValue::from_static("keep-alive"));
683+
}
684+
Entry::Vacant(entry) => {
685+
entry.insert(HeaderValue::from_static("keep-alive"));
686+
}
687+
},
683688
_ => (),
684689
}
685690
}
@@ -698,8 +703,16 @@ where
698703
}
699704
Version::HTTP_11 => {
700705
if let KA::Disabled = self.state.keep_alive.status() {
701-
head.headers
702-
.insert(CONNECTION, HeaderValue::from_static("close"));
706+
match head.headers.entry(CONNECTION) {
707+
Entry::Occupied(mut entry) => {
708+
if !entry.iter().any(headers::connection_close) {
709+
entry.append(HeaderValue::from_static("close"));
710+
}
711+
}
712+
Entry::Vacant(entry) => {
713+
entry.insert(HeaderValue::from_static("close"));
714+
}
715+
}
703716
}
704717
}
705718
_ => (),

tests/client.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,35 @@ test! {
15581558
body: None,
15591559
}
15601560

1561+
// https://github.com/hyperium/hyper/issues/4195
1562+
test! {
1563+
name: client_hop_by_hop_headers,
1564+
1565+
server:
1566+
expected: "\
1567+
GET / HTTP/1.1\r\n\
1568+
connection: close, x-hop\r\n\
1569+
x-hop: ...\r\n\
1570+
host: {addr}\r\n\
1571+
\r\n\
1572+
",
1573+
reply: REPLY_OK,
1574+
1575+
client:
1576+
request: {
1577+
method: GET,
1578+
url: "http://{addr}/",
1579+
headers: {
1580+
"connection" => "close, x-hop",
1581+
"x-hop" => "...",
1582+
},
1583+
},
1584+
response:
1585+
status: OK,
1586+
headers: {},
1587+
body: None,
1588+
}
1589+
15611590
mod conn {
15621591
use std::error::Error;
15631592
use std::io::{self, Read, Write};

0 commit comments

Comments
 (0)