pub use http::header::*;
pub use name::OrigHeaderName;
pub trait IntoOrigHeaderName: sealed::Sealed {
fn into_orig_header_name(self) -> OrigHeaderName;
}
#[derive(Debug, Clone, Default)]
pub struct OrigHeaderMap(HeaderMap<OrigHeaderName>);
impl OrigHeaderMap {
#[inline]
pub fn new() -> Self {
Self(HeaderMap::default())
}
#[inline]
pub fn with_capacity(size: usize) -> Self {
Self(HeaderMap::with_capacity(size))
}
#[inline]
pub fn insert<N>(&mut self, orig: N) -> bool
where
N: IntoOrigHeaderName,
{
let orig_header_name = orig.into_orig_header_name();
match &orig_header_name.kind {
name::Kind::Cased(bytes) => HeaderName::from_bytes(bytes)
.map(|name| self.0.append(name, orig_header_name))
.unwrap_or(false),
name::Kind::Standard(header_name) => {
self.0.append(header_name.clone(), orig_header_name)
}
}
}
#[inline]
pub fn extend(&mut self, iter: OrigHeaderMap) {
self.0.extend(iter.0);
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = (&HeaderName, &OrigHeaderName)> {
self.0.iter()
}
}
impl OrigHeaderMap {
pub(crate) fn sort_headers(&self, headers: &mut HeaderMap) {
if headers.len() <= 1 || self.0.is_empty() {
return;
}
let mut sorted_headers = HeaderMap::with_capacity(headers.keys_len());
for name in self.0.keys() {
for value in headers.get_all(name) {
sorted_headers.append(name.clone(), value.clone());
}
headers.remove(name);
}
let mut prev_name: Option<HeaderName> = None;
for (name, value) in headers.drain() {
match (name, &prev_name) {
(Some(name), _) => {
prev_name.replace(name.clone());
sorted_headers.insert(name, value);
}
(None, Some(prev_name)) => {
sorted_headers.append(prev_name, value);
}
_ => {}
}
}
std::mem::swap(headers, &mut sorted_headers);
}
pub(crate) fn sort_headers_for_each<F>(&self, headers: &HeaderMap, mut dst: F)
where
F: FnMut(&[u8], &HeaderValue),
{
for (name, orig_name) in self.iter() {
let Some(first_orig_name) = self.0.get_all(name).iter().next() else {
continue;
};
if !std::ptr::eq(first_orig_name, orig_name) {
continue;
}
for value in headers.get_all(name) {
dst(orig_name.as_ref(), value);
}
}
for (name, value) in headers {
if !self.0.contains_key(name) {
dst(name.as_ref(), value);
}
}
}
}
impl<'a> IntoIterator for &'a OrigHeaderMap {
type Item = (&'a HeaderName, &'a OrigHeaderName);
type IntoIter = <&'a HeaderMap<OrigHeaderName> as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl IntoIterator for OrigHeaderMap {
type Item = (Option<HeaderName>, OrigHeaderName);
type IntoIter = <HeaderMap<OrigHeaderName> as IntoIterator>::IntoIter;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl_request_config_value!(OrigHeaderMap);
mod name {
use bytes::Bytes;
use http::HeaderName;
use super::IntoOrigHeaderName;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OrigHeaderName {
pub(super) kind: Kind,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum Kind {
Cased(Bytes),
Standard(HeaderName),
}
impl AsRef<[u8]> for OrigHeaderName {
#[inline]
fn as_ref(&self) -> &[u8] {
match &self.kind {
Kind::Standard(name) => name.as_ref(),
Kind::Cased(orig) => orig.as_ref(),
}
}
}
impl IntoOrigHeaderName for &'static str {
#[inline]
fn into_orig_header_name(self) -> OrigHeaderName {
Bytes::from_static(self.as_bytes()).into_orig_header_name()
}
}
impl IntoOrigHeaderName for String {
#[inline]
fn into_orig_header_name(self) -> OrigHeaderName {
Bytes::from(self).into_orig_header_name()
}
}
impl IntoOrigHeaderName for Bytes {
#[inline]
fn into_orig_header_name(self) -> OrigHeaderName {
OrigHeaderName {
kind: Kind::Cased(self),
}
}
}
impl IntoOrigHeaderName for &HeaderName {
#[inline]
fn into_orig_header_name(self) -> OrigHeaderName {
OrigHeaderName {
kind: Kind::Standard(self.clone()),
}
}
}
impl IntoOrigHeaderName for HeaderName {
#[inline]
fn into_orig_header_name(self) -> OrigHeaderName {
OrigHeaderName {
kind: Kind::Standard(self),
}
}
}
impl IntoOrigHeaderName for OrigHeaderName {
#[inline]
fn into_orig_header_name(self) -> OrigHeaderName {
self
}
}
impl IntoOrigHeaderName for &OrigHeaderName {
#[inline]
fn into_orig_header_name(self) -> OrigHeaderName {
self.clone()
}
}
}
mod sealed {
use bytes::Bytes;
use http::HeaderName;
use crate::header::OrigHeaderName;
pub trait Sealed {}
impl Sealed for &'static str {}
impl Sealed for String {}
impl Sealed for Bytes {}
impl Sealed for &HeaderName {}
impl Sealed for HeaderName {}
impl Sealed for &OrigHeaderName {}
impl Sealed for OrigHeaderName {}
}
#[cfg(test)]
mod test {
use http::{HeaderMap, HeaderName, HeaderValue};
use super::OrigHeaderMap;
fn collect_sorted_headers(
orig_headers: &OrigHeaderMap,
headers: &HeaderMap,
) -> Vec<(Vec<u8>, HeaderValue)> {
let mut ordered = Vec::new();
orig_headers.sort_headers_for_each(headers, |name, value| {
ordered.push((name.to_vec(), value.clone()));
});
ordered
}
#[inline]
pub(crate) fn get_all<'a>(
orig_headers: &'a OrigHeaderMap,
name: &HeaderName,
) -> impl Iterator<Item = impl AsRef<[u8]> + 'a> + 'a {
orig_headers.0.get_all(name).into_iter()
}
#[test]
fn test_header_order() {
let mut headers = OrigHeaderMap::new();
headers.insert("X-Test");
headers.insert("X-Another");
headers.insert("x-test2");
let mut iter = headers.iter();
assert_eq!(iter.next().unwrap().1.as_ref(), b"X-Test");
assert_eq!(iter.next().unwrap().1.as_ref(), b"X-Another");
assert_eq!(iter.next().unwrap().1.as_ref(), b"x-test2");
}
#[test]
fn test_extend_preserves_order() {
use super::OrigHeaderMap;
let mut map1 = OrigHeaderMap::new();
map1.insert("A-Header");
map1.insert("B-Header");
let mut map2 = OrigHeaderMap::new();
map2.insert("C-Header");
map2.insert("D-Header");
map1.extend(map2);
let names: Vec<_> = map1.iter().map(|(_, orig)| orig.as_ref()).collect();
assert_eq!(
names,
vec![b"A-Header", b"B-Header", b"C-Header", b"D-Header"]
);
}
#[test]
fn test_header_case() {
let mut headers = OrigHeaderMap::new();
headers.insert("X-Test");
headers.insert("x-test");
let all_x_test: Vec<_> = get_all(&headers, &"X-Test".parse().unwrap()).collect();
assert_eq!(all_x_test.len(), 2);
assert!(all_x_test.iter().any(|v| v.as_ref() == b"X-Test"));
assert!(all_x_test.iter().any(|v| v.as_ref() == b"x-test"));
}
#[test]
fn test_header_multiple_cases() {
let mut headers = OrigHeaderMap::new();
headers.insert("X-test");
headers.insert("x-test");
headers.insert("X-test");
let all_x_test: Vec<_> = get_all(&headers, &"x-test".parse().unwrap()).collect();
assert_eq!(all_x_test.len(), 3);
assert!(all_x_test.iter().any(|v| v.as_ref() == b"X-test"));
assert!(all_x_test.iter().any(|v| v.as_ref() == b"x-test"));
assert!(all_x_test.iter().any(|v| v.as_ref() == b"X-test"));
}
#[test]
fn test_sort_headers_preserves_multiple_cookie_values() {
let mut orig_headers = OrigHeaderMap::new();
orig_headers.insert("Cookie");
orig_headers.insert("User-Agent");
orig_headers.insert("Accept");
let mut headers = HeaderMap::new();
headers.append("cookie", HeaderValue::from_static("session=abc123"));
headers.append("cookie", HeaderValue::from_static("theme=dark"));
headers.append("cookie", HeaderValue::from_static("lang=en"));
headers.insert("user-agent", HeaderValue::from_static("Mozilla/5.0"));
headers.insert("accept", HeaderValue::from_static("text/html"));
headers.insert("host", HeaderValue::from_static("example.com"));
let original_cookies: Vec<_> = headers
.get_all("cookie")
.iter()
.map(|v| v.to_str().unwrap().to_string())
.collect();
orig_headers.sort_headers(&mut headers);
let sorted_cookies: Vec<_> = headers
.get_all("cookie")
.iter()
.map(|v| v.to_str().unwrap().to_string())
.collect();
assert_eq!(
original_cookies.len(),
sorted_cookies.len(),
"Cookie count should be preserved"
);
assert_eq!(original_cookies.len(), 3, "Should have 3 cookie values");
for original_cookie in &original_cookies {
assert!(
sorted_cookies.contains(original_cookie),
"Cookie '{original_cookie}' should be preserved"
);
}
let header_names: Vec<_> = headers.keys().collect();
assert_eq!(
header_names[0].as_str(),
"cookie",
"Cookie should be first header"
);
assert_eq!(
headers.len(),
6,
"Should have 6 total header values (3 cookies + 3 others)"
);
assert!(headers.contains_key("user-agent"));
assert!(headers.contains_key("accept"));
assert!(headers.contains_key("host"));
}
#[test]
fn test_sort_headers_multiple_values_different_headers() {
let mut orig_headers = OrigHeaderMap::new();
orig_headers.insert("Accept");
orig_headers.insert("Cookie");
let mut headers = HeaderMap::new();
headers.append("accept", HeaderValue::from_static("text/html"));
headers.append("accept", HeaderValue::from_static("application/json"));
headers.append("cookie", HeaderValue::from_static("a=1"));
headers.append("cookie", HeaderValue::from_static("b=2"));
headers.insert("host", HeaderValue::from_static("example.com"));
let total_before = headers.len();
orig_headers.sort_headers(&mut headers);
assert_eq!(
headers.len(),
total_before,
"Total header count should be preserved"
);
assert_eq!(
headers.get_all("accept").iter().count(),
2,
"Accept headers should be preserved"
);
assert_eq!(
headers.get_all("cookie").iter().count(),
2,
"Cookie headers should be preserved"
);
assert_eq!(
headers.get_all("host").iter().count(),
1,
"Host header should be preserved"
);
}
#[test]
fn test_sort_headers_for_each_preserves_effective_order_and_original_case() {
let mut orig_headers = OrigHeaderMap::new();
orig_headers.insert("X-Test");
orig_headers.insert("Accept");
let mut headers = HeaderMap::new();
headers.insert("user-agent", HeaderValue::from_static("agent/1.0"));
headers.append("x-test", HeaderValue::from_static("one"));
headers.append("x-test", HeaderValue::from_static("two"));
headers.insert("host", HeaderValue::from_static("example.com"));
headers.insert("accept", HeaderValue::from_static("application/json"));
let ordered = collect_sorted_headers(&orig_headers, &headers);
let rendered: Vec<_> = ordered
.into_iter()
.map(|(name, value)| (name, value.to_str().unwrap().to_owned()))
.collect();
assert_eq!(
rendered,
vec![
(b"X-Test".to_vec(), "one".to_owned()),
(b"X-Test".to_vec(), "two".to_owned()),
(b"Accept".to_vec(), "application/json".to_owned()),
(b"user-agent".to_vec(), "agent/1.0".to_owned()),
(b"host".to_vec(), "example.com".to_owned()),
]
);
}
#[test]
fn test_sort_headers_for_each_skips_duplicate_normalized_original_names() {
let mut orig_headers = OrigHeaderMap::new();
orig_headers.insert("X-Test");
orig_headers.insert("x-test");
orig_headers.insert("X-Test");
orig_headers.insert("Accept");
let mut headers = HeaderMap::new();
headers.append("x-test", HeaderValue::from_static("one"));
headers.append("x-test", HeaderValue::from_static("two"));
headers.insert("accept", HeaderValue::from_static("text/plain"));
let ordered = collect_sorted_headers(&orig_headers, &headers);
let rendered: Vec<_> = ordered
.into_iter()
.map(|(name, value)| (name, value.to_str().unwrap().to_owned()))
.collect();
assert_eq!(
rendered,
vec![
(b"X-Test".to_vec(), "one".to_owned()),
(b"X-Test".to_vec(), "two".to_owned()),
(b"Accept".to_vec(), "text/plain".to_owned()),
]
);
}
#[test]
fn test_sort_headers_for_each_does_not_mutate_source_headers() {
let mut orig_headers = OrigHeaderMap::new();
orig_headers.insert("X-Test");
orig_headers.insert("Accept");
let mut headers = HeaderMap::new();
headers.insert("user-agent", HeaderValue::from_static("agent/1.0"));
headers.append("x-test", HeaderValue::from_static("one"));
headers.append("x-test", HeaderValue::from_static("two"));
headers.insert("accept", HeaderValue::from_static("application/json"));
let expected = headers.clone();
let _ = collect_sorted_headers(&orig_headers, &headers);
assert_eq!(headers, expected);
}
}