diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a9b4570236..4079fb0afb 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -40,7 +40,7 @@ jobs: - name: msrv os: ubuntu-20.04 # sync MSRV with docs: guide/src/guide/installation.md and Cargo.toml - rust: 1.74.0 + rust: 1.75.0 target: x86_64-unknown-linux-gnu name: ${{ matrix.name }} steps: diff --git a/Cargo.lock b/Cargo.lock index 3fb221bcef..bf31d79343 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -131,6 +131,57 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "axum" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" +dependencies = [ + "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", + "tokio", + "tokio-tungstenite", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -148,9 +199,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bit-set" @@ -213,9 +264,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cc" @@ -437,12 +488,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" @@ -521,7 +566,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -608,25 +652,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 = "6.2.0" @@ -639,37 +664,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror", -] - -[[package]] -name = "hashbrown" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" - -[[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", + "thiserror 1.0.68", ] [[package]] @@ -708,9 +703,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", @@ -718,27 +713,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.9.5" @@ -759,26 +761,39 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.31" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +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 0.4.13", "tower-service", - "tracing", - "want", ] [[package]] @@ -959,16 +974,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "indexmap" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" -dependencies = [ - "equivalent", - "hashbrown", -] - [[package]] name = "inotify" version = "0.9.6" @@ -1137,6 +1142,12 @@ dependencies = [ "xml5ever", ] +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + [[package]] name = "mdbook" version = "0.4.43" @@ -1144,6 +1155,7 @@ dependencies = [ "ammonia", "anyhow", "assert_cmd", + "axum", "chrono", "clap", "clap_complete", @@ -1172,8 +1184,8 @@ dependencies = [ "tokio", "toml", "topological-sort", + "tower-http", "walkdir", - "warp", ] [[package]] @@ -1385,7 +1397,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.68", "ucd-trie", ] @@ -1735,6 +1747,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" @@ -1750,12 +1768,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" @@ -1811,18 +1823,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" @@ -1942,6 +1942,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384595c11a4e2969895cad5a8c4029115f5ab956a9e5ef4de79d11a426e5f20c" + [[package]] name = "synstructure" version = "0.13.1" @@ -1999,7 +2005,16 @@ version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.68", +] + +[[package]] +name = "thiserror" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" +dependencies = [ + "thiserror-impl 2.0.9", ] [[package]] @@ -2013,6 +2028,17 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "thiserror-impl" +version = "2.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "tinystr" version = "0.7.6" @@ -2052,9 +2078,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.21.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" +checksum = "be4bf6fecd69fcdede0ec680aaf474cdab988f9de6bc73d3758f0160e3b7025a" dependencies = [ "futures-util", "log", @@ -2090,6 +2116,68 @@ 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", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8437150ab6bbc8c5f0f519e3d5ed4aa883a83dd4cdd3d1b21f9482936046cb97" +dependencies = [ + "bitflags 2.6.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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -2116,28 +2204,21 @@ 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" -version = "0.21.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" +checksum = "413083a99c579593656008130e29255e54dcaae495be556cc26888f211648c24" dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.1.0", + "http", "httparse", "log", "rand", "sha1", - "thiserror", - "url", + "thiserror 2.0.9", "utf-8", ] @@ -2225,43 +2306,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 04063a45a6..662a39bf4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ license = "MPL-2.0" readme = "README.md" repository = "https://github.com/rust-lang/mdBook" description = "Creates a book from markdown files" -rust-version = "1.74" +rust-version = "1.75" [dependencies] anyhow = "1.0.71" @@ -49,7 +49,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.8.1", default-features = false, features = ["http1", "tokio", "ws"], optional = true } +tower-http = { version = "0.6.1", default-features = false, features = ["fs"], optional = true } # Search feature elasticlunr-rs = { version = "3.0.2", optional = true } @@ -66,7 +67,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/guide/src/guide/installation.md b/guide/src/guide/installation.md index 173b078cd7..19c93ba9c7 100644 --- a/guide/src/guide/installation.md +++ b/guide/src/guide/installation.md @@ -20,7 +20,7 @@ To make it easier to run, put the path to the binary into your `PATH`. To build the `mdbook` executable from source, you will first need to install Rust and Cargo. Follow the instructions on the [Rust installation page]. -mdBook currently requires at least Rust version 1.74. +mdBook currently requires at least Rust version 1.75. Once you have installed Rust, the following command can be used to build and install mdBook: diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index 7b1ccab6eb..f0e086bd08 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,17 @@ 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: + .fallback_service( + ServeDir::new(build_dir.clone()) + // The fallback route for 404 errors: + .not_found_service(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 +136,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; + } + }) }