use std::borrow::{Borrow, Cow};
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::convert::{TryFrom, TryInto};
use std::error::Error;
use std::fmt;
use std::fmt::Debug;
use std::ops::Deref;
use std::str;
use std::str::{FromStr, Utf8Error};
#[derive(PartialEq, Eq, Debug, Clone, Hash, Default)]
pub struct Headers(BTreeMap<HeaderName, HeaderValue>);
impl Headers {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn append(&mut self, name: HeaderName, value: HeaderValue) {
match self.0.entry(name) {
Entry::Occupied(e) => {
let val = &mut e.into_mut().0;
val.extend_from_slice(b", ");
val.extend_from_slice(&value.0);
}
Entry::Vacant(e) => {
e.insert(value);
}
}
}
#[inline]
pub fn remove(&mut self, name: &HeaderName) {
self.0.remove(name);
}
#[inline]
pub fn get(&self, name: &HeaderName) -> Option<&HeaderValue> {
self.0.get(name)
}
#[inline]
pub fn contains(&self, name: &HeaderName) -> bool {
self.0.contains_key(name)
}
#[inline]
pub fn set(&mut self, name: HeaderName, value: HeaderValue) {
self.0.insert(name, value);
}
#[inline]
pub fn iter(&self) -> Iter<'_> {
Iter(self.0.iter())
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl IntoIterator for Headers {
type Item = (HeaderName, HeaderValue);
type IntoIter = IntoIter;
#[inline]
fn into_iter(self) -> IntoIter {
IntoIter(self.0.into_iter())
}
}
impl<'a> IntoIterator for &'a Headers {
type Item = (&'a HeaderName, &'a HeaderValue);
type IntoIter = Iter<'a>;
#[inline]
fn into_iter(self) -> Iter<'a> {
self.iter()
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Hash)]
pub struct HeaderName(Cow<'static, str>);
impl HeaderName {
#[inline]
pub(crate) fn new_unchecked(name: impl Into<Cow<'static, str>>) -> Self {
Self(name.into())
}
pub const ACCEPT: Self = Self(Cow::Borrowed("accept"));
pub const ACCEPT_ENCODING: Self = Self(Cow::Borrowed("accept-encoding"));
pub const ACCEPT_LANGUAGE: Self = Self(Cow::Borrowed("accept-language"));
pub const ACCEPT_RANGES: Self = Self(Cow::Borrowed("accept-ranges"));
pub const ALLOW: Self = Self(Cow::Borrowed("allow"));
pub const AUTHENTICATION_INFO: Self = Self(Cow::Borrowed("authentication-info"));
pub const AUTHORIZATION: Self = Self(Cow::Borrowed("authorization"));
pub const CONNECTION: Self = Self(Cow::Borrowed("connection"));
pub const CONTENT_ENCODING: Self = Self(Cow::Borrowed("content-encoding"));
pub const CONTENT_LANGUAGE: Self = Self(Cow::Borrowed("content-language"));
pub const CONTENT_LENGTH: Self = Self(Cow::Borrowed("content-length"));
pub const CONTENT_LOCATION: Self = Self(Cow::Borrowed("content-location"));
pub const CONTENT_RANGE: Self = Self(Cow::Borrowed("content-range"));
pub const CONTENT_TYPE: Self = Self(Cow::Borrowed("content-type"));
pub const DATE: Self = Self(Cow::Borrowed("date"));
pub const ETAG: Self = Self(Cow::Borrowed("etag"));
pub const EXPECT: Self = Self(Cow::Borrowed("expect"));
pub const FROM: Self = Self(Cow::Borrowed("from"));
pub const HOST: Self = Self(Cow::Borrowed("host"));
pub const IF_MATCH: Self = Self(Cow::Borrowed("if-match"));
pub const IF_MODIFIED_SINCE: Self = Self(Cow::Borrowed("if-modified-since"));
pub const IF_NONE_MATCH: Self = Self(Cow::Borrowed("if-none-match"));
pub const IF_RANGE: Self = Self(Cow::Borrowed("if-range"));
pub const IF_UNMODIFIED_SINCE: Self = Self(Cow::Borrowed("if-unmodified-since"));
pub const LAST_MODIFIED: Self = Self(Cow::Borrowed("last-modified"));
pub const LOCATION: Self = Self(Cow::Borrowed("location"));
pub const MAX_FORWARDS: Self = Self(Cow::Borrowed("max-forwards"));
pub const PROXY_AUTHENTICATE: Self = Self(Cow::Borrowed("proxy-authenticate"));
pub const PROXY_AUTHENTICATION_INFO: Self = Self(Cow::Borrowed("proxy-authentication-info"));
pub const PROXY_AUTHORIZATION: Self = Self(Cow::Borrowed("proxy-authorization"));
pub const RANGE: Self = Self(Cow::Borrowed("range"));
pub const REFERER: Self = Self(Cow::Borrowed("referer"));
pub const RETRY_AFTER: Self = Self(Cow::Borrowed("retry-after"));
pub const SERVER: Self = Self(Cow::Borrowed("server"));
pub const TE: Self = Self(Cow::Borrowed("te"));
pub const TRAILER: Self = Self(Cow::Borrowed("trailer"));
pub const TRANSFER_ENCODING: Self = Self(Cow::Borrowed("transfer-encoding"));
pub const UPGRADE: Self = Self(Cow::Borrowed("upgrade"));
pub const USER_AGENT: Self = Self(Cow::Borrowed("user-agent"));
pub const VARY: Self = Self(Cow::Borrowed("vary"));
pub const VIA: Self = Self(Cow::Borrowed("via"));
pub const WWW_AUTHENTICATE: Self = Self(Cow::Borrowed("www-authenticate"));
}
impl Deref for HeaderName {
type Target = str;
#[inline]
fn deref(&self) -> &str {
&self.0
}
}
impl AsRef<str> for HeaderName {
#[inline]
fn as_ref(&self) -> &str {
&self.0
}
}
impl Borrow<str> for HeaderName {
#[inline]
fn borrow(&self) -> &str {
&self.0
}
}
impl FromStr for HeaderName {
type Err = InvalidHeader;
#[inline]
fn from_str(name: &str) -> Result<Self, InvalidHeader> {
name.to_owned().try_into()
}
}
impl TryFrom<&str> for HeaderName {
type Error = InvalidHeader;
#[inline]
fn try_from(value: &str) -> Result<Self, InvalidHeader> {
value.to_owned().try_into()
}
}
impl TryFrom<String> for HeaderName {
type Error = InvalidHeader;
#[inline]
fn try_from(mut name: String) -> Result<Self, InvalidHeader> {
name.make_ascii_lowercase(); if name.is_empty() {
Err(InvalidHeader(InvalidHeaderAlt::EmptyName))
} else {
for c in name.chars() {
if !matches!(c, '!' | '#' | '$' | '%' | '&' | '\'' | '*'
| '+' | '-' | '.' | '^' | '_' | '`' | '|' | '~'
| '0'..='9' | 'a'..='z')
{
return Err(InvalidHeader(InvalidHeaderAlt::InvalidNameChar {
name: name.to_owned(),
invalid_char: c,
}));
}
}
Ok(Self(name.into()))
}
}
}
impl fmt::Display for HeaderName {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Hash, Default)]
pub struct HeaderValue(Vec<u8>);
impl HeaderValue {
#[inline]
pub(crate) fn new_unchecked(value: impl Into<Vec<u8>>) -> Self {
Self(value.into())
}
#[inline]
pub fn to_str(&self) -> Result<&str, Utf8Error> {
str::from_utf8(self)
}
}
impl Deref for HeaderValue {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
&self.0
}
}
impl AsRef<[u8]> for HeaderValue {
#[inline]
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl Borrow<[u8]> for HeaderValue {
#[inline]
fn borrow(&self) -> &[u8] {
&self.0
}
}
impl FromStr for HeaderValue {
type Err = InvalidHeader;
#[inline]
fn from_str(value: &str) -> Result<Self, InvalidHeader> {
value.to_owned().try_into()
}
}
impl TryFrom<&str> for HeaderValue {
type Error = InvalidHeader;
#[inline]
fn try_from(value: &str) -> Result<Self, InvalidHeader> {
value.to_owned().try_into()
}
}
impl TryFrom<String> for HeaderValue {
type Error = InvalidHeader;
#[inline]
fn try_from(value: String) -> Result<Self, InvalidHeader> {
value.into_bytes().try_into()
}
}
impl TryFrom<&[u8]> for HeaderValue {
type Error = InvalidHeader;
#[inline]
fn try_from(value: &[u8]) -> Result<Self, InvalidHeader> {
value.to_owned().try_into()
}
}
impl TryFrom<Vec<u8>> for HeaderValue {
type Error = InvalidHeader;
#[inline]
fn try_from(value: Vec<u8>) -> Result<Self, InvalidHeader> {
if let Some(c) = value.first().cloned() {
if matches!(c, b'\t' | b' ') {
return Err(InvalidHeader(InvalidHeaderAlt::InvalidValueByte {
value,
invalid_byte: c,
}));
}
}
if let Some(c) = value.last().cloned() {
if matches!(c, b'\t' | b' ') {
return Err(InvalidHeader(InvalidHeaderAlt::InvalidValueByte {
value,
invalid_byte: c,
}));
}
}
for c in &value {
if matches!(*c, b'\r' | b'\n') {
return Err(InvalidHeader(InvalidHeaderAlt::InvalidValueByte {
value: value.clone(),
invalid_byte: *c,
}));
}
}
Ok(HeaderValue(value))
}
}
impl fmt::Display for HeaderValue {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", String::from_utf8_lossy(&self.0))
}
}
#[derive(Debug)]
pub struct Iter<'a>(std::collections::btree_map::Iter<'a, HeaderName, HeaderValue>);
impl<'a> Iterator for Iter<'a> {
type Item = (&'a HeaderName, &'a HeaderValue);
#[inline]
fn next(&mut self) -> Option<(&'a HeaderName, &'a HeaderValue)> {
self.0.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
#[inline]
fn last(self) -> Option<(&'a HeaderName, &'a HeaderValue)> {
self.0.last()
}
}
impl<'a> DoubleEndedIterator for Iter<'a> {
#[inline]
fn next_back(&mut self) -> Option<(&'a HeaderName, &'a HeaderValue)> {
self.0.next_back()
}
}
impl<'a> ExactSizeIterator for Iter<'a> {
#[inline]
fn len(&self) -> usize {
self.0.len()
}
}
#[derive(Debug)]
pub struct IntoIter(std::collections::btree_map::IntoIter<HeaderName, HeaderValue>);
impl Iterator for IntoIter {
type Item = (HeaderName, HeaderValue);
#[inline]
fn next(&mut self) -> Option<(HeaderName, HeaderValue)> {
self.0.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
#[inline]
fn last(self) -> Option<(HeaderName, HeaderValue)> {
self.0.last()
}
}
impl DoubleEndedIterator for IntoIter {
#[inline]
fn next_back(&mut self) -> Option<(HeaderName, HeaderValue)> {
self.0.next_back()
}
}
impl ExactSizeIterator for IntoIter {
#[inline]
fn len(&self) -> usize {
self.0.len()
}
}
#[derive(Debug, Clone)]
pub struct InvalidHeader(InvalidHeaderAlt);
#[derive(Debug, Clone)]
enum InvalidHeaderAlt {
EmptyName,
InvalidNameChar { name: String, invalid_char: char },
InvalidValueByte { value: Vec<u8>, invalid_byte: u8 },
}
impl fmt::Display for InvalidHeader {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
InvalidHeaderAlt::EmptyName => f.write_str("header names should not be empty"),
InvalidHeaderAlt::InvalidNameChar { name, invalid_char } => write!(
f,
"The character '{}' is not valid inside of header name '{}'",
invalid_char, name
),
InvalidHeaderAlt::InvalidValueByte {
value,
invalid_byte,
} => write!(
f,
"The byte '{}' is not valid inside of header value '{}'",
invalid_byte,
String::from_utf8_lossy(value)
),
}
}
}
impl Error for InvalidHeader {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validate_header_name() {
assert!(HeaderName::from_str("").is_err());
assert!(HeaderName::from_str("ffo bar").is_err());
assert!(HeaderName::from_str("ffo\tbar").is_err());
assert!(HeaderName::from_str("ffo\rbar").is_err());
assert!(HeaderName::from_str("ffo\nbar").is_err());
assert!(HeaderName::from_str("ffoébar").is_err());
assert!(HeaderName::from_str("foo-bar").is_ok());
}
#[test]
fn validate_header_value() {
assert!(HeaderValue::from_str("").is_ok());
assert!(HeaderValue::from_str(" ffobar").is_err());
assert!(HeaderValue::from_str("ffobar ").is_err());
assert!(HeaderValue::from_str("ffo\rbar").is_err());
assert!(HeaderValue::from_str("ffo\nbar").is_err());
assert!(HeaderValue::from_str("ffoébar").is_ok());
}
}