use std::hint::black_box;
const MINIMAL: &[u8] = b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
const HTMX: &[u8] = b"GET /about HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Accept: text/html\r\n\
HX-Request: true\r\n\
HX-Target: #content\r\n\
HX-Current-URL: http://localhost:8080/\r\n\
User-Agent: Mozilla/5.0\r\n\
Accept-Language: en-US,en;q=0.9\r\n\
Connection: keep-alive\r\n\
\r\n";
const BROWSER: &[u8] = b"GET /articles/digital-signatures-explained HTTP/1.1\r\n\
Host: authentidoc.com\r\n\
Connection: keep-alive\r\n\
Cache-Control: max-age=0\r\n\
Upgrade-Insecure-Requests: 1\r\n\
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\r\n\
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8\r\n\
Accept-Encoding: gzip, deflate, br\r\n\
Accept-Language: en-US,en;q=0.9,es;q=0.8\r\n\
Cookie: session=abc123; theme=dark\r\n\
If-None-Match: \"etag-value\"\r\n\
HX-Request: true\r\n\
HX-Target: #content\r\n\
HX-Current-URL: https://authentidoc.com/\r\n\
HX-Trigger: nav-link\r\n\
\r\n";
fn many_headers() -> Vec<u8> {
let mut raw = b"GET /features HTTP/1.1\r\nHost: localhost\r\n".to_vec();
for i in 0..30 {
raw.extend_from_slice(format!("X-Header-{i}: value-{i}\r\n").as_bytes());
}
raw.extend_from_slice(b"\r\n");
raw
}
fn main() {
let mut args = std::env::args().skip(1);
let fixture = args.next().unwrap_or_else(|| "htmx".to_string());
let iters: u64 = args
.next()
.as_deref()
.and_then(|s| s.parse().ok())
.unwrap_or(100_000_000);
let many = many_headers();
let raw: &[u8] = match fixture.as_str() {
"minimal" => MINIMAL,
"htmx" => HTMX,
"browser" => BROWSER,
"many" => &many,
other => {
eprintln!("unknown fixture: {other}. expected one of: minimal htmx browser many");
std::process::exit(2);
}
};
eprintln!(
"fixture={fixture} bytes={} iters={iters} parser=httparse",
raw.len()
);
let start = std::time::Instant::now();
for _ in 0..iters {
let mut headers = [httparse::EMPTY_HEADER; 32];
let mut req = httparse::Request::new(&mut headers);
let s = req.parse(black_box(raw)).unwrap();
black_box(s);
}
let elapsed = start.elapsed();
#[allow(clippy::cast_precision_loss)]
let ns_per = elapsed.as_secs_f64() * 1e9 / iters as f64;
eprintln!("elapsed={elapsed:?} ns/iter={ns_per:.2}");
}