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

            
4
use crate::{BlockOn, CompoundRuntime};
5
use std::io::{Error as IoError, ErrorKind, 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
332
#[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<Handle, Handle, Handle, NativeTlsProvider>;
43

            
44
/// A [`Runtime`](crate::Runtime) built around a Handle to a tokio runtime, and `rustls`.
45
101
#[derive(Clone)]
46
#[cfg(feature = "rustls")]
47
pub struct TokioRustlsRuntime {
48
    /// The actual [`CompoundRuntime`] that implements this.
49
    inner: RustlsHandleInner,
50
}
51

            
52
/// Implementation for a TokioRuntimeRustlsHandle
53
#[cfg(feature = "rustls")]
54
type RustlsHandleInner = CompoundRuntime<Handle, Handle, Handle, RustlsProvider>;
55

            
56
#[cfg(feature = "native-tls")]
57
20
crate::opaque::implement_opaque_runtime! {
58
20
    TokioNativeTlsRuntime { inner : HandleInner }
59
20
}
60

            
61
#[cfg(feature = "rustls")]
62
1
crate::opaque::implement_opaque_runtime! {
63
1
    TokioRustlsRuntime { inner : RustlsHandleInner }
64
1
}
65

            
66
#[cfg(feature = "native-tls")]
67
impl From<tokio_crate::runtime::Handle> for TokioNativeTlsRuntime {
68
1
    fn from(h: tokio_crate::runtime::Handle) -> Self {
69
1
        let h = Handle::new(h);
70
1
        TokioNativeTlsRuntime {
71
1
            inner: CompoundRuntime::new(h.clone(), h.clone(), h, NativeTlsProvider::default()),
72
1
        }
73
1
    }
74
}
75

            
76
#[cfg(feature = "rustls")]
77
impl From<tokio_crate::runtime::Handle> for TokioRustlsRuntime {
78
1
    fn from(h: tokio_crate::runtime::Handle) -> Self {
79
1
        let h = Handle::new(h);
80
1
        TokioRustlsRuntime {
81
1
            inner: CompoundRuntime::new(h.clone(), h.clone(), h, RustlsProvider::default()),
82
1
        }
83
1
    }
84
}
85

            
86
#[cfg(feature = "native-tls")]
87
impl TokioNativeTlsRuntime {
88
    /// Create a new [`TokioNativeTlsRuntime`].
89
    ///
90
    /// The return value will own the underlying Tokio runtime object, which
91
    /// will be dropped when the last copy of this handle is freed.
92
    ///
93
    /// If you want to use a currently running runtime instead, call
94
    /// [`TokioNativeTlsRuntime::current()`].
95
1512
    pub fn create() -> IoResult<Self> {
96
1548
        crate::impls::tokio::create_runtime().map(|r| TokioNativeTlsRuntime {
97
1548
            inner: CompoundRuntime::new(r.clone(), r.clone(), r, NativeTlsProvider::default()),
98
1548
        })
99
1512
    }
100

            
101
    /// Return a [`TokioNativeTlsRuntime`] wrapping the currently running
102
    /// Tokio runtime.
103
    ///
104
    /// # Usage note
105
    ///
106
    /// We should never call this from inside other Arti crates, or from library
107
    /// crates that want to support multiple runtimes!  This function is for
108
    /// Arti _users_ who want to wrap some existing Tokio runtime as a
109
    /// [`Runtime`](crate::Runtime).  It is not for library crates that want to work with
110
    /// multiple runtimes.
111
    ///
112
    /// Once you have a runtime returned by this function, you should just
113
    /// create more handles to it via [`Clone`].
114
2
    pub fn current() -> IoResult<Self> {
115
2
        Ok(current_handle()?.into())
116
2
    }
117

            
118
    /// Helper to run a single test function in a freshly created runtime.
119
    ///
120
    /// # Panics
121
    ///
122
    /// Panics if we can't create this runtime.
123
    ///
124
    /// # Warning
125
    ///
126
    /// This API is **NOT** for consumption outside Arti. Semver guarantees are not provided.
127
    #[doc(hidden)]
128
48
    pub fn run_test<P, F, O>(func: P) -> O
129
48
    where
130
48
        P: FnOnce(Self) -> F,
131
48
        F: futures::Future<Output = O>,
132
48
    {
133
48
        let runtime = Self::create().expect("Failed to create runtime");
134
48
        runtime.clone().block_on(func(runtime))
135
48
    }
136
}
137

            
138
#[cfg(feature = "rustls")]
139
impl TokioRustlsRuntime {
140
    /// Create a new [`TokioRustlsRuntime`].
141
    ///
142
    /// The return value will own the underlying Tokio runtime object, which
143
    /// will be dropped when the last copy of this handle is freed.
144
    ///
145
    /// If you want to use a currently running runtime instead, call
146
    /// [`TokioRustlsRuntime::current()`].
147
849
    pub fn create() -> IoResult<Self> {
148
849
        crate::impls::tokio::create_runtime().map(|r| TokioRustlsRuntime {
149
849
            inner: CompoundRuntime::new(r.clone(), r.clone(), r, RustlsProvider::default()),
150
849
        })
151
849
    }
152

            
153
    /// Return a [`TokioRustlsRuntime`] wrapping the currently running
154
    /// Tokio runtime.
155
    ///
156
    /// # Usage note
157
    ///
158
    /// We should never call this from inside other Arti crates, or from library
159
    /// crates that want to support multiple runtimes!  This function is for
160
    /// Arti _users_ who want to wrap some existing Tokio runtime as a
161
    /// [`Runtime`](crate::Runtime).  It is not for library crates that want to work with
162
    /// multiple runtimes.
163
    ///
164
    /// Once you have a runtime returned by this function, you should just
165
    /// create more handles to it via [`Clone`].
166
2
    pub fn current() -> IoResult<Self> {
167
2
        Ok(current_handle()?.into())
168
2
    }
169

            
170
    /// Helper to run a single test function in a freshly created runtime.
171
    ///
172
    /// # Panics
173
    ///
174
    /// Panics if we can't create this runtime.
175
    ///
176
    /// # Warning
177
    ///
178
    /// This API is **NOT** for consumption outside Arti. Semver guarantees are not provided.
179
    #[doc(hidden)]
180
48
    pub fn run_test<P, F, O>(func: P) -> O
181
48
    where
182
48
        P: FnOnce(Self) -> F,
183
48
        F: futures::Future<Output = O>,
184
48
    {
185
48
        let runtime = Self::create().expect("Failed to create runtime");
186
48
        runtime.clone().block_on(func(runtime))
187
48
    }
188
}
189

            
190
/// As `Handle::try_current()`, but return an IoError on failure.
191
#[cfg(any(feature = "native-tls", feature = "rustls"))]
192
4
fn current_handle() -> std::io::Result<tokio_crate::runtime::Handle> {
193
4
    tokio_crate::runtime::Handle::try_current().map_err(|e| IoError::new(ErrorKind::Other, e))
194
4
}
195

            
196
#[cfg(test)]
197
mod test {
198
    #![allow(clippy::unwrap_used)]
199
    use super::*;
200

            
201
    #[test]
202
    fn no_current() {
203
        // There should be no running tokio runtime in this context.
204

            
205
        #[cfg(feature = "native-tls")]
206
        assert!(TokioNativeTlsRuntime::current().is_err());
207

            
208
        #[cfg(feature = "rustls")]
209
        assert!(TokioRustlsRuntime::current().is_err());
210
    }
211

            
212
    #[test]
213
    fn current() {
214
        // Now start a tokio runtime and make sure that the "current" functions do work in that case.
215
        let runtime = PreferredRuntime::create().unwrap();
216
        runtime.block_on(async {
217
            #[cfg(feature = "native-tls")]
218
            assert!(TokioNativeTlsRuntime::current().is_ok());
219

            
220
            #[cfg(feature = "rustls")]
221
            assert!(TokioRustlsRuntime::current().is_ok());
222
        });
223
    }
224

            
225
    #[test]
226
    fn debug() {
227
        #[cfg(feature = "native-tls")]
228
        assert_eq!(
229
            format!("{:?}", TokioNativeTlsRuntime::create().unwrap()),
230
            "TokioNativeTlsRuntime { .. }"
231
        );
232
        #[cfg(feature = "rustls")]
233
        assert_eq!(
234
            format!("{:?}", TokioRustlsRuntime::create().unwrap()),
235
            "TokioRustlsRuntime { .. }"
236
        );
237

            
238
        // Just for fun, let's try the Debug output for the Compound.
239
        assert_eq!(
240
            format!("{:?}", PreferredRuntime::create().unwrap().inner),
241
            "CompoundRuntime { .. }"
242
        );
243
    }
244
}