Ferrit Explore
中文·繁體·EN·日本語 Sign in Register
cielxl / veld / src / http / headers.rs
//! Case-insensitive HTTP header map with insertion-order preservation.
//!
//! [`HeaderMap`] stores header names in a case-insensitive manner while
//! preserving the insertion order of distinct header names.  Multiple
//! values for the same header (e.g. `Set-Cookie`) are grouped under a
//! single entry via [`HeaderMap::append`].
//!
//! Header values are backed by [`bytes::Bytes`] to enable zero-copy
//! slicing when parsing directly from a network buffer.

use bytes::Bytes;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::iter::FromIterator;

// ---------------------------------------------------------------------------
// HeaderName
// ---------------------------------------------------------------------------

/// A case-insensitive HTTP header name.
///
/// Two `HeaderName` values are considered equal regardless of ASCII
/// casing (`content-type` == `Content-Type` == `CONTENT-TYPE`).
/// The original casing supplied at construction time is preserved for
/// display purposes.
#[derive(Debug, Clone)]
pub struct HeaderName {
    /// The original-casing representation (used for `Display`).
    original: String,
    /// Lower-cased representation (used for `Hash` / `Eq`).
    lower: String,
}

impl HeaderName {
    /// Create a `HeaderName` from a string (compatibility constructor).
    #[inline]
    pub fn new(name: &str) -> Self {
        Self::from_string(name.to_owned())
    }

    /// Create a `HeaderName` from an owned `String`.
    #[inline]
    pub fn from_string(name: String) -> Self {
        let lower = name.to_ascii_lowercase();
        Self {
            original: name,
            lower,
        }
    }

    /// Return the original-casing name (compatibility alias).
    #[inline]
    pub fn original(&self) -> &str {
        &self.original
    }

    /// Create a `HeaderName` from a string slice.
    #[inline]
    pub fn from_static(name: &'static str) -> Self {
        Self {
            lower: name.to_ascii_lowercase(),
            original: name.to_owned(),
        }
    }

    /// Create a `HeaderName` without copying the input string.
    ///
    /// Returns an error if the name contains characters that are illegal
    /// in HTTP header field-names (anything outside the range `0x21-0x7E`
    /// excluding the delimiters `()<>@,;:\"/[]?={}`).
    pub fn try_from_bytes(bytes: &[u8]) -> Result<Self, InvalidHeaderName> {
        if !bytes.iter().all(|&b| is_valid_header_name_byte(b)) {
            return Err(InvalidHeaderName);
        }
        let original = String::from_utf8_lossy(bytes).into_owned();
        Ok(Self::from_string(original))
    }

    /// Return the header name as a lowercase `&str`.
    #[inline]
    pub fn as_str(&self) -> &str {
        &self.lower
    }

    /// Return the original-casing representation.
    #[inline]
    pub fn as_original(&self) -> &str {
        &self.original
    }

    /// Consume the `HeaderName` and return the original-casing `String`.
    #[inline]
    pub fn into_string(self) -> String {
        self.original
    }

    /// Consume the `HeaderName` and return the lowercase `String`.
    #[inline]
    pub fn into_lower(self) -> String {
        self.lower
    }
}

/// Error returned when a header name contains invalid characters.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidHeaderName;

impl fmt::Display for InvalidHeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("invalid HTTP header name")
    }
}

impl std::error::Error for InvalidHeaderName {}

impl fmt::Display for HeaderName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.original)
    }
}

impl PartialEq for HeaderName {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.lower == other.lower
    }
}

impl Eq for HeaderName {}

impl Hash for HeaderName {
    #[inline]
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.lower.hash(state);
    }
}

impl From<&str> for HeaderName {
    fn from(s: &str) -> Self {
        Self::from_string(s.to_owned())
    }
}

impl From<String> for HeaderName {
    fn from(s: String) -> Self {
        Self::from_string(s)
    }
}

// ---------------------------------------------------------------------------
// HeaderValue
// ---------------------------------------------------------------------------

/// A single HTTP header value backed by [`Bytes`].
///
/// The value is stored as raw bytes and is assumed to be valid
/// ISO-8859-1 (the default encoding for HTTP/1.1 header values).
/// UTF-8 content is the common case and is handled efficiently.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HeaderValue {
    inner: Bytes,
}

impl HeaderValue {
    /// Create a value from a string (compatibility constructor).
    #[inline]
    pub fn new(value: &str) -> Self {
        Self::from_string(value.to_owned())
    }

