diff --git a/Cargo.lock b/Cargo.lock index 2e16fb7..c6f8a27 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,18 +100,19 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.20" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" dependencies = [ "async-trait", "axum-core", - "bitflags 1.3.2", "bytes", "futures-util", "http", "http-body", + "http-body-util", "hyper", + "hyper-util", "itoa", "matchit", "memchr", @@ -128,23 +129,28 @@ dependencies = [ "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] name = "axum-core" -version = "0.3.4" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" dependencies = [ "async-trait", "bytes", "futures-util", "http", "http-body", + "http-body-util", "mime", + "pin-project-lite", "rustversion", + "sync_wrapper", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -185,9 +191,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" @@ -420,6 +426,25 @@ version = "0.27.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +[[package]] +name = "h2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "handlebars" version = "4.3.7" @@ -454,9 +479,9 @@ checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "http" -version = "0.2.9" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" dependencies = [ "bytes", "fnv", @@ -465,20 +490,32 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", "pin-project-lite", ] [[package]] name = "http-range-header" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" +checksum = "3ce4ef31cda248bbdb6e6820603b82dfcd9e833db65a43e997a0ccec777d11fe" [[package]] name = "httparse" @@ -500,25 +537,37 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.27" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", + "h2", "http", "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.9", "tokio", - "tower-service", - "tracing", - "want", +] + +[[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", ] [[package]] @@ -550,9 +599,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "linux-raw-sys" @@ -615,9 +664,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", "wasi", @@ -744,9 +793,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -985,6 +1034,15 @@ dependencies = [ "libc", ] +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.11.0" @@ -993,19 +1051,9 @@ checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" [[package]] name = "socket2" -version = "0.4.9" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys", @@ -1065,9 +1113,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.32.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -1077,16 +1125,16 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.3", + "socket2", "tokio-macros", "windows-sys", ] [[package]] name = "tokio-macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", @@ -1104,6 +1152,7 @@ dependencies = [ "futures-sink", "pin-project-lite", "tokio", + "tracing", ] [[package]] @@ -1146,16 +1195,16 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ae70283aba8d2a8b411c695c437fe25b8b5e44e23e780662002fc72fb47a82" +checksum = "0da193277a4e2c33e59e09b5861580c33dd0a637c3883d0fa74ba40c0374af2e" dependencies = [ "bitflags 2.4.0", "bytes", - "futures-core", "futures-util", "http", "http-body", + "http-body-util", "http-range-header", "httpdate", "mime", @@ -1177,9 +1226,9 @@ checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" [[package]] name = "tower-livereload" -version = "0.8.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e90e6da0427c5e111e03c764d49c4e970f5a9f6569fe408e5a1cbe257f48388" +checksum = "61d6cbbab4b2d3cafd21fb211cc4b06525a0df919c3e8ca3d36485b1c1bd4cd4" dependencies = [ "bytes", "http", @@ -1227,6 +1276,7 @@ dependencies = [ "copy_dir", "env_logger", "handlebars", + "http-body", "log", "pulldown-cmark", "rand", @@ -1248,12 +1298,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - [[package]] name = "typenum" version = "1.16.0" @@ -1318,15 +1362,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 = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/crates/treehouse/Cargo.toml b/crates/treehouse/Cargo.toml index 6cdb339..a1591a2 100644 --- a/crates/treehouse/Cargo.toml +++ b/crates/treehouse/Cargo.toml @@ -8,22 +8,21 @@ edition = "2021" treehouse-format = { workspace = true } anyhow = "1.0.75" -axum = "0.6.20" +axum = "0.7.4" clap = { version = "4.3.22", features = ["derive"] } codespan-reporting = "0.11.1" copy_dir = "0.1.3" env_logger = "0.10.0" log = { workspace = true } handlebars = "4.3.7" +http-body = "1.0.0" pulldown-cmark = { version = "0.9.3", default-features = false } +rand = "0.8.5" serde = { version = "1.0.183", features = ["derive"] } +serde_json = "1.0.105" tokio = { version = "1.32.0", features = ["full"] } toml_edit = { version = "0.19.14", features = ["serde"] } -tower-http = { version = "0.4.3", features = ["fs"] } -tower-livereload = "0.8.0" +tower-http = { version = "0.5.1", features = ["fs"] } +tower-livereload = "0.9.2" walkdir = "2.3.3" ulid = "1.0.0" -rand = "0.8.5" -serde_json = "1.0.105" - - diff --git a/crates/treehouse/src/cli/serve.rs b/crates/treehouse/src/cli/serve.rs index b6c2626..12caddf 100644 --- a/crates/treehouse/src/cli/serve.rs +++ b/crates/treehouse/src/cli/serve.rs @@ -1,14 +1,18 @@ -use std::{path::PathBuf, sync::Arc}; +#[cfg(debug_assertions)] +mod live_reload; + +use std::{net::Ipv4Addr, path::PathBuf, sync::Arc}; use anyhow::Context; use axum::{ extract::{RawQuery, State}, - response::Html, + response::{Html, IntoResponse, Response}, routing::get, Router, }; use log::{error, info}; use pulldown_cmark::escape::escape_html; +use tokio::net::TcpListener; use tower_http::services::ServeDir; use crate::state::{Source, Treehouse}; @@ -18,6 +22,7 @@ use super::Paths; struct SystemPages { four_oh_four: String, b_docs: String, + sandbox: String, } struct Server { @@ -30,6 +35,7 @@ pub async fn serve(treehouse: Treehouse, paths: &Paths<'_>, port: u16) -> anyhow let app = Router::new() .nest_service("/", ServeDir::new(paths.target_dir)) .route("/b", get(branch)) + .route("/sandbox", get(sandbox)) .with_state(Arc::new(Server { treehouse, target_dir: paths.target_dir.to_owned(), @@ -38,16 +44,30 @@ pub async fn serve(treehouse: Treehouse, paths: &Paths<'_>, port: u16) -> anyhow .context("cannot read 404 page")?, b_docs: std::fs::read_to_string(paths.target_dir.join("_treehouse/b.html")) .context("cannot read /b documentation page")?, + sandbox: std::fs::read_to_string(paths.target_dir.join("static/html/sandbox.html")) + .context("cannot read sandbox page")?, }, })); #[cfg(debug_assertions)] - let app = app.layer(tower_livereload::LiveReloadLayer::new()); + let app = live_reload::live_reload(app); info!("serving on port {port}"); - Ok(axum::Server::bind(&([0, 0, 0, 0], port).into()) - .serve(app.into_make_service()) - .await?) + let listener = TcpListener::bind((Ipv4Addr::from([0u8, 0, 0, 0]), port)).await?; + Ok(axum::serve(listener, app).await?) +} + +async fn sandbox(State(state): State>) -> Response { + // Small hack to prevent the LiveReloadLayer from injecting itself into the sandbox. + // The sandbox is always nested under a different page, so there's no need to do that. + let mut response = Html(state.system_pages.sandbox.clone()).into_response(); + #[cfg(debug_assertions)] + { + response + .extensions_mut() + .insert(live_reload::DisableLiveReload); + } + response } async fn branch(RawQuery(named_id): RawQuery, State(state): State>) -> Html { diff --git a/crates/treehouse/src/cli/serve/live_reload.rs b/crates/treehouse/src/cli/serve/live_reload.rs new file mode 100644 index 0000000..d88fc52 --- /dev/null +++ b/crates/treehouse/src/cli/serve/live_reload.rs @@ -0,0 +1,17 @@ +use axum::{http::Response, Router}; + +#[derive(Debug, Clone, Copy)] +pub struct DisableLiveReload; + +pub fn live_reload(router: Router) -> Router { + router.layer(tower_livereload::LiveReloadLayer::new().response_predicate( + |response: &Response<_>| { + let is_html = response + .headers() + .get("Content-Type") + .is_some_and(|v| v == "text/html"); + let is_disabled = response.extensions().get::().is_some(); + is_html && !is_disabled + }, + )) +}