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