Skip to main content

hitbox_tower/
future.rs

1//! Future types for the cache service.
2//!
3//! This module provides [`CacheServiceFuture`](crate::future::CacheServiceFuture),
4//! the future returned by [`CacheService::call`]. It wraps the inner cache future
5//! and adds cache status headers to responses.
6//!
7//! Users typically don't interact with this module directly.
8//!
9//! [`CacheService::call`]: crate::service::CacheService
10
11use std::pin::Pin;
12use std::task::{Context, Poll};
13
14use futures::Future;
15use futures::ready;
16use hitbox::{CacheContext, CacheStatusExt};
17use hitbox_http::{BufferedBody, CacheableHttpResponse};
18use http::Response;
19use http::header::HeaderName;
20use pin_project::pin_project;
21
22/// Future returned by [`CacheService::call`](crate::service::CacheService).
23///
24/// This future wraps the inner `CacheFuture` and performs the final transformation:
25/// converting [`CacheableHttpResponse`] to `http::Response` and adding the cache
26/// status header (`HIT`/`MISS`/`STALE`).
27///
28/// # When You'll Encounter This
29///
30/// You typically don't create this directly. It's the `Future` type returned when
31/// calling the [`CacheService`](crate::service::CacheService) as a Tower service.
32///
33/// # Type Parameters
34///
35/// * `F` - The inner future (typically `CacheFuture`)
36/// * `ResBody` - Response body type
37/// * `E` - Error type from the upstream service
38///
39/// [`CacheableHttpResponse`]: hitbox_http::CacheableHttpResponse
40#[pin_project]
41pub struct CacheServiceFuture<F, ResBody, E>
42where
43    F: Future<Output = (Result<CacheableHttpResponse<ResBody>, E>, CacheContext)>,
44    ResBody: hyper::body::Body,
45{
46    #[pin]
47    inner: F,
48    cache_status_header: HeaderName,
49}
50
51impl<F, ResBody, E> CacheServiceFuture<F, ResBody, E>
52where
53    F: Future<Output = (Result<CacheableHttpResponse<ResBody>, E>, CacheContext)>,
54    ResBody: hyper::body::Body,
55{
56    /// Creates a new future that will add cache status headers to the response.
57    pub fn new(inner: F, cache_status_header: HeaderName) -> Self {
58        Self {
59            inner,
60            cache_status_header,
61        }
62    }
63}
64
65impl<F, ResBody, E> Future for CacheServiceFuture<F, ResBody, E>
66where
67    F: Future<Output = (Result<CacheableHttpResponse<ResBody>, E>, CacheContext)>,
68    ResBody: hyper::body::Body,
69{
70    type Output = Result<Response<BufferedBody<ResBody>>, E>;
71
72    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
73        let this = self.project();
74
75        // Poll the inner CacheFuture
76        let (result, cache_context) = ready!(this.inner.poll(cx));
77
78        // Transform the response and add cache headers
79        let response = result.map(|mut cacheable_response| {
80            // Add cache status header based on cache context
81            cacheable_response.cache_status(cache_context.status, this.cache_status_header);
82
83            cacheable_response.into_response()
84        });
85
86        Poll::Ready(response)
87    }
88}