-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Version
from Cargo.toml
hyper = { version = "0.14", features = [ "client", "server", "http2", "http1", "tcp" ] }
tokio = { version = "1.0", features = ["rt", "macros", "rt-multi-thread"] }
h2 = "0.3"
http = "0.2"
from cargo tree
h2 v0.3.15
hyper v0.14.23
Platform
Linux zita 5.15.74-1-pve #1 SMP PVE 5.15.74-1 (Mon, 14 Nov 2022 20:17:15 +0100) x86_64 GNU/Linux
Description
Closing a HTTP2 connection from the client side that resides on the same host (localhost/127.0.0.1),
the server sometimes gets an error:
'connection error: not connected'
This never happens (AFAICT) when having the server on a different host than the client
I tried this code (most copied from the hyper/h2 examples)
server:
use hyper::header::UPGRADE;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Request, Response, Server, StatusCode};
use std::convert::Infallible;
use std::net::SocketAddr;
async fn handle(_req: Request<Body>) -> Result<Response<Body>, Infallible> {
Ok(Response::new(Body::from("Hello World")))
}
async fn start_h2(req: Request<Body>) -> Result<Response<Body>, Infallible> {
// start the h2 future
tokio::spawn(async move {
let conn = match hyper::upgrade::on(req).await {
Ok(conn) => conn,
Err(err) => {
eprintln!("error during upgrade: {err}");
return;
}
};
let mut http = hyper::server::conn::Http::new();
http.http2_only(true);
match http.serve_connection(conn, service_fn(handle)).await {
Ok(_) => eprintln!("finished gracefully"),
Err(err) => println!("ERROR: {err}"),
}
});
Ok(Response::builder()
.status(StatusCode::SWITCHING_PROTOCOLS)
.header(UPGRADE, "h2c")
.body(Body::empty())
.unwrap())
}
#[tokio::main]
async fn main() {
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
let make_service = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(start_h2)) });
let server = Server::bind(&addr).serve(make_service);
if let Err(e) = server.await {
eprintln!("server error: {}", e);
}
}
client:
use h2::client;
use http::{header::UPGRADE, Method, Request};
use hyper::Body;
use std::error::Error;
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn Error>> {
let client = hyper::Client::new();
let req = Request::builder()
.uri("http://127.0.0.1:3000/")
.header(UPGRADE, "h2c")
.body(Body::empty())?;
let response = client.request(req).await?;
let upgraded = hyper::upgrade::on(response).await?;
let (h2, connection) = client::Builder::new()
.handshake::<_, &[u8]>(upgraded)
.await?;
tokio::spawn(async move { connection.await });
let mut h2 = h2.ready().await?;
let request = Request::builder()
.method(Method::GET)
.uri("/")
.body(())
.unwrap();
let (response, _) = h2.send_request(request, true).unwrap();
let (head, _) = response.await?.into_parts();
println!("Received response: {:?}", head);
Ok(())
}
Letting the server run, and using the client in a loop, i get here ~50%
ERROR: connection error: not connected
How can we gracefully close the connection from the client without getting those errors?