    /// Create a value from an owned `String` (zero-allocation if the
    /// caller no longer needs the `String`).
    #[inline]
    pub fn from_string(value: String) -> Self {
        Self {
            inner: Bytes::from(value),
        }
    }

    /// Return the value as a string slice (lossy).
    #[inline]
    pub fn as_str(&self) -> &str {
        std::str::from_utf8(&self.inner).unwrap_or("")
    }

    /// Parse the value as a given type.
    pub fn parse<T: std::str::FromStr>(&self) -> Result<T, T::Err> {
        self.as_str().parse()
    }

    /// Return a lowercased version of the value.
    pub fn to_lowercase(&self) -> String {
        self.as_str().to_lowercase()
    }

    /// Create a value from a static string slice (no allocation).
    #[inline]
    pub fn from_static(value: &'static str) -> Self {
        Self {
            inner: Bytes::from_static(value.as_bytes()),
        }
    }

    /// Create a value from raw bytes.
    #[inline]
    pub fn from_bytes(value: impl Into<Bytes>) -> Self {
        Self {
            inner: value.into(),
        }
    }

    /// Return the value as a byte slice.
    #[inline]
    pub fn as_bytes(&self) -> &[u8] {
        &self.inner
    }

    /// Try to interpret the value as a UTF-8 string.
    #[inline]
    pub fn to_str(&self) -> Result<&str, std::str::Utf8Error> {
        std::str::from_utf8(&self.inner)
    }

    /// Consume and return the inner [`Bytes`].
    #[inline]
    pub fn into_bytes(self) -> Bytes {
        self.inner
    }

    /// Return `true` if the value is empty.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.inner.is_empty()
    }

    /// Return the length of the value in bytes.
    #[inline]
    pub fn len(&self) -> usize {
        self.inner.len()
    }
}

impl fmt::Display for HeaderValue {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match std::str::from_utf8(&self.inner) {
            Ok(s) => f.write_str(s),
            // Lossy: render replacement chars for non-UTF-8 bytes.
            Err(_) => {
                for &b in &self.inner {
                    if b.is_ascii_graphic() || b == b' ' {
                        f.write_str(std::str::from_utf8(&[b]).unwrap())?;
                    } else {
                        write!(f, "\\x{:02x}", b)?;
                    }
                }
                Ok(())
            }
        }
    }
}

impl From<String> for HeaderValue {
    fn from(s: String) -> Self {
        Self::from_string(s)
    }
}

impl From<&String> for HeaderValue {
    fn from(s: &String) -> Self {
        Self::from_string(s.clone())
    }
}

impl From<&str> for HeaderValue {
    fn from(s: &str) -> Self {
        Self::from_string(s.to_owned())
    }
}

impl PartialEq<&str> for HeaderValue {
    fn eq(&self, other: &&str) -> bool {
        self.as_str().eq_ignore_ascii_case(other)
    }
}

impl From<Bytes> for HeaderValue {
    fn from(b: Bytes) -> Self {
        Self { inner: b }
    }
}

impl From<Vec<u8>> for HeaderValue {
    fn from(v: Vec<u8>) -> Self {
        Self {
            inner: Bytes::from(v),
        }
    }
}

impl<'a> From<&'a [u8]> for HeaderValue {
    fn from(s: &'a [u8]) -> Self {
        Self {
            inner: Bytes::copy_from_slice(s),
        }
    }
}

// ---------------------------------------------------------------------------
// HeaderMap
// ---------------------------------------------------------------------------

/// An ordered, case-insensitive map of HTTP header names to values.
///
/// Insertion order of distinct header names is preserved.  Multiple
/// values for the same name (added via [`append`](HeaderMap::append))
/// are grouped together under a single entry.
///
/// # Examples
///
/// ```
/// use veld::http::headers::{HeaderMap, HeaderName, HeaderValue};
///
/// let mut map = HeaderMap::new();
/// map.insert("Content-Type", "text/html");
/// map.append("Set-Cookie", "a=1");
/// map.append("Set-Cookie", "b=2");
///
/// assert_eq!(map.get("content-type").unwrap().to_str().unwrap(), "text/html");
/// assert_eq!(map.get_all("set-cookie").len(), 2);
/// ```
#[derive(Debug, Clone)]
pub struct HeaderMap {
    /// Ordered list of (name, values) pairs.
    ///
    /// HTTP messages carry few headers, so a linear scan with a
    /// case-insensitive compare is faster than a hash map here -- and,
    /// crucially, lookups need not allocate a `HeaderName` for the key,
    /// which removes several heap allocations from every request.
    entries: Vec<(HeaderName, Vec<HeaderValue>)>,
}

