Skip to main content

tower_http_cache_plus/cache/middleware/responses/
upstream.rs

1use super::super::{configuration::*, hooks::*};
2
3use {
4    http::{header::*, *},
5    kutil::{http::*, transcoding::*},
6};
7
8//
9// UpstreamResponse
10//
11
12/// Upstream response.
13pub trait UpstreamResponse<ResponseBodyT> {
14    /// Check if we should skip the cache.
15    ///
16    /// Also returns the value of `Content-Length` if available.
17    ///
18    /// If the response passes all our checks then we turn to the hook to give it one last chance
19    /// to skip the cache.
20    fn should_skip_cache<RequestBodyT, CacheT, CacheKeyT>(
21        &self,
22        uri: &Uri,
23        configuration: &MiddlewareCachingConfiguration<CacheT, CacheKeyT, RequestBodyT>,
24    ) -> (bool, Option<usize>);
25
26    /// Validate encoding.
27    ///
28    /// Checks `content_length`, if provided, against `min_body_size`. And gives the hook one last
29    /// chance to skip encoding.
30    ///
31    /// Will return true if we are forcing a skip.
32    fn validate_encoding(
33        &self,
34        uri: &Uri,
35        encoding: Encoding,
36        content_length: Option<usize>,
37        configuration: &MiddlewareEncodingConfiguration,
38    ) -> (Encoding, bool);
39}
40
41impl<ResponseBodyT> UpstreamResponse<ResponseBodyT> for Response<ResponseBodyT> {
42    fn should_skip_cache<RequestBodyT, CacheT, CacheKeyT>(
43        &self,
44        uri: &Uri,
45        configuration: &MiddlewareCachingConfiguration<CacheT, CacheKeyT, RequestBodyT>,
46    ) -> (bool, Option<usize>) {
47        let headers = self.headers();
48        let status = self.status();
49
50        let mut skip_cache = if !headers.xx_cache(configuration.inner.cacheable_by_default) {
51            tracing::debug!("skip ({}=false)", XX_CACHE);
52            (true, None)
53        } else if !status.is_success() {
54            tracing::debug!("skip (status={})", status.as_u16());
55            (true, None)
56        } else if headers.contains_key(CONTENT_RANGE) {
57            tracing::debug!("skip (range)");
58            (true, None)
59        } else {
60            match headers.content_length() {
61                Some(content_length) => {
62                    if content_length < configuration.inner.min_body_size {
63                        tracing::debug!("skip (Content-Length too small)");
64                        (true, Some(content_length))
65                    } else if content_length > configuration.inner.max_body_size {
66                        tracing::debug!("skip (Content-Length too big)");
67                        (true, Some(content_length))
68                    } else {
69                        (false, Some(content_length))
70                    }
71                }
72
73                None => (false, None),
74            }
75        };
76
77        if !skip_cache.0
78            && let Some(cacheable) = &configuration.cacheable_by_response
79            && !cacheable(CacheableHookContext::new(uri, headers))
80        {
81            tracing::debug!("skip (cacheable_by_response=false)");
82            skip_cache.0 = true;
83        }
84
85        skip_cache
86    }
87
88    fn validate_encoding(
89        &self,
90        uri: &Uri,
91        encoding: Encoding,
92        content_length: Option<usize>,
93        configuration: &MiddlewareEncodingConfiguration,
94    ) -> (Encoding, bool) {
95        if encoding == Encoding::Identity {
96            (encoding, false)
97        } else {
98            if let Some(content_length) = content_length {
99                let min_body_size = configuration.inner.min_body_size;
100                if min_body_size != 0 {
101                    if content_length < min_body_size {
102                        tracing::debug!("not encoding to {} (too small)", encoding);
103                        return (Encoding::Identity, true);
104                    }
105                }
106            }
107
108            match &configuration.encodable_by_response {
109                Some(encodable) => {
110                    if encodable(EncodableHookContext::new(&encoding, uri, self.headers())) {
111                        (encoding, false)
112                    } else {
113                        tracing::debug!(
114                            "not encoding to {} (encodable_by_response=false)",
115                            encoding
116                        );
117                        (Encoding::Identity, true)
118                    }
119                }
120
121                None => (encoding, false),
122            }
123        }
124    }
125}