Improve docs of more (potentially re-exported) arti-client types

Most of the structs in `arti-client` have example code now, to give a
clearer idea of how they're used.

Annoyingly, a lot of the types exposed in `arti-client` are actually
re-exports, which makes documentation a bit harder: example code that
references other parts of `arti-client` can't actually be run as a
doctest, since the crate it's in is a dependency of `arti-client`.

We might be able to fix this in future by doing the documentation in
`arti-client` itself, but rustdoc seems to have some weird behaviours
there that need to be investigated first (for example, it seems to merge
the re-export and original documentation, and also put the re-export
documentation on the `impl` block for some reason).

For now, though, this commit just writes the docs from the point of view
of an `arti-client` consumer, removing notes specific to the crate in
which they're defined. It's not ideal, but at least the end user
experience is decent.
This commit is contained in:
eta 2021-10-29 14:06:06 +01:00
parent c11fe200d1
commit a12fffc66a
4 changed files with 130 additions and 31 deletions

View File

@ -9,6 +9,8 @@ use thiserror::Error;
/// An object that can be converted to a [`TorAddr`] with a minimum
/// of risk.
///
/// [*See also: the `TorAddr` documentation.*](TorAddr)
pub trait IntoTorAddr {
/// Try to make a [`TorAddr`] to represent connecting to this
/// address.
@ -24,6 +26,8 @@ pub trait IntoTorAddr {
/// where did you get the [`SocketAddr`] in the first place? If it
/// comes from a local DNS lookup, then you have leaked the address
/// you were resolving to your DNS resolver, and probably your ISP.
///
/// [*See also: the `TorAddr` documentation.*](TorAddr)
pub trait DangerouslyIntoTorAddr {
/// Try to make a [`TorAddr`] to represent connecting to this
/// address.
@ -45,6 +49,47 @@ pub trait DangerouslyIntoTorAddr {
/// In order to discourage local hostname lookups, the functions that
/// construct a [`TorAddr`] from [`IpAddr`], [`SocketAddr`], and so
/// forth are labeled as "dangerous".
///
/// # Examples
///
/// Making a `TorAddr` from various "safe" sources:
///
/// ```rust
/// # use anyhow::Result;
/// # fn main() -> Result<()> {
/// use arti_client::IntoTorAddr;
///
/// let example_from_tuple = ("example.com", 80).into_tor_addr()?;
/// let example_from_string = "example.com:80".into_tor_addr()?;
///
/// assert_eq!(example_from_tuple, example_from_string);
/// # Ok(())
/// # }
/// ```
///
/// Making a `TorAddr` from an IP address and port:
///
/// > **Warning:** This example is only safe because we're not doing a DNS lookup; rather, the
/// > intent is to connect to a hardcoded IP address.
/// > If you're using [`DangerouslyIntoTorAddr`], pay careful attention to where your IP addresses
/// > are coming from, and whether there's a risk of information leakage.
///
/// ```rust
/// # use anyhow::Result;
/// # fn main() -> Result<()> {
/// use arti_client::DangerouslyIntoTorAddr;
/// use std::net::{IpAddr, SocketAddr};
///
/// let quad_one_dns: SocketAddr = "1.1.1.1:53".parse()?;
/// let addr_from_socketaddr = quad_one_dns.into_tor_addr_dangerously()?;
///
/// let quad_one_ip: IpAddr = "1.1.1.1".parse()?;
/// let addr_from_tuple = (quad_one_ip, 53).into_tor_addr_dangerously()?;
///
/// assert_eq!(addr_from_socketaddr, addr_from_tuple);
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TorAddr {
/// The target host.

View File

@ -180,18 +180,6 @@ pub use client::{ConnectPrefs, TorClient};
pub use config::TorClientConfig;
pub use tor_circmgr::IsolationToken;
/// An anonymized stream over the Tor network.
///
/// For most purposes, you can think of this type as an anonymized
/// TCP stream: it can read and write data, and get closed when it's done.
///
/// To get one of these, clients should use [`TorClient::connect()`].
/// [`DataStream`] implements [`futures::io::AsyncRead`] and
/// [`futures::io::AsyncWrite`], so you can use it anywhere that those
/// types are expected.
///
/// This type is a re-export from [`tor_proto::stream::DataStream`];
/// see that crate for its documentation in a more low-level context.
pub use tor_proto::stream::DataStream;
mod err;

View File

@ -66,10 +66,44 @@ impl TargetPort {
/// [`IsolationToken::no_isolation`]. However, tokens created with
/// [`IsolationToken::no_isolation`] are all equal to one another.
///
/// # Semver note
/// # Examples
///
/// This type is re-exported by `arti-client`: any changes to it must be
/// reflected in `arti-client`'s version.
/// Creating distinct isolation tokens:
///
/// ```rust
/// # use tor_circmgr::IsolationToken;
/// let token_1 = IsolationToken::new();
/// let token_2 = IsolationToken::new();
///
/// assert_ne!(token_1, token_2);
///
/// // Demonstrating the behaviour of no_isolation() tokens:
/// assert_ne!(token_1, IsolationToken::no_isolation());
/// assert_eq!(IsolationToken::no_isolation(), IsolationToken::no_isolation());
/// ```
///
/// Using an isolation token to route streams differently over the Tor network:
///
/// ```ignore
/// use arti_client::ConnectPrefs;
///
/// let token_1 = IsolationToken::new();
/// let token_2 = IsolationToken::new();
///
/// let mut prefs_1 = ConnectPrefs::new();
/// prefs_1.set_isolation_group(token_1);
///
/// let mut prefs_2 = ConnectPrefs::new();
/// prefs_2.set_isolation_group(token_2);
///
/// // These two connections will come from different source IP addresses.
/// tor_client.connect(("example.com", 80), Some(prefs_1)).await?;
/// tor_client.connect(("example.com", 80), Some(prefs_2)).await?;
/// ```
// # Semver note
//
// This type is re-exported by `arti-client`: any changes to it must be
// reflected in `arti-client`'s version.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct IsolationToken(u64);

View File

@ -22,31 +22,63 @@ use std::sync::Arc;
use tor_cell::relaycell::msg::{Data, RelayMsg};
/// A DataStream is a Tor stream packaged so as to be useful for
/// byte-oriented IO.
/// An anonymized stream over the Tor network.
///
/// It's suitable for use with BEGIN or BEGIN_DIR streams.
/// For most purposes, you can think of this type as an anonymized
/// TCP stream: it can read and write data, and get closed when it's done.
///
/// # Note for tokio users
/// [`DataStream`] implements [`futures::io::AsyncRead`] and
/// [`futures::io::AsyncWrite`], so you can use it anywhere that those
/// traits are expected.
///
/// By default, this type implements only the versions of `AsyncRead`
/// and `AsyncWrite` traits from the [`futures`] crate. If you need
/// it to implement the `tokio` versions of those traits, make sure
/// this crate is built with the `tokio` feature.
/// # Examples
///
/// Connecting to an HTTP server and sending a request, using
/// [`AsyncWriteExt::write_all`](futures::io::AsyncWriteExt::write_all):
///
/// ```ignore
/// let mut stream = tor_client.connect(("icanhazip.com", 80), None).await?;
///
/// use futures::io::AsyncWriteExt;
///
/// stream
/// .write_all(b"GET / HTTP/1.1\r\nHost: icanhazip.com\r\nConnection: close\r\n\r\n")
/// .await?;
///
/// // Flushing the stream is important; see below!
/// stream.flush().await?;
/// ```
///
/// Reading the result, using [`AsyncReadExt::read_to_end`](futures::io::AsyncReadExt::read_to_end):
///
/// ```ignore
/// use futures::io::AsyncReadExt;
///
/// let mut buf = Vec::new();
/// stream.read_to_end(&mut buf).await?;
///
/// println!("{}", String::from_utf8_lossy(&buf));
/// ```
///
/// # Usage with Tokio
///
/// If the `tokio` crate feature is enabled, this type also implements
/// [`tokio::io::AsyncRead`](tokio_crate::io::AsyncRead) and
/// [`tokio::io::AsyncWrite`](tokio_crate::io::AsyncWrite) for easier integration
/// with code that expects those traits.
///
/// # Remember to call `flush`!
///
/// DataStream buffers data internally, in order to write as few cells
/// as possible onto the network. In order to make sure that your
/// data has actually been sent, you need to call make sure that
/// `poll_flush` runs to completion: probably via
/// data has actually been sent, you need to make sure that
/// [`AsyncWrite::poll_flush`] runs to completion: probably via
/// [`AsyncWriteExt::flush`](futures::io::AsyncWriteExt::flush).
///
/// # Semver note
///
/// Note that this type is re-exported as a part of the public API of
/// the `arti-client` crate. Any changes to its API here in
/// `tor-proto` need to be reflected above.
// # Semver note
//
// Note that this type is re-exported as a part of the public API of
// the `arti-client` crate. Any changes to its API here in
// `tor-proto` need to be reflected above.
pub struct DataStream {
/// Underlying writer for this stream
w: DataWriter,