pub mod config;
pub mod packet;
pub mod registry;
pub mod route;
mod middleman;
mod register;
use std::io;
use std::net::SocketAddr;
pub use config::WakeServerConfig;
#[derive(Debug, thiserror::Error)]
pub enum WakeServerError {
#[error("wake server failed to bind to {addr}: {source}")]
Bind { addr: SocketAddr, source: io::Error },
#[error("wake server socket error: {0}")]
Socket(#[from] io::Error),
#[error("BcUdp framing error: {0}")]
Frame(#[from] bairelay_neolink_core::Error),
#[error("BcUdp XML error: {0}")]
Xml(#[from] quick_xml::de::DeError),
#[error("invalid wake-server config: {0}")]
Config(String),
#[error("wake server received unexpected BcUdp packet kind: {kind}")]
UnexpectedPacketKind { kind: &'static str },
}
use std::sync::Arc;
use tokio::net::UdpSocket;
use tokio_util::sync::CancellationToken;
pub fn make_registry() -> Arc<registry::CameraRegistry> {
Arc::new(registry::CameraRegistry::new())
}
pub async fn run_with_sockets(
cfg: config::RuntimeConfig,
registry: Arc<registry::CameraRegistry>,
middleman_sock: UdpSocket,
register_sock: UdpSocket,
cancel: CancellationToken,
) -> Result<(), WakeServerError> {
let middleman_sock = Arc::new(middleman_sock);
let register_sock = Arc::new(register_sock);
let anchors = Arc::new(registry::SessionAnchors::new());
let register_local = register_sock
.local_addr()
.map_err(WakeServerError::Socket)?;
let mid = {
let sock = Arc::clone(&middleman_sock);
let cancel = cancel.clone();
let bind = cfg.bind;
let anchors = Arc::clone(&anchors);
tokio::spawn(
async move { middleman::run(sock, register_local, bind, anchors, cancel).await },
)
};
let reg = {
let sock = Arc::clone(®ister_sock);
let cancel = cancel.clone();
let registry = Arc::clone(®istry);
let anchors = Arc::clone(&anchors);
let cfg = cfg.clone();
tokio::spawn(async move { register::run(sock, registry, anchors, cfg, cancel).await })
};
let res: Result<(), WakeServerError> = tokio::select! {
r = mid => match r {
Ok(inner) => inner,
Err(join_err) => Err(WakeServerError::Config(format!("middleman join: {join_err}"))),
},
r = reg => match r {
Ok(inner) => inner,
Err(join_err) => Err(WakeServerError::Config(format!("register join: {join_err}"))),
},
};
cancel.cancel();
res
}
pub async fn run(
cfg: config::RuntimeConfig,
registry: Arc<registry::CameraRegistry>,
cancel: CancellationToken,
) -> Result<(), WakeServerError> {
let middleman_addr = std::net::SocketAddr::new(cfg.bind, cfg.middleman_port);
let register_addr = std::net::SocketAddr::new(cfg.bind, cfg.register_port);
let middleman =
UdpSocket::bind(middleman_addr)
.await
.map_err(|source| WakeServerError::Bind {
addr: middleman_addr,
source,
})?;
let register =
UdpSocket::bind(register_addr)
.await
.map_err(|source| WakeServerError::Bind {
addr: register_addr,
source,
})?;
run_with_sockets(cfg, registry, middleman, register, cancel).await
}
#[cfg(test)]
mod error_tests {
use super::*;
use std::net::Ipv4Addr;
#[test]
fn bind_error_displays_addr_and_source() {
let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 9999);
let source = io::Error::new(io::ErrorKind::AddrInUse, "in use");
let err = WakeServerError::Bind { addr, source };
let s = format!("{err}");
assert!(s.contains("9999"));
assert!(s.contains("in use"));
}
}