lazy-init.rs 2.75 KB
Newer Older
1
// @@ begin test lint list maintained by maint/add_warning @@
2
3
4
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
5
6
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
7
#![allow(clippy::single_char_pattern)]
8
#![allow(clippy::unwrap_used)]
9
#![allow(clippy::unchecked_duration_subtraction)]
10
11
//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->

12
use anyhow::Result;
Nick Mathewson's avatar
Nick Mathewson committed
13
use arti_client::{TorClient, TorClientConfig};
14
15
16
17
use tokio_crate as tokio;

use futures::io::{AsyncReadExt, AsyncWriteExt};
use once_cell::sync::OnceCell;
18
use tor_rtcompat::PreferredRuntime;
19

20
static TOR_CLIENT: OnceCell<TorClient<PreferredRuntime>> = OnceCell::new();
21
22
23
24
25
26
27
28

/// Get a `TorClient` by copying the globally shared client stored in `TOR_CLIENT`.
/// If that client hasn't been initialized yet, initializes it first.
///
/// # Errors
///
/// Errors if called outside a Tokio runtime context, or creating the Tor client
/// failed.
29
pub fn get_tor_client() -> Result<TorClient<PreferredRuntime>> {
30
31
32
33
34
35
36
37
38
39
    let client = TOR_CLIENT.get_or_try_init(|| -> Result<TorClient<_>> {
        // The client config includes things like where to store persistent Tor network state.
        // The defaults provided are the same as the Arti standalone application, and save data
        // to a conventional place depending on operating system (for example, ~/.local/share/arti
        // on Linux platforms)
        let config = TorClientConfig::default();

        eprintln!("creating unbootstrapped Tor client");

        // Create an unbootstrapped Tor client. Bootstrapping will happen when the client is used,
Nick Mathewson's avatar
Nick Mathewson committed
40
        // since `BootstrapBehavior::OnDemand` is the default.
41
        Ok(TorClient::builder()
Nick Mathewson's avatar
Nick Mathewson committed
42
43
            .config(config)
            .create_unbootstrapped()?)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
    })?;

    Ok(client.clone())
}

#[tokio::main]
async fn main() -> Result<()> {
    // Arti uses the `tracing` crate for logging. Install a handler for this, to print Arti's logs.
    tracing_subscriber::fmt::init();

    eprintln!("getting shared Tor client...");

    let tor_client = get_tor_client()?;

    eprintln!("connecting to example.com...");

    // Initiate a connection over Tor to example.com, port 80.
    let mut stream = tor_client.connect(("example.com", 80)).await?;

    eprintln!("sending request...");

    stream
        .write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")
        .await?;

    // IMPORTANT: Make sure the request was written.
    // Arti buffers data, so flushing the buffer is usually required.
    stream.flush().await?;

    eprintln!("reading response...");

    // Read and print the result.
    let mut buf = Vec::new();
    stream.read_to_end(&mut buf).await?;

    println!("{}", String::from_utf8_lossy(&buf));

    Ok(())
}