2023-09-15 19:06:28 +00:00
|
|
|
use std::{
|
2023-09-18 05:43:48 +00:00
|
|
|
fs,
|
|
|
|
io::{self, Read},
|
2023-10-12 04:09:32 +00:00
|
|
|
sync::Arc,
|
2023-09-15 19:06:28 +00:00
|
|
|
time,
|
|
|
|
};
|
2023-06-08 07:01:34 +00:00
|
|
|
|
|
|
|
use anyhow::Context;
|
|
|
|
|
2023-06-16 18:38:19 +00:00
|
|
|
use tokio::task::JoinSet;
|
|
|
|
|
2023-10-12 04:09:32 +00:00
|
|
|
use crate::{Config, Origin, Session};
|
2023-09-15 19:06:28 +00:00
|
|
|
|
2023-06-08 07:01:34 +00:00
|
|
|
pub struct Server {
|
2023-10-12 04:09:32 +00:00
|
|
|
quic: quinn::Endpoint,
|
2023-06-08 07:01:34 +00:00
|
|
|
|
2023-07-20 01:04:45 +00:00
|
|
|
// The active connections.
|
|
|
|
conns: JoinSet<anyhow::Result<()>>,
|
2023-06-08 07:01:34 +00:00
|
|
|
|
2023-09-15 19:06:28 +00:00
|
|
|
// The map of active broadcasts by path.
|
2023-10-12 04:09:32 +00:00
|
|
|
origin: Origin,
|
2023-06-08 07:01:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Server {
|
|
|
|
// Create a new server
|
2023-10-12 04:09:32 +00:00
|
|
|
pub async fn new(config: Config) -> anyhow::Result<Self> {
|
2023-06-08 07:01:34 +00:00
|
|
|
// 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);
|
2023-10-12 04:09:32 +00:00
|
|
|
|
|
|
|
let certs: Vec<rustls::Certificate> = rustls_pemfile::certs(&mut certs)?
|
2023-06-08 07:01:34 +00:00
|
|
|
.into_iter()
|
|
|
|
.map(rustls::Certificate)
|
|
|
|
.collect();
|
|
|
|
|
2023-10-12 04:09:32 +00:00
|
|
|
anyhow::ensure!(!certs.is_empty(), "could not find certificate");
|
|
|
|
|
2023-06-08 07:01:34 +00:00
|
|
|
// Read the PEM private key
|
2023-09-18 05:43:48 +00:00
|
|
|
let mut keys = fs::File::open(config.key).context("failed to open key file")?;
|
|
|
|
|
|
|
|
// Read the keys into a Vec so we can try parsing it twice.
|
|
|
|
let mut buf = Vec::new();
|
|
|
|
keys.read_to_end(&mut buf)?;
|
|
|
|
|
|
|
|
// Try to parse a PKCS#8 key
|
|
|
|
// -----BEGIN PRIVATE KEY-----
|
|
|
|
let mut keys = rustls_pemfile::pkcs8_private_keys(&mut io::Cursor::new(&buf))?;
|
|
|
|
|
|
|
|
// Try again but with EC keys this time
|
|
|
|
// -----BEGIN EC PRIVATE KEY-----
|
|
|
|
if keys.is_empty() {
|
|
|
|
keys = rustls_pemfile::ec_private_keys(&mut io::Cursor::new(&buf))?
|
|
|
|
};
|
|
|
|
|
|
|
|
anyhow::ensure!(!keys.is_empty(), "could not find private key");
|
|
|
|
anyhow::ensure!(keys.len() < 2, "expected a single key");
|
2023-06-08 07:01:34 +00:00
|
|
|
|
|
|
|
let key = rustls::PrivateKey(keys.remove(0));
|
|
|
|
|
2023-10-12 04:09:32 +00:00
|
|
|
// Set up a QUIC endpoint that can act as both a client and server.
|
|
|
|
|
|
|
|
// Create a list of acceptable root certificates.
|
|
|
|
let mut client_roots = rustls::RootCertStore::empty();
|
|
|
|
|
|
|
|
// Add the platform's native root certificates.
|
2023-10-13 06:59:54 +00:00
|
|
|
for cert in rustls_native_certs::load_native_certs().context("could not load platform certs")? {
|
|
|
|
client_roots
|
|
|
|
.add(&rustls::Certificate(cert.0))
|
|
|
|
.context("failed to add root cert")?;
|
2023-10-12 04:09:32 +00:00
|
|
|
}
|
|
|
|
|
2023-10-13 06:59:54 +00:00
|
|
|
// For local development, we'll accept our own certificate.
|
2023-10-12 04:09:32 +00:00
|
|
|
let mut client_config = rustls::ClientConfig::builder()
|
|
|
|
.with_safe_defaults()
|
|
|
|
.with_root_certificates(client_roots)
|
|
|
|
.with_no_client_auth();
|
|
|
|
|
|
|
|
let mut server_config = rustls::ServerConfig::builder()
|
2023-10-13 06:59:54 +00:00
|
|
|
.with_safe_defaults()
|
2023-06-08 07:01:34 +00:00
|
|
|
.with_no_client_auth()
|
|
|
|
.with_single_cert(certs, key)?;
|
|
|
|
|
2023-10-12 04:09:32 +00:00
|
|
|
client_config.alpn_protocols = vec![webtransport_quinn::ALPN.to_vec()];
|
|
|
|
server_config.alpn_protocols = vec![webtransport_quinn::ALPN.to_vec()];
|
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();
|
2023-10-12 04:09:32 +00:00
|
|
|
transport_config.max_idle_timeout(Some(time::Duration::from_secs(10).try_into().unwrap()));
|
|
|
|
transport_config.keep_alive_interval(Some(time::Duration::from_secs(4))); // TODO make this smarter
|
2023-09-15 19:06:28 +00:00
|
|
|
transport_config.congestion_controller_factory(Arc::new(quinn::congestion::BbrConfig::default()));
|
2023-10-12 04:09:32 +00:00
|
|
|
let transport_config = Arc::new(transport_config);
|
|
|
|
|
|
|
|
let mut client_config = quinn::ClientConfig::new(Arc::new(client_config));
|
|
|
|
let mut server_config = quinn::ServerConfig::with_crypto(Arc::new(server_config));
|
|
|
|
server_config.transport_config(transport_config.clone());
|
|
|
|
client_config.transport_config(transport_config);
|
|
|
|
|
|
|
|
// There's a bit more boilerplate to make a generic endpoint.
|
|
|
|
let runtime = quinn::default_runtime().context("no async runtime")?;
|
|
|
|
let endpoint_config = quinn::EndpointConfig::default();
|
|
|
|
let socket = std::net::UdpSocket::bind(config.listen).context("failed to bind UDP socket")?;
|
|
|
|
|
|
|
|
// Create the generic QUIC endpoint.
|
|
|
|
let mut quic = quinn::Endpoint::new(endpoint_config, Some(server_config), socket, runtime)
|
|
|
|
.context("failed to create QUIC endpoint")?;
|
|
|
|
quic.set_default_client_config(client_config);
|
|
|
|
|
|
|
|
let api = config.api.map(|url| {
|
|
|
|
log::info!("using moq-api: url={}", url);
|
|
|
|
moq_api::Client::new(url)
|
|
|
|
});
|
|
|
|
|
|
|
|
if let Some(ref node) = config.node {
|
|
|
|
log::info!("advertising origin: url={}", node);
|
|
|
|
}
|
2023-06-08 07:01:34 +00:00
|
|
|
|
2023-10-12 04:09:32 +00:00
|
|
|
let origin = Origin::new(api, config.node, quic.clone());
|
2023-07-20 01:04:45 +00:00
|
|
|
let conns = JoinSet::new();
|
2023-06-08 07:01:34 +00:00
|
|
|
|
2023-10-12 04:09:32 +00:00
|
|
|
Ok(Self { quic, origin, conns })
|
2023-06-08 07:01:34 +00:00
|
|
|
}
|
|
|
|
|
2023-06-26 18:51:06 +00:00
|
|
|
pub async fn run(mut self) -> anyhow::Result<()> {
|
2023-10-12 04:09:32 +00:00
|
|
|
log::info!("listening on {}", self.quic.local_addr()?);
|
2023-09-19 05:37:49 +00:00
|
|
|
|
2023-06-08 07:01:34 +00:00
|
|
|
loop {
|
2023-06-16 18:38:19 +00:00
|
|
|
tokio::select! {
|
2023-10-12 04:09:32 +00:00
|
|
|
res = self.quic.accept() => {
|
2023-07-20 01:04:45 +00:00
|
|
|
let conn = res.context("failed to accept QUIC connection")?;
|
2023-10-12 04:09:32 +00:00
|
|
|
let mut session = Session::new(self.origin.clone());
|
2023-09-15 19:06:28 +00:00
|
|
|
self.conns.spawn(async move { session.run(conn).await });
|
2023-06-16 18:38:19 +00:00
|
|
|
},
|
2023-07-20 01:04:45 +00:00
|
|
|
res = self.conns.join_next(), if !self.conns.is_empty() => {
|
2023-06-16 18:38:19 +00:00
|
|
|
let res = res.expect("no tasks").expect("task aborted");
|
|
|
|
if let Err(err) = res {
|
2023-09-15 19:06:28 +00:00
|
|
|
log::warn!("connection terminated: {:?}", err);
|
2023-06-16 18:38:19 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
2023-06-08 07:01:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|