kutil_http/cache/middleware/responses/
upstream.rs

1use super::super::{super::super::headers::*, configuration::*, hooks::*};
2
3use {
4    http::{header::*, *},
5    kutil_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            if let Some(cacheable) = &configuration.cacheable_by_response {
79                if !cacheable(CacheableHookContext::new(uri, headers)) {
80                    tracing::debug!("skip (cacheable_by_response=false)");
81                    skip_cache.0 = true;
82                }
83            }
84        }
85
86        skip_cache
87    }
88
89    fn validate_encoding(
90        &self,
91        uri: &Uri,
92        encoding: Encoding,
93        content_length: Option<usize>,
94        configuration: &MiddlewareEncodingConfiguration,
95    ) -> (Encoding, bool) {
96        if encoding == Encoding::Identity {
97            (encoding, false)
98        } else {
99            if let Some(content_length) = content_length {
100                let min_body_size = configuration.inner.min_body_size;
101                if min_body_size != 0 {
102                    if content_length < min_body_size {
103                        tracing::debug!("not encoding to {} (too small)", encoding);
104                        return (Encoding::Identity, true);
105                    }
106                }
107            }
108
109            match &configuration.encodable_by_response {
110                Some(encodable) => {
111                    if encodable(EncodableHookContext::new(&encoding, uri, self.headers())) {
112                        (encoding, false)
113                    } else {
114                        tracing::debug!("not encoding to {} (encodable_by_response=false)", encoding);
115                        (Encoding::Identity, true)
116                    }
117                }
118
119                None => (encoding, false),
120            }
121        }
122    }
123}