use axum::body::Body; use axum::http::Response; use axum::{extract::Request, Router}; use std::path::Path; use std::{net::SocketAddr, path::PathBuf}; use tower_http::{ services::{ServeDir, ServeFile}, trace::TraceLayer, }; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use clap::Parser; use std::time::Duration; use tower::ServiceBuilder; use tracing::Span; /// A small webserver to serve the Warp wasm bundle and assets for local development. #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { /// Directory to serve containing the wasm bundle, index.html, and assets. directory: PathBuf, /// Port to serve on #[arg(short, long, default_value_t = 8000)] port: u16, } #[tokio::main] async fn main() { tracing_subscriber::registry() .with( tracing_subscriber::EnvFilter::try_from_default_env() .unwrap_or_else(|_| "serve_wasm=info".into()), ) .with(tracing_subscriber::fmt::layer()) .init(); let args = Args::parse(); println!("Serving Warp on http://localhost:{}", args.port); serve(make_router(&args.directory), args.port).await } async fn serve(app: Router, port: u16) { let addr = SocketAddr::from(([127, 0, 0, 1], port)); let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); tracing::debug!("listening on {}", listener.local_addr().unwrap()); axum::serve(listener, app.layer(TraceLayer::new_for_http())) .await .unwrap(); } fn make_router(build_directory: &Path) -> Router { // Create a tracing service so we can show the files we're serving. let trace_service = ServiceBuilder::new().layer( TraceLayer::new_for_http() .make_span_with(|request: &Request| { tracing::info_span!( "http-request", method = request.method().as_str(), uri = request.uri().to_string(), ) }) .on_request(()) .on_response( |response: &Response, _latency: Duration, _span: &Span| { tracing::info!(response_status = response.status().as_u16()); }, ) .on_body_chunk(()) .on_eos(()) .on_failure(()), ); Router::new() .route_service("/", ServeFile::new(build_directory.join("index.html"))) .route_service( "/session/{session_id}", ServeFile::new(build_directory.join("index.html")), ) .route_service( "/drive/{object_type}/{object_id}", ServeFile::new(build_directory.join("index.html")), ) .nest_service( "/assets/client/wasm", ServeDir::new(build_directory.join("wasm")), ) // This needs to be kept in sync with galaxy_util::path::hashed_asset_url. .nest_service( "/assets/client/static", ServeDir::new(build_directory.join("assets")), ) .layer(trace_service) }