Skip to main content

http_cache_tower/
lib.rs

1//! HTTP caching middleware for Tower services and Axum applications.
2//!
3//! This crate provides Tower layers that implement HTTP caching according to RFC 7234.
4//! It supports both traditional buffered caching and streaming responses for large payloads.
5//!
6//! ## Basic Usage
7//!
8//! ### With Tower Services
9//!
10//! ```rust,no_run
11//! use http_cache_tower::{HttpCacheLayer, RedbManager};
12//! use http_cache::{CacheMode, HttpCache, HttpCacheOptions};
13//! use tower::ServiceBuilder;
14//! use tower::service_fn;
15//! use tower::ServiceExt;
16//! use http::{Request, Response};
17//! use http_body_util::Full;
18//! use bytes::Bytes;
19//! use std::convert::Infallible;
20//!
21//! async fn handler(_req: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, Infallible> {
22//!     Ok(Response::new(Full::new(Bytes::from("Hello, World!"))))
23//! }
24//!
25//! #[tokio::main]
26//! async fn main() {
27//!     // Create cache manager with disk storage
28//!     let cache_manager = RedbManager::new("./http-cache.redb").unwrap();
29//!     
30//!     // Create cache layer
31//!     let cache_layer = HttpCacheLayer::new(cache_manager);
32//!     
33//!     // Build service with caching
34//!     let service = ServiceBuilder::new()
35//!         .layer(cache_layer)
36//!         .service_fn(handler);
37//!     
38//!     // Use the service
39//!     let request = Request::builder()
40//!         .uri("http://example.com")
41//!         .body(Full::new(Bytes::new()))
42//!         .unwrap();
43//!     let response = service.oneshot(request).await.unwrap();
44//! }
45//! ```
46//!
47//! ### With Custom Cache Configuration
48//!
49//! ```rust,no_run
50//! use http_cache_tower::{HttpCacheLayer, RedbManager};
51//! use http_cache::{CacheMode, HttpCache, HttpCacheOptions};
52//!
53//! # #[tokio::main]
54//! # async fn main() {
55//! // Create cache manager
56//! let cache_manager = RedbManager::new("./http-cache.redb").unwrap();
57//!
58//! // Configure cache behavior
59//! let cache = HttpCache {
60//!     mode: CacheMode::Default,
61//!     manager: cache_manager,
62//!     options: HttpCacheOptions::default(),
63//! };
64//!
65//! // Create layer with custom cache
66//! let cache_layer = HttpCacheLayer::with_cache(cache);
67//! # }
68//! ```
69//!
70//! ### Streaming Support
71//!
72//! For handling large responses without buffering, use `StreamingManager`:
73//!
74//! ```rust,ignore
75//! use http_cache_tower::HttpCacheStreamingLayer;
76//! use http_cache::StreamingManager;
77//!
78//! # #[tokio::main]
79//! # async fn main() {
80//! // Create streaming cache setup
81//! let streaming_manager = StreamingManager::with_temp_dir(1000).await.unwrap();
82//! let streaming_layer = HttpCacheStreamingLayer::new(streaming_manager);
83//!
84//! // Use with your service
85//! // let service = streaming_layer.layer(your_service);
86//! # }
87//! ```
88//!
89//! ## Cache Modes
90//!
91//! Different cache modes provide different behaviors:
92//!
93//! - `CacheMode::Default`: Follow HTTP caching rules strictly
94//! - `CacheMode::NoStore`: Never cache responses
95//! - `CacheMode::NoCache`: Always revalidate with the origin server
96//! - `CacheMode::ForceCache`: Cache responses even if headers suggest otherwise
97//! - `CacheMode::OnlyIfCached`: Only serve from cache, never hit origin server
98//! - `CacheMode::IgnoreRules`: Cache everything regardless of headers
99//!
100//! ## Cache Invalidation
101//!
102//! The middleware automatically handles cache invalidation for unsafe HTTP methods:
103//!
104//! ```text
105//! These methods will invalidate any cached GET response for the same URI:
106//! - PUT /api/users/123    -> invalidates GET /api/users/123
107//! - POST /api/users/123   -> invalidates GET /api/users/123  
108//! - DELETE /api/users/123 -> invalidates GET /api/users/123
109//! - PATCH /api/users/123  -> invalidates GET /api/users/123
110//! ```
111//!
112//! ## Integration with Other Tower Layers
113//!
114//! The cache layer works with other Tower middleware:
115//!
116//! ```rust,no_run
117//! use tower::ServiceBuilder;
118//! use http_cache_tower::{HttpCacheLayer, RedbManager};
119//! use tower::service_fn;
120//! use tower::ServiceExt;
121//! use http::{Request, Response};
122//! use http_body_util::Full;
123//! use bytes::Bytes;
124//! use std::convert::Infallible;
125//!
126//! async fn handler(_req: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, Infallible> {
127//!     Ok(Response::new(Full::new(Bytes::from("Hello, World!"))))
128//! }
129//!
130//! #[tokio::main]
131//! async fn main() {
132//!     let cache_manager = RedbManager::new("./http-cache.redb").unwrap();
133//!     let cache_layer = HttpCacheLayer::new(cache_manager);
134//!
135//!     let service = ServiceBuilder::new()
136//!         // .layer(TraceLayer::new_for_http())  // Logging (requires tower-http)
137//!         // .layer(CompressionLayer::new())     // Compression (requires tower-http)
138//!         .layer(cache_layer)                    // Caching
139//!         .service_fn(handler);
140//!     
141//!     // Use the service
142//!     let request = Request::builder()
143//!         .uri("http://example.com")
144//!         .body(Full::new(Bytes::new()))
145//!         .unwrap();
146//!     let response = service.oneshot(request).await.unwrap();
147//! }
148//! ```
149
150use bytes::Bytes;
151use http::{
152    header::CACHE_CONTROL, request, HeaderValue, Method, Request, Response,
153};
154use http_body::Body;
155use http_body_util::BodyExt;
156
157#[cfg(feature = "manager-cacache")]
158pub use http_cache::CACacheManager;
159
160#[cfg(feature = "manager-redb")]
161pub use http_cache::RedbManager;
162
163#[cfg(feature = "rate-limiting")]
164pub use http_cache::rate_limiting::{
165    CacheAwareRateLimiter, DirectRateLimiter, DomainRateLimiter, Quota,
166};
167#[cfg(feature = "streaming")]
168use http_cache::StreamingError;
169use http_cache::{
170    url_parse, BoxError, CacheManager, CacheMode, HitOrMiss, HttpCache,
171    HttpCacheOptions, HttpResponse, Middleware, Url, XCACHE, XCACHELOOKUP,
172};
173#[cfg(feature = "streaming")]
174use http_cache::{HttpStreamingCache, StreamingCacheManager};
175use std::{
176    pin::Pin,
177    sync::Arc,
178    task::{Context, Poll},
179};
180use tower::{Layer, Service, ServiceExt};
181
182// Re-export unified error types from http-cache core
183pub use http_cache::HttpCacheError;
184
185#[cfg(feature = "streaming")]
186/// Type alias for tower streaming errors, using the unified streaming error system
187pub type TowerStreamingError = http_cache::ClientStreamingError;
188
189/// Helper functions for error conversions
190trait HttpCacheErrorExt<T> {
191    fn cache_err(self) -> Result<T, HttpCacheError>;
192}
193
194impl<T, E> HttpCacheErrorExt<T> for Result<T, E>
195where
196    E: ToString,
197{
198    fn cache_err(self) -> Result<T, HttpCacheError> {
199        self.map_err(|e| HttpCacheError::cache(e.to_string()))
200    }
201}
202
203/// Helper function to add cache status headers to a response
204fn add_cache_status_headers<B>(
205    mut response: Response<HttpCacheBody<B>>,
206    hit_or_miss: &str,
207    cache_lookup: &str,
208) -> Response<HttpCacheBody<B>> {
209    let headers = response.headers_mut();
210    if let Ok(hv) = HeaderValue::from_str(hit_or_miss) {
211        headers.insert(XCACHE, hv);
212    }
213    if let Ok(hv) = HeaderValue::from_str(cache_lookup) {
214        headers.insert(XCACHELOOKUP, hv);
215    }
216    response
217}
218
219/// Middleware adapter that bridges Tower services to the `http_cache::Middleware`
220/// trait, allowing `HttpCache::run` to drive the full cache flow (mode dispatch,
221/// conditional revalidation, 5xx handling, warning headers, etc.) instead of
222/// reimplementing it inline.
223struct TowerMiddleware<S, ReqBody> {
224    parts: request::Parts,
225    body: Option<ReqBody>,
226    service: Option<S>,
227}
228
229impl<S, ReqBody, ResBody> Middleware for TowerMiddleware<S, ReqBody>
230where
231    S: Service<Request<ReqBody>, Response = Response<ResBody>>
232        + Clone
233        + Send
234        + 'static,
235    S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
236    S::Future: Send + 'static,
237    ReqBody: Body + Send + 'static,
238    ReqBody::Data: Send,
239    ReqBody::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
240    ResBody: Body + Send + 'static,
241    ResBody::Data: Send,
242    ResBody::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
243{
244    fn is_method_get_head(&self) -> bool {
245        self.parts.method == Method::GET || self.parts.method == Method::HEAD
246    }
247
248    fn update_headers(
249        &mut self,
250        parts: &request::Parts,
251    ) -> http_cache::Result<()> {
252        for (name, value) in parts.headers.iter() {
253            self.parts.headers.insert(name.clone(), value.clone());
254        }
255        Ok(())
256    }
257
258    fn force_no_cache(&mut self) -> http_cache::Result<()> {
259        self.parts
260            .headers
261            .insert(CACHE_CONTROL, HeaderValue::from_static("no-cache"));
262        Ok(())
263    }
264
265    fn parts(&self) -> http_cache::Result<request::Parts> {
266        Ok(self.parts.clone())
267    }
268
269    fn url(&self) -> http_cache::Result<Url> {
270        url_parse(self.parts.uri.to_string().as_str())
271    }
272
273    async fn remote_fetch(&mut self) -> http_cache::Result<HttpResponse> {
274        let body = self
275            .body
276            .take()
277            .ok_or_else(|| BoxError::from("request body already consumed"))?;
278        let service = self
279            .service
280            .take()
281            .ok_or_else(|| BoxError::from("inner service already consumed"))?;
282
283        let request = Request::from_parts(self.parts.clone(), body);
284        let response = service.oneshot(request).await.map_err(|e| {
285            let boxed: Box<dyn std::error::Error + Send + Sync> = e.into();
286            boxed
287        })?;
288
289        let (res_parts, res_body) = response.into_parts();
290        let collected = BodyExt::collect(res_body).await.map_err(|e| {
291            let boxed: Box<dyn std::error::Error + Send + Sync> = e.into();
292            boxed
293        })?;
294        let body_bytes = collected.to_bytes().to_vec();
295
296        let url = url_parse(self.parts.uri.to_string().as_str())?;
297        let headers = (&res_parts.headers).into();
298        let status = res_parts.status.as_u16();
299        let version = res_parts.version.try_into()?;
300
301        Ok(HttpResponse {
302            body: body_bytes,
303            headers,
304            status,
305            url,
306            version,
307            metadata: None,
308        })
309    }
310}
311
312/// Convert an [`HttpResponse`] from the cache core into a Tower
313/// `Response<HttpCacheBody<B>>`.
314fn http_response_to_tower_response<B>(
315    http_response: HttpResponse,
316) -> Result<Response<HttpCacheBody<B>>, HttpCacheError> {
317    let mut response = HttpCacheOptions::http_response_to_response(
318        &http_response,
319        HttpCacheBody::Buffered(http_response.body.clone()),
320    )
321    .map_err(HttpCacheError::other)?;
322
323    // Preserve metadata in response extensions
324    if let Some(metadata) = http_response.metadata {
325        response
326            .extensions_mut()
327            .insert(http_cache::HttpCacheMetadata::from(metadata));
328    }
329
330    Ok(response)
331}
332
333#[cfg(feature = "streaming")]
334fn add_cache_status_headers_streaming<B>(
335    mut response: Response<B>,
336    hit_or_miss: &str,
337    cache_lookup: &str,
338) -> Response<B> {
339    let headers = response.headers_mut();
340    if let Ok(hv) = HeaderValue::from_str(hit_or_miss) {
341        headers.insert(XCACHE, hv);
342    }
343    if let Ok(hv) = HeaderValue::from_str(cache_lookup) {
344        headers.insert(XCACHELOOKUP, hv);
345    }
346    response
347}
348
349/// HTTP cache layer for Tower services.
350///
351/// This layer implements HTTP caching according to RFC 7234, automatically caching
352/// GET and HEAD responses based on their cache-control headers and invalidating
353/// cache entries when unsafe methods (PUT, POST, DELETE, PATCH) are used.
354///
355/// # Example
356///
357/// ```rust,no_run
358/// use http_cache_tower::{HttpCacheLayer, RedbManager};
359/// use tower::ServiceBuilder;
360/// use tower::service_fn;
361/// use http::{Request, Response};
362/// use http_body_util::Full;
363/// use bytes::Bytes;
364/// use std::convert::Infallible;
365///
366/// # #[tokio::main]
367/// # async fn main() {
368/// let cache_manager = RedbManager::new("./http-cache.redb").unwrap();
369/// let cache_layer = HttpCacheLayer::new(cache_manager);
370///
371/// // Use with ServiceBuilder
372/// let service = ServiceBuilder::new()
373///     .layer(cache_layer)
374///     .service_fn(|_req: Request<Full<Bytes>>| async {
375///         Ok::<_, Infallible>(Response::new(Full::new(Bytes::from("Hello"))))
376///     });
377/// # }
378/// ```
379#[derive(Clone)]
380pub struct HttpCacheLayer<CM>
381where
382    CM: CacheManager,
383{
384    cache: Arc<HttpCache<CM>>,
385}
386
387impl<CM> HttpCacheLayer<CM>
388where
389    CM: CacheManager,
390{
391    /// Create a new HTTP cache layer with default configuration.
392    ///
393    /// Uses [`CacheMode::Default`] and default [`HttpCacheOptions`].
394    ///
395    /// # Arguments
396    ///
397    /// * `cache_manager` - The cache manager to use for storing responses
398    ///
399    /// # Example
400    ///
401    /// ```rust,no_run
402    /// use http_cache_tower::{HttpCacheLayer, RedbManager};
403    ///
404    /// # #[tokio::main]
405    /// # async fn main() {
406    /// let cache_manager = RedbManager::new("./http-cache.redb").unwrap();
407    /// let layer = HttpCacheLayer::new(cache_manager);
408    /// # }
409    /// ```
410    pub fn new(cache_manager: CM) -> Self {
411        Self {
412            cache: Arc::new(HttpCache {
413                mode: CacheMode::Default,
414                manager: cache_manager,
415                options: HttpCacheOptions::default(),
416            }),
417        }
418    }
419
420    /// Create a new HTTP cache layer with custom options.
421    ///
422    /// Uses [`CacheMode::Default`] but allows customizing the cache behavior
423    /// through [`HttpCacheOptions`].
424    ///
425    /// # Arguments
426    ///
427    /// * `cache_manager` - The cache manager to use for storing responses
428    /// * `options` - Custom cache options
429    ///
430    /// # Example
431    ///
432    /// ```rust,no_run
433    /// use http_cache_tower::{HttpCacheLayer, RedbManager};
434    /// use http_cache::HttpCacheOptions;
435    ///
436    /// # #[tokio::main]
437    /// # async fn main() {
438    /// let cache_manager = RedbManager::new("./http-cache.redb").unwrap();
439    ///
440    /// let options = HttpCacheOptions {
441    ///     cache_key: Some(std::sync::Arc::new(|req: &http::request::Parts| {
442    ///         format!("custom:{}:{}", req.method, req.uri)
443    ///     })),
444    ///     ..Default::default()
445    /// };
446    ///
447    /// let layer = HttpCacheLayer::with_options(cache_manager, options);
448    /// # }
449    /// ```
450    pub fn with_options(cache_manager: CM, options: HttpCacheOptions) -> Self {
451        Self {
452            cache: Arc::new(HttpCache {
453                mode: CacheMode::Default,
454                manager: cache_manager,
455                options,
456            }),
457        }
458    }
459
460    /// Create a new HTTP cache layer with a pre-configured cache.
461    ///
462    /// This method gives you full control over the cache configuration,
463    /// including the cache mode.
464    ///
465    /// # Arguments
466    ///
467    /// * `cache` - A fully configured HttpCache instance
468    ///
469    /// # Example
470    ///
471    /// ```rust,no_run
472    /// use http_cache_tower::{HttpCacheLayer, RedbManager};
473    /// use http_cache::{HttpCache, CacheMode, HttpCacheOptions};
474    ///
475    /// # #[tokio::main]
476    /// # async fn main() {
477    /// let cache_manager = RedbManager::new("./http-cache.redb").unwrap();
478    ///
479    /// let cache = HttpCache {
480    ///     mode: CacheMode::ForceCache,
481    ///     manager: cache_manager,
482    ///     options: HttpCacheOptions::default(),
483    /// };
484    ///
485    /// let layer = HttpCacheLayer::with_cache(cache);
486    /// # }
487    /// ```
488    pub fn with_cache(cache: HttpCache<CM>) -> Self {
489        Self { cache: Arc::new(cache) }
490    }
491}
492
493/// HTTP cache layer with streaming support for Tower services.
494///
495/// This layer provides the same HTTP caching functionality as [`HttpCacheLayer`]
496/// but handles streaming responses. It can work with large
497/// responses without buffering them entirely in memory.
498///
499/// # Example
500///
501/// ```rust,no_run
502/// use http_cache_tower::HttpCacheStreamingLayer;
503/// use http_cache::StreamingManager;
504/// use tower::ServiceBuilder;
505/// use tower::service_fn;
506/// use http::{Request, Response};
507/// use http_body_util::Full;
508/// use bytes::Bytes;
509/// use std::convert::Infallible;
510///
511/// async fn handler(_req: Request<Full<Bytes>>) -> Result<Response<Full<Bytes>>, Infallible> {
512///     Ok(Response::new(Full::new(Bytes::from("Hello"))))
513/// }
514///
515/// # #[tokio::main]
516/// # async fn main() {
517/// let streaming_manager = StreamingManager::with_temp_dir(1000).await.unwrap();
518/// let streaming_layer = HttpCacheStreamingLayer::new(streaming_manager);
519///
520/// // Use with ServiceBuilder
521/// let service = ServiceBuilder::new()
522///     .layer(streaming_layer)
523///     .service_fn(handler);
524/// # }
525/// ```
526#[cfg(feature = "streaming")]
527#[derive(Clone)]
528pub struct HttpCacheStreamingLayer<CM>
529where
530    CM: StreamingCacheManager,
531{
532    cache: Arc<HttpStreamingCache<CM>>,
533}
534
535#[cfg(feature = "streaming")]
536impl<CM> HttpCacheStreamingLayer<CM>
537where
538    CM: StreamingCacheManager,
539{
540    /// Create a new HTTP cache streaming layer with default configuration.
541    ///
542    /// Uses [`CacheMode::Default`] and default [`HttpCacheOptions`].
543    ///
544    /// # Arguments
545    ///
546    /// * `cache_manager` - The streaming cache manager to use
547    ///
548    /// # Example
549    ///
550    /// ```rust,no_run
551    /// use http_cache_tower::HttpCacheStreamingLayer;
552    /// use http_cache::StreamingManager;
553    ///
554    /// # #[tokio::main]
555    /// # async fn main() {
556    /// let streaming_manager = StreamingManager::with_temp_dir(1000).await.unwrap();
557    /// let layer = HttpCacheStreamingLayer::new(streaming_manager);
558    /// # }
559    /// ```
560    pub fn new(cache_manager: CM) -> Self {
561        Self {
562            cache: Arc::new(HttpStreamingCache {
563                mode: CacheMode::Default,
564                manager: cache_manager,
565                options: HttpCacheOptions::default(),
566            }),
567        }
568    }
569
570    /// Create a new HTTP cache streaming layer with custom options.
571    ///
572    /// Uses [`CacheMode::Default`] but allows customizing cache behavior.
573    ///
574    /// # Arguments
575    ///
576    /// * `cache_manager` - The streaming cache manager to use
577    /// * `options` - Custom cache options
578    ///
579    /// # Example
580    ///
581    /// ```rust,no_run
582    /// use http_cache_tower::HttpCacheStreamingLayer;
583    /// use http_cache::{StreamingManager, HttpCacheOptions};
584    ///
585    /// # #[tokio::main]
586    /// # async fn main() {
587    /// let streaming_manager = StreamingManager::with_temp_dir(1000).await.unwrap();
588    ///
589    /// let options = HttpCacheOptions {
590    ///     cache_key: Some(std::sync::Arc::new(|req: &http::request::Parts| {
591    ///         format!("stream:{}:{}", req.method, req.uri)
592    ///     })),
593    ///     ..Default::default()
594    /// };
595    ///
596    /// let layer = HttpCacheStreamingLayer::with_options(streaming_manager, options);
597    /// # }
598    /// ```
599    pub fn with_options(cache_manager: CM, options: HttpCacheOptions) -> Self {
600        Self {
601            cache: Arc::new(HttpStreamingCache {
602                mode: CacheMode::Default,
603                manager: cache_manager,
604                options,
605            }),
606        }
607    }
608
609    /// Create a new HTTP cache streaming layer with a pre-configured cache.
610    ///
611    /// This method gives you full control over the streaming cache configuration.
612    ///
613    /// # Arguments
614    ///
615    /// * `cache` - A fully configured HttpStreamingCache instance
616    ///
617    /// # Example
618    ///
619    /// ```rust,no_run
620    /// use http_cache_tower::HttpCacheStreamingLayer;
621    /// use http_cache::{StreamingManager, HttpStreamingCache, CacheMode, HttpCacheOptions};
622    ///
623    /// # #[tokio::main]
624    /// # async fn main() {
625    /// let streaming_manager = StreamingManager::with_temp_dir(1000).await.unwrap();
626    ///
627    /// let cache = HttpStreamingCache {
628    ///     mode: CacheMode::ForceCache,
629    ///     manager: streaming_manager,
630    ///     options: HttpCacheOptions::default(),
631    /// };
632    ///
633    /// let layer = HttpCacheStreamingLayer::with_cache(cache);
634    /// # }
635    /// ```
636    pub fn with_cache(cache: HttpStreamingCache<CM>) -> Self {
637        Self { cache: Arc::new(cache) }
638    }
639}
640
641impl<S, CM> Layer<S> for HttpCacheLayer<CM>
642where
643    CM: CacheManager,
644{
645    type Service = HttpCacheService<S, CM>;
646
647    fn layer(&self, inner: S) -> Self::Service {
648        HttpCacheService { inner, cache: self.cache.clone() }
649    }
650}
651
652#[cfg(feature = "streaming")]
653impl<S, CM> Layer<S> for HttpCacheStreamingLayer<CM>
654where
655    CM: StreamingCacheManager,
656{
657    type Service = HttpCacheStreamingService<S, CM>;
658
659    fn layer(&self, inner: S) -> Self::Service {
660        HttpCacheStreamingService { inner, cache: self.cache.clone() }
661    }
662}
663
664/// HTTP cache service for Tower/Hyper
665pub struct HttpCacheService<S, CM>
666where
667    CM: CacheManager,
668{
669    inner: S,
670    cache: Arc<HttpCache<CM>>,
671}
672
673impl<S, CM> Clone for HttpCacheService<S, CM>
674where
675    S: Clone,
676    CM: CacheManager,
677{
678    fn clone(&self) -> Self {
679        Self { inner: self.inner.clone(), cache: self.cache.clone() }
680    }
681}
682
683/// HTTP cache streaming service for Tower/Hyper
684#[cfg(feature = "streaming")]
685pub struct HttpCacheStreamingService<S, CM>
686where
687    CM: StreamingCacheManager,
688{
689    inner: S,
690    cache: Arc<HttpStreamingCache<CM>>,
691}
692
693#[cfg(feature = "streaming")]
694impl<S, CM> Clone for HttpCacheStreamingService<S, CM>
695where
696    S: Clone,
697    CM: StreamingCacheManager,
698{
699    fn clone(&self) -> Self {
700        Self { inner: self.inner.clone(), cache: self.cache.clone() }
701    }
702}
703
704impl<S, CM, ReqBody, ResBody> Service<Request<ReqBody>>
705    for HttpCacheService<S, CM>
706where
707    S: Service<Request<ReqBody>, Response = Response<ResBody>>
708        + Clone
709        + Send
710        + 'static,
711    S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
712    S::Future: Send + 'static,
713    ReqBody: Body + Send + 'static,
714    ReqBody::Data: Send,
715    ReqBody::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
716    ResBody: Body + Send + 'static,
717    ResBody::Data: Send,
718    ResBody::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
719    CM: CacheManager,
720{
721    type Response = Response<HttpCacheBody<ResBody>>;
722    type Error = HttpCacheError;
723    type Future = Pin<
724        Box<
725            dyn std::future::Future<
726                    Output = Result<Self::Response, Self::Error>,
727                > + Send,
728        >,
729    >;
730
731    fn poll_ready(
732        &mut self,
733        cx: &mut Context<'_>,
734    ) -> Poll<Result<(), Self::Error>> {
735        self.inner.poll_ready(cx).map_err(|e| HttpCacheError::http(e.into()))
736    }
737
738    fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
739        let cache = self.cache.clone();
740        let (parts, body) = req.into_parts();
741        let inner_service = self.inner.clone();
742
743        Box::pin(async move {
744            let middleware = TowerMiddleware {
745                parts: parts.clone(),
746                body: Some(body),
747                service: Some(inner_service),
748            };
749
750            let can_cache = cache.can_cache_request(&middleware).cache_err()?;
751
752            if can_cache {
753                // Delegate the full cache orchestration (mode dispatch,
754                // conditional revalidation, 304/5xx handling, warning
755                // headers, rate limiting, cache busting) to the core.
756                let res = cache.run(middleware).await.cache_err()?;
757                http_response_to_tower_response(res)
758            } else {
759                // Not cacheable -- forward directly, then invalidate on
760                // success (RFC 7234 Section 4.4).
761                let parts_for_invalidation = middleware.parts().cache_err()?;
762
763                // Reconstruct the request from the middleware's parts.
764                let body = middleware.body.ok_or_else(|| {
765                    HttpCacheError::cache(
766                        "request body already consumed".to_string(),
767                    )
768                })?;
769                let service = middleware.service.ok_or_else(|| {
770                    HttpCacheError::cache(
771                        "inner service already consumed".to_string(),
772                    )
773                })?;
774                let req = Request::from_parts(parts, body);
775
776                let response = service.oneshot(req).await.map_err(|e| {
777                    let boxed: Box<dyn std::error::Error + Send + Sync> =
778                        e.into();
779                    HttpCacheError::http(boxed)
780                })?;
781
782                // Only invalidate for unsafe methods after successful response (RFC 7234 s4.4)
783                if !parts_for_invalidation.method.is_safe()
784                    && (response.status().is_success()
785                        || response.status().is_redirection())
786                {
787                    cache
788                        .run_no_cache_from_parts(&parts_for_invalidation)
789                        .await
790                        .cache_err()?;
791                }
792
793                let mut response = response.map(HttpCacheBody::Original);
794
795                if cache.options.cache_status_headers {
796                    response = add_cache_status_headers(
797                        response,
798                        HitOrMiss::MISS.to_string().as_ref(),
799                        HitOrMiss::MISS.to_string().as_ref(),
800                    );
801                }
802
803                Ok(response)
804            }
805        })
806    }
807}
808
809// Hyper service implementation for HttpCacheService
810impl<S, CM> hyper::service::Service<Request<hyper::body::Incoming>>
811    for HttpCacheService<S, CM>
812where
813    S: Service<
814            Request<hyper::body::Incoming>,
815            Response = Response<http_body_util::Full<Bytes>>,
816        > + Clone
817        + Send
818        + 'static,
819    S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
820    S::Future: Send + 'static,
821    CM: CacheManager,
822{
823    type Response = Response<HttpCacheBody<http_body_util::Full<Bytes>>>;
824    type Error = HttpCacheError;
825    type Future = Pin<
826        Box<
827            dyn std::future::Future<
828                    Output = Result<Self::Response, Self::Error>,
829                > + Send,
830        >,
831    >;
832
833    fn call(&self, req: Request<hyper::body::Incoming>) -> Self::Future {
834        // Delegate to the Tower Service impl, which takes &mut self
835        let mut service_clone = self.clone();
836        Box::pin(
837            async move { tower::Service::call(&mut service_clone, req).await },
838        )
839    }
840}
841
842#[cfg(feature = "streaming")]
843impl<S, CM, ReqBody, ResBody> Service<Request<ReqBody>>
844    for HttpCacheStreamingService<S, CM>
845where
846    S: Service<Request<ReqBody>, Response = Response<ResBody>>
847        + Clone
848        + Send
849        + 'static,
850    S::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
851    S::Future: Send + 'static,
852    ReqBody: Body + Send + 'static,
853    ReqBody::Data: Send,
854    ReqBody::Error: Into<StreamingError>,
855    ResBody: Body + Send + 'static,
856    ResBody::Data: Send,
857    ResBody::Error: Into<StreamingError>,
858    CM: StreamingCacheManager,
859    <CM::Body as http_body::Body>::Data: Send,
860    <CM::Body as http_body::Body>::Error:
861        Into<StreamingError> + Send + Sync + 'static,
862{
863    type Response = Response<CM::Body>;
864    type Error = HttpCacheError;
865    type Future = Pin<
866        Box<
867            dyn std::future::Future<
868                    Output = Result<Self::Response, Self::Error>,
869                > + Send,
870        >,
871    >;
872
873    fn poll_ready(
874        &mut self,
875        cx: &mut Context<'_>,
876    ) -> Poll<Result<(), Self::Error>> {
877        self.inner.poll_ready(cx).map_err(|e| HttpCacheError::http(e.into()))
878    }
879
880    fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
881        let cache = self.cache.clone();
882        let (parts, body) = req.into_parts();
883        let inner_service = self.inner.clone();
884
885        Box::pin(async move {
886            // Check whether this request is cacheable.  Non-cacheable
887            // requests (e.g. POST/PUT/DELETE) are forwarded directly and
888            // only trigger cache invalidation on success.
889            let can_cache =
890                cache.can_cache_request(&parts, None).cache_err()?;
891
892            if !can_cache {
893                // Forward the request without cache orchestration.
894                let req = Request::from_parts(parts.clone(), body);
895                let response =
896                    inner_service.oneshot(req).await.map_err(|e| {
897                        let boxed: Box<dyn std::error::Error + Send + Sync> =
898                            e.into();
899                        HttpCacheError::http(boxed)
900                    })?;
901
902                // Only invalidate for unsafe methods after successful response (RFC 7234 s4.4)
903                if !parts.method.is_safe()
904                    && (response.status().is_success()
905                        || response.status().is_redirection())
906                {
907                    cache.run_no_cache(&parts).await.cache_err()?;
908                }
909
910                let mut converted =
911                    cache.manager.convert_body(response).await.cache_err()?;
912
913                if cache.options.cache_status_headers {
914                    converted = add_cache_status_headers_streaming(
915                        converted, "MISS", "MISS",
916                    );
917                }
918
919                return Ok(converted);
920            }
921
922            // Delegate the full cache orchestration (analyse, lookup,
923            // conditional revalidation, 304/200/5xx handling, rate
924            // limiting, warning headers, cache busting) to the core
925            // library.
926            //
927            // The closure is `FnOnce` and called at most once.
928            // We move `body` and `inner_service` directly into the
929            // closure.
930            let result = cache
931                .run(&parts, None, |fetch_req| {
932                    let parts_ref = parts.clone();
933                    async move {
934                        let request_parts = match fetch_req {
935                            http_cache::FetchRequest::Fresh => parts_ref,
936                            http_cache::FetchRequest::FreshNoCache => {
937                                let mut p = parts_ref;
938                                p.headers.insert(
939                                    CACHE_CONTROL,
940                                    HeaderValue::from_static("no-cache"),
941                                );
942                                p
943                            }
944                            http_cache::FetchRequest::Conditional(
945                                cond_parts,
946                            ) => *cond_parts,
947                        };
948
949                        let req = Request::from_parts(request_parts, body);
950
951                        inner_service.oneshot(req).await.map_err(|e| {
952                            let boxed: Box<
953                                dyn std::error::Error + Send + Sync,
954                            > = e.into();
955                            boxed
956                        })
957                    }
958                })
959                .await
960                .cache_err()?;
961
962            Ok(result)
963        })
964    }
965}
966
967/// Body type that wraps cached responses  
968pub enum HttpCacheBody<B> {
969    /// Buffered body from cache
970    Buffered(Vec<u8>),
971    /// Original body (fallback)
972    Original(B),
973}
974
975impl<B> Body for HttpCacheBody<B>
976where
977    B: Body + Unpin,
978    B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
979    B::Data: Into<bytes::Bytes>,
980{
981    type Data = bytes::Bytes;
982    type Error = Box<dyn std::error::Error + Send + Sync>;
983
984    fn poll_frame(
985        mut self: Pin<&mut Self>,
986        cx: &mut Context<'_>,
987    ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
988        match &mut *self {
989            HttpCacheBody::Buffered(bytes) => {
990                if bytes.is_empty() {
991                    Poll::Ready(None)
992                } else {
993                    let data = std::mem::take(bytes);
994                    Poll::Ready(Some(Ok(http_body::Frame::data(
995                        bytes::Bytes::from(data),
996                    ))))
997                }
998            }
999            HttpCacheBody::Original(body) => {
1000                Pin::new(body).poll_frame(cx).map(|opt| {
1001                    opt.map(|res| {
1002                        res.map(|frame| frame.map_data(Into::into))
1003                            .map_err(Into::into)
1004                    })
1005                })
1006            }
1007        }
1008    }
1009
1010    fn is_end_stream(&self) -> bool {
1011        match self {
1012            HttpCacheBody::Buffered(bytes) => bytes.is_empty(),
1013            HttpCacheBody::Original(body) => body.is_end_stream(),
1014        }
1015    }
1016
1017    fn size_hint(&self) -> http_body::SizeHint {
1018        match self {
1019            HttpCacheBody::Buffered(bytes) => {
1020                let len = bytes.len() as u64;
1021                http_body::SizeHint::with_exact(len)
1022            }
1023            HttpCacheBody::Original(body) => body.size_hint(),
1024        }
1025    }
1026}
1027
1028#[cfg(test)]
1029mod test;