main.rs
 1  use anyhow::Result;
 2  use http_body_util::{BodyExt, Empty};
 3  use hyper::body::Bytes;
 4  use hyper::http::uri::Scheme;
 5  use hyper::{Request, Uri};
 6  use hyper_util::rt::TokioIo;
 7  use tokio::io::{AsyncRead, AsyncWrite};
 8  use tokio_native_tls::native_tls::TlsConnector;
 9  
10  use arti_client::{TorClient, TorClientConfig};
11  
12  #[tokio::main]
13  async 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.)
17      tracing_subscriber::fmt::init();
18  
19      // 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.
21      let url: Uri = std::env::args()
22          .nth(1)
23          .unwrap_or_else(|| "https://icanhazip.com".into())
24          .parse()?;
25      let host = url.host().unwrap();
26      let https = url.scheme() == Some(&Scheme::HTTPS);
27  
28      eprintln!("starting Arti...");
29  
30      // 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)
34      let config = TorClientConfig::default();
35  
36      // 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.)
38      let tor_client = TorClient::create_bootstrapped(config).await?;
39  
40      let port = match url.port_u16() {
41          Some(port) => port,
42          _ if https => 443,
43          _ => 80,
44      };
45  
46      let stream = tor_client.connect((host, port)).await?;
47  
48      // The rest is just standard usage of Hyper.
49      eprintln!("requesting {} via Tor...", url);
50  
51      if https {
52          let cx = TlsConnector::builder().build()?;
53          let cx = tokio_native_tls::TlsConnector::from(cx);
54          let stream = cx.connect(host, stream).await?;
55          make_request(host, stream).await
56      } else {
57          make_request(host, stream).await
58      }
59  }
60  
61  async fn make_request(
62      host: &str,
63      stream: impl AsyncRead + AsyncWrite + Unpin + Send + 'static,
64  ) -> Result<()> {
65      let (mut request_sender, connection) =
66          hyper::client::conn::http1::handshake(TokioIo::new(stream)).await?;
67  
68      // spawn a task to poll the connection and drive the HTTP state
69      tokio::spawn(async move {
70          connection.await.unwrap();
71      });
72  
73      let 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?;
81  
82      eprintln!("status: {}", resp.status());
83  
84      while let Some(frame) = resp.body_mut().frame().await {
85          let bytes = frame?.into_data().unwrap();
86          eprintln!("body: {}", std::str::from_utf8(&bytes)?);
87      }
88  
89      Ok(())
90  }