1
#![cfg_attr(docsrs, feature(doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// @@ begin lint list maintained by maint/add_warning @@
4
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6
#![warn(missing_docs)]
7
#![warn(noop_method_call)]
8
#![warn(unreachable_pub)]
9
#![warn(clippy::all)]
10
#![deny(clippy::await_holding_lock)]
11
#![deny(clippy::cargo_common_metadata)]
12
#![deny(clippy::cast_lossless)]
13
#![deny(clippy::checked_conversions)]
14
#![allow(clippy::cognitive_complexity)] // See arti#2556
15
#![deny(clippy::debug_assert_with_mut_call)]
16
#![deny(clippy::exhaustive_enums)]
17
#![deny(clippy::exhaustive_structs)]
18
#![deny(clippy::expl_impl_clone_on_copy)]
19
#![deny(clippy::fallible_impl_from)]
20
#![deny(clippy::implicit_clone)]
21
#![deny(clippy::large_stack_arrays)]
22
#![warn(clippy::manual_ok_or)]
23
#![deny(clippy::missing_docs_in_private_items)]
24
#![warn(clippy::needless_borrow)]
25
#![warn(clippy::needless_pass_by_value)]
26
#![warn(clippy::option_option)]
27
#![deny(clippy::print_stderr)]
28
#![deny(clippy::print_stdout)]
29
#![warn(clippy::rc_buffer)]
30
#![deny(clippy::ref_option_ref)]
31
#![warn(clippy::semicolon_if_nothing_returned)]
32
#![warn(clippy::trait_duplication_in_bounds)]
33
#![deny(clippy::unchecked_time_subtraction)]
34
#![deny(clippy::unnecessary_wraps)]
35
#![warn(clippy::unseparated_literal_suffix)]
36
#![deny(clippy::unwrap_used)]
37
#![deny(clippy::mod_module_files)]
38
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
39
#![allow(clippy::uninlined_format_args)]
40
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
41
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
42
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
43
#![allow(clippy::needless_lifetimes)] // See arti#1765
44
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
45
#![allow(clippy::collapsible_if)] // See arti#2342
46
#![deny(clippy::unused_async)]
47
#![deny(clippy::string_slice)] // See arti#2571
48
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
49
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
50

            
51
// TODO #1645 (either remove this, or decide to have it everywhere)
52
#![cfg_attr(not(all(feature = "full")), allow(unused))]
53

            
54
#[cfg(all(
55
    any(feature = "native-tls", feature = "rustls"),
56
    any(feature = "async-std", feature = "tokio", feature = "smol")
57
))]
58
pub(crate) mod impls;
59
pub mod task;
60

            
61
mod coarse_time;
62
mod compound;
63
mod dyn_time;
64
pub mod general;
65
mod network;
66
mod opaque;
67
pub mod scheduler;
68
mod timer;
69
mod traits;
70
pub mod unimpl;
71
pub mod unix;
72

            
73
#[cfg(any(feature = "async-std", feature = "tokio", feature = "smol"))]
74
use std::io;
75
pub use traits::{
76
    Blocking, CertifiedConn, CoarseTimeProvider, NetStreamListener, NetStreamProvider,
77
    NoOpStreamOpsHandle, Runtime, SleepProvider, SpawnExt, StreamOps, TlsProvider, ToplevelBlockOn,
78
    ToplevelRuntime, UdpProvider, UdpSocket, UnsupportedStreamOp,
79
};
80

            
81
pub use coarse_time::{CoarseDuration, CoarseInstant, RealCoarseTimeProvider};
82
pub use dyn_time::DynTimeProvider;
83
pub use network::{
84
    CommonConnectOptions, CommonListenOptions, TcpConnectOptions, TcpListenOptions,
85
    UnixConnectOptions, UnixListenOptions,
86
};
87
pub use timer::{SleepProviderExt, Timeout, TimeoutError};
88

            
89
/// Traits used to describe TLS connections and objects that can
90
/// create them.
91
pub mod tls {
92
    #[cfg(all(
93
        any(feature = "native-tls", feature = "rustls"),
94
        any(feature = "async-std", feature = "tokio", feature = "smol")
95
    ))]
96
    pub use crate::impls::unimpl_tls::UnimplementedTls;
97
    pub use crate::traits::{
98
        CertifiedConn, TlsAcceptorSettings, TlsConnector, TlsServerUnsupported,
99
    };
100

            
101
    #[cfg(all(
102
        feature = "native-tls",
103
        any(feature = "tokio", feature = "async-std", feature = "smol")
104
    ))]
105
    pub use crate::impls::native_tls::NativeTlsProvider;
106
    #[cfg(all(
107
        feature = "rustls",
108
        any(feature = "tokio", feature = "async-std", feature = "smol")
109
    ))]
110
    pub use crate::impls::rustls::RustlsProvider;
111
    #[cfg(all(
112
        feature = "rustls",
113
        feature = "tls-server",
114
        any(feature = "tokio", feature = "async-std", feature = "smol")
115
    ))]
116
    pub use crate::impls::rustls::rustls_server::{RustlsAcceptor, RustlsServerStream};
117
}
118

            
119
#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "tokio"))]
120
pub mod tokio;
121

            
122
#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "async-std"))]
123
pub mod async_std;
124

            
125
#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "smol"))]
126
pub mod smol;
127

            
128
pub use compound::{CompoundRuntime, RuntimeSubstExt};
129

            
130
#[cfg(all(
131
    any(feature = "native-tls", feature = "rustls"),
132
    feature = "async-std",
133
    not(feature = "tokio")
134
))]
135
use async_std as preferred_backend_mod;
136
#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "tokio"))]
137
use tokio as preferred_backend_mod;
138

            
139
/// The runtime that we prefer to use, out of all the runtimes compiled into the
140
/// tor-rtcompat crate.
141
///
142
/// If `tokio` and `async-std` are both available, we prefer `tokio` for its
143
/// performance.
144
/// If `native_tls` and `rustls` are both available, we prefer `native_tls` since
145
/// it has been used in Arti for longer.
146
///
147
/// The process [**may not fork**](crate#do-not-fork)
148
/// (except, very carefully, before exec)
149
/// after creating this or any other `Runtime`.
150
#[cfg(all(
151
    any(feature = "native-tls", feature = "rustls"),
152
    any(feature = "async-std", feature = "tokio")
153
))]
154
#[derive(Clone)]
155
pub struct PreferredRuntime {
156
    /// The underlying runtime object.
157
    inner: preferred_backend_mod::PreferredRuntime,
158
}
159

            
160
#[cfg(all(
161
    any(feature = "native-tls", feature = "rustls"),
162
    any(feature = "async-std", feature = "tokio")
163
))]
164
crate::opaque::implement_opaque_runtime! {
165
    PreferredRuntime { inner : preferred_backend_mod::PreferredRuntime }
166
}
167

            
168
#[cfg(all(
169
    any(feature = "native-tls", feature = "rustls"),
170
    any(feature = "async-std", feature = "tokio")
171
))]
172
impl PreferredRuntime {
173
    /// Obtain a [`PreferredRuntime`] from the currently running asynchronous runtime.
174
    /// Generally, this is what you want.
175
    ///
176
    /// This tries to get a handle to a currently running asynchronous runtime, and
177
    /// wraps it; the returned [`PreferredRuntime`] isn't the same thing as the
178
    /// asynchronous runtime object itself (e.g. `tokio::runtime::Runtime`).
179
    ///
180
    /// # Panics
181
    ///
182
    /// When `tor-rtcompat` is compiled with the `tokio` feature enabled
183
    /// (regardless of whether the `async-std` feature is also enabled),
184
    /// panics if called outside of Tokio runtime context.
185
    /// See `tokio::runtime::Handle::current`.
186
    ///
187
    /// # Usage notes
188
    ///
189
    /// Once you have a runtime returned by this function, you should
190
    /// just create more handles to it via [`Clone`].
191
    ///
192
    /// # Limitations
193
    ///
194
    /// If the `tor-rtcompat` crate was compiled with `tokio` support,
195
    /// this function will never return a runtime based on `async_std`.
196
    ///
197
    /// The process [**may not fork**](crate#do-not-fork)
198
    /// (except, very carefully, before exec)
199
    /// after creating this or any other `Runtime`.
200
    //
201
    // ## Note to Arti developers
202
    //
203
    // We should never call this from inside other Arti crates, or from
204
    // library crates that want to support multiple runtimes!  This
205
    // function is for Arti _users_ who want to wrap some existing Tokio
206
    // or Async_std runtime as a [`Runtime`].  It is not for library
207
    // crates that want to work with multiple runtimes.
208
162
    pub fn current() -> io::Result<Self> {
209
162
        let rt = preferred_backend_mod::PreferredRuntime::current()?;
210

            
211
162
        Ok(Self { inner: rt })
212
162
    }
213

            
214
    /// Create and return a new instance of the default [`Runtime`].
215
    ///
216
    /// Generally you should call this function at most once, and then use
217
    /// [`Clone::clone()`] to create additional references to that runtime.
218
    ///
219
    /// Tokio users may want to avoid this function and instead obtain a runtime using
220
    /// [`PreferredRuntime::current`]: this function always _builds_ a runtime,
221
    /// and if you already have a runtime, that isn't what you want with Tokio.
222
    ///
223
    /// If you need more fine-grained control over a runtime, you can create it
224
    /// using an appropriate builder type or function.
225
    ///
226
    /// The process [**may not fork**](crate#do-not-fork)
227
    /// (except, very carefully, before exec)
228
    /// after creating this or any other `Runtime`.
229
    //
230
    // ## Note to Arti developers
231
    //
232
    // We should never call this from inside other Arti crates, or from
233
    // library crates that want to support multiple runtimes!  This
234
    // function is for Arti _users_ who want to wrap some existing Tokio
235
    // or Async_std runtime as a [`Runtime`].  It is not for library
236
    // crates that want to work with multiple runtimes.
237
3476
    pub fn create() -> io::Result<Self> {
238
3476
        let rt = preferred_backend_mod::PreferredRuntime::create()?;
239

            
240
3476
        Ok(Self { inner: rt })
241
3476
    }
242

            
243
    /// Helper to run a single test function in a freshly created runtime.
244
    ///
245
    /// # Panics
246
    ///
247
    /// Panics if we can't create this runtime.
248
    ///
249
    /// # Warning
250
    ///
251
    /// This API is **NOT** for consumption outside Arti. Semver guarantees are not provided.
252
    #[doc(hidden)]
253
118
    pub fn run_test<P, F, O>(func: P) -> O
254
118
    where
255
118
        P: FnOnce(Self) -> F,
256
118
        F: futures::Future<Output = O>,
257
    {
258
118
        let runtime = Self::create().expect("Failed to create runtime");
259
118
        runtime.clone().block_on(func(runtime))
260
118
    }
261
}
262

            
263
/// Helpers for test_with_all_runtimes
264
///
265
/// # Warning
266
///
267
/// This API is **NOT** for consumption outside Arti. Semver guarantees are not provided.
268
#[doc(hidden)]
269
pub mod testing__ {
270
    /// A trait for an object that might represent a test failure, or which
271
    /// might just be `()`.
272
    pub trait TestOutcome {
273
        /// Abort if the test has failed.
274
        fn check_ok(&self);
275
    }
276
    impl TestOutcome for () {
277
        fn check_ok(&self) {}
278
    }
279
    impl<E: std::fmt::Debug> TestOutcome for Result<(), E> {
280
        fn check_ok(&self) {
281
            self.as_ref().expect("Test failure");
282
        }
283
    }
284
}
285

            
286
/// Helper: define a macro that expands a token tree iff a pair of features are
287
/// both present.
288
macro_rules! declare_conditional_macro {
289
    ( $(#[$meta:meta])* macro $name:ident = ($f1:expr, $f2:expr) ) => {
290
        $( #[$meta] )*
291
        #[cfg(all(feature=$f1, feature=$f2))]
292
        #[macro_export]
293
        macro_rules! $name {
294
            ($tt:tt) => {
295
                $tt
296
            };
297
        }
298

            
299
        $( #[$meta] )*
300
        #[cfg(not(all(feature=$f1, feature=$f2)))]
301
        #[macro_export]
302
        macro_rules! $name {
303
            ($tt:tt) => {};
304
        }
305

            
306
        // Needed so that we can access this macro at this path, both within the
307
        // crate and without.
308
        pub use $name;
309
    };
310
}
311

            
312
/// Defines macros that will expand when certain runtimes are available.
313
#[doc(hidden)]
314
pub mod cond {
315
    declare_conditional_macro! {
316
        /// Expand a token tree if the TokioNativeTlsRuntime is available.
317
        #[doc(hidden)]
318
        macro if_tokio_native_tls_present = ("tokio", "native-tls")
319
    }
320
    declare_conditional_macro! {
321
        /// Expand a token tree if the TokioRustlsRuntime is available.
322
        #[doc(hidden)]
323
        macro if_tokio_rustls_present = ("tokio", "rustls")
324
    }
325
    declare_conditional_macro! {
326
        /// Expand a token tree if the TokioNativeTlsRuntime is available.
327
        #[doc(hidden)]
328
        macro if_async_std_native_tls_present = ("async-std", "native-tls")
329
    }
330
    declare_conditional_macro! {
331
        /// Expand a token tree if the TokioNativeTlsRuntime is available.
332
        #[doc(hidden)]
333
        macro if_async_std_rustls_present = ("async-std", "rustls")
334
    }
335
    declare_conditional_macro! {
336
        /// Expand a token tree if the SmolNativeTlsRuntime is available.
337
        #[doc(hidden)]
338
        macro if_smol_native_tls_present = ("smol", "native-tls")
339
    }
340
    declare_conditional_macro! {
341
        /// Expand a token tree if the SmolRustlsRuntime is available.
342
        #[doc(hidden)]
343
        macro if_smol_rustls_present = ("smol", "rustls")
344
    }
345
}
346

            
347
/// Run a test closure, passing as argument every supported runtime.
348
///
349
/// Usually, prefer `tor_rtmock::MockRuntime::test_with_various` to this.
350
/// Use this macro only when you need to interact with things
351
/// that `MockRuntime` can't handle,
352
///
353
/// If everything in your test case is supported by `MockRuntime`,
354
/// you should use that instead:
355
/// that will give superior test coverage *and* a (more) deterministic test.
356
///
357
/// (This is a macro so that it can repeat the closure as multiple separate
358
/// expressions, so it can take on two different types, if needed.)
359
//
360
// NOTE(eta): changing this #[cfg] can affect tests inside this crate that use
361
//            this macro, like in scheduler.rs
362
#[macro_export]
363
#[cfg(all(
364
    any(feature = "native-tls", feature = "rustls"),
365
    any(feature = "tokio", feature = "async-std", feature = "smol"),
366
))]
367
macro_rules! test_with_all_runtimes {
368
    ( $fn:expr ) => {{
369
        use $crate::cond::*;
370
        use $crate::testing__::TestOutcome;
371
        // We have to do this outcome-checking business rather than just using
372
        // the ? operator or calling expect() because some of the closures that
373
        // we use this macro with return (), and some return Result.
374

            
375
        if_tokio_native_tls_present! {{
376
           $crate::tokio::TokioNativeTlsRuntime::run_test($fn).check_ok();
377
        }}
378
        if_tokio_rustls_present! {{
379
            $crate::tokio::TokioRustlsRuntime::run_test($fn).check_ok();
380
        }}
381
        if_async_std_native_tls_present! {{
382
            $crate::async_std::AsyncStdNativeTlsRuntime::run_test($fn).check_ok();
383
        }}
384
        if_async_std_rustls_present! {{
385
            $crate::async_std::AsyncStdRustlsRuntime::run_test($fn).check_ok();
386
        }}
387
        if_smol_native_tls_present! {{
388
            $crate::smol::SmolNativeTlsRuntime::run_test($fn).check_ok();
389
        }}
390
        if_smol_rustls_present! {{
391
            $crate::smol::SmolRustlsRuntime::run_test($fn).check_ok();
392
        }}
393
    }};
394
}
395

            
396
/// Run a test closure, passing as argument one supported runtime.
397
///
398
/// Usually, prefer `tor_rtmock::MockRuntime::test_with_various` to this.
399
/// Use this macro only when you need to interact with things
400
/// that `MockRuntime` can't handle.
401
///
402
/// If everything in your test case is supported by `MockRuntime`,
403
/// you should use that instead:
404
/// that will give superior test coverage *and* a (more) deterministic test.
405
///
406
/// (Always prefers tokio if present.)
407
#[macro_export]
408
#[cfg(all(
409
    any(feature = "native-tls", feature = "rustls"),
