this is my http3 server code. It use tcp first to tell browser that change to http3, but failed.
the http3 server can be visit in curl.
the quic result in wireshark:
error info :
[Expert Info (Warning/Decryption): Failed to create decryption context: Secrets are not available]
the code
[package]
name = "http3_server"
version = "0.1.0"
edition = "2024"
[dependencies]
bytes = "1.10.1"
h2 = "0.4.8"
h3 = "0.0.6"
h3-quinn = "0.0.7"
http = "1.2.0"
quinn = "0.11.6"
rustls = "0.23.23"
rustls-pemfile = "2.2.0"
tokio = { version = "1.43.0", features = ["full"] }
tokio-rustls = "0.26.2"
use bytes::Bytes;
use quinn::crypto::rustls::QuicServerConfig;
use quinn::{Endpoint, ServerConfig};
use rustls::pki_types::{PrivateKeyDer, pem::PemObject};
use rustls_pemfile::certs;
use std::error::Error;
use std::{fs::File, io::BufReader, sync::Arc};
use tokio::join;
use tokio::{
io::{self},
net::TcpListener,
};
use tokio_rustls::TlsAcceptor;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed install default CryptoProvider");
join!(
//
tcp_server(),
//
udp_server()
);
Ok(())
}
pub async fn tcp_server() {
let mut cert_file =
BufReader::new(File::open("/home/aksjfds/codes/http3_server/cert.pem").unwrap());
let key_file = BufReader::new(File::open("/home/aksjfds/codes/http3_server/key.pem").unwrap());
let cert_chain: Vec<_> = certs(&mut cert_file)
.collect::<Result<Vec<_>, _>>()
.unwrap();
let key = PrivateKeyDer::from_pem_reader(key_file).unwrap();
let mut config = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(cert_chain, key)
.unwrap();
config.alpn_protocols = vec![b"h3".to_vec(), b"h2".to_vec()];
let acceptor = TlsAcceptor::from(Arc::new(config));
let addr = "127.0.0.1:443";
let listener = TcpListener::bind(addr).await.unwrap();
println!("Server running on {}", addr);
loop {
let (stream, peer_addr) = listener.accept().await.unwrap();
let acceptor = acceptor.clone();
println!("{:#?}", peer_addr);
let fut = async move {
let stream = acceptor.accept(stream).await?;
let mut h2 = h2::server::handshake(stream).await.unwrap();
while let Some(Ok((_req, mut respond))) = h2.accept().await {
println!("Received request");
let response = http::Response::builder()
.status(206)
.header("Content-Type", "text/plain; charset=utf-8")
.header("Alt-Svc", "h3=\":443\"; ma=3600")
.body(())
.unwrap();
let mut send = respond.send_response(response, false).unwrap();
send.send_data("Hello, HTTP/2!".into(), true).unwrap();
}
Ok(()) as io::Result<()>
};
tokio::spawn(fut);
}
}
// HTTP/3 Server (UDP)
pub async fn udp_server() {
let mut cert_file =
BufReader::new(File::open("/home/aksjfds/codes/http3_server/cert.pem").unwrap());
let key_file = BufReader::new(File::open("/home/aksjfds/codes/http3_server/key.pem").unwrap());
let cert_chain: Vec<_> = certs(&mut cert_file)
.collect::<Result<Vec<_>, _>>()
.unwrap();
let key = PrivateKeyDer::from_pem_reader(key_file).unwrap();
let mut rustls_config = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(cert_chain, key)
.unwrap();
rustls_config.alpn_protocols = vec![b"h3".to_vec()];
let server_config = QuicServerConfig::try_from(rustls_config).unwrap();
let server_config = ServerConfig::with_crypto(Arc::new(server_config));
let addr = "127.0.0.1:443".parse().unwrap();
let endpoint = Endpoint::server(server_config, addr).unwrap();
println!("HTTP/3 server running on https://{}", addr);
while let Some(incoming) = endpoint.accept().await {
let incoming = incoming.await.unwrap();
// ε€η QUIC θΏζ₯
let task = async move {
let h3_conn = h3_quinn::Connection::new(incoming);
let mut h3_conn = h3::server::Connection::new(h3_conn).await.unwrap();
while let Some((req, mut stream)) = h3_conn.accept().await.unwrap() {
println!("HTTP/3 request: {:?}", req);
let response = http::Response::builder()
.status(200)
.header("Content-Type", "text/plain; charset=utf-8")
.body(())
.unwrap();
stream.send_response(response).await.unwrap();
stream
.send_data(Bytes::from_static(b"Hello, HTTP/3!"))
.await
.unwrap();
stream.finish().await.unwrap();
}
};
tokio::spawn(task);
}
}