summaryrefslogtreecommitdiffstats
path: root/vendor/http/tests/header_map_fuzz.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/http/tests/header_map_fuzz.rs')
-rw-r--r--vendor/http/tests/header_map_fuzz.rs375
1 files changed, 375 insertions, 0 deletions
diff --git a/vendor/http/tests/header_map_fuzz.rs b/vendor/http/tests/header_map_fuzz.rs
new file mode 100644
index 000000000..68a8604f7
--- /dev/null
+++ b/vendor/http/tests/header_map_fuzz.rs
@@ -0,0 +1,375 @@
+use http::header::*;
+use http::*;
+
+use quickcheck::{Arbitrary, Gen, QuickCheck, TestResult};
+use rand::rngs::StdRng;
+use rand::seq::SliceRandom;
+use rand::{Rng, SeedableRng};
+
+use std::collections::HashMap;
+
+#[test]
+fn header_map_fuzz() {
+ fn prop(fuzz: Fuzz) -> TestResult {
+ fuzz.run();
+ TestResult::from_bool(true)
+ }
+
+ QuickCheck::new().quickcheck(prop as fn(Fuzz) -> TestResult)
+}
+
+#[derive(Debug, Clone)]
+#[allow(dead_code)]
+struct Fuzz {
+ // The magic seed that makes the test case reproducible
+ seed: [u8; 32],
+
+ // Actions to perform
+ steps: Vec<Step>,
+
+ // Number of steps to drop
+ reduce: usize,
+}
+
+#[derive(Debug)]
+struct Weight {
+ insert: usize,
+ remove: usize,
+ append: usize,
+}
+
+#[derive(Debug, Clone)]
+struct Step {
+ action: Action,
+ expect: AltMap,
+}
+
+#[derive(Debug, Clone)]
+enum Action {
+ Insert {
+ name: HeaderName, // Name to insert
+ val: HeaderValue, // Value to insert
+ old: Option<HeaderValue>, // Old value
+ },
+ Append {
+ name: HeaderName,
+ val: HeaderValue,
+ ret: bool,
+ },
+ Remove {
+ name: HeaderName, // Name to remove
+ val: Option<HeaderValue>, // Value to get
+ },
+}
+
+// An alternate implementation of HeaderMap backed by HashMap
+#[derive(Debug, Clone, Default)]
+struct AltMap {
+ map: HashMap<HeaderName, Vec<HeaderValue>>,
+}
+
+impl Fuzz {
+ fn new(seed: [u8; 32]) -> Fuzz {
+ // Seed the RNG
+ let mut rng = StdRng::from_seed(seed);
+
+ let mut steps = vec![];
+ let mut expect = AltMap::default();
+ let num = rng.gen_range(5, 500);
+
+ let weight = Weight {
+ insert: rng.gen_range(1, 10),
+ remove: rng.gen_range(1, 10),
+ append: rng.gen_range(1, 10),
+ };
+
+ while steps.len() < num {
+ steps.push(expect.gen_step(&weight, &mut rng));
+ }
+
+ Fuzz {
+ seed: seed,
+ steps: steps,
+ reduce: 0,
+ }
+ }
+
+ fn run(self) {
+ // Create a new header map
+ let mut map = HeaderMap::new();
+
+ // Number of steps to perform
+ let take = self.steps.len() - self.reduce;
+
+ for step in self.steps.into_iter().take(take) {
+ step.action.apply(&mut map);
+
+ step.expect.assert_identical(&map);
+ }
+ }
+}
+
+impl Arbitrary for Fuzz {
+ fn arbitrary<G: Gen>(g: &mut G) -> Self {
+ Fuzz::new(Rng::gen(g))
+ }
+}
+
+impl AltMap {
+ fn gen_step(&mut self, weight: &Weight, rng: &mut StdRng) -> Step {
+ let action = self.gen_action(weight, rng);
+
+ Step {
+ action: action,
+ expect: self.clone(),
+ }
+ }
+
+ /// This will also apply the action against `self`
+ fn gen_action(&mut self, weight: &Weight, rng: &mut StdRng) -> Action {
+ let sum = weight.insert + weight.remove + weight.append;
+
+ let mut num = rng.gen_range(0, sum);
+
+ if num < weight.insert {
+ return self.gen_insert(rng);
+ }
+
+ num -= weight.insert;
+
+ if num < weight.remove {
+ return self.gen_remove(rng);
+ }
+
+ num -= weight.remove;
+
+ if num < weight.append {
+ return self.gen_append(rng);
+ }
+
+ unreachable!();
+ }
+
+ fn gen_insert(&mut self, rng: &mut StdRng) -> Action {
+ let name = self.gen_name(4, rng);
+ let val = gen_header_value(rng);
+ let old = self.insert(name.clone(), val.clone());
+
+ Action::Insert {
+ name: name,
+ val: val,
+ old: old,
+ }
+ }
+
+ fn gen_remove(&mut self, rng: &mut StdRng) -> Action {
+ let name = self.gen_name(-4, rng);
+ let val = self.remove(&name);
+
+ Action::Remove {
+ name: name,
+ val: val,
+ }
+ }
+
+ fn gen_append(&mut self, rng: &mut StdRng) -> Action {
+ let name = self.gen_name(-5, rng);
+ let val = gen_header_value(rng);
+
+ let vals = self.map.entry(name.clone()).or_insert(vec![]);
+
+ let ret = !vals.is_empty();
+ vals.push(val.clone());
+
+ Action::Append {
+ name: name,
+ val: val,
+ ret: ret,
+ }
+ }
+
+ /// Negative numbers weigh finding an existing header higher
+ fn gen_name(&self, weight: i32, rng: &mut StdRng) -> HeaderName {
+ let mut existing = rng.gen_ratio(1, weight.abs() as u32);
+
+ if weight < 0 {
+ existing = !existing;
+ }
+
+ if existing {
+ // Existing header
+ if let Some(name) = self.find_random_name(rng) {
+ name
+ } else {
+ gen_header_name(rng)
+ }
+ } else {
+ gen_header_name(rng)
+ }
+ }
+
+ fn find_random_name(&self, rng: &mut StdRng) -> Option<HeaderName> {
+ if self.map.is_empty() {
+ None
+ } else {
+ let n = rng.gen_range(0, self.map.len());
+ self.map.keys().nth(n).map(Clone::clone)
+ }
+ }
+
+ fn insert(&mut self, name: HeaderName, val: HeaderValue) -> Option<HeaderValue> {
+ let old = self.map.insert(name, vec![val]);
+ old.and_then(|v| v.into_iter().next())
+ }
+
+ fn remove(&mut self, name: &HeaderName) -> Option<HeaderValue> {
+ self.map.remove(name).and_then(|v| v.into_iter().next())
+ }
+
+ fn assert_identical(&self, other: &HeaderMap<HeaderValue>) {
+ assert_eq!(self.map.len(), other.keys_len());
+
+ for (key, val) in &self.map {
+ // Test get
+ assert_eq!(other.get(key), val.get(0));
+
+ // Test get_all
+ let vals = other.get_all(key);
+ let actual: Vec<_> = vals.iter().collect();
+ assert_eq!(&actual[..], &val[..]);
+ }
+ }
+}
+
+impl Action {
+ fn apply(self, map: &mut HeaderMap<HeaderValue>) {
+ match self {
+ Action::Insert { name, val, old } => {
+ let actual = map.insert(name, val);
+ assert_eq!(actual, old);
+ }
+ Action::Remove { name, val } => {
+ // Just to help track the state, load all associated values.
+ let _ = map.get_all(&name).iter().collect::<Vec<_>>();
+
+ let actual = map.remove(&name);
+ assert_eq!(actual, val);
+ }
+ Action::Append { name, val, ret } => {
+ assert_eq!(ret, map.append(name, val));
+ }
+ }
+ }
+}
+
+fn gen_header_name(g: &mut StdRng) -> HeaderName {
+ const STANDARD_HEADERS: &'static [HeaderName] = &[
+ header::ACCEPT,
+ header::ACCEPT_CHARSET,
+ header::ACCEPT_ENCODING,
+ header::ACCEPT_LANGUAGE,
+ header::ACCEPT_RANGES,
+ header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
+ header::ACCESS_CONTROL_ALLOW_HEADERS,
+ header::ACCESS_CONTROL_ALLOW_METHODS,
+ header::ACCESS_CONTROL_ALLOW_ORIGIN,
+ header::ACCESS_CONTROL_EXPOSE_HEADERS,
+ header::ACCESS_CONTROL_MAX_AGE,
+ header::ACCESS_CONTROL_REQUEST_HEADERS,
+ header::ACCESS_CONTROL_REQUEST_METHOD,
+ header::AGE,
+ header::ALLOW,
+ header::ALT_SVC,
+ header::AUTHORIZATION,
+ header::CACHE_CONTROL,
+ header::CACHE_STATUS,
+ header::CDN_CACHE_CONTROL,
+ header::CONNECTION,
+ header::CONTENT_DISPOSITION,
+ header::CONTENT_ENCODING,
+ header::CONTENT_LANGUAGE,
+ header::CONTENT_LENGTH,
+ header::CONTENT_LOCATION,
+ header::CONTENT_RANGE,
+ header::CONTENT_SECURITY_POLICY,
+ header::CONTENT_SECURITY_POLICY_REPORT_ONLY,
+ header::CONTENT_TYPE,
+ header::COOKIE,
+ header::DNT,
+ header::DATE,
+ header::ETAG,
+ header::EXPECT,
+ header::EXPIRES,
+ header::FORWARDED,
+ header::FROM,
+ header::HOST,
+ header::IF_MATCH,
+ header::IF_MODIFIED_SINCE,
+ header::IF_NONE_MATCH,
+ header::IF_RANGE,
+ header::IF_UNMODIFIED_SINCE,
+ header::LAST_MODIFIED,
+ header::LINK,
+ header::LOCATION,
+ header::MAX_FORWARDS,
+ header::ORIGIN,
+ header::PRAGMA,
+ header::PROXY_AUTHENTICATE,
+ header::PROXY_AUTHORIZATION,
+ header::PUBLIC_KEY_PINS,
+ header::PUBLIC_KEY_PINS_REPORT_ONLY,
+ header::RANGE,
+ header::REFERER,
+ header::REFERRER_POLICY,
+ header::REFRESH,
+ header::RETRY_AFTER,
+ header::SEC_WEBSOCKET_ACCEPT,
+ header::SEC_WEBSOCKET_EXTENSIONS,
+ header::SEC_WEBSOCKET_KEY,
+ header::SEC_WEBSOCKET_PROTOCOL,
+ header::SEC_WEBSOCKET_VERSION,
+ header::SERVER,
+ header::SET_COOKIE,
+ header::STRICT_TRANSPORT_SECURITY,
+ header::TE,
+ header::TRAILER,
+ header::TRANSFER_ENCODING,
+ header::UPGRADE,
+ header::UPGRADE_INSECURE_REQUESTS,
+ header::USER_AGENT,
+ header::VARY,
+ header::VIA,
+ header::WARNING,
+ header::WWW_AUTHENTICATE,
+ header::X_CONTENT_TYPE_OPTIONS,
+ header::X_DNS_PREFETCH_CONTROL,
+ header::X_FRAME_OPTIONS,
+ header::X_XSS_PROTECTION,
+ ];
+
+ if g.gen_ratio(1, 2) {
+ STANDARD_HEADERS.choose(g).unwrap().clone()
+ } else {
+ let value = gen_string(g, 1, 25);
+ HeaderName::from_bytes(value.as_bytes()).unwrap()
+ }
+}
+
+fn gen_header_value(g: &mut StdRng) -> HeaderValue {
+ let value = gen_string(g, 0, 70);
+ HeaderValue::from_bytes(value.as_bytes()).unwrap()
+}
+
+fn gen_string(g: &mut StdRng, min: usize, max: usize) -> String {
+ let bytes: Vec<_> = (min..max)
+ .map(|_| {
+ // Chars to pick from
+ b"ABCDEFGHIJKLMNOPQRSTUVabcdefghilpqrstuvwxyz----"
+ .choose(g)
+ .unwrap()
+ .clone()
+ })
+ .collect();
+
+ String::from_utf8(bytes).unwrap()
+}