1#![cfg_attr(rustfmt, rustfmt_skip)] 2 3#[global_allocator] 4static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; 5 6use criterion::*; 7use nom::{IResult, bytes::complete::{tag, take_while1}, character::complete::{line_ending, char}, multi::many1}; 8 9#[cfg_attr(rustfmt, rustfmt_skip)] 10#[derive(Debug)] 11struct Request<'a> { 12 method: &'a [u8], 13 uri: &'a [u8], 14 version: &'a [u8], 15} 16 17#[derive(Debug)] 18struct 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))] 25fn 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 51fn not_line_ending(c: u8) -> bool { 52 c != b'\r' && c != b'\n' 53} 54 55fn is_space(c: u8) -> bool { 56 c == b' ' 57} 58 59fn is_not_space(c: u8) -> bool { 60 c != b' ' 61} 62fn is_horizontal_space(c: u8) -> bool { 63 c == b' ' || c == b'\t' 64} 65 66fn is_version(c: u8) -> bool { 67 c >= b'0' && c <= b'9' || c == b'.' 68} 69 70fn 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 81fn 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 88fn 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 96fn 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 104fn 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 113fn 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] 140fn small_test(b: &mut Bencher) { 141 let data = include_bytes!("../../http-requests.txt"); 142 b.iter(||{ 143 parse(data) 144 }); 145} 146 147#[bench] 148fn bigger_test(b: &mut Bencher) { 149 let data = include_bytes!("../../bigger.txt"); 150 b.iter(||{ 151 parse(data) 152 }); 153} 154*/ 155 156fn one_test(c: &mut Criterion) { 157 let data = &b"GET / HTTP/1.1 158Host: www.reddit.com 159User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:15.0) Gecko/20100101 Firefox/15.0.1 160Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 161Accept-Language: en-us,en;q=0.5 162Accept-Encoding: gzip, deflate 163Connection: 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/* 180fn 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 198criterion_group!(http, one_test); 199criterion_main!(http); 200