410
    any(feature = "tokio", feature = "async-std"),
411
))]
412
macro_rules! test_with_one_runtime {
413
    ( $fn:expr ) => {{ $crate::PreferredRuntime::run_test($fn) }};
414
}
415

            
416
#[cfg(all(
417
    test,
418
    any(feature = "native-tls", feature = "rustls"),
419
    any(feature = "async-std", feature = "tokio", feature = "smol"),
420
    not(miri), // Many of these tests use real sockets or SystemTime.
421
))]
422
mod test {
423
    // @@ begin test lint list maintained by maint/add_warning @@
424
    #![allow(clippy::bool_assert_comparison)]
425
    #![allow(clippy::clone_on_copy)]
426
    #![allow(clippy::dbg_macro)]
427
    #![allow(clippy::mixed_attributes_style)]
428
    #![allow(clippy::print_stderr)]
429
    #![allow(clippy::print_stdout)]
430
    #![allow(clippy::single_char_pattern)]
431
    #![allow(clippy::unwrap_used)]
432
    #![allow(clippy::unchecked_time_subtraction)]
433
    #![allow(clippy::useless_vec)]
434
    #![allow(clippy::needless_pass_by_value)]
435
    #![allow(clippy::string_slice)] // See arti#2571
436
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
437
    #![allow(clippy::unnecessary_wraps)]
438
    use crate::SleepProviderExt;
439
    use crate::ToplevelRuntime;
440

            
441
    use crate::traits::*;
442

            
443
    use futures::io::{AsyncReadExt, AsyncWriteExt};
444
    use futures::stream::StreamExt;
445
    use native_tls_crate as native_tls;
446
    use std::io::Result as IoResult;
447
    use std::net::SocketAddr;
448
    use std::net::{Ipv4Addr, SocketAddrV4};
449
    use web_time_compat::{Duration, Instant, InstantExt, SystemTimeExt};
450

            
451
    // Test "sleep" with a tiny delay, and make sure that at least that
452
    // much delay happens.
453
    fn small_delay<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
454
        let rt = runtime.clone();
455
        runtime.block_on(async {
456
            let i1 = Instant::get();
457
            let one_msec = Duration::from_millis(1);
458
            rt.sleep(one_msec).await;
459
            let i2 = Instant::get();
460
            assert!(i2 >= i1 + one_msec);
461
        });
462
        Ok(())
463
    }
464

            
465
    // Try a timeout operation that will succeed.
466
    fn small_timeout_ok<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
467
        let rt = runtime.clone();
468
        runtime.block_on(async {
469
            let one_day = Duration::from_secs(86400);
470
            let outcome = rt.timeout(one_day, async { 413_u32 }).await;
471
            assert_eq!(outcome, Ok(413));
472
        });
473
        Ok(())
474
    }
475

            
476
    // Try a timeout operation that will time out.
477
    fn small_timeout_expire<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
478
        use futures::future::pending;
479

            
480
        let rt = runtime.clone();
481
        runtime.block_on(async {
482
            let one_micros = Duration::from_micros(1);
483
            let outcome = rt.timeout(one_micros, pending::<()>()).await;
484
            assert_eq!(outcome, Err(crate::TimeoutError));
485
            assert_eq!(
486
                outcome.err().unwrap().to_string(),
487
                "Timeout expired".to_string()
488
            );
489
        });
490
        Ok(())
491
    }
492
    // Try a little wallclock delay.
493
    //
494
    // NOTE: This test will fail if the clock jumps a lot while it's
495
    // running.  We should use simulated time instead.
496
    fn tiny_wallclock<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
497
        let rt = runtime.clone();
498
        runtime.block_on(async {
499
            let i1 = Instant::get();
500
            let now = runtime.wallclock();
501
            let one_millis = Duration::from_millis(1);
502
            let one_millis_later = now + one_millis;
503

            
504
            rt.sleep_until_wallclock(one_millis_later).await;
505

            
506
            let i2 = Instant::get();
507
            let newtime = runtime.wallclock();
508
            assert!(newtime >= one_millis_later);
509
            assert!(i2 - i1 >= one_millis);
510
        });
511
        Ok(())
512
    }
513

            
514
    // Try connecting to ourself and sending a little data.
515
    //
516
    // NOTE: requires Ipv4 localhost.
517
    fn self_connect_tcp<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
