I have been trying out using Rustles + Hyper, but for some reason, I am getting a long response time for even the smallest requests.
If I send multiple http1.1 requests they all exceed 300ms+.
If I send multiple HTTP2 requests, the first one exceeds ~300ms, and every request after is ~0ms, but I get errors on the server saying some connections could not be handled.
The same scenarios occur even on --release and whether I call through cargo or not.
use std::net::{SocketAddr};
use std::sync::Arc;
use std::{fs, io};
use std::any::{Any};
use std::convert::Infallible;
use std::future::Future;
use std::str::FromStr;
use http_body_util::{Full};
use hyper::body::{Body, Bytes};
use hyper::server::conn::{http2};
use hyper::service::{Service, service_fn};
use hyper_util::rt::{TokioIo};
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls::{ServerConfig};
use tokio::net::TcpListener;
use tokio_rustls::{rustls, TlsAcceptor};
use crate::exchange::{ExchangeContext, };
use crate::handler::Handler;
#[derive(Clone)]
pub struct TokioExecutor;
impl<F> hyper::rt::Executor<F> for TokioExecutor
where
F: std::future::Future + Send + 'static,
F::Output: Send + 'static,
{
fn execute(&self, fut: F) {
tokio::task::spawn(fut);
}
}
fn load_certs(filename: &str) -> io::Result<Vec<CertificateDer<'static>>> {
let public_cert = fs::File::open(filename)
.map_err(|e| {
error(format!("failed to open {}: {}", filename, e))
})?;
let mut reader = io::BufReader::new(public_cert);
rustls_pemfile::certs(&mut reader).collect()
}
fn load_private_key(filename: &str) -> io::Result<PrivateKeyDer<'static>> {
let private_key = fs::File::open(filename).map_err(|e| {
error(format!("failed to open {}: {}", filename, e))
})?;
let mut reader = io::BufReader::new(private_key);
rustls_pemfile::private_key(&mut reader).map(|key| match key {
Some(x) => x,
None => panic!("Could not load private key: '{}'", filename),
})
}
fn main() {
if let Err(e) = run_server() {
eprintln!("FAILED: {:?}", e);
std::process::exit(1);
}
}
fn error(err: String) -> io::Error {
io::Error::new(io::ErrorKind::Other, err)
}
#[tokio::main]
async fn run_server() -> Result<(), ()> {
let port = 8080;
let addr = SocketAddr::new("0.0.0.0".parse().unwrap(), port);
let server_certs = match load_certs("./server.pem") {
Ok(certs) => certs,
Err(_) => todo!()
};
let key = match load_private_key("./server.rsa") {
Ok(key) => key,
Err(_) => todo!()
};
println!("Starting to serve on https://{}", addr);
let incoming = match TcpListener::bind(&addr).await {
Ok(incoming) => incoming,
Err(_) => todo!()
};
let mut server_config = match ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(server_certs, key)
.map_err(|e| error(e.to_string())) {
Ok(config) => config,
Err(_) => todo!()
};
server_config.alpn_protocols = vec![b"h2".to_vec(),b"http/1.1".to_vec()];
let server_tls_config = Arc::new(server_config);
let acceptor = TlsAcceptor::from(server_tls_config.clone());
loop {
let (tcp_stream, _remote_addr) = match incoming.accept().await {
Ok(stream) => stream,
Err(_) => todo!()
};
let tls_acceptor = acceptor.clone();
match tls_acceptor.accept(tcp_stream).await {
Ok(tls_stream) => {
let io = TokioIo::new(tls_stream);
tokio::task::spawn(async move {
if let Err(err) = http2::Builder::new(TokioExecutor).serve_connection(io, service_fn(hello)).await {
eprintln!("failed to serve connection: {:#}", err);
}
});
},
Err(err) => {
eprintln!("failed to perform tls handshake: {err:#?}");
return Err(());
}
};
}
}
async fn hello(_: hyper::Request<hyper::body::Incoming>) -> Result<hyper::Response<Full<Bytes>>, Infallible> {
Ok(hyper::Response::new(Full::new(Bytes::from("Hello, World!"))))
}
[dependencies]
hyper = { version = "1.5.1", features = [ "http1", "http2", "server" ] }
http-body-util = "0.1.2"
hyper-util = { version = "0.1.10", features = ["full"] }
tokio = { version = "1.42.0", features = ["full"] }
tokio-rustls = "0.26.1"
rustls = { version = "0.23.19", features = ["tls12"] }
rustls-pemfile = "2.2.0"