Skip to main content

tower_http_cache/backend/
mod.rs

1//! Storage backends for the cache layer.
2//!
3//! The cache layer requires a [`CacheBackend`] implementation to persist
4//! cached responses. This module ships with:
5//! - [`memory::InMemoryBackend`] — a fast, process-local cache backed by [`moka`].
6//! - `redis::RedisBackend` *(optional)* — a distributed cache when the
7//!   `redis-backend` crate feature is enabled.
8//!
9//! Backends are responsible for answering cache lookups, storing entries,
10//! and enforcing per-entry stale windows.
11
12#[cfg(feature = "in-memory")]
13pub mod memory;
14pub mod multi_tier;
15#[cfg(feature = "redis-backend")]
16pub mod redis;
17
18use bytes::Bytes;
19use http::{HeaderName, HeaderValue, Response, StatusCode, Version};
20use std::future::Future;
21use std::time::{Duration, SystemTime};
22
23use crate::error::CacheError;
24use crate::layer::SyncBoxBody;
25
26/// Cached response payload captured by the cache layer.
27#[derive(Debug, Clone)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct CacheEntry {
30    #[cfg_attr(feature = "serde", serde(with = "status_code_serde"))]
31    pub status: StatusCode,
32    #[cfg_attr(feature = "serde", serde(with = "version_serde"))]
33    pub version: Version,
34    pub headers: Vec<(String, Vec<u8>)>,
35    #[cfg_attr(feature = "serde", serde(with = "bytes_serde"))]
36    pub body: Bytes,
37    pub tags: Option<Vec<String>>,
38}
39
40// Custom serde helpers for http types
41#[cfg(feature = "serde")]
42mod status_code_serde {
43    use http::StatusCode;
44    use serde::{Deserialize, Deserializer, Serialize, Serializer};
45
46    pub fn serialize<S>(status: &StatusCode, serializer: S) -> Result<S::Ok, S::Error>
47    where
48        S: Serializer,
49    {
50        status.as_u16().serialize(serializer)
51    }
52
53    pub fn deserialize<'de, D>(deserializer: D) -> Result<StatusCode, D::Error>
54    where
55        D: Deserializer<'de>,
56    {
57        let code = u16::deserialize(deserializer)?;
58        StatusCode::from_u16(code).map_err(serde::de::Error::custom)
59    }
60}
61
62#[cfg(feature = "serde")]
63mod version_serde {
64    use http::Version;
65    use serde::{Deserialize, Deserializer, Serialize, Serializer};
66
67    pub fn serialize<S>(version: &Version, serializer: S) -> Result<S::Ok, S::Error>
68    where
69        S: Serializer,
70    {
71        // The `u8` annotation is load-bearing. Without it the literals default
72        // to `i32`, so this wrote four bytes while `deserialize` below read
73        // one -- which made `CacheEntry`'s derived impls unusable with any
74        // non-self-describing format. See CHANGELOG 0.6.0.
75        let v: u8 = match *version {
76            Version::HTTP_09 => 0,
77            Version::HTTP_10 => 1,
78            Version::HTTP_11 => 2,
79            Version::HTTP_2 => 3,
80            Version::HTTP_3 => 4,
81            _ => 5,
82        };
83        v.serialize(serializer)
84    }
85
86    pub fn deserialize<'de, D>(deserializer: D) -> Result<Version, D::Error>
87    where
88        D: Deserializer<'de>,
89    {
90        let v = u8::deserialize(deserializer)?;
91        Ok(match v {
92            0 => Version::HTTP_09,
93            1 => Version::HTTP_10,
94            2 => Version::HTTP_11,
95            3 => Version::HTTP_2,
96            4 => Version::HTTP_3,
97            _ => Version::HTTP_11, // Default fallback
98        })
99    }
100}
101
102#[cfg(feature = "serde")]
103mod bytes_serde {
104    use bytes::Bytes;
105    use serde::{Deserialize, Deserializer, Serializer};
106
107    pub fn serialize<S>(bytes: &Bytes, serializer: S) -> Result<S::Ok, S::Error>
108    where
109        S: Serializer,
110    {
111        serializer.serialize_bytes(bytes)
112    }
113
114    pub fn deserialize<'de, D>(deserializer: D) -> Result<Bytes, D::Error>
115    where
116        D: Deserializer<'de>,
117    {
118        let vec = Vec::<u8>::deserialize(deserializer)?;
119        Ok(Bytes::from(vec))
120    }
121}
122
123impl CacheEntry {
124    /// Creates a new cached response entry.
125    ///
126    /// The entry captures the response status, HTTP version, a serialized
127    /// subset of headers, and the collected response body.
128    pub fn new(
129        status: StatusCode,
130        version: Version,
131        headers: Vec<(String, Vec<u8>)>,
132        body: Bytes,
133    ) -> Self {
134        Self {
135            status,
136            version,
137            headers,
138            body,
139            tags: None,
140        }
141    }
142
143    /// Creates a new cached response entry with tags.
144    pub fn with_tags(mut self, tags: Vec<String>) -> Self {
145        self.tags = Some(tags);
146        self
147    }
148
149    /// Converts the entry back into an `http::Response`.
150    pub fn into_response(self) -> Response<SyncBoxBody> {
151        use http_body_util::BodyExt;
152
153        let full_body = http_body_util::Full::from(self.body);
154        let boxed_body = full_body
155            .map_err(|never| -> Box<dyn std::error::Error + Send + Sync> { match never {} })
156            .boxed();
157
158        let mut response = Response::new(SyncBoxBody::new(boxed_body));
159        *response.status_mut() = self.status;
160        *response.version_mut() = self.version;
161
162        let headers = response.headers_mut();
163        headers.clear();
164        for (name, value) in self.headers {
165            if let (Ok(name), Ok(value)) = (
166                HeaderName::from_bytes(name.as_bytes()),
167                HeaderValue::from_bytes(&value),
168            ) {
169                headers.append(name, value);
170            }
171        }
172
173        response
174    }
175}
176
177#[derive(Debug, Clone)]
178pub struct CacheRead {
179    /// Cached entry together with timing metadata.
180    pub entry: CacheEntry,
181    pub expires_at: Option<SystemTime>,
182    pub stale_until: Option<SystemTime>,
183}
184
185/// Storage a [`CacheLayer`](crate::layer::CacheLayer) can read and write.
186///
187/// Every method returns `impl Future<..> + Send` rather than being an
188/// `async fn`. The `+ Send` is required because the cache layer boxes backend
189/// futures into a `Send` future; a bare `async fn` in a trait does not
190/// guarantee it at the bound site.
191///
192/// **Implementing this trait is unaffected by that.** Write plain `async fn`
193/// in your impl block, with no `#[async_trait]` attribute:
194///
195/// ```
196/// # use std::time::Duration;
197/// # use tower_http_cache::backend::{CacheBackend, CacheEntry, CacheRead};
198/// # use tower_http_cache::error::CacheError;
199/// #[derive(Clone)]
200/// struct MyBackend;
201///
202/// impl CacheBackend for MyBackend {
203///     async fn get(&self, _key: &str) -> Result<Option<CacheRead>, CacheError> {
204///         Ok(None)
205///     }
206///     async fn set(
207///         &self,
208///         _key: String,
209///         _entry: CacheEntry,
210///         _ttl: Duration,
211///         _stale_for: Duration,
212///     ) -> Result<(), CacheError> {
213///         Ok(())
214///     }
215///     async fn invalidate(&self, _key: &str) -> Result<(), CacheError> {
216///         Ok(())
217///     }
218/// }
219/// ```
220pub trait CacheBackend: Send + Sync + Clone + 'static {
221    /// Fetches a cached entry by key.
222    ///
223    /// Returns `Ok(None)` when the backend does not have a value or the
224    /// entry has expired.
225    fn get(&self, key: &str) -> impl Future<Output = Result<Option<CacheRead>, CacheError>> + Send;
226
227    /// Stores an entry with a time-to-live and additional stale window.
228    fn set(
229        &self,
230        key: String,
231        entry: CacheEntry,
232        ttl: Duration,
233        stale_for: Duration,
234    ) -> impl Future<Output = Result<(), CacheError>> + Send;
235
236    /// Invalidates the cache entry for `key`, if present.
237    fn invalidate(&self, key: &str) -> impl Future<Output = Result<(), CacheError>> + Send;
238
239    /// Retrieves all cache keys associated with a tag.
240    ///
241    /// Returns an empty vector if tags are not supported by this backend.
242    fn get_keys_by_tag(
243        &self,
244        _tag: &str,
245    ) -> impl Future<Output = Result<Vec<String>, CacheError>> + Send {
246        async { Ok(Vec::new()) }
247    }
248
249    /// Invalidates all cache entries associated with a tag.
250    ///
251    /// Returns the number of entries invalidated.
252    fn invalidate_by_tag(
253        &self,
254        tag: &str,
255    ) -> impl Future<Output = Result<usize, CacheError>> + Send {
256        async move {
257            let keys = self.get_keys_by_tag(tag).await?;
258            let count = keys.len();
259            for key in keys {
260                let _ = self.invalidate(&key).await;
261            }
262            Ok(count)
263        }
264    }
265
266    /// Invalidates all cache entries associated with multiple tags.
267    ///
268    /// Returns the total number of entries invalidated (may include duplicates).
269    fn invalidate_by_tags(
270        &self,
271        tags: &[String],
272    ) -> impl Future<Output = Result<usize, CacheError>> + Send {
273        async move {
274            let mut total = 0;
275            for tag in tags {
276                total += self.invalidate_by_tag(tag).await?;
277            }
278            Ok(total)
279        }
280    }
281
282    /// Lists all currently indexed tags.
283    ///
284    /// Returns an empty vector if tags are not supported by this backend.
285    fn list_tags(&self) -> impl Future<Output = Result<Vec<String>, CacheError>> + Send {
286        async { Ok(Vec::new()) }
287    }
288}