Skip to content

Commit ec2522f

Browse files
authored
Merge branch 'master' into const-statuscode-from_u16
2 parents 22ad62f + 439d1c5 commit ec2522f

8 files changed

Lines changed: 151 additions & 77 deletions

File tree

.github/FUNDING.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github: seanmonstar

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ itoa = "1"
4343

4444
[dev-dependencies]
4545
quickcheck = "1"
46-
rand = "0.8.0"
46+
rand = "0.9.1"
4747
serde = "1.0"
4848
serde_json = "1.0"
4949
doc-comment = "0.3"

src/extensions.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::any::{Any, TypeId};
1+
use std::any::{type_name, Any, TypeId};
22
use std::collections::HashMap;
33
use std::fmt;
44
use std::hash::{BuildHasherDefault, Hasher};
@@ -267,7 +267,21 @@ impl Extensions {
267267

268268
impl fmt::Debug for Extensions {
269269
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270-
f.debug_struct("Extensions").finish()
270+
struct TypeName(&'static str);
271+
impl fmt::Debug for TypeName {
272+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273+
f.write_str(self.0)
274+
}
275+
}
276+
277+
let mut set = f.debug_set();
278+
if let Some(map) = &self.map {
279+
set.entries(
280+
map.values()
281+
.map(|any_clone| TypeName(any_clone.as_ref().type_name())),
282+
);
283+
}
284+
set.finish()
271285
}
272286
}
273287

@@ -276,6 +290,7 @@ trait AnyClone: Any {
276290
fn as_any(&self) -> &dyn Any;
277291
fn as_any_mut(&mut self) -> &mut dyn Any;
278292
fn into_any(self: Box<Self>) -> Box<dyn Any>;
293+
fn type_name(&self) -> &'static str;
279294
}
280295

281296
impl<T: Clone + Send + Sync + 'static> AnyClone for T {
@@ -294,6 +309,10 @@ impl<T: Clone + Send + Sync + 'static> AnyClone for T {
294309
fn into_any(self: Box<Self>) -> Box<dyn Any> {
295310
self
296311
}
312+
313+
fn type_name(&self) -> &'static str {
314+
type_name::<T>()
315+
}
297316
}
298317

