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}