Skip to main content

ntex_net/
lib.rs

1//! Utility for async runtime abstraction
2#![deny(clippy::pedantic)]
3#![allow(
4    clippy::clone_on_copy,
5    clippy::cast_possible_truncation,
6    clippy::missing_fields_in_debug,
7    clippy::must_use_candidate,
8    clippy::missing_errors_doc,
9    clippy::missing_panics_doc,
10    clippy::unused_async_trait_impl
11)]
12use std::{any::Any, io, net, net::SocketAddr, panic};
13
14use ntex_io::Io;
15use ntex_rt::{BlockFuture, Driver, Runner};
16use ntex_service::cfg::SharedCfg;
17
18pub mod channel;
19pub mod connect;
20
21#[cfg(unix)]
22pub mod polling;
23
24#[cfg(target_os = "linux")]
25pub mod uring;
26
27#[cfg(windows)]
28pub mod iocp;
29
30#[cfg(any(unix, windows))]
31mod helpers;
32
33#[cfg(feature = "tokio")]
34pub mod tokio;
35
36#[cfg(feature = "compio")]
37pub mod compio;
38
39#[allow(clippy::wrong_self_convention)]
40pub trait Reactor: Driver {
41    fn tcp_connect(&self, addr: net::SocketAddr, cfg: SharedCfg) -> channel::Receiver<Io>;
42
43    fn unix_connect(&self, addr: std::path::PathBuf, cfg: SharedCfg) -> channel::Receiver<Io>;
44
45    /// Convert std `TcpStream` to `Io`
46    fn from_tcp_stream(&self, stream: net::TcpStream, cfg: SharedCfg) -> io::Result<Io>;
47
48    #[cfg(unix)]
49    /// Convert std `UnixStream` to `Io`
50    fn from_unix_stream(&self, _: std::os::unix::net::UnixStream, _: SharedCfg) -> io::Result<Io>;
51}
52
53#[inline]
54/// Opens a TCP connection to a remote host.
55pub async fn tcp_connect(addr: SocketAddr, cfg: SharedCfg) -> io::Result<Io> {
56    with_current(|driver| driver.tcp_connect(addr, cfg)).await
57}
58
59#[inline]
60/// Opens a unix stream connection.
61pub async fn unix_connect<'a, P>(addr: P, cfg: SharedCfg) -> io::Result<Io>
62where
63    P: AsRef<std::path::Path> + 'a,
64{
65    with_current(|driver| driver.unix_connect(addr.as_ref().into(), cfg)).await
66}
67
68#[inline]
69/// Convert std `TcpStream` to `TcpStream`
70pub fn from_tcp_stream(stream: net::TcpStream, cfg: SharedCfg) -> io::Result<Io> {
71    with_current(|driver| driver.from_tcp_stream(stream, cfg))
72}
73
74#[cfg(unix)]
75#[inline]
76/// Convert std `UnixStream` to `UnixStream`
77pub fn from_unix_stream(stream: std::os::unix::net::UnixStream, cfg: SharedCfg) -> io::Result<Io> {
78    with_current(|driver| driver.from_unix_stream(stream, cfg))
79}
80
81fn with_current<T, F: FnOnce(&dyn Reactor) -> T>(f: F) -> T {
82    #[cold]
83    fn not_in_ntex_driver() -> ! {
84        panic!("not in a ntex driver")
85    }
86
87    if CURRENT_DRIVER.is_set() {
88        CURRENT_DRIVER.with(|d| f(&**d))
89    } else {
90        not_in_ntex_driver()
91    }
92}
93
94#[allow(clippy::borrowed_box)]
95/// Sets the current reactor and runs the provided closure.
96pub fn with_reactor<R, F: FnOnce() -> R>(r: &Box<dyn Reactor>, f: F) -> R {
97    #[cold]
98    fn reactor_is_set() -> ! {
99        panic!("reactor is already set");
100    }
101
102    if CURRENT_DRIVER.is_set() {
103        reactor_is_set()
104    }
105    CURRENT_DRIVER.set(r, f)
106}
107
108scoped_tls::scoped_thread_local!(static CURRENT_DRIVER: Box<dyn Reactor>);
109
110/// The default runtime.
111///
112/// Automatically selects the runtime implementation based on the
113/// configured features and the platform on which it runs.
114#[derive(Copy, Clone, Debug)]
115pub struct DefaultRuntime;
116
117impl Runner for DefaultRuntime {
118    #[allow(unused_variables, clippy::too_many_lines)]
119    fn block_on(&self, fut: BlockFuture) -> Result<(), Box<dyn Any + Send>> {
120        #[cfg(feature = "tokio")]
121        {
122            let driver: Box<dyn Reactor> = Box::new(self::tokio::Reactor);
123
124            with_reactor(&driver, || crate::tokio::block_on(fut));
125            Ok(())
126        }
127
128        #[cfg(all(feature = "compio", not(feature = "tokio")))]
129        {
130            let driver: Box<dyn Reactor> = Box::new(self::compio::Reactor);
131
132            with_reactor(&driver, || crate::compio::block_on(fut));
133            Ok(())
134        }
135
136        #[cfg(all(windows, not(feature = "tokio"), not(feature = "compio")))]
137        {
138            let driver: Box<dyn Reactor> =
139                Box::new(crate::iocp::Reactor::new().expect("Cannot construct driver"));
140
141            with_reactor(&driver, || {
142                panic::catch_unwind(panic::AssertUnwindSafe(|| {
143                    let rt = ntex_rt::Runtime::new(driver.handle());
144                    rt.block_on(fut, &*driver);
145                }))
146            })
147        }
148
149        #[cfg(all(unix, not(feature = "tokio"), not(feature = "compio")))]
150        {
151            #[cfg(feature = "neon-polling")]
152            {
153                let driver: Box<dyn Reactor> = Box::new(
154                    crate::polling::Reactor::new().expect("Cannot construct polling reactor"),
155                );
156
157                with_reactor(&driver, || {
158                    panic::catch_unwind(panic::AssertUnwindSafe(|| {
159                        let rt = ntex_rt::Runtime::new(driver.handle());
160                        rt.block_on(fut, &*driver);
161                    }))
162                })
163            }
164
165            #[cfg(all(target_os = "linux", feature = "neon-uring"))]
166            {
167                let driver: Box<dyn Reactor> = Box::new(
168                    crate::uring::Reactor::new(2048).expect("Cannot construct io-uring reactor"),
169                );
170
171                with_reactor(&driver, || {
172                    panic::catch_unwind(panic::AssertUnwindSafe(|| {
173                        let rt = ntex_rt::Runtime::new(driver.handle());
174                        rt.block_on(fut, &*driver);
175                    }))
176                })
177            }
178
179            #[cfg(all(not(feature = "neon-uring"), not(feature = "neon-polling")))]
180            {
181                #[cfg(target_os = "linux")]
182                let driver: Box<dyn Reactor> = if let Ok(reactor) = crate::uring::Reactor::new(2048)
183                {
184                    Box::new(reactor)
185                } else {
186                    Box::new(
187                        crate::polling::Reactor::new().expect("Cannot construct io-uring reactor"),
188                    )
189                };
190
191                #[cfg(not(target_os = "linux"))]
192                let driver: Box<dyn Reactor> = Box::new(
193                    crate::polling::Reactor::new().expect("Cannot construct polling reactor"),
194                );
195
196                with_reactor(&driver, || {
197                    panic::catch_unwind(panic::AssertUnwindSafe(|| {
198                        let rt = ntex_rt::Runtime::new(driver.handle());
199                        rt.block_on(fut, &*driver);
200                    }))
201                })
202            }
203        }
204    }
205}