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