1 use crate::hpack::{Decoder, Encoder, Header};
2
3 use http::header::{HeaderName, HeaderValue};
4
5 use bytes::BytesMut;
6 use quickcheck::{Arbitrary, Gen, QuickCheck, TestResult};
7 use rand::{Rng, SeedableRng, StdRng};
8
9 use std::io::Cursor;
10
11 const MAX_CHUNK: usize = 2 * 1024;
12
13 #[test]
hpack_fuzz()14 fn hpack_fuzz() {
15 let _ = env_logger::try_init();
16 fn prop(fuzz: FuzzHpack) -> TestResult {
17 fuzz.run();
18 TestResult::from_bool(true)
19 }
20
21 QuickCheck::new()
22 .tests(100)
23 .quickcheck(prop as fn(FuzzHpack) -> TestResult)
24 }
25
26 /*
27 // If wanting to test with a specific feed, uncomment and fill in the seed.
28 #[test]
29 fn hpack_fuzz_seeded() {
30 let _ = env_logger::try_init();
31 let seed = [/* fill me in*/];
32 FuzzHpack::new(seed).run();
33 }
34 */
35
36 #[derive(Debug, Clone)]
37 struct FuzzHpack {
38 // The set of headers to encode / decode
39 frames: Vec<HeaderFrame>,
40 }
41
42 #[derive(Debug, Clone)]
43 struct HeaderFrame {
44 resizes: Vec<usize>,
45 headers: Vec<Header<Option<HeaderName>>>,
46 }
47
48 impl FuzzHpack {
new(seed: [usize; 4]) -> FuzzHpack49 fn new(seed: [usize; 4]) -> FuzzHpack {
50 // Seed the RNG
51 let mut rng = StdRng::from_seed(&seed);
52
53 // Generates a bunch of source headers
54 let mut source: Vec<Header<Option<HeaderName>>> = vec![];
55
56 for _ in 0..2000 {
57 source.push(gen_header(&mut rng));
58 }
59
60 // Actual test run headers
61 let num: usize = rng.gen_range(40, 500);
62
63 let mut frames: Vec<HeaderFrame> = vec![];
64 let mut added = 0;
65
66 let skew: i32 = rng.gen_range(1, 5);
67
68 // Rough number of headers to add
69 while added < num {
70 let mut frame = HeaderFrame {
71 resizes: vec![],
72 headers: vec![],
73 };
74
75 match rng.gen_range(0, 20) {
76 0 => {
77 // Two resizes
78 let high = rng.gen_range(128, MAX_CHUNK * 2);
79 let low = rng.gen_range(0, high);
80
81 frame.resizes.extend(&[low, high]);
82 }
83 1..=3 => {
84 frame.resizes.push(rng.gen_range(128, MAX_CHUNK * 2));
85 }
86 _ => {}
87 }
88
89 let mut is_name_required = true;
90
91 for _ in 0..rng.gen_range(1, (num - added) + 1) {
92 let x: f64 = rng.gen_range(0.0, 1.0);
93 let x = x.powi(skew);
94
95 let i = (x * source.len() as f64) as usize;
96
97 let header = &source[i];
98 match header {
99 Header::Field { name: None, .. } => {
100 if is_name_required {
101 continue;
102 }
103 }
104 Header::Field { .. } => {
105 is_name_required = false;
106 }
107 _ => {
108 // pseudos can't be followed by a header with no name
109 is_name_required = true;
110 }
111 }
112
113 frame.headers.push(header.clone());
114
115 added += 1;
116 }
117
118 frames.push(frame);
119 }
120
121 FuzzHpack { frames }
122 }
123
run(self)124 fn run(self) {
125 let frames = self.frames;
126 let mut expect = vec![];
127
128 let mut encoder = Encoder::default();
129 let mut decoder = Decoder::default();
130
131 for frame in frames {
132 // build "expected" frames, such that decoding headers always
133 // includes a name
134 let mut prev_name = None;
135 for header in &frame.headers {
136 match header.clone().reify() {
137 Ok(h) => {
138 prev_name = match h {
139 Header::Field { ref name, .. } => Some(name.clone()),
140 _ => None,
141 };
142 expect.push(h);
143 }
144 Err(value) => {
145 expect.push(Header::Field {
146 name: prev_name.as_ref().cloned().expect("previous header name"),
147 value,
148 });
149 }
150 }
151 }
152
153 let mut buf = BytesMut::new();
154
155 if let Some(max) = frame.resizes.iter().max() {
156 decoder.queue_size_update(*max);
157 }
158
159 // Apply resizes
160 for resize in &frame.resizes {
161 encoder.update_max_size(*resize);
162 }
163
164 encoder.encode(frame.headers, &mut buf);
165
166 // Decode the chunk!
167 decoder
168 .decode(&mut Cursor::new(&mut buf), |h| {
169 let e = expect.remove(0);
170 assert_eq!(h, e);
171 })
172 .expect("full decode");
173 }
174
175 assert_eq!(0, expect.len());
176 }
177 }
178
179 impl Arbitrary for FuzzHpack {
arbitrary<G: Gen>(g: &mut G) -> Self180 fn arbitrary<G: Gen>(g: &mut G) -> Self {
181 FuzzHpack::new(quickcheck::Rng::gen(g))
182 }
183 }
184
gen_header(g: &mut StdRng) -> Header<Option<HeaderName>>185 fn gen_header(g: &mut StdRng) -> Header<Option<HeaderName>> {
186 use http::{Method, StatusCode};
187
188 if g.gen_weighted_bool(10) {
189 match g.next_u32() % 5 {
190 0 => {
191 let value = gen_string(g, 4, 20);
192 Header::Authority(to_shared(value))
193 }
194 1 => {
195 let method = match g.next_u32() % 6 {
196 0 => Method::GET,
197 1 => Method::POST,
198 2 => Method::PUT,
199 3 => Method::PATCH,
200 4 => Method::DELETE,
201 5 => {
202 let n: usize = g.gen_range(3, 7);
203 let bytes: Vec<u8> = (0..n)
204 .map(|_| g.choose(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ").unwrap().clone())
205 .collect();
206
207 Method::from_bytes(&bytes).unwrap()
208 }
209 _ => unreachable!(),
210 };
211
212 Header::Method(method)
213 }
214 2 => {
215 let value = match g.next_u32() % 2 {
216 0 => "http",
217 1 => "https",
218 _ => unreachable!(),
219 };
220
221 Header::Scheme(to_shared(value.to_string()))
222 }
223 3 => {
224 let value = match g.next_u32() % 100 {
225 0 => "/".to_string(),
226 1 => "/index.html".to_string(),
227 _ => gen_string(g, 2, 20),
228 };
229
230 Header::Path(to_shared(value))
231 }
232 4 => {
233 let status = (g.gen::<u16>() % 500) + 100;
234
235 Header::Status(StatusCode::from_u16(status).unwrap())
236 }
237 _ => unreachable!(),
238 }
239 } else {
240 let name = if g.gen_weighted_bool(10) {
241 None
242 } else {
243 Some(gen_header_name(g))
244 };
245 let mut value = gen_header_value(g);
246
247 if g.gen_weighted_bool(30) {
248 value.set_sensitive(true);
249 }
250
251 Header::Field { name, value }
252 }
253 }
254
gen_header_name(g: &mut StdRng) -> HeaderName255 fn gen_header_name(g: &mut StdRng) -> HeaderName {
256 use http::header;
257
258 if g.gen_weighted_bool(2) {
259 g.choose(&[
260 header::ACCEPT,
261 header::ACCEPT_CHARSET,
262 header::ACCEPT_ENCODING,
263 header::ACCEPT_LANGUAGE,
264 header::ACCEPT_RANGES,
265 header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
266 header::ACCESS_CONTROL_ALLOW_HEADERS,
267 header::ACCESS_CONTROL_ALLOW_METHODS,
268 header::ACCESS_CONTROL_ALLOW_ORIGIN,
269 header::ACCESS_CONTROL_EXPOSE_HEADERS,
270 header::ACCESS_CONTROL_MAX_AGE,
271 header::ACCESS_CONTROL_REQUEST_HEADERS,
272 header::ACCESS_CONTROL_REQUEST_METHOD,
273 header::AGE,
274 header::ALLOW,
275 header::ALT_SVC,
276 header::AUTHORIZATION,
277 header::CACHE_CONTROL,
278 header::CONNECTION,
279 header::CONTENT_DISPOSITION,
280 header::CONTENT_ENCODING,
281 header::CONTENT_LANGUAGE,
282 header::CONTENT_LENGTH,
283 header::CONTENT_LOCATION,
284 header::CONTENT_RANGE,
285 header::CONTENT_SECURITY_POLICY,
286 header::CONTENT_SECURITY_POLICY_REPORT_ONLY,
287 header::CONTENT_TYPE,
288 header::COOKIE,
289 header::DNT,
290 header::DATE,
291 header::ETAG,
292 header::EXPECT,
293 header::EXPIRES,
294 header::FORWARDED,
295 header::FROM,
296 header::HOST,
297 header::IF_MATCH,
298 header::IF_MODIFIED_SINCE,
299 header::IF_NONE_MATCH,
300 header::IF_RANGE,
301 header::IF_UNMODIFIED_SINCE,
302 header::LAST_MODIFIED,
303 header::LINK,
304 header::LOCATION,
305 header::MAX_FORWARDS,
306 header::ORIGIN,
307 header::PRAGMA,
308 header::PROXY_AUTHENTICATE,
309 header::PROXY_AUTHORIZATION,
310 header::PUBLIC_KEY_PINS,
311 header::PUBLIC_KEY_PINS_REPORT_ONLY,
312 header::RANGE,
313 header::REFERER,
314 header::REFERRER_POLICY,
315 header::REFRESH,
316 header::RETRY_AFTER,
317 header::SERVER,
318 header::SET_COOKIE,
319 header::STRICT_TRANSPORT_SECURITY,
320 header::TE,
321 header::TRAILER,
322 header::TRANSFER_ENCODING,
323 header::USER_AGENT,
324 header::UPGRADE,
325 header::UPGRADE_INSECURE_REQUESTS,
326 header::VARY,
327 header::VIA,
328 header::WARNING,
329 header::WWW_AUTHENTICATE,
330 header::X_CONTENT_TYPE_OPTIONS,
331 header::X_DNS_PREFETCH_CONTROL,
332 header::X_FRAME_OPTIONS,
333 header::X_XSS_PROTECTION,
334 ])
335 .unwrap()
336 .clone()
337 } else {
338 let value = gen_string(g, 1, 25);
339 HeaderName::from_bytes(value.as_bytes()).unwrap()
340 }
341 }
342
gen_header_value(g: &mut StdRng) -> HeaderValue343 fn gen_header_value(g: &mut StdRng) -> HeaderValue {
344 let value = gen_string(g, 0, 70);
345 HeaderValue::from_bytes(value.as_bytes()).unwrap()
346 }
347
gen_string(g: &mut StdRng, min: usize, max: usize) -> String348 fn gen_string(g: &mut StdRng, min: usize, max: usize) -> String {
349 let bytes: Vec<_> = (min..max)
350 .map(|_| {
351 // Chars to pick from
352 g.choose(b"ABCDEFGHIJKLMNOPQRSTUVabcdefghilpqrstuvwxyz----")
353 .unwrap()
354 .clone()
355 })
356 .collect();
357
358 String::from_utf8(bytes).unwrap()
359 }
360
to_shared(src: String) -> crate::hpack::BytesStr361 fn to_shared(src: String) -> crate::hpack::BytesStr {
362 crate::hpack::BytesStr::from(src.as_str())
363 }
364