use std::fs;
use bytes::Bytes;
use criterion::{criterion_group, criterion_main, Criterion};
use http_body_util::BodyExt;
use hyper::{HeaderMap, Method};
use mini_static::{ResponseBody, Server};
use tempfile::TempDir;
use tokio::runtime::Builder;
const SMALL_FILE_BYTES: usize = 1024;
const LARGE_FILE_BYTES: usize = 1024 * 1024;
fn build_fixture_root() -> TempDir {
let dir = tempfile::tempdir().expect("create fixture tempdir");
let root = dir.path();
fs::write(root.join("small.txt"), vec![b'a'; SMALL_FILE_BYTES]).expect("write small fixture");
fs::write(root.join("large.bin"), vec![b'b'; LARGE_FILE_BYTES]).expect("write large fixture");
fs::write(root.join("asset.js"), vec![b'c'; SMALL_FILE_BYTES]).expect("write asset fixture");
fs::write(root.join("asset.js.br"), vec![b'd'; 256]).expect("write sidecar fixture");
dir
}
fn headers_with(name: &str, value: &str) -> HeaderMap {
let mut headers = HeaderMap::new();
headers.insert(
hyper::header::HeaderName::from_bytes(name.as_bytes()).expect("valid header name"),
value.parse().expect("valid header value"),
);
headers
}
async fn drain(server: &Server, method: &Method, path: &str, headers: &HeaderMap) -> Bytes {
let response: hyper::Response<ResponseBody> =
server.handle_request(method, path, headers).await;
response
.into_body()
.collect()
.await
.expect("body collection is infallible")
.to_bytes()
}
fn bench_handle_request(c: &mut Criterion) {
let runtime = Builder::new_current_thread()
.enable_all()
.build()
.expect("build tokio runtime");
let fixture = build_fixture_root();
let server = Server::new(fixture.path()).expect("canonicalize fixture root");
let no_headers = HeaderMap::new();
let etag = runtime.block_on(async {
server
.handle_request(&Method::GET, "/small.txt", &no_headers)
.await
.headers()
.get("ETag")
.expect("a 200 carries an ETag")
.to_str()
.expect("ETag is ASCII")
.to_string()
});
let revalidate = headers_with("if-none-match", &etag);
let accept_brotli = headers_with("accept-encoding", "br");
let mut group = c.benchmark_group("handle_request");
group.bench_function("small_file_200", |b| {
b.iter(|| runtime.block_on(drain(&server, &Method::GET, "/small.txt", &no_headers)))
});
group.bench_function("large_file_200", |b| {
b.iter(|| runtime.block_on(drain(&server, &Method::GET, "/large.bin", &no_headers)))
});
group.bench_function("not_modified_304", |b| {
b.iter(|| runtime.block_on(drain(&server, &Method::GET, "/small.txt", &revalidate)))
});
group.bench_function("sidecar_hit_200", |b| {
b.iter(|| runtime.block_on(drain(&server, &Method::GET, "/asset.js", &accept_brotli)))
});
group.finish();
}
criterion_group!(benches, bench_handle_request);
criterion_main!(benches);