tower_http_cache_plus/cache/
response.rs1use super::{body::*, configuration::*, hooks::*, weight::*};
2
3use {
4 core::any::*,
5 duration_str::*,
6 http::{header::*, response::*, *},
7 http_body::*,
8 kutil::{
9 http::*,
10 std::{error::*, immutable::*},
11 transcoding::*,
12 },
13 std::{io, mem::*, result::Result, sync::*, time::*},
14};
15
16pub type CachedResponseRef = Arc<CachedResponse>;
18
19#[derive(Clone, Debug)]
27pub struct CachedResponse {
28 pub parts: Parts,
30
31 pub body: CachedBody,
33
34 pub duration: Option<Duration>,
36}
37
38impl CachedResponse {
39 pub async fn new_for<BodyT>(
61 uri: &Uri,
62 response: Response<BodyT>,
63 declared_body_size: Option<usize>,
64 mut preferred_encoding: Encoding,
65 skip_encoding: bool,
66 caching_configuration: &CachingConfiguration,
67 encoding_configuration: &EncodingConfiguration,
68 ) -> Result<Self, ErrorWithResponsePieces<ReadBodyError, BodyT>>
69 where
70 BodyT: Body + Unpin,
71 BodyT::Error: Into<CapturedError>,
72 {
73 let (mut parts, body) = response.into_parts();
74
75 let bytes = match body
76 .read_into_bytes_or_pieces(
77 declared_body_size,
78 caching_configuration.min_body_size,
79 caching_configuration.max_body_size,
80 )
81 .await
82 {
83 Ok((bytes, _trailers)) => bytes,
84 Err(error) => {
85 return Err(ErrorWithResponsePieces::new_from_body(error, parts));
86 }
87 };
88
89 if preferred_encoding != Encoding::Identity {
90 if !parts
91 .headers
92 .xx_encode(encoding_configuration.encodable_by_default)
93 {
94 tracing::debug!(
95 "not encoding to {} ({}=false)",
96 preferred_encoding,
97 XX_ENCODE
98 );
99 preferred_encoding = Encoding::Identity;
100 } else if bytes.len() < encoding_configuration.min_body_size {
101 tracing::debug!("not encoding to {} (too small)", preferred_encoding);
102 preferred_encoding = Encoding::Identity;
103 }
104 }
105
106 let body = CachedBody::new_with(
107 bytes,
108 parts.headers.content_encoding().into(),
109 preferred_encoding,
110 encoding_configuration,
111 )
112 .await
113 .map_err(|error| ErrorWithResponsePieces::from(ReadBodyError::from(error)))?;
115
116 let duration = match parts.headers.xx_cache_duration() {
118 Some(duration) => Some(duration),
119 None => caching_configuration
120 .cache_duration
121 .as_ref()
122 .and_then(|duration| duration(CacheDurationHookContext::new(uri, &parts.headers))),
123 };
124
125 if let Some(duration) = duration {
126 tracing::debug!("duration: {}", duration.human_format());
127 }
128
129 if !parts.headers.contains_key(LAST_MODIFIED) {
131 parts.headers.set_into_header_value(LAST_MODIFIED, now());
132 }
133
134 parts.headers.remove(XX_CACHE);
135 parts.headers.remove(XX_CACHE_DURATION);
136 parts.headers.remove(CONTENT_ENCODING);
137 parts.headers.remove(CONTENT_LENGTH);
138 parts.headers.remove(CONTENT_DIGEST);
139
140 if skip_encoding {
144 parts.headers.set_bool_value(XX_ENCODE, true);
145 }
146
147 parts.headers.remove(ACCEPT_RANGES);
150
151 Ok(Self {
152 parts,
153 body,
154 duration,
155 })
156 }
157
158 pub fn clone_with_body(&self, body: CachedBody) -> Self {
160 Self {
161 parts: self.parts.clone(),
162 body,
163 duration: self.duration.clone(),
164 }
165 }
166
167 pub fn headers(&self) -> &HeaderMap {
169 &self.parts.headers
170 }
171
172 pub async fn to_response<BodyT>(
186 &self,
187 mut encoding: &Encoding,
188 configuration: &EncodingConfiguration,
189 ) -> io::Result<(Response<BodyT>, Option<Self>)>
190 where
191 BodyT: Body + From<ImmutableBytes>,
192 {
193 if (*encoding != Encoding::Identity)
194 && !self.headers().xx_encode(configuration.encodable_by_default)
195 {
196 tracing::debug!("not encoding to {} ({}=false)", encoding, XX_ENCODE);
197 encoding = &Encoding::Identity;
198 }
199
200 let (bytes, modified) = self.body.get(encoding, configuration).await?;
201
202 let mut parts = self.parts.clone();
203
204 parts.headers.remove(XX_ENCODE);
205
206 if *encoding != Encoding::Identity {
207 parts
209 .headers
210 .set_into_header_value(CONTENT_ENCODING, encoding.clone());
211 }
212
213 parts.headers.set_value(CONTENT_LENGTH, bytes.len());
214
215 Ok((
216 Response::from_parts(parts, bytes.into()),
217 modified.map(|body| self.clone_with_body(body)),
218 ))
219 }
220}
221
222impl CacheWeight for CachedResponse {
223 fn cache_weight(&self) -> usize {
224 const SELF_SIZE: usize = size_of::<CachedResponse>();
225 const HEADER_MAP_ENTRY_SIZE: usize = size_of::<HeaderName>() + size_of::<HeaderValue>();
226 const EXTENSION_ENTRY_SIZE: usize = size_of::<TypeId>();
227
228 let mut size = SELF_SIZE;
229
230 let parts = &self.parts;
231 for (name, value) in &parts.headers {
232 size += HEADER_MAP_ENTRY_SIZE + name.as_str().len() + value.len()
233 }
234 size += parts.extensions.len() * EXTENSION_ENTRY_SIZE;
235
236 size += self.body.cache_weight();
237
238 size
239 }
240}