joecalsend/src/lib.rs

160 lines
4.6 KiB
Rust
Raw Normal View History

2025-07-04 00:00:11 +00:00
pub mod discovery;
pub mod error;
2025-07-06 21:15:08 +00:00
pub mod http_server;
2025-07-04 00:00:11 +00:00
pub mod models;
pub mod transfer;
2025-07-04 22:15:52 +00:00
use std::{
collections::HashMap,
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
2025-07-07 22:55:40 +00:00
sync::{Arc, OnceLock},
2025-07-07 22:16:18 +00:00
time::Duration,
2025-07-04 22:15:52 +00:00
};
2025-07-06 23:02:11 +00:00
use models::Device;
use serde::{Deserialize, Serialize};
2025-07-06 23:02:11 +00:00
use tokio::{
net::UdpSocket,
sync::{Mutex, mpsc},
task::JoinHandle,
};
2025-07-06 21:15:08 +00:00
use transfer::Session;
2025-07-04 22:15:52 +00:00
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);
2025-07-04 00:00:11 +00:00
2025-07-07 22:55:40 +00:00
type ShutdownSender = mpsc::Sender<()>;
2025-07-08 00:48:25 +00:00
/// Contains the main network and backend state for an application session.
2025-07-04 22:15:52 +00:00
#[derive(Clone)]
pub struct JoecalState {
2025-07-06 23:02:11 +00:00
pub device: Device,
pub peers: Arc<Mutex<HashMap<String, (SocketAddr, Device)>>>,
2025-07-04 22:15:52 +00:00
pub sessions: Arc<Mutex<HashMap<String, Session>>>, // Session ID to Session
pub running_state: Arc<Mutex<RunningState>>,
pub socket: Arc<UdpSocket>,
2025-07-06 23:02:11 +00:00
pub client: reqwest::Client,
2025-07-07 22:55:40 +00:00
stop_tx: OnceLock<ShutdownSender>,
2025-07-04 22:15:52 +00:00
}
impl JoecalState {
2025-07-06 23:02:11 +00:00
pub async fn new(device: Device) -> crate::error::Result<Self> {
let socket = UdpSocket::bind(LISTENING_SOCKET_ADDR).await?;
2025-07-04 00:00:11 +00:00
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))?;
2025-07-04 00:00:11 +00:00
Ok(Self {
device,
2025-07-08 00:48:25 +00:00
client: reqwest::Client::new(),
socket: socket.into(),
peers: Default::default(),
sessions: Default::default(),
running_state: Default::default(),
2025-07-06 23:02:11 +00:00
stop_tx: Default::default(),
2025-07-04 00:00:11 +00:00
})
}
pub async fn start(
&self,
config: &Config,
2025-07-04 00:00:11 +00:00
) -> crate::error::Result<(JoinHandle<()>, JoinHandle<()>, JoinHandle<()>)> {
let state = self.clone();
let konfig = config.clone();
2025-07-04 00:00:11 +00:00
let server_handle = {
2025-07-07 22:55:40 +00:00
let (tx, shutdown_rx) = mpsc::channel(1);
2025-07-06 23:02:11 +00:00
self.stop_tx.get_or_init(|| tx);
2025-07-04 00:00:11 +00:00
tokio::spawn(async move {
2025-07-07 22:55:40 +00:00
if let Err(e) = state.start_http_server(shutdown_rx, &konfig).await {
2025-07-04 00:00:11 +00:00
eprintln!("HTTP server error: {e}");
}
})
};
let state = self.clone();
let konfig = config.clone();
2025-07-04 00:00:11 +00:00
let udp_handle = {
tokio::spawn(async move {
if let Err(e) = state.listen_multicast(&konfig).await {
2025-07-05 17:12:09 +00:00
eprintln!("UDP listener error: {e}");
2025-07-04 00:00:11 +00:00
}
})
};
let state = self.clone();
let config = config.clone();
2025-07-04 00:00:11 +00:00
let announcement_handle = {
tokio::spawn(async move {
loop {
2025-07-07 22:55:40 +00:00
let rstate = state.running_state.lock().await;
if *rstate == RunningState::Stopping {
break;
}
2025-07-07 04:43:24 +00:00
if let Err(e) = state.announce(None, &config).await {
eprintln!("Announcement error: {e}");
}
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
2025-07-04 00:00:11 +00:00
}
})
};
Ok((server_handle, udp_handle, announcement_handle))
}
2025-07-06 23:02:11 +00:00
pub async fn stop(&self) {
loop {
2025-07-07 22:55:40 +00:00
let mut rstate = self.running_state.lock().await;
*rstate = RunningState::Stopping;
if self
.stop_tx
.get()
.expect("Could not get stop signal transmitter")
.send(())
.await
.is_ok()
{
break;
2025-07-07 22:16:18 +00:00
} else {
tokio::time::sleep(Duration::from_millis(777)).await;
2025-07-06 23:02:11 +00:00
}
}
}
2025-07-04 00:00:11 +00:00
pub async fn refresh_peers(&self) {
let mut peers = self.peers.lock().await;
peers.clear();
}
}
2025-07-06 23:02:11 +00:00
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RunningState {
Running,
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(),
}
}
}