use std::convert::Infallible;
use anyhow::{anyhow, Result};
use http::{
header::{AsHeaderName, InvalidHeaderValue},
HeaderMap, HeaderName, HeaderValue,
};
use macro_toolset::{
b64_decode, b64_encode,
string::{base64::Base64EncoderT, StringExtT},
wrapper,
};
pub trait HeaderKeyT {
fn as_str_ext(&self) -> &str;
fn to_header_name(self) -> HeaderName;
fn default_header_value(&self) -> Option<HeaderValue> {
None
}
}
pub trait HeaderAsciiKeyT: HeaderKeyT {}
pub trait HeaderBinaryKeyT: HeaderKeyT {}
impl HeaderKeyT for &'static str {
#[inline]
fn as_str_ext(&self) -> &str {
self
}
#[inline]
fn to_header_name(self) -> HeaderName {
HeaderName::from_static(self)
}
}
impl HeaderAsciiKeyT for &'static str {}
impl HeaderKeyT for HeaderName {
#[inline]
fn as_str_ext(&self) -> &str {
self.as_str()
}
#[inline]
fn to_header_name(self) -> HeaderName {
self
}
}
impl HeaderAsciiKeyT for HeaderName {}
wrapper! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub BinaryKeyWrapper<T>(pub T)
}
impl<T: HeaderKeyT> HeaderKeyT for BinaryKeyWrapper<T> {
#[inline]
fn as_str_ext(&self) -> &str {
self.inner.as_str_ext()
}
#[inline]
fn to_header_name(self) -> HeaderName {
debug_assert!(self.as_str_ext().ends_with("-bin"));
self.inner.to_header_name()
}
#[inline]
fn default_header_value(&self) -> Option<HeaderValue> {
debug_assert!(self.as_str_ext().ends_with("-bin"));
self.inner.default_header_value()
}
}
impl<T: HeaderKeyT> HeaderBinaryKeyT for BinaryKeyWrapper<T> {}
pub trait HeaderMapExtT {
#[inline]
fn get_ascii<K>(&self, key: K) -> Option<&str>
where
K: HeaderAsciiKeyT,
{
self.get_maybe_ascii(key)
}
#[doc(hidden)]
#[inline]
fn get_maybe_ascii<K>(&self, key: K) -> Option<&str>
where
K: HeaderKeyT,
{
self.get_exact(key.to_header_name()).and_then(|v| {
v.to_str()
.inspect_err(|_e| {
#[cfg(feature = "feat-tracing")]
tracing::warn!("Invalid header value [{v:?}]: {_e:?}");
})
.ok()
})
}
#[inline]
fn get_bin<K>(&self, key: K) -> Result<Option<Vec<u8>>>
where
K: HeaderBinaryKeyT,
{
if let Some(b64_str) = self.get_maybe_ascii(key) {
let decoded_bytes = b64_decode!(STANDARD_NO_PAD: b64_str)
.map_err(|e| anyhow!(e).context(b64_str.to_string()))?;
Ok(Some(decoded_bytes))
} else {
Ok(None)
}
}
#[inline]
fn get_bin_to_buffer<K>(&self, key: K, buffer: &mut Vec<u8>) -> Result<()>
where
K: HeaderBinaryKeyT,
{
if let Some(b64_str) = self.get_maybe_ascii(key) {
b64_decode!(STANDARD_NO_PAD: b64_str, buffer)?;
}
Ok(())
}
#[inline]
fn get_bin_struct<K, T>(&self, key: K) -> Result<Option<T>>
where
K: HeaderBinaryKeyT,
T: prost::Message + Default,
{
if let Some(bin) = self.get_bin(key)? {
Ok(Some(T::decode(bin.as_slice())?))
} else {
Ok(None)
}
}
#[inline]
fn get_bin_struct_or_default<K, T>(&self, key: K) -> Result<T>
where
K: HeaderBinaryKeyT,
T: prost::Message + Default,
{
if let Some(bin) = self.get_bin(key)? {
Ok(T::decode(bin.as_slice())?)
} else {
Ok(T::default())
}
}
#[inline]
fn insert_ascii<K, V>(&mut self, key: K, value: V) -> Result<&mut Self, InvalidHeaderValue>
where
K: HeaderAsciiKeyT,
V: TryInto<HeaderValue, Error = InvalidHeaderValue>,
{
self.insert_maybe_ascii(key, value)
}
#[doc(hidden)]
#[inline]
fn insert_maybe_ascii<K, V>(
&mut self,
key: K,
value: V,
) -> Result<&mut Self, InvalidHeaderValue>
where
K: HeaderKeyT,
V: TryInto<HeaderValue, Error = InvalidHeaderValue>,
{
self.insert_exact(key.to_header_name(), value.try_into()?);
Ok(self)
}
#[inline]
fn insert_ascii_any<K, V>(&mut self, key: K, value: V) -> Result<&mut Self, InvalidHeaderValue>
where
K: HeaderAsciiKeyT,
V: StringExtT,
{
self.insert_exact(key.to_header_name(), value.to_http_header_value()?);
Ok(self)
}
#[inline]
fn insert_ascii_infallible<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: HeaderAsciiKeyT,
V: TryInto<HeaderValue, Error = Infallible>,
{
self.insert_exact(key.to_header_name(), value.try_into().unwrap());
self
}
#[inline]
fn insert_ascii_static<K>(&mut self, key: K, value: &'static str) -> &mut Self
where
K: HeaderAsciiKeyT,
{
self.insert_exact(key.to_header_name(), HeaderValue::from_static(value));
self
}
#[inline]
fn insert_bin<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: HeaderBinaryKeyT,
V: TryInto<HeaderValue, Error = InvalidHeaderValue>,
{
self.insert_maybe_ascii(key, value)
.expect("Base64 string should be valid header value")
}
#[inline]
fn insert_bin_any<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: HeaderBinaryKeyT,
V: Base64EncoderT,
{
self.insert_exact(
key.to_header_name(),
value
.to_http_header_value()
.expect("Base64 string should be valid header value"),
)
}
#[inline]
fn insert_bin_byte<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: HeaderBinaryKeyT,
V: AsRef<[u8]>,
{
let value = HeaderValue::from_maybe_shared(
b64_encode!(STANDARD_NO_PAD: value.as_ref() => BYTES).freeze(),
)
.expect("Base64 string should be valid header value");
self.insert_exact(key.to_header_name(), value);
self
}
#[inline]
fn insert_bin_struct<K, V>(&mut self, key: K, value: V) -> Result<&mut Self, prost::EncodeError>
where
K: HeaderBinaryKeyT,
V: prost::Message + Default,
{
let mut buf = Vec::with_capacity(64);
value.encode(&mut buf)?;
let value =
HeaderValue::from_maybe_shared(b64_encode!(STANDARD_NO_PAD: buf => BYTES).freeze())
.expect("Base64 string should be valid header value");
self.insert_exact(key.to_header_name(), value);
Ok(self)
}
#[inline]
fn insert_bin_static<K>(&mut self, key: K, value: &'static str) -> &mut Self
where
K: HeaderBinaryKeyT,
{
self.insert_exact(key.to_header_name(), HeaderValue::from_static(value));
self
}
#[inline]
fn insert_default(&mut self, key: impl HeaderKeyT) -> &mut Self {
if let Some(v) = key.default_header_value() {
self.insert_exact(key.to_header_name(), v);
}
self
}
fn contains_headerkey(&self, key: impl HeaderKeyT) -> bool;
fn get_exact<K>(&self, key: K) -> Option<&HeaderValue>
where
K: AsHeaderName;
fn insert_exact(&mut self, key: HeaderName, value: HeaderValue) -> &mut Self;
}
impl<T> HeaderMapExtT for &mut T
where
T: HeaderMapExtT,
{
#[inline]
fn contains_headerkey(&self, key: impl HeaderKeyT) -> bool {
(**self).contains_headerkey(key)
}
#[inline]
fn get_exact<K>(&self, key: K) -> Option<&HeaderValue>
where
K: AsHeaderName,
{
(**self).get_exact(key)
}
#[inline]
fn insert_exact(&mut self, key: HeaderName, value: HeaderValue) -> &mut Self {
(**self).insert_exact(key, value);
self
}
}
impl HeaderMapExtT for HeaderMap {
#[inline]
fn contains_headerkey(&self, key: impl HeaderKeyT) -> bool {
self.contains_key(key.to_header_name())
}
#[inline]
fn get_exact<K>(&self, key: K) -> Option<&HeaderValue>
where
K: AsHeaderName,
{
self.get(key)
}
#[inline]
fn insert_exact(&mut self, key: HeaderName, value: HeaderValue) -> &mut Self {
self.insert(key, value);
self
}
}