use core::fmt;
use std::collections::HashMap;
use crate::ids::contains_control_char;
#[derive(Clone, Default, PartialEq, Eq)]
pub struct Headers(HashMap<String, String>);
impl fmt::Debug for Headers {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map()
.entries(self.0.keys().map(|k| (k, "<redacted>")))
.finish()
}
}
impl Headers {
pub const RESERVED_PREFIX: &'static str = "reliar-";
pub const MAX_KEY_LEN: usize = 128;
pub const MAX_VALUE_LEN: usize = 1024;
pub const MAX_COUNT: usize = 32;
pub fn insert(
&mut self,
key: impl Into<String>,
value: impl Into<String>,
) -> Result<Option<String>, HeaderError> {
let key = key.into();
let value = value.into();
if Self::has_reserved_prefix(&key) {
return Err(HeaderError::Reserved { key });
}
if key.is_empty() {
return Err(HeaderError::EmptyKey);
}
if contains_control_char(&key) {
return Err(HeaderError::ControlCharacterInKey { key });
}
if key.len() > Self::MAX_KEY_LEN {
return Err(HeaderError::KeyTooLong { len: key.len() });
}
if contains_control_char(&value) {
return Err(HeaderError::ControlCharacterInValue { key });
}
if value.len() > Self::MAX_VALUE_LEN {
return Err(HeaderError::ValueTooLong { len: value.len() });
}
if !self.0.contains_key(&key) && self.0.len() >= Self::MAX_COUNT {
return Err(HeaderError::TooManyHeaders {
limit: Self::MAX_COUNT,
});
}
Ok(self.0.insert(key, value))
}
#[must_use]
pub fn get(&self, key: &str) -> Option<&str> {
self.0.get(key).map(String::as_str)
}
pub fn remove(&mut self, key: &str) -> Option<String> {
self.0.remove(key)
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
self.0.iter().map(|(k, v)| (k.as_str(), v.as_str()))
}
fn has_reserved_prefix(key: &str) -> bool {
key.len() >= Self::RESERVED_PREFIX.len()
&& key.as_bytes()[..Self::RESERVED_PREFIX.len()]
.eq_ignore_ascii_case(Self::RESERVED_PREFIX.as_bytes())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum HeaderError {
Reserved {
key: String,
},
EmptyKey,
ControlCharacterInKey {
key: String,
},
ControlCharacterInValue {
key: String,
},
KeyTooLong {
len: usize,
},
ValueTooLong {
len: usize,
},
TooManyHeaders {
limit: usize,
},
}
impl fmt::Display for HeaderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Reserved { key } => {
write!(f, "header key {key:?} uses the reserved `reliar-` prefix")
}
Self::EmptyKey => f.write_str("header key must not be empty"),
Self::ControlCharacterInKey { key } => {
write!(f, "header key {key:?} contains a control character")
}
Self::ControlCharacterInValue { key } => {
write!(
f,
"header value for key {key:?} contains a control character"
)
}
Self::KeyTooLong { len } => write!(
f,
"header key length {len} exceeds the maximum of {}",
Headers::MAX_KEY_LEN
),
Self::ValueTooLong { len } => write!(
f,
"header value length {len} exceeds the maximum of {}",
Headers::MAX_VALUE_LEN
),
Self::TooManyHeaders { limit } => {
write!(f, "header count would exceed the maximum of {limit}")
}
}
}
}
impl std::error::Error for HeaderError {}