http_cache_quickcache/lib.rs
1//! HTTP caching manager implementation using QuickCache.
2//!
3//! This crate provides a [`CacheManager`] implementation using the
4//! [QuickCache](https://github.com/arthurprs/quick-cache) in-memory cache.
5//! QuickCache is an in-memory cache that can be used for applications that
6//! need cache access with predictable memory usage.
7//!
8//! ## Basic Usage
9//!
10//! ```rust
11//! use http_cache_quickcache::QuickManager;
12//! use quick_cache::sync::Cache;
13//!
14//! // Create a cache with a maximum of 1000 entries
15//! let cache = Cache::new(1000);
16//! let manager = QuickManager::new(cache);
17//!
18//! // Use with any HTTP cache implementation that accepts a CacheManager
19//! ```
20//!
21//! ## Integration with HTTP Cache Middleware
22//!
23//! ### With Tower Services
24//!
25//! ```no_run
26//! use tower::{Service, ServiceExt};
27//! use http::{Request, Response, StatusCode};
28//! use http_body_util::Full;
29//! use bytes::Bytes;
30//! use http_cache_quickcache::QuickManager;
31//! use std::convert::Infallible;
32//!
33//! // Example Tower service that uses QuickManager for caching
34//! #[derive(Clone)]
35//! struct CachingService {
36//! cache_manager: QuickManager,
37//! }
38//!
39//! impl Service<Request<Full<Bytes>>> for CachingService {
40//! type Response = Response<Full<Bytes>>;
41//! type Error = Box<dyn std::error::Error + Send + Sync>;
42//! type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>> + Send>>;
43//!
44//! fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
45//! std::task::Poll::Ready(Ok(()))
46//! }
47//!
48//! fn call(&mut self, req: Request<Full<Bytes>>) -> Self::Future {
49//! let manager = self.cache_manager.clone();
50//! Box::pin(async move {
51//! // Cache logic using the manager would go here
52//! let response = Response::builder()
53//! .status(StatusCode::OK)
54//! .body(Full::new(Bytes::from("Hello from cached service!")))?;
55//! Ok(response)
56//! })
57//! }
58//! }
59//! ```
60//!
61//! ### With Hyper
62//!
63//! ```no_run
64//! use hyper::{Request, Response, StatusCode, body::Incoming};
65//! use http_body_util::Full;
66//! use bytes::Bytes;
67//! use http_cache_quickcache::QuickManager;
68//! use std::convert::Infallible;
69//!
70//! async fn handle_request(
71//! _req: Request<Incoming>,
72//! cache_manager: QuickManager,
73//! ) -> Result<Response<Full<Bytes>>, Infallible> {
74//! // Use cache_manager here for caching responses
75//! Ok(Response::builder()
76//! .status(StatusCode::OK)
77//! .header("cache-control", "max-age=3600")
78//! .body(Full::new(Bytes::from("Hello from Hyper with caching!")))
79//! .unwrap())
80//! }
81//! ```
82//!
83//! ## Usage Characteristics
84//!
85//! QuickCache is designed for scenarios where:
86//! - You need predictable memory usage
87//! - In-memory storage is acceptable
88//! - You want to avoid complex configuration
89//! - Memory-based caching fits your use case
90//!
91//! For applications that need persistent caching across restarts, consider using
92//! [`CACacheManager`](https://docs.rs/http-cache/latest/http_cache/struct.CACacheManager.html)
93//! instead, which provides disk-based storage.
94
95use http_cache::{CacheManager, HttpResponse, Result};
96
97use std::{fmt, sync::Arc};
98
99use http_cache_semantics::CachePolicy;
100use quick_cache::sync::Cache;
101use serde::{Deserialize, Serialize};
102
103/// HTTP cache manager implementation using QuickCache.
104///
105/// This manager provides in-memory caching using the QuickCache library and implements
106/// the [`CacheManager`] trait for HTTP caching support.
107///
108/// ## Examples
109///
110/// ### Basic Usage
111///
112/// ```rust
113/// use http_cache_quickcache::QuickManager;
114/// use quick_cache::sync::Cache;
115///
116/// // Create a cache with 1000 entry limit
117/// let cache = Cache::new(1000);
118/// let manager = QuickManager::new(cache);
119/// ```
120///
121/// ## Default Configuration
122///
123/// The default configuration creates a cache with 42 entries:
124///
125/// ```rust
126/// use http_cache_quickcache::QuickManager;
127///
128/// let manager = QuickManager::default();
129/// ```
130#[derive(Clone)]
131pub struct QuickManager {
132 /// The underlying QuickCache instance.
133 ///
134 /// This is wrapped in an `Arc` to allow sharing across threads while
135 /// maintaining the `Clone` implementation for the manager.
136 cache: Arc<Cache<String, Arc<Vec<u8>>>>,
137}
138
139impl fmt::Debug for QuickManager {
140 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141 f.debug_struct("QuickManager")
142 .field("cache", &"Cache<String, Arc<Vec<u8>>>")
143 .finish_non_exhaustive()
144 }
145}
146
147impl Default for QuickManager {
148 /// Creates a new QuickManager with a default cache size of 42 entries.
149 ///
150 /// For production use, consider using [`QuickManager::new`] with a
151 /// cache size appropriate for your use case.
152 fn default() -> Self {
153 Self::new(Cache::new(42))
154 }
155}
156
157#[derive(Debug, Deserialize, Serialize)]
158struct Store {
159 response: HttpResponse,
160 policy: CachePolicy,
161}
162
163impl QuickManager {
164 /// Creates a new QuickManager from a pre-configured QuickCache.
165 ///
166 /// This allows you to customize the cache configuration, such as setting
167 /// the maximum number of entries.
168 ///
169 /// # Arguments
170 ///
171 /// * `cache` - A configured QuickCache instance
172 ///
173 /// # Examples
174 ///
175 /// ```rust
176 /// use http_cache_quickcache::QuickManager;
177 /// use quick_cache::sync::Cache;
178 ///
179 /// // Create a cache with 10,000 entry limit
180 /// let cache = Cache::new(10_000);
181 /// let manager = QuickManager::new(cache);
182 /// ```
183 ///
184 /// ## Cache Size Considerations
185 ///
186 /// Choose your cache size based on:
187 /// - Available memory
188 /// - Expected number of unique cacheable requests
189 /// - Average response size
190 /// - Cache hit rate requirements
191 ///
192 /// ```rust
193 /// use http_cache_quickcache::QuickManager;
194 /// use quick_cache::sync::Cache;
195 ///
196 /// // For an application with many unique endpoints
197 /// let large_cache = QuickManager::new(Cache::new(50_000));
198 ///
199 /// // For an application with few cacheable responses
200 /// let small_cache = QuickManager::new(Cache::new(100));
201 /// ```
202 pub fn new(cache: Cache<String, Arc<Vec<u8>>>) -> Self {
203 Self { cache: Arc::new(cache) }
204 }
205}
206
207#[async_trait::async_trait]
208impl CacheManager for QuickManager {
209 async fn get(
210 &self,
211 cache_key: &str,
212 ) -> Result<Option<(HttpResponse, CachePolicy)>> {
213 let store: Store = match self.cache.get(cache_key) {
214 Some(d) => bincode::deserialize(&d)?,
215 None => return Ok(None),
216 };
217 Ok(Some((store.response, store.policy)))
218 }
219
220 async fn put(
221 &self,
222 cache_key: String,
223 response: HttpResponse,
224 policy: CachePolicy,
225 ) -> Result<HttpResponse> {
226 let data = Store { response: response.clone(), policy };
227 let bytes = bincode::serialize(&data)?;
228 self.cache.insert(cache_key, Arc::new(bytes));
229 Ok(response)
230 }
231
232 async fn delete(&self, cache_key: &str) -> Result<()> {
233 self.cache.remove(cache_key);
234 Ok(())
235 }
236}
237
238#[cfg(test)]
239mod test;