#[macro_use]
extern crate tracing;
use std::io::ErrorKind;
use std::net::UdpSocket;
use std::process;
use std::thread;
use std::time::Instant;
use rouille::Server;
use rouille::{Request, Response};
use str0m::change::SdpOffer;
use str0m::crypto::from_feature_flags;
use str0m::net::Protocol;
use str0m::net::Receive;
use str0m::{Candidate, Event, IceConnectionState, Input, Output, Rtc, RtcConfig, RtcError};
mod util;
fn init_log() {
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
let env_filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("http_post=debug,str0m=debug,dimpl=debug"));
tracing_subscriber::registry()
.with(fmt::layer())
.with(env_filter)
.init();
}
pub fn main() {
init_log();
from_feature_flags().install_process_default();
let certificate = include_bytes!("cer.pem").to_vec();
let private_key = include_bytes!("key.pem").to_vec();
let host_addr = util::select_host_address();
let server = Server::new_ssl("0.0.0.0:3000", web_request, certificate, private_key)
.expect("starting the web server");
let port = server.server_addr().port();
info!("Connect a browser to https://{:?}:{:?}", host_addr, port);
server.run();
}
fn web_request(request: &Request) -> Response {
if request.method() == "GET" {
return Response::html(include_str!("http-post.html"));
}
let mut data = request.data().expect("body to be available");
let offer: SdpOffer = serde_json::from_reader(&mut data).expect("serialized offer");
let mut rtc = RtcConfig::new()
.build(Instant::now());
let addr = util::select_host_address();
let socket = UdpSocket::bind(format!("{addr}:0")).expect("binding a random UDP port");
let addr = socket.local_addr().expect("a local socket address");
let candidate = Candidate::host(addr, "udp").expect("a host candidate");
rtc.add_local_candidate(candidate).unwrap();
let answer = rtc
.sdp_api()
.accept_offer(offer)
.expect("offer to be accepted");
thread::spawn(|| {
if let Err(e) = run(rtc, socket) {
eprintln!("Exited: {e:?}");
process::exit(1);
}
});
let body = serde_json::to_vec(&answer).expect("answer to serialize");
Response::from_data("application/json", body)
}
fn run(mut rtc: Rtc, socket: UdpSocket) -> Result<(), RtcError> {
let mut buf = Vec::new();
loop {
let timeout = match rtc.poll_output()? {
Output::Timeout(v) => v,
Output::Transmit(v) => {
socket.send_to(&v.contents, v.destination)?;
continue;
}
Output::Event(v) => {
if v == Event::IceConnectionStateChange(IceConnectionState::Disconnected) {
return Ok(());
}
continue;
}
};
let timeout = timeout - Instant::now();
if timeout.is_zero() {
rtc.handle_input(Input::Timeout(Instant::now()))?;
continue;
}
socket.set_read_timeout(Some(timeout))?;
buf.resize(2000, 0);
let input = match socket.recv_from(&mut buf) {
Ok((n, source)) => {
buf.truncate(n);
Input::Receive(
Instant::now(),
Receive {
proto: Protocol::Udp,
source,
destination: socket.local_addr().unwrap(),
contents: buf.as_slice().try_into()?,
},
)
}
Err(e) => match e.kind() {
ErrorKind::WouldBlock | ErrorKind::TimedOut => Input::Timeout(Instant::now()),
_ => return Err(e.into()),
},
};
rtc.handle_input(input)?;
}
}