use crate::courierust_bytes::Bytes;
use crate::courierust_error::{Error, Result};
use crate::courierust_http::is_token;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
#[inline]
pub(crate) fn eq_ignore_ascii_case(a: &[u8], b: &[u8]) -> bool {
a.len() == b.len()
&& a.iter()
.zip(b.iter())
.all(|(x, y)| x.eq_ignore_ascii_case(y))
}
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct HeaderName(Box<str>);
impl HeaderName {
pub fn from_bytes(b: &[u8]) -> Result<Self> {
if !is_token(b) {
return Err(Error::invalid_header_name());
}
let mut lower = Vec::with_capacity(b.len());
for &c in b {
lower.push(c.to_ascii_lowercase());
}
Ok(Self(
String::from_utf8(lower)
.map_err(|_| Error::invalid_header_name())?
.into_boxed_str(),
))
}
#[inline]
pub fn from_static(s: &'static str) -> Self {
Self::from_bytes(s.as_bytes()).expect("invalid static header name")
}
pub fn from_hpack_bytes(b: &[u8]) -> Result<Self> {
if b.first() == Some(&b':') {
let rest = &b[1..];
if rest.is_empty() || !is_token(rest) || rest.iter().any(|c| c.is_ascii_uppercase()) {
return Err(Error::invalid_header_name());
}
return Ok(Self(core::str::from_utf8(b)?.into()));
}
Self::from_bytes(b)
}
#[inline]
pub fn from_lowercase(s: &'static str) -> Self {
debug_assert!({
let b = s.as_bytes();
let rest = if b.first() == Some(&b':') { &b[1..] } else { b };
!rest.is_empty()
&& rest
.iter()
.all(|&c| c.is_ascii_lowercase() || c.is_ascii_digit() || b"-_.~".contains(&c))
});
Self(s.into())
}
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
#[inline]
pub fn is_pseudo(&self) -> bool {
self.0.starts_with(':')
}
}
impl fmt::Display for HeaderName {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl fmt::Debug for HeaderName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "HeaderName({})", self.0)
}
}
impl core::str::FromStr for HeaderName {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Self::from_bytes(s.as_bytes())
}
}
impl From<&'static str> for HeaderName {
fn from(s: &'static str) -> Self {
Self::from_bytes(s.as_bytes())
.unwrap_or_else(|_| panic!("invalid static header name: {s:?}"))
}
}
impl From<HeaderName> for String {
fn from(n: HeaderName) -> Self {
n.0.into()
}
}
#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct HeaderValue(Bytes);
impl HeaderValue {
pub fn from_bytes(b: &[u8]) -> Result<Self> {
if b.iter()
.any(|&c| c == 0 || c == b'\r' || c == b'\n' || (c < 0x20 && c != b'\t'))
{
return Err(Error::invalid_header_value());
}
Ok(Self(Bytes::from(b)))
}
#[inline]
pub fn from_static(s: &'static str) -> Self {
Self(Bytes::from_static(s.as_bytes()))
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
}
#[inline]
pub fn to_str(&self) -> Result<&str> {
Ok(core::str::from_utf8(self.0.as_slice())?)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn into_bytes(self) -> Bytes {
self.0
}
}
impl fmt::Display for HeaderValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match core::str::from_utf8(self.0.as_slice()) {
Ok(s) => f.write_str(s),
Err(_) => write!(f, "<binary {} bytes>", self.0.len()),
}
}
}
impl fmt::Debug for HeaderValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "HeaderValue({:?})", self.to_str().unwrap_or("<binary>"))
}
}
impl From<Bytes> for HeaderValue {
fn from(b: Bytes) -> Self {
Self(b)
}
}
impl From<Vec<u8>> for HeaderValue {
fn from(v: Vec<u8>) -> Self {
Self(Bytes::from(v))
}
}
impl From<String> for HeaderValue {
fn from(s: String) -> Self {
Self(Bytes::from(s))
}
}
impl From<&str> for HeaderValue {
fn from(s: &str) -> Self {
Self(Bytes::from(s))
}
}
#[derive(Clone, Default)]
pub struct HeaderMap {
entries: Vec<(HeaderName, HeaderValue)>,
}
impl HeaderMap {
#[inline]
pub fn new() -> Self {
Self {
entries: Vec::new(),
}
}
#[inline]
pub fn with_capacity(cap: usize) -> Self {
Self {
entries: Vec::with_capacity(cap),
}
}
pub fn insert(&mut self, name: HeaderName, value: HeaderValue) -> Option<HeaderValue> {
let mut removed = None;
let mut i = 0;
while i < self.entries.len() {
if eq_ignore_ascii_case(self.entries[i].0.as_bytes(), name.as_bytes()) {
removed = Some(self.entries.remove(i).1);
} else {
i += 1;
}
}
self.entries.push((name, value));
removed
}
pub fn append(&mut self, name: HeaderName, value: HeaderValue) {
self.entries.push((name, value));
}
pub fn insert_pseudo(&mut self, name: HeaderName, value: HeaderValue) {
debug_assert!(name.is_pseudo());
let pos = self
.entries
.iter()
.position(|(n, _)| !n.is_pseudo())
.unwrap_or(self.entries.len());
self.entries.insert(pos, (name, value));
}
pub fn get(&self, name: &str) -> Option<&HeaderValue> {
self.entries
.iter()
.find(|(n, _)| eq_ignore_ascii_case(n.as_bytes(), name.as_bytes()))
.map(|(_, v)| v)
}
pub fn get_all<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a HeaderValue> + 'a {
self.entries
.iter()
.filter(move |(n, _)| eq_ignore_ascii_case(n.as_bytes(), name.as_bytes()))
.map(|(_, v)| v)
}
pub fn contains_key(&self, name: &str) -> bool {
self.get(name).is_some()
}
pub fn remove(&mut self, name: &str) -> Option<HeaderValue> {
let mut removed = None;
let mut i = 0;
while i < self.entries.len() {
if eq_ignore_ascii_case(self.entries[i].0.as_bytes(), name.as_bytes()) {
let (_, v) = self.entries.remove(i);
if removed.is_none() {
removed = Some(v);
}
} else {
i += 1;
}
}
removed
}
#[inline]
pub fn len(&self) -> usize {
self.entries.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[inline]
pub fn clear(&mut self) {
self.entries.clear();
}
pub fn iter(&self) -> impl Iterator<Item = (&HeaderName, &HeaderValue)> {
self.entries.iter().map(|(n, v)| (n, v))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&HeaderName, &mut HeaderValue)> {
self.entries.iter_mut().map(|(n, v)| (&*n, v))
}
pub fn values(&self) -> impl Iterator<Item = &HeaderValue> {
self.entries.iter().map(|(_, v)| v)
}
pub fn names(&self) -> Vec<&HeaderName> {
let mut out: Vec<&HeaderName> = Vec::new();
for (n, _) in &self.entries {
if !out.iter().any(|x| x.as_bytes() == n.as_bytes()) {
out.push(n);
}
}
out
}
#[inline]
pub fn into_vec(self) -> Vec<(HeaderName, HeaderValue)> {
self.entries
}
#[inline]
pub fn as_vec(&self) -> &[(HeaderName, HeaderValue)] {
&self.entries
}
}
impl fmt::Debug for HeaderMap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list()
.entries(
self.entries
.iter()
.map(|(n, v)| (n.as_str(), v.to_str().unwrap_or("<binary>"))),
)
.finish()
}
}
impl Extend<(HeaderName, HeaderValue)> for HeaderMap {
fn extend<T: IntoIterator<Item = (HeaderName, HeaderValue)>>(&mut self, iter: T) {
self.entries.extend(iter);
}
}
impl From<Vec<(HeaderName, HeaderValue)>> for HeaderMap {
fn from(v: Vec<(HeaderName, HeaderValue)>) -> Self {
Self { entries: v }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn header_name_lowercases_and_validates() {
assert_eq!(
HeaderName::from_bytes(b"Content-Type").unwrap().as_str(),
"content-type"
);
assert!(HeaderName::from_bytes(b"bad name").is_err());
assert!(HeaderName::from_bytes(b"").is_err());
}
#[test]
fn header_value_rejects_crlf() {
assert!(HeaderValue::from_bytes(b"ok value").is_ok());
assert!(HeaderValue::from_bytes(b"bad\r\nvalue").is_err());
assert!(HeaderValue::from_bytes(b"bad\x00value").is_err());
}
#[test]
fn map_insert_replace_and_append() {
let mut m = HeaderMap::new();
m.append(
HeaderName::from_static("set-cookie"),
HeaderValue::from_static("a=1"),
);
m.append(
HeaderName::from_static("set-cookie"),
HeaderValue::from_static("b=2"),
);
assert_eq!(m.get_all("set-cookie").count(), 2);
m.insert(
HeaderName::from_static("SET-COOKIE"),
HeaderValue::from_static("c=3"),
);
assert_eq!(m.get_all("set-cookie").count(), 1);
assert_eq!(m.get("set-cookie").unwrap().to_str().unwrap(), "c=3");
}
}