use super::session::Session; use crate::media; use std::{fs, io, net, path, sync, time}; use super::WebTransportSession; use anyhow::Context; pub struct Server { // The QUIC server, yielding new connections and sessions. server: quinn::Endpoint, // The media source broadcast: media::Broadcast, } pub struct ServerConfig { pub addr: net::SocketAddr, pub cert: path::PathBuf, pub key: path::PathBuf, pub broadcast: media::Broadcast, } impl Server { // Create a new server pub fn new(config: ServerConfig) -> anyhow::Result { // Read the PEM certificate chain let certs = fs::File::open(config.cert).context("failed to open cert file")?; let mut certs = io::BufReader::new(certs); let certs = rustls_pemfile::certs(&mut certs)? .into_iter() .map(rustls::Certificate) .collect(); // Read the PEM private key let keys = fs::File::open(config.key).context("failed to open key file")?; let mut keys = io::BufReader::new(keys); let mut keys = rustls_pemfile::pkcs8_private_keys(&mut keys)?; anyhow::ensure!(keys.len() == 1, "expected a single key"); let key = rustls::PrivateKey(keys.remove(0)); let mut tls_config = rustls::ServerConfig::builder() .with_safe_default_cipher_suites() .with_safe_default_kx_groups() .with_protocol_versions(&[&rustls::version::TLS13]) .unwrap() .with_no_client_auth() .with_single_cert(certs, key)?; tls_config.max_early_data_size = u32::MAX; let alpn: Vec> = vec![ b"h3".to_vec(), b"h3-32".to_vec(), b"h3-31".to_vec(), b"h3-30".to_vec(), b"h3-29".to_vec(), ]; tls_config.alpn_protocols = alpn; let mut server_config = quinn::ServerConfig::with_crypto(sync::Arc::new(tls_config)); // Enable BBR congestion control // TODO validate the implementation let mut transport_config = quinn::TransportConfig::default(); transport_config.keep_alive_interval(Some(time::Duration::from_secs(2))); transport_config.congestion_controller_factory(sync::Arc::new(quinn::congestion::BbrConfig::default())); server_config.transport = sync::Arc::new(transport_config); let server = quinn::Endpoint::server(server_config, config.addr)?; let broadcast = config.broadcast; Ok(Self { server, broadcast }) } pub async fn run(&mut self) -> anyhow::Result<()> { loop { let conn = self.server.accept().await.context("failed to accept connection")?; let broadcast = self.broadcast.clone(); tokio::spawn(async move { let session = Self::accept_session(conn).await.context("failed to accept session")?; // Use a wrapper run the session. let session = Session::new(session); session.serve_broadcast(broadcast).await }); } } async fn accept_session(conn: quinn::Connecting) -> anyhow::Result { let conn = conn.await.context("failed to accept h3 connection")?; let mut conn = h3::server::builder() .enable_webtransport(true) .enable_connect(true) .enable_datagram(true) .max_webtransport_sessions(1) .send_grease(true) .build(h3_quinn::Connection::new(conn)) .await .context("failed to create h3 server")?; let (req, stream) = conn .accept() .await .context("failed to accept h3 session")? .context("failed to accept h3 request")?; let ext = req.extensions(); anyhow::ensure!(req.method() == http::Method::CONNECT, "expected CONNECT request"); anyhow::ensure!( ext.get::() == Some(&h3::ext::Protocol::WEB_TRANSPORT), "expected WebTransport CONNECT" ); let session = WebTransportSession::accept(req, stream, conn) .await .context("failed to accept WebTransport session")?; Ok(session) } }