impl HeaderMap {
    /// Create an empty `HeaderMap`.
    #[inline]
    pub fn new() -> Self {
        Self {
            entries: Vec::new(),
        }
    }

    /// Create an empty `HeaderMap` with the given capacity for distinct
    /// header names.
    #[inline]
    pub fn with_capacity(capacity: usize) -> Self {
        Self {
            entries: Vec::with_capacity(capacity),
        }
    }

    /// Return the number of distinct header names in the map.
    #[inline]
    pub fn len(&self) -> usize {
        self.entries.len()
    }

    /// Return `true` if the map contains no headers.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.entries.is_empty()
    }

    // -- insertion / removal -------------------------------------------------

    /// Insert a header, replacing any existing values for the same name.
    ///
    /// If the name already exists its position in the iteration order is
    /// preserved and the old values are replaced.  Returns the previous
    /// values if any.
    pub fn insert<N, V>(&mut self, name: N, value: V) -> Option<Vec<HeaderValue>>
    where
        N: Into<HeaderName>,
        V: Into<HeaderValue>,
    {
        let name = name.into();
        let value = value.into();

        // Both `as_str()` values are lowercase, so `==` is a case-insensitive
        // name match.
        if let Some(entry) = self
            .entries
            .iter_mut()
            .find(|(n, _)| n.as_str() == name.as_str())
        {
            Some(std::mem::replace(&mut entry.1, vec![value]))
        } else {
            self.entries.push((name, vec![value]));
            None
        }
    }

    /// Append a value to an existing header, or create a new entry.
    ///
    /// This is the correct way to add multiple values for the same
    /// header name (e.g. `Set-Cookie`).
    pub fn append<N, V>(&mut self, name: N, value: V)
    where
        N: Into<HeaderName>,
        V: Into<HeaderValue>,
    {
        let name = name.into();
        let value = value.into();

        if let Some(entry) = self
            .entries
            .iter_mut()
            .find(|(n, _)| n.as_str() == name.as_str())
        {
            entry.1.push(value);
        } else {
            self.entries.push((name, vec![value]));
        }
    }

    /// Get the first value for the given header name, or `None` if the
    /// name is not present.
    pub fn get<N>(&self, name: &N) -> Option<&HeaderValue>
    where
        N: ?Sized + AsRef<str>,
    {
        let name = name.as_ref();
        self.entries
            .iter()
            .find(|(n, _)| n.as_str().eq_ignore_ascii_case(name))
            .and_then(|(_, v)| v.first())
    }

    /// Get all values for the given header name.
    ///
    /// Returns an empty slice if the name is not present.
    pub fn get_all<N>(&self, name: &N) -> &[HeaderValue]
    where
        N: ?Sized + AsRef<str>,
    {
        let name = name.as_ref();
        self.entries
            .iter()
            .find(|(n, _)| n.as_str().eq_ignore_ascii_case(name))
            .map(|(_, v)| v.as_slice())
            .unwrap_or(&[])
    }

    /// Return `true` if the map contains the given header name.
    pub fn contains<N>(&self, name: &N) -> bool
    where
        N: ?Sized + AsRef<str>,
    {
        let name = name.as_ref();
        self.entries
            .iter()
            .any(|(n, _)| n.as_str().eq_ignore_ascii_case(name))
    }

    /// Remove a header and all its values.  Returns the values if the
    /// header was present.
    ///
    /// Subsequent entries preserve their relative iteration order.
    pub fn remove<N>(&mut self, name: &N) -> Option<Vec<HeaderValue>>
    where
        N: ?Sized + AsRef<str>,
    {
        let name = name.as_ref();
        if let Some(pos) = self
            .entries
            .iter()
            .position(|(n, _)| n.as_str().eq_ignore_ascii_case(name))
        {
            Some(self.entries.remove(pos).1)
        } else {
            None
        }
    }

    /// Remove all headers from the map.
    #[inline]
    pub fn clear(&mut self) {
        self.entries.clear();
    }

    // -- iteration -----------------------------------------------------------

    /// Return an iterator over `(&HeaderName, &Vec<HeaderValue>)` pairs
    /// in insertion order.
    #[inline]
    pub fn iter(&self) -> Iter<'_> {
        Iter {
            inner: self.entries.iter(),
        }
    }

    /// Return a mutable iterator over `(&HeaderName, &mut Vec<HeaderValue>)`
    /// pairs in insertion order.
    #[inline]
    pub fn iter_mut(&mut self) -> IterMut<'_> {
        IterMut {
            inner: self.entries.iter_mut(),
        }
    }

    /// Return an iterator over header names in insertion order.
    #[inline]
    pub fn keys(&self) -> Keys<'_> {
        Keys {
            inner: self.entries.iter(),
        }
    }

    // -- conversion helpers --------------------------------------------------

    /// Return the total number of individual header values (counting
    /// all values for multi-valued headers).
    #[inline]
    pub fn values_len(&self) -> usize {
        self.entries.iter().map(|(_, v)| v.len()).sum()
    }

    /// Serialize headers to HTTP wire format ("Name: value\r\n" for each).
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(self.serialized_len());
        self.write_to(&mut buf);
        buf
    }

    /// Calculate the exact byte length of the serialized header block.
    ///
    /// Each header contributes: `name.len() + 2 + value.len() + 2` bytes
    /// (the `2`s are for `: ` and `\r\n`).
    pub fn serialized_len(&self) -> usize {
        self.entries
            .iter()
            .map(|(name, values)| {
                let name_len = name.original().len();
                values
                    .iter()
                    .map(|v| name_len + 2 + v.len() + 2)
                    .sum::<usize>()
            })
            .sum()
    }

    /// Write headers directly into the provided buffer in HTTP wire format.
    ///
    /// This avoids the intermediate `Vec<u8>` allocation that
    /// [`to_bytes`](Self::to_bytes) creates when the caller already has a
    /// buffer to write into (e.g. response serialization).
    pub fn write_to(&self, buf: &mut Vec<u8>) {
        for (name, values) in &self.entries {
            for value in values {
                buf.extend_from_slice(name.original().as_bytes());
                buf.extend_from_slice(b": ");
                buf.extend_from_slice(value.as_bytes());
                buf.extend_from_slice(b"\r\n");
            }
        }
    }
}

