From 864fd03917a0409c6a0380c258d96252359a559b Mon Sep 17 00:00:00 2001 From: eta Date: Wed, 11 May 2022 13:26:35 +0100 Subject: [PATCH] Improve documentation around Cargo features; make Runtime require Debug - arti#445 highlighted the lack of good documentation around Arti's multiple runtime support, as well as it being difficult to determine what runtime was actually in use. - Improve the documentation to solve the first problem. - To solve the second problem, make Runtime require Debug (which is arguably a good idea anyway, since it makes them easier to embed in things), and print out the current runtime's Debug information when arti is invoked with `--version`. - (It also prints out other Cargo features, too!) fixes arti#445 --- README.md | 26 ++++++++++- crates/arti/src/lib.rs | 63 ++++++++++++++++++++------ crates/tor-rtcompat/src/traits.rs | 3 ++ crates/tor-rtmock/src/lib.rs | 2 + crates/tor-rtmock/src/net.rs | 8 ++++ crates/tor-rtmock/src/net_runtime.rs | 2 +- crates/tor-rtmock/src/sleep_runtime.rs | 2 +- crates/tor-rtmock/src/time.rs | 8 ++++ doc/TROUBLESHOOTING.md | 34 ++++++++++++++ doc/semver_status.md | 4 ++ 10 files changed, 134 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 3365df98b..ad687efdd 100644 --- a/README.md +++ b/README.md @@ -67,17 +67,39 @@ follow Rust's semantic versioning best practices.) Arti can act as a SOCKS proxy that uses the Tor network. -To try it out, run the demo program in `arti` as follows. It will open a +To try it out, compile and run the `arti` binary using the below. It will open a SOCKS proxy on port 9150. - % cargo run --release -- proxy + $ cargo run -p arti --release -- proxy Again, do not use this program yet if you seriously need anonymity, privacy, security, or stability. +You can build a binary (but not run it) with: + + $ cargo build -p arti --release + +The result can be found as `target/release/arti`. + If you run into any trouble building the program, please have a look at [the troubleshooting guide](doc/TROUBLESHOOTING.md). +### Custom compile-time options + +Arti has a number of configurable [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html) that, +among other things, can affect which asynchronous runtime to use. Use + + $ cargo doc -p arti --open + +to view the Arti crate-level docs in your browser, which contain a full list. + +You can pass these features to Cargo while building with `--features` (note that you might need `--no-default-features` +in order to not use the default runtime choices, too). For example, to use `async-std` instead of Tokio: + + $ cargo run -p arti --no-default-features --features async-std,native-tls -- proxy + +Use `target/release/arti --version` to see what features the currently built Arti binary is using. + ## Minimum supported Rust Version Our current Minimum Supported Rust Version (MSRV) is 1.56. diff --git a/crates/arti/src/lib.rs b/crates/arti/src/lib.rs index eb48c2c22..2b4c34978 100644 --- a/crates/arti/src/lib.rs +++ b/crates/arti/src/lib.rs @@ -140,6 +140,39 @@ use tracing::{info, warn}; /// Shorthand for a boxed and pinned Future. type PinnedFuture = std::pin::Pin>>; +/// Create a runtime for Arti to use. +fn create_runtime() -> std::io::Result { + cfg_if::cfg_if! { + if #[cfg(all(feature="tokio", feature="native-tls"))] { + use tor_rtcompat::tokio::TokioNativeTlsRuntime as ChosenRuntime; + } else if #[cfg(all(feature="tokio", feature="rustls"))] { + use tor_rtcompat::tokio::TokioRustlsRuntime as ChosenRuntime; + } else if #[cfg(all(feature="async-std", feature="native-tls"))] { + use tor_rtcompat::async_std::AsyncStdNativeTlsRuntime as ChosenRuntime; + } else if #[cfg(all(feature="async-std", feature="rustls"))] { + use tor_rtcompat::async_std::AsyncStdRustlsRuntime as ChosenRuntime; + } else { + compile_error!("You must configure both an async runtime and a TLS stack. See doc/TROUBLESHOOTING.md for more."); + } + } + ChosenRuntime::create() +} + +/// Return a (non-exhaustive) array of enabled Cargo features, for version printing purposes. +fn list_enabled_features() -> &'static [&'static str] { + // HACK(eta): We can't get this directly, so we just do this awful hack instead. + // Note that we only list features that aren't about the runtime used, since that already + // gets printed separately. + &[ + #[cfg(feature = "journald")] + "journald", + #[cfg(feature = "static-sqlite")] + "static-sqlite", + #[cfg(feature = "static-native-tls")] + "static-native-tls", + ] +} + /// Run the main loop of the proxy. /// /// # Panics @@ -234,9 +267,25 @@ pub fn main_main() -> Result<()> { config_file_help.push_str(&format!(" Defaults to {:?}", default)); } + // We create the runtime now so that we can use its `Debug` impl to describe it for + // the version string. + let runtime = create_runtime()?; + let features = list_enabled_features(); + let long_version = format!( + "{}\nusing runtime: {:?}\noptional features: {}", + env!("CARGO_PKG_VERSION"), + runtime, + if features.is_empty() { + "".into() + } else { + features.join(", ") + } + ); + let matches = App::new("Arti") .version(env!("CARGO_PKG_VERSION")) + .long_version(&long_version as &str) .author("The Tor Project Developers") .about("A Rust Tor implementation.") // HACK(eta): clap generates "arti [OPTIONS] " for this usage string by @@ -379,20 +428,6 @@ pub fn main_main() -> Result<()> { process::use_max_file_limit(&config); - cfg_if::cfg_if! { - if #[cfg(all(feature="tokio", feature="native-tls"))] { - use tor_rtcompat::tokio::TokioNativeTlsRuntime as ChosenRuntime; - } else if #[cfg(all(feature="tokio", feature="rustls"))] { - use tor_rtcompat::tokio::TokioRustlsRuntime as ChosenRuntime; - } else if #[cfg(all(feature="async-std", feature="native-tls"))] { - use tor_rtcompat::async_std::AsyncStdNativeTlsRuntime as ChosenRuntime; - } else if #[cfg(all(feature="async-std", feature="rustls"))] { - use tor_rtcompat::async_std::AsyncStdRustlsRuntime as ChosenRuntime; - } - } - - let runtime = ChosenRuntime::create()?; - let rt_copy = runtime.clone(); rt_copy.block_on(run( runtime, diff --git a/crates/tor-rtcompat/src/traits.rs b/crates/tor-rtcompat/src/traits.rs index 0ec17c8e2..9ca8caeb3 100644 --- a/crates/tor-rtcompat/src/traits.rs +++ b/crates/tor-rtcompat/src/traits.rs @@ -3,6 +3,7 @@ use async_trait::async_trait; use futures::stream; use futures::task::Spawn; use futures::{AsyncRead, AsyncWrite, Future}; +use std::fmt::Debug; use std::io::Result as IoResult; use std::net::SocketAddr; use std::time::{Duration, Instant, SystemTime}; @@ -53,6 +54,7 @@ pub trait Runtime: + TcpProvider + TlsProvider + UdpProvider + + Debug + 'static { } @@ -67,6 +69,7 @@ impl Runtime for T where + TcpProvider + TlsProvider + UdpProvider + + Debug + 'static { } diff --git a/crates/tor-rtmock/src/lib.rs b/crates/tor-rtmock/src/lib.rs index ed6971cfa..81a8e2596 100644 --- a/crates/tor-rtmock/src/lib.rs +++ b/crates/tor-rtmock/src/lib.rs @@ -137,6 +137,8 @@ #![warn(clippy::unseparated_literal_suffix)] #![deny(clippy::unwrap_used)] +extern crate core; + pub mod io; pub mod net; pub mod time; diff --git a/crates/tor-rtmock/src/net.rs b/crates/tor-rtmock/src/net.rs index 5575e2475..c9f98c785 100644 --- a/crates/tor-rtmock/src/net.rs +++ b/crates/tor-rtmock/src/net.rs @@ -7,6 +7,7 @@ use super::io::{stream_pair, LocalStream}; use super::MockNetRuntime; +use core::fmt; use tor_rtcompat::tls::TlsConnector; use tor_rtcompat::{CertifiedConn, Runtime, TcpListener, TcpProvider, TlsProvider}; @@ -18,6 +19,7 @@ use futures::sink::SinkExt; use futures::stream::{Stream, StreamExt}; use futures::FutureExt; use std::collections::HashMap; +use std::fmt::Formatter; use std::io::{Error as IoError, ErrorKind, Result as IoResult}; use std::net::{IpAddr, SocketAddr}; use std::pin::Pin; @@ -103,6 +105,12 @@ pub struct MockNetProvider { inner: Arc, } +impl fmt::Debug for MockNetProvider { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MockNetProvider").finish_non_exhaustive() + } +} + /// Shared part of a MockNetworkProvider. /// /// This is separate because providers need to implement Clone, but diff --git a/crates/tor-rtmock/src/net_runtime.rs b/crates/tor-rtmock/src/net_runtime.rs index 12b706cbf..0f9c9cdc6 100644 --- a/crates/tor-rtmock/src/net_runtime.rs +++ b/crates/tor-rtmock/src/net_runtime.rs @@ -16,7 +16,7 @@ use std::time::{Duration, Instant, SystemTime}; /// A wrapper Runtime that overrides the SleepProvider trait for the /// underlying runtime. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct MockNetRuntime { /// The underlying runtime. Most calls get delegated here. runtime: R, diff --git a/crates/tor-rtmock/src/sleep_runtime.rs b/crates/tor-rtmock/src/sleep_runtime.rs index bcbc9c306..bc4298f04 100644 --- a/crates/tor-rtmock/src/sleep_runtime.rs +++ b/crates/tor-rtmock/src/sleep_runtime.rs @@ -14,7 +14,7 @@ use tracing::trace; /// A wrapper Runtime that overrides the SleepProvider trait for the /// underlying runtime. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct MockSleepRuntime { /// The underlying runtime. Most calls get delegated here. runtime: R, diff --git a/crates/tor-rtmock/src/time.rs b/crates/tor-rtmock/src/time.rs index c4d703e7b..8b93061d1 100644 --- a/crates/tor-rtmock/src/time.rs +++ b/crates/tor-rtmock/src/time.rs @@ -9,6 +9,7 @@ use std::{ cmp::{Eq, Ordering, PartialEq, PartialOrd}, collections::BinaryHeap, + fmt, pin::Pin, sync::{Arc, Mutex, Weak}, task::{Context, Poll, Waker}, @@ -19,6 +20,7 @@ use futures::Future; use tracing::trace; use std::collections::HashSet; +use std::fmt::Formatter; use tor_rtcompat::SleepProvider; /// A dummy [`SleepProvider`] instance for testing. @@ -35,6 +37,12 @@ pub struct MockSleepProvider { state: Arc>, } +impl fmt::Debug for MockSleepProvider { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("MockSleepProvider").finish_non_exhaustive() + } +} + /// Shared backend for sleep provider and Sleeping futures. struct SleepSchedule { /// What time do we pretend it is (monotonic)? This value only diff --git a/doc/TROUBLESHOOTING.md b/doc/TROUBLESHOOTING.md index 1e90fe583..d5193463b 100644 --- a/doc/TROUBLESHOOTING.md +++ b/doc/TROUBLESHOOTING.md @@ -51,3 +51,37 @@ then you might be hitting a on native_tls on Alpine linux. You can solve this by building with `RUSTFLAGS=-Ctarget-feature=-crt-static`. + +### I get an error about "configuring both an async runtime and a TLS stack" when building Arti! + +If you're getting + +``` +error: You must configure both an async runtime and a TLS stack. See doc/TROUBLESHOOTING.md for more. +``` + +...then the combination of [Cargo features](https://doc.rust-lang.org/cargo/reference/features.html) (`--features`) you +you configured while compiling Arti doesn't specify both an asynchronous runtime *and* a TLS stack to use. + +You must choose: + +- at least one asynchronous runtime (features `tokio`, `async-std`) + - (if you choose both, `tokio` will be used) +- at least one TLS stack (features `native-tls`, `rustls`) + - (if you choose both, `native-tls` will be used) + +Pass these in with the `--features` argument when compiling (e.g. `--features tokio,native-tls`). + +Note that Arti configures Tokio and native-tls by default, so if you're gettnig this error, you probably tried to do +something fancy with `--no-default-features`. + +### Arti isn't respecting my custom runtime choice! + +Make sure you're building just the Arti binary, and not the whole workspace; to do this, you'll need to specify `-p arti` +when invoking Cargo, e.g. + + $ cargo build -p arti --no-default-features --features async-std,native-tls + +You can verify which runtime is being used by passing `--version` to Arti, e.g. + + $ target/release/arti --version diff --git a/doc/semver_status.md b/doc/semver_status.md index bb0c04e8a..2982b3989 100644 --- a/doc/semver_status.md +++ b/doc/semver_status.md @@ -39,3 +39,7 @@ BREAKING: Routerstatus::nickname() now returns &str, not &String. +BREAKING: Replaced from_path with from_path_and_mistrust +### tor-rtcompat + +BREAKING: Runtime now requires the Debug trait to be implemented. +