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
This commit is contained in:
eta 2022-05-11 13:26:35 +01:00
parent 0597c31a6f
commit 864fd03917
10 changed files with 134 additions and 18 deletions

View File

@ -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.

View File

@ -140,6 +140,39 @@ use tracing::{info, warn};
/// Shorthand for a boxed and pinned Future.
type PinnedFuture<T> = std::pin::Pin<Box<dyn futures::Future<Output = T>>>;
/// Create a runtime for Arti to use.
fn create_runtime() -> std::io::Result<impl Runtime> {
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() {
"<none>".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] <SUBCOMMAND>" 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,

View File

@ -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<Self::TcpStream>
+ UdpProvider
+ Debug
+ 'static
{
}
@ -67,6 +69,7 @@ impl<T> Runtime for T where
+ TcpProvider
+ TlsProvider<Self::TcpStream>
+ UdpProvider
+ Debug
+ 'static
{
}

View File

@ -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;

View File

@ -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<MockNetProviderInner>,
}
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

View File

@ -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<R: Runtime> {
/// The underlying runtime. Most calls get delegated here.
runtime: R,

View File

@ -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<R: Runtime> {
/// The underlying runtime. Most calls get delegated here.
runtime: R,

View File

@ -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<Mutex<SleepSchedule>>,
}
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

View File

@ -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

View File

@ -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.