kutil_http/cache/
response.rs1use super::{
2 super::{body::*, headers::*, pieces::*},
3 body::*,
4 configuration::*,
5 hooks::*,
6 weight::*,
7};
8
9use {
10 ::bytes::*,
11 core::any::*,
12 duration_str::*,
13 http::{header::*, response::*, *},
14 http_body::*,
15 kutil_std::error::*,
16 kutil_transcoding::*,
17 std::{io, mem::*, result::Result, sync::*, time::*},
18};
19
20pub type CachedResponseRef = Arc<CachedResponse>;
22
23#[derive(Clone, Debug)]
31pub struct CachedResponse {
32 pub parts: Parts,
34
35 pub body: CachedBody,
37
38 pub duration: Option<Duration>,
40}
41
42impl CachedResponse {
43 pub async fn new_for<BodyT>(
65 uri: &Uri,
66 response: Response<BodyT>,
67 declared_body_size: Option<usize>,
68 mut preferred_encoding: Encoding,
69 skip_encoding: bool,
70 caching_configuration: &CachingConfiguration,
71 encoding_configuration: &EncodingConfiguration,
72 ) -> Result<Self, ErrorWithResponsePieces<ReadBodyError, BodyT>>
73 where
74 BodyT: Body + Unpin,
75 BodyT::Error: Into<CapturedError>,
76 {
77 let (mut parts, body) = response.into_parts();
78
79 let bytes = match body
80 .read_into_bytes_or_pieces(
81 declared_body_size,
82 caching_configuration.min_body_size,
83 caching_configuration.max_body_size,
84 )
85 .await
86 {
87 Ok((bytes, _trailers)) => bytes,
88 Err(error) => {
89 return Err(ErrorWithResponsePieces::new_from_body(error, parts));
90 }
91 };
92
93 if preferred_encoding != Encoding::Identity {
94 if !parts.headers.xx_encode(encoding_configuration.encodable_by_default) {
95 tracing::debug!("not encoding to {} ({}=false)", preferred_encoding, XX_ENCODE);
96 preferred_encoding = Encoding::Identity;
97 } else if bytes.len() < encoding_configuration.min_body_size {
98 tracing::debug!("not encoding to {} (too small)", preferred_encoding);
99 preferred_encoding = Encoding::Identity;
100 }
101 }
102
103 let body = CachedBody::new_with(
104 bytes,
105 parts.headers.content_encoding().into(),
106 preferred_encoding,
107 encoding_configuration,
108 )
109 .await
110 .map_err(|error| ErrorWithResponsePieces::from(ReadBodyError::from(error)))?;
112
113 let duration = match parts.headers.xx_cache_duration() {
115 Some(duration) => Some(duration),
116 None => match &caching_configuration.cache_duration {
117 Some(cache_duration) => cache_duration(CacheDurationHookContext::new(uri, &parts.headers)),
118 None => None,
119 },
120 };
121
122 if let Some(duration) = duration {
123 tracing::debug!("duration: {}", duration.human_format());
124 }
125
126 if !parts.headers.contains_key(LAST_MODIFIED) {
128 parts.headers.set_into_header_value(LAST_MODIFIED, now());
129 }
130
131 parts.headers.remove(XX_CACHE);
132 parts.headers.remove(XX_CACHE_DURATION);
133 parts.headers.remove(CONTENT_ENCODING);
134 parts.headers.remove(CONTENT_LENGTH);
135 parts.headers.remove(CONTENT_DIGEST);
136
137 if skip_encoding {
141 parts.headers.set_bool_value(XX_ENCODE, true);
142 }
143
144 parts.headers.remove(ACCEPT_RANGES);
147
148 Ok(Self { parts, body, duration })
149 }
150
151 pub fn clone_with_body(&self, body: CachedBody) -> Self {
153 Self { parts: self.parts.clone(), body, duration: self.duration.clone() }
154 }
155
156 pub fn headers(&self) -> &HeaderMap {
158 &self.parts.headers
159 }
160
161 pub async fn to_response<BodyT>(
175 &self,
176 mut encoding: &Encoding,
177 configuration: &EncodingConfiguration,
178 ) -> io::Result<(Response<BodyT>, Option<Self>)>
179 where
180 BodyT: Body + From<Bytes>,
181 {
182 if (*encoding != Encoding::Identity) && !self.headers().xx_encode(configuration.encodable_by_default) {
183 tracing::debug!("not encoding to {} ({}=false)", encoding, XX_ENCODE);
184 encoding = &Encoding::Identity;
185 }
186
187 let (bytes, modified) = self.body.get(encoding, configuration).await?;
188
189 let mut parts = self.parts.clone();
190
191 parts.headers.remove(XX_ENCODE);
192
193 if *encoding != Encoding::Identity {
194 parts.headers.set_into_header_value(CONTENT_ENCODING, encoding.clone());
196 }
197
198 parts.headers.set_value(CONTENT_LENGTH, bytes.len());
199
200 Ok((Response::from_parts(parts, bytes.into()), modified.map(|body| self.clone_with_body(body))))
201 }
202}
203
204impl CacheWeight for CachedResponse {
205 fn cache_weight(&self) -> usize {
206 const SELF_SIZE: usize = size_of::<CachedResponse>();
207 const HEADER_MAP_ENTRY_SIZE: usize = size_of::<HeaderName>() + size_of::<HeaderValue>();
208 const EXTENSION_ENTRY_SIZE: usize = size_of::<TypeId>();
209
210 let mut size = SELF_SIZE;
211
212 let parts = &self.parts;
213 for (name, value) in &parts.headers {
214 size += HEADER_MAP_ENTRY_SIZE + name.as_str().len() + value.len()
215 }
216 size += parts.extensions.len() * EXTENSION_ENTRY_SIZE;
217
218 size += self.body.cache_weight();
219
220 size
221 }
222}