torrust_tracker/servers/udp/server/
states.rs1use std::fmt::Debug;
2use std::net::SocketAddr;
3use std::sync::Arc;
4
5use derive_more::derive::Display;
6use derive_more::Constructor;
7use tokio::task::JoinHandle;
8use tracing::{instrument, Level};
9
10use super::spawner::Spawner;
11use super::{Server, UdpError};
12use crate::bootstrap::jobs::Started;
13use crate::core::Tracker;
14use crate::servers::registar::{ServiceRegistration, ServiceRegistrationForm};
15use crate::servers::signals::Halted;
16use crate::servers::udp::server::launcher::Launcher;
17use crate::servers::udp::UDP_TRACKER_LOG_TARGET;
18
19#[allow(clippy::module_name_repetitions)]
21pub type StoppedUdpServer = Server<Stopped>;
22
23#[allow(clippy::module_name_repetitions)]
25pub type RunningUdpServer = Server<Running>;
26
27#[derive(Debug, Display)]
29#[display("Stopped: {spawner}")]
30pub struct Stopped {
31 pub spawner: Spawner,
32}
33
34#[derive(Debug, Display, Constructor)]
36#[display("Running (with local address): {local_addr}")]
37pub struct Running {
38 pub local_addr: SocketAddr,
40 pub halt_task: tokio::sync::oneshot::Sender<Halted>,
41 pub task: JoinHandle<Spawner>,
42}
43
44impl Server<Stopped> {
45 #[must_use]
47 pub fn new(spawner: Spawner) -> Self {
48 Self {
49 state: Stopped { spawner },
50 }
51 }
52
53 #[instrument(skip(self, tracker, form), err, ret(Display, level = Level::INFO))]
65 pub async fn start(self, tracker: Arc<Tracker>, form: ServiceRegistrationForm) -> Result<Server<Running>, std::io::Error> {
66 let (tx_start, rx_start) = tokio::sync::oneshot::channel::<Started>();
67 let (tx_halt, rx_halt) = tokio::sync::oneshot::channel::<Halted>();
68
69 assert!(!tx_halt.is_closed(), "Halt channel for UDP tracker should be open");
70
71 let task = self.state.spawner.spawn_launcher(tracker, tx_start, rx_halt);
73
74 let local_addr = rx_start.await.expect("it should be able to start the service").address;
75
76 form.send(ServiceRegistration::new(local_addr, Launcher::check))
77 .expect("it should be able to send service registration");
78
79 let running_udp_server: Server<Running> = Server {
80 state: Running {
81 local_addr,
82 halt_task: tx_halt,
83 task,
84 },
85 };
86
87 let local_addr = format!("udp://{local_addr}");
88 tracing::trace!(target: UDP_TRACKER_LOG_TARGET, local_addr, "UdpServer<Stopped>::start (running)");
89
90 Ok(running_udp_server)
91 }
92}
93
94impl Server<Running> {
95 #[instrument(skip(self), err, ret(Display, level = Level::INFO))]
107 pub async fn stop(self) -> Result<Server<Stopped>, UdpError> {
108 self.state
109 .halt_task
110 .send(Halted::Normal)
111 .map_err(|e| UdpError::FailedToStartOrStopServer(e.to_string()))?;
112
113 let launcher = self.state.task.await.expect("it should shutdown service");
114
115 let stopped_api_server: Server<Stopped> = Server {
116 state: Stopped { spawner: launcher },
117 };
118
119 Ok(stopped_api_server)
120 }
121}