299318
impl Clone for Box<dyn AnyClone + Send + Sync> {
@@ -308,13 +327,23 @@ fn test_extensions() {
308327
struct MyType(i32);
309328

310329
let mut extensions = Extensions::new();
330+
assert_eq!(format!("{extensions:?}"), "{}");
311331

312332
extensions.insert(5i32);
313333
extensions.insert(MyType(10));
314334

315335
assert_eq!(extensions.get(), Some(&5i32));
316336
assert_eq!(extensions.get_mut(), Some(&mut 5i32));
317337

338+
let dbg = format!("{extensions:?}");
339+
// map order is NOT deterministic
340+
assert!(
341+
(dbg == "{http::extensions::test_extensions::MyType, i32}")
342+
|| (dbg == "{i32, http::extensions::test_extensions::MyType}"),
343+
"{}",
344+
dbg
345+
);
346+
318347
let ext2 = extensions.clone();
319348

320349
assert_eq!(extensions.remove::<i32>(), Some(5i32));

src/header/map.rs

Lines changed: 59 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,47 @@ use super::HeaderValue;
1414
pub use self::as_header_name::AsHeaderName;
1515
pub use self::into_header_name::IntoHeaderName;
1616

17-
/// A set of HTTP headers
17+
/// A specialized [multimap](<https://en.wikipedia.org/wiki/Multimap>) for
18+
/// header names and values.
1819
///
19-
/// `HeaderMap` is a multimap of [`HeaderName`] to values.
20+
/// # Overview
21+
///
22+
/// `HeaderMap` is designed specifically for efficient manipulation of HTTP
23+
/// headers. It supports multiple values per header name and provides
24+
/// specialized APIs for insertion, retrieval, and iteration.
25+
///
26+
/// The internal implementation is optimized for common usage patterns in HTTP,
27+
/// and may change across versions. For example, the current implementation uses
28+
/// [Robin Hood
29+
/// hashing](<https://en.wikipedia.org/wiki/Hash_table#Robin_Hood_hashing>) to
30+
/// store entries compactly and enable high load factors with good performance.
31+
/// However, the collision resolution strategy and storage mechanism are not
32+
/// part of the public API and may be altered in future releases.
33+
///
34+
/// # Iteration order
35+
///
36+
/// Unless otherwise specified, the order in which items are returned by
37+
/// iterators from `HeaderMap` methods is arbitrary; there is no guaranteed
38+
/// ordering among the elements yielded by such an iterator. Changes to the
39+
/// iteration order are not considered breaking changes, so users must not rely
40+
/// on any incidental order produced by such an iterator. However, for a given
41+
/// crate version, the iteration order will be consistent across all platforms.
42+
///
43+
/// # Adaptive hashing
44+
///
45+
/// `HeaderMap` uses an adaptive strategy for hashing to maintain fast lookups
46+
/// while resisting hash collision attacks. The default hash function
47+
/// prioritizes performance. In scenarios where high collision rates are
48+
/// detected—typically indicative of denial-of-service attacks—the
49+
/// implementation switches to a more secure, collision-resistant hash function.
50+
///
51+
/// # Limitations
52+
///
53+
/// A `HeaderMap` can store at most 32,768 entries \(header name/value pairs\).
54+
/// Attempting to exceed this limit will result in a panic.
2055
///
2156
/// [`HeaderName`]: struct.HeaderName.html
57+
/// [`HeaderMap`]: struct.HeaderMap.html
2258
///
2359
/// # Examples
2460
///
@@ -445,8 +481,21 @@ impl HeaderMap {
445481
/// assert!(map.is_empty());
446482
/// assert_eq!(0, map.capacity());
447483
/// ```
484+
#[inline]
448485
pub fn new() -> Self {
449-
HeaderMap::try_with_capacity(0).unwrap()
486+
Self::default()
487+
}
488+
}
489+
490+
impl<T> Default for HeaderMap<T> {
491+
fn default() -> Self {
492+
HeaderMap {
493+
mask: 0,
494+
indices: Box::new([]), // as a ZST, this doesn't actually allocate anything
495+
entries: Vec::new(),
496+
extra_values: Vec::new(),
497+
danger: Danger::Green,
498+
}
450499
}
451500
}
452501

@@ -501,13 +550,7 @@ impl<T> HeaderMap<T> {
501550
/// ```
502551
pub fn try_with_capacity(capacity: usize) -> Result<HeaderMap<T>, MaxSizeReached> {
503552
if capacity == 0 {
504-
Ok(HeaderMap {
505-
mask: 0,
506-
indices: Box::new([]), // as a ZST, this doesn't actually allocate anything
507-
entries: Vec::new(),
508-
extra_values: Vec::new(),
509-
danger: Danger::Green,
510-
})
553+
Ok(Self::default())
511554
} else {
512555
let raw_cap = match to_raw_capacity(capacity).checked_next_power_of_two() {
513556
Some(c) => c,
@@ -2164,12 +2207,6 @@ impl<T: fmt::Debug> fmt::Debug for HeaderMap<T> {
21642207
}
21652208
}
21662209

2167-
impl<T> Default for HeaderMap<T> {
2168-
fn default() -> Self {
2169-
HeaderMap::try_with_capacity(0).expect("zero capacity should never fail")
2170-
}
2171-
}
2172-
21732210
impl<K, T> ops::Index<K> for HeaderMap<T>
21742211
where
21752212
K: AsHeaderName,
@@ -2315,15 +2352,15 @@ impl<'a, T> IterMut<'a, T> {
23152352
self.cursor = Some(Cursor::Head);
23162353
}
23172354

2318-
let entry = unsafe { &mut (*self.map).entries[self.entry] };
2355+
let entry = &mut unsafe { &mut *self.map }.entries[self.entry];
23192356

23202357
match self.cursor.unwrap() {
23212358
Head => {
23222359
self.cursor = entry.links.map(|l| Values(l.next));
23232360
Some((&entry.key, &mut entry.value as *mut _))
23242361
}
23252362
Values(idx) => {
2326-
let extra = unsafe { &mut (*self.map).extra_values[idx] };
2363+
let extra = &mut unsafe { &mut (*self.map) }.extra_values[idx];
23272364

23282365
match extra.next {
23292366
Link::Entry(_) => self.cursor = None,
@@ -2963,7 +3000,7 @@ impl<'a, T: 'a> Iterator for ValueIterMut<'a, T> {
29633000
fn next(&mut self) -> Option<Self::Item> {
29643001
use self::Cursor::*;
29653002

2966-
let entry = unsafe { &mut (*self.map).entries[self.index] };
3003+
let entry = &mut unsafe { &mut *self.map }.entries[self.index];
29673004

29683005
match self.front {
29693006
Some(Head) => {
@@ -2983,7 +3020,7 @@ impl<'a, T: 'a> Iterator for ValueIterMut<'a, T> {
29833020
Some(&mut entry.value)
29843021
}
29853022
Some(Values(idx)) => {
2986-
let extra = unsafe { &mut (*self.map).extra_values[idx] };
3023+
let extra = &mut unsafe { &mut *self.map }.extra_values[idx];
29873024

29883025
if self.front == self.back {
29893026
self.front = None;
@@ -3006,7 +3043,7 @@ impl<'a, T: 'a> DoubleEndedIterator for ValueIterMut<'a, T> {
30063043
fn next_back(&mut self) -> Option<Self::Item> {
30073044
use self::Cursor::*;
30083045

3009-
let entry = unsafe { &mut (*self.map).entries[self.index] };
3046+
let entry = &mut unsafe { &mut *self.map }.entries[self.index];
30103047

30113048
match self.back {
30123049
Some(Head) => {
@@ -3015,7 +3052,7 @@ impl<'a, T: 'a> DoubleEndedIterator for ValueIterMut<'a, T> {
30153052
Some(&mut entry.value)
30163053
}
30173054
Some(Values(idx)) => {
3018-
let extra = unsafe { &mut (*self.map).extra_values[idx] };
3055+
let extra = &mut unsafe { &mut *self.map }.extra_values[idx];
30193056

30203057
if self.front == self.back {
30213058
self.front = None;

src/header/mod.rs

Lines changed: 6 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -28,47 +28,13 @@
2828
//!
2929
//! # `HeaderMap`
3030
//!
31-
//! `HeaderMap` is a map structure of header names highly optimized for use
32-
//! cases common with HTTP. It is a [multimap] structure, where each header name
33-
//! may have multiple associated header values. Given this, some of the APIs
34-
//! diverge from [`HashMap`].
31+
//! The [`HeaderMap`] type is a specialized
32+
//! [multimap](<https://en.wikipedia.org/wiki/Multimap>) structure for storing
33+
//! header names and values. It is designed specifically for efficient
34+
//! manipulation of HTTP headers. It supports multiple values per header name
35+
//! and provides specialized APIs for insertion, retrieval, and iteration.
3536
//!
36-
//! ## Overview
37-
//!
38-
//! Just like `HashMap` in Rust's stdlib, `HeaderMap` is based on [Robin Hood
39-
//! hashing]. This algorithm tends to reduce the worst case search times in the
40-
//! table and enables high load factors without seriously affecting performance.
41-
//! Internally, keys and values are stored in vectors. As such, each insertion
42-
//! will not incur allocation overhead. However, once the underlying vector
43-
//! storage is full, a larger vector must be allocated and all values copied.
44-
//!
45-
//! ## Deterministic ordering
46-
//!
47-
//! Unlike Rust's `HashMap`, values in `HeaderMap` are deterministically
48-
//! ordered. Roughly, values are ordered by insertion. This means that a
49-
//! function that deterministically operates on a header map can rely on the
50-
//! iteration order to remain consistent across processes and platforms.
51-
//!
52-
//! ## Adaptive hashing
53-
//!
54-
//! `HeaderMap` uses an adaptive hashing strategy in order to efficiently handle
55-
//! most common cases. All standard headers have statically computed hash values
56-
//! which removes the need to perform any hashing of these headers at runtime.
57-
//! The default hash function emphasizes performance over robustness. However,
58-
//! `HeaderMap` detects high collision rates and switches to a secure hash
59-
//! function in those events. The threshold is set such that only denial of
60-
//! service attacks should trigger it.
61-
//!
62-
//! ## Limitations
63-
//!
64-
//! `HeaderMap` can store a maximum of 32,768 headers (header name / value
65-
//! pairs). Attempting to insert more will result in a panic.
66-
//!
67-
//! [`HeaderName`]: struct.HeaderName.html
68-
//! [`HeaderMap`]: struct.HeaderMap.html
69-
//! [multimap]: https://en.wikipedia.org/wiki/Multimap
70-
//! [`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
71-
//! [Robin Hood hashing]: https://en.wikipedia.org/wiki/Hash_table#Robin_Hood_hashing
37+
//! [*See also the `HeaderMap` type.*](HeaderMap)
7238
7339
mod map;
7440
mod name;

src/version.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,14 @@ impl Version {
4343
}
4444

4545
#[derive(PartialEq, PartialOrd, Copy, Clone, Eq, Ord, Hash)]
46+
#[non_exhaustive]
4647
enum Http {
4748
Http09,
4849
Http10,
4950
Http11,
5051
H2,
5152
H3,
52-
__NonExhaustive,
53+
//__NonExhaustive,
5354
}
5455

5556
impl Default for Version {
@@ -69,7 +70,7 @@ impl fmt::Debug for Version {
6970
Http11 => "HTTP/1.1",
7071
H2 => "HTTP/2.0",
7172
H3 => "HTTP/3.0",
72-
__NonExhaustive => unreachable!(),
73+
//__NonExhaustive => unreachable!(),
7374
})
7475
}
7576
}

tests/header_map_fuzz.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use http::header::*;
22
use http::*;
33

44
use quickcheck::{Arbitrary, Gen, QuickCheck, TestResult};
5+
use rand::prelude::IndexedRandom;
56
use rand::rngs::StdRng;
6-
use rand::seq::SliceRandom;
77
use rand::{Rng, SeedableRng};
88

99
use std::collections::HashMap;
@@ -76,12 +76,12 @@ impl Fuzz {
7676

7777
let mut steps = vec![];
7878
let mut expect = AltMap::default();
79-
let num = rng.gen_range(5..500);
79+
let num = rng.random_range(5..500);
8080

8181
let weight = Weight {
82-
insert: rng.gen_range(1..10),
83-
remove: rng.gen_range(1..10),
84-
append: rng.gen_range(1..10),
82+
insert: rng.random_range(1..10),
83+
remove: rng.random_range(1..10),
84+
append: rng.random_range(1..10),
8585
};
8686

8787
while steps.len() < num {
@@ -112,7 +112,7 @@ impl Fuzz {
112112

113113
impl Arbitrary for Fuzz {
114114
fn arbitrary(_: &mut Gen) -> Self {
115-
Self::new(rand::thread_rng().gen())
115+
Self::new(rand::rng().random())
116116
}
117117
}
118118

@@ -130,7 +130,7 @@ impl AltMap {
130130
fn gen_action(&mut self, weight: &Weight, rng: &mut StdRng) -> Action {
131131
let sum = weight.insert + weight.remove + weight.append;
132132

133-
let mut num = rng.gen_range(0..sum);
133+
let mut num = rng.random_range(0..sum);
134134

135135
if num < weight.insert {
136136
return self.gen_insert(rng);
@@ -180,7 +180,7 @@ impl AltMap {
180180

181181
/// Negative numbers weigh finding an existing header higher
182182
fn gen_name(&self, weight: i32, rng: &mut StdRng) -> HeaderName {
183-
let mut existing = rng.gen_ratio(1, weight.abs() as u32);
183+
let mut existing = rng.random_ratio(1, weight.abs() as u32);
184184

185185
if weight < 0 {
186186
existing = !existing;
@@ -202,7 +202,7 @@ impl AltMap {
202202
if self.map.is_empty() {
203203
None
204204
} else {
205-
let n = rng.gen_range(0..self.map.len());
205+
let n = rng.random_range(0..self.map.len());
206206
self.map.keys().nth(n).map(Clone::clone)
207207
}
208208
}
@@ -337,7 +337,7 @@ fn gen_header_name(g: &mut StdRng) -> HeaderName {
337337
header::X_XSS_PROTECTION,
338338
];
339339

340-
if g.gen_ratio(1, 2) {
340+
if g.random_ratio(1, 2) {
341341
STANDARD_HEADERS.choose(g).unwrap().clone()
342342
} else {
343343
let value = gen_string(g, 1, 25);

0 commit comments

Comments
 (0)