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:
parent
c11fe200d1
commit
a12fffc66a
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue