trillium_http/headers/
unknown_header_name.rs1use super::{HeaderName, HeaderNameInner::UnknownHeader};
2use crate::compact_cow::CompactCow;
3use hashbrown::{Equivalent, HashMap, HashSet};
4use std::{
5 cell::RefCell,
6 cmp::Ordering,
7 fmt::{self, Debug, Display, Formatter},
8 hash::{Hash, Hasher},
9 ops::Deref,
10 sync::{OnceLock, RwLock},
11};
12
13#[derive(Clone)]
14pub(crate) struct UnknownHeaderName<'a>(CompactCow<'a>);
15
16impl UnknownHeaderName<'_> {
17 pub(crate) fn is_valid_lower(&self) -> bool {
18 !self.is_empty()
22 && self.chars().all(|c| {
23 matches!(c,
24 'a'..='z'
25 | '0'..='9'
26 | '!'
27 | '#'
28 | '$'
29 | '%'
30 | '&'
31 | '\''
32 | '*'
33 | '+'
34 | '-'
35 | '.'
36 | '^'
37 | '_'
38 | '`'
39 | '|'
40 | '~',
41 )
42 })
43 }
44
45 pub(crate) fn into_lower(self) -> Self {
46 match self.0 {
47 CompactCow::Borrowed(borrowed) => {
48 if let Some(first_upper) = borrowed.chars().position(|c| c.is_ascii_uppercase()) {
49 Self(CompactCow::Owned(
50 borrowed[..first_upper]
51 .chars()
52 .chain(
53 borrowed[first_upper..]
54 .chars()
55 .map(|c| c.to_ascii_lowercase()),
56 )
57 .collect(),
58 ))
59 } else {
60 Self(CompactCow::Borrowed(borrowed))
61 }
62 }
63 CompactCow::Owned(mut compact_string) => {
64 compact_string.make_ascii_lowercase();
65 Self(CompactCow::Owned(compact_string))
66 }
67 CompactCow::Shared(shared) => {
68 if shared.bytes().any(|b| b.is_ascii_uppercase()) {
69 Self(CompactCow::Owned(shared.to_ascii_lowercase().into()))
70 } else {
71 Self(CompactCow::Shared(shared))
72 }
73 }
74 }
75 }
76}
77
78impl PartialOrd for UnknownHeaderName<'_> {
79 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
80 Some(self.cmp(other))
81 }
82}
83
84impl Ord for UnknownHeaderName<'_> {
85 fn cmp(&self, other: &Self) -> Ordering {
86 self.0.cmp(&*other.0)
87 }
88}
89
90impl PartialEq for UnknownHeaderName<'_> {
91 fn eq(&self, other: &Self) -> bool {
92 self.0.eq_ignore_ascii_case(&other.0)
93 }
94}
95
96impl Eq for UnknownHeaderName<'_> {}
97
98impl Hash for UnknownHeaderName<'_> {
99 fn hash<H: Hasher>(&self, state: &mut H) {
100 for c in self.0.as_bytes() {
101 c.to_ascii_lowercase().hash(state);
102 }
103 }
104}
105
106impl Debug for UnknownHeaderName<'_> {
107 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
108 Debug::fmt(&self.0, f)
109 }
110}
111
112impl Display for UnknownHeaderName<'_> {
113 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
114 Display::fmt(&self.0, f)
115 }
116}
117
118impl<'a> From<UnknownHeaderName<'a>> for HeaderName<'a> {
119 fn from(value: UnknownHeaderName<'a>) -> Self {
120 HeaderName(UnknownHeader(value))
121 }
122}
123
124impl<'a> From<&'a UnknownHeaderName<'_>> for HeaderName<'a> {
125 fn from(value: &'a UnknownHeaderName<'_>) -> Self {
126 HeaderName(UnknownHeader(value.reborrow()))
127 }
128}
129
130fn is_tchar(c: char) -> bool {
131 matches!(
132 c,
133 'a'..='z'
134 | 'A'..='Z'
135 | '0'..='9'
136 | '!'
137 | '#'
138 | '$'
139 | '%'
140 | '&'
141 | '\''
142 | '*'
143 | '+'
144 | '-'
145 | '.'
146 | '^'
147 | '_'
148 | '`'
149 | '|'
150 | '~'
151 )
152}
153
154impl UnknownHeaderName<'_> {
155 pub(crate) fn is_valid(&self) -> bool {
156 !self.is_empty() && self.0.chars().all(is_tchar)
157 }
158
159 pub(crate) fn into_owned(self) -> UnknownHeaderName<'static> {
160 UnknownHeaderName(self.0.into_owned())
161 }
162}
163
164impl<'a> UnknownHeaderName<'a> {
165 pub(crate) fn reborrow<'b: 'a>(&'b self) -> UnknownHeaderName<'b> {
166 Self(CompactCow::Borrowed(&self.0))
167 }
168}
169
170impl From<String> for UnknownHeaderName<'static> {
171 fn from(value: String) -> Self {
172 Self(value.into())
173 }
174}
175
176impl<'a> From<&'a str> for UnknownHeaderName<'a> {
177 fn from(value: &'a str) -> Self {
178 Self(value.into())
179 }
180}
181
182impl<'a> From<CompactCow<'a>> for UnknownHeaderName<'a> {
183 fn from(value: CompactCow<'a>) -> Self {
184 Self(value)
185 }
186}
187
188impl<'a> From<UnknownHeaderName<'a>> for CompactCow<'a> {
189 fn from(value: UnknownHeaderName<'a>) -> Self {
190 value.0
191 }
192}
193
194impl Deref for UnknownHeaderName<'_> {
195 type Target = str;
196
197 fn deref(&self) -> &Self::Target {
198 &self.0
199 }
200}
201
202impl Equivalent<UnknownHeaderName<'_>> for &UnknownHeaderName<'_> {
203 fn equivalent(&self, key: &UnknownHeaderName<'_>) -> bool {
204 key.eq_ignore_ascii_case(self)
205 }
206}
207
208static LOWER_INTERN: OnceLock<RwLock<HashSet<InternKey>>> = OnceLock::new();
221
222#[derive(Copy, Clone, Eq)]
227struct InternKey(&'static str);
228
229impl PartialEq for InternKey {
230 fn eq(&self, other: &Self) -> bool {
231 self.0.eq_ignore_ascii_case(other.0)
232 }
233}
234
235impl Hash for InternKey {
236 fn hash<H: Hasher>(&self, state: &mut H) {
237 for b in self.0.bytes() {
238 state.write_u8(b.to_ascii_lowercase());
239 }
240 }
241}
242
243fn intern_table() -> &'static RwLock<HashSet<InternKey>> {
244 LOWER_INTERN.get_or_init(|| RwLock::new(HashSet::new()))
245}
246
247thread_local! {
248 static LOCAL_INTERN: RefCell<HashMap<(usize, usize), &'static str>> =
252 RefCell::new(HashMap::new());
253}
254
255fn intern_lowercase(s: &'static str) -> &'static str {
266 if !s.bytes().any(|b| b.is_ascii_uppercase()) {
267 return s;
268 }
269 let key = (s.as_ptr().addr(), s.len());
270 if let Some(interned) = LOCAL_INTERN.with_borrow(|local| local.get(&key).copied()) {
271 return interned;
272 }
273 let interned = intern_lowercase_shared(s);
274 LOCAL_INTERN.with_borrow_mut(|local| local.insert(key, interned));
275 interned
276}
277
278fn intern_lowercase_shared(s: &'static str) -> &'static str {
279 let probe = InternKey(s);
280 let table = intern_table();
281 {
282 let read = table.read().expect("intern table poisoned");
283 if let Some(hit) = read.get(&probe) {
284 return hit.0;
285 }
286 }
287 let mut write = table.write().expect("intern table poisoned");
291 if let Some(hit) = write.get(&probe) {
292 return hit.0;
293 }
294 let lowered: String = s.chars().map(|c| c.to_ascii_lowercase()).collect();
295 let leaked: &'static str = Box::leak(lowered.into_boxed_str());
296 write.insert(InternKey(leaked));
297 leaked
298}
299
300impl UnknownHeaderName<'static> {
301 pub(crate) fn as_static_str(&self) -> Option<&'static str> {
305 match self.0 {
306 CompactCow::Borrowed(s) => Some(s),
307 CompactCow::Owned(_) | CompactCow::Shared(_) => None,
308 }
309 }
310
311 pub(crate) fn into_lower_static(self) -> Self {
320 match self.0 {
321 CompactCow::Borrowed(s) => {
322 if s.bytes().any(|b| b.is_ascii_uppercase()) {
323 Self(CompactCow::Borrowed(intern_lowercase(s)))
324 } else {
325 Self(CompactCow::Borrowed(s))
326 }
327 }
328 CompactCow::Owned(_) | CompactCow::Shared(_) => self.into_lower(),
329 }
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336
337 fn ensure_interned(s: &'static str) -> &'static str {
338 intern_lowercase(s)
339 }
340
341 #[test]
342 fn interned_names_are_canonical_across_threads_and_addresses() {
343 let here = ensure_interned("X-Thread-Front");
344 let there = std::thread::spawn(|| ensure_interned("X-Thread-Front"))
345 .join()
346 .unwrap();
347 let elsewhere = ensure_interned(String::from("X-THREAD-FRONT").leak());
348 assert_eq!(here, "x-thread-front");
349 assert_eq!(here.as_ptr(), there.as_ptr());
350 assert_eq!(here.as_ptr(), elsewhere.as_ptr());
351 }
352
353 #[test]
354 fn intern_idempotent() {
355 let a = ensure_interned("X-Idempotent-Header");
356 let b = ensure_interned("X-Idempotent-Header");
357 assert_eq!(a, "x-idempotent-header");
358 assert!(
359 std::ptr::eq(a, b),
360 "intern must return identical &'static str on repeat uppercase input",
361 );
362 }
363
364 #[test]
365 fn intern_lowercase_input_is_passthrough() {
366 let original: &'static str = "x-already-lowercase";
369 let got = ensure_interned(original);
370 assert!(
371 std::ptr::eq(got, original),
372 "pure-lowercase literal should bypass interning entirely",
373 );
374 }
375
376 #[test]
377 fn intern_uppercase_then_lowercase_content_equal() {
378 let upper = ensure_interned("X-Cross-Casing-Header");
379 let lower = ensure_interned("x-cross-casing-header");
380 assert_eq!(upper, lower);
381 }
383
384 #[test]
385 fn intern_case_insensitive_hash_collapses_uppercase_variants() {
386 let a = ensure_interned("X-Mixed-Casing");
389 let b = ensure_interned("x-MIXED-casing");
390 assert!(
391 std::ptr::eq(a, b),
392 "case-insensitive hash must collapse uppercase variants",
393 );
394 }
395
396 #[test]
397 fn into_lower_static_borrowed_uppercase() {
398 let n = UnknownHeaderName(CompactCow::Borrowed("X-Static-Upper")).into_lower_static();
399 assert_eq!(n.as_static_str(), Some("x-static-upper"));
400 }
401
402 #[test]
403 fn into_lower_static_borrowed_lowercase_passthrough() {
404 let original: &'static str = "x-static-lower";
405 let n = UnknownHeaderName(CompactCow::Borrowed(original)).into_lower_static();
406 let got = n.as_static_str().unwrap();
407 assert!(
408 std::ptr::eq(got, original),
409 "already-lowercase literal should pass through without interning",
410 );
411 }
412
413 #[test]
414 fn into_lower_static_owned_stays_owned() {
415 let owned = UnknownHeaderName::from(String::from("X-Owned-Upper"));
416 let lowered = owned.into_lower_static();
417 assert_eq!(lowered.as_static_str(), None);
418 assert_eq!(&*lowered, "x-owned-upper");
419 }
420}