1use std::sync::Arc;
23use anyhow::Result;
4use futures::StreamExt;
5use hyper::body::Incoming;
6use hyper::server::conn::http1;
7use hyper::service::service_fn;
8use hyper::{Request, Response, StatusCode};
9use hyper_util::rt::TokioIo;
10use tokio_util::sync::CancellationToken;
1112use arti_client::{TorClient, TorClientConfig};
13use safelog::sensitive;
14use tor_cell::relaycell::msg::Connected;
15use tor_hsservice::config::OnionServiceConfigBuilder;
16use tor_hsservice::StreamRequest;
17use tor_proto::stream::IncomingStreamRequest;
1819struct WebHandler {
20 shutdown: CancellationToken,
21}
2223impl WebHandler {
24async fn serve(&self, request: Request<Incoming>) -> Result<Response<String>> {
25let path = request.uri().path();
2627// TODO: Unauthenticated management. This route is accessible by anyone, and exists solely
28 // to demonstrate how to safely shutdown further incoming requests. You should probably
29 // move this elsewhere to ensure proper checks are in place!
30if path == "/shutdown" {
31self.shutdown.cancel();
32 }
3334Ok(Response::builder().status(StatusCode::OK).body(format!(
35"{} {}",
36 request.method(),
37 path
38 ))?)
39 }
40}
4142#[tokio::main]
43async fn main() {
44// Make sure you read doc/OnionService.md to extract your Onion service hostname
4546 // Arti uses the `tracing` crate for logging. Install a handler for this, to print Arti's logs.
47 // (You'll need to set RUST_LOG=info as an environment variable to actually see much; also try
48 // =debug for more detailed logging.)
49tracing_subscriber::fmt::init();
5051// Initialize web server data, if you need to
52let handler = Arc::new(WebHandler {
53 shutdown: CancellationToken::new(),
54 });
5556// The client config includes things like where to store persistent Tor network state.
57 // The defaults provided are the same as the Arti standalone application, and save data
58 // to a conventional place depending on operating system (for example, ~/.local/share/arti
59 // on Linux platforms)
60let config = TorClientConfig::default();
6162// We now let the Arti client start and bootstrap a connection to the network.
63 // (This takes a while to gather the necessary consensus state, etc.)
64let client = TorClient::create_bootstrapped(config).await.unwrap();
6566let svc_cfg = OnionServiceConfigBuilder::default()
67 .nickname("allium-ampeloprasum".parse().unwrap())
68 .build()
69 .unwrap();
70let (service, request_stream) = client.launch_onion_service(svc_cfg).unwrap();
7172eprintln!("ready to serve connections");
7374let stream_requests = tor_hsservice::handle_rend_requests(request_stream)
75 .take_until(handler.shutdown.cancelled());
76tokio::pin!(stream_requests);
7778while let Some(stream_request) = stream_requests.next().await {
79// incoming connection
80let handler = handler.clone();
8182 tokio::spawn(async move {
83let request = stream_request.request().clone();
84let result = handle_stream_request(stream_request, handler).await;
8586match result {
87Ok(()) => {}
88Err(err) => {
89eprintln!("error serving connection {:?}: {}", sensitive(request), err);
90 }
91 }
92 });
93 }
9495 drop(service);
96eprintln!("onion service exited cleanly");
97}
9899async fn handle_stream_request(
100 stream_request: StreamRequest,
101 handler: Arc<WebHandler>,
102) -> Result<()> {
103match stream_request.request() {
104 IncomingStreamRequest::Begin(begin) if begin.port() == 80 => {
105let onion_service_stream = stream_request.accept(Connected::new_empty()).await?;
106let io = TokioIo::new(onion_service_stream);
107108 http1::Builder::new()
109 .serve_connection(io, service_fn(|request| handler.serve(request)))
110 .await?;
111 }
112_ => {
113 stream_request.shutdown_circuit()?;
114 }
115 }
116117Ok(())
118}