Version
hyper 1.11.1
Platform
all
Summary
Since hyper 1.11.1, custom hop-by-hop connection headers from the client no longer make it to the server.
For example if the client sends this:
connection: close, x-hop
x-hop: ...
In hyper 1.11.0 and older it used to arrive as written. In 1.11.1 it arrives as:
connection: close
x-hop: ...
This bisects to #4110 which causes headers.insert(CONNECTION, "close") to be called in enforce_version. The behavior of http::header::HeaderMap::insert is:
If the map did have this key present, the new value is associated with the key and all previous values are removed.
Code Sample
// [dependencies]
// anyhow = "1"
// bytes = "1"
// http = "1"
// http-body-util = "0.1"
// hyper = { version = "1", features = ["client", "http1", "server"] }
// hyper-util = { version = "0.1", features = ["tokio"] }
// tokio = { version = "1", features = ["macros", "net", "rt-multi-thread"] }
use bytes::Bytes;
use http::{Request, Response};
use http_body_util::{BodyExt, Empty};
use hyper::service::service_fn;
use hyper_util::rt::TokioIo;
use std::net::Ipv4Addr;
use tokio::net::{TcpListener, TcpStream};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).await?;
let addr = listener.local_addr()?;
let server = tokio::spawn(async move {
let (stream, _) = listener.accept().await?;
hyper::server::conn::http1::Builder::new()
.keep_alive(true)
.serve_connection(
TokioIo::new(stream),
service_fn(async |req| {
for (header, value) in req.headers() {
println!("{header}: {value:?}");
}
anyhow::Ok(Response::new(Empty::<Bytes>::new()))
}),
)
.await?;
anyhow::Ok(())
});
let stream = TcpStream::connect(addr).await?;
let (mut sender, conn) = hyper::client::conn::http1::handshake(TokioIo::new(stream)).await?;
tokio::spawn(conn);
let req = Request::builder()
.uri("/")
.header(http::header::CONNECTION, "close, x-hop")
.header(http::header::CONNECTION, "y-hop")
.header("x-hop", "...")
.header("y-hop", "...")
.body(Empty::<Bytes>::new())?;
let res = sender.send_request(req).await?;
let mut body = res.into_body();
while body.frame().await.is_some() {}
drop(sender);
server.await??;
Ok(())
}
Expected Behavior
This program starts a server on localhost and sends it a request containing custom hop-by-hop headers. The server prints the headers received.
connection: "close, x-hop"
connection: "y-hop"
x-hop: "..."
y-hop: "..."
Actual Behavior
Because of the regression, "x-hop" and "y-hop" are missing from the connection header.
connection: "close"
x-hop: "..."
y-hop: "..."
Additional Context
No response
Version
hyper 1.11.1
Platform
all
Summary
Since hyper 1.11.1, custom hop-by-hop connection headers from the client no longer make it to the server.
For example if the client sends this:
In hyper 1.11.0 and older it used to arrive as written. In 1.11.1 it arrives as:
This bisects to #4110 which causes
headers.insert(CONNECTION, "close")to be called inenforce_version. The behavior ofhttp::header::HeaderMap::insertis:Code Sample
Expected Behavior
This program starts a server on localhost and sends it a request containing custom hop-by-hop headers. The server prints the headers received.
Actual Behavior
Because of the regression, "x-hop" and "y-hop" are missing from the connection header.
Additional Context
No response