From bf3282e1b1f606ac0ad0358cbea55a6491c711f3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 13 Sep 2026 09:07:54 -0700 Subject: [PATCH 1/2] Add test of custom hop-by-hop headers --- tests/client.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/client.rs b/tests/client.rs index b512260cc5..bc5dd6dfb2 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -1558,6 +1558,35 @@ test! { body: None, } +// FIXME: https://github.com/hyperium/hyper/issues/4195 +test! { + name: client_hop_by_hop_headers, + + server: + expected: "\ + GET / HTTP/1.1\r\n\ + connection: close\r\n\ + x-hop: ...\r\n\ + host: {addr}\r\n\ + \r\n\ + ", + reply: REPLY_OK, + + client: + request: { + method: GET, + url: "http://{addr}/", + headers: { + "connection" => "close, x-hop", + "x-hop" => "...", + }, + }, + response: + status: OK, + headers: {}, + body: None, +} + mod conn { use std::error::Error; use std::io::{self, Read, Write}; From ade5e2e89dac957fb2b9fd86a37269c3746b00de Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 13 Sep 2026 00:41:46 -0700 Subject: [PATCH 2/2] Preserve connection headers when setting close or keep-alive --- src/proto/h1/conn.rs | 35 ++++++++++++++++++++++++----------- tests/client.rs | 4 ++-- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/proto/h1/conn.rs b/src/proto/h1/conn.rs index 593d3d9eda..69ca50b1a4 100644 --- a/src/proto/h1/conn.rs +++ b/src/proto/h1/conn.rs @@ -11,7 +11,7 @@ use std::time::Duration; use crate::rt::{Read, Write}; use bytes::{Buf, Bytes}; use futures_core::ready; -use http::header::{HeaderValue, CONNECTION}; +use http::header::{Entry, HeaderValue, CONNECTION}; use http::{HeaderMap, Method, Version}; use http_body::Frame; use httparse::ParserConfig; @@ -664,10 +664,11 @@ where // Fix keep-alive when Connection: keep-alive header is not present fn fix_keep_alive(&mut self, head: &mut MessageHead) { - let outgoing_is_keep_alive = head - .headers - .get(CONNECTION) - .map_or(false, headers::connection_keep_alive); + let connection_entry = head.headers.entry(CONNECTION); + let outgoing_is_keep_alive = match &connection_entry { + Entry::Occupied(entry) => entry.iter().any(headers::connection_keep_alive), + Entry::Vacant(_) => false, + }; if !outgoing_is_keep_alive { match head.version { @@ -676,10 +677,14 @@ where Version::HTTP_10 => self.state.disable_keep_alive(), // If response is version 1.1 and keep-alive is wanted, add // Connection: keep-alive header when not present - Version::HTTP_11 if self.state.wants_keep_alive() => { - head.headers - .insert(CONNECTION, HeaderValue::from_static("keep-alive")); - } + Version::HTTP_11 if self.state.wants_keep_alive() => match connection_entry { + Entry::Occupied(mut entry) => { + entry.append(HeaderValue::from_static("keep-alive")); + } + Entry::Vacant(entry) => { + entry.insert(HeaderValue::from_static("keep-alive")); + } + }, _ => (), } } @@ -698,8 +703,16 @@ where } Version::HTTP_11 => { if let KA::Disabled = self.state.keep_alive.status() { - head.headers - .insert(CONNECTION, HeaderValue::from_static("close")); + match head.headers.entry(CONNECTION) { + Entry::Occupied(mut entry) => { + if !entry.iter().any(headers::connection_close) { + entry.append(HeaderValue::from_static("close")); + } + } + Entry::Vacant(entry) => { + entry.insert(HeaderValue::from_static("close")); + } + } } } _ => (), diff --git a/tests/client.rs b/tests/client.rs index bc5dd6dfb2..e138f09efe 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -1558,14 +1558,14 @@ test! { body: None, } -// FIXME: https://github.com/hyperium/hyper/issues/4195 +// https://github.com/hyperium/hyper/issues/4195 test! { name: client_hop_by_hop_headers, server: expected: "\ GET / HTTP/1.1\r\n\ - connection: close\r\n\ + connection: close, x-hop\r\n\ x-hop: ...\r\n\ host: {addr}\r\n\ \r\n\