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}