kutil_http/headers/headers.rs
1use super::{bool::*, encoding::*, etag::*, into::*, language::*, media_type::*, preferences::*};
2
3use {
4 base64::{engine::general_purpose::*, *},
5 bytes::Bytes,
6 bytestring::*,
7 http::header::*,
8 httpdate::*,
9 kutil_std::collections::*,
10 std::{any::*, fmt, str::*, time::*},
11};
12
13//
14// HeaderValues
15//
16
17/// Access header values.
18pub trait HeaderValues {
19 // General get
20
21 /// Parse a header value as an ASCII string.
22 ///
23 /// [None](Option::None) could mean that there is no such header *or* that it is not a valid
24 /// ASCII string.
25 fn string_value(&self, name: HeaderName) -> Option<&str>;
26
27 /// Parse all header values as ASCII strings.
28 ///
29 /// Will skip over non-ASCII values.
30 fn string_values(&self, name: HeaderName) -> Vec<&str>;
31
32 /// Parse a header value as an ASCII string.
33 ///
34 /// [None](Option::None) could mean that there is no such header *or* that it is not a valid
35 /// ASCII string.
36 ///
37 /// Unfortunately this is *not* zero-copy because [HeaderValue] does not give us access to its
38 /// inner [Bytes].
39 fn byte_string_value(&self, name: HeaderName) -> Option<ByteString>;
40
41 /// Parse all header values as ASCII strings.
42 ///
43 /// Will skip over non-ASCII values.
44 ///
45 /// Unfortunately this is *not* zero-copy because [HeaderValue] does not give us access to its
46 /// inner [Bytes].
47 fn byte_string_values(&self, name: HeaderName) -> Vec<ByteString>;
48
49 /// Parse a header value as a boolean ("true" or "false") or return a default a value.
50 fn bool_value(&self, name: HeaderName, default: bool) -> bool {
51 if let Some(value) = self.string_value(name) {
52 match value.to_lowercase().as_str() {
53 "true" => return true,
54 "false" => return false,
55 _ => {}
56 }
57 }
58
59 default
60 }
61
62 /// Parse a header from its ASCII string value.
63 ///
64 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
65 fn parse_value<FromStrT>(&self, name: HeaderName) -> Option<FromStrT>
66 where
67 FromStrT: FromStr,
68 FromStrT::Err: fmt::Display,
69 {
70 match self.string_value(name)?.parse() {
71 Ok(value) => Some(value),
72
73 Err(error) => {
74 tracing::warn!("malformed {}: {}", type_name::<FromStrT>(), error);
75 None
76 }
77 }
78 }
79
80 /// Parse, combine, and sort all header values as [Preferences].
81 ///
82 /// Will skip over malformed values.
83 fn preferences<FormatT>(&self, name: HeaderName) -> Preferences<FormatT>
84 where
85 FormatT: Clone + Eq + FromStr,
86 {
87 Preferences::parse(&self.string_values(name))
88 }
89
90 /// Parse a header value as a [Duration].
91 ///
92 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
93 ///
94 /// See [duration-str](https://github.com/baoyachi/duration-str).
95 fn duration_value(&self, name: HeaderName) -> Option<Duration> {
96 match duration_str::parse(self.string_value(name)?) {
97 Ok(value) => Some(value),
98
99 Err(error) => {
100 tracing::warn!("malformed duration: {}", error);
101 None
102 }
103 }
104 }
105
106 /// Parse a header value as an [HttpDate].
107 ///
108 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
109 fn date_value(&self, name: HeaderName) -> Option<HttpDate> {
110 self.parse_value(name)
111 }
112
113 // General set
114
115 /// Set a header value.
116 ///
117 /// Makes sure to remove existing values first.
118 fn set_value<ValueT>(&mut self, name: HeaderName, value: ValueT)
119 where
120 ValueT: Into<HeaderValue>;
121
122 /// Set a header value.
123 ///
124 /// Makes sure to remove existing values first.
125 fn set_into_header_value<ValueT>(&mut self, name: HeaderName, value: ValueT)
126 where
127 ValueT: IntoHeaderValue;
128
129 /// Set a header string value.
130 ///
131 /// Makes sure to remove existing values first.
132 ///
133 /// Will fail if not an ASCII string.
134 fn set_string_value(&mut self, name: HeaderName, value: &str) -> Result<(), InvalidHeaderValue>;
135
136 /// Set header string values.
137 ///
138 /// Invalid header names will be skipped.
139 ///
140 /// Makes sure to remove existing values first for each header.
141 ///
142 /// Will fail if a value is not an ASCII string.
143 fn set_string_values<IteratorT, NameT, ValueT>(
144 &mut self,
145 name_value_pairs: IteratorT,
146 ) -> Result<(), InvalidHeaderValue>
147 where
148 IteratorT: Iterator<Item = (NameT, ValueT)>,
149 NameT: AsRef<str>,
150 ValueT: AsRef<str>,
151 {
152 for (name, value) in name_value_pairs {
153 if let Ok(name) = HeaderName::from_lowercase(name.as_ref().to_lowercase().as_bytes()) {
154 self.set_string_value(name, value.as_ref())?;
155 }
156 }
157 Ok(())
158 }
159
160 /// Set a boolean header value ("true" or "false").
161 ///
162 /// Makes sure to remove existing values first.
163 fn set_bool_value(&mut self, name: HeaderName, value: bool) {
164 self.set_into_header_value(name, if value { TRUE_HEADER_VALUE } else { FALSE_HEADER_VALUE });
165 }
166
167 // Request and response headers
168
169 /// Parse the [`Content-Length`](CONTENT_LENGTH) header value.
170 ///
171 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
172 fn content_length(&self) -> Option<usize> {
173 self.parse_value(CONTENT_LENGTH)
174 }
175
176 /// Parse the [`Content-Type`](CONTENT_TYPE) header value.
177 ///
178 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
179 fn content_type(&self) -> Option<MediaType> {
180 self.parse_value(CONTENT_TYPE)
181 }
182
183 // Request headers
184
185 /// Parse, combine, and sort all [`Accept`](ACCEPT) request header values.
186 ///
187 /// Will skip over malformed values.
188 fn accept(&self) -> Preferences<MediaTypeSelector> {
189 self.preferences(ACCEPT)
190 }
191
192 /// Parse, combine, and sort all [`Accept-Encoding`](ACCEPT_ENCODING) request header values.
193 ///
194 /// Will skip over malformed values.
195 fn accept_encoding(&self) -> Preferences<EncodingHeaderValue> {
196 self.preferences(ACCEPT_ENCODING)
197 }
198
199 /// Parse, combine, and sort all [`Accept-Language`](ACCEPT_LANGUAGE) request header values.
200 ///
201 /// Note that we convert all subtags to lowercase for comparison efficiency.
202 ///
203 /// Will skip over malformed values.
204 fn accept_language(&self) -> Preferences<Language> {
205 self.preferences(ACCEPT_LANGUAGE)
206 }
207
208 /// Parse the [`If-Modified-Since`](IF_MODIFIED_SINCE) request header value.
209 ///
210 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
211 fn if_modified_since(&self) -> Option<HttpDate> {
212 self.date_value(IF_MODIFIED_SINCE)
213 }
214
215 /// Parse the [`If-None-Match`](IF_NONE_MATCH) request header value.
216 ///
217 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
218 fn if_none_match(&self) -> Option<ETagMatcher> {
219 self.parse_value(IF_NONE_MATCH)
220 }
221
222 /// Parse the [`If-Unmodified-Since`](IF_UNMODIFIED_SINCE) request header value.
223 ///
224 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
225 fn if_unmodified_since(&self) -> Option<HttpDate> {
226 self.date_value(IF_UNMODIFIED_SINCE)
227 }
228
229 /// Parse the [`If-Match`](IF_MATCH) request header value.
230 ///
231 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
232 fn if_match(&self) -> Option<ETagMatcher> {
233 self.parse_value(IF_MATCH)
234 }
235
236 /// Parse the [`Authorization`](AUTHORIZATION) request header value for the `Basic` scheme.
237 ///
238 /// Expects UTF-8 strings.
239 ///
240 /// Returns the username and password.
241 ///
242 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
243 fn authorization_basic(&self) -> Option<(String, String)> {
244 if let Some(authorization) = self.string_value(AUTHORIZATION) {
245 if authorization.starts_with("Basic ") {
246 let authorization = &authorization[6..];
247 if let Ok(authorization) = STANDARD.decode(authorization) {
248 if let Ok(authorization) = str::from_utf8(&authorization) {
249 if let Some((username, password)) = authorization.split_once(':') {
250 return Some((username.into(), password.into()));
251 }
252 }
253 }
254 }
255 }
256
257 None
258 }
259
260 // Response headers
261
262 /// Parse the [`Content-Encoding`](CONTENT_ENCODING) response header value.
263 ///
264 /// Defaults to [Identity](kutil_transcoding::Encoding::Identity) if there is no such header
265 /// *or* that is malformed.
266 fn content_encoding(&self) -> EncodingHeaderValue {
267 self.parse_value(CONTENT_ENCODING).unwrap_or_default()
268 }
269
270 /// Parse the [`Content-Language`](CONTENT_LANGUAGE) response header value.
271 ///
272 /// Note that we convert all subtags to lowercase for comparison efficiency.
273 ///
274 /// Despite the header name, there can be more than one language listed. We will skip over
275 /// duplicates.
276 fn content_language(&self) -> Option<FastHashSet<Language>> {
277 Language::parse_list(self.string_value(CONTENT_LANGUAGE)?)
278 }
279
280 /// Parse the [`Last-Modified`](LAST_MODIFIED) response header value.
281 ///
282 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
283 fn last_modified(&self) -> Option<HttpDate> {
284 self.date_value(LAST_MODIFIED)
285 }
286
287 /// Parse the [`ETag`](ETAG) response header value.
288 ///
289 /// [None](Option::None) could mean that there is no such header *or* that it is malformed.
290 fn etag(&self) -> Option<ETag> {
291 self.parse_value(ETAG)
292 }
293}
294
295impl HeaderValues for HeaderMap {
296 fn string_value(&self, name: HeaderName) -> Option<&str> {
297 match self.get(name)?.to_str() {
298 Ok(value) => Some(value),
299
300 Err(error) => {
301 tracing::warn!("value is not ASCII: {}", error);
302 None
303 }
304 }
305 }
306
307 fn string_values(&self, name: HeaderName) -> Vec<&str> {
308 self.get_all(name).iter().filter_map(|value| value.to_str().ok()).collect()
309 }
310
311 fn byte_string_value(&self, name: HeaderName) -> Option<ByteString> {
312 let bytes = Bytes::copy_from_slice(self.get(name)?.as_bytes());
313 match ByteString::try_from(bytes) {
314 Ok(value) => Some(value),
315
316 Err(error) => {
317 tracing::warn!("value is not ASCII: {}", error);
318 None
319 }
320 }
321 }
322
323 fn byte_string_values(&self, name: HeaderName) -> Vec<ByteString> {
324 self.get_all(name)
325 .iter()
326 .filter_map(|value| {
327 let bytes = Bytes::copy_from_slice(value.as_bytes());
328 ByteString::try_from(bytes).ok()
329 })
330 .collect()
331 }
332
333 fn set_value<ValueT>(&mut self, name: HeaderName, value: ValueT)
334 where
335 ValueT: Into<HeaderValue>,
336 {
337 self.remove(&name);
338 self.insert(name, value.into());
339 }
340
341 fn set_into_header_value<ValueT>(&mut self, name: HeaderName, value: ValueT)
342 where
343 ValueT: IntoHeaderValue,
344 {
345 self.remove(&name);
346 self.insert(name, value.into_header_value());
347 }
348
349 fn set_string_value(&mut self, name: HeaderName, value: &str) -> Result<(), InvalidHeaderValue> {
350 self.remove(&name);
351 self.insert(name, HeaderValue::from_str(value)?);
352 Ok(())
353 }
354}