1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use std::fmt;
use crate::error::{Error, ErrorKind, Result};
/// A validated, lowercase HTTP header name.
///
/// Header names are ASCII-only, case-insensitive, and stored in lowercase.
/// Only alphanumeric characters and hyphens (`-`) are permitted.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct HeaderName(String);
/// A validated HTTP header value.
///
/// The value may contain any UTF-8 characters except carriage return (`\r`)
/// and newline (`\n`), which are rejected to prevent header-injection attacks.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HeaderValue(String);
/// An ordered, multi-valued HTTP header map.
///
/// Insertion order is preserved. The same header name may appear multiple
/// times (e.g. `Set-Cookie`). [`HeaderMap::insert`] replaces any existing
/// entry with the same name; [`HeaderMap::append`] adds a second entry.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct HeaderMap(Vec<(HeaderName, HeaderValue)>);
impl HeaderName {
/// Parse and validate a header name.
///
/// Returns an error if `name` is empty or contains characters outside the
/// set `[A-Za-z0-9-]`. The stored name is lowercased.
pub fn new(name: impl AsRef<str>) -> Result<Self> {
let name = name.as_ref().trim();
if name.is_empty() {
return Err(Error::new(
ErrorKind::InvalidHeaderName,
"header name is empty",
));
}
if !name
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-'))
{
return Err(Error::new(
ErrorKind::InvalidHeaderName,
format!("invalid header name: {name}"),
));
}
Ok(Self(name.to_ascii_lowercase()))
}
/// Return the header name as a lowercase `&str`.
pub fn as_str(&self) -> &str {
&self.0
}
}
impl HeaderValue {
/// Parse and validate a header value.
///
/// Returns an error if `value` contains `\r` or `\n`.
pub fn new(value: impl AsRef<str>) -> Result<Self> {
let value = value.as_ref();
if value.contains(['\r', '\n']) {
return Err(Error::new(
ErrorKind::InvalidHeaderValue,
"header value contains a newline",
));
}
Ok(Self(value.to_owned()))
}
/// Return the header value as a `&str`.
pub fn as_str(&self) -> &str {
&self.0
}
}
impl HeaderMap {
/// Create an empty header map.
pub fn new() -> Self {
Self::default()
}
/// Insert or replace a header.
///
/// Any existing entries with the same name are removed before inserting
/// the new value.
pub fn insert(&mut self, name: impl AsRef<str>, value: impl AsRef<str>) -> Result<()> {
let name = HeaderName::new(name)?;
let value = HeaderValue::new(value)?;
self.0.retain(|(existing, _)| existing != &name);
self.0.push((name, value));
Ok(())
}
/// Append a header without removing existing entries with the same name.
pub fn append(&mut self, name: impl AsRef<str>, value: impl AsRef<str>) -> Result<()> {
self.0
.push((HeaderName::new(name)?, HeaderValue::new(value)?));
Ok(())
}
/// Return the value of the first header with the given name, if any.
pub fn get(&self, name: &str) -> Option<&str> {
let name = HeaderName::new(name).ok()?;
self.0
.iter()
.find(|(existing, _)| existing == &name)
.map(|(_, value)| value.as_str())
}
/// Return all values for the given header name.
pub fn get_all(&self, name: &str) -> Vec<&str> {
let Ok(name) = HeaderName::new(name) else {
return Vec::new();
};
self.0
.iter()
.filter(|(existing, _)| existing == &name)
.map(|(_, value)| value.as_str())
.collect()
}
/// Iterate over all `(name, value)` pairs in insertion order.
pub fn iter(&self) -> impl Iterator<Item = (&HeaderName, &HeaderValue)> {
self.0.iter().map(|(name, value)| (name, value))
}
/// Remove all entries with the given name.
pub fn remove(&mut self, name: &str) {
if let Ok(name) = HeaderName::new(name) {
self.0.retain(|(existing, _)| existing != &name);
}
}
}
impl fmt::Display for HeaderName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl fmt::Display for HeaderValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}