hyper_http_client_example/
hyper-http-client-example.rs

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};
8
9use arti_client::{TorClient, TorClientConfig};
10
11const TEST_URL: &str = "https://check.torproject.org/api/ip";
12
13#[tokio::main]
14async fn main() -> Result<()> {
15    // Arti uses the `tracing` crate for logging. Install a handler for this, to print Arti's logs.
16    // (You'll need to set RUST_LOG=info as an environment variable to actually see much; also try
17    // =debug for more detailed logging.)
18    tracing_subscriber::fmt::init();
19
20    // You can run this example with any arbitrary HTTP/1.1 (raw or within TLS) URL, but we'll default to check.torproject.org
21    // because it's a good way of demonstrating that the connection is via Tor.
22    let url: Uri = std::env::args()
23        .nth(1)
24        .unwrap_or_else(|| TEST_URL.into())
25        .parse()?;
26    let host = url.host().unwrap();
27    let https = url.scheme() == Some(&Scheme::HTTPS);
28
29    // The client config includes things like where to store persistent Tor network state.
30    // The defaults provided are the same as the Arti standalone application, and save data
31    // to a conventional place depending on operating system (for example, ~/.local/share/arti
32    // on Linux platforms)
33    let config = TorClientConfig::default();
34
35    // We now let the Arti client start and bootstrap a connection to the network.
36    // (This takes a while to gather the necessary consensus state, etc.)
37    let tor_client = TorClient::create_bootstrapped(config).await?;
38
39    let port = match url.port_u16() {
40        Some(port) => port,
41        _ if https => 443,
42        _ => 80,
43    };
44
45    let stream = tor_client.connect((host, port)).await?;
46
47    // Following part is just standard usage of Hyper.
48    eprintln!("[+] Making request to: {}", url);
49
50    if https {
51        let cx = tokio_native_tls::native_tls::TlsConnector::builder().build()?;
52        let cx = tokio_native_tls::TlsConnector::from(cx);
53        let stream = cx.connect(host, stream).await?;
54        make_request(host, stream).await
55    } else {
56        make_request(host, stream).await
57    }
58}
59
60async fn make_request(
61    host: &str,
62    stream: impl AsyncRead + AsyncWrite + Unpin + Send + 'static,
63) -> Result<()> {
64    let (mut request_sender, connection) =
65        hyper::client::conn::http1::handshake(TokioIo::new(stream)).await?;
66
67    // Spawn a task to poll the connection and drive the HTTP state.
68    tokio::spawn(async move {
69        connection.await.unwrap();
70    });
71
72    let mut resp = request_sender
73        .send_request(
74            Request::builder()
75                .header("Host", host)
76                .uri(TEST_URL)
77                .method("GET")
78                .body(Empty::<Bytes>::new())?,
79        )
80        .await?;
81
82    eprintln!("[+] Response status: {}", resp.status());
83    while let Some(frame) = resp.body_mut().frame().await {
84        let bytes = frame?.into_data().unwrap();
85        eprintln!("[+] Response body:\n\n{}", std::str::from_utf8(&bytes)?);
86    }
87
88    Ok(())
89}