tinyudp/
sync.rs

1//! Sync operations
2use crate::{Error, ReadOptions};
3use std::net::{Ipv4Addr, SocketAddrV4, ToSocketAddrs, UdpSocket};
4
5/// Send a message to the specified address.
6pub fn send(target: impl ToSocketAddrs, message: &[u8]) -> Result<(), Error> {
7    bind()?
8        .send_to(message, target)
9        .map_err(Error::SendFailed)?;
10    Ok(())
11}
12
13/// Send a message to the specified address and read the response.
14pub fn send_and_receive(
15    target: impl ToSocketAddrs,
16    message: &[u8],
17    options: ReadOptions,
18) -> Result<Vec<u8>, Error> {
19    let socket = bind()?;
20
21    // send
22    socket.send_to(message, target).map_err(Error::SendFailed)?;
23    socket
24        .set_read_timeout(Some(options.timeout()))
25        .map_err(|_| Error::TimeoutReached)?;
26
27    // receive
28    let mut buffer = vec![0; options.buffer_size()];
29    let (bytes_read, _) = socket
30        .recv_from(&mut buffer)
31        .map_err(Error::ReceiveFailed)?;
32    let response = buffer[..bytes_read].to_vec();
33    Ok(response)
34}
35
36fn bind() -> Result<UdpSocket, Error> {
37    let address = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
38    UdpSocket::bind(address).map_err(Error::BindFailed)
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use crate::ReadOptions;
45    use anyhow::Result;
46    use std::time::Duration;
47
48    #[test]
49    #[cfg_attr(feature = "ci", ignore)]
50    fn test_send() {
51        let res = send("quake.se:28501", b"\xff\xff\xff\xffstatus");
52        assert!(res.is_ok());
53    }
54
55    #[test]
56    #[cfg_attr(feature = "ci", ignore)]
57    fn test_send_and_receive() -> Result<()> {
58        let response = send_and_receive(
59            "quake.se:28501",
60            b"\xff\xff\xff\xffstatus",
61            ReadOptions::new(Duration::from_secs_f32(0.2), 32 * 1024),
62        )?;
63        assert!(String::from_utf8_lossy(&response).contains("QUAKE.SE KTX"));
64        Ok(())
65    }
66}