impl Default for HeaderMap {
    #[inline]
    fn default() -> Self {
        Self::new()
    }
}

// -- Display: render all headers as "Name: value\r\n" ------------------------

impl fmt::Display for HeaderMap {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (name, values) in &self.entries {
            for value in values {
                writeln!(f, "{}: {}", name, value)?;
            }
        }
        Ok(())
    }
}

// -- FromIterator ------------------------------------------------------------

impl FromIterator<(HeaderName, HeaderValue)> for HeaderMap {
    /// Build a `HeaderMap` from an iterator of `(name, value)` pairs.
    ///
    /// Duplicate names cause values to be appended (equivalent to
    /// calling [`append`](HeaderMap::append) for each pair).
    fn from_iter<I: IntoIterator<Item = (HeaderName, HeaderValue)>>(iter: I) -> Self {
        let mut map = HeaderMap::new();
        for (name, value) in iter {
            map.append(name, value);
        }
        map
    }
}

impl FromIterator<(String, String)> for HeaderMap {
    /// Convenience: build from `(String, String)` pairs.
    fn from_iter<I: IntoIterator<Item = (String, String)>>(iter: I) -> Self {
        let mut map = HeaderMap::new();
        for (name, value) in iter {
            map.append(name, value);
        }
        map
    }
}

impl FromIterator<(String, Bytes)> for HeaderMap {
    /// Convenience: build from `(String, Bytes)` pairs.
    fn from_iter<I: IntoIterator<Item = (String, Bytes)>>(iter: I) -> Self {
        let mut map = HeaderMap::new();
        for (name, value) in iter {
            map.append(
                HeaderName::from_string(name),
                HeaderValue::from_bytes(value),
            );
        }
        map
    }
}

// -- IntoIterator ------------------------------------------------------------

/// Owned iterator over `(HeaderName, Vec<HeaderValue>)` pairs.
pub struct IntoIter {
    inner: std::vec::IntoIter<(HeaderName, Vec<HeaderValue>)>,
}

impl Iterator for IntoIter {
    type Item = (HeaderName, Vec<HeaderValue>);

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.inner.size_hint()
    }
}

impl ExactSizeIterator for IntoIter {}

impl IntoIterator for HeaderMap {
    type Item = (HeaderName, Vec<HeaderValue>);
    type IntoIter = IntoIter;

