1
//! Entry points for use with Tokio runtimes.
2
use crate::impls::tokio::TokioRuntimeHandle as Handle;
3

            
4
use crate::{CompoundRuntime, RealCoarseTimeProvider, ToplevelBlockOn};
5
use std::io::{Error as IoError, Result as IoResult};
6

            
7
#[cfg(feature = "native-tls")]
8
use crate::impls::native_tls::NativeTlsProvider;
9
#[cfg(feature = "rustls")]
10
use crate::impls::rustls::RustlsProvider;
11

            
12
/// An alias for the Tokio runtime that we prefer to use, based on whatever TLS
13
/// implementation has been enabled.
14
///
15
/// If only one of `native_tls` and `rustls` bas been enabled within the
16
/// `tor-rtcompat` crate, that will be the TLS backend that this uses.
17
///
18
/// Currently, `native_tls` is preferred over `rustls` when both are available,
19
/// because of its maturity within Arti.  However, this might change in the
20
/// future.
21
#[cfg(feature = "native-tls")]
22
pub use TokioNativeTlsRuntime as PreferredRuntime;
23
#[cfg(all(feature = "rustls", not(feature = "native-tls")))]
24
pub use TokioRustlsRuntime as PreferredRuntime;
25

            
26
/// A [`Runtime`](crate::Runtime) built around a Handle to a tokio runtime, and `native_tls`.
27
///
28
/// # Limitations
29
///
30
/// Note that Arti requires that the runtime should have working
31
/// implementations for Tokio's time, net, and io facilities, but we have
32
/// no good way to check that when creating this object.
33
#[derive(Clone)]
34
#[cfg(feature = "native-tls")]
35
pub struct TokioNativeTlsRuntime {
36
    /// The actual [`CompoundRuntime`] that implements this.
37
    inner: HandleInner,
38
}
39

            
40
/// Implementation type for a TokioRuntimeHandle.
41
#[cfg(feature = "native-tls")]
42
type HandleInner = CompoundRuntime<
43
    Handle,
44
    Handle,
45
    RealCoarseTimeProvider,
46
    Handle,
47
    Handle,
48
    NativeTlsProvider,
49
    Handle,
50
>;
51

            
52
/// A [`Runtime`](crate::Runtime) built around a Handle to a tokio runtime, and `rustls`.
53
#[derive(Clone)]
54
#[cfg(feature = "rustls")]
55
pub struct TokioRustlsRuntime {
56
    /// The actual [`CompoundRuntime`] that implements this.
57
    inner: RustlsHandleInner,
58
}
59

            
60
/// Implementation for a TokioRuntimeRustlsHandle
61
#[cfg(feature = "rustls")]
62
type RustlsHandleInner =
63
    CompoundRuntime<Handle, Handle, RealCoarseTimeProvider, Handle, Handle, RustlsProvider, Handle>;
