1 use http::header::*;
2 use http::*;
3
4 use quickcheck::{Arbitrary, Gen, QuickCheck, TestResult};
5 use rand::rngs::StdRng;
6 use rand::seq::SliceRandom;
7 use rand::{Rng, SeedableRng};
8
9 use std::collections::HashMap;
10
11 #[test]
header_map_fuzz()12 fn header_map_fuzz() {
13 fn prop(fuzz: Fuzz) -> TestResult {
14 fuzz.run();
15 TestResult::from_bool(true)
16 }
17
18 QuickCheck::new().quickcheck(prop as fn(Fuzz) -> TestResult)
19 }
20
21 #[derive(Debug, Clone)]
22 #[allow(dead_code)]
23 struct Fuzz {
24 // The magic seed that makes the test case reproducible
25 seed: [u8; 32],
26
27 // Actions to perform
28 steps: Vec<Step>,
29
30 // Number of steps to drop
31 reduce: usize,
32 }
33
34 #[derive(Debug)]
35 struct Weight {
36 insert: usize,
37 remove: usize,
38 append: usize,
39 }
40
41 #[derive(Debug, Clone)]
42 struct Step {
43 action: Action,
44 expect: AltMap,
45 }
46
47 #[derive(Debug, Clone)]
48 enum Action {
49 Insert {
50 name: HeaderName, // Name to insert
51 val: HeaderValue, // Value to insert
52 old: Option<HeaderValue>, // Old value
53 },
54 Append {
55 name: HeaderName,
56 val: HeaderValue,
57 ret: bool,
58 },
59 Remove {
60 name: HeaderName, // Name to remove
61 val: Option<HeaderValue>, // Value to get
62 },
63 }
64
65 // An alternate implementation of HeaderMap backed by HashMap
66 #[derive(Debug, Clone, Default)]
67 struct AltMap {
68 map: HashMap<HeaderName, Vec<HeaderValue>>,
69 }
70
71 impl Fuzz {
new(seed: [u8; 32]) -> Fuzz72 fn new(seed: [u8; 32]) -> Fuzz {
73 // Seed the RNG
74 let mut rng = StdRng::from_seed(seed);
75
76 let mut steps = vec![];
77 let mut expect = AltMap::default();
78 let num = rng.gen_range(5, 500);
79
80 let weight = Weight {
81 insert: rng.gen_range(1, 10),
82 remove: rng.gen_range(1, 10),
83 append: rng.gen_range(1, 10),
84 };
85
86 while steps.len() < num {
87 steps.push(expect.gen_step(&weight, &mut rng));
88 }
89
90 Fuzz {
91 seed: seed,
92 steps: steps,
93 reduce: 0,
94 }
95 }
96
run(self)97 fn run(self) {
98 // Create a new header map
99 let mut map = HeaderMap::new();
100
101 // Number of steps to perform
102 let take = self.steps.len() - self.reduce;
103
104 for step in self.steps.into_iter().take(take) {
105 step.action.apply(&mut map);
106
107 step.expect.assert_identical(&map);
108 }
109 }
110 }
111
112 impl Arbitrary for Fuzz {
arbitrary<G: Gen>(g: &mut G) -> Self113 fn arbitrary<G: Gen>(g: &mut G) -> Self {
114 Fuzz::new(Rng::gen(g))
115 }
116 }
117
118 impl AltMap {
gen_step(&mut self, weight: &Weight, rng: &mut StdRng) -> Step119 fn gen_step(&mut self, weight: &Weight, rng: &mut StdRng) -> Step {
120 let action = self.gen_action(weight, rng);
121
122 Step {
123 action: action,
124 expect: self.clone(),
125 }
126 }
127
128 /// This will also apply the action against `self`
gen_action(&mut self, weight: &Weight, rng: &mut StdRng) -> Action129 fn gen_action(&mut self, weight: &Weight, rng: &mut StdRng) -> Action {
130 let sum = weight.insert + weight.remove + weight.append;
131
132 let mut num = rng.gen_range(0, sum);
133
134 if num < weight.insert {
135 return self.gen_insert(rng);
136 }
137
138 num -= weight.insert;
139
140 if num < weight.remove {
141 return self.gen_remove(rng);
142 }
143
144 num -= weight.remove;
145
146 if num < weight.append {
147 return self.gen_append(rng);
148 }
149
150 unreachable!();
151 }
152
gen_insert(&mut self, rng: &mut StdRng) -> Action153 fn gen_insert(&mut self, rng: &mut StdRng) -> Action {
154 let name = self.gen_name(4, rng);
155 let val = gen_header_value(rng);
156 let old = self.insert(name.clone(), val.clone());
157
158 Action::Insert {
159 name: name,
160 val: val,
161 old: old,
162 }
163 }
164
gen_remove(&mut self, rng: &mut StdRng) -> Action165 fn gen_remove(&mut self, rng: &mut StdRng) -> Action {
166 let name = self.gen_name(-4, rng);
167 let val = self.remove(&name);
168
169 Action::Remove {
170 name: name,
171 val: val,
172 }
173 }
174
gen_append(&mut self, rng: &mut StdRng) -> Action175 fn gen_append(&mut self, rng: &mut StdRng) -> Action {
176 let name = self.gen_name(-5, rng);
177 let val = gen_header_value(rng);
178
179 let vals = self.map.entry(name.clone()).or_insert(vec![]);
180
181 let ret = !vals.is_empty();
182 vals.push(val.clone());
183
184 Action::Append {
185 name: name,
186 val: val,
187 ret: ret,
188 }
189 }
190
191 /// Negative numbers weigh finding an existing header higher
gen_name(&self, weight: i32, rng: &mut StdRng) -> HeaderName192 fn gen_name(&self, weight: i32, rng: &mut StdRng) -> HeaderName {
193 let mut existing = rng.gen_ratio(1, weight.abs() as u32);
194
195 if weight < 0 {
196 existing = !existing;
197 }
198
199 if existing {
200 // Existing header
201 if let Some(name) = self.find_random_name(rng) {
202 name
203 } else {
204 gen_header_name(rng)
205 }
206 } else {
207 gen_header_name(rng)
208 }
209 }
210
find_random_name(&self, rng: &mut StdRng) -> Option<HeaderName>211 fn find_random_name(&self, rng: &mut StdRng) -> Option<HeaderName> {
212 if self.map.is_empty() {
213 None
214 } else {
215 let n = rng.gen_range(0, self.map.len());
216 self.map.keys().nth(n).map(Clone::clone)
217 }
218 }
219
insert(&mut self, name: HeaderName, val: HeaderValue) -> Option<HeaderValue>220 fn insert(&mut self, name: HeaderName, val: HeaderValue) -> Option<HeaderValue> {
221 let old = self.map.insert(name, vec![val]);
222 old.and_then(|v| v.into_iter().next())
223 }
224
remove(&mut self, name: &HeaderName) -> Option<HeaderValue>225 fn remove(&mut self, name: &HeaderName) -> Option<HeaderValue> {
226 self.map.remove(name).and_then(|v| v.into_iter().next())
227 }
228
assert_identical(&self, other: &HeaderMap<HeaderValue>)229 fn assert_identical(&self, other: &HeaderMap<HeaderValue>) {
230 assert_eq!(self.map.len(), other.keys_len());
231
232 for (key, val) in &self.map {
233 // Test get
234 assert_eq!(other.get(key), val.get(0));
235
236 // Test get_all
237 let vals = other.get_all(key);
238 let actual: Vec<_> = vals.iter().collect();
239 assert_eq!(&actual[..], &val[..]);
240 }
241 }
242 }
243
244 impl Action {
apply(self, map: &mut HeaderMap<HeaderValue>)245 fn apply(self, map: &mut HeaderMap<HeaderValue>) {
246 match self {
247 Action::Insert { name, val, old } => {
248 let actual = map.insert(name, val);
249 assert_eq!(actual, old);
250 }
251 Action::Remove { name, val } => {
252 // Just to help track the state, load all associated values.
253 let _ = map.get_all(&name).iter().collect::<Vec<_>>();
254
255 let actual = map.remove(&name);
256 assert_eq!(actual, val);
257 }
258 Action::Append { name, val, ret } => {
259 assert_eq!(ret, map.append(name, val));
260 }
261 }
262 }
263 }
264
gen_header_name(g: &mut StdRng) -> HeaderName265 fn gen_header_name(g: &mut StdRng) -> HeaderName {
266 const STANDARD_HEADERS: &'static [HeaderName] = &[
267 header::ACCEPT,
268 header::ACCEPT_CHARSET,
269 header::ACCEPT_ENCODING,
270 header::ACCEPT_LANGUAGE,
271 header::ACCEPT_RANGES,
272 header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
273 header::ACCESS_CONTROL_ALLOW_HEADERS,
274 header::ACCESS_CONTROL_ALLOW_METHODS,
275 header::ACCESS_CONTROL_ALLOW_ORIGIN,
276 header::ACCESS_CONTROL_EXPOSE_HEADERS,
277 header::ACCESS_CONTROL_MAX_AGE,
278 header::ACCESS_CONTROL_REQUEST_HEADERS,
279 header::ACCESS_CONTROL_REQUEST_METHOD,
280 header::AGE,
281 header::ALLOW,
282 header::ALT_SVC,
283 header::AUTHORIZATION,
284 header::CACHE_CONTROL,
285 header::CACHE_STATUS,
286 header::CDN_CACHE_CONTROL,
287 header::CONNECTION,
288 header::CONTENT_DISPOSITION,
289 header::CONTENT_ENCODING,
290 header::CONTENT_LANGUAGE,
291 header::CONTENT_LENGTH,
292 header::CONTENT_LOCATION,
293 header::CONTENT_RANGE,
294 header::CONTENT_SECURITY_POLICY,
295 header::CONTENT_SECURITY_POLICY_REPORT_ONLY,
296 header::CONTENT_TYPE,
297 header::COOKIE,
298 header::DNT,
299 header::DATE,
300 header::ETAG,
301 header::EXPECT,
302 header::EXPIRES,
303 header::FORWARDED,
304 header::FROM,
305 header::HOST,
306 header::IF_MATCH,
307 header::IF_MODIFIED_SINCE,
308 header::IF_NONE_MATCH,
309 header::IF_RANGE,
310 header::IF_UNMODIFIED_SINCE,
311 header::LAST_MODIFIED,
312 header::LINK,
313 header::LOCATION,
314 header::MAX_FORWARDS,
315 header::ORIGIN,
316 header::PRAGMA,
317 header::PROXY_AUTHENTICATE,
318 header::PROXY_AUTHORIZATION,
319 header::PUBLIC_KEY_PINS,
320 header::PUBLIC_KEY_PINS_REPORT_ONLY,
321 header::RANGE,
322 header::REFERER,
323 header::REFERRER_POLICY,
324 header::REFRESH,
325 header::RETRY_AFTER,
326 header::SEC_WEBSOCKET_ACCEPT,
327 header::SEC_WEBSOCKET_EXTENSIONS,
328 header::SEC_WEBSOCKET_KEY,
329 header::SEC_WEBSOCKET_PROTOCOL,
330 header::SEC_WEBSOCKET_VERSION,
331 header::SERVER,
332 header::SET_COOKIE,
333 header::STRICT_TRANSPORT_SECURITY,
334 header::TE,
335 header::TRAILER,
336 header::TRANSFER_ENCODING,
337 header::UPGRADE,
338 header::UPGRADE_INSECURE_REQUESTS,
339 header::USER_AGENT,
340 header::VARY,
341 header::VIA,
342 header::WARNING,
343 header::WWW_AUTHENTICATE,
344 header::X_CONTENT_TYPE_OPTIONS,
345 header::X_DNS_PREFETCH_CONTROL,
346 header::X_FRAME_OPTIONS,
347 header::X_XSS_PROTECTION,
348 ];
349
350 if g.gen_ratio(1, 2) {
351 STANDARD_HEADERS.choose(g).unwrap().clone()
352 } else {
353 let value = gen_string(g, 1, 25);
354 HeaderName::from_bytes(value.as_bytes()).unwrap()
355 }
356 }
357
gen_header_value(g: &mut StdRng) -> HeaderValue358 fn gen_header_value(g: &mut StdRng) -> HeaderValue {
359 let value = gen_string(g, 0, 70);
360 HeaderValue::from_bytes(value.as_bytes()).unwrap()
361 }
362
gen_string(g: &mut StdRng, min: usize, max: usize) -> String363 fn gen_string(g: &mut StdRng, min: usize, max: usize) -> String {
364 let bytes: Vec<_> = (min..max)
365 .map(|_| {
366 // Chars to pick from
367 b"ABCDEFGHIJKLMNOPQRSTUVabcdefghilpqrstuvwxyz----"
368 .choose(g)
369 .unwrap()
370 .clone()
371 })
372 .collect();
373
374 String::from_utf8(bytes).unwrap()
375 }
376