1use anyhow::Result;
2use http_body_util::{BodyExt, Empty};
3use hyper::body::Bytes;
4use hyper::http::uri::Scheme;
5use hyper::{Request, Uri};
6use hyper_util::rt::TokioIo;
7use tokio::io::{AsyncRead, AsyncWrite};
8use tokio_native_tls::native_tls::TlsConnector;
910use arti_client::{TorClient, TorClientConfig};
1112#[tokio::main]
13async fn main() -> Result<()> {
14// Arti uses the `tracing` crate for logging. Install a handler for this, to print Arti's logs.
15 // (You'll need to set RUST_LOG=info as an environment variable to actually see much; also try
16 // =debug for more detailed logging.)
17tracing_subscriber::fmt::init();
1819// You can run this example with any arbitrary HTTP/1.1 (raw or within TLS) URL, but we'll default to icanhazip
20 // because it's a good way of demonstrating that the connection is via Tor.
21let url: Uri = std::env::args()
22 .nth(1)
23 .unwrap_or_else(|| "https://icanhazip.com".into())
24 .parse()?;
25let host = url.host().unwrap();
26let https = url.scheme() == Some(&Scheme::HTTPS);
2728eprintln!("starting Arti...");
2930// The client config includes things like where to store persistent Tor network state.
31 // The defaults provided are the same as the Arti standalone application, and save data
32 // to a conventional place depending on operating system (for example, ~/.local/share/arti
33 // on Linux platforms)
34let config = TorClientConfig::default();
3536// We now let the Arti client start and bootstrap a connection to the network.
37 // (This takes a while to gather the necessary consensus state, etc.)
38let tor_client = TorClient::create_bootstrapped(config).await?;
3940let port = match url.port_u16() {
41Some(port) => port,
42_ if https => 443,
43_ => 80,
44 };
4546let stream = tor_client.connect((host, port)).await?;
4748// The rest is just standard usage of Hyper.
49eprintln!("requesting {} via Tor...", url);
5051if https {
52let cx = TlsConnector::builder().build()?;
53let cx = tokio_native_tls::TlsConnector::from(cx);
54let stream = cx.connect(host, stream).await?;
55 make_request(host, stream).await
56} else {
57 make_request(host, stream).await
58}
59}
6061async fn make_request(
62 host: &str,
63 stream: impl AsyncRead + AsyncWrite + Unpin + Send + 'static,
64) -> Result<()> {
65let (mut request_sender, connection) =
66 hyper::client::conn::http1::handshake(TokioIo::new(stream)).await?;
6768// spawn a task to poll the connection and drive the HTTP state
69tokio::spawn(async move {
70 connection.await.unwrap();
71 });
7273let mut resp = request_sender
74 .send_request(
75 Request::builder()
76 .header("Host", host)
77 .method("GET")
78 .body(Empty::<Bytes>::new())?,
79 )
80 .await?;
8182eprintln!("status: {}", resp.status());
8384while let Some(frame) = resp.body_mut().frame().await {
85let bytes = frame?.into_data().unwrap();
86eprintln!("body: {}", std::str::from_utf8(&bytes)?);
87 }
8889Ok(())
90}