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
#![warn(clippy::or_fun_call)]
28
#![deny(clippy::print_stderr)]
29
#![deny(clippy::print_stdout)]
30
#![warn(clippy::rc_buffer)]
31
#![deny(clippy::ref_option_ref)]
32
#![warn(clippy::semicolon_if_nothing_returned)]
33
#![warn(clippy::trait_duplication_in_bounds)]
34
#![deny(clippy::unchecked_time_subtraction)]
35
#![deny(clippy::unnecessary_wraps)]
36
#![warn(clippy::unseparated_literal_suffix)]
37
#![deny(clippy::unwrap_used)]
38
#![deny(clippy::mod_module_files)]
39
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
40
#![allow(clippy::uninlined_format_args)]
41
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
42
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
43
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
44
#![allow(clippy::needless_lifetimes)] // See arti#1765
45
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
46
#![allow(clippy::collapsible_if)] // See arti#2342
47
#![deny(clippy::unused_async)]
48
#![deny(clippy::string_slice)] // See arti#2571
49
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
50
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
51

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
212
365
        Ok(Self { inner: rt })
213
365
    }
214

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

            
241
4974
        Ok(Self { inner: rt })
242
4974
    }
243

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

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

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

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

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

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

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

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

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

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

            
442
    use crate::traits::*;
443

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

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

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

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

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

            
505
            rt.sleep_until_wallclock(one_millis_later).await;
506

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

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

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

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

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

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

            
547
            Ok(())
548
        })
549
    }
550

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

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

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

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

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

            
581
            Ok(())
582
        })
583
    }
584

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

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

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

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

            
630
            assert_eq!(n?, 6);
631

            
632
            Ok(())
633
        })
634
    }
635

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

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

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

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

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

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

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

            
680
        let connector = runtime.tls_connector();
681

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

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

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

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

            
719
        let tls_connector = runtime.tls_connector();
720

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

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

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

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

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

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

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

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

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

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

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