133 lines
3.8 KiB
Rust
133 lines
3.8 KiB
Rust
pub mod discovery;
|
|
pub mod error;
|
|
pub mod http_server;
|
|
pub mod models;
|
|
pub mod transfer;
|
|
|
|
use std::{
|
|
collections::HashMap,
|
|
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
|
|
sync::Arc,
|
|
};
|
|
|
|
use models::device::DeviceInfo;
|
|
use serde::{Deserialize, Serialize};
|
|
use tokio::{net::UdpSocket, sync::Mutex, task::JoinHandle};
|
|
use transfer::Session;
|
|
|
|
pub const DEFAULT_PORT: u16 = 53317;
|
|
pub const MULTICAST_IP: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 167);
|
|
pub const LISTENING_SOCKET_ADDR: SocketAddrV4 =
|
|
SocketAddrV4::new(Ipv4Addr::from_bits(0), DEFAULT_PORT);
|
|
|
|
#[derive(Clone)]
|
|
pub struct JoecalState {
|
|
pub device: DeviceInfo,
|
|
pub peers: Arc<Mutex<HashMap<String, (SocketAddr, DeviceInfo)>>>,
|
|
pub sessions: Arc<Mutex<HashMap<String, Session>>>, // Session ID to Session
|
|
pub running_state: Arc<Mutex<RunningState>>,
|
|
pub socket: Arc<UdpSocket>,
|
|
pub client: Arc<reqwest::Client>,
|
|
}
|
|
|
|
impl JoecalState {
|
|
pub async fn new(device: DeviceInfo) -> crate::error::Result<Self> {
|
|
let socket = UdpSocket::bind(LISTENING_SOCKET_ADDR).await?;
|
|
socket.set_multicast_loop_v4(true)?;
|
|
socket.set_multicast_ttl_v4(2)?; // one hop out from localnet
|
|
socket.join_multicast_v4(MULTICAST_IP, Ipv4Addr::from_bits(0))?;
|
|
|
|
Ok(Self {
|
|
device,
|
|
peers: Default::default(),
|
|
sessions: Default::default(),
|
|
running_state: Default::default(),
|
|
socket: socket.into(),
|
|
client: reqwest::Client::new().into(),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum RunningState {
|
|
Running,
|
|
Sending,
|
|
Receiving,
|
|
Stopping,
|
|
}
|
|
|
|
impl Default for RunningState {
|
|
fn default() -> Self {
|
|
Self::Running
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Config {
|
|
pub multicast_addr: SocketAddrV4,
|
|
pub port: u16,
|
|
pub download_dir: String,
|
|
}
|
|
|
|
impl Default for Config {
|
|
fn default() -> Self {
|
|
let home = std::env::home_dir().unwrap_or("/tmp".into());
|
|
let dd = home.join("joecalsend-downloads");
|
|
Self {
|
|
multicast_addr: SocketAddrV4::new(MULTICAST_IP, DEFAULT_PORT),
|
|
port: DEFAULT_PORT,
|
|
download_dir: dd.to_string_lossy().into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl JoecalState {
|
|
pub async fn start(
|
|
&self,
|
|
config: &Config,
|
|
) -> crate::error::Result<(JoinHandle<()>, JoinHandle<()>, JoinHandle<()>)> {
|
|
let state = self.clone();
|
|
let konfig = config.clone();
|
|
let server_handle = {
|
|
tokio::spawn(async move {
|
|
if let Err(e) = state.start_http_server(&konfig).await {
|
|
eprintln!("HTTP server error: {e}");
|
|
}
|
|
})
|
|
};
|
|
let state = self.clone();
|
|
let konfig = config.clone();
|
|
let udp_handle = {
|
|
tokio::spawn(async move {
|
|
if let Err(e) = state.listen_multicast(&konfig).await {
|
|
eprintln!("UDP listener error: {e}");
|
|
}
|
|
})
|
|
};
|
|
|
|
let state = self.clone();
|
|
let config = config.clone();
|
|
let announcement_handle = {
|
|
tokio::spawn(async move {
|
|
loop {
|
|
if let Err(e) = state.announce(None, &config).await {
|
|
eprintln!("Announcement error: {e}");
|
|
}
|
|
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
|
if let Ok(lock) = state.running_state.try_lock()
|
|
&& *lock == RunningState::Stopping
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
})
|
|
};
|
|
|
|
Ok((server_handle, udp_handle, announcement_handle))
|
|
}
|
|
|
|
pub async fn refresh_peers(&self) {
|
|
let mut peers = self.peers.lock().await;
|
|
peers.clear();
|
|
}
|
|
}
|