From 2374ed3d8f407e3e4018e9961bdd2926b9bb1a48 Mon Sep 17 00:00:00 2001 From: Amaury <1293565+amaury1729@users.noreply.github.com> Date: Wed, 13 Dec 2023 14:12:18 +0100 Subject: [PATCH] refactor: Remove async-std (#1408) * Use trust-dns * Fix MX to trust-dns * Better logs * select_ok * Fix build * Remove async-std * Read system conf like before * ignore headless tests * don't unwrap * Use hickory * Fx build * Use asyncresolver --- Cargo.lock | 276 +++++++++--------------------- core/Cargo.toml | 5 +- core/src/lib.rs | 2 +- core/src/mx/mod.rs | 32 ++-- core/src/smtp/connect.rs | 5 +- core/src/smtp/mod.rs | 4 +- core/src/smtp/outlook/headless.rs | 23 ++- core/src/smtp/yahoo/headless.rs | 35 ++-- 8 files changed, 133 insertions(+), 249 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23833f1a9..38c62d8a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,17 +125,6 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" -[[package]] -name = "async-channel" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" -dependencies = [ - "concurrent-queue 1.2.4", - "event-listener 2.5.3", - "futures-core", -] - [[package]] name = "async-channel" version = "2.1.1" @@ -155,7 +144,7 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ - "async-lock 3.2.0", + "async-lock", "async-task", "concurrent-queue 2.4.0", "fastrand 2.0.1", @@ -169,10 +158,10 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4353121d5644cdf2beb5726ab752e79a8db1ebb52031770ec47db31d245526" dependencies = [ - "async-channel 2.1.1", + "async-channel", "async-executor", "async-io 2.2.1", - "async-lock 3.2.0", + "async-lock", "blocking", "futures-lite 2.1.0", "once_cell", @@ -214,7 +203,7 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6d3b15875ba253d1110c740755e246537483f152fa334f91abd7fe84c88b3ff" dependencies = [ - "async-lock 3.2.0", + "async-lock", "cfg-if", "concurrent-queue 2.4.0", "futures-io", @@ -227,15 +216,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "async-lock" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" -dependencies = [ - "event-listener 2.5.3", -] - [[package]] name = "async-lock" version = "3.2.0" @@ -259,23 +239,6 @@ dependencies = [ "url", ] -[[package]] -name = "async-process" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2c06e30a24e8c78a3987d07f0930edf76ef35e027e7bdb063fccafdad1f60c" -dependencies = [ - "async-io 1.7.0", - "blocking", - "cfg-if", - "event-listener 2.5.3", - "futures-lite 1.12.0", - "libc", - "once_cell", - "signal-hook", - "winapi", -] - [[package]] name = "async-reactor-trait" version = "1.1.0" @@ -323,48 +286,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-channel 1.7.1", - "async-global-executor", - "async-io 1.7.0", - "async-lock 2.5.0", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite 1.12.0", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - -[[package]] -name = "async-std-resolver" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2f8a4a203be3325981310ab243a28e6e4ea55b6519bffce05d41ab60e09ad8" -dependencies = [ - "async-std", - "async-trait", - "futures-io", - "futures-util", - "pin-utils", - "socket2", - "trust-dns-resolver", -] - [[package]] name = "async-task" version = "4.5.0" @@ -496,8 +417,8 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel 2.1.1", - "async-lock 3.2.0", + "async-channel", + "async-lock", "async-task", "fastrand 2.0.1", "futures-io", @@ -567,12 +488,12 @@ dependencies = [ "async-native-tls", "async-recursion", "async-smtp", - "async-std", - "async-std-resolver", "chrono", "fantoccini", "fast-socks5 0.9.1", "futures", + "hickory-proto", + "hickory-resolver", "levenshtein", "log", "mailchecker", @@ -585,7 +506,6 @@ dependencies = [ "serde", "serde_json", "tokio", - "trust-dns-proto", ] [[package]] @@ -945,14 +865,14 @@ dependencies = [ [[package]] name = "enum-as-inner" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" +checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" dependencies = [ "heck", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.38", ] [[package]] @@ -1171,11 +1091,10 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "matches", "percent-encoding", ] @@ -1334,18 +1253,6 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -[[package]] -name = "gloo-timers" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fb7d06c1c8cc2a29bee7ec961009a0b2caa0793ee4900c2ffb348734ba1c8f9" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "h2" version = "0.3.21" @@ -1447,6 +1354,51 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hickory-proto" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091a6fbccf4860009355e3efc52ff4acf37a63489aad7435372d44ceeb6fbbcf" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35b8f021164e6a984c9030023544c57789c51760065cd510572fedcfb04164e8" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "hkdf" version = "0.12.3" @@ -1593,11 +1545,20 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.2.3" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -1705,15 +1666,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lapin" version = "2.3.1" @@ -1807,9 +1759,6 @@ name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -dependencies = [ - "value-bag", -] [[package]] name = "lru-cache" @@ -1836,12 +1785,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - [[package]] name = "md-5" version = "0.10.1" @@ -2183,9 +2126,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" @@ -2919,16 +2862,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "signal-hook" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" -dependencies = [ - "libc", - "signal-hook-registry", -] - [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -3609,50 +3542,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trust-dns-proto" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c31f240f59877c3d4bb3b3ea0ec5a6a0cff07323580ff8c7a605cd7d08b255d" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna", - "ipnet", - "lazy_static", - "log", - "rand", - "smallvec", - "thiserror", - "tinyvec", - "tokio", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4ba72c2ea84515690c9fcef4c6c660bb9df3036ed1051686de84605b74fd558" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", - "lazy_static", - "log", - "lru-cache", - "parking_lot", - "resolv-conf", - "smallvec", - "thiserror", - "trust-dns-proto", -] - [[package]] name = "try-lock" version = "0.2.3" @@ -3704,9 +3593,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" @@ -3716,9 +3605,9 @@ checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" [[package]] name = "unicode-normalization" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] @@ -3743,13 +3632,12 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.2.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", - "idna", - "matches", + "idna 0.5.0", "percent-encoding", "serde", ] @@ -3791,12 +3679,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "value-bag" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" - [[package]] name = "vcpkg" version = "0.2.15" diff --git a/core/Cargo.toml b/core/Cargo.toml index d9d1f0bb8..a69b892d5 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,12 +16,12 @@ repository = "https://github.com/reacherhq/check-if-email-exists" async-native-tls = { version = "0.4", default-features = false } async-recursion = "1.0.5" async-smtp = { version = "0.6.0", features = ["socks5"] } -async-std = "1.12.0" -async-std-resolver = "0.21.2" chrono = "=0.4.22" fantoccini = { version = "0.19.3", optional = true } futures = { version = "0.3.29", optional = true } fast-socks5 = "0.9.1" +hickory-proto = "0.24.0" +hickory-resolver = "0.24.0" levenshtein = "1.0.5" log = "0.4.20" mailchecker = "6.0.1" @@ -33,7 +33,6 @@ regex = "1.10.2" reqwest = { version = "0.11.22", features = ["json", "socks"] } serde = { version = "1.0.192", features = ["derive"] } serde_json = "1.0.107" -trust-dns-proto = "0.21.2" [dev-dependencies] tokio = { version = "1.29.1" } diff --git a/core/src/lib.rs b/core/src/lib.rs index 571101e2e..e73a52360 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -70,13 +70,13 @@ pub mod smtp; pub mod syntax; mod util; +use hickory_proto::rr::rdata::MX; use misc::{check_misc, MiscDetails}; use mx::check_mx; use rand::Rng; use smtp::{check_smtp, SmtpDetails, SmtpError}; use std::time::{Duration, SystemTime}; use syntax::{check_syntax, get_similar_mail_provider}; -use trust_dns_proto::rr::rdata::MX; pub use util::constants::LOG_TARGET; pub use util::input_output::*; diff --git a/core/src/mx/mod.rs b/core/src/mx/mod.rs index 2f6892a16..423e20c35 100644 --- a/core/src/mx/mod.rs +++ b/core/src/mx/mod.rs @@ -14,11 +14,15 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +use std::io; + use crate::syntax::SyntaxDetails; use crate::util::ser_with_display::ser_with_display; -use async_std_resolver::{lookup::MxLookup, resolver_from_system_conf, ResolveError}; +use hickory_resolver::error::ResolveError; +use hickory_resolver::lookup::MxLookup; +use hickory_resolver::system_conf::read_system_conf; +use hickory_resolver::TokioAsyncResolver; use serde::{ser::SerializeMap, Serialize, Serializer}; -use std::io::Error; /// Details about the MX lookup. #[derive(Debug)] @@ -70,28 +74,30 @@ impl Serialize for MxDetails { pub enum MxError { /// Error with IO. #[serde(serialize_with = "ser_with_display")] - IoError(Error), + IoError(io::Error), /// Error while resolving MX lookups. #[serde(serialize_with = "ser_with_display")] ResolveError(Box), } +impl From for MxError { + fn from(e: io::Error) -> Self { + MxError::IoError(e) + } +} + impl From for MxError { - fn from(error: ResolveError) -> Self { - MxError::ResolveError(Box::new(error)) + fn from(e: ResolveError) -> Self { + MxError::ResolveError(Box::new(e)) } } /// Make a MX lookup. pub async fn check_mx(syntax: &SyntaxDetails) -> Result { // Construct a new Resolver with default configuration options - let resolver = resolver_from_system_conf().await?; + let (config, opts) = read_system_conf()?; + let resolver = TokioAsyncResolver::tokio(config, opts); - // Lookup the MX records associated with a name. - // The final dot forces this to be an FQDN, otherwise the search rules as specified - // in `ResolverOpts` will take effect. FQDN's are generally cheaper queries. - match resolver.mx_lookup(syntax.domain.as_str()).await { - Ok(lookup) => Ok(MxDetails::from(lookup)), - Err(err) => Ok(MxDetails { lookup: Err(err) }), - } + let mx_response: MxLookup = resolver.mx_lookup(&syntax.domain).await?; + Ok(MxDetails::from(mx_response)) } diff --git a/core/src/smtp/connect.rs b/core/src/smtp/connect.rs index ea35a9024..ae804b07c 100644 --- a/core/src/smtp/connect.rs +++ b/core/src/smtp/connect.rs @@ -55,12 +55,13 @@ async fn connect_to_host( ) -> Result { let smtp_timeout = if let Some(t) = input.smtp_timeout { if has_rule(domain, host, &Rule::SmtpTimeout45s) { + let duration = t.max(Duration::from_secs(45)); log::debug!( target: LOG_TARGET, - "[email={}] Bumping SMTP timeout to at least 45s", + "[email={}] Bumping SMTP timeout to {duration:?} because of rule", input.to_email, ); - Some(t.max(Duration::from_secs(45))) + Some(duration) } else { input.smtp_timeout } diff --git a/core/src/smtp/mod.rs b/core/src/smtp/mod.rs index d4b613fa4..9afecaf4e 100644 --- a/core/src/smtp/mod.rs +++ b/core/src/smtp/mod.rs @@ -28,8 +28,8 @@ use std::default::Default; use std::env; use async_smtp::EmailAddress; +use hickory_proto::rr::Name; use serde::{Deserialize, Serialize}; -use trust_dns_proto::rr::Name; use crate::{ util::input_output::CheckEmailInput, GmailVerifMethod, HotmailVerifMethod, YahooVerifMethod, @@ -202,9 +202,9 @@ pub async fn check_smtp( mod tests { use super::{check_smtp, CheckEmailInput, SmtpConnection, SmtpError}; use async_smtp::{smtp::error::Error, EmailAddress}; + use hickory_proto::rr::Name; use std::{str::FromStr, time::Duration}; use tokio::runtime::Runtime; - use trust_dns_proto::rr::Name; #[test] fn should_timeout() { diff --git a/core/src/smtp/outlook/headless.rs b/core/src/smtp/outlook/headless.rs index 726fad35d..5d1cda7d7 100644 --- a/core/src/smtp/outlook/headless.rs +++ b/core/src/smtp/outlook/headless.rs @@ -14,11 +14,10 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -use std::{thread::sleep, time::Duration}; +use std::{pin::Pin, thread::sleep, time::Duration}; -use async_std::prelude::FutureExt; -use fantoccini::Locator; -use futures::TryFutureExt; +use fantoccini::{error::CmdError, Locator}; +use futures::{future::select_ok, Future, TryFutureExt}; use crate::{ smtp::{ @@ -83,7 +82,13 @@ pub async fn check_password_recovery( .for_element(Locator::Id("iEnterVerification")) .and_then(|_| async { Ok(true) }); - let is_deliverable = f1.try_race(f2).try_race(f3).try_race(f4).await?; + let vec = vec![ + Box::pin(f1) as Pin> + Send>>, + Box::pin(f2), + Box::pin(f3), + Box::pin(f4), + ]; + let (is_deliverable, _) = select_ok(vec).await?; if is_deliverable { log::debug!( @@ -113,7 +118,7 @@ pub async fn check_password_recovery( #[cfg(test)] mod tests { use super::check_password_recovery; - use async_std::prelude::FutureExt; + use futures::future::join; // Ignoring this test as it requires a local process of WebDriver running on // "http://localhost:9515". To debug the headless password recovery page, @@ -144,13 +149,13 @@ mod tests { // but will fail with geckodriver. // ref: https://github.com/jonhoo/fantoccini/issues/111#issuecomment-727650629 #[tokio::test] - #[ignore = "Run a **chromedriver** server locally to test this"] + #[ignore = "Run a webdriver server locally to test this"] async fn test_parallel() { // This email does not exist. let f1 = check_password_recovery("foo@bar.baz", "http://localhost:9515"); let f2 = check_password_recovery("foo@bar.baz", "http://localhost:9515"); - let f = f1.try_join(f2).await; - assert!(f.is_ok(), "{:?}", f); + let f = join(f1, f2).await; + assert!(f.0.is_ok(), "{:?}", f); } } diff --git a/core/src/smtp/yahoo/headless.rs b/core/src/smtp/yahoo/headless.rs index 26c3b19ff..a33005581 100644 --- a/core/src/smtp/yahoo/headless.rs +++ b/core/src/smtp/yahoo/headless.rs @@ -14,11 +14,14 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +use std::pin::Pin; +use std::vec; use std::{thread::sleep, time::Duration}; -use async_std::prelude::FutureExt; +use fantoccini::error::CmdError; use fantoccini::Locator; -use futures::TryFutureExt; +use futures::future::select_ok; +use futures::{Future, TryFutureExt}; use crate::smtp::headless::{create_headless_client, HeadlessError}; use crate::{smtp::SmtpDetails, LOG_TARGET}; @@ -82,26 +85,14 @@ pub async fn check_headless(to_email: &str, webdriver: &str) -> Result> + Send>>, + Box::pin(f2), + Box::pin(f3), + Box::pin(f4), + Box::pin(f5), + ]; + let ((is_deliverable, is_disabled), _) = select_ok(vec).await?; c.close().await?;