Make the native-tls crate optional.

This commit puts the native-tls crate behind a feature.  The feature
is off-by-default in the tor-rtcompat crate, but can be enabled
either from arti or arti-client.

There is an included script that I used to test that tor-rtcompat
could build and run its tests with all subsets of its features.

Closes #300
This commit is contained in:
Nick Mathewson 2022-01-26 10:53:06 -05:00
parent 8af3528cd3
commit 30b3818a9e
21 changed files with 143 additions and 45 deletions

View File

@ -21,7 +21,7 @@ serde_json = "1.0.50"
tracing = "0.1.18"
tracing-subscriber = { version = "0.3.0", features = ["env-filter"] }
tokio = { version = "1.4", features = ["full"] }
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features = ["tokio"] }
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features = ["tokio", "native-tls"] }
arti-config = { path="../arti-config", version = "0.0.3"}
arti-client = { package="arti-client", path = "../arti-client", version = "0.0.3"}
tokio-socks = "0.5"

View File

@ -11,9 +11,11 @@ categories = [ "network-programming", "cryptography" ]
repository="https://gitlab.torproject.org/tpo/core/arti.git/"
[features]
default = [ "tokio" ]
default = [ "tokio", "native-tls" ]
async-std = [ "tor-rtcompat/async-std" ]
tokio = [ "tor-rtcompat/tokio", "tor-proto/tokio" ]
native-tls = [ "tor-rtcompat/native-tls" ]
rustls = [ "tor-rtcompat/rustls" ]
static = [ "tor-rtcompat/static", "tor-dirmgr/static" ]
# Enable experimental APIs that are not yet officially supported.
@ -42,7 +44,7 @@ serde = { version = "1.0.103", features = ["derive"] }
thiserror = "1"
[dev-dependencies]
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio"] }
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio", "native-tls" ] }
tokio-crate = { package = "tokio", version = "1.4", features = ["rt", "rt-multi-thread", "io-util", "net", "time", "macros" ] }
hyper = { version = "0.14", features = ["http1", "client", "runtime"] }
pin-project = "1"

View File

