grafos_net/
listener.rs

1//! Fabric-aware TCP listener on a leased network interface.
2
3use std::io;
4use std::net::{SocketAddr, TcpListener, TcpStream};
5
6use grafos_std::net::NetLease;
7
8/// A TCP listener bound to a leased fabric network interface.
9///
10/// Accepts connections on a specific port, with the bandwidth guarantee
11/// from the underlying [`NetLease`]. On native targets, delegates to
12/// `std::net::TcpListener`.
13///
14/// # Example
15///
16/// ```rust
17/// use grafos_net::FabricListener;
18/// use grafos_std::net::NetBuilder;
19///
20/// # grafos_std::host::reset_mock();
21/// # grafos_std::host::mock_set_net_interface("lo", 1_000_000_000);
22/// let lease = NetBuilder::new().acquire()?;
23/// let listener = FabricListener::bind(lease, 0)?;
24/// let addr = listener.local_addr()?;
25/// println!("listening on {}", addr);
26/// # Ok::<(), Box<dyn std::error::Error>>(())
27/// ```
28pub struct FabricListener {
29    inner: TcpListener,
30    _lease: NetLease,
31}
32
33impl FabricListener {
34    /// Bind a listener to the given port on the leased interface.
35    ///
36    /// On native targets, binds to `0.0.0.0:port`.
37    ///
38    /// # Errors
39    ///
40    /// Returns an I/O error if the bind fails.
41    pub fn bind(lease: NetLease, port: u16) -> io::Result<Self> {
42        let addr = SocketAddr::from(([0, 0, 0, 0], port));
43        let inner = TcpListener::bind(addr)?;
44        Ok(FabricListener {
45            inner,
46            _lease: lease,
47        })
48    }
49
50    /// Accept a new incoming connection.
51    ///
52    /// Returns the connected stream and the peer's address.
53    ///
54    /// # Errors
55    ///
56    /// Returns an I/O error if the accept fails.
57    pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
58        self.inner.accept()
59    }
60
61    /// Returns the local address this listener is bound to.
62    ///
63    /// # Errors
64    ///
65    /// Returns an I/O error if the address cannot be retrieved.
66    pub fn local_addr(&self) -> io::Result<SocketAddr> {
67        self.inner.local_addr()
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use grafos_std::host;
75    use grafos_std::net::NetBuilder;
76    use std::io::{Read, Write};
77
78    #[test]
79    fn listener_bind_and_accept() {
80        host::reset_mock();
81        host::mock_set_net_interface("lo", 1_000_000_000);
82
83        let lease = NetBuilder::new().acquire().expect("acquire");
84        let listener = FabricListener::bind(lease, 0).expect("bind");
85        let addr = listener.local_addr().expect("local_addr");
86
87        let handle = std::thread::spawn(move || {
88            let mut stream = std::net::TcpStream::connect(addr).expect("connect");
89            stream.write_all(b"fabric hello").expect("write");
90        });
91
92        let (mut conn, _peer) = listener.accept().expect("accept");
93        let mut buf = vec![0u8; 64];
94        let n = conn.read(&mut buf).expect("read");
95        assert_eq!(&buf[..n], b"fabric hello");
96
97        handle.join().expect("join");
98    }
99}