64

            
65
#[cfg(feature = "native-tls")]
66
crate::opaque::implement_opaque_runtime! {
67
    TokioNativeTlsRuntime { inner : HandleInner }
68
}
69

            
70
#[cfg(feature = "rustls")]
71
crate::opaque::implement_opaque_runtime! {
72
    TokioRustlsRuntime { inner : RustlsHandleInner }
73
}
74

            
75
#[cfg(feature = "native-tls")]
76
impl From<tokio_crate::runtime::Handle> for TokioNativeTlsRuntime {
77
63
    fn from(h: tokio_crate::runtime::Handle) -> Self {
78
63
        let h = Handle::new(h);
79
63
        TokioNativeTlsRuntime {
80
63
            inner: CompoundRuntime::new(
81
63
                h.clone(),
82
63
                h.clone(),
83
63
                RealCoarseTimeProvider::new(),
84
63
                h.clone(),
85
63
                h.clone(),
86
63
                NativeTlsProvider::default(),
87
63
                h,
88
63
            ),
89
63
        }
90
63
    }
91
}
92

            
93
#[cfg(feature = "rustls")]
94
impl From<tokio_crate::runtime::Handle> for TokioRustlsRuntime {
95
2
    fn from(h: tokio_crate::runtime::Handle) -> Self {
96
2
        let h = Handle::new(h);
97
2
        TokioRustlsRuntime {
98
2
            inner: CompoundRuntime::new(
99
2
                h.clone(),
100
2
                h.clone(),
101
2
                RealCoarseTimeProvider::new(),
102
2
                h.clone(),
103
2
                h.clone(),
104
2
                RustlsProvider::default(),
105
2
                h,
106
2
            ),
107
2
        }
108
2
    }
109
}
110

            
111
#[cfg(feature = "native-tls")]
112
impl TokioNativeTlsRuntime {
113
    /// Create a new [`TokioNativeTlsRuntime`].
114
    ///
115
    /// The return value will own the underlying Tokio runtime object, which
116
    /// will be dropped when the last copy of this handle is freed.
117
    ///
118
    /// If you want to use a currently running runtime instead, call
119
    /// [`TokioNativeTlsRuntime::current()`].
120
7197
    pub fn create() -> IoResult<Self> {
121
7344
        crate::impls::tokio::create_runtime().map(|r| TokioNativeTlsRuntime {
122
7197
            inner: CompoundRuntime::new(
123
7197
                r.clone(),
124
7197
                r.clone(),
125
7197
                RealCoarseTimeProvider::new(),
126
7197
                r.clone(),
127
7197
                r.clone(),
128
7197
                NativeTlsProvider::default(),
129
7197
                r,
130
7197
            ),
131
7344
        })
132
7197
    }
133

            
134
    /// Return a [`TokioNativeTlsRuntime`] wrapping the currently running
135
    /// Tokio runtime.
136
    ///
137
    /// # Usage note
138
    ///
139
    /// We should never call this from inside other Arti crates, or from library
140
    /// crates that want to support multiple runtimes!  This function is for
141
    /// Arti _users_ who want to wrap some existing Tokio runtime as a
142
    /// [`Runtime`](crate::Runtime).  It is not for library crates that want to work with
143
    /// multiple runtimes.
144
    ///
145
    /// Once you have a runtime returned by this function, you should just
146
    /// create more handles to it via [`Clone`].
147
65
    pub fn current() -> IoResult<Self> {
148
65
        Ok(current_handle()?.into())
149
65
    }
150

            
151
    /// Helper to run a single test function in a freshly created runtime.
152
    ///
153
    /// # Panics
154
    ///
155
    /// Panics if we can't create this runtime.
156
    ///
157
    /// # Warning
158
    ///
159
    /// This API is **NOT** for consumption outside Arti. Semver guarantees are not provided.
160
    #[doc(hidden)]
161
138
    pub fn run_test<P, F, O>(func: P) -> O
162
138
    where
163
138
        P: FnOnce(Self) -> F,
164
138
        F: futures::Future<Output = O>,
165
138
    {
166
138
        let runtime = Self::create().expect("Failed to create runtime");
167
138
        runtime.clone().block_on(func(runtime))
168
138
    }
169
}
170

            
171
#[cfg(feature = "rustls")]
172
impl TokioRustlsRuntime {
173
    /// Create a new [`TokioRustlsRuntime`].
174
    ///
175
    /// The return value will own the underlying Tokio runtime object, which
176
    /// will be dropped when the last copy of this handle is freed.
177
    ///
178
    /// If you want to use a currently running runtime instead, call
179
    /// [`TokioRustlsRuntime::current()`].
180
3686
    pub fn create() -> IoResult<Self> {
181
3759
        crate::impls::tokio::create_runtime().map(|r| TokioRustlsRuntime {
182
3686
            inner: CompoundRuntime::new(
183
3686
                r.clone(),
184
3686
                r.clone(),
185
3686
                RealCoarseTimeProvider::new(),
186
3686
                r.clone(),
187
3686
                r.clone(),
188
3686
                RustlsProvider::default(),
189
3686
                r,
190
3686
            ),
191
3759
        })
192
3686
    }
193

            
194
    /// Return a [`TokioRustlsRuntime`] wrapping the currently running
195
    /// Tokio runtime.
196
    ///
197
    /// # Usage note
198
    ///
199
    /// We should never call this from inside other Arti crates, or from library
200
    /// crates that want to support multiple runtimes!  This function is for
201
    /// Arti _users_ who want to wrap some existing Tokio runtime as a
202
    /// [`Runtime`](crate::Runtime).  It is not for library crates that want to work with
203
    /// multiple runtimes.
204
    ///
205
    /// Once you have a runtime returned by this function, you should just
206
    /// create more handles to it via [`Clone`].
207
4
    pub fn current() -> IoResult<Self> {
208
4
        Ok(current_handle()?.into())
209
4
    }
210

            
211
    /// Helper to run a single test function in a freshly created runtime.
212
    ///
213
    /// # Panics
214
    ///
215
    /// Panics if we can't create this runtime.
216
    ///
217
    /// # Warning
218
    ///
219
    /// This API is **NOT** for consumption outside Arti. Semver guarantees are not provided.
220
    #[doc(hidden)]
221
138
    pub fn run_test<P, F, O>(func: P) -> O
222
138
    where
223
138
        P: FnOnce(Self) -> F,
224
138
        F: futures::Future<Output = O>,
225
138
    {
226
138
        let runtime = Self::create().expect("Failed to create runtime");
227
138
        runtime.clone().block_on(func(runtime))
228
138
    }
229
}
230

            
231
/// As `Handle::try_current()`, but return an IoError on failure.
232
#[cfg(any(feature = "native-tls", feature = "rustls"))]
233
69
fn current_handle() -> std::io::Result<tokio_crate::runtime::Handle> {
234
69
    tokio_crate::runtime::Handle::try_current().map_err(IoError::other)
235
69
}
236

            
237
#[cfg(all(
238
    test,
