diff --git a/Cargo.lock b/Cargo.lock index 5e43d12d77..3a78bf896c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,12 +124,76 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "async-trait" +version = "0.1.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] + [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "base64", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sha1", + "sync_wrapper 1.0.0", + "tokio", + "tokio-tungstenite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.71" @@ -422,12 +486,6 @@ dependencies = [ "log", ] -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - [[package]] name = "errno" version = "0.3.9" @@ -506,7 +564,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -593,25 +650,6 @@ dependencies = [ "regex-syntax", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "handlebars" version = "5.1.2" @@ -626,36 +664,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" - -[[package]] -name = "headers" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" -dependencies = [ - "base64", - "bytes", - "headers-core", - "http 0.2.12", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http 0.2.12", -] - [[package]] name = "hermit-abi" version = "0.3.9" @@ -692,9 +700,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -702,27 +710,34 @@ dependencies = [ ] [[package]] -name = "http" -version = "1.1.0" +name = "http-body" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "fnv", - "itoa", + "http", ] [[package]] -name = "http-body" -version = "0.4.6" +name = "http-body-util" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ "bytes", - "http 0.2.12", + "futures-core", + "http", + "http-body", "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe" + [[package]] name = "httparse" version = "1.8.0" @@ -743,26 +758,37 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", - "h2", - "http 0.2.12", + "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", "socket2", "tokio", - "tower-service", - "tracing", - "want", ] [[package]] @@ -814,16 +840,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "indexmap" -version = "2.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" -dependencies = [ - "equivalent", - "hashbrown", -] - [[package]] name = "inotify" version = "0.9.6" @@ -975,6 +991,12 @@ dependencies = [ "xml5ever", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "mdbook" version = "0.4.37" @@ -982,6 +1004,7 @@ dependencies = [ "ammonia", "anyhow", "assert_cmd", + "axum", "chrono", "clap", "clap_complete", @@ -1010,8 +1033,8 @@ dependencies = [ "tokio", "toml", "topological-sort", + "tower-http", "walkdir", - "warp", ] [[package]] @@ -1532,6 +1555,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "ryu" version = "1.0.18" @@ -1547,12 +1576,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -1607,18 +1630,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - [[package]] name = "sha1" version = "0.10.6" @@ -1732,6 +1743,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384595c11a4e2969895cad5a8c4029115f5ab956a9e5ef4de79d11a426e5f20c" + [[package]] name = "tempfile" version = "3.10.1" @@ -1874,6 +1897,52 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "bitflags 2.5.0", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "http-range-header", + "httpdate", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -1886,7 +1955,6 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", "pin-project-lite", "tracing-core", ] @@ -1896,15 +1964,6 @@ name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" @@ -1915,7 +1974,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http", "httparse", "log", "rand", @@ -2015,43 +2074,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "warp" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4378d202ff965b011c64817db11d5829506d3404edeadb61f190d111da3f231c" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "headers", - "http 0.2.12", - "hyper", - "log", - "mime", - "mime_guess", - "percent-encoding", - "pin-project", - "scoped-tls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-tungstenite", - "tokio-util", - "tower-service", - "tracing", -] - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index a8646186cc..5dc1ce12f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,8 @@ walkdir = { version = "2.3.3", optional = true } # Serve feature futures-util = { version = "0.3.28", optional = true } tokio = { version = "1.28.1", features = ["macros", "rt-multi-thread"], optional = true } -warp = { version = "0.3.6", default-features = false, features = ["websocket"], optional = true } +axum = { version = "0.7.5", default-features = false, features = ["http1", "tokio", "ws"], optional = true } +tower-http = { version = "0.5.2", default-features = false, features = ["fs"], optional = true } # Search feature elasticlunr-rs = { version = "3.0.2", optional = true } @@ -63,7 +64,7 @@ walkdir = "2.3.3" [features] default = ["watch", "serve", "search"] watch = ["dep:notify", "dep:notify-debouncer-mini", "dep:ignore", "dep:pathdiff", "dep:walkdir"] -serve = ["dep:futures-util", "dep:tokio", "dep:warp"] +serve = ["dep:futures-util", "dep:tokio", "dep:axum", "dep:tower-http"] search = ["dep:elasticlunr-rs", "dep:ammonia"] [[bin]] diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index 5e637a023b..70610d67e2 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -2,6 +2,9 @@ use super::command_prelude::*; #[cfg(feature = "watch")] use super::watch; use crate::{get_book_dir, open}; +use axum::extract::ws::Message; +use axum::extract::{State, WebSocketUpgrade}; +use axum::{response::IntoResponse, routing::get}; use clap::builder::NonEmptyStringValueParser; use futures_util::sink::SinkExt; use futures_util::StreamExt; @@ -11,8 +14,7 @@ use mdbook::MDBook; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::PathBuf; use tokio::sync::broadcast; -use warp::ws::Message; -use warp::Filter; +use tower_http::services::{ServeDir, ServeFile}; /// The HTTP endpoint for the websocket used to trigger reloads when a file changes. const LIVE_RELOAD_ENDPOINT: &str = "__livereload"; @@ -100,7 +102,7 @@ pub fn execute(args: &ArgMatches) -> Result<()> { { let watcher = watch::WatcherKind::from_str(args.get_one::("watcher").unwrap()); watch::rebuild_on_change(watcher, &book_dir, &update_config, &move || { - let _ = tx.send(Message::text("reload")); + let _ = tx.send("reload".into()); }); } @@ -116,32 +118,18 @@ async fn serve( reload_tx: broadcast::Sender, file_404: &str, ) { - // A warp Filter which captures `reload_tx` and provides an `rx` copy to - // receive reload messages. - let sender = warp::any().map(move || reload_tx.subscribe()); - - // A warp Filter to handle the livereload endpoint. This upgrades to a - // websocket, and then waits for any filesystem change notifications, and - // relays them over the websocket. - let livereload = warp::path(LIVE_RELOAD_ENDPOINT) - .and(warp::ws()) - .and(sender) - .map(|ws: warp::ws::Ws, mut rx: broadcast::Receiver| { - ws.on_upgrade(move |ws| async move { - let (mut user_ws_tx, _user_ws_rx) = ws.split(); - trace!("websocket got connection"); - if let Ok(m) = rx.recv().await { - trace!("notify of reload"); - let _ = user_ws_tx.send(m).await; - } - }) - }); - // A warp Filter that serves from the filesystem. - let book_route = warp::fs::dir(build_dir.clone()); - // The fallback route for 404 errors - let fallback_route = warp::fs::file(build_dir.join(file_404)) - .map(|reply| warp::reply::with_status(reply, warp::http::StatusCode::NOT_FOUND)); - let routes = livereload.or(book_route).or(fallback_route); + let app = axum::Router::new() + // This upgrades to a websocket, and then waits for any filesystem change notifications, + // and relays them over the websocket: + .route(&format!("/{LIVE_RELOAD_ENDPOINT}"), get(reload_ws_handler)) + // Serve from the filesystem: + .nest_service( + "/", + ServeDir::new(build_dir.clone()) + // The fallback route for 404 errors: + .fallback(ServeFile::new(build_dir.join(file_404))), + ) + .with_state(reload_tx); std::panic::set_hook(Box::new(move |panic_info| { // exit if serve panics @@ -149,5 +137,22 @@ async fn serve( std::process::exit(1); })); - warp::serve(routes).run(address).await; + let listener = tokio::net::TcpListener::bind(address).await.unwrap(); + axum::serve(listener, app).await.unwrap(); +} + +async fn reload_ws_handler( + ws: WebSocketUpgrade, + State(reload_tx): State>, +) -> impl IntoResponse { + let mut rx = reload_tx.subscribe(); + + ws.on_upgrade(move |ws| async move { + let (mut user_ws_tx, _user_ws_rx) = ws.split(); + trace!("websocket got connection"); + if let Ok(m) = rx.recv().await { + trace!("notify of reload"); + let _ = user_ws_tx.send(m).await; + } + }) }