axum_hello_world/
main.rs

1use anyhow::Result;
2use axum::routing::get;
3use axum::Router;
4use futures::StreamExt;
5use hyper::body::Incoming;
6use hyper::Request;
7use hyper_util::rt::{TokioExecutor, TokioIo};
8use hyper_util::server;
9use tower::Service;
10
11use arti_client::{TorClient, TorClientConfig};
12use safelog::sensitive;
13use tor_cell::relaycell::msg::Connected;
14use tor_hsservice::config::OnionServiceConfigBuilder;
15use tor_hsservice::StreamRequest;
16use tor_proto::stream::IncomingStreamRequest;
17
18#[tokio::main]
19async fn main() {
20    // Make sure you read doc/OnionService.md to extract your Onion service hostname
21
22    // Arti uses the `tracing` crate for logging. Install a handler for this, to print Arti's logs.
23    // (You'll need to set RUST_LOG=info as an environment variable to actually see much; also try
24    // =debug for more detailed logging.)
25    tracing_subscriber::fmt::init();
26
27    // Axum router
28    let router = Router::new().route("/", get(|| async { "Hello world!" }));
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 client = TorClient::create_bootstrapped(config).await.unwrap();
39
40    let svc_cfg = OnionServiceConfigBuilder::default()
41        .nickname("allium-ampeloprasum".parse().unwrap())
42        .build()
43        .unwrap();
44
45    let (service, request_stream) = client.launch_onion_service(svc_cfg).unwrap();
46    println!("{}", service.onion_address().unwrap());
47
48    // Wait until the service is believed to be fully reachable.
49    eprintln!("waiting for service to become fully reachable");
50    while let Some(status) = service.status_events().next().await {
51        if status.state().is_fully_reachable() {
52            break;
53        }
54    }
55
56    let stream_requests = tor_hsservice::handle_rend_requests(request_stream);
57    tokio::pin!(stream_requests);
58    eprintln!("ready to serve connections");
59
60    while let Some(stream_request) = stream_requests.next().await {
61        let router = router.clone();
62
63        tokio::spawn(async move {
64            let request = stream_request.request().clone();
65            if let Err(err) = handle_stream_request(stream_request, router).await {
66                eprintln!("error serving connection {:?}: {}", sensitive(request), err);
67            };
68        });
69    }
70
71    drop(service);
72    eprintln!("onion service exited cleanly");
73}
74
75async fn handle_stream_request(stream_request: StreamRequest, router: Router) -> Result<()> {
76    match stream_request.request() {
77        IncomingStreamRequest::Begin(begin) if begin.port() == 80 => {
78            let onion_service_stream = stream_request.accept(Connected::new_empty()).await?;
79            let io = TokioIo::new(onion_service_stream);
80
81            let hyper_service = hyper::service::service_fn(move |request: Request<Incoming>| {
82                router.clone().call(request)
83            });
84
85            server::conn::auto::Builder::new(TokioExecutor::new())
86                .serve_connection(io, hyper_service)
87                .await
88                .map_err(|x| anyhow::anyhow!(x))?;
89        }
90        _ => {
91            stream_request.shutdown_circuit()?;
92        }
93    }
94
95    Ok(())
96}