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;
1011use 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;
1718#[tokio::main]
19async fn main() {
20// Make sure you read doc/OnionService.md to extract your Onion service hostname
2122 // 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.)
25tracing_subscriber::fmt::init();
2627// Axum router
28let router = Router::new().route("/", get(|| async { "Hello world!" }));
2930// 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)
34let config = TorClientConfig::default();
3536// 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.)
38let client = TorClient::create_bootstrapped(config).await.unwrap();
3940let svc_cfg = OnionServiceConfigBuilder::default()
41 .nickname("allium-ampeloprasum".parse().unwrap())
42 .build()
43 .unwrap();
4445let (service, request_stream) = client.launch_onion_service(svc_cfg).unwrap();
46println!("{}", service.onion_address().unwrap());
4748// Wait until the service is believed to be fully reachable.
49eprintln!("waiting for service to become fully reachable");
50while let Some(status) = service.status_events().next().await {
51if status.state().is_fully_reachable() {
52break;
53 }
54 }
5556let stream_requests = tor_hsservice::handle_rend_requests(request_stream);
57tokio::pin!(stream_requests);
58eprintln!("ready to serve connections");
5960while let Some(stream_request) = stream_requests.next().await {
61let router = router.clone();
6263 tokio::spawn(async move {
64let request = stream_request.request().clone();
65if let Err(err) = handle_stream_request(stream_request, router).await {
66eprintln!("error serving connection {:?}: {}", sensitive(request), err);
67 };
68 });
69 }
7071 drop(service);
72eprintln!("onion service exited cleanly");
73}
7475async fn handle_stream_request(stream_request: StreamRequest, router: Router) -> Result<()> {
76match stream_request.request() {
77 IncomingStreamRequest::Begin(begin) if begin.port() == 80 => {
78let onion_service_stream = stream_request.accept(Connected::new_empty()).await?;
79let io = TokioIo::new(onion_service_stream);
8081let hyper_service = hyper::service::service_fn(move |request: Request<Incoming>| {
82 router.clone().call(request)
83 });
8485 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 }
9495Ok(())
96}