diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index dc64de8..6e27c89 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -25,6 +25,7 @@ jobs: - http-cache-surf - http-cache-ureq - http-cache-tower + - http-cache-tower-server - http-cache-quickcache steps: - uses: actions/checkout@v6 diff --git a/Cargo.toml b/Cargo.toml index a82ec4d..9145f78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -resolver = "2" +resolver = "3" members = [ "http-cache", "http-cache-reqwest", diff --git a/http-cache-quickcache/CHANGELOG.md b/http-cache-quickcache/CHANGELOG.md index 445ae06..6e29059 100644 --- a/http-cache-quickcache/CHANGELOG.md +++ b/http-cache-quickcache/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Changed + +- Migrated to the 2024 edition, which requires Rust 1.85.0 or newer +- MSRV lowered from 1.90.0 to 1.89.0. `rust-version` now covers default features resolved with `resolver = "3"`; see the MSRV policy in the README + ## [1.0.0-alpha.8] - 2026-09-08 ### Changed diff --git a/http-cache-quickcache/Cargo.toml b/http-cache-quickcache/Cargo.toml index 31f5afb..93b391e 100644 --- a/http-cache-quickcache/Cargo.toml +++ b/http-cache-quickcache/Cargo.toml @@ -12,8 +12,8 @@ categories = [ "caching", "web-programming::http-client" ] -edition = "2021" -rust-version = "1.90.0" +edition = "2024" +rust-version = "1.89.0" [dependencies] postcard = { version = "1.1", default-features = false, features = ["alloc"] } diff --git a/http-cache-quickcache/README.md b/http-cache-quickcache/README.md index f162b4d..cbf5e89 100644 --- a/http-cache-quickcache/README.md +++ b/http-cache-quickcache/README.md @@ -12,7 +12,9 @@ An http-cache manager implementation for [quick-cache](https://github.com/arthur ## Minimum Supported Rust Version (MSRV) -1.90.0 +1.89.0 + +See the [MSRV policy](https://github.com/06chaynes/http-cache#minimum-supported-rust-version-msrv). ## Install diff --git a/http-cache-quickcache/src/test.rs b/http-cache-quickcache/src/test.rs index 9c55a0b..cc6ba6c 100644 --- a/http-cache-quickcache/src/test.rs +++ b/http-cache-quickcache/src/test.rs @@ -6,7 +6,7 @@ use http_cache_reqwest::Cache; use http_cache_semantics::CachePolicy; use reqwest::Client; use reqwest_middleware::ClientBuilder; -use wiremock::{matchers::method, Mock, MockServer, ResponseTemplate}; +use wiremock::{Mock, MockServer, ResponseTemplate, matchers::method}; use macro_rules_attribute::apply; use smol_macros::test; diff --git a/http-cache-reqwest/CHANGELOG.md b/http-cache-reqwest/CHANGELOG.md index 4b4b6de..b764fec 100644 --- a/http-cache-reqwest/CHANGELOG.md +++ b/http-cache-reqwest/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Changed + +- Migrated to the 2024 edition, which requires Rust 1.85.0 or newer +- MSRV lowered from 1.90.0 to 1.89.0. `rust-version` now covers default features resolved with `resolver = "3"`; see the MSRV policy in the README + ## [1.0.0-alpha.9] - 2026-09-08 ### Changed diff --git a/http-cache-reqwest/Cargo.toml b/http-cache-reqwest/Cargo.toml index 5704140..e58cddb 100644 --- a/http-cache-reqwest/Cargo.toml +++ b/http-cache-reqwest/Cargo.toml @@ -12,8 +12,8 @@ license = "MIT OR Apache-2.0" readme = "README.md" keywords = ["cache", "http", "middleware", "reqwest"] categories = ["caching", "web-programming::http-client"] -edition = "2021" -rust-version = "1.90.0" +edition = "2024" +rust-version = "1.89.0" [dependencies] anyhow = "1.0.95" diff --git a/http-cache-reqwest/README.md b/http-cache-reqwest/README.md index a65e189..89a1009 100644 --- a/http-cache-reqwest/README.md +++ b/http-cache-reqwest/README.md @@ -15,7 +15,9 @@ Uses [reqwest-middleware](https://github.com/TrueLayer/reqwest-middleware) for m ## Minimum Supported Rust Version (MSRV) -1.90.0 +1.89.0 + +See the [MSRV policy](https://github.com/06chaynes/http-cache#minimum-supported-rust-version-msrv). ## Install diff --git a/http-cache-reqwest/examples/reqwest_basic.rs b/http-cache-reqwest/examples/reqwest_basic.rs index 1bc25d3..9260374 100644 --- a/http-cache-reqwest/examples/reqwest_basic.rs +++ b/http-cache-reqwest/examples/reqwest_basic.rs @@ -7,7 +7,7 @@ use http_cache_reqwest::{CACacheManager, Cache}; use reqwest::Client; use reqwest_middleware::ClientBuilder; use std::time::Instant; -use wiremock::{matchers::method, Mock, MockServer, ResponseTemplate}; +use wiremock::{Mock, MockServer, ResponseTemplate, matchers::method}; #[tokio::main] async fn main() -> Result<(), Box> { diff --git a/http-cache-reqwest/examples/reqwest_streaming.rs b/http-cache-reqwest/examples/reqwest_streaming.rs index c816d79..8ddfff0 100644 --- a/http-cache-reqwest/examples/reqwest_streaming.rs +++ b/http-cache-reqwest/examples/reqwest_streaming.rs @@ -10,7 +10,7 @@ use http_cache_reqwest::StreamingCache; use reqwest::Client; use reqwest_middleware::ClientBuilder; use std::time::Instant; -use wiremock::{matchers::method, Mock, MockServer, ResponseTemplate}; +use wiremock::{Mock, MockServer, ResponseTemplate, matchers::method}; #[tokio::main] async fn main() -> Result<(), Box> { diff --git a/http-cache-reqwest/examples/reqwest_streaming_memory_profile.rs b/http-cache-reqwest/examples/reqwest_streaming_memory_profile.rs index 1a23b0f..52d28b1 100644 --- a/http-cache-reqwest/examples/reqwest_streaming_memory_profile.rs +++ b/http-cache-reqwest/examples/reqwest_streaming_memory_profile.rs @@ -19,8 +19,8 @@ use std::time::Duration; use tempfile::tempdir; use tokio::time::sleep; use wiremock::{ - matchers::{method, path}, Mock, MockServer, ResponseTemplate, + matchers::{method, path}, }; // Memory tracking allocator @@ -44,7 +44,9 @@ impl MemoryTracker { unsafe impl GlobalAlloc for MemoryTracker { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let ptr = System.alloc(layout); + // SAFETY: the caller upholds `GlobalAlloc::alloc`'s contract, which is + // exactly what `System` requires. + let ptr = unsafe { System.alloc(layout) }; if !ptr.is_null() { self.allocations.fetch_add(layout.size(), Ordering::Relaxed); } @@ -52,7 +54,9 @@ unsafe impl GlobalAlloc for MemoryTracker { } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - System.dealloc(ptr, layout); + // SAFETY: `ptr` came from `alloc` with this same `layout`, per the + // caller's obligation. + unsafe { System.dealloc(ptr, layout) }; self.allocations.fetch_sub(layout.size(), Ordering::Relaxed); } } @@ -186,7 +190,9 @@ async fn run_memory_analysis() { println!("============================================================"); println!("This analysis measures memory efficiency differences between"); println!("traditional buffered caching and file-based streaming caching."); - println!("Measurements are taken during cache hits to compare memory usage patterns."); + println!( + "Measurements are taken during cache hits to compare memory usage patterns." + ); println!(); let payload_sizes = [ @@ -343,8 +349,8 @@ const GATE_CHUNK_COUNT: usize = 4096; /// instead of committing; 512MiB gives 2x headroom. const GATE_MAX_BODY_SIZE: u64 = 512 * 1024 * 1024; -fn gate_body_stream( -) -> impl futures_util::Stream> +fn gate_body_stream() +-> impl futures_util::Stream> { futures_util::stream::iter((0..GATE_CHUNK_COUNT).map(|_| { Ok::<_, std::convert::Infallible>(bytes::Bytes::from_static( @@ -357,7 +363,7 @@ fn gate_body_stream( /// copies of `GATE_CHUNK` as one cacheable response, without ever holding /// more than one chunk in memory at a time. Returns the bound address. async fn serve_gate_body() -> std::net::SocketAddr { - use axum::{body::Body, response::Response, routing::get, Router}; + use axum::{Router, body::Body, response::Response, routing::get}; let app = Router::new().route( "/gate", diff --git a/http-cache-reqwest/src/lib.rs b/http-cache-reqwest/src/lib.rs index c8ea16f..c5e6c58 100644 --- a/http-cache-reqwest/src/lib.rs +++ b/http-cache-reqwest/src/lib.rs @@ -344,12 +344,12 @@ use std::str::FromStr; pub use http::request::Parts; use http::{ - header::{HeaderName, CACHE_CONTROL}, Extensions, HeaderValue, Method, + header::{CACHE_CONTROL, HeaderName}, }; use http_cache::{ - url_parse, BoxError, HitOrMiss, Middleware, Result, Url, XCACHE, - XCACHELOOKUP, + BoxError, HitOrMiss, Middleware, Result, Url, XCACHE, XCACHELOOKUP, + url_parse, }; use reqwest::{Request, Response, ResponseBuilderExt}; #[cfg(all(feature = "reqwest-middleware", feature = "middlewest"))] @@ -758,10 +758,10 @@ where .await .map_err(from_box_error)?; - if result.extensions().get::().is_none() { - if let Ok(u) = ::url::Url::parse(&parts.uri.to_string()) { - result.extensions_mut().insert(FinalUrl(u)); - } + if result.extensions().get::().is_none() + && let Ok(u) = ::url::Url::parse(&parts.uri.to_string()) + { + result.extensions_mut().insert(FinalUrl(u)); } convert_streaming_body_to_reqwest::(result).await.map_err(|e| { diff --git a/http-cache-reqwest/src/test.rs b/http-cache-reqwest/src/test.rs index 85cfebd..481cd6d 100644 --- a/http-cache-reqwest/src/test.rs +++ b/http-cache-reqwest/src/test.rs @@ -10,8 +10,8 @@ use reqwest::Client; #[cfg(any(feature = "streaming", feature = "rate-limiting"))] use wiremock::matchers::path; use wiremock::{ - matchers::{header, method}, Mock, MockServer, ResponseTemplate, + matchers::{header, method}, }; /// Helper function to create a temporary cache manager @@ -2053,9 +2053,8 @@ mod streaming_tests { fn until_key_ready( &self, key: &str, - ) -> std::pin::Pin< - Box + Send + '_>, - > { + ) -> std::pin::Pin + Send + '_>> + { let key = key.to_string(); Box::pin(async move { self.calls.lock().unwrap().push(key); @@ -2152,9 +2151,8 @@ mod streaming_tests { fn until_key_ready( &self, key: &str, - ) -> std::pin::Pin< - Box + Send + '_>, - > { + ) -> std::pin::Pin + Send + '_>> + { let key = key.to_string(); Box::pin(async move { self.calls.lock().unwrap().push(key); @@ -2252,8 +2250,7 @@ mod rate_limiting_tests { fn until_key_ready( &self, key: &str, - ) -> std::pin::Pin + Send + '_>> - { + ) -> std::pin::Pin + Send + '_>> { let key = key.to_string(); Box::pin(async move { self.calls.lock().unwrap().push(key); diff --git a/http-cache-surf/CHANGELOG.md b/http-cache-surf/CHANGELOG.md index 633a461..9e5562b 100644 --- a/http-cache-surf/CHANGELOG.md +++ b/http-cache-surf/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Changed + +- Migrated to the 2024 edition, which requires Rust 1.85.0 or newer +- MSRV lowered from 1.90.0 to 1.89.0. `rust-version` now covers default features resolved with `resolver = "3"`; see the MSRV policy in the README + ## [1.0.0-alpha.8] - 2026-09-08 ### Changed diff --git a/http-cache-surf/Cargo.toml b/http-cache-surf/Cargo.toml index 81a22e9..1c82051 100644 --- a/http-cache-surf/Cargo.toml +++ b/http-cache-surf/Cargo.toml @@ -12,8 +12,8 @@ categories = [ "caching", "web-programming::http-client" ] -edition = "2021" -rust-version = "1.90.0" +edition = "2024" +rust-version = "1.89.0" [dependencies] http = "1.2.0" diff --git a/http-cache-surf/README.md b/http-cache-surf/README.md index 11d8b78..aed3b31 100644 --- a/http-cache-surf/README.md +++ b/http-cache-surf/README.md @@ -15,7 +15,9 @@ Should likely be registered after any middleware modifying the request. ## Minimum Supported Rust Version (MSRV) -1.90.0 +1.89.0 + +See the [MSRV policy](https://github.com/06chaynes/http-cache#minimum-supported-rust-version-msrv). ## Install diff --git a/http-cache-surf/examples/surf_basic.rs b/http-cache-surf/examples/surf_basic.rs index 4eeb912..3eae9b6 100644 --- a/http-cache-surf/examples/surf_basic.rs +++ b/http-cache-surf/examples/surf_basic.rs @@ -13,7 +13,7 @@ use http_cache::{CacheMode, HttpCache, HttpCacheOptions}; use http_cache_surf::{CACacheManager, Cache}; use std::time::Instant; use surf::Client; -use wiremock::{matchers::method, Mock, MockServer, ResponseTemplate}; +use wiremock::{Mock, MockServer, ResponseTemplate, matchers::method}; #[tokio::main(flavor = "current_thread")] async fn main() -> Result<(), Box> { diff --git a/http-cache-surf/src/lib.rs b/http-cache-surf/src/lib.rs index f8fe20b..c7e5128 100644 --- a/http-cache-surf/src/lib.rs +++ b/http-cache-surf/src/lib.rs @@ -151,17 +151,16 @@ use http::{ request::{self, Parts}, }; use http_cache::{ - url_parse, BadHeader, BoxError, CacheManager, HitOrMiss, HttpResponse, - Middleware, Result, Url, XCACHE, XCACHELOOKUP, + BadHeader, BoxError, CacheManager, HitOrMiss, HttpResponse, Middleware, + Result, Url, XCACHE, XCACHELOOKUP, url_parse, }; pub use http_cache::{CacheMode, HttpCache, HttpHeaders}; +use http_types::{Method as HttpTypesMethod, Request}; use http_types::{ - headers::HeaderValue as HttpTypesHeaderValue, Response as HttpTypesResponse, StatusCode as HttpTypesStatusCode, - Version as HttpTypesVersion, + Version as HttpTypesVersion, headers::HeaderValue as HttpTypesHeaderValue, }; -use http_types::{Method as HttpTypesMethod, Request}; -use surf::{middleware::Next, Client}; +use surf::{Client, middleware::Next}; // Re-export managers and cache types #[cfg(feature = "manager-cacache")] diff --git a/http-cache-surf/src/test.rs b/http-cache-surf/src/test.rs index 3468828..c948171 100644 --- a/http-cache-surf/src/test.rs +++ b/http-cache-surf/src/test.rs @@ -2,7 +2,7 @@ use crate::{BadRequest, Cache, HttpCacheError}; use http_cache::*; use surf::Client; -use wiremock::{matchers::method, Mock, MockServer, ResponseTemplate}; +use wiremock::{Mock, MockServer, ResponseTemplate, matchers::method}; #[tokio::test] async fn test_non_cloneable_request_graceful_fallback() -> Result<()> { @@ -1089,9 +1089,8 @@ mod with_moka { fn until_key_ready( &self, key: &str, - ) -> std::pin::Pin< - Box + Send + '_>, - > { + ) -> std::pin::Pin + Send + '_>> + { let key = key.to_string(); Box::pin(async move { self.calls.lock().unwrap().push(key); diff --git a/http-cache-tower-server/CHANGELOG.md b/http-cache-tower-server/CHANGELOG.md index 13b8aeb..48d1a2f 100644 --- a/http-cache-tower-server/CHANGELOG.md +++ b/http-cache-tower-server/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Changed + +- Migrated to the 2024 edition, which requires Rust 1.85.0 or newer +- MSRV lowered from 1.90.0 to 1.89.0. `rust-version` now covers default features resolved with `resolver = "3"`; see the MSRV policy in the README + ## [0.2.5] - 2026-09-08 ### Changed diff --git a/http-cache-tower-server/Cargo.toml b/http-cache-tower-server/Cargo.toml index 6a3b2e0..f8a1e48 100644 --- a/http-cache-tower-server/Cargo.toml +++ b/http-cache-tower-server/Cargo.toml @@ -12,8 +12,8 @@ categories = [ "caching", "web-programming::http-server" ] -edition = "2021" -rust-version = "1.90.0" +edition = "2024" +rust-version = "1.89.0" [dependencies] http-cache = { version = "1.0.0-alpha.8", path = "../http-cache", default-features = false } diff --git a/http-cache-tower-server/README.md b/http-cache-tower-server/README.md index 28e9742..71def84 100644 --- a/http-cache-tower-server/README.md +++ b/http-cache-tower-server/README.md @@ -398,7 +398,9 @@ cargo run --example axum_basic --features manager-cacache ## Minimum Supported Rust Version (MSRV) -1.90.0 +1.89.0 + +See the [MSRV policy](https://github.com/06chaynes/http-cache#minimum-supported-rust-version-msrv). ## Contributing diff --git a/http-cache-tower-server/examples/axum_advanced.rs b/http-cache-tower-server/examples/axum_advanced.rs index c31cde8..218cc20 100644 --- a/http-cache-tower-server/examples/axum_advanced.rs +++ b/http-cache-tower-server/examples/axum_advanced.rs @@ -76,11 +76,11 @@ //! ``` use axum::{ + BoxError, Router, error_handling::HandleErrorLayer, extract::{Query, State}, response::{IntoResponse, Response}, routing::{delete, get}, - BoxError, Router, }; use http::{Request, StatusCode}; use http_cache::CACacheManager; diff --git a/http-cache-tower-server/examples/axum_basic.rs b/http-cache-tower-server/examples/axum_basic.rs index 1738ae0..85af526 100644 --- a/http-cache-tower-server/examples/axum_basic.rs +++ b/http-cache-tower-server/examples/axum_basic.rs @@ -16,11 +16,11 @@ //! Run with: cargo run --example axum_basic --features manager-cacache use axum::{ + BoxError, Router, error_handling::HandleErrorLayer, extract::Path, response::{IntoResponse, Response}, routing::get, - BoxError, Router, }; use http::StatusCode; use http_cache::CACacheManager; diff --git a/http-cache-tower-server/src/lib.rs b/http-cache-tower-server/src/lib.rs index 201a6ed..e626bd1 100644 --- a/http-cache-tower-server/src/lib.rs +++ b/http-cache-tower-server/src/lib.rs @@ -117,7 +117,7 @@ #![deny(unsafe_code)] use bytes::Bytes; -use http::{header::HeaderValue, Request, Response}; +use http::{Request, Response, header::HeaderValue}; use http_body::{Body as HttpBody, Frame}; use http_body_util::BodyExt; use http_cache::{CacheManager, HttpResponse, HttpVersion}; @@ -126,8 +126,8 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::error::Error as StdError; use std::pin::Pin; -use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicU64, Ordering}; use std::task::{Context, Poll}; use std::time::{Duration, SystemTime}; use tower::{Layer, Service}; @@ -159,11 +159,7 @@ impl CacheMetrics { pub fn hit_rate(&self) -> f64 { let hits = self.hits.load(Ordering::Relaxed); let total = hits + self.misses.load(Ordering::Relaxed); - if total == 0 { - 0.0 - } else { - hits as f64 / total as f64 - } + if total == 0 { 0.0 } else { hits as f64 / total as f64 } } /// Reset all metrics to zero. @@ -903,21 +899,16 @@ fn should_cache( } // RFC 7234: Check for Expires header if no Cache-Control - if let Some(expires) = res_parts.headers.get(http::header::EXPIRES) { - if let Ok(expires_str) = expires.to_str() { - if let Some(ttl) = parse_expires(expires_str) { - let ttl = apply_ttl_constraints(ttl, options); - return Some(ttl); - } - } + if let Some(expires) = res_parts.headers.get(http::header::EXPIRES) + && let Ok(expires_str) = expires.to_str() + && let Some(ttl) = parse_expires(expires_str) + { + let ttl = apply_ttl_constraints(ttl, options); + return Some(ttl); } // No explicit caching directive - if options.cache_by_default { - options.default_ttl - } else { - None - } + if options.cache_by_default { options.default_ttl } else { None } } /// Apply min/max TTL constraints from options. diff --git a/http-cache-tower/CHANGELOG.md b/http-cache-tower/CHANGELOG.md index 44313de..cd80614 100644 --- a/http-cache-tower/CHANGELOG.md +++ b/http-cache-tower/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Changed + +- Migrated to the 2024 edition, which requires Rust 1.85.0 or newer +- MSRV lowered from 1.90.0 to 1.89.0. `rust-version` now covers default features resolved with `resolver = "3"`; see the MSRV policy in the README + ## [1.0.0-alpha.8] - 2026-09-08 ### Changed diff --git a/http-cache-tower/Cargo.toml b/http-cache-tower/Cargo.toml index e86ec00..bb6b965 100644 --- a/http-cache-tower/Cargo.toml +++ b/http-cache-tower/Cargo.toml @@ -12,8 +12,8 @@ categories = [ "caching", "web-programming::http-client" ] -edition = "2021" -rust-version = "1.90.0" +edition = "2024" +rust-version = "1.89.0" [dependencies] http-cache = { version = "1.0.0-alpha.8", path = "../http-cache", default-features = false } diff --git a/http-cache-tower/README.md b/http-cache-tower/README.md index 9b709f9..f1b0a21 100644 --- a/http-cache-tower/README.md +++ b/http-cache-tower/README.md @@ -14,7 +14,9 @@ This crate provides Tower Layer and Service implementations that add HTTP cachin ## Minimum Supported Rust Version (MSRV) -1.90.0 +1.89.0 + +See the [MSRV policy](https://github.com/06chaynes/http-cache#minimum-supported-rust-version-msrv). ## Install diff --git a/http-cache-tower/benches/streaming_benchmark.rs b/http-cache-tower/benches/streaming_benchmark.rs index 741c773..386233c 100644 --- a/http-cache-tower/benches/streaming_benchmark.rs +++ b/http-cache-tower/benches/streaming_benchmark.rs @@ -1,5 +1,5 @@ use bytes::Bytes; -use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; +use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}; use http::{Request, Response, StatusCode}; use http_body_util::{BodyExt, Full}; use http_cache::{CACacheManager, StreamingManager}; diff --git a/http-cache-tower/examples/hyper_streaming.rs b/http-cache-tower/examples/hyper_streaming.rs index e636161..22463c8 100644 --- a/http-cache-tower/examples/hyper_streaming.rs +++ b/http-cache-tower/examples/hyper_streaming.rs @@ -11,8 +11,8 @@ use http_cache::{HttpCacheOptions, StreamingManager}; use http_cache_tower::HttpCacheStreamingLayer; use std::future::Future; use std::pin::Pin; -use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicU32, Ordering}; use std::task::{Context, Poll}; use std::time::Instant; use tower::{Service, ServiceBuilder}; diff --git a/http-cache-tower/examples/tower_streaming_memory_profile.rs b/http-cache-tower/examples/tower_streaming_memory_profile.rs index e6f1eef..e66bc68 100644 --- a/http-cache-tower/examples/tower_streaming_memory_profile.rs +++ b/http-cache-tower/examples/tower_streaming_memory_profile.rs @@ -42,7 +42,9 @@ impl MemoryTracker { unsafe impl GlobalAlloc for MemoryTracker { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let ptr = System.alloc(layout); + // SAFETY: the caller upholds `GlobalAlloc::alloc`'s contract, which is + // exactly what `System` requires. + let ptr = unsafe { System.alloc(layout) }; if !ptr.is_null() { self.allocations.fetch_add(layout.size(), Ordering::Relaxed); } @@ -50,7 +52,9 @@ unsafe impl GlobalAlloc for MemoryTracker { } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - System.dealloc(ptr, layout); + // SAFETY: `ptr` came from `alloc` with this same `layout`, per the + // caller's obligation. + unsafe { System.dealloc(ptr, layout) }; self.allocations.fetch_sub(layout.size(), Ordering::Relaxed); } } @@ -404,7 +408,9 @@ async fn main() -> Result<(), Box> { println!("=================================================="); println!("This analysis measures memory efficiency differences between"); println!("traditional buffered caching and file-based streaming caching."); - println!("Measurements are taken during cache hits to compare memory usage patterns.\n"); + println!( + "Measurements are taken during cache hits to compare memory usage patterns.\n" + ); // Memory profiling analysis for different payload sizes let payload_sizes = vec![ diff --git a/http-cache-tower/src/lib.rs b/http-cache-tower/src/lib.rs index c4dead8..a05f9d9 100644 --- a/http-cache-tower/src/lib.rs +++ b/http-cache-tower/src/lib.rs @@ -149,7 +149,7 @@ use bytes::Bytes; use http::{ - header::CACHE_CONTROL, request, HeaderValue, Method, Request, Response, + HeaderValue, Method, Request, Response, header::CACHE_CONTROL, request, }; use http_body::Body; use http_body_util::BodyExt; @@ -160,15 +160,15 @@ pub use http_cache::CACacheManager; #[cfg(feature = "manager-redb")] pub use http_cache::RedbManager; +#[cfg(feature = "streaming")] +use http_cache::StreamingError; #[cfg(feature = "rate-limiting")] pub use http_cache::rate_limiting::{ CacheAwareRateLimiter, DirectRateLimiter, DomainRateLimiter, Quota, }; -#[cfg(feature = "streaming")] -use http_cache::StreamingError; use http_cache::{ - url_parse, BoxError, CacheManager, CacheMode, HitOrMiss, HttpCache, - HttpCacheOptions, HttpResponse, Middleware, Url, XCACHE, XCACHELOOKUP, + BoxError, CacheManager, CacheMode, HitOrMiss, HttpCache, HttpCacheOptions, + HttpResponse, Middleware, Url, XCACHE, XCACHELOOKUP, url_parse, }; #[cfg(feature = "streaming")] use http_cache::{HttpStreamingCache, StreamingCacheManager}; diff --git a/http-cache-tower/src/test.rs b/http-cache-tower/src/test.rs index 24bef34..eb0f616 100644 --- a/http-cache-tower/src/test.rs +++ b/http-cache-tower/src/test.rs @@ -626,7 +626,9 @@ mod tests { .body(Full::new(Bytes::from(TEST_BODY)))?) } else { // This shouldn't be called on second request if cached properly - panic!("Service called twice when response should be cached") + panic!( + "Service called twice when response should be cached" + ) } }) } diff --git a/http-cache-ureq/CHANGELOG.md b/http-cache-ureq/CHANGELOG.md index 72cc8f3..61917b7 100644 --- a/http-cache-ureq/CHANGELOG.md +++ b/http-cache-ureq/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Changed + +- Migrated to the 2024 edition, which requires Rust 1.85.0 or newer +- MSRV lowered from 1.90.0 to 1.89.0. `rust-version` now covers default features resolved with `resolver = "3"`; see the MSRV policy in the README + ## [1.0.0-alpha.8] - 2026-09-08 ### Changed diff --git a/http-cache-ureq/Cargo.toml b/http-cache-ureq/Cargo.toml index d37ed72..b68b22b 100644 --- a/http-cache-ureq/Cargo.toml +++ b/http-cache-ureq/Cargo.toml @@ -12,8 +12,8 @@ categories = [ "caching", "web-programming::http-client" ] -edition = "2021" -rust-version = "1.90.0" +edition = "2024" +rust-version = "1.89.0" [dependencies] http = "1.2.0" diff --git a/http-cache-ureq/README.md b/http-cache-ureq/README.md index 8c3df77..7be103a 100644 --- a/http-cache-ureq/README.md +++ b/http-cache-ureq/README.md @@ -15,7 +15,9 @@ Provides a simple caching wrapper around [ureq](https://github.com/algesten/ureq ## Minimum Supported Rust Version (MSRV) -1.90.0 +1.89.0 + +See the [MSRV policy](https://github.com/06chaynes/http-cache#minimum-supported-rust-version-msrv). ## Install diff --git a/http-cache-ureq/examples/ureq_basic.rs b/http-cache-ureq/examples/ureq_basic.rs index 11ff396..16f089a 100644 --- a/http-cache-ureq/examples/ureq_basic.rs +++ b/http-cache-ureq/examples/ureq_basic.rs @@ -10,7 +10,7 @@ use http_cache_ureq::{CACacheManager, CachedAgent}; use std::time::Instant; -use wiremock::{matchers::method, Mock, MockServer, ResponseTemplate}; +use wiremock::{Mock, MockServer, ResponseTemplate, matchers::method}; fn main() -> Result<(), Box> { let tokio_rt = tokio::runtime::Runtime::new()?; diff --git a/http-cache-ureq/src/lib.rs b/http-cache-ureq/src/lib.rs index 87ce204..04fa227 100644 --- a/http-cache-ureq/src/lib.rs +++ b/http-cache-ureq/src/lib.rs @@ -206,10 +206,10 @@ pub use http_cache::{BadRequest, HttpCacheError}; use std::{collections::HashMap, result::Result, str::FromStr}; pub use http::request::Parts; -use http::{header::CACHE_CONTROL, Method}; +use http::{Method, header::CACHE_CONTROL}; use http_cache::{ - url_parse, BoxError, CacheManager, HitOrMiss, HttpResponse, Middleware, - Url, XCACHE, XCACHELOOKUP, + BoxError, CacheManager, HitOrMiss, HttpResponse, Middleware, Url, XCACHE, + XCACHELOOKUP, url_parse, }; pub use http_cache::{ diff --git a/http-cache-ureq/src/test.rs b/http-cache-ureq/src/test.rs index 24d5d3f..5e56bfb 100644 --- a/http-cache-ureq/src/test.rs +++ b/http-cache-ureq/src/test.rs @@ -3,8 +3,8 @@ use http_cache::{CacheKey, *}; use std::{sync::Arc, time::Duration}; use tempfile::TempDir; use wiremock::{ - matchers::{header, method, path}, Mock, MockServer, ResponseTemplate, + matchers::{header, method, path}, }; const GET: &str = "GET"; @@ -1150,8 +1150,7 @@ mod rate_limiting_tests { fn until_key_ready( &self, key: &str, - ) -> std::pin::Pin + Send + '_>> - { + ) -> std::pin::Pin + Send + '_>> { let key = key.to_string(); Box::pin(async move { self.calls.lock().unwrap().push(key); diff --git a/http-cache/CHANGELOG.md b/http-cache/CHANGELOG.md index b59a2ff..ab85beb 100644 --- a/http-cache/CHANGELOG.md +++ b/http-cache/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Changed + +- Migrated to the 2024 edition, which requires Rust 1.85.0 or newer +- MSRV lowered from 1.90.0 to 1.89.0. `rust-version` now covers default features resolved with `resolver = "3"`; see the MSRV policy in the README + ## [1.0.0-alpha.8] - 2026-09-08 ### Changed diff --git a/http-cache/Cargo.toml b/http-cache/Cargo.toml index 426f66f..91e3d4b 100644 --- a/http-cache/Cargo.toml +++ b/http-cache/Cargo.toml @@ -12,8 +12,8 @@ categories = [ "caching", "web-programming::http-client" ] -edition = "2021" -rust-version = "1.90.0" +edition = "2024" +rust-version = "1.89.0" [dependencies] bincode = { version = "1.3.3", optional = true } diff --git a/http-cache/README.md b/http-cache/README.md index 434e976..5698e34 100644 --- a/http-cache/README.md +++ b/http-cache/README.md @@ -20,7 +20,19 @@ See the [Provided Client Implementations](#provided-client-implementations) sect ## Minimum Supported Rust Version (MSRV) -1.90.0 +1.89.0 + +Each crate's `rust-version` covers its **default features**, resolved with +`resolver = "3"`. Optional backends may require a newer toolchain; that +requirement is not part of the guarantee. + +MSRV is raised only when a default-feature dependency forces it, and +dependencies are not held back to avoid it. An MSRV increase is not a breaking +change and may land in any release, including a patch. + +On an older toolchain: the resolver is chosen by your own workspace, not by +this one. Edition 2024 packages use `resolver = "3"` by default; on an earlier +edition, set it explicitly or pin with `cargo update --precise `. ## Install diff --git a/http-cache/src/body.rs b/http-cache/src/body.rs index 138eea1..48ffb01 100644 --- a/http-cache/src/body.rs +++ b/http-cache/src/body.rs @@ -257,10 +257,10 @@ where } if *size == 0 { *done = true; - if let Some(c) = check.take() { - if let Err(e) = c.finish() { - return Poll::Ready(Some(Err(e))); - } + if let Some(c) = check.take() + && let Err(e) = c.finish() + { + return Poll::Ready(Some(Err(e))); } return Poll::Ready(None); } @@ -297,11 +297,11 @@ where } if *size == 0 { *done = true; - if let Some(c) = check.take() { - if let Err(e) = c.finish() { - buffer.clear(); - return Poll::Ready(Some(Err(e))); - } + if let Some(c) = check.take() + && let Err(e) = c.finish() + { + buffer.clear(); + return Poll::Ready(Some(Err(e))); } } let bytes = buffer.split().freeze(); diff --git a/http-cache/src/lib.rs b/http-cache/src/lib.rs index 3e2ee0f..00fa1e0 100644 --- a/http-cache/src/lib.rs +++ b/http-cache/src/lib.rs @@ -336,7 +336,7 @@ use std::{ }; use http::{ - header::CACHE_CONTROL, request, response, HeaderValue, Response, StatusCode, + HeaderValue, Response, StatusCode, header::CACHE_CONTROL, request, response, }; use http_cache_semantics::{AfterResponse, BeforeRequest, CachePolicy}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -416,11 +416,7 @@ pub fn url_hostname(url: &Url) -> Option<&str> { #[cfg(feature = "url-ada")] { let hostname = url.hostname(); - if hostname.is_empty() { - None - } else { - Some(hostname) - } + if hostname.is_empty() { None } else { Some(hostname) } } #[cfg(not(feature = "url-ada"))] { @@ -1028,8 +1024,9 @@ pub trait StreamingCacheManager: Send + Sync + 'static { fn get( &self, cache_key: &str, - ) -> impl Future, CachePolicy)>>> - + Send + ) -> impl Future< + Output = Result, CachePolicy)>>, + > + Send where ::Data: Send, ::Error: @@ -1802,12 +1799,11 @@ impl HttpCacheOptions { http_response: &HttpResponse, original_mode: CacheMode, ) -> CacheMode { - if let Some(response_cache_mode_fn) = &self.response_cache_mode_fn { - if let Some(override_mode) = + if let Some(response_cache_mode_fn) = &self.response_cache_mode_fn + && let Some(override_mode) = response_cache_mode_fn(request_parts, http_response) - { - return override_mode; - } + { + return override_mode; } original_mode } @@ -2300,10 +2296,9 @@ where // Handle warning headers per RFC 7234 §4.3.4 if let Some(warning_code) = response_warning_code(&cached_response) + && (100..200).contains(&warning_code) { - if (100..200).contains(&warning_code) { - response_remove_warning(&mut cached_response); - } + response_remove_warning(&mut cached_response); } // 5. Branch on cache mode @@ -2936,10 +2931,9 @@ impl HttpCache { // Allow response-based cache mode override if let Some(response_cache_mode_fn) = &self.options.response_cache_mode_fn + && let Some(override_mode) = response_cache_mode_fn(&parts, &res) { - if let Some(override_mode) = response_cache_mode_fn(&parts, &res) { - mode = override_mode; - } + mode = override_mode; } let is_cacheable = self.options.should_cache_response( diff --git a/http-cache/src/managers/redb.rs b/http-cache/src/managers/redb.rs index 822f365..9bb936b 100644 --- a/http-cache/src/managers/redb.rs +++ b/http-cache/src/managers/redb.rs @@ -23,8 +23,8 @@ //! database — the drop-time flush must acquire the writer lock. use std::path::Path; -use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicU64, Ordering}; use crate::{CacheManager, HttpResponse, Result}; @@ -102,10 +102,10 @@ impl FlushState { impl Drop for FlushState { fn drop(&mut self) { - if self.unflushed.load(Ordering::Relaxed) > 0 { - if let Err(e) = self.flush() { - log::warn!("redb flush on drop failed: {e}"); - } + if self.unflushed.load(Ordering::Relaxed) > 0 + && let Err(e) = self.flush() + { + log::warn!("redb flush on drop failed: {e}"); } } } diff --git a/http-cache/src/managers/streaming_cache.rs b/http-cache/src/managers/streaming_cache.rs index c2fe947..a387a33 100644 --- a/http-cache/src/managers/streaming_cache.rs +++ b/http-cache/src/managers/streaming_cache.rs @@ -74,14 +74,14 @@ use std::{ }; use crate::{ + HttpHeaders, StreamingCacheManager, Url, body::StreamingBody, error::{Result, StreamingError}, - HttpHeaders, StreamingCacheManager, Url, }; use bytes::{Buf, Bytes}; use http::{Response, Version}; use http_body::Body; -use http_body_util::{combinators::UnsyncBoxBody, BodyExt}; +use http_body_util::{BodyExt, combinators::UnsyncBoxBody}; use http_cache_semantics::CachePolicy; use moka::future::Cache; use rand::RngExt; @@ -1207,18 +1207,17 @@ impl StreamingCacheManager for StreamingManager { } // RFC 9111 §3.3: never store a response we know is incomplete. - if !is_head { - if let Some(cl) = content_length { - if cl != written { - log::debug!( - "put: content-length {cl} != received {written}; \ - serving uncached (incomplete response)" - ); - return serve_uncached_spooled( - parts, file, guard, written, None, None, - ); - } - } + if !is_head + && let Some(cl) = content_length + && cl != written + { + log::debug!( + "put: content-length {cl} != received {written}; \ + serving uncached (incomplete response)" + ); + return serve_uncached_spooled( + parts, file, guard, written, None, None, + ); } if let Err(e) = file.sync_all().await { log::debug!("put: fsync failed; serving uncached: {e}"); @@ -1380,10 +1379,10 @@ impl StreamingCacheManager for StreamingManager { // Identity check: refuse to staple this revision's headers onto a // concurrently-stored replacement entry's body. - if let Some(t) = token { - if t.0.as_slice() != metadata.nonce { - return Ok(false); - } + if let Some(t) = token + && t.0.as_slice() != metadata.nonce + { + return Ok(false); } metadata.headers = stored_headers(headers); @@ -2703,11 +2702,9 @@ mod tests { .unwrap(); // Nothing cached, no spool file ever created. - assert!(manager - .get("GET:https://example.com/big") - .await - .unwrap() - .is_none()); + assert!( + manager.get("GET:https://example.com/big").await.unwrap().is_none() + ); let tmp_entries = std::fs::read_dir(dir.path().join("tmp")) .map(|rd| rd.count()) .unwrap_or(0); @@ -2839,11 +2836,13 @@ mod tests { ) .await .unwrap(); - assert!(manager - .get("GET:https://example.com/exact") - .await - .unwrap() - .is_some()); + assert!( + manager + .get("GET:https://example.com/exact") + .await + .unwrap() + .is_some() + ); } /// Content-Length lie (RFC 9111 §3.3): upstream declared 100 bytes but sent @@ -2871,11 +2870,13 @@ mod tests { .unwrap(); let bytes = returned.into_body().collect().await.unwrap().to_bytes(); assert_eq!(&bytes[..], b"short"); - assert!(manager - .get("GET:https://example.com/truncated") - .await - .unwrap() - .is_none()); + assert!( + manager + .get("GET:https://example.com/truncated") + .await + .unwrap() + .is_none() + ); } /// Upstream body error mid-stream: put() errors (unchanged contract) and @@ -2905,11 +2906,13 @@ mod tests { ) .await; assert!(result.is_err(), "upstream error must propagate"); - assert!(manager - .get("GET:https://example.com/reset") - .await - .unwrap() - .is_none()); + assert!( + manager + .get("GET:https://example.com/reset") + .await + .unwrap() + .is_none() + ); let tmp_entries = std::fs::read_dir(dir.path().join("tmp")).unwrap().count(); assert_eq!(tmp_entries, 0, "tmp must be cleaned on upstream error"); @@ -3029,11 +3032,13 @@ mod tests { .unwrap(); let bytes = returned.into_body().collect().await.unwrap().to_bytes(); assert_eq!(&bytes[..], b"never cached"); - assert!(manager - .get("GET:https://example.com/zero") - .await - .unwrap() - .is_none()); + assert!( + manager + .get("GET:https://example.com/zero") + .await + .unwrap() + .is_none() + ); } /// Pass-through decline is true streaming: put() returns without consuming @@ -3218,8 +3223,8 @@ mod tests { } #[tokio::test] - async fn test_serve_uncached_spooled_chains_prefix_pending_rest_and_unlinks( - ) { + async fn test_serve_uncached_spooled_chains_prefix_pending_rest_and_unlinks() + { let dir = TempDir::new().unwrap(); let tmp = dir.path().join("spool.tmp"); diff --git a/http-cache/src/rate_limiting.rs b/http-cache/src/rate_limiting.rs index 6964ca5..177936c 100644 --- a/http-cache/src/rate_limiting.rs +++ b/http-cache/src/rate_limiting.rs @@ -10,9 +10,9 @@ use std::pin::Pin; #[cfg(feature = "rate-limiting")] pub use governor::{ - clock::DefaultClock, - state::{keyed::DefaultKeyedStateStore, InMemoryState}, DefaultDirectRateLimiter, DefaultKeyedRateLimiter, Quota, RateLimiter, + clock::DefaultClock, + state::{InMemoryState, keyed::DefaultKeyedStateStore}, }; /// A trait for rate limiting that can be implemented by different rate limiting strategies diff --git a/http-cache/src/test.rs b/http-cache/src/test.rs index 0c35524..4d00d8b 100644 --- a/http-cache/src/test.rs +++ b/http-cache/src/test.rs @@ -1,8 +1,8 @@ use crate::{ - error, url_parse, CacheMode, HitOrMiss, HttpHeaders, HttpResponse, - HttpVersion, Result, Url, + CacheMode, HitOrMiss, HttpHeaders, HttpResponse, HttpVersion, Result, Url, + error, url_parse, }; -use http::{header::CACHE_CONTROL, StatusCode}; +use http::{StatusCode, header::CACHE_CONTROL}; use std::str::FromStr; @@ -804,8 +804,8 @@ mod with_foyer { #[cfg(feature = "manager-cacache")] mod interface_tests { use crate::{ - url_parse, CACacheManager, CacheMode, HttpCache, HttpCacheInterface, - HttpCacheOptions, + CACacheManager, CacheMode, HttpCache, HttpCacheInterface, + HttpCacheOptions, url_parse, }; use http::{Request, Response, StatusCode}; use std::{ @@ -1545,11 +1545,11 @@ mod metadata_provider_tests { #[cfg(all(test, feature = "rate-limiting"))] mod rate_limiting_tests { use super::*; + use crate::HttpCacheOptions; use crate::rate_limiting::{ CacheAwareRateLimiter, DirectRateLimiter, DomainRateLimiter, Quota, }; use crate::url_hostname; - use crate::HttpCacheOptions; use std::num::NonZero; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; @@ -1571,8 +1571,7 @@ mod rate_limiting_tests { fn until_key_ready( &self, key: &str, - ) -> std::pin::Pin + Send + '_>> - { + ) -> std::pin::Pin + Send + '_>> { let key = key.to_string(); Box::pin(async move { self.calls.lock().unwrap().push(key); diff --git a/rustfmt.toml b/rustfmt.toml index 21c9eb0..0382960 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,3 @@ max_width = 80 use_small_heuristics = "max" -edition = "2021" \ No newline at end of file +style_edition = "2024" \ No newline at end of file