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 }