518
        let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
519
        let rt1 = runtime.clone();
520

            
521
        let listen_options = Default::default();
522
        let listener =
523
            runtime.block_on(rt1.listen(&(SocketAddr::from(localhost)), &listen_options))?;
524
        let addr = listener.local_addr()?;
525

            
526
        runtime.block_on(async {
527
            let task1 = async {
528
                let mut buf = vec![0_u8; 11];
529
                let (mut con, _addr) = listener.incoming().next().await.expect("closed?")?;
530
                con.read_exact(&mut buf[..]).await?;
531
                IoResult::Ok(buf)
532
            };
533
            let task2 = async {
534
                let connect_options = Default::default();
535
                let mut con = rt1.connect(&addr, &connect_options).await?;
536
                con.write_all(b"Hello world").await?;
537
                con.flush().await?;
538
                IoResult::Ok(())
539
            };
540

            
541
            let (data, send_r) = futures::join!(task1, task2);
542
            send_r?;
543

            
544
            assert_eq!(&data?[..], b"Hello world");
545

            
546
            Ok(())
547
        })
548
    }
549

            
550
    // Try connecting to ourself and sending a little data.
551
    //
552
    // NOTE: requires Ipv4 localhost.
553
    fn self_connect_udp<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
554
        let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
555
        let rt1 = runtime.clone();
556

            
557
        let socket1 = runtime.block_on(rt1.bind(&(localhost.into())))?;
558
        let addr1 = socket1.local_addr()?;
559

            
560
        let socket2 = runtime.block_on(rt1.bind(&(localhost.into())))?;
561
        let addr2 = socket2.local_addr()?;
562

            
563
        runtime.block_on(async {
564
            let task1 = async {
565
                let mut buf = [0_u8; 16];
566
                let (len, addr) = socket1.recv(&mut buf[..]).await?;
567
                IoResult::Ok((buf[..len].to_vec(), addr))
568
            };
569
            let task2 = async {
570
                socket2.send(b"Hello world", &addr1).await?;
571
                IoResult::Ok(())
572
            };
573

            
574
            let (recv_r, send_r) = futures::join!(task1, task2);
575
            send_r?;
576
            let (buff, addr) = recv_r?;
577
            assert_eq!(addr2, addr);
578
            assert_eq!(&buff, b"Hello world");
579

            
580
            Ok(())
581
        })
582
    }
583

            
584
    // Try out our incoming connection stream code.
585
    //
586
    // We launch a few connections and make sure that we can read data on
587
    // them.
588
    fn listener_stream<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
589
        let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
590
        let rt1 = runtime.clone();
591

            
592
        let listen_options = Default::default();
593
        let listener = runtime
594
            .block_on(rt1.listen(&SocketAddr::from(localhost), &listen_options))
595
            .unwrap();
596
        let addr = listener.local_addr().unwrap();
597
        let mut stream = listener.incoming();
598

            
599
        runtime.block_on(async {
600
            let task1 = async {
601
                let mut n = 0_u32;
602
                loop {
603
                    let (mut con, _addr) = stream.next().await.unwrap()?;
604
                    let mut buf = [0_u8; 11];
605
                    con.read_exact(&mut buf[..]).await?;
606
                    n += 1;
607
                    if &buf[..] == b"world done!" {
608
                        break IoResult::Ok(n);
609
                    }
610
                }
611
            };
612
            let task2 = async {
613
                let connect_options = Default::default();
614
                for _ in 0_u8..5 {
615
                    let mut con = rt1.connect(&addr, &connect_options).await?;
616
                    con.write_all(b"Hello world").await?;
617
                    con.flush().await?;
618
                }
619
                let mut con = rt1.connect(&addr, &connect_options).await?;
620
                con.write_all(b"world done!").await?;
621
                con.flush().await?;
622
                con.close().await?;
623
                IoResult::Ok(())
624
            };
625

            
626
            let (n, send_r) = futures::join!(task1, task2);
627
            send_r?;
628

            
629
            assert_eq!(n?, 6);
630

            
631
            Ok(())
632
        })
633
    }
634

            
635
    // Try listening on an address and connecting there, except using TLS.
636
    //
637
    // Note that since we didn't have TLS server support when this test was first written,
638
    // we're going to use a thread.
639
    fn simple_tls<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
