• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![cfg_attr(rustfmt, rustfmt_skip)]
2 
3 #[global_allocator]
4 static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
5 
6 use criterion::*;
7 use nom::{IResult, bytes::complete::{tag, take_while1}, character::complete::{line_ending, char}, multi::many1};
8 
9 #[cfg_attr(rustfmt, rustfmt_skip)]
10 #[derive(Debug)]
11 struct Request<'a> {
12   method:  &'a [u8],
13   uri:     &'a [u8],
14   version: &'a [u8],
15 }
16 
17 #[derive(Debug)]
18 struct Header<'a> {
19   name: &'a [u8],
20   value: Vec<&'a [u8]>,
21 }
22 
23 #[cfg_attr(rustfmt, rustfmt_skip)]
24 #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
is_token(c: u8) -> bool25 fn is_token(c: u8) -> bool {
26   match c {
27     128..=255 => false,
28     0..=31    => false,
29     b'('      => false,
30     b')'      => false,
31     b'<'      => false,
32     b'>'      => false,
33     b'@'      => false,
34     b','      => false,
35     b';'      => false,
36     b':'      => false,
37     b'\\'     => false,
38     b'"'      => false,
39     b'/'      => false,
40     b'['      => false,
41     b']'      => false,
42     b'?'      => false,
43     b'='      => false,
44     b'{'      => false,
45     b'}'      => false,
46     b' '      => false,
47     _         => true,
48   }
49 }
50 
not_line_ending(c: u8) -> bool51 fn not_line_ending(c: u8) -> bool {
52   c != b'\r' && c != b'\n'
53 }
54 
is_space(c: u8) -> bool55 fn is_space(c: u8) -> bool {
56   c == b' '
57 }
58 
is_not_space(c: u8) -> bool59 fn is_not_space(c: u8) -> bool {
60   c != b' '
61 }
is_horizontal_space(c: u8) -> bool62 fn is_horizontal_space(c: u8) -> bool {
63   c == b' ' || c == b'\t'
64 }
65 
is_version(c: u8) -> bool66 fn is_version(c: u8) -> bool {
67   c >= b'0' && c <= b'9' || c == b'.'
68 }
69 
request_line(input: &[u8]) -> IResult<&[u8], Request<'_>>70 fn request_line(input: &[u8]) -> IResult<&[u8], Request<'_>> {
71   let (input, method) = take_while1(is_token)(input)?;
72   let (input, _) = take_while1(is_space)(input)?;
73   let (input, uri) = take_while1(is_not_space)(input)?;
74   let (input, _) = take_while1(is_space)(input)?;
75   let (input, version) = http_version(input)?;
76   let (input, _) = line_ending(input)?;
77 
78   Ok((input, Request {method, uri, version}))
79 }
80 
http_version(input: &[u8]) -> IResult<&[u8], &[u8]>81 fn http_version(input: &[u8]) -> IResult<&[u8], &[u8]> {
82   let (input, _) = tag("HTTP/")(input)?;
83   let (input, version) = take_while1(is_version)(input)?;
84 
85   Ok((input, version))
86 }
87 
message_header_value(input: &[u8]) -> IResult<&[u8], &[u8]>88 fn message_header_value(input: &[u8]) -> IResult<&[u8], &[u8]> {
89   let (input, _) = take_while1(is_horizontal_space)(input)?;
90   let (input, data) = take_while1(not_line_ending)(input)?;
91   let (input, _) = line_ending(input)?;
92 
93   Ok((input, data))
94 }
95 
message_header(input: &[u8]) -> IResult<&[u8], Header<'_>>96 fn message_header(input: &[u8]) -> IResult<&[u8], Header<'_>> {
97   let (input, name) = take_while1(is_token)(input)?;
98   let (input, _) = char(':')(input)?;
99   let (input, value) = many1(message_header_value)(input)?;
100 
101   Ok((input, Header{ name, value }))
102 }
103 
request(input: &[u8]) -> IResult<&[u8], (Request<'_>, Vec<Header<'_>>)>104 fn request(input: &[u8]) -> IResult<&[u8], (Request<'_>, Vec<Header<'_>>)> {
105   let (input, req) = request_line(input)?;
106   let (input, h) = many1(message_header)(input)?;
107   let (input, _) = line_ending(input)?;
108 
109   Ok((input, (req, h)))
110 }
111 
112 
parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>>113 fn parse(data: &[u8]) -> Option<Vec<(Request<'_>, Vec<Header<'_>>)>> {
114   let mut buf = &data[..];
115   let mut v = Vec::new();
116   loop {
117     match request(buf) {
118       Ok((b, r)) => {
119         buf = b;
120         v.push(r);
121 
122         if b.is_empty() {
123 
124           //println!("{}", i);
125           break;
126         }
127       }
128       Err(e) => {
129         println!("error: {:?}", e);
130         return None;
131       },
132     }
133   }
134 
135   Some(v)
136 }
137 
138 /*
139 #[bench]
140 fn small_test(b: &mut Bencher) {
141   let data = include_bytes!("../../http-requests.txt");
142   b.iter(||{
143     parse(data)
144   });
145 }
146 
147 #[bench]
148 fn bigger_test(b: &mut Bencher) {
149   let data = include_bytes!("../../bigger.txt");
150   b.iter(||{
151     parse(data)
152   });
153 }
154 */
155 
one_test(c: &mut Criterion)156 fn one_test(c: &mut Criterion) {
157   let data = &b"GET / HTTP/1.1
158 Host: www.reddit.com
159 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:15.0) Gecko/20100101 Firefox/15.0.1
160 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
161 Accept-Language: en-us,en;q=0.5
162 Accept-Encoding: gzip, deflate
163 Connection: keep-alive
164 
165 "[..];
166 
167   let mut http_group = c.benchmark_group("http");
168   http_group.throughput(Throughput::Bytes(data.len() as u64));
169   http_group.bench_with_input(
170     BenchmarkId::new("parse", data.len()),
171      data,
172       |b, data| {
173     b.iter(|| parse(data).unwrap());
174   });
175 
176   http_group.finish();
177 }
178 
179 /*
180 fn main() {
181   let mut contents: Vec<u8> = Vec::new();
182 
183   {
184     use std::io::Read;
185 
186     let mut file = File::open(env::args().nth(1).expect("File to read")).expect("Failed to open file");
187 
188     let _ = file.read_to_end(&mut contents).unwrap();
189   }
190 
191   let buf = &contents[..];
192   loop {
193     parse(buf);
194   }
195 }
196 */
197 
198 criterion_group!(http, one_test);
199 criterion_main!(http);
200