mod header_name;
mod header_value;
mod header_values;
mod known_header_name;
mod unknown_header_name;
pub use header_name::HeaderName;
pub use header_value::HeaderValue;
pub use header_values::HeaderValues;
pub use known_header_name::KnownHeaderName;
use header_name::HeaderNameInner;
use unknown_header_name::UnknownHeaderName;
use hashbrown::{
hash_map::{self, Entry},
HashMap,
};
use smartcow::SmartCow;
use std::{
fmt::{self, Debug, Display, Formatter},
hash::{BuildHasherDefault, Hasher},
iter::FromIterator,
};
#[derive(Debug, Clone, PartialEq, Eq)]
#[must_use]
pub struct Headers {
known: HashMap<KnownHeaderName, HeaderValues, BuildHasherDefault<DirectHasher>>,
unknown: HashMap<UnknownHeaderName<'static>, HeaderValues>,
}
impl Default for Headers {
fn default() -> Self {
Self::with_capacity(15)
}
}
impl Display for Headers {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for (n, v) in self {
for v in v {
f.write_fmt(format_args!("{n}: {v}\r\n"))?;
}
}
Ok(())
}
}
impl Headers {
pub fn with_capacity(capacity: usize) -> Self {
Self {
known: HashMap::with_capacity_and_hasher(capacity, BuildHasherDefault::default()),
unknown: HashMap::with_capacity(0),
}
}
pub fn new() -> Self {
Self::default()
}
pub fn reserve(&mut self, additional: usize) {
self.known.reserve(additional);
}
pub fn iter(&self) -> Iter<'_> {
self.into()
}
pub fn is_empty(&self) -> bool {
self.known.is_empty() && self.unknown.is_empty()
}
pub fn len(&self) -> usize {
self.known.len() + self.unknown.len()
}
pub fn append(&mut self, name: impl Into<HeaderName<'static>>, value: impl Into<HeaderValues>) {
let value = value.into();
match name.into().0 {
HeaderNameInner::KnownHeader(known) => match self.known.entry(known) {
Entry::Occupied(mut o) => {
o.get_mut().extend(value);
}
Entry::Vacant(v) => {
v.insert(value);
}
},
HeaderNameInner::UnknownHeader(unknown) => match self.unknown.entry(unknown) {
Entry::Occupied(mut o) => {
o.get_mut().extend(value);
}
Entry::Vacant(v) => {
v.insert(value);
}
},
}
}
pub fn append_all(&mut self, other: Headers) {
self.known.reserve(other.known.len());
for (name, value) in other.known {
match self.known.entry(name) {
Entry::Occupied(mut entry) => {
entry.get_mut().extend(value);
}
Entry::Vacant(entry) => {
entry.insert(value);
}
}
}
for (name, value) in other.unknown {
match self.unknown.entry(name) {
Entry::Occupied(mut entry) => {
entry.get_mut().extend(value);
}
Entry::Vacant(entry) => {
entry.insert(value);
}
}
}
}
pub fn insert_all(&mut self, other: Headers) {
self.known.reserve(other.known.len());
for (name, value) in other.known {
self.known.insert(name, value);
}
for (name, value) in other.unknown {
self.unknown.insert(name, value);
}
}
pub fn insert(&mut self, name: impl Into<HeaderName<'static>>, value: impl Into<HeaderValues>) {
let value = value.into();
match name.into().0 {
HeaderNameInner::KnownHeader(known) => {
self.known.insert(known, value);
}
HeaderNameInner::UnknownHeader(unknown) => {
self.unknown.insert(unknown, value);
}
}
}
pub fn try_insert(
&mut self,
name: impl Into<HeaderName<'static>>,
value: impl Into<HeaderValues>,
) {
let value = value.into();
match name.into().0 {
HeaderNameInner::KnownHeader(known) => {
self.known.entry(known).or_insert(value);
}
HeaderNameInner::UnknownHeader(unknown) => {
self.unknown.entry(unknown).or_insert(value);
}
}
}
pub fn get_str<'a>(&self, name: impl Into<HeaderName<'a>>) -> Option<&str> {
self.get_values(name).and_then(HeaderValues::as_str)
}
pub(crate) fn get_lower<'a>(&self, name: impl Into<HeaderName<'a>>) -> Option<SmartCow<'_>> {
self.get_values(name).and_then(HeaderValues::as_lower)
}
pub fn get<'a>(&self, name: impl Into<HeaderName<'a>>) -> Option<&HeaderValue> {
self.get_values(name).and_then(HeaderValues::one)
}
pub fn remove<'a>(&mut self, name: impl Into<HeaderName<'a>>) -> Option<HeaderValues> {
match name.into().0 {
HeaderNameInner::KnownHeader(known) => self.known.remove(&known),
HeaderNameInner::UnknownHeader(unknown) => self.unknown.remove(&&unknown),
}
}
pub fn get_values<'a>(&self, name: impl Into<HeaderName<'a>>) -> Option<&HeaderValues> {
match name.into().0 {
HeaderNameInner::KnownHeader(known) => self.known.get(&known),
HeaderNameInner::UnknownHeader(unknown) => self.unknown.get(&&unknown),
}
}
pub fn has_header<'a>(&self, name: impl Into<HeaderName<'a>>) -> bool {
match name.into().0 {
HeaderNameInner::KnownHeader(known) => self.known.contains_key(&known),
HeaderNameInner::UnknownHeader(unknown) => self.unknown.contains_key(&unknown),
}
}
pub fn eq_ignore_ascii_case<'a>(
&'a self,
name: impl Into<HeaderName<'a>>,
needle: &str,
) -> bool {
self.get_str(name).map(|v| v == needle).unwrap_or_default()
}
pub fn contains_ignore_ascii_case<'a>(
&self,
name: impl Into<HeaderName<'a>>,
needle: &str,
) -> bool {
self.get_str(name)
.map(|h| {
let needle = if needle.chars().all(|c| c.is_ascii_lowercase()) {
SmartCow::Borrowed(needle)
} else {
SmartCow::Owned(needle.chars().map(|c| c.to_ascii_lowercase()).collect())
};
if h.chars().all(|c| c.is_ascii_lowercase()) {
h.contains(&*needle)
} else {
h.to_ascii_lowercase().contains(&*needle)
}
})
.unwrap_or_default()
}
pub fn with_inserted_header(
mut self,
name: impl Into<HeaderName<'static>>,
values: impl Into<HeaderValues>,
) -> Self {
self.insert(name, values);
self
}
pub fn with_appended_header(
mut self,
name: impl Into<HeaderName<'static>>,
values: impl Into<HeaderValues>,
) -> Self {
self.append(name, values);
self
}
pub fn without_header(mut self, name: impl Into<HeaderName<'static>>) -> Self {
self.remove(name);
self
}
pub fn without_headers<I, H>(mut self, names: I) -> Self
where
I: IntoIterator<Item = H>,
H: Into<HeaderName<'static>>,
{
for header in names {
self.remove(header.into());
}
self
}
}
impl<HN, HV> Extend<(HN, HV)> for Headers
where
HN: Into<HeaderName<'static>>,
HV: Into<HeaderValues>,
{
fn extend<T: IntoIterator<Item = (HN, HV)>>(&mut self, iter: T) {
let iter = iter.into_iter();
match iter.size_hint() {
(additional, _) if additional > 0 => self.known.reserve(additional),
_ => {}
};
for (name, values) in iter {
self.append(name, values);
}
}
}
impl<HN, HV> FromIterator<(HN, HV)> for Headers
where
HN: Into<HeaderName<'static>>,
HV: Into<HeaderValues>,
{
fn from_iter<T: IntoIterator<Item = (HN, HV)>>(iter: T) -> Self {
let iter = iter.into_iter();
let mut headers = match iter.size_hint() {
(0, _) => Self::new(),
(n, _) => Self::with_capacity(n),
};
for (name, values) in iter {
headers.append(name, values);
}
headers
}
}
#[derive(Default)]
struct DirectHasher(u8);
impl Hasher for DirectHasher {
fn write(&mut self, _: &[u8]) {
unreachable!("KnownHeaderName calls write_u64");
}
#[inline]
fn write_u8(&mut self, i: u8) {
self.0 = i;
}
#[inline]
fn finish(&self) -> u64 {
u64::from(self.0)
}
}
impl<'a> IntoIterator for &'a Headers {
type Item = (HeaderName<'a>, &'a HeaderValues);
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.into()
}
}
#[derive(Debug)]
pub struct IntoIter {
known: hash_map::IntoIter<KnownHeaderName, HeaderValues>,
unknown: hash_map::IntoIter<UnknownHeaderName<'static>, HeaderValues>,
}
impl Iterator for IntoIter {
type Item = (HeaderName<'static>, HeaderValues);
fn next(&mut self) -> Option<Self::Item> {
let IntoIter { known, unknown } = self;
known
.next()
.map(|(k, v)| (HeaderName::from(k), v))
.or_else(|| unknown.next().map(|(k, v)| (HeaderName::from(k), v)))
}
}
impl From<Headers> for IntoIter {
fn from(value: Headers) -> Self {
Self {
known: value.known.into_iter(),
unknown: value.unknown.into_iter(),
}
}
}
#[derive(Debug)]
pub struct Iter<'a> {
known: hash_map::Iter<'a, KnownHeaderName, HeaderValues>,
unknown: hash_map::Iter<'a, UnknownHeaderName<'static>, HeaderValues>,
}
impl<'a> From<&'a Headers> for Iter<'a> {
fn from(value: &'a Headers) -> Self {
Iter {
known: value.known.iter(),
unknown: value.unknown.iter(),
}
}
}
impl<'a> Iterator for Iter<'a> {
type Item = (HeaderName<'a>, &'a HeaderValues);
fn next(&mut self) -> Option<Self::Item> {
let Iter { known, unknown } = self;
known
.next()
.map(|(k, v)| (HeaderName::from(*k), v))
.or_else(|| unknown.next().map(|(k, v)| (HeaderName::from(&**k), v)))
}
}
impl IntoIterator for Headers {
type Item = (HeaderName<'static>, HeaderValues);
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.into()
}
}
#[cfg(test)]
mod tests {
use super::Headers;
#[test]
fn header_names_are_case_insensitive_for_access_but_retain_initial_case_in_headers() {
let mut headers = Headers::new();
headers.insert("my-Header-name", "initial-value");
headers.insert("my-Header-NAME", "my-header-value");
assert_eq!(headers.len(), 1);
assert_eq!(
headers.get_str("My-Header-Name").unwrap(),
"my-header-value"
);
headers.append("mY-hEaDer-NaMe", "second-value");
assert_eq!(
&*headers.get_values("my-header-name").unwrap(),
["my-header-value", "second-value"].as_slice()
);
assert_eq!(
headers.iter().next().unwrap().0.to_string(),
"my-Header-name"
);
assert!(headers.remove("my-HEADER-name").is_some());
assert!(headers.is_empty());
}
}