1use std::borrow::Cow;
6
7#[derive(Debug, Clone, PartialEq, PartialOrd, Hash, Eq, Ord)]
13pub struct HeaderName(pub(super) Cow<'static, str>);
14
15#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
21#[error("Invalid header name: {0:?}")]
22pub struct InvalidHeaderName(Cow<'static, str>);
23
24impl From<&'static str> for HeaderName {
25 fn from(s: &'static str) -> HeaderName {
26 match HeaderName::new(s) {
27 Ok(v) => v,
28 Err(e) => {
29 panic!("Illegal locally specified header {}", e);
30 }
31 }
32 }
33}
34
35impl From<String> for HeaderName {
36 fn from(s: String) -> HeaderName {
37 match HeaderName::new(s) {
38 Ok(v) => v,
39 Err(e) => {
40 panic!("Illegal locally specified header {}", e);
41 }
42 }
43 }
44}
45
46impl From<Cow<'static, str>> for HeaderName {
47 fn from(s: Cow<'static, str>) -> HeaderName {
48 match HeaderName::new(s) {
49 Ok(v) => v,
50 Err(e) => {
51 panic!("Illegal locally specified header {}", e);
52 }
53 }
54 }
55}
56
57impl InvalidHeaderName {
58 pub fn name(&self) -> &str {
59 &self.0[..]
60 }
61}
62
63fn validate_header(mut name: Cow<'static, str>) -> Result<HeaderName, InvalidHeaderName> {
64 if name.is_empty() {
65 return Err(invalid_header_name(name));
66 }
67 let mut need_lower_case = false;
68 for b in name.bytes() {
69 let validity = VALID_HEADER_LUT[b as usize];
70 if validity == 0 {
71 return Err(invalid_header_name(name));
72 }
73 if validity == 2 {
74 need_lower_case = true;
75 }
76 }
77 if need_lower_case {
78 name.to_mut().make_ascii_lowercase();
80 }
81 Ok(HeaderName(name))
82}
83
84impl HeaderName {
85 #[inline]
90 pub fn new<S: Into<Cow<'static, str>>>(s: S) -> Result<Self, InvalidHeaderName> {
91 validate_header(s.into())
92 }
93
94 #[inline]
95 pub fn as_str(&self) -> &str {
96 &self.0[..]
97 }
98}
99
100impl std::fmt::Display for HeaderName {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 f.write_str(self.as_str())
103 }
104}
105
106#[cold]
108#[inline(never)]
109fn invalid_header_name(s: Cow<'static, str>) -> InvalidHeaderName {
110 crate::warn!("Invalid header name: {}", s);
111 InvalidHeaderName(s)
112}
113static VALID_HEADER_LUT: [u8; 256] = [
119 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
120 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
121 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 1, 1,
122 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0,
123 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
124 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127];
128
129impl std::ops::Deref for HeaderName {
130 type Target = str;
131 #[inline]
132 fn deref(&self) -> &str {
133 self.as_str()
134 }
135}
136
137impl AsRef<str> for HeaderName {
138 #[inline]
139 fn as_ref(&self) -> &str {
140 self.as_str()
141 }
142}
143
144impl AsRef<[u8]> for HeaderName {
145 #[inline]
146 fn as_ref(&self) -> &[u8] {
147 self.as_str().as_bytes()
148 }
149}
150
151impl From<HeaderName> for String {
152 #[inline]
153 fn from(h: HeaderName) -> Self {
154 h.0.into()
155 }
156}
157
158impl From<HeaderName> for Cow<'static, str> {
159 #[inline]
160 fn from(h: HeaderName) -> Self {
161 h.0
162 }
163}
164
165impl From<HeaderName> for Vec<u8> {
166 #[inline]
167 fn from(h: HeaderName) -> Self {
168 String::from(h.0).into()
169 }
170}
171
172macro_rules! partialeq_boilerplate {
173 ($T0:ty, $T1:ty) => {
174 #[allow(clippy::extra_unused_lifetimes)]
176 impl<'a> PartialEq<$T0> for $T1 {
177 fn eq(&self, other: &$T0) -> bool {
178 (&*self).eq_ignore_ascii_case(&*other)
180 }
181 }
182 #[allow(clippy::extra_unused_lifetimes)]
183 impl<'a> PartialEq<$T1> for $T0 {
184 fn eq(&self, other: &$T1) -> bool {
185 PartialEq::eq(other, self)
186 }
187 }
188 };
189}
190
191partialeq_boilerplate!(HeaderName, str);
192partialeq_boilerplate!(HeaderName, &'a str);
193partialeq_boilerplate!(HeaderName, String);
194partialeq_boilerplate!(HeaderName, &'a String);
195partialeq_boilerplate!(HeaderName, Cow<'a, str>);
196
197#[cfg(test)]
198mod test {
199 use super::*;
200
201 #[test]
202 fn test_lut() {
203 let mut expect = [0u8; 256];
204 for b in b'0'..=b'9' {
205 expect[b as usize] = 1;
206 }
207 for b in b'a'..=b'z' {
208 expect[b as usize] = 1;
209 }
210 for b in b'A'..=b'Z' {
211 expect[b as usize] = 2;
212 }
213 for b in b"!#$%&'*+-.^_`|~" {
214 expect[*b as usize] = 1;
215 }
216 assert_eq!(&VALID_HEADER_LUT[..], &expect[..]);
217 }
218 #[test]
219 fn test_validate() {
220 assert!(validate_header("".into()).is_err());
221 assert!(validate_header(" foo ".into()).is_err());
222 assert!(validate_header("a=b".into()).is_err());
223 assert_eq!(
224 validate_header("content-type".into()),
225 Ok(HeaderName("content-type".into()))
226 );
227 assert_eq!(
228 validate_header("Content-Type".into()),
229 Ok(HeaderName("content-type".into()))
230 );
231 }
232}