mod driver;
mod stun;
mod wire;
use std::net::IpAddr;
use std::sync::Arc;
use tokio::sync::{mpsc, oneshot, watch};
use crate::database::Inner;
use crate::model::DeviceId;
use crate::remote::RemoteEntry;
pub const DEFAULT_STUN_SERVER: &str = "stun.l.google.com:19302";
#[derive(Clone, Debug)]
pub struct P2pConfig {
pub(crate) stun_servers: Vec<String>,
pub(crate) host: Option<IpAddr>,
pub(crate) inline_segments: bool,
}
impl Default for P2pConfig {
fn default() -> Self {
Self {
stun_servers: vec![DEFAULT_STUN_SERVER.to_owned()],
host: None,
inline_segments: true,
}
}
}
impl P2pConfig {
pub fn new() -> Self {
Self::default()
}
pub fn stun_servers(mut self, servers: Vec<String>) -> Self {
self.stun_servers = servers;
self
}
pub fn host_address(mut self, ip: IpAddr) -> Self {
self.host = Some(ip);
self
}
pub fn inline_segments(mut self, on: bool) -> Self {
self.inline_segments = on;
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum PeerState {
Signaling,
Connected,
Failed,
Disconnected,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct PeerStatus {
pub device_id: DeviceId,
pub state: PeerState,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct P2pStatus {
pub running: bool,
pub discovering: bool,
pub peers: Vec<PeerStatus>,
}
impl P2pStatus {
pub fn connected(&self) -> usize {
self.peers
.iter()
.filter(|p| p.state == PeerState::Connected)
.count()
}
}
pub(crate) enum Cmd {
Announce { seq: u64, bytes: Vec<u8> },
Signals(Vec<RemoteEntry>),
Stop(oneshot::Sender<()>),
}
pub(crate) struct Link {
cmd: mpsc::UnboundedSender<Cmd>,
status: watch::Receiver<P2pStatus>,
}
impl Link {
pub(crate) fn send(&self, cmd: Cmd) {
let _ = self.cmd.send(cmd);
}
pub(crate) fn status(&self) -> P2pStatus {
self.status.borrow().clone()
}
}
pub(crate) fn start(inner: &Arc<Inner>, config: P2pConfig) -> tokio::task::JoinHandle<()> {
let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();
let (status_tx, status_rx) = watch::channel(P2pStatus {
running: true,
discovering: true,
peers: Vec::new(),
});
inner.set_p2p(Some(Link {
cmd: cmd_tx,
status: status_rx,
}));
tokio::spawn(driver::run(inner.clone(), config, cmd_rx, status_tx))
}
pub(crate) async fn stop(inner: &Arc<Inner>, task: tokio::task::JoinHandle<()>) {
if let Some(link) = inner.take_p2p() {
let (tx, rx) = oneshot::channel();
link.send(Cmd::Stop(tx));
let _ = tokio::time::timeout(std::time::Duration::from_secs(5), rx).await;
}
task.abort();
let _ = task.await;
}