640
        /*
641
         A simple expired self-signed rsa-2048 certificate.
642

            
643
         Generated by running the make-cert.c program in tor-rtcompat/test-data-helper,
644
         and then making a PFX file using
645

            
646
         openssl pkcs12 -export -certpbe PBE-SHA1-3DES -out test.pfx -inkey test.key -in test.crt
647

            
648
         The password is "abc".
649
        */
650
        static PFX_ID: &[u8] = include_bytes!("test.pfx");
651
        // Note that we need to set a password on the pkcs12 file, since apparently
652
        // OSX doesn't support pkcs12 with empty passwords. (That was arti#111).
653
        static PFX_PASSWORD: &str = "abc";
654

            
655
        let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
656
        let listener = std::net::TcpListener::bind(localhost)?;
657
        let addr = listener.local_addr()?;
658

            
659
        let identity = native_tls::Identity::from_pkcs12(PFX_ID, PFX_PASSWORD).unwrap();
660

            
661
        // See note on function for why we're using a thread here.
662
        let th = std::thread::spawn(move || {
663
            // Accept a single TLS connection and run an echo server
664
            use std::io::{Read, Write};
665
            let acceptor = native_tls::TlsAcceptor::new(identity).unwrap();
666
            let (con, _addr) = listener.accept()?;
667
            let mut con = acceptor.accept(con).unwrap();
668
            let mut buf = [0_u8; 16];
669
            loop {
670
                let n = con.read(&mut buf)?;
671
                if n == 0 {
672
                    break;
673
                }
674
                con.write_all(&buf[..n])?;
675
            }
676
            IoResult::Ok(())
677
        });
678

            
679
        let connector = runtime.tls_connector();
680

            
681
        runtime.block_on(async {
682
            let text = b"I Suddenly Dont Understand Anything";
683
            let mut buf = vec![0_u8; text.len()];
684
            let connect_options = Default::default();
685
            let conn = runtime.connect(&addr, &connect_options).await?;
686
            let mut conn = connector.negotiate_unvalidated(conn, "Kan.Aya").await?;
687
            assert!(conn.peer_certificate()?.is_some());
688
            conn.write_all(text).await?;
689
            conn.flush().await?;
690
            conn.read_exact(&mut buf[..]).await?;
691
            assert_eq!(&buf[..], text);
692
            conn.close().await?;
693
            IoResult::Ok(())
694
        })?;
695

            
696
        th.join().unwrap()?;
697
        IoResult::Ok(())
698
    }
699

            
700
    fn simple_tls_server<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
701
        let mut rng = tor_basic_utils::test_rng::testing_rng();
702
        let tls_cert = tor_cert_x509::TlsKeyAndCert::create(
703
            &mut rng,
704
            std::time::SystemTime::get(),
705
            "prospit.example.org",
706
            "derse.example.org",
707
        )
708
        .unwrap();
709
        let cert = tls_cert.certificates_der()[0].to_vec();
710
        let settings = TlsAcceptorSettings::new(tls_cert).unwrap();
711

            
712
        let Ok(tls_acceptor) = runtime.tls_acceptor(settings) else {
713
            println!("Skipping tls-server test for runtime {:?}", runtime);
714
            return IoResult::Ok(());
715
        };
716
        println!("Running tls-server test for runtime {:?}", runtime);
717

            
718
        let tls_connector = runtime.tls_connector();
719

            
720
        let localhost: SocketAddr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0).into();
721
        let rt1 = runtime.clone();
722

            
723
        let msg = b"Derse Reviles Him And Outlaws Frogs Wherever They Can";
