1#![doc = include_str!("../README.md")]
2
3use std::task::Poll;
4use tower_layer::Layer;
5use tower_service::Service;
6
7mod cache_provider;
8mod err;
9mod future;
10mod passthrough_predicate;
11mod response;
12
13#[cfg(feature = "simple-etag-cache-key")]
14pub mod simple_etag_cache_key;
15
16#[cfg(feature = "base64-blake3-body-etag")]
17pub mod base64_blake3_body_etag;
18
19#[cfg(feature = "const-lru-provider")]
20pub mod const_lru_provider;
21
22pub use cache_provider::*;
23pub use err::*;
24pub use future::*;
25pub use passthrough_predicate::*;
26pub use response::*;
27
28#[derive(Clone, Copy, Debug)]
30pub struct EtagCache<C, P, S> {
31 cache_provider: C,
32 passthrough_predicate: P,
33 inner: S,
34}
35
36impl<C, P, S> EtagCache<C, P, S> {
37 pub fn new(cache_provider: C, passthrough_predicate: P, inner: S) -> Self {
38 Self {
39 cache_provider,
40 passthrough_predicate,
41 inner,
42 }
43 }
44}
45
46impl<C, S> EtagCache<C, DefaultPredicate, S> {
47 pub fn with_default_predicate(cache_provider: C, inner: S) -> Self {
48 Self {
49 cache_provider,
50 passthrough_predicate: DefaultPredicate,
51 inner,
52 }
53 }
54}
55
56#[derive(Clone, Copy, Debug)]
58pub struct EtagCacheLayer<C, P> {
59 cache_provider: C,
60 passthrough_predicate: P,
61}
62
63impl<C, P> EtagCacheLayer<C, P> {
64 pub fn new(cache_provider: C, passthrough_predicate: P) -> Self {
65 Self {
66 cache_provider,
67 passthrough_predicate,
68 }
69 }
70}
71
72impl<C> EtagCacheLayer<C, DefaultPredicate> {
73 pub fn with_default_predicate(cache_provider: C) -> Self {
74 Self {
75 cache_provider,
76 passthrough_predicate: DefaultPredicate,
77 }
78 }
79}
80
81impl<C: Clone, P: Clone, S> Layer<S> for EtagCacheLayer<C, P> {
82 type Service = EtagCache<C, P, S>;
83
84 fn layer(&self, inner: S) -> Self::Service {
85 EtagCache::new(
86 self.cache_provider.clone(),
87 self.passthrough_predicate.clone(),
88 inner,
89 )
90 }
91}
92
93impl<ReqBody, ResBody, C, P, S> Service<http::Request<ReqBody>> for EtagCache<C, P, S>
94where
95 C: CacheProvider<ReqBody, ResBody> + Clone,
96 P: PassthroughPredicate,
97 S: Service<http::Request<ReqBody>, Response = http::Response<ResBody>> + Clone,
98{
99 type Response = http::Response<EtagCacheResBody<ResBody, C::TResBody>>;
100
101 type Error = EtagCacheServiceError<
102 <C as Service<http::Request<ReqBody>>>::Error,
103 S::Error,
104 <C as Service<(C::Key, http::Response<ResBody>)>>::Error,
105 >;
106
107 type Future = EtagCacheServiceFuture<ReqBody, ResBody, C, P, S>;
108
109 fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> Poll<Result<(), Self::Error>> {
112 Poll::Ready(Ok(()))
113 }
114
115 fn call(&mut self, req: http::Request<ReqBody>) -> Self::Future {
116 match self.passthrough_predicate.should_passthrough_req(&req) {
117 true => EtagCacheServiceFuture::passthrough(
118 self.cache_provider.clone(),
119 self.passthrough_predicate.clone(),
120 self.inner.clone(),
121 req,
122 ),
123 false => EtagCacheServiceFuture::start(
124 self.cache_provider.clone(),
125 self.passthrough_predicate.clone(),
126 self.inner.clone(),
127 req,
128 ),
129 }
130 }
131}