1#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![allow(renamed_and_removed_lints)] #![allow(unknown_lints)] #![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#![warn(clippy::cognitive_complexity)]
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_duration_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)] #![allow(clippy::uninlined_format_args)]
40#![allow(clippy::significant_drop_in_scrutinee)] #![allow(clippy::result_large_err)] #![allow(clippy::needless_raw_string_hashes)] #![allow(clippy::needless_lifetimes)] #![allow(mismatched_lifetime_syntaxes)] #![cfg_attr(not(all(feature = "full")), allow(unused))]
49
50#[cfg(all(
51 any(feature = "native-tls", feature = "rustls"),
52 any(feature = "async-std", feature = "tokio", feature = "smol")
53))]
54pub(crate) mod impls;
55pub mod task;
56
57mod coarse_time;
58mod compound;
59mod dyn_time;
60pub mod general;
61mod opaque;
62pub mod scheduler;
63mod timer;
64mod traits;
65pub mod unimpl;
66pub mod unix;
67
68#[cfg(any(feature = "async-std", feature = "tokio", feature = "smol"))]
69use std::io;
70pub use traits::{
71 Blocking, CertifiedConn, CoarseTimeProvider, NetStreamListener, NetStreamProvider,
72 NoOpStreamOpsHandle, Runtime, SleepProvider, StreamOps, TlsProvider, ToplevelBlockOn,
73 ToplevelRuntime, UdpProvider, UdpSocket, UnsupportedStreamOp,
74};
75
76pub use coarse_time::{CoarseDuration, CoarseInstant, RealCoarseTimeProvider};
77pub use dyn_time::DynTimeProvider;
78pub use timer::{SleepProviderExt, Timeout, TimeoutError};
79
80pub mod tls {
83 pub use crate::traits::{CertifiedConn, TlsConnector};
84
85 #[cfg(all(
86 feature = "native-tls",
87 any(feature = "tokio", feature = "async-std", feature = "smol")
88 ))]
89 pub use crate::impls::native_tls::NativeTlsProvider;
90 #[cfg(all(
91 feature = "rustls",
92 any(feature = "tokio", feature = "async-std", feature = "smol")
93 ))]
94 pub use crate::impls::rustls::RustlsProvider;
95}
96
97#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "tokio"))]
98pub mod tokio;
99
100#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "async-std"))]
101pub mod async_std;
102
103#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "smol"))]
104pub mod smol;
105
106pub use compound::{CompoundRuntime, RuntimeSubstExt};
107
108#[cfg(all(
109 any(feature = "native-tls", feature = "rustls"),
110 feature = "async-std",
111 not(feature = "tokio")
112))]
113use async_std as preferred_backend_mod;
114#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "tokio"))]
115use tokio as preferred_backend_mod;
116
117#[cfg(all(
129 any(feature = "native-tls", feature = "rustls"),
130 any(feature = "async-std", feature = "tokio")
131))]
132#[derive(Clone)]
133pub struct PreferredRuntime {
134 inner: preferred_backend_mod::PreferredRuntime,
136}
137
138#[cfg(all(
139 any(feature = "native-tls", feature = "rustls"),
140 any(feature = "async-std", feature = "tokio")
141))]
142crate::opaque::implement_opaque_runtime! {
143 PreferredRuntime { inner : preferred_backend_mod::PreferredRuntime }
144}
145
146#[cfg(all(
147 any(feature = "native-tls", feature = "rustls"),
148 any(feature = "async-std", feature = "tokio")
149))]
150impl PreferredRuntime {
151 pub fn current() -> io::Result<Self> {
187 let rt = preferred_backend_mod::PreferredRuntime::current()?;
188
189 Ok(Self { inner: rt })
190 }
191
192 pub fn create() -> io::Result<Self> {
216 let rt = preferred_backend_mod::PreferredRuntime::create()?;
217
218 Ok(Self { inner: rt })
219 }
220
221 #[doc(hidden)]
231 pub fn run_test<P, F, O>(func: P) -> O
232 where
233 P: FnOnce(Self) -> F,
234 F: futures::Future<Output = O>,
235 {
236 let runtime = Self::create().expect("Failed to create runtime");
237 runtime.clone().block_on(func(runtime))
238 }
239}
240
241#[doc(hidden)]
247pub mod testing__ {
248 pub trait TestOutcome {
251 fn check_ok(&self);
253 }
254 impl TestOutcome for () {
255 fn check_ok(&self) {}
256 }
257 impl<E: std::fmt::Debug> TestOutcome for Result<(), E> {
258 fn check_ok(&self) {
259 self.as_ref().expect("Test failure");
260 }
261 }
262}
263
264macro_rules! declare_conditional_macro {
267 ( $(#[$meta:meta])* macro $name:ident = ($f1:expr, $f2:expr) ) => {
268 $( #[$meta] )*
269 #[cfg(all(feature=$f1, feature=$f2))]
270 #[macro_export]
271 macro_rules! $name {
272 ($tt:tt) => {
273 $tt
274 };
275 }
276
277 $( #[$meta] )*
278 #[cfg(not(all(feature=$f1, feature=$f2)))]
279 #[macro_export]
280 macro_rules! $name {
281 ($tt:tt) => {};
282 }
283
284 pub use $name;
287 };
288}
289
290#[doc(hidden)]
292pub mod cond {
293 declare_conditional_macro! {
294 #[doc(hidden)]
296 macro if_tokio_native_tls_present = ("tokio", "native-tls")
297 }
298 declare_conditional_macro! {
299 #[doc(hidden)]
301 macro if_tokio_rustls_present = ("tokio", "rustls")
302 }
303 declare_conditional_macro! {
304 #[doc(hidden)]
306 macro if_async_std_native_tls_present = ("async-std", "native-tls")
307 }
308 declare_conditional_macro! {
309 #[doc(hidden)]
311 macro if_async_std_rustls_present = ("async-std", "rustls")
312 }
313 declare_conditional_macro! {
314 #[doc(hidden)]
316 macro if_smol_native_tls_present = ("smol", "native-tls")
317 }
318 declare_conditional_macro! {
319 #[doc(hidden)]
321 macro if_smol_rustls_present = ("smol", "rustls")
322 }
323}
324
325#[macro_export]
341#[cfg(all(
342 any(feature = "native-tls", feature = "rustls"),
343 any(feature = "tokio", feature = "async-std", feature = "smol"),
344))]
345macro_rules! test_with_all_runtimes {
346 ( $fn:expr ) => {{
347 use $crate::cond::*;
348 use $crate::testing__::TestOutcome;
349 if_tokio_native_tls_present! {{
354 $crate::tokio::TokioNativeTlsRuntime::run_test($fn).check_ok();
355 }}
356 if_tokio_rustls_present! {{
357 $crate::tokio::TokioRustlsRuntime::run_test($fn).check_ok();
358 }}
359 if_async_std_native_tls_present! {{
360 $crate::async_std::AsyncStdNativeTlsRuntime::run_test($fn).check_ok();
361 }}
362 if_async_std_rustls_present! {{
363 $crate::async_std::AsyncStdRustlsRuntime::run_test($fn).check_ok();
364 }}
365 if_smol_native_tls_present! {{
366 $crate::smol::SmolNativeTlsRuntime::run_test($fn).check_ok();
367 }}
368 if_smol_rustls_present! {{
369 $crate::smol::SmolRustlsRuntime::run_test($fn).check_ok();
370 }}
371 }};
372}
373
374#[macro_export]
386#[cfg(all(
387 any(feature = "native-tls", feature = "rustls"),
388 any(feature = "tokio", feature = "async-std"),
389))]
390macro_rules! test_with_one_runtime {
391 ( $fn:expr ) => {{ $crate::PreferredRuntime::run_test($fn) }};
392}
393
394#[cfg(all(
395 test,
396 any(feature = "native-tls", feature = "rustls"),
397 any(feature = "async-std", feature = "tokio", feature = "smol"),
398 not(miri), ))]
400mod test {
401 #![allow(clippy::unwrap_used, clippy::unnecessary_wraps)]
402 use crate::SleepProviderExt;
403 use crate::ToplevelRuntime;
404
405 use crate::traits::*;
406
407 use futures::io::{AsyncReadExt, AsyncWriteExt};
408 use futures::stream::StreamExt;
409 use native_tls_crate as native_tls;
410 use std::io::Result as IoResult;
411 use std::net::SocketAddr;
412 use std::net::{Ipv4Addr, SocketAddrV4};
413 use std::time::{Duration, Instant};
414
415 fn small_delay<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
418 let rt = runtime.clone();
419 runtime.block_on(async {
420 let i1 = Instant::now();
421 let one_msec = Duration::from_millis(1);
422 rt.sleep(one_msec).await;
423 let i2 = Instant::now();
424 assert!(i2 >= i1 + one_msec);
425 });
426 Ok(())
427 }
428
429 fn small_timeout_ok<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
431 let rt = runtime.clone();
432 runtime.block_on(async {
433 let one_day = Duration::from_secs(86400);
434 let outcome = rt.timeout(one_day, async { 413_u32 }).await;
435 assert_eq!(outcome, Ok(413));
436 });
437 Ok(())
438 }
439
440 fn small_timeout_expire<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
442 use futures::future::pending;
443
444 let rt = runtime.clone();
445 runtime.block_on(async {
446 let one_micros = Duration::from_micros(1);
447 let outcome = rt.timeout(one_micros, pending::<()>()).await;
448 assert_eq!(outcome, Err(crate::TimeoutError));
449 assert_eq!(
450 outcome.err().unwrap().to_string(),
451 "Timeout expired".to_string()
452 );
453 });
454 Ok(())
455 }
456 fn tiny_wallclock<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
461 let rt = runtime.clone();
462 runtime.block_on(async {
463 let i1 = Instant::now();
464 let now = runtime.wallclock();
465 let one_millis = Duration::from_millis(1);
466 let one_millis_later = now + one_millis;
467
468 rt.sleep_until_wallclock(one_millis_later).await;
469
470 let i2 = Instant::now();
471 let newtime = runtime.wallclock();
472 assert!(newtime >= one_millis_later);
473 assert!(i2 - i1 >= one_millis);
474 });
475 Ok(())
476 }
477
478 fn self_connect_tcp<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
482 let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
483 let rt1 = runtime.clone();
484
485 let listener = runtime.block_on(rt1.listen(&(SocketAddr::from(localhost))))?;
486 let addr = listener.local_addr()?;
487
488 runtime.block_on(async {
489 let task1 = async {
490 let mut buf = vec![0_u8; 11];
491 let (mut con, _addr) = listener.incoming().next().await.expect("closed?")?;
492 con.read_exact(&mut buf[..]).await?;
493 IoResult::Ok(buf)
494 };
495 let task2 = async {
496 let mut con = rt1.connect(&addr).await?;
497 con.write_all(b"Hello world").await?;
498 con.flush().await?;
499 IoResult::Ok(())
500 };
501
502 let (data, send_r) = futures::join!(task1, task2);
503 send_r?;
504
505 assert_eq!(&data?[..], b"Hello world");
506
507 Ok(())
508 })
509 }
510
511 fn self_connect_udp<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
515 let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
516 let rt1 = runtime.clone();
517
518 let socket1 = runtime.block_on(rt1.bind(&(localhost.into())))?;
519 let addr1 = socket1.local_addr()?;
520
521 let socket2 = runtime.block_on(rt1.bind(&(localhost.into())))?;
522 let addr2 = socket2.local_addr()?;
523
524 runtime.block_on(async {
525 let task1 = async {
526 let mut buf = [0_u8; 16];
527 let (len, addr) = socket1.recv(&mut buf[..]).await?;
528 IoResult::Ok((buf[..len].to_vec(), addr))
529 };
530 let task2 = async {
531 socket2.send(b"Hello world", &addr1).await?;
532 IoResult::Ok(())
533 };
534
535 let (recv_r, send_r) = futures::join!(task1, task2);
536 send_r?;
537 let (buff, addr) = recv_r?;
538 assert_eq!(addr2, addr);
539 assert_eq!(&buff, b"Hello world");
540
541 Ok(())
542 })
543 }
544
545 fn listener_stream<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
550 let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
551 let rt1 = runtime.clone();
552
553 let listener = runtime
554 .block_on(rt1.listen(&SocketAddr::from(localhost)))
555 .unwrap();
556 let addr = listener.local_addr().unwrap();
557 let mut stream = listener.incoming();
558
559 runtime.block_on(async {
560 let task1 = async {
561 let mut n = 0_u32;
562 loop {
563 let (mut con, _addr) = stream.next().await.unwrap()?;
564 let mut buf = [0_u8; 11];
565 con.read_exact(&mut buf[..]).await?;
566 n += 1;
567 if &buf[..] == b"world done!" {
568 break IoResult::Ok(n);
569 }
570 }
571 };
572 let task2 = async {
573 for _ in 0_u8..5 {
574 let mut con = rt1.connect(&addr).await?;
575 con.write_all(b"Hello world").await?;
576 con.flush().await?;
577 }
578 let mut con = rt1.connect(&addr).await?;
579 con.write_all(b"world done!").await?;
580 con.flush().await?;
581 con.close().await?;
582 IoResult::Ok(())
583 };
584
585 let (n, send_r) = futures::join!(task1, task2);
586 send_r?;
587
588 assert_eq!(n?, 6);
589
590 Ok(())
591 })
592 }
593
594 fn simple_tls<R: ToplevelRuntime>(runtime: &R) -> IoResult<()> {
599 static PFX_ID: &[u8] = include_bytes!("test.pfx");
610 static PFX_PASSWORD: &str = "abc";
613
614 let localhost = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0);
615 let listener = std::net::TcpListener::bind(localhost)?;
616 let addr = listener.local_addr()?;
617
618 let identity = native_tls::Identity::from_pkcs12(PFX_ID, PFX_PASSWORD).unwrap();
619
620 let th = std::thread::spawn(move || {
622 use std::io::{Read, Write};
624 let acceptor = native_tls::TlsAcceptor::new(identity).unwrap();
625 let (con, _addr) = listener.accept()?;
626 let mut con = acceptor.accept(con).unwrap();
627 let mut buf = [0_u8; 16];
628 loop {
629 let n = con.read(&mut buf)?;
630 if n == 0 {
631 break;
632 }
633 con.write_all(&buf[..n])?;
634 }
635 IoResult::Ok(())
636 });
637
638 let connector = runtime.tls_connector();
639
640 runtime.block_on(async {
641 let text = b"I Suddenly Dont Understand Anything";
642 let mut buf = vec![0_u8; text.len()];
643 let conn = runtime.connect(&addr).await?;
644 let mut conn = connector.negotiate_unvalidated(conn, "Kan.Aya").await?;
645 assert!(conn.peer_certificate()?.is_some());
646 conn.write_all(text).await?;
647 conn.flush().await?;
648 conn.read_exact(&mut buf[..]).await?;
649 assert_eq!(&buf[..], text);
650 conn.close().await?;
651 IoResult::Ok(())
652 })?;
653
654 th.join().unwrap()?;
655 IoResult::Ok(())
656 }
657
658 macro_rules! tests_with_runtime {
659 { $runtime:expr => $($id:ident),* $(,)? } => {
660 $(
661 #[test]
662 fn $id() -> std::io::Result<()> {
663 super::$id($runtime)
664 }
665 )*
666 }
667 }
668
669 macro_rules! runtime_tests {
670 { $($id:ident),* $(,)? } =>
671 {
672 #[cfg(feature="tokio")]
673 mod tokio_runtime_tests {
674 tests_with_runtime! { &crate::tokio::PreferredRuntime::create()? => $($id),* }
675 }
676 #[cfg(feature="async-std")]
677 mod async_std_runtime_tests {
678 tests_with_runtime! { &crate::async_std::PreferredRuntime::create()? => $($id),* }
679 }
680 #[cfg(feature="smol")]
681 mod smol_runtime_tests {
682 tests_with_runtime! { &crate::smol::PreferredRuntime::create()? => $($id),* }
683 }
684 mod default_runtime_tests {
685 tests_with_runtime! { &crate::PreferredRuntime::create()? => $($id),* }
686 }
687 }
688 }
689
690 macro_rules! tls_runtime_tests {
691 { $($id:ident),* $(,)? } =>
692 {
693 #[cfg(all(feature="tokio", feature = "native-tls"))]
694 mod tokio_native_tls_tests {
695 tests_with_runtime! { &crate::tokio::TokioNativeTlsRuntime::create()? => $($id),* }
696 }
697 #[cfg(all(feature="async-std", feature = "native-tls"))]
698 mod async_std_native_tls_tests {
699 tests_with_runtime! { &crate::async_std::AsyncStdNativeTlsRuntime::create()? => $($id),* }
700 }
701 #[cfg(all(feature="smol", feature = "native-tls"))]
702 mod smol_native_tls_tests {
703 tests_with_runtime! { &crate::smol::SmolNativeTlsRuntime::create()? => $($id),* }
704 }
705 #[cfg(all(feature="tokio", feature="rustls"))]
706 mod tokio_rustls_tests {
707 tests_with_runtime! { &crate::tokio::TokioRustlsRuntime::create()? => $($id),* }
708 }
709 #[cfg(all(feature="async-std", feature="rustls"))]
710 mod async_std_rustls_tests {
711 tests_with_runtime! { &crate::async_std::AsyncStdRustlsRuntime::create()? => $($id),* }
712 }
713 #[cfg(all(feature="smol", feature="rustls"))]
714 mod smol_rustls_tests {
715 tests_with_runtime! { &crate::smol::SmolRustlsRuntime::create()? => $($id),* }
716 }
717 mod default_runtime_tls_tests {
718 tests_with_runtime! { &crate::PreferredRuntime::create()? => $($id),* }
719 }
720 }
721 }
722
723 runtime_tests! {
724 small_delay,
725 small_timeout_ok,
726 small_timeout_expire,
727 tiny_wallclock,
728 self_connect_tcp,
729 self_connect_udp,
730 listener_stream,
731 }
732
733 tls_runtime_tests! {
734 simple_tls,
735 }
736}