I'm trying to write a simple http3 server in rust that receives get requests with "commands" and returns JSON object as output. i use tokio
with h3-quinn
.
these are all the packages that I use:
quinn = "0.10.2"
tokio = { version = "1.34.0", features = ["rt-multi-thread", "full"] }
serde_json = { version = "1.0.108", features = [] }
serde = { version = "1.0.192", features = ["derive"] }
anyhow = "1.0.75"
rcgen = "0.11.3"
rustls = { version = "0.21.8" }
rustls-pemfile = "1.0.4"
h3-quinn = "0.0.4"
log = "0.4.20"
tracing = "0.1.40"
h3 = "0.0.3"
clap = { version = "4.4.8", features = ["derive"] }
env_logger = "0.10.1"
I'm creating a self signed certificate and listening on 0.0.0.0:4443
, when i try to test it with curl, it just hangs.
i'm using Gentoo Linux and i compiled curl with http3, and this is how i use it:
curl --http3 -I https://localhost:4443
when i try google and other sites i do get results so it's not a curl related problem.
I added some info messages and i just see the messages starting!
and listening on 0.0.0.0:4443
, running the curl command doesn't show anymore information.
looks like it's stuck at endpoint.accept().await
, i don't think it's a certificate problem because i should have gotten some kind of error instead.
this is the full code:
use std::error::Error;
use std::fs::File;
use std::io::BufReader;
use std::sync::Arc;
use std::time::Duration;
use anyhow::Result;
use rcgen::generate_simple_self_signed;
use h3_quinn::quinn::{self, Endpoint, ServerConfig};
use quinn::{Connection, ConnectionError, TransportConfig};
use rustls::{Certificate, PrivateKey, ServerConfig as RustlsServerConfig};
use log::{error, info};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use clap::Parser;
use env_logger::Env;
// Define a struct for your data
#[derive(serde::Serialize)]
struct MusicWidget {
name: String,
short_name: String,
version: String,
}
// Create instances of the struct
async fn handle_connection(conn: Connection) -> Result<()> {
let widgets: Vec<MusicWidget> = vec![
MusicWidget {
name: "WidgetA".to_string(),
short_name: "WA".to_string(),
version: "1.0".to_string(),
},
MusicWidget {
name: "WidgetB".to_string(),
short_name: "WB".to_string(),
version: "2.0".to_string(),
},
MusicWidget {
name: "WidgetC".to_string(),
short_name: "WC".to_string(),
version: "3.0".to_string(),
},
];
while let Ok((mut send, mut recv)) = conn.accept_bi().await {
let mut buf = vec![0; 1024];
let _ = recv.read(&mut buf).await?;
let command = String::from_utf8_lossy(&buf);
if command.trim() == "get_music_widget_list" {
let response = serde_json::to_vec(&widgets)?;
send.write_all(&response).await?;
send.finish().await?;
}
}
Ok(())
}
fn load_private_key_from_file(path: &str) -> Result<PrivateKey, Box<dyn std::error::Error>> {
let file = File::open(&path)?;
let mut reader = BufReader::new(file);
let keys = rustls_pemfile::pkcs8_private_keys(&mut reader)?;
if keys.is_empty() {
Err("No PKCS8-encoded private key found".into())
} else {
Ok(PrivateKey(keys[0].clone()))
}
}
/// Returns default server configuration along with its certificate.
fn configure_server() -> Result<(ServerConfig, Vec<u8>), Box<dyn Error>> {
let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()]).unwrap();
let cert_der = cert.serialize_der().unwrap();
let priv_key = cert.serialize_private_key_der();
let priv_key = rustls::PrivateKey(priv_key);
let cert_chain = vec![rustls::Certificate(cert_der.clone())];
let mut server_config = ServerConfig::with_single_cert(cert_chain, priv_key)?;
let transport_config = Arc::get_mut(&mut server_config.transport).unwrap();
transport_config.max_concurrent_uni_streams(0_u8.into());
Ok((server_config, cert_der))
}
fn generate_self_signed_cert() -> Result<(Vec<u8>, PrivateKey), Box<dyn std::error::Error>> {
let cert = generate_simple_self_signed(vec!["localhost".into()])?;
let key = PrivateKey(cert.serialize_private_key_der());
let cert_der = cert.serialize_der()?;
Ok((cert_der, key))
}
fn create_server_config() -> Result<ServerConfig, Box<dyn std::error::Error>> {
let (cert, key) = generate_self_signed_cert()?;
let cert = Certificate(cert);
let server_crypto_config = RustlsServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(vec![cert], key)?;
let transport_config = Arc::new(TransportConfig::default());
let mut server_config = ServerConfig::with_crypto(Arc::new(server_crypto_config));
server_config.transport = transport_config;
Ok(server_config)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let env = Env::default()
.filter_or("MY_LOG_LEVEL", "info");
env_logger::init_from_env(env);
info!("starting!");
// Create a multi-threaded Tokio runtime
// Create the server configuration
let (server_config, cf) = configure_server()?;
//let server_config = create_server_config()?;
let endpoint = Endpoint::server(server_config, "0.0.0.0:4443".parse()?)?;
// Start the server and handle incoming connections
info!("listening on 0.0.0.0:4443");
while let Some(new_conn) = endpoint.accept().await {
info!("New connection being attempted");
tokio::spawn(async move {
info!("new connection something");
match new_conn.await {
Ok(conn) => {
info!("new connection something");
if let Err(err) = handle_connection(conn).await {
tracing::error!("Failed to handle connection: {err:?}");
}
}
Err(e) => {
tracing::error!("Failed to establish connection: {:?}", e);
}
_ => {}
}
});
}
info!("wait idle");
endpoint.wait_idle().await;
Ok(())
}
any information regarding this issue would be greatly appreciated.
thanks