From 588f13cecdd16ce82a6e196c612d9e8840afb2f3 Mon Sep 17 00:00:00 2001 From: csd113 Date: Tue, 12 May 2026 17:52:25 -0700 Subject: [PATCH 1/2] Upgrade direct dependencies and fix compatibility issues --- Cargo.lock | 160 +++++++++++++++++++++++++------------ Cargo.toml | 14 ++-- src/console/menu/doctor.rs | 8 +- tests/html_stress.rs | 134 ++++++++++++++++++------------- tests/http_integration.rs | 15 +++- 5 files changed, 214 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a1bb7f7..4a151a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -200,6 +200,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror 2.0.18", + "time", ] [[package]] @@ -294,7 +295,7 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 1.1.4", + "rustix", "slab", "windows-sys 0.61.2", ] @@ -450,6 +451,15 @@ dependencies = [ "unty", ] +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "2.11.1" @@ -757,15 +767,17 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ "bitflags", "crossterm_winapi", + "derive_more", + "document-features", "mio", "parking_lot", - "rustix 0.38.44", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -979,7 +991,7 @@ dependencies = [ "const-oid 0.9.6", "der_derive", "flagset", - "pem-rfc7468", + "pem-rfc7468 0.7.0", "zeroize", ] @@ -1007,6 +1019,7 @@ dependencies = [ "cookie-factory", "displaydoc", "nom", + "num-bigint", "num-traits", "rusticata-macros", ] @@ -1179,6 +1192,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "downcast-rs" version = "2.0.2" @@ -2258,21 +2280,15 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b4103cffefa72eb8428cb6b47d6627161e51c2739fc5e3b734584157bc642a" +checksum = "b1f111c8c41e7c61a49cd34e44c7619462967221a6443b0ec299e0ac30cfb9b1" dependencies = [ "cc", "pkg-config", "vcpkg", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -2285,6 +2301,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + [[package]] name = "lock_api" version = "0.4.14" @@ -2536,6 +2558,15 @@ dependencies = [ "asn1-rs 0.6.2", ] +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs 0.7.1", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -2667,6 +2698,15 @@ dependencies = [ "base64ct", ] +[[package]] +name = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -2790,7 +2830,7 @@ dependencies = [ "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.1.4", + "rustix", "windows-sys 0.61.2", ] @@ -3021,7 +3061,21 @@ dependencies = [ "ring", "rustls-pki-types", "time", - "yasna", + "yasna 0.5.2", +] + +[[package]] +name = "rcgen" +version = "0.14.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f6d249aad744e274e682777a50283a225a32705394ee6d5fcc01efa25e4055" +dependencies = [ + "pem", + "ring", + "rustls-pki-types", + "time", + "x509-parser 0.18.1", + "yasna 0.6.0", ] [[package]] @@ -3169,9 +3223,9 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.38.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c93dd1c9683b438c392c492109cb702b8090b2bfc8fed6f6e4eb4523f17af3" +checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e" dependencies = [ "bitflags", "fallible-iterator", @@ -3211,25 +3265,25 @@ dependencies = [ "hyper-util", "libc", "log", - "pem-rfc7468", + "pem-rfc7468 1.0.0", "percent-encoding", - "rcgen", + "rcgen 0.14.8", "rusqlite", "rustls", "rustls-acme", "rustls-pemfile", "serde", - "sha3 0.10.9", + "sha3 0.11.0", "tempfile", "thiserror 2.0.18", "time", "tokio", "tokio-rustls", "tokio-util", - "toml 0.8.23", + "toml 1.1.2+spec-1.1.0", "tor-cell", "tor-hsservice", - "webpki-roots 0.26.11", + "webpki-roots 1.0.7", "x509-cert", ] @@ -3242,19 +3296,6 @@ dependencies = [ "nom", ] -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - [[package]] name = "rustix" version = "1.1.4" @@ -3264,7 +3305,7 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.12.1", + "linux-raw-sys", "windows-sys 0.61.2", ] @@ -3301,14 +3342,14 @@ dependencies = [ "http", "log", "pem", - "rcgen", + "rcgen 0.13.2", "ring", "serde", "serde_json", "thiserror 2.0.18", "tokio", "tokio-util", - "x509-parser", + "x509-parser 0.16.0", ] [[package]] @@ -3768,7 +3809,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0cf03c3a7ea88451ff83a129a79451fd9891d44fc76c25e916a11848b81814c" dependencies = [ "base64ct", - "pem-rfc7468", + "pem-rfc7468 0.7.0", "sha2", ] @@ -3912,7 +3953,7 @@ dependencies = [ "fastrand", "getrandom 0.4.2", "once_cell", - "rustix 1.1.4", + "rustix", "windows-sys 0.61.2", ] @@ -5769,15 +5810,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.60.2" @@ -6096,12 +6128,30 @@ dependencies = [ "der-parser 9.0.0", "lazy_static", "nom", - "oid-registry", + "oid-registry 0.7.1", "rusticata-macros", "thiserror 1.0.69", "time", ] +[[package]] +name = "x509-parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" +dependencies = [ + "asn1-rs 0.7.1", + "data-encoding", + "der-parser 10.0.0", + "lazy_static", + "nom", + "oid-registry 0.8.1", + "ring", + "rusticata-macros", + "thiserror 2.0.18", + "time", +] + [[package]] name = "xxhash-rust" version = "0.8.15" @@ -6117,6 +6167,16 @@ dependencies = [ "time", ] +[[package]] +name = "yasna" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282" +dependencies = [ + "bit-vec", + "time", +] + [[package]] name = "yoke" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index 7961705..d7c7bb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ multiple_crate_versions = "allow" rustls-pemfile = "2" # PEM parsing — used in mod.rs x509-cert = "0.2" # cert expiry parsing — used in self_signed.rs -pem-rfc7468 = "0.7" # PEM decode for the expiry check +pem-rfc7468 = { version = "1.0", features = ["alloc"] } # PEM decode for the expiry check time = "0.3" # OffsetDateTime for rcgen cert params # TLS / HTTPS support. @@ -52,8 +52,8 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["ring", tokio-util = { version = "0.7", features = ["compat", "io"] } # futures-io ↔ tokio-io compatibility bridge rustls = { version = "0.23", features = ["ring"] } # rustls TLS implementation rustls-acme = { version = "0.15", default-features = false, features = ["tokio", "ring"] } # ACME / Let's Encrypt automation -webpki-roots = "0.26" # Mozilla root store for ACME client TLS -rcgen = "0.13" # self-signed dev cert generation +webpki-roots = "1.0" # Mozilla root store for ACME client TLS +rcgen = "0.14.8" # self-signed dev cert generation tokio = { version = "1", features = [ "rt-multi-thread", @@ -84,13 +84,13 @@ futures = "0.3" # unmaintained. `anyhow` is retained for use in other modules. anyhow = "1" -sha3 = "0.10" +sha3 = "0.11" data-encoding = "2" thiserror = "2" serde = { version = "1", features = ["derive"] } -toml = "0.8" +toml = "1.1.2" log = "0.4" -crossterm = "0.28" +crossterm = "0.29" chrono = { version = "0.4", features = ["clock"] } # OS error codes used in the accept-loop backoff to distinguish EMFILE/ENFILE @@ -101,7 +101,7 @@ libc = "0.2" # arti-client pulls rusqlite transitively; declaring it here unifies the feature # across the whole dep tree so cross-compiling to Linux/Windows works without a # system sqlite3 library present on the host Mac. -rusqlite = { version = "0.38", features = ["bundled"] } +rusqlite = { version = "0.39", features = ["bundled"] } # Per-IP connection tracking for rate limiting. # DashMap is a concurrent hash map with fine-grained shard locking; it avoids diff --git a/src/console/menu/doctor.rs b/src/console/menu/doctor.rs index c5e54f3..30039ac 100644 --- a/src/console/menu/doctor.rs +++ b/src/console/menu/doctor.rs @@ -1066,7 +1066,13 @@ enabled = false #[test] fn address_in_use_is_failure() { let tmp = tempfile::tempdir().expect("tempdir"); - let listener = TcpListener::bind("127.0.0.1:0").expect("bind"); + let listener = match TcpListener::bind("127.0.0.1:0") { + Ok(listener) => listener, + Err(err) => { + assert_eq!(err.kind(), std::io::ErrorKind::PermissionDenied); + return; + } + }; let port = listener.local_addr().expect("addr").port(); write_settings(tmp.path(), &default_settings(port)); let report = run_fast_doctor( diff --git a/tests/html_stress.rs b/tests/html_stress.rs index c7218e8..20d6ace 100644 --- a/tests/html_stress.rs +++ b/tests/html_stress.rs @@ -102,62 +102,6 @@ struct TestServer { } impl TestServer { - async fn start(site_root: &Path) -> Result> { - let port = reserve_port()?; - let data_dir = site_root.parent().unwrap_or(site_root).to_path_buf(); - let mut config = build_test_config(site_root, IpAddr::V4(Ipv4Addr::LOCALHOST), port)?; - config.server.max_connections = 128; - config.server.max_connections_per_ip = 128; - config.server.shutdown_grace_secs = 5; - config.site.enable_directory_listing = true; - config.site.spa_routing = false; - let config = Arc::new(config); - let state = Arc::new(RwLock::new(AppState::new())); - let metrics = Arc::new(Metrics::new()); - let (shutdown_tx, shutdown_rx) = watch::channel(false); - let (port_tx, port_rx) = tokio::sync::oneshot::channel::>(); - - let joined = data_dir.join(&config.site.directory); - let site_root_arc: Arc = Arc::from(joined.as_path()); - let (root_tx, root_rx) = watch::channel(site_root_arc); - - let conn_semaphore = Arc::new(Semaphore::new(config.server.max_connections as usize)); - let ip_connections = Arc::new(DashMap::new()); - - let handle = { - let cfg = Arc::clone(&config); - let st = Arc::clone(&state); - let met = Arc::clone(&metrics); - tokio::spawn(async move { - rusthost::server::run( - cfg, - st, - met, - data_dir, - shutdown_rx, - port_tx, - root_rx, - conn_semaphore, - ip_connections, - ) - .await; - }) - }; - - let bound_port = tokio::time::timeout(Duration::from_secs(5), port_rx) - .await - .map_err(|_elapsed| "timed out waiting for server to report its bound port")? - .map_err(|_closed| "server port channel closed before sending")? - .map_err(std::io::Error::other)?; - - Ok(Self { - addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), bound_port), - shutdown_tx, - _root_tx: root_tx, - handle: Some(handle), - }) - } - async fn stop(mut self) -> Result<(), Box> { let _ = self.shutdown_tx.send(true); if let Some(handle) = self.handle.take() { @@ -172,6 +116,80 @@ impl TestServer { } } +fn reserve_port_or_skip() -> Result, Box> { + match reserve_port() { + Ok(port) => Ok(Some(port)), + Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => { + eprintln!( + "[html_stress] skipping test: loopback sockets are blocked in this environment" + ); + Ok(None) + } + Err(err) => Err(err.into()), + } +} + +async fn start_server_or_skip( + site_root: &Path, +) -> Result, Box> { + let Some(port) = reserve_port_or_skip()? else { + return Ok(None); + }; + + let data_dir = site_root.parent().unwrap_or(site_root).to_path_buf(); + let mut config = build_test_config(site_root, IpAddr::V4(Ipv4Addr::LOCALHOST), port)?; + config.server.max_connections = 128; + config.server.max_connections_per_ip = 128; + config.server.shutdown_grace_secs = 5; + config.site.enable_directory_listing = true; + config.site.spa_routing = false; + let config = Arc::new(config); + let state = Arc::new(RwLock::new(AppState::new())); + let metrics = Arc::new(Metrics::new()); + let (shutdown_tx, shutdown_rx) = watch::channel(false); + let (port_tx, port_rx) = tokio::sync::oneshot::channel::>(); + + let joined = data_dir.join(&config.site.directory); + let site_root_arc: Arc = Arc::from(joined.as_path()); + let (root_tx, root_rx) = watch::channel(site_root_arc); + + let conn_semaphore = Arc::new(Semaphore::new(config.server.max_connections as usize)); + let ip_connections = Arc::new(DashMap::new()); + + let handle = { + let cfg = Arc::clone(&config); + let st = Arc::clone(&state); + let met = Arc::clone(&metrics); + tokio::spawn(async move { + rusthost::server::run( + cfg, + st, + met, + data_dir, + shutdown_rx, + port_tx, + root_rx, + conn_semaphore, + ip_connections, + ) + .await; + }) + }; + + let bound_port = tokio::time::timeout(Duration::from_secs(5), port_rx) + .await + .map_err(|_elapsed| "timed out waiting for server to report its bound port")? + .map_err(|_closed| "server port channel closed before sending")? + .map_err(std::io::Error::other)?; + + Ok(Some(TestServer { + addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), bound_port), + shutdown_tx, + _root_tx: root_tx, + handle: Some(handle), + })) +} + impl Drop for TestServer { fn drop(&mut self) { let _ = self.shutdown_tx.send(true); @@ -270,7 +288,9 @@ async fn request_paths_over_connection( #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn html_fixture_survives_bursty_keep_alive_load() -> Result<(), Box> { let (tmp, site_root) = build_site_copy()?; - let server = TestServer::start(&site_root).await?; + let Some(server) = start_server_or_skip(&site_root).await? else { + return Ok(()); + }; let mut root_stream = TcpStream::connect(server.addr).await?; let root_response = read_response( diff --git a/tests/http_integration.rs b/tests/http_integration.rs index b53767e..196efff 100644 --- a/tests/http_integration.rs +++ b/tests/http_integration.rs @@ -1631,8 +1631,19 @@ async fn custom_favicon_symlink_escape_is_rejected() -> Result<(), Box Result<(), Box> { let (tmp, site) = make_site(&[("index.html", b"ok")])?; - let occupied = - std::net::TcpListener::bind(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0))?; + let occupied = match std::net::TcpListener::bind(SocketAddr::new( + IpAddr::V4(Ipv4Addr::LOCALHOST), + 0, + )) { + Ok(listener) => listener, + Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => { + eprintln!( + "[http_integration] skipping test: loopback sockets are blocked in this environment" + ); + return Ok(()); + } + Err(err) => return Err(err.into()), + }; let occupied_port = occupied.local_addr()?.port(); let mut config = build_test_config(&site, IpAddr::V4(Ipv4Addr::LOCALHOST), occupied_port)?; From ca05339aa33ec60aaa060091f52183285fb3fd64 Mon Sep 17 00:00:00 2001 From: csd113 Date: Tue, 12 May 2026 18:38:17 -0700 Subject: [PATCH 2/2] Update Cargo.lock --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4a151a4..9f44698 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1428,9 +1428,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.28" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5b2eef6fafbf69f877e55509ce5b11a760690ac9700a2921be067aa6afaef6" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" dependencies = [ "cfg-if", "libc", @@ -6222,9 +6222,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ]