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