std_embedded_nal/
tcp.rs

1use crate::SocketState;
2use embedded_nal::nb;
3use embedded_nal::{TcpClientStack, TcpFullStack};
4use std::io::{Error, Read, Write};
5use std::net::{IpAddr, Ipv6Addr, SocketAddr, TcpListener, TcpStream};
6
7#[derive(Debug)]
8pub struct TcpError(pub Error);
9
10impl From<Error> for TcpError {
11    fn from(e: Error) -> Self {
12        Self(e)
13    }
14}
15
16impl TcpError {
17    fn to_nb(e: Error) -> nb::Error<Self> {
18        use std::io::ErrorKind::{TimedOut, WouldBlock};
19        match e.kind() {
20            WouldBlock | TimedOut => nb::Error::WouldBlock,
21            _ => nb::Error::Other(Self(e)),
22        }
23    }
24}
25
26impl embedded_nal::TcpError for TcpError {
27    fn kind(&self) -> embedded_nal::TcpErrorKind {
28        match self.0.kind() {
29            std::io::ErrorKind::BrokenPipe => embedded_nal::TcpErrorKind::PipeClosed,
30            _ => embedded_nal::TcpErrorKind::Other,
31        }
32    }
33}
34
35pub struct TcpSocket {
36    state: SocketState<TcpStream, TcpListener>,
37}
38
39impl TcpSocket {
40    fn new() -> Self {
41        Self {
42            state: SocketState::new(),
43        }
44    }
45
46    fn connected(s: TcpStream) -> Self {
47        Self {
48            state: SocketState::Connected(s),
49        }
50    }
51
52    /// Return the raw file descriptor underlying the current socket.
53    ///
54    /// This is primarily intended for use with `select` style mechanisms: Any of the `nb` methods
55    /// of the socket's traits, once returning [`nb::Error::WouldBlock`], will only make progress
56    /// if data or buffer is available on that file descriptor.
57    ///
58    /// If this returns `None`, then the socket is still in a state where it doesn't even have an
59    /// underlying operating system socket, and needs further operations ([UdpFullStack::bind] or
60    /// [UdpClientStack::connect]) to be performed before it can be waited on. (Then again, a
61    /// socket that doesn't return a raw file descriptor should never return `WouldBlock`). Being
62    /// fallible, this is a method and not a trait implemntation of [std::os::fd::AsRawFd].
63    #[cfg(any(unix, target_os = "hermit", target_os = "wasi"))]
64    pub fn as_raw_fd(&self) -> Option<std::os::fd::RawFd> {
65        use std::os::fd::AsRawFd;
66
67        match &self.state {
68            SocketState::Connected(s) => Some(s.as_raw_fd()),
69            SocketState::Bound(s) => Some(s.as_raw_fd()),
70            SocketState::Building => None,
71        }
72    }
73}
74
75impl TcpClientStack for crate::Stack {
76    type TcpSocket = TcpSocket;
77    type Error = TcpError;
78
79    fn socket(&mut self) -> Result<TcpSocket, Self::Error> {
80        Ok(TcpSocket::new())
81    }
82
83    fn connect(
84        &mut self,
85        socket: &mut TcpSocket,
86        remote: SocketAddr,
87    ) -> nb::Result<(), Self::Error> {
88        let soc = TcpStream::connect(remote).map_err(Self::Error::from)?;
89
90        soc.set_nonblocking(true).map_err(Self::Error::from)?;
91
92        socket.state = SocketState::Connected(soc);
93        Ok(())
94    }
95
96    fn send(&mut self, socket: &mut TcpSocket, buffer: &[u8]) -> nb::Result<usize, Self::Error> {
97        let socket = socket.state.get_running().map_err(Self::Error::from)?;
98        socket.write(buffer).map_err(Self::Error::to_nb)
99    }
100
101    fn receive(
102        &mut self,
103        socket: &mut TcpSocket,
104        buffer: &mut [u8],
105    ) -> nb::Result<usize, Self::Error> {
106        let socket = socket.state.get_running().map_err(Self::Error::from)?;
107        socket.read(buffer).map_err(Self::Error::to_nb)
108    }
109
110    fn close(&mut self, _: TcpSocket) -> Result<(), Self::Error> {
111        // No-op: Socket gets closed when it is freed
112        //
113        // Could wrap it in an Option, but really that'll only make things messier; users will
114        // probably drop the socket anyway after closing, and can't expect it to be usable with
115        // this API.
116        Ok(())
117    }
118}
119
120impl TcpFullStack for crate::Stack {
121    fn bind(&mut self, socket: &mut TcpSocket, port: u16) -> Result<(), Self::Error> {
122        let anyaddressthisport = SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), port);
123
124        let sock = TcpListener::bind(anyaddressthisport)?;
125
126        sock.set_nonblocking(true)?;
127
128        socket.state = SocketState::Bound(sock);
129        Ok(())
130    }
131
132    fn listen(&mut self, _: &mut TcpSocket) -> Result<(), Self::Error> {
133        // Seems to be implied in listener creation
134        Ok(())
135    }
136
137    fn accept(
138        &mut self,
139        socket: &mut TcpSocket,
140    ) -> nb::Result<(TcpSocket, SocketAddr), Self::Error> {
141        let sock = socket.state.get_bound().map_err(Self::Error::from)?;
142        sock.accept()
143            .map_err(Self::Error::to_nb)
144            .map(|(s, a)| (TcpSocket::connected(s), a))
145    }
146}