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
151pub trait FromStream: Sized {
153 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 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 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 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 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 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 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 assert!(path.exists());
312 }
313}