-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
fix(http1): add http10_disable_keep_alive() for server and client #3936
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 0.14.x
Are you sure you want to change the base?
Conversation
add http10_disable_keep_alive() for server and client to disable keep_alive connections for HTTP/1.0 requests and response
I don't expect to merge much more to 0.14.x in general... But even for 1.x, my initial feelings are that it's easier to do outside of hyper, simply with something like: if resp.version() == Version::HTTP_10 {
resp.headers.insert("connection", "close");
} |
@seanmonstar In certain cases you simply can’t force a connection to close outside of Hyper. For example, if you send an HTTP/1.1 keep-alive request but the server replies with HTTP/1.0 keep-alive, Hyper will put that connection back in the pool—and there’s no way to evict it, even if you add a I fully respect the back-porting policy. If upstream decides this patch isn’t suitable, we can leave it in our own branch. |
Ah, gotcha! Good point. For the 0.14 let captured = hyper::client::connect::capture_connection(&mut req);
let resp = client.request(req).await?;
if resp.version() == Version::HTTP_10 {
captured.connection_metadata().unwrap().poison();
}
For reference, the AWS SDK introduced this mechanism so they could evict connections based on custom criteria. |
Wow! Excellent! That's exactly what I am looking for! |
@seanmonstar I found something unexpected with poison(). Setupclient sent an HTTP1.0 request with "connection": "close", server responsed with HTTP1.1 response (connection header ommited, hyper treats it as keep-alive by default). I implemented a dumb server which intentionally NOT closing tcp connection even recv a "connection": "close" request. I observed that even with poison(), for the second request, conn evicted from pool, but conn stay connected for exactly 1min instead of closing immediately. It seems that with poison(), hyper relies on the idle checking task to regularly check for poisoned connections and reap them off, intead of respecting "connection": "close" header and close the socket once the response is fully received. If I use the patch in this PR, client closes the connection immediately every time as expected. POCsteps
Client (a proxy) // incomming req..
println!("request: {:?}", req);
if matches!(req.version(), Version::HTTP_10) {
// if http1.0 req, rewrite to connection: close
req.headers_mut().insert(CONNECTION, "close".parse().unwrap());
};
let req_version = req.version();
let result = client.request(req).await;
println!("response: {:?}", result);
result.map(|mut resp| {
if matches!(resp.version(), Version::HTTP_10) || matches!(req_version, Version::HTTP_10) {
// drop connection for http1.0
if captured.connection_metadata().as_ref().map(|conn| {
println!("poison connection");
conn.poison();
})
resp.headers_mut().insert(CONNECTION, "close".parse().unwrap());
};
resp
}) Dumb server #[tokio::main]
async fn main() {
let listen: SocketAddr = "127.0.0.1:8084".parse().unwrap();
println!("server h11 {listen:?}");
let listener = TcpListener::bind(listen).await.unwrap();
loop {
let (mut stream, _) = listener.accept().await.unwrap();
println!("new stream");
tokio::spawn(async move {
let mut data = [0u8; 1024 * 32];
while let Ok(size) = stream.read(&mut data).await {
if size == 0 {
continue
}
let data = String::from_utf8_lossy(&data[..size]);
println!("[{}]", data);
if data.contains("HTTP/1.0") {
println!("HTTP/1.0 received, I can't handle it correctly");
stream.write_all(b"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n").await.unwrap();
} else {
stream.write_all(b"HTTP/1.1 200 OK\r\nconnection: keep-alive\r\nContent-Length: 0\r\n\r\n").await.unwrap();
}
}
});
}
} LOG
|
add http10_disable_keep_alive() for server and client to disable keep_alive connections for HTTP/1.0 requests and response
Server and Client can optionally disable HTTP/1.0 keepalive. If it's set, for http1.0 request and response, connection: keep-alive will be ignored and overridden with connection: close. Connections' state.keepalive is maintained accordingly.
@seanmonstar this is our vendored patch to fix following issues. Do you think if it is appropriate to merge into upstream?
fix #3588 and https://github.com/hyperium/hyper/security/advisories/GHSA-85g5-4q7m-6cgx