Skip to main content

actix_server/
socket.rs

1pub(crate) use std::net::{
2    SocketAddr as StdSocketAddr, TcpListener as StdTcpListener, ToSocketAddrs,
3};
4use std::{fmt, io};
5
6use actix_rt::net::TcpStream;
7pub(crate) use mio::net::TcpListener as MioTcpListener;
8use mio::{event::Source, Interest, Registry, Token};
9#[cfg(unix)]
10pub(crate) use {
11    mio::net::UnixListener as MioUnixListener, std::os::unix::net::UnixListener as StdUnixListener,
12};
13
14use crate::builder::MpTcp;
15
16pub(crate) enum MioListener {
17    Tcp(MioTcpListener),
18    #[cfg(unix)]
19    Uds(MioUnixListener),
20}
21
22impl MioListener {
23    pub(crate) fn local_addr(&self) -> SocketAddr {
24        match *self {
25            MioListener::Tcp(ref lst) => lst
26                .local_addr()
27                .map(SocketAddr::Tcp)
28                .unwrap_or(SocketAddr::Unknown),
29            #[cfg(unix)]
30            MioListener::Uds(ref lst) => lst
31                .local_addr()
32                .map(SocketAddr::Uds)
33                .unwrap_or(SocketAddr::Unknown),
34        }
35    }
36
37    pub(crate) fn accept(&self) -> io::Result<MioStream> {
38        match *self {
39            MioListener::Tcp(ref lst) => lst.accept().map(|(stream, _)| MioStream::Tcp(stream)),
40            #[cfg(unix)]
41            MioListener::Uds(ref lst) => lst.accept().map(|(stream, _)| MioStream::Uds(stream)),
42        }
43    }
44}
45
46impl Source for MioListener {
47    fn register(
48        &mut self,
49        registry: &Registry,
50        token: Token,
51        interests: Interest,
52    ) -> io::Result<()> {
53        match *self {
54            MioListener::Tcp(ref mut lst) => lst.register(registry, token, interests),
55            #[cfg(unix)]
56            MioListener::Uds(ref mut lst) => lst.register(registry, token, interests),
57        }
58    }
59
60    fn reregister(
61        &mut self,
62        registry: &Registry,
63        token: Token,
64        interests: Interest,
65    ) -> io::Result<()> {
66        match *self {
67            MioListener::Tcp(ref mut lst) => lst.reregister(registry, token, interests),
68            #[cfg(unix)]
69            MioListener::Uds(ref mut lst) => lst.reregister(registry, token, interests),
70        }
71    }
72
73    fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
74        match *self {
75            MioListener::Tcp(ref mut lst) => lst.deregister(registry),
76            #[cfg(unix)]
77            MioListener::Uds(ref mut lst) => lst.deregister(registry),
78        }
79    }
80}
81
82impl From<StdTcpListener> for MioListener {
83    fn from(lst: StdTcpListener) -> Self {
84        MioListener::Tcp(MioTcpListener::from_std(lst))
85    }
86}
87
88#[cfg(unix)]
89impl From<StdUnixListener> for MioListener {
90    fn from(lst: StdUnixListener) -> Self {
91        MioListener::Uds(MioUnixListener::from_std(lst))
92    }
93}
94
95impl fmt::Debug for MioListener {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        match *self {
98            MioListener::Tcp(ref lst) => write!(f, "{lst:?}"),
99            #[cfg(unix)]
100            MioListener::Uds(ref lst) => write!(f, "{lst:?}"),
101        }
102    }
103}
104
105impl fmt::Display for MioListener {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        match *self {
108            MioListener::Tcp(ref lst) => write!(f, "{lst:?}"),
109            #[cfg(unix)]
110            MioListener::Uds(ref lst) => write!(f, "{lst:?}"),
111        }
112    }
113}
114
115pub(crate) enum SocketAddr {
116    Unknown,
117    Tcp(StdSocketAddr),
118    #[cfg(unix)]
119    Uds(std::os::unix::net::SocketAddr),
120}
121
122impl fmt::Display for SocketAddr {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        match *self {
125            Self::Unknown => write!(f, "Unknown SocketAddr"),
126            Self::Tcp(ref addr) => write!(f, "{addr}"),
127            #[cfg(unix)]
128            Self::Uds(ref addr) => write!(f, "{addr:?}"),
129        }
130    }
131}
132
133impl fmt::Debug for SocketAddr {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135        match *self {
136            Self::Unknown => write!(f, "Unknown SocketAddr"),
137            Self::Tcp(ref addr) => write!(f, "{addr:?}"),
138            #[cfg(unix)]
139            Self::Uds(ref addr) => write!(f, "{addr:?}"),
140        }
141    }
142}
143
144#[derive(Debug)]
145pub enum MioStream {
146    Tcp(mio::net::TcpStream),
147    #[cfg(unix)]
148    Uds(mio::net::UnixStream),
149}
150
151/// Helper trait for converting a Mio stream into a Tokio stream.
152pub trait FromStream: Sized {
153    /// Creates stream from a `mio` stream.
154    fn from_mio(sock: MioStream) -> io::Result<Self>;
155}
156
157#[cfg(windows)]
158mod win_impl {
159    use std::os::windows::io::{FromRawSocket, IntoRawSocket};
160
161    use super::*;
162
163    // TODO: This is a workaround and we need an efficient way to convert between Mio and Tokio stream
164    impl FromStream for TcpStream {
165        fn from_mio(sock: MioStream) -> io::Result<Self> {
166            match sock {
167                MioStream::Tcp(mio) => {
168                    let raw = IntoRawSocket::into_raw_socket(mio);
169                    // SAFETY: This is an in-place conversion from Mio stream to Tokio stream.
170                    TcpStream::from_std(unsafe { FromRawSocket::from_raw_socket(raw) })
171                }
172            }
173        }
174    }
175}
176
177#[cfg(unix)]
178mod unix_impl {
179    use std::os::unix::io::{FromRawFd, IntoRawFd};
180
181    use actix_rt::net::UnixStream;
182
183    use super::*;
184
185    // HACK: This is a workaround and we need an efficient way to convert between Mio and Tokio stream
186    impl FromStream for TcpStream {
187        fn from_mio(sock: MioStream) -> io::Result<Self> {
188            match sock {
189                MioStream::Tcp(mio) => {
190                    let raw = IntoRawFd::into_raw_fd(mio);
191                    // SAFETY: This is an in-place conversion from Mio stream to Tokio stream.
192                    TcpStream::from_std(unsafe { FromRawFd::from_raw_fd(raw) })
193                }
194                MioStream::Uds(_) => {
195                    panic!("Should not happen, bug in server impl");
196                }
197            }
198        }
199    }
200
201    // HACK: This is a workaround and we need an efficient way to convert between Mio and Tokio stream
202    impl FromStream for UnixStream {
203        fn from_mio(sock: MioStream) -> io::Result<Self> {
204            match sock {
205                MioStream::Tcp(_) => panic!("Should not happen, bug in server impl"),
206                MioStream::Uds(mio) => {
207                    let raw = IntoRawFd::into_raw_fd(mio);
208                    // SAFETY: This is an in-place conversion from Mio stream to Tokio stream.
209                    UnixStream::from_std(unsafe { FromRawFd::from_raw_fd(raw) })
210                }
211            }
212        }
213    }
214}
215
216pub(crate) fn create_mio_tcp_listener(
217    addr: StdSocketAddr,
218    backlog: u32,
219    mptcp: &MpTcp,
220) -> io::Result<MioTcpListener> {
221    use socket2::{Domain, Protocol, Socket, Type};
222
223    #[cfg(not(target_os = "linux"))]
224    let protocol = Protocol::TCP;
225    #[cfg(target_os = "linux")]
226    let protocol = if matches!(mptcp, MpTcp::Disabled) {
227        Protocol::TCP
228    } else {
229        Protocol::MPTCP
230    };
231
232    let socket = match Socket::new(Domain::for_address(addr), Type::STREAM, Some(protocol)) {
233        Ok(sock) => sock,
234
235        Err(err) if matches!(mptcp, MpTcp::TcpFallback) => {
236            tracing::warn!("binding socket as MPTCP failed: {err}");
237            tracing::warn!("falling back to TCP");
238            Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?
239        }
240
241        Err(err) => return Err(err),
242    };
243
244    socket.set_reuse_address(true)?;
245    socket.set_nonblocking(true)?;
246    socket.bind(&addr.into())?;
247    socket.listen(backlog as i32)?;
248
249    Ok(MioTcpListener::from_std(StdTcpListener::from(socket)))
250}
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255
256    #[test]
257    fn socket_addr() {
258        let addr = SocketAddr::Tcp("127.0.0.1:8080".parse().unwrap());
259        assert!(format!("{addr:?}").contains("127.0.0.1:8080"));
260        assert_eq!(format!("{addr}"), "127.0.0.1:8080");
261
262        let addr: StdSocketAddr = "127.0.0.1:0".parse().unwrap();
263        let lst = create_mio_tcp_listener(addr, 128, &MpTcp::Disabled).unwrap();
264        let lst = MioListener::Tcp(lst);
265        assert!(format!("{lst:?}").contains("TcpListener"));
266        assert!(format!("{lst}").contains("127.0.0.1"));
267    }
268
269    #[test]
270    #[cfg(unix)]
271    fn uds() {
272        let _ = std::fs::remove_file("/tmp/sock.xxxxx");
273        if let Ok(socket) = MioUnixListener::bind("/tmp/sock.xxxxx") {
274            let addr = socket.local_addr().expect("Couldn't get local address");
275            let a = SocketAddr::Uds(addr);
276            assert!(format!("{a:?}").contains("/tmp/sock.xxxxx"));
277            assert!(format!("{a}").contains("/tmp/sock.xxxxx"));
278
279            let lst = MioListener::Uds(socket);
280            assert!(format!("{lst:?}").contains("/tmp/sock.xxxxx"));
281            assert!(format!("{lst}").contains("/tmp/sock.xxxxx"));
282        }
283    }
284
285    #[test]
286    #[cfg(unix)]
287    fn uds_deregister_does_not_unlink_socket_file() {
288        use std::sync::atomic::{AtomicUsize, Ordering};
289
290        static CNT: AtomicUsize = AtomicUsize::new(0);
291
292        let name = format!(
293            "actix-server-test-uds-{}-{}.sock",
294            std::process::id(),
295            CNT.fetch_add(1, Ordering::Relaxed)
296        );
297        let path = std::env::temp_dir().join(name);
298
299        let _ = std::fs::remove_file(&path);
300
301        let mut lst = MioListener::Uds(MioUnixListener::bind(&path).unwrap());
302        assert!(path.exists());
303
304        let poll = mio::Poll::new().unwrap();
305        poll.registry()
306            .register(&mut lst, mio::Token(0), mio::Interest::READABLE)
307            .unwrap();
308        poll.registry().deregister(&mut lst).unwrap();
309
310        // Regression test for https://github.com/actix/actix-net/issues/364.
311        assert!(path.exists());
312    }
313}