main.rs
1 use std::sync::Arc; 2 3 use anyhow::Result; 4 use futures::StreamExt; 5 use hyper::body::Incoming; 6 use hyper::server::conn::http1; 7 use hyper::service::service_fn; 8 use hyper::{Request, Response, StatusCode}; 9 use hyper_util::rt::TokioIo; 10 use tokio_util::sync::CancellationToken; 11 12 use arti_client::{TorClient, TorClientConfig}; 13 use safelog::sensitive; 14 use tor_cell::relaycell::msg::Connected; 15 use tor_hsservice::config::OnionServiceConfigBuilder; 16 use tor_hsservice::StreamRequest; 17 use tor_proto::stream::IncomingStreamRequest; 18 19 struct WebHandler { 20 shutdown: CancellationToken, 21 } 22 23 impl WebHandler { 24 async fn serve(&self, request: Request<Incoming>) -> Result<Response<String>> { 25 let path = request.uri().path(); 26 27 // 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! 30 if path == "/shutdown" { 31 self.shutdown.cancel(); 32 } 33 34 Ok(Response::builder().status(StatusCode::OK).body(format!( 35 "{} {}", 36 request.method(), 37 path 38 ))?) 39 } 40 } 41 42 #[tokio::main] 43 async fn main() { 44 // Make sure you read doc/OnionService.md to extract your Onion service hostname 45 46 // 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.) 49 tracing_subscriber::fmt::init(); 50 51 // Initialize web server data, if you need to 52 let handler = Arc::new(WebHandler { 53 shutdown: CancellationToken::new(), 54 }); 55 56 // 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) 60 let config = TorClientConfig::default(); 61 62 // 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.) 64 let client = TorClient::create_bootstrapped(config).await.unwrap(); 65 66 let svc_cfg = OnionServiceConfigBuilder::default() 67 .nickname("allium-ampeloprasum".parse().unwrap()) 68 .build() 69 .unwrap(); 70 let (service, request_stream) = client.launch_onion_service(svc_cfg).unwrap(); 71 72 eprintln!("ready to serve connections"); 73 74 let stream_requests = tor_hsservice::handle_rend_requests(request_stream) 75 .take_until(handler.shutdown.cancelled()); 76 tokio::pin!(stream_requests); 77 78 while let Some(stream_request) = stream_requests.next().await { 79 // incoming connection 80 let handler = handler.clone(); 81 82 tokio::spawn(async move { 83 let request = stream_request.request().clone(); 84 let result = handle_stream_request(stream_request, handler).await; 85 86 match result { 87 Ok(()) => {} 88 Err(err) => { 89 eprintln!("error serving connection {:?}: {}", sensitive(request), err); 90 } 91 } 92 }); 93 } 94 95 drop(service); 96 eprintln!("onion service exited cleanly"); 97 } 98 99 async fn handle_stream_request( 100 stream_request: StreamRequest, 101 handler: Arc<WebHandler>, 102 ) -> Result<()> { 103 match stream_request.request() { 104 IncomingStreamRequest::Begin(begin) if begin.port() == 80 => { 105 let onion_service_stream = stream_request.accept(Connected::new_empty()).await?; 106 let io = TokioIo::new(onion_service_stream); 107 108 http1::Builder::new() 109 .serve_connection(io, service_fn(|request| handler.serve(request))) 110 .await?; 111 } 112 _ => { 113 stream_request.shutdown_circuit()?; 114 } 115 } 116 117 Ok(()) 118 }