239
    not(miri), // tokio makes some syscalls that don't work with miri
240
))]
241
mod test {
242
    // @@ begin test lint list maintained by maint/add_warning @@
243
    #![allow(clippy::bool_assert_comparison)]
244
    #![allow(clippy::clone_on_copy)]
245
    #![allow(clippy::dbg_macro)]
246
    #![allow(clippy::mixed_attributes_style)]
247
    #![allow(clippy::print_stderr)]
248
    #![allow(clippy::print_stdout)]
249
    #![allow(clippy::single_char_pattern)]
250
    #![allow(clippy::unwrap_used)]
251
    #![allow(clippy::unchecked_duration_subtraction)]
252
    #![allow(clippy::useless_vec)]
253
    #![allow(clippy::needless_pass_by_value)]
254
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
255
    use super::*;
256

            
257
    #[test]
258
    fn no_current() {
259
        // There should be no running tokio runtime in this context.
260

            
261
        #[cfg(feature = "native-tls")]
262
        assert!(TokioNativeTlsRuntime::current().is_err());
263

            
264
        #[cfg(feature = "rustls")]
265
        assert!(TokioRustlsRuntime::current().is_err());
266
    }
267

            
268
    #[test]
269
    fn current() {
270
        // Now start a tokio runtime and make sure that the "current" functions do work in that case.
271
        let runtime = PreferredRuntime::create().unwrap();
272
        runtime.block_on(async {
273
            #[cfg(feature = "native-tls")]
274
            assert!(TokioNativeTlsRuntime::current().is_ok());
275

            
276
            #[cfg(feature = "rustls")]
277
            assert!(TokioRustlsRuntime::current().is_ok());
278
        });
279
    }
280

            
281
    #[test]
282
    fn debug() {
283
        #[cfg(feature = "native-tls")]
284
        assert_eq!(
285
            format!("{:?}", TokioNativeTlsRuntime::create().unwrap()),
286
            "TokioNativeTlsRuntime { .. }"
287
        );
288
        #[cfg(feature = "rustls")]
289
        assert_eq!(
290
            format!("{:?}", TokioRustlsRuntime::create().unwrap()),
291
            "TokioRustlsRuntime { .. }"
292
        );
293

            
294
        // Just for fun, let's try the Debug output for the Compound.
295
        assert_eq!(
296
            format!("{:?}", PreferredRuntime::create().unwrap().inner),
297
            "CompoundRuntime { .. }"
298
        );
299
    }
300
}