2023-06-08 07:01:34 +00:00
|
|
|
use std::{fs, io, net, path, sync};
|
2023-04-14 20:32:02 +00:00
|
|
|
|
2023-06-08 07:01:34 +00:00
|
|
|
use anyhow::Context;
|
2023-04-14 20:32:02 +00:00
|
|
|
use clap::Parser;
|
2023-05-25 00:20:44 +00:00
|
|
|
use ring::digest::{digest, SHA256};
|
|
|
|
use warp::Filter;
|
2023-04-14 20:32:02 +00:00
|
|
|
|
2023-06-26 18:51:06 +00:00
|
|
|
use moq_warp::{relay, source};
|
2023-06-17 03:36:07 +00:00
|
|
|
|
2023-04-14 20:32:02 +00:00
|
|
|
/// Search for a pattern in a file and display the lines that contain it.
|
2023-05-25 00:20:44 +00:00
|
|
|
#[derive(Parser, Clone)]
|
2023-04-14 20:32:02 +00:00
|
|
|
struct Cli {
|
2023-05-23 19:04:27 +00:00
|
|
|
/// Listen on this address
|
|
|
|
#[arg(short, long, default_value = "[::]:4443")]
|
2023-06-08 07:01:34 +00:00
|
|
|
addr: net::SocketAddr,
|
2023-04-14 20:32:02 +00:00
|
|
|
|
2023-05-23 19:04:27 +00:00
|
|
|
/// Use the certificate file at this path
|
2023-05-25 00:20:44 +00:00
|
|
|
#[arg(short, long, default_value = "cert/localhost.crt")]
|
2023-06-08 07:01:34 +00:00
|
|
|
cert: path::PathBuf,
|
2023-04-14 20:32:02 +00:00
|
|
|
|
2023-05-23 19:04:27 +00:00
|
|
|
/// Use the private key at this path
|
2023-05-25 00:20:44 +00:00
|
|
|
#[arg(short, long, default_value = "cert/localhost.key")]
|
2023-06-08 07:01:34 +00:00
|
|
|
key: path::PathBuf,
|
2023-04-24 17:18:55 +00:00
|
|
|
|
2023-05-23 19:04:27 +00:00
|
|
|
/// Use the media file at this path
|
2023-05-25 00:20:44 +00:00
|
|
|
#[arg(short, long, default_value = "media/fragmented.mp4")]
|
2023-06-08 07:01:34 +00:00
|
|
|
media: path::PathBuf,
|
2023-04-24 17:18:55 +00:00
|
|
|
}
|
|
|
|
|
2023-05-25 00:20:44 +00:00
|
|
|
#[tokio::main]
|
|
|
|
async fn main() -> anyhow::Result<()> {
|
2023-05-23 19:04:27 +00:00
|
|
|
env_logger::init();
|
2023-04-24 17:18:55 +00:00
|
|
|
|
2023-06-08 07:01:34 +00:00
|
|
|
let args = Cli::parse();
|
2023-05-25 00:20:44 +00:00
|
|
|
|
2023-06-08 07:01:34 +00:00
|
|
|
// Create a web server to serve the fingerprint
|
|
|
|
let serve = serve_http(args.clone());
|
2023-05-25 00:20:44 +00:00
|
|
|
|
2023-06-08 07:01:34 +00:00
|
|
|
// Create a fake media source from disk.
|
2023-06-26 18:51:06 +00:00
|
|
|
let media = source::File::new(args.media).context("failed to open file source")?;
|
2023-04-14 20:32:02 +00:00
|
|
|
|
2023-06-26 18:51:06 +00:00
|
|
|
let broker = relay::broker::Broadcasts::new();
|
|
|
|
broker
|
|
|
|
.announce("demo", media.source())
|
|
|
|
.context("failed to announce file source")?;
|
2023-06-16 18:38:19 +00:00
|
|
|
|
2023-06-08 07:01:34 +00:00
|
|
|
// Create a server to actually serve the media
|
2023-06-26 18:51:06 +00:00
|
|
|
let config = relay::ServerConfig {
|
2023-05-23 19:04:27 +00:00
|
|
|
addr: args.addr,
|
|
|
|
cert: args.cert,
|
|
|
|
key: args.key,
|
2023-06-26 18:51:06 +00:00
|
|
|
broker,
|
2023-05-23 19:04:27 +00:00
|
|
|
};
|
2023-04-14 20:32:02 +00:00
|
|
|
|
2023-06-26 18:51:06 +00:00
|
|
|
let server = relay::Server::new(config).context("failed to create server")?;
|
2023-06-08 07:01:34 +00:00
|
|
|
|
|
|
|
// Run all of the above
|
|
|
|
tokio::select! {
|
|
|
|
res = server.run() => res.context("failed to run server"),
|
|
|
|
res = media.run() => res.context("failed to run media source"),
|
|
|
|
res = serve => res.context("failed to run HTTP server"),
|
|
|
|
}
|
2023-05-02 18:05:21 +00:00
|
|
|
}
|
2023-05-25 00:20:44 +00:00
|
|
|
|
|
|
|
// Run a HTTP server using Warp
|
|
|
|
// TODO remove this when Chrome adds support for self-signed certificates using WebTransport
|
2023-06-08 07:01:34 +00:00
|
|
|
async fn serve_http(args: Cli) -> anyhow::Result<()> {
|
2023-05-25 00:20:44 +00:00
|
|
|
// Read the PEM certificate file
|
2023-06-08 07:01:34 +00:00
|
|
|
let crt = fs::File::open(&args.cert)?;
|
|
|
|
let mut crt = io::BufReader::new(crt);
|
2023-05-25 00:20:44 +00:00
|
|
|
|
|
|
|
// Parse the DER certificate
|
|
|
|
let certs = rustls_pemfile::certs(&mut crt)?;
|
|
|
|
let cert = certs.first().expect("no certificate found");
|
|
|
|
|
|
|
|
// Compute the SHA-256 digest
|
|
|
|
let fingerprint = digest(&SHA256, cert.as_ref());
|
|
|
|
let fingerprint = hex::encode(fingerprint.as_ref());
|
2023-06-08 07:01:34 +00:00
|
|
|
let fingerprint = sync::Arc::new(fingerprint);
|
2023-05-25 00:20:44 +00:00
|
|
|
|
|
|
|
let cors = warp::cors().allow_any_origin();
|
|
|
|
|
|
|
|
// What an annoyingly complicated way to serve a static String
|
|
|
|
// I spent a long time trying to find the exact way of cloning and dereferencing the Arc.
|
|
|
|
let routes = warp::path!("fingerprint")
|
|
|
|
.map(move || (*(fingerprint.clone())).clone())
|
|
|
|
.with(cors);
|
|
|
|
|
|
|
|
warp::serve(routes)
|
|
|
|
.tls()
|
|
|
|
.cert_path(args.cert)
|
|
|
|
.key_path(args.key)
|
2023-06-08 07:01:34 +00:00
|
|
|
.run(args.addr)
|
2023-05-25 00:20:44 +00:00
|
|
|
.await;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|