724
        runtime.block_on(async move {
725
            let listen_options = Default::default();
726
            let listener = runtime.listen(&localhost, &listen_options).await.unwrap();
727
            let address = listener.local_addr().unwrap();
728

            
729
            let h1 = runtime
730
                .spawn_with_handle(async move {
731
                    let conn = listener.incoming().next().await.unwrap().unwrap().0;
732
                    let mut conn = tls_acceptor.negotiate_unvalidated(conn, "").await.unwrap();
733

            
734
                    let mut buf = vec![];
735
                    conn.read_to_end(&mut buf).await.unwrap();
736
                    (buf, conn.own_certificate().unwrap().unwrap().into_owned())
737
                })
738
                .unwrap();
739

            
740
            let h2 = runtime
741
                .spawn_with_handle(async move {
742
                    let connect_options = Default::default();
743
                    let conn = rt1.connect(&address, &connect_options).await.unwrap();
744
                    let mut conn = tls_connector
745
                        .negotiate_unvalidated(conn, "prospit.example.org")
746
                        .await
747
                        .unwrap();
748
                    conn.write_all(msg).await.unwrap();
749
                    conn.close().await.unwrap();
750
                    conn.peer_certificate().unwrap().unwrap().into_owned()
751
                })
752
                .unwrap();
753

            
754
            let (received, server_own_cert) = h1.await;
755
            let client_peer_cert = h2.await;
756
            assert_eq!(received, msg);
757
            assert_eq!(&server_own_cert, &cert);
758
            assert_eq!(&client_peer_cert, &cert);
759
        });
760
        IoResult::Ok(())
761
    }
762

            
763
    macro_rules! tests_with_runtime {
764
        { $runtime:expr  => $($id:ident),* $(,)? } => {
765
            $(
766
                #[test]
767
                fn $id() -> std::io::Result<()> {
768
                    super::$id($runtime)
769
                }
770
            )*
771
        }
772
    }
773

            
774
    macro_rules! runtime_tests {
775
        { $($id:ident),* $(,)? } =>
776
        {
777
           #[cfg(feature="tokio")]
778
            mod tokio_runtime_tests {
779
                tests_with_runtime! { &crate::tokio::PreferredRuntime::create()? => $($id),* }
780
            }
781
            #[cfg(feature="async-std")]
782
            mod async_std_runtime_tests {
783
                tests_with_runtime! { &crate::async_std::PreferredRuntime::create()? => $($id),* }
784
            }
785
            #[cfg(feature="smol")]
786
            mod smol_runtime_tests {
787
                tests_with_runtime! { &crate::smol::PreferredRuntime::create()? => $($id),* }
788
            }
789
            mod default_runtime_tests {
790
                tests_with_runtime! { &crate::PreferredRuntime::create()? => $($id),* }
791
            }
792
        }
793
    }
794

            
795
    macro_rules! tls_runtime_tests {
796
        { $($id:ident),* $(,)? } =>
797
        {
798
            #[cfg(all(feature="tokio", feature = "native-tls"))]
799
            mod tokio_native_tls_tests {
800
                tests_with_runtime! { &crate::tokio::TokioNativeTlsRuntime::create()? => $($id),* }
801
            }
802
            #[cfg(all(feature="async-std", feature = "native-tls"))]
803
            mod async_std_native_tls_tests {
804
                tests_with_runtime! { &crate::async_std::AsyncStdNativeTlsRuntime::create()? => $($id),* }
805
            }
806
            #[cfg(all(feature="smol", feature = "native-tls"))]
807
            mod smol_native_tls_tests {
808
                tests_with_runtime! { &crate::smol::SmolNativeTlsRuntime::create()? => $($id),* }
809
            }
810
            #[cfg(all(feature="tokio", feature="rustls"))]
811
            mod tokio_rustls_tests {
812
                tests_with_runtime! {  &crate::tokio::TokioRustlsRuntime::create()? => $($id),* }
813
            }
814
            #[cfg(all(feature="async-std", feature="rustls"))]
815
            mod async_std_rustls_tests {
816
                tests_with_runtime! {  &crate::async_std::AsyncStdRustlsRuntime::create()? => $($id),* }
817
            }
818
            #[cfg(all(feature="smol", feature="rustls"))]
819
            mod smol_rustls_tests {
820
                tests_with_runtime! {  &crate::smol::SmolRustlsRuntime::create()? => $($id),* }
821
            }
822
            mod default_runtime_tls_tests {
823
                tests_with_runtime! { &crate::PreferredRuntime::create()? => $($id),* }
824
            }
825
        }
826
    }
827

            
828
    runtime_tests! {
829
        small_delay,
830
        small_timeout_ok,
831
        small_timeout_expire,
832
        tiny_wallclock,
833
        self_connect_tcp,
834
        self_connect_udp,
835
        listener_stream,
836
    }
837

            
838
    tls_runtime_tests! {
839
        simple_tls,
840
        simple_tls_server,
841
    }
842
}