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