@ -24,9 +24,9 @@ use std::time::Duration;
use crate::{status, Error, Result};
#[cfg(feature = "async-std")]
use tor_rtcompat::async_std::AsyncStdNativeTlsRuntime;
use tor_rtcompat::async_std::PreferredRuntime as PreferredAsyncStdRuntime;
#[cfg(feature = "tokio")]
use tor_rtcompat::tokio::TokioNativeTlsRuntime;
use tor_rtcompat::tokio::PreferredRuntime as PreferredTokioRuntime;
use tracing::{debug, error, info, warn};
/// An active client session on the Tor network.
@ -246,7 +246,7 @@ impl StreamPrefs {
}
#[cfg(feature = "tokio")]
impl TorClient<TokioNativeTlsRuntime> {
impl TorClient<PreferredTokioRuntime> {
/// Bootstrap a connection to the Tor network, using the current Tokio runtime.
///
/// Returns a client once there is enough directory material to
@ -259,14 +259,14 @@ impl TorClient<TokioNativeTlsRuntime> {
/// Panics if called outside of the context of a Tokio runtime.
pub async fn bootstrap_with_tokio(
config: TorClientConfig,
) -> Result<TorClient<TokioNativeTlsRuntime>> {
let rt = TokioNativeTlsRuntime::current().expect("called outside of Tokio runtime");
) -> Result<TorClient<PreferredTokioRuntime>> {
let rt = PreferredTokioRuntime::current().expect("called outside of Tokio runtime");
Self::bootstrap(rt, config).await
}
}
#[cfg(feature = "async-std")]
impl TorClient<AsyncStdNativeTlsRuntime> {
impl TorClient<PreferredAsyncStdRuntime> {
/// Bootstrap a connection to the Tor network, using the current async-std runtime.
///
/// Returns a client once there is enough directory material to
@ -275,9 +275,9 @@ impl TorClient<AsyncStdNativeTlsRuntime> {
/// This is a convenience wrapper around [`TorClient::bootstrap`].
pub async fn bootstrap_with_async_std(
config: TorClientConfig,
) -> Result<TorClient<AsyncStdNativeTlsRuntime>> {
) -> Result<TorClient<PreferredAsyncStdRuntime>> {
// FIXME(eta): not actually possible for this to fail
let rt = AsyncStdNativeTlsRuntime::current().expect("failed to get async-std runtime");
let rt = PreferredAsyncStdRuntime::current().expect("failed to get async-std runtime");
Self::bootstrap(rt, config).await
}
}

View File

@ -11,9 +11,11 @@ categories = [ "command-line-utilities", "cryptography" ]
repository="https://gitlab.torproject.org/tpo/core/arti.git/"
[features]
default = [ "tokio" ]
default = [ "tokio", "native-tls" ]
async-std = [ "arti-client/async-std", "tor-rtcompat/async-std", "async-ctrlc", "once_cell" ]
tokio = [ "tokio-crate", "arti-client/tokio", "tor-rtcompat/tokio" ]
native-tls = [ "arti-client/native-tls", "tor-rtcompat/native-tls" ]
rustls = [ "arti-client/rustls", "tor-rtcompat/rustls" ]
static = [ "arti-client/static" ]
journald = [ "tracing-journald" ]

View File

@ -230,9 +230,9 @@ fn main() -> Result<()> {
process::use_max_file_limit();
#[cfg(all(feature = "async-std", not(feature = "tokio")))]
use tor_rtcompat::tokio::AsyncStdNativeTlsRuntime as ChosenRuntime;
use tor_rtcompat::tokio::PreferredRuntime as ChosenRuntime;
#[cfg(feature = "tokio")]
use tor_rtcompat::tokio::TokioNativeTlsRuntime as ChosenRuntime;
use tor_rtcompat::tokio::PreferredRuntime as ChosenRuntime;
let runtime = ChosenRuntime::create()?;

View File

@ -30,4 +30,4 @@ float_eq = "0.7"
futures-await-test = "0.3.0"
hex-literal = "0.3"
tor-rtmock = { path="../tor-rtmock", version = "0.0.3"}
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio"] }
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio", "native-tls"] }

View File

@ -50,4 +50,4 @@ tor-guardmgr = { path="../tor-guardmgr", version = "0.0.3", features=["testing"]
tor-llcrypto = { path="../tor-llcrypto", version = "0.0.3"}
tor-netdir = { path="../tor-netdir", version = "0.0.3", features=["testing"] }
tor-persist = { path="../tor-persist", version = "0.0.3", features=["testing"] }
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio"] }
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio", "native-tls" ] }

View File

@ -38,5 +38,5 @@ thiserror = "1"
[dev-dependencies]
futures-await-test = "0.3.0"
tor-rtmock = { path="../tor-rtmock", version = "0.0.3"}
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio"] }
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio", "native-tls" ] }

View File

@ -56,7 +56,5 @@ humantime-serde = "1"
futures-await-test = "0.3.0"
hex-literal = "0.3"
tempfile = "3"
tor-rtcompat = { path = "../tor-rtcompat", version = "0.0.3", features = [
"tokio",
] }
tor-rtcompat = { path = "../tor-rtcompat", version = "0.0.3", features = [ "tokio", "native-tls" ] }
float_eq = "0.7"

View File

@ -42,5 +42,5 @@ tracing = "0.1.18"
tor-netdir = { path="../tor-netdir", version = "0.0.3", features=["testing"]}
tor-netdoc = { path="../tor-netdoc", version = "0.0.3"}
tor-persist = { path="../tor-persist", version = "0.0.3", features=["testing"]}
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio"]}
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio", "native-tls"]}
tor-rtmock = { path="../tor-rtmock", version = "0.0.3"}

View File

@ -49,8 +49,6 @@ tokio-util = { version = "0.6", features = ["compat"], optional = true }
coarsetime = { version = "0.1.20", optional = true }
[dev-dependencies]
tor-rtcompat = { path = "../tor-rtcompat", version = "0.0.3", features = [
"tokio",
] }
tor-rtcompat = { path = "../tor-rtcompat", version = "0.0.3", features = [ "tokio", "native-tls" ] }
hex-literal = "0.3"
hex = "0.4"

View File

@ -15,7 +15,10 @@ repository="https://gitlab.torproject.org/tpo/core/arti.git/"
default = [ ]
async-std = [ "async-std-crate", "async-io", "async_executors/async_std" ]
tokio = [ "tokio-crate", "tokio-util", "async_executors/tokio_tp" ]
static = [ "native-tls/vendored" ]
# TODO: This feature makes us link native-tls statically even if we
# don't want to use native-tls in the first place. That's not so clever!
static = [ "native-tls-crate/vendored" ]
native-tls = [ "native-tls-crate", "async-native-tls" ]
rustls = [ "rustls-crate", "async-rustls", "x509-signature" ]
[dependencies]
@ -24,15 +27,20 @@ async_executors = { version = "0.4", default_features = false }
async-trait = "0.1.2"
futures = "0.3"
pin-project = "1"
native-tls = "0.2"
native-tls-crate = { package = "native-tls", version = "0.2", optional = true }
rustls-crate = { package = "rustls", version = "0.19", optional = true, features = [ "dangerous_configuration" ] }
async-std-crate = { package = "async-std", version = "1.7.0", optional = true }
async-io = { version = "1.4.1", optional = true }
async-native-tls = { version = "0.4.0" }
async-native-tls = { version = "0.4.0", optional = true }
tokio-crate = { package = "tokio", version = "1.4", optional = true, features = ["rt", "rt-multi-thread", "io-util", "net", "time" ] }
tokio-util = { version = "0.6", features = ["compat"], optional = true }
async-rustls = { version = "0.2.0", optional = true }
x509-signature = { version = "0.5.0", optional = true }
[dev-dependencies]
# Used for testing our TLS implementation.
native-tls-crate = { package = "native-tls", version = "0.2" }

View File

@ -0,0 +1,26 @@
#!/usr/bin/python
#
# Run a provided command over all possible subsets of important
# tor-rtcompat features.
import sys, subprocess
if sys.argv[1:] == []:
print("You need to name a program to run with different features")
sys.exit(1)
FEATURES = [ "tokio", "async-std", "native-tls", "rustls" ]
COMBINATIONS = [ [] ]
# Generate all combinations of features.
for feature in FEATURES:
new_combinations = [ c + [ feature ] for c in COMBINATIONS ]
COMBINATIONS.extend(new_combinations)
for c in COMBINATIONS:
arg = "--features={}".format(",".join(c))
commandline = sys.argv[1:] + [arg]
print(" ".join(commandline))
subprocess.check_call(commandline)

View File

@ -3,8 +3,8 @@ pub use crate::impls::async_std::create_runtime as create_runtime_impl;
use crate::{compound::CompoundRuntime, SpawnBlocking};
use std::io::Result as IoResult;
#[cfg(feature = "native-tls")]
use crate::impls::native_tls::NativeTlsProvider;
#[cfg(feature = "rustls")]
use crate::impls::rustls::RustlsProvider;
use async_std_crate::net::TcpStream;
@ -20,18 +20,25 @@ use async_executors::AsyncStd;
/// Currently, `native_tls` is preferred over `rustls` when both are available,
/// because of its maturity within Arti. However, this might change in the
/// future.
#[cfg(all(feature = "native-tls"))]
pub use AsyncStdNativeTlsRuntime as PreferredRuntime;
#[cfg(all(feature = "rustls", not(feature = "native-tls")))]
pub use AsyncStdRustlsRuntime as PreferredRuntime;
/// A [`Runtime`](crate::Runtime) powered by `async_std` and `native_tls`.
#[derive(Clone)]
#[cfg(all(feature = "native-tls"))]
pub struct AsyncStdNativeTlsRuntime {
/// The actual runtime object.
inner: NativeTlsInner,
}
/// Implementation type for AsyncStdRuntime.
#[cfg(all(feature = "native-tls"))]
type NativeTlsInner = CompoundRuntime<AsyncStd, AsyncStd, AsyncStd, NativeTlsProvider<TcpStream>>;
#[cfg(all(feature = "native-tls"))]
crate::opaque::implement_opaque_runtime! {
AsyncStdNativeTlsRuntime { inner : NativeTlsInner }
}
@ -53,6 +60,7 @@ crate::opaque::implement_opaque_runtime! {
AsyncStdRustlsRuntime { inner: RustlsInner }
}
#[cfg(all(feature = "native-tls"))]
impl AsyncStdNativeTlsRuntime {
/// Return a new [`AsyncStdNativeTlsRuntime`]
///
@ -103,6 +111,7 @@ impl AsyncStdRustlsRuntime {
}
/// Run a test function using a freshly created async_std runtime.
#[cfg(any(feature = "native-tls", feature = "rustls"))]
pub fn test_with_runtime<P, F, O>(func: P) -> O
where
P: FnOnce(PreferredRuntime) -> F,

View File

@ -11,4 +11,5 @@ pub(crate) mod tokio;
#[cfg(all(feature = "rustls"))]
pub(crate) mod rustls;
#[cfg(all(feature = "native-tls"))]
pub(crate) mod native_tls;

View File

@ -4,6 +4,7 @@ use crate::traits::{CertifiedConn, TlsConnector, TlsProvider};
use async_trait::async_trait;
use futures::{AsyncRead, AsyncWrite};
use native_tls_crate as native_tls;
use std::{
convert::TryInto,
io::{Error as IoError, Result as IoResult},

View File

@ -140,6 +140,10 @@
#![warn(clippy::unseparated_literal_suffix)]
#![deny(clippy::unwrap_used)]
#[cfg(all(
any(feature = "native-tls", feature = "rustls"),
any(feature = "async-std", feature = "tokio")
))]
pub(crate) mod impls;
pub mod task;
@ -148,7 +152,11 @@ mod opaque;
mod timer;
mod traits;
#[cfg(all(test, any(feature = "tokio", feature = "async-std")))]
#[cfg(all(
test,
any(feature = "native-tls", feature = "rustls"),
any(feature = "async-std", feature = "tokio")
))]
mod test;
pub use traits::{
@ -163,17 +171,21 @@ pub mod tls {
pub use crate::traits::{CertifiedConn, TlsConnector};
}
#[cfg(feature = "tokio")]
#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "tokio"))]
pub mod tokio;
#[cfg(feature = "async-std")]
#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "async-std"))]
pub mod async_std;
pub use compound::CompoundRuntime;
#[cfg(all(feature = "async_std", not(feature = "tokio")))]
#[cfg(all(
any(feature = "native-tls", feature = "rustls"),
feature = "async-std",
not(feature = "tokio")
))]
use async_std as preferred_backend_mod;
#[cfg(feature = "tokio")]
#[cfg(all(any(feature = "native-tls", feature = "rustls"), feature = "tokio"))]
use tokio as preferred_backend_mod;
/// The runtime that we prefer to use, out of all the runtimes compiled into the
@ -183,7 +195,10 @@ use tokio as preferred_backend_mod;
/// performance.
/// If `native_tls` and `rustls` are both available, we prefer `native_tls` since
/// it has been used in Arti for longer.
#[cfg(any(feature = "tokio", feature = "async-std"))]
#[cfg(all(
any(feature = "native-tls", feature = "rustls"),
any(feature = "async-std", feature = "tokio")
))]
pub use preferred_backend_mod::PreferredRuntime;
/// Try to return an instance of the currently running [`Runtime`].
@ -203,7 +218,10 @@ pub use preferred_backend_mod::PreferredRuntime;
///
/// Once you have a runtime returned by this function, you should
/// just create more handles to it via [`Clone`].
#[cfg(any(feature = "async-std", feature = "tokio"))]
#[cfg(all(
any(feature = "native-tls", feature = "rustls"),
any(feature = "async-std", feature = "tokio")
))]
pub fn current_user_runtime() -> std::io::Result<impl Runtime> {
PreferredRuntime::current()
}
@ -222,7 +240,10 @@ pub fn current_user_runtime() -> std::io::Result<impl Runtime> {
///
/// If you need more fine-grained control over a runtime, you can
/// create it using an appropriate builder type or function.
#[cfg(any(feature = "async-std", feature = "tokio"))]
#[cfg(all(
any(feature = "native-tls", feature = "rustls"),
any(feature = "async-std", feature = "tokio")
))]
pub fn create_runtime() -> std::io::Result<impl Runtime> {
PreferredRuntime::create()
}
@ -249,7 +270,11 @@ pub mod testing__ {
/// (This is a macro so that it can repeat the closure as two separate
/// expressions, so it can take on two different types, if needed.)
#[macro_export]
#[cfg(all(feature = "tokio", feature = "async-std"))]
#[cfg(all(
any(feature = "native-tls", feature = "rustls"),
feature = "tokio",
feature = "async-std"
))]
macro_rules! test_with_all_runtimes {
( $fn:expr ) => {{
use $crate::testing__::TestOutcome;
@ -260,7 +285,11 @@ macro_rules! test_with_all_runtimes {
/// Run a test closure, passing as argument every supported runtime.
#[macro_export]
#[cfg(all(feature = "tokio", not(feature = "async-std")))]
#[cfg(all(
any(feature = "native-tls", feature = "rustls"),
feature = "tokio",
not(feature = "async-std")
))]
macro_rules! test_with_all_runtimes {
( $fn:expr ) => {{
$crate::tokio::test_with_runtime($fn)
@ -269,7 +298,11 @@ macro_rules! test_with_all_runtimes {
/// Run a test closure, passing as argument every supported runtime.
#[macro_export]
#[cfg(all(not(feature = "tokio"), feature = "async-std"))]
#[cfg(all(
any(feature = "native-tls", feature = "rustls"),
not(feature = "tokio"),
feature = "async-std"
))]
macro_rules! test_with_all_runtimes {
( $fn:expr ) => {{
$crate::async_std::test_with_runtime($fn)
@ -291,7 +324,11 @@ macro_rules! test_with_one_runtime {
///
/// (Always prefers tokio if present.)
#[macro_export]
#[cfg(all(not(feature = "tokio"), feature = "async-std"))]
#[cfg(all(
any(feature = "native-tls", feature = "rustls"),
not(feature = "tokio"),
feature = "async-std"
))]
macro_rules! test_with_one_runtime {
( $fn:expr ) => {{
$crate::async_std::test_with_runtime($fn)

View File

@ -38,7 +38,11 @@ impl Future for YieldFuture {
}
}
#[cfg(all(test, any(feature = "tokio", feature = "async-std")))]
#[cfg(all(
test,
any(feature = "native-tls", feature = "rustls"),
any(feature = "tokio", feature = "async-std")
))]
mod test {
use super::yield_now;
use crate::test_with_all_runtimes;

View File

@ -6,6 +6,7 @@ use crate::traits::*;
use futures::io::{AsyncReadExt, AsyncWriteExt};
use futures::stream::StreamExt;
use native_tls_crate as native_tls;
use std::io::Result as IoResult;
use std::net::{Ipv4Addr, SocketAddrV4};
use std::time::{Duration, Instant, SystemTime};
@ -244,7 +245,7 @@ macro_rules! runtime_tests {
macro_rules! tls_runtime_tests {
{ $($id:ident),* $(,)? } => {
#[cfg(feature="tokio")]
#[cfg(all(feature="tokio", feature = "native-tls"))]
mod tokio_native_tls_tests {
use std::io::Result as IoResult;
$(
@ -254,7 +255,7 @@ macro_rules! tls_runtime_tests {
}
)*
}
#[cfg(feature="async-std")]
#[cfg(all(feature="async-std", feature = "native-tls"))]
mod async_std_native_tls_tests {
use std::io::Result as IoResult;
$(

View File

@ -1,10 +1,11 @@
//! Entry points for use with Tokio runtimes.
use crate::impls::native_tls::NativeTlsProvider;
use crate::impls::tokio::TokioRuntimeHandle as Handle;
use crate::{CompoundRuntime, SpawnBlocking};
use std::io::{Error as IoError, ErrorKind, Result as IoResult};
#[cfg(feature = "native-tls")]
use crate::impls::native_tls::NativeTlsProvider;
#[cfg(feature = "rustls")]
use crate::impls::rustls::RustlsProvider;
use crate::impls::tokio::net::TcpStream;
@ -18,7 +19,10 @@ use crate::impls::tokio::net::TcpStream;
/// Currently, `native_tls` is preferred over `rustls` when both are available,
/// because of its maturity within Arti. However, this might change in the
/// future.
#[cfg(feature = "native-tls")]
pub use TokioNativeTlsRuntime as PreferredRuntime;
#[cfg(all(feature = "rustls", not(feature = "native-tls")))]
pub use TokioRustlsRuntime as PreferredRuntime;
/// A [`Runtime`] built around a Handle to a tokio runtime, and `native_tls`.
///
@ -28,12 +32,14 @@ pub use TokioNativeTlsRuntime as PreferredRuntime;
/// implementations for Tokio's time, net, and io facilities, but we have
/// no good way to check that when creating this object.
#[derive(Clone)]
#[cfg(feature = "native-tls")]
pub struct TokioNativeTlsRuntime {
/// The actual [`CompoundRuntime`] that implements this.
inner: HandleInner,
}
/// Implementation type for a TokioRuntimeHandle.
#[cfg(feature = "native-tls")]
type HandleInner = CompoundRuntime<Handle, Handle, Handle, NativeTlsProvider<TcpStream>>;
/// A [`Runtime`] built around a Handle to a tokio runtime, and `rustls`.
@ -48,6 +54,7 @@ pub struct TokioRustlsRuntime {
#[cfg(feature = "rustls")]
type RustlsHandleInner = CompoundRuntime<Handle, Handle, Handle, RustlsProvider<TcpStream>>;
#[cfg(feature = "native-tls")]
crate::opaque::implement_opaque_runtime! {
TokioNativeTlsRuntime { inner : HandleInner }
}
@ -57,6 +64,7 @@ crate::opaque::implement_opaque_runtime! {
TokioRustlsRuntime { inner : RustlsHandleInner }
}
#[cfg(feature = "native-tls")]
impl From<tokio_crate::runtime::Handle> for TokioNativeTlsRuntime {
fn from(h: tokio_crate::runtime::Handle) -> Self {
let h = Handle::new(h);
@ -76,6 +84,7 @@ impl From<tokio_crate::runtime::Handle> for TokioRustlsRuntime {
}
}
#[cfg(feature = "native-tls")]
impl TokioNativeTlsRuntime {
/// Create a new [`TokioNativeTlsRuntime`].
///
@ -142,6 +151,7 @@ impl TokioRustlsRuntime {
}
/// As `Handle::try_current()`, but return an IoError on failure.
#[cfg(any(feature = "native-tls", feature = "rustls"))]
fn current_handle() -> std::io::Result<tokio_crate::runtime::Handle> {
tokio_crate::runtime::Handle::try_current().map_err(|e| IoError::new(ErrorKind::Other, e))
}
@ -151,6 +161,7 @@ fn current_handle() -> std::io::Result<tokio_crate::runtime::Handle> {
/// # Panics
///
/// Panics if we can't create a tokio runtime.
#[cfg(any(feature = "native-tls", feature = "rustls"))]
pub fn test_with_runtime<P, F, O>(func: P) -> O
where
P: FnOnce(PreferredRuntime) -> F,

View File

@ -22,4 +22,4 @@ tor-rtcompat = { version = "0.0.3", path = "../tor-rtcompat" }
[dev-dependencies]
futures-await-test = "0.3.0"
rand = "0.8"
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio"] }
tor-rtcompat = { path="../tor-rtcompat", version = "0.0.3", features=["tokio", "native-tls" ] }