miku_http_util/request/
header.rs

1//! HTTP request utilities: HTTP header related.
2
3use std::convert::Infallible;
4
5use anyhow::{anyhow, Result};
6use http::{
7    header::{AsHeaderName, InvalidHeaderValue},
8    HeaderMap, HeaderName, HeaderValue,
9};
10use macro_toolset::{
11    b64_decode, b64_encode,
12    string::{base64::Base64EncoderT, StringExtT},
13    wrapper,
14};
15
16/// Trait helper for managing HTTP header keys.
17pub trait HeaderKeyT {
18    /// `as_str_ext` and most times should be &'static
19    fn as_str_ext(&self) -> &str;
20
21    /// Get the key name
22    fn to_header_name(self) -> HeaderName;
23
24    /// Get default value of the key
25    ///
26    /// Return `None` if no default one.
27    fn default_header_value(&self) -> Option<HeaderValue> {
28        None
29    }
30}
31
32/// Trait helper for managing HTTP header keys.
33///
34/// Marker trait for binary keys.
35pub trait HeaderAsciiKeyT: HeaderKeyT {}
36
37/// Trait helper for managing HTTP header keys.
38///
39/// Marker trait for binary keys.
40pub trait HeaderBinaryKeyT: HeaderKeyT {}
41
42impl HeaderKeyT for &'static str {
43    #[inline]
44    fn as_str_ext(&self) -> &str {
45        self
46    }
47
48    #[inline]
49    fn to_header_name(self) -> HeaderName {
50        HeaderName::from_static(self)
51    }
52}
53
54impl HeaderAsciiKeyT for &'static str {}
55
56impl HeaderKeyT for HeaderName {
57    #[inline]
58    fn as_str_ext(&self) -> &str {
59        self.as_str()
60    }
61
62    #[inline]
63    fn to_header_name(self) -> HeaderName {
64        self
65    }
66}
67
68impl HeaderAsciiKeyT for HeaderName {}
69
70wrapper! {
71    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
72    /// Wrapper for binary key, though you have to make sure the key is valid (with `-bin` suffix).
73    ///
74    /// # Panics
75    ///
76    /// Panics if the key is not valid (with `-bin` suffix) when debug mode.
77    pub BinaryKeyWrapper<T>(pub T)
78}
79
80impl<T: HeaderKeyT> HeaderKeyT for BinaryKeyWrapper<T> {
81    #[inline]
82    fn as_str_ext(&self) -> &str {
83        self.inner.as_str_ext()
84    }
85
86    #[inline]
87    fn to_header_name(self) -> HeaderName {
88        debug_assert!(self.as_str_ext().ends_with("-bin"));
89
90        self.inner.to_header_name()
91    }
92
93    #[inline]
94    fn default_header_value(&self) -> Option<HeaderValue> {
95        debug_assert!(self.as_str_ext().ends_with("-bin"));
96
97        self.inner.default_header_value()
98    }
99}
100
101impl<T: HeaderKeyT> HeaderBinaryKeyT for BinaryKeyWrapper<T> {}
102
103/// Trait for extending [`http::HeaderMap`]'s methods.
104///
105/// If `T` implements this trait, `&mut T` will also implement this trait.
106pub trait HeaderMapExtT {
107    #[inline]
108    /// Returns a reference to the value associated with the key.
109    ///
110    /// For gRPC Metadata, please use [`get_bin`](HeaderMapExtT::get_bin)
111    /// instead.
112    ///
113    /// Notice: if value contains invalid header value characters(non-ascii), it
114    /// will be ignored and return `None`.
115    fn get_ascii<K>(&self, key: K) -> Option<&str>
116    where
117        K: HeaderAsciiKeyT,
118    {
119        self.get_maybe_ascii(key)
120    }
121
122    #[doc(hidden)]
123    #[inline]
124    /// See [`get_ascii`](HeaderMapExtT::get_ascii) for more details.
125    fn get_maybe_ascii<K>(&self, key: K) -> Option<&str>
126    where
127        K: HeaderKeyT,
128    {
129        self.get_exact(key.to_header_name()).and_then(|v| {
130            v.to_str()
131                .inspect_err(|_e| {
132                    #[cfg(feature = "feat-tracing")]
133                    tracing::warn!("Invalid header value [{v:?}]: {_e:?}");
134                })
135                .ok()
136        })
137    }
138
139    #[inline]
140    /// Returns the decoded base64-encoded value associated with the key, if the
141    /// key-value pair exists.
142    ///
143    /// # Errors
144    ///
145    /// - Invalid Base64 string.
146    fn get_bin<K>(&self, key: K) -> Result<Option<Vec<u8>>>
147    where
148        K: HeaderBinaryKeyT,
149    {
150        if let Some(b64_str) = self.get_maybe_ascii(key) {
151            let decoded_bytes = b64_decode!(STANDARD_NO_PAD: b64_str)
152                .map_err(|e| anyhow!(e).context(b64_str.to_string()))?;
153            Ok(Some(decoded_bytes))
154        } else {
155            Ok(None)
156        }
157    }
158
159    #[inline]
160    /// Extend the given buffer with the decoded base64-encoded value associated
161    /// with the key, if the key-value pair exists.
162    ///
163    /// # Errors
164    ///
165    /// - Invalid Base64 string.
166    fn get_bin_to_buffer<K>(&self, key: K, buffer: &mut Vec<u8>) -> Result<()>
167    where
168        K: HeaderBinaryKeyT,
169    {
170        if let Some(b64_str) = self.get_maybe_ascii(key) {
171            b64_decode!(STANDARD_NO_PAD: b64_str, buffer)?;
172        }
173
174        Ok(())
175    }
176
177    #[inline]
178    /// Returns the struct decoded from the gRPC metadata binary value, if the
179    /// key-value pair exists.
180    ///
181    /// # Errors
182    ///
183    /// - [`prost::DecodeError`].
184    /// - Invalid Base64 string.
185    fn get_bin_struct<K, T>(&self, key: K) -> Result<Option<T>>
186    where
187        K: HeaderBinaryKeyT,
188        T: prost::Message + Default,
189    {
190        if let Some(bin) = self.get_bin(key)? {
191            Ok(Some(T::decode(bin.as_slice())?))
192        } else {
193            Ok(None)
194        }
195    }
196
197    #[inline]
198    /// Returns the struct decoded from the gRPC metadata binary value, or a
199    /// default one if the key-value pair does not exist.
200    ///
201    /// # Errors
202    ///
203    /// - [`prost::DecodeError`].
204    /// - Invalid Base64 string.
205    fn get_bin_struct_or_default<K, T>(&self, key: K) -> Result<T>
206    where
207        K: HeaderBinaryKeyT,
208        T: prost::Message + Default,
209    {
210        if let Some(bin) = self.get_bin(key)? {
211            Ok(T::decode(bin.as_slice())?)
212        } else {
213            Ok(T::default())
214        }
215    }
216
217    /// Inserts a key-value pair into the inner [`HeaderMap`].
218    ///
219    /// For gRPC Metadata, please use
220    /// [`insert_bin`](HeaderMapExtT::insert_bin) instead.
221    ///
222    /// # Errors
223    ///
224    /// - [`InvalidHeaderValue`] if the value contains invalid header value
225    ///   characters.
226    #[inline]
227    fn insert_ascii<K, V>(&mut self, key: K, value: V) -> Result<&mut Self, InvalidHeaderValue>
228    where
229        K: HeaderAsciiKeyT,
230        V: TryInto<HeaderValue, Error = InvalidHeaderValue>,
231    {
232        self.insert_maybe_ascii(key, value)
233    }
234
235    #[doc(hidden)]
236    /// See [`insert_ascii`](HeaderMapExtT::insert_ascii).
237    #[inline]
238    fn insert_maybe_ascii<K, V>(
239        &mut self,
240        key: K,
241        value: V,
242    ) -> Result<&mut Self, InvalidHeaderValue>
243    where
244        K: HeaderKeyT,
245        V: TryInto<HeaderValue, Error = InvalidHeaderValue>,
246    {
247        self.insert_exact(key.to_header_name(), value.try_into()?);
248        Ok(self)
249    }
250
251    /// Inserts a key-value pair into the inner [`HeaderMap`].
252    ///
253    /// `value` can be any type that implements [`StringExtT`].
254    ///
255    /// For gRPC Metadata, please use
256    /// [`insert_bin`](HeaderMapExtT::insert_bin) instead.
257    ///
258    /// # Errors
259    ///
260    /// - [`InvalidHeaderValue`] if the value contains invalid header value
261    ///   characters.
262    #[inline]
263    fn insert_ascii_any<K, V>(&mut self, key: K, value: V) -> Result<&mut Self, InvalidHeaderValue>
264    where
265        K: HeaderAsciiKeyT,
266        V: StringExtT,
267    {
268        self.insert_exact(key.to_header_name(), value.to_http_header_value()?);
269        Ok(self)
270    }
271
272    /// Inserts a key-value pair into the inner [`HeaderMap`].
273    ///
274    /// For gRPC Metadata, please use
275    /// [`insert_bin`](HeaderMapExtT::insert_bin) instead.
276    #[inline]
277    fn insert_ascii_infallible<K, V>(&mut self, key: K, value: V) -> &mut Self
278    where
279        K: HeaderAsciiKeyT,
280        V: TryInto<HeaderValue, Error = Infallible>,
281    {
282        self.insert_exact(key.to_header_name(), value.try_into().unwrap());
283        self
284    }
285
286    /// Inserts a key-value pair into the inner [`HeaderMap`].
287    ///
288    /// For gRPC Metadata, please use
289    /// [`insert_bin`](HeaderMapExtT::insert_bin) instead.
290    #[inline]
291    fn insert_ascii_static<K>(&mut self, key: K, value: &'static str) -> &mut Self
292    where
293        K: HeaderAsciiKeyT,
294    {
295        self.insert_exact(key.to_header_name(), HeaderValue::from_static(value));
296        self
297    }
298
299    /// Inserts a key-value pair into the inner [`HeaderMap`].
300    ///
301    /// `value` should be base64 string.
302    ///
303    /// # Panics
304    ///
305    /// Panic if the value is not a valid header value (for base64 string, it's
306    /// not possible).
307    #[inline]
308    fn insert_bin<K, V>(&mut self, key: K, value: V) -> &mut Self
309    where
310        K: HeaderBinaryKeyT,
311        V: TryInto<HeaderValue, Error = InvalidHeaderValue>,
312    {
313        self.insert_maybe_ascii(key, value)
314            .expect("Base64 string should be valid header value")
315    }
316
317    /// Inserts a key-value pair into the inner [`HeaderMap`].
318    ///
319    /// `value` can be any type that implement [`Base64EncoderT`].
320    /// See [`b64_padding::STANDARD_NO_PAD::encode`]\(data\), etc for more
321    /// details.
322    ///
323    /// # Panics
324    ///
325    /// Panic if the value is not a valid header value (it's not possible unless
326    /// upstream bug).
327    #[inline]
328    fn insert_bin_any<K, V>(&mut self, key: K, value: V) -> &mut Self
329    where
330        K: HeaderBinaryKeyT,
331        V: Base64EncoderT,
332    {
333        self.insert_exact(
334            key.to_header_name(),
335            value
336                .to_http_header_value()
337                .expect("Base64 string should be valid header value"),
338        )
339    }
340
341    /// Inserts a key-value pair into the inner [`HeaderMap`].
342    ///
343    /// `value` can be any type that implement [`AsRef`]<[u8]>.
344    ///
345    /// # Panics
346    ///
347    /// Panic if the value is not a valid header value (it's not possible unless
348    /// upstream bug).
349    #[inline]
350    fn insert_bin_byte<K, V>(&mut self, key: K, value: V) -> &mut Self
351    where
352        K: HeaderBinaryKeyT,
353        V: AsRef<[u8]>,
354    {
355        // SAFE: Base64 encoded data value must be valid http header value
356        // Here we avoid copy_from_slice since we own the data
357        let value = HeaderValue::from_maybe_shared(
358            b64_encode!(STANDARD_NO_PAD: value.as_ref() => BYTES).freeze(),
359        )
360        .expect("Base64 string should be valid header value");
361        self.insert_exact(key.to_header_name(), value);
362
363        self
364    }
365
366    /// Inserts a key-value pair into the inner [`HeaderMap`].
367    ///
368    /// `value` can be any type that implement [`AsRef`]<[u8]>.
369    ///
370    /// # Errors
371    ///
372    /// - [`prost::EncodeError`]
373    ///
374    /// # Panics
375    ///
376    /// Panic if the value is not a valid header value (it's not possible unless
377    /// upstream bug).
378    #[inline]
379    fn insert_bin_struct<K, V>(&mut self, key: K, value: V) -> Result<&mut Self, prost::EncodeError>
380    where
381        K: HeaderBinaryKeyT,
382        V: prost::Message + Default,
383    {
384        let mut buf = Vec::with_capacity(64);
385        value.encode(&mut buf)?;
386
387        // SAFE: Base64 encoded data value must be valid http header value
388        // Here we avoid copy_from_slice since we own the data
389        let value =
390            HeaderValue::from_maybe_shared(b64_encode!(STANDARD_NO_PAD: buf => BYTES).freeze())
391                .expect("Base64 string should be valid header value");
392        self.insert_exact(key.to_header_name(), value);
393
394        Ok(self)
395    }
396
397    /// Inserts a key-value pair into the inner [`HeaderMap`].
398    ///
399    /// Caller must ensure the value is valid base64 string.
400    #[inline]
401    fn insert_bin_static<K>(&mut self, key: K, value: &'static str) -> &mut Self
402    where
403        K: HeaderBinaryKeyT,
404    {
405        self.insert_exact(key.to_header_name(), HeaderValue::from_static(value));
406        self
407    }
408
409    /// Insert default value of `T` that implement [`HeaderKeyT`]
410    ///
411    /// It's a no-op if there's no default value.
412    #[inline]
413    fn insert_default(&mut self, key: impl HeaderKeyT) -> &mut Self {
414        if let Some(v) = key.default_header_value() {
415            self.insert_exact(key.to_header_name(), v);
416        }
417        self
418    }
419
420    /// Check if key exist, just a bridge to [`HeaderMap`] or any else
421    fn contains_headerkey(&self, key: impl HeaderKeyT) -> bool;
422
423    /// Get value with exact type, just a bridge to [`HeaderMap`] or any else
424    ///
425    /// Accept any key type that can be converted to [`HeaderName`], see
426    /// [`AsHeaderName`]. It can be [`HeaderName`], &'a [`HeaderName`], &'a
427    /// [str] or [String].
428    fn get_exact<K>(&self, key: K) -> Option<&HeaderValue>
429    where
430        K: AsHeaderName;
431
432    /// Insert value with exact type, just a bridge to [`HeaderMap`] or any else
433    fn insert_exact(&mut self, key: HeaderName, value: HeaderValue) -> &mut Self;
434}
435
436// auto impl for `&mut T`
437impl<T> HeaderMapExtT for &mut T
438where
439    T: HeaderMapExtT,
440{
441    #[inline]
442    fn contains_headerkey(&self, key: impl HeaderKeyT) -> bool {
443        (**self).contains_headerkey(key)
444    }
445
446    #[inline]
447    fn get_exact<K>(&self, key: K) -> Option<&HeaderValue>
448    where
449        K: AsHeaderName,
450    {
451        (**self).get_exact(key)
452    }
453
454    #[inline]
455    fn insert_exact(&mut self, key: HeaderName, value: HeaderValue) -> &mut Self {
456        (**self).insert_exact(key, value);
457        self
458    }
459}
460
461impl HeaderMapExtT for HeaderMap {
462    #[inline]
463    fn contains_headerkey(&self, key: impl HeaderKeyT) -> bool {
464        self.contains_key(key.to_header_name())
465    }
466
467    #[inline]
468    fn get_exact<K>(&self, key: K) -> Option<&HeaderValue>
469    where
470        K: AsHeaderName,
471    {
472        self.get(key)
473    }
474
475    #[inline]
476    fn insert_exact(&mut self, key: HeaderName, value: HeaderValue) -> &mut Self {
477        self.insert(key, value);
478        self
479    }
480}