    #[inline]
    fn into_iter(self) -> IntoIter {
        IntoIter {
            inner: self.entries.into_iter(),
        }
    }
}

/// Borrowing iterator over `(&HeaderName, &Vec<HeaderValue>)` pairs.
pub struct Iter<'a> {
    inner: std::slice::Iter<'a, (HeaderName, Vec<HeaderValue>)>,
}

impl<'a> Iterator for Iter<'a> {
    type Item = (&'a HeaderName, &'a Vec<HeaderValue>);

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next().map(|(n, v)| (n, v))
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.inner.size_hint()
    }
}

impl<'a> ExactSizeIterator for Iter<'a> {}

impl<'a> IntoIterator for &'a HeaderMap {
    type Item = (&'a HeaderName, &'a Vec<HeaderValue>);
    type IntoIter = Iter<'a>;

    #[inline]
    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

/// Mutable borrowing iterator.
pub struct IterMut<'a> {
    inner: std::slice::IterMut<'a, (HeaderName, Vec<HeaderValue>)>,
}

impl<'a> Iterator for IterMut<'a> {
    type Item = (&'a HeaderName, &'a mut Vec<HeaderValue>);

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next().map(|(n, v)| (&*n, v))
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.inner.size_hint()
    }
}

impl<'a> ExactSizeIterator for IterMut<'a> {}

/// Borrowing iterator over header names.
pub struct Keys<'a> {
    inner: std::slice::Iter<'a, (HeaderName, Vec<HeaderValue>)>,
}

impl<'a> Iterator for Keys<'a> {
    type Item = &'a HeaderName;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next().map(|(n, _)| n)
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.inner.size_hint()
    }
}

impl<'a> ExactSizeIterator for Keys<'a> {}

// ---------------------------------------------------------------------------
// Header name validation
// ---------------------------------------------------------------------------

/// Return `true` if the byte is a valid HTTP header field-name character.
///
/// Per RFC 9110 section 5.1: `!`, `#`, `$`, `%`, `&`, `'`, `*`, `+`,
/// `-`, `.`, `^`, `` ` ``, `|`, `~`, and digits / alpha.
fn is_valid_header_name_byte(b: u8) -> bool {
    matches!(b,
        b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' |
        b'-' | b'.' | b'^' | b'`' | b'|' | b'~' |
        b'0'..=b'9' | b'A'..=b'Z' | b'a'..=b'z'
    )
}

// ---------------------------------------------------------------------------
// Well-known header name constants
// ---------------------------------------------------------------------------

macro_rules! header_constants {
    ($($name:ident => $value:literal),+ $(,)?) => {
        impl HeaderName {
            $(
                pub const $name: &'static str = $value;
            )+
        }

        /// Convenience free functions that return a pre-built `HeaderName`
        /// for each well-known header.  These are **not** const; they
        /// allocate.  For a zero-alloc comparison, compare against the
        /// `&str` constants on `HeaderName` instead.
        pub mod header_names {
            use super::HeaderName;

            $(
                #[doc = concat!("`", $value, "`")]
                #[inline]
                #[allow(non_snake_case)] // names mirror the SCREAMING_CASE header constants
                pub fn $name() -> HeaderName {
                    HeaderName::from_static($value)
                }
            )+
        }
    };
}

header_constants! {
    // -- General --
    CACHE_CONTROL        => "Cache-Control",
    CONNECTION           => "Connection",
    DATE                 => "Date",
    PRAGMA               => "Pragma",
    TRAILER              => "Trailer",
    TRANSFER_ENCODING    => "Transfer-Encoding",
    UPGRADE              => "Upgrade",
    VIA                  => "Via",
    WARNING              => "Warning",

    // -- Request --
    ACCEPT               => "Accept",
    ACCEPT_CHARSET       => "Accept-Charset",
    ACCEPT_ENCODING      => "Accept-Encoding",
    ACCEPT_LANGUAGE      => "Accept-Language",
    AUTHORIZATION        => "Authorization",
    COOKIE               => "Cookie",
    EXPECT               => "Expect",
    FROM                 => "From",
    HOST                 => "Host",
    IF_MATCH             => "If-Match",
    IF_MODIFIED_SINCE    => "If-Modified-Since",
    IF_NONE_MATCH        => "If-None-Match",
    IF_RANGE             => "If-Range",
    IF_UNMODIFIED_SINCE  => "If-Unmodified-Since",
    MAX_FORWARDS         => "Max-Forwards",
    PROXY_AUTHORIZATION  => "Proxy-Authorization",
    RANGE                => "Range",
    REFERER              => "Referer",
    TE                   => "TE",
    USER_AGENT           => "User-Agent",

    // -- Response --
    ACCEPT_RANGES        => "Accept-Ranges",
    AGE                  => "Age",
    ALLOW                => "Allow",
    CONTENT_ENCODING     => "Content-Encoding",
    CONTENT_LANGUAGE     => "Content-Language",
    CONTENT_LENGTH       => "Content-Length",
    CONTENT_LOCATION     => "Content-Location",
    CONTENT_RANGE        => "Content-Range",
    CONTENT_TYPE         => "Content-Type",
    ETAG                 => "ETag",
    EXPIRES              => "Expires",
    LAST_MODIFIED        => "Last-Modified",
    LOCATION             => "Location",
    PROXY_AUTHENTICATE   => "Proxy-Authenticate",
    RETRY_AFTER          => "Retry-After",
    SERVER               => "Server",
    SET_COOKIE           => "Set-Cookie",
    VARY                 => "Vary",
    WWW_AUTHENTICATE     => "WWW-Authenticate",
}

// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn header_name_case_insensitive_eq() {
        let a = HeaderName::from("Content-Type");
        let b = HeaderName::from("content-type");
        let c = HeaderName::from("CONTENT-TYPE");
        assert_eq!(a, b);
        assert_eq!(b, c);
        assert_eq!(a, c);
    }

    #[test]
    fn header_name_case_insensitive_hash() {
        use std::collections::hash_map::DefaultHasher;
        let a = HeaderName::from("Content-Type");
        let b = HeaderName::from("content-type");

        let mut ha = DefaultHasher::new();
        let mut hb = DefaultHasher::new();
        a.hash(&mut ha);
        b.hash(&mut hb);
        assert_eq!(ha.finish(), hb.finish());
    }

    #[test]
    fn header_name_display_preserves_casing() {
        let name = HeaderName::from("X-Custom-Header");
        assert_eq!(name.to_string(), "X-Custom-Header");
        assert_eq!(name.as_str(), "x-custom-header");
    }

    #[test]
    fn header_name_from_static() {
        let name = HeaderName::from_static("Host");
        assert_eq!(name.as_original(), "Host");
        assert_eq!(name.as_str(), "host");
    }

    #[test]
    fn header_name_try_from_bytes_valid() {
        let name = HeaderName::try_from_bytes(b"Accept-Encoding").unwrap();
        assert_eq!(name.as_str(), "accept-encoding");
    }

    #[test]
    fn header_name_try_from_bytes_invalid() {
        // Space is not allowed in header names.
        assert!(HeaderName::try_from_bytes(b"Bad Name").is_err());
    }

    // -- HeaderValue ---------------------------------------------------------

    #[test]
    fn header_value_display_utf8() {
        let val = HeaderValue::from("text/html; charset=utf-8");
        assert_eq!(val.to_string(), "text/html; charset=utf-8");
    }

    #[test]
    fn header_value_display_non_utf8() {
        let val = HeaderValue::from_bytes(vec![0xFF, 0xFE]);
        let s = val.to_string();
        assert!(s.contains("ff"));
        assert!(s.contains("fe"));
    }

    #[test]
    fn header_value_from_string() {
        let val = HeaderValue::from_string("hello".to_owned());
        assert_eq!(val.to_str().unwrap(), "hello");
        assert_eq!(val.len(), 5);
        assert!(!val.is_empty());
    }

    #[test]
    fn header_value_empty() {
        let val = HeaderValue::from_static("");
        assert!(val.is_empty());
        assert_eq!(val.len(), 0);
    }

    #[test]
    fn header_value_from_bytes_zero_copy() {
        let original = Bytes::from("shared buffer");
        let val = HeaderValue::from(original.clone());
        // Same underlying allocation.
        assert_eq!(val.as_bytes().as_ptr(), original.as_ptr());
    }

    // -- HeaderMap -----------------------------------------------------------

    #[test]
    fn map_insert_and_get() {
        let mut map = HeaderMap::new();
        map.insert("Content-Type", "text/html");
        map.insert("Host", "example.com");

        assert_eq!(map.len(), 2);
        assert_eq!(
            map.get("content-type").unwrap().to_str().unwrap(),
            "text/html"
        );
        assert_eq!(map.get("HOST").unwrap().to_str().unwrap(), "example.com");
    }

    #[test]
    fn map_insert_replaces() {
        let mut map = HeaderMap::new();
        map.insert("X-Foo", "old");
        map.insert("x-foo", "new");

        assert_eq!(map.len(), 1);
        assert_eq!(map.get("x-foo").unwrap().to_str().unwrap(), "new");
    }

    #[test]
    fn map_insert_returns_old_values() {
        let mut map = HeaderMap::new();
        map.insert("Set-Cookie", "a=1");
        let old = map.insert("set-cookie", "b=2");
        assert!(old.is_some());
        let old = old.unwrap();
        assert_eq!(old.len(), 1);
        assert_eq!(old[0].to_str().unwrap(), "a=1");
    }

    #[test]
    fn map_append_multi_value() {
        let mut map = HeaderMap::new();
        map.append("Set-Cookie", "a=1");
        map.append("Set-Cookie", "b=2");
        map.append("Set-Cookie", "c=3");

        let all = map.get_all("set-cookie");
        assert_eq!(all.len(), 3);
        assert_eq!(all[0].to_str().unwrap(), "a=1");
        assert_eq!(all[1].to_str().unwrap(), "b=2");
        assert_eq!(all[2].to_str().unwrap(), "c=3");
    }

    #[test]
    fn map_contains() {
        let mut map = HeaderMap::new();
        assert!(!map.contains("Host"));
        map.insert("Host", "example.com");
        assert!(map.contains("host"));
        assert!(map.contains("HOST"));
    }

    #[test]
    fn map_remove() {
        let mut map = HeaderMap::new();
        map.insert("A", "1");
        map.insert("B", "2");
        map.insert("C", "3");

        let removed = map.remove("b");
        assert!(removed.is_some());
        assert_eq!(map.len(), 2);
        assert!(!map.contains("b"));

        // Remaining entries are still accessible.
        assert_eq!(map.get("a").unwrap().to_str().unwrap(), "1");
        assert_eq!(map.get("c").unwrap().to_str().unwrap(), "3");
    }

    #[test]
    fn map_remove_nonexistent() {
        let mut map = HeaderMap::new();
        assert!(map.remove("nope").is_none());
    }

    #[test]
    fn map_get_all_empty_slice() {
        let map = HeaderMap::new();
        assert!(map.get_all("anything").is_empty());
    }

    #[test]
    fn map_insertion_order_preserved() {
        let mut map = HeaderMap::new();
        map.insert("Z-Last", "z");
        map.insert("A-First", "a");
        map.insert("M-Middle", "m");

        let names: Vec<_> = map.keys().map(|k| k.as_original().to_owned()).collect();
        assert_eq!(names, vec!["Z-Last", "A-First", "M-Middle"]);
    }

    #[test]
    fn map_clear() {
        let mut map = HeaderMap::new();
        map.insert("A", "1");
        map.insert("B", "2");
        map.clear();
        assert!(map.is_empty());
        assert_eq!(map.len(), 0);
    }

    #[test]
    fn map_values_len() {
        let mut map = HeaderMap::new();
        map.insert("A", "1");
        map.append("B", "2");
        map.append("B", "3");
        map.append("B", "4");
        assert_eq!(map.values_len(), 4);
    }

    #[test]
    fn map_default() {
        let map: HeaderMap = HeaderMap::default();
        assert!(map.is_empty());
    }

    // -- Display -------------------------------------------------------------

    #[test]
    fn map_display_format() {
        let mut map = HeaderMap::new();
        map.insert("Content-Type", "text/html");
        map.insert("Content-Length", "42");

        let rendered = map.to_string();
        // Both lines must be present with trailing newlines.
        assert!(rendered.contains("Content-Type: text/html\n"));
        assert!(rendered.contains("Content-Length: 42\n"));
    }

    #[test]
    fn map_display_multi_value() {
        let mut map = HeaderMap::new();
        map.append("Set-Cookie", "a=1");
        map.append("Set-Cookie", "b=2");

        let rendered = map.to_string();
        assert!(rendered.contains("Set-Cookie: a=1\n"));
        assert!(rendered.contains("Set-Cookie: b=2\n"));
    }

    // -- FromIterator --------------------------------------------------------

    #[test]
    fn map_from_iter_headername_value() {
        let pairs = vec![
            (HeaderName::from("A"), HeaderValue::from("1")),
            (HeaderName::from("B"), HeaderValue::from("2")),
        ];
        let map: HeaderMap = pairs.into_iter().collect();
        assert_eq!(map.len(), 2);
        assert_eq!(map.get("a").unwrap().to_str().unwrap(), "1");
    }

    #[test]
    fn map_from_iter_string_string() {
        let pairs = vec![
            ("Content-Type".to_owned(), "text/plain".to_owned()),
            ("Host".to_owned(), "example.com".to_owned()),
        ];
        let map: HeaderMap = pairs.into_iter().collect();
        assert_eq!(map.len(), 2);
    }

    #[test]
    fn map_from_iter_dedup_appends() {
        let pairs = vec![
            ("Set-Cookie".to_owned(), "a=1".to_owned()),
            ("Set-Cookie".to_owned(), "b=2".to_owned()),
        ];
        let map: HeaderMap = pairs.into_iter().collect();
        assert_eq!(map.len(), 1);
        assert_eq!(map.get_all("set-cookie").len(), 2);
    }

    // -- IntoIterator --------------------------------------------------------

    #[test]
    fn map_into_iter_owned() {
        let mut map = HeaderMap::new();
        map.insert("A", "1");
        map.insert("B", "2");

        let items: Vec<_> = map.into_iter().collect();
        assert_eq!(items.len(), 2);
        assert_eq!(items[0].0.as_str(), "a");
        assert_eq!(items[0].1[0].to_str().unwrap(), "1");
        assert_eq!(items[1].0.as_str(), "b");
    }

    #[test]
    fn map_iter_borrowed() {
        let mut map = HeaderMap::new();
        map.insert("A", "1");
        map.insert("B", "2");

        let mut names = Vec::new();
        for (name, _values) in &map {
            names.push(name.as_original().to_owned());
        }
        assert_eq!(names, vec!["A", "B"]);
    }

    #[test]
    fn map_iter_mut() {
        let mut map = HeaderMap::new();
        map.insert("A", "old");

        for (_name, values) in map.iter_mut() {
            values.clear();
            values.push(HeaderValue::from("new"));
        }

        assert_eq!(map.get("a").unwrap().to_str().unwrap(), "new");
    }

    // -- Well-known constants ------------------------------------------------

    #[test]
    fn well_known_constants() {
        assert_eq!(HeaderName::CONTENT_TYPE, "Content-Type");
        assert_eq!(HeaderName::HOST, "Host");
        assert_eq!(HeaderName::SET_COOKIE, "Set-Cookie");
        assert_eq!(HeaderName::TRANSFER_ENCODING, "Transfer-Encoding");
        assert_eq!(HeaderName::AUTHORIZATION, "Authorization");
    }

    // -- with_capacity -------------------------------------------------------

    #[test]
    fn map_with_capacity() {
        let map = HeaderMap::with_capacity(32);
        assert!(map.is_empty());
        assert_eq!(map.len(), 0);
    }

    // -- Mixed insert/append/remove interaction ------------------------------

    #[test]
    fn map_mixed_operations() {
        let mut map = HeaderMap::new();
        map.insert("Host", "example.com");
        map.append("Set-Cookie", "a=1");
        map.append("Set-Cookie", "b=2");
        map.insert("Content-Length", "100");

        // Remove one header.
        map.remove("content-length");
        assert_eq!(map.len(), 2);

        // Replace Set-Cookie entirely.
        map.insert("set-cookie", "c=3");
        assert_eq!(map.get_all("Set-Cookie").len(), 1);
        assert_eq!(map.get("Set-Cookie").unwrap().to_str().unwrap(), "c=3");

        // Host still present.
        assert_eq!(map.get("HOST").unwrap().to_str().unwrap(), "example.com");
    }

    // -- HeaderValue edge cases ----------------------------------------------

    #[test]
    fn header_value_as_bytes() {
        let val = HeaderValue::from("raw bytes");
        assert_eq!(val.as_bytes(), b"raw bytes");
    }

    #[test]
    fn header_value_into_bytes() {
        let val = HeaderValue::from("take ownership");
        let b: Bytes = val.into_bytes();
        assert_eq!(&b[..], b"take ownership");
    }

    #[test]
    fn header_name_into_lower() {
        let name = HeaderName::from("X-Forwarded-For");
        assert_eq!(name.into_lower(), "x-forwarded-for");
    }

    #[test]
    fn header_name_into_string() {
        let name = HeaderName::from("X-Forwarded-For");
        assert_eq!(name.into_string(), "X-Forwarded-For");
    }
}