1use sova_core::extend::{wait_shutdown, BoxFuture, StateMap};
4use sova_core::{BackgroundService, Shutdown};
5use std::net::SocketAddr;
6use std::sync::Arc;
7use tokio::net::UdpSocket;
8
9pub type UdpHandler =
10 Arc<dyn Fn(SocketAddr, Vec<u8>, Arc<UdpSocket>) -> BoxFuture<()> + Send + Sync>;
11
12pub struct UdpService {
14 addr: SocketAddr,
15 handler: UdpHandler,
16 name: String,
17}
18
19impl UdpService {
20 pub fn new<F, Fut>(addr: SocketAddr, handler: F) -> Self
21 where
22 F: Fn(SocketAddr, Vec<u8>, Arc<UdpSocket>) -> Fut + Send + Sync + 'static,
23 Fut: std::future::Future<Output = ()> + Send + 'static,
24 {
25 Self {
26 addr,
27 handler: Arc::new(move |a, b, s| Box::pin(handler(a, b, s))),
28 name: format!("udp:{addr}"),
29 }
30 }
31
32 pub fn echo(addr: SocketAddr) -> Self {
33 Self::new(addr, |_peer, data, sock| async move {
34 let _ = sock.send_to(&data, _peer).await;
35 })
36 }
37}
38
39impl BackgroundService for UdpService {
40 fn name(&self) -> &str {
41 &self.name
42 }
43
44 fn run(
45 self: Box<Self>,
46 _state: Arc<StateMap>,
47 shutdown: Shutdown,
48 ) -> BoxFuture<()> {
49 Box::pin(async move {
50 let sock = match UdpSocket::bind(self.addr).await {
51 Ok(s) => Arc::new(s),
52 Err(e) => {
53 tracing::error!(error = %e, addr = %self.addr, "udp bind failed");
54 return;
55 }
56 };
57 tracing::info!(addr = %self.addr, "udp listening");
58 let mut buf = vec![0u8; 65535];
59 loop {
60 tokio::select! {
61 _ = wait_shutdown(shutdown.clone()) => break,
62 res = sock.recv_from(&mut buf) => {
63 match res {
64 Ok((n, peer)) => {
65 let data = buf[..n].to_vec();
66 (self.handler)(peer, data, Arc::clone(&sock)).await;
67 }
68 Err(e) => {
69 tracing::debug!(error = %e, "udp recv");
70 break;
71 }
72 }
73 }
74 }
75 }
76 })
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use std::time::Duration;
84
85 #[tokio::test]
86 async fn echo_roundtrip() {
87 let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
88 let sock = UdpSocket::bind(addr).await.unwrap();
89 let addr = sock.local_addr().unwrap();
90 drop(sock);
91
92 let (tx, shutdown) = sova_core::shutdown_channel();
93 let svc = Box::new(UdpService::echo(addr));
94 let handle = tokio::spawn(svc.run(Arc::new(StateMap::new()), shutdown));
95
96 tokio::time::sleep(Duration::from_millis(50)).await;
97 let client = UdpSocket::bind("127.0.0.1:0").await.unwrap();
98 client.send_to(b"ping", addr).await.unwrap();
99 let mut buf = [0u8; 16];
100 let (n, _) = tokio::time::timeout(Duration::from_secs(1), client.recv_from(&mut buf))
101 .await
102 .unwrap()
103 .unwrap();
104 assert_eq!(&buf[..n], b"ping");
105 let _ = tx.send(true);
106 let _ = handle.await;
107 }
108
109 #[tokio::test]
110 async fn shutdown_stops_recv_loop() {
111 let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
112 let sock = UdpSocket::bind(addr).await.unwrap();
113 let addr = sock.local_addr().unwrap();
114 drop(sock);
115
116 let (tx, shutdown) = sova_core::shutdown_channel();
117 let svc = Box::new(UdpService::echo(addr));
118 let handle = tokio::spawn(svc.run(Arc::new(StateMap::new()), shutdown));
119
120 tokio::time::sleep(Duration::from_millis(50)).await;
121 let _ = tx.send(true);
122 tokio::time::timeout(Duration::from_secs(1), handle)
123 .await
124 .expect("service did not stop after shutdown")
125 .expect("join error");
126 }
127
128 #[tokio::test]
129 async fn max_datagram_size_accepted() {
130 let addr: SocketAddr = "127.0.0.1:0".parse().unwrap();
131 let sock = UdpSocket::bind(addr).await.unwrap();
132 let addr = sock.local_addr().unwrap();
133 drop(sock);
134
135 let (tx, shutdown) = sova_core::shutdown_channel();
136 let svc = Box::new(UdpService::echo(addr));
137 let handle = tokio::spawn(svc.run(Arc::new(StateMap::new()), shutdown));
138
139 tokio::time::sleep(Duration::from_millis(50)).await;
140 let client = UdpSocket::bind("127.0.0.1:0").await.unwrap();
141 let payload = vec![0xABu8; 8192];
142 client.send_to(&payload, addr).await.unwrap();
143 let mut buf = vec![0u8; 8192];
144 let (n, _) = tokio::time::timeout(Duration::from_secs(1), client.recv_from(&mut buf))
145 .await
146 .unwrap()
147 .unwrap();
148 assert_eq!(&buf[..n], &payload[..]);
149 let _ = tx.send(true);
150 let _ = handle.await;
151 }
152}