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