2023-06-08 07:01:34 +00:00
|
|
|
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<Self> {
|
|
|
|
// 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<u8>> = 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));
|
2023-06-08 17:08:18 +00:00
|
|
|
|
|
|
|
// Enable BBR congestion control
|
|
|
|
// TODO validate the implementation
|
2023-06-08 07:01:34 +00:00
|
|
|
let mut transport_config = quinn::TransportConfig::default();
|
|
|
|
transport_config.keep_alive_interval(Some(time::Duration::from_secs(2)));
|
2023-06-08 17:08:18 +00:00
|
|
|
transport_config.congestion_controller_factory(sync::Arc::new(quinn::congestion::BbrConfig::default()));
|
2023-06-08 07:01:34 +00:00
|
|
|
|
2023-06-08 17:08:18 +00:00
|
|
|
server_config.transport = sync::Arc::new(transport_config);
|
2023-06-08 07:01:34 +00:00
|
|
|
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<WebTransportSession> {
|
|
|
|
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::<h3::ext::Protocol>() == 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)
|
|
|
|
}
|
|
|
|
}
|