use criterion::{black_box, criterion_group, criterion_main, Criterion};
use derusted::mitm::{
http_parser::{parse_http1_request, parse_http1_response},
log_storage::LogStorage,
logging::{PiiRedactor, RequestMetadata},
};
use tempfile::TempDir;
fn create_test_metadata() -> RequestMetadata {
RequestMetadata {
timestamp: chrono::Utc::now().timestamp(),
method: "GET".to_string(),
host: "example.com".to_string(),
port: 443,
path: "/api/test".to_string(),
http_version: "HTTP/1.1".to_string(),
status_code: Some(200),
request_size: 1024,
response_size: 2048,
duration_ms: 150,
tls_version: Some("TLS 1.3".to_string()),
mitm_applied: true,
bypass_reason: None,
}
}
fn bench_http_request_parsing(c: &mut Criterion) {
let mut group = c.benchmark_group("http_request_parsing");
let small_request = b"GET /api/users?id=123 HTTP/1.1\r\nHost: example.com\r\nUser-Agent: Mozilla/5.0\r\nAccept: application/json\r\nAuthorization: Bearer token123\r\n\r\n";
group.bench_function("small_request_1kb", |b| {
b.iter(|| parse_http1_request(black_box(small_request)).unwrap())
});
let body_content = "x".repeat(3900);
let medium_request = format!(
"POST /api/data HTTP/1.1\r\nHost: example.com\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}",
body_content.len(),
body_content
);
group.bench_function("medium_request_4kb", |b| {
b.iter(|| parse_http1_request(black_box(medium_request.as_bytes())).unwrap())
});
let large_body = "y".repeat(7900);
let large_request = format!(
"POST /api/upload HTTP/1.1\r\nHost: api.example.com\r\nContent-Type: application/octet-stream\r\nContent-Length: {}\r\n\r\n{}",
large_body.len(),
large_body
);
group.bench_function("large_request_8kb", |b| {
b.iter(|| parse_http1_request(black_box(large_request.as_bytes())).unwrap())
});
group.finish();
}
fn bench_http_response_parsing(c: &mut Criterion) {
let mut group = c.benchmark_group("http_response_parsing");
let small_response = b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: 50\r\n\r\n{\"status\":\"success\",\"data\":{\"id\":123,\"name\":\"test\"}}";
group.bench_function("small_response_1kb", |b| {
b.iter(|| parse_http1_response(black_box(small_response)).unwrap())
});
let response_body = r#"{"data":["#.to_string() + &"x".repeat(3900) + "]}";
let medium_response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}",
response_body.len(),
response_body
);
group.bench_function("medium_response_4kb", |b| {
b.iter(|| parse_http1_response(black_box(medium_response.as_bytes())).unwrap())
});
let mut binary_response =
b"HTTP/1.1 200 OK\r\nContent-Type: image/png\r\nContent-Length: 1000\r\n\r\n".to_vec();
binary_response.extend_from_slice(&vec![0xFF; 1000]);
group.bench_function("binary_response_1kb", |b| {
b.iter(|| parse_http1_response(black_box(&binary_response)).unwrap())
});
group.finish();
}
fn bench_pii_redaction(c: &mut Criterion) {
let mut group = c.benchmark_group("pii_redaction");
let clean_path = "/api/users/123/profile?page=1&sort=name";
group.bench_function("no_pii", |b| {
b.iter(|| PiiRedactor::redact(black_box(clean_path)))
});
let path_with_cc = "/payment?card=4532-1234-5678-9010&amount=100";
group.bench_function("single_pii_credit_card", |b| {
b.iter(|| PiiRedactor::redact(black_box(path_with_cc)))
});
let path_multi_pii =
"/checkout?card=4532-1234-5678-9010&email=user@test.com&phone=555-123-4567&ssn=123-45-6789";
group.bench_function("multiple_pii", |b| {
b.iter(|| PiiRedactor::redact(black_box(path_multi_pii)))
});
let long_path = format!(
"/api/transaction?id=123&card=4532-1234-5678-9010&email=user@example.com&{}",
"a=b&".repeat(100)
);
group.bench_function("long_path_with_pii", |b| {
b.iter(|| PiiRedactor::redact(black_box(&long_path)))
});
group.finish();
}
fn bench_sampling(c: &mut Criterion) {
let mut group = c.benchmark_group("sampling");
group.bench_function("should_sample_check", |b| {
b.iter(|| {
PiiRedactor::should_sample(black_box(0.01)) })
});
group.finish();
}
fn bench_sqlite_logging(c: &mut Criterion) {
let mut group = c.benchmark_group("sqlite_logging");
let rt = tokio::runtime::Runtime::new().unwrap();
let storage = rt.block_on(async { LogStorage::new(":memory:").await.unwrap() });
let metadata = create_test_metadata();
group.bench_function("log_request_memory_db", |b| {
b.to_async(&rt)
.iter(|| async { storage.log_request(black_box(&metadata)).await.unwrap() })
});
let temp_dir = TempDir::new().unwrap();
let db_path = temp_dir.path().join("bench.db");
let disk_storage =
rt.block_on(async { LogStorage::new(db_path.to_str().unwrap()).await.unwrap() });
group.bench_function("log_request_disk_db", |b| {
b.to_async(&rt).iter(|| async {
disk_storage
.log_request(black_box(&metadata))
.await
.unwrap()
})
});
group.finish();
}
fn bench_sqlite_queries(c: &mut Criterion) {
let mut group = c.benchmark_group("sqlite_queries");
let rt = tokio::runtime::Runtime::new().unwrap();
let storage = rt.block_on(async {
let storage = LogStorage::new(":memory:").await.unwrap();
for i in 0..1000 {
let metadata = RequestMetadata {
timestamp: chrono::Utc::now().timestamp() - (i * 60),
method: "GET".to_string(),
host: format!("host{}.example.com", i % 10),
port: 443,
path: format!("/api/endpoint{}", i),
http_version: "HTTP/1.1".to_string(),
status_code: Some(200),
request_size: 1024,
response_size: 2048,
duration_ms: 150,
tls_version: Some("TLS 1.3".to_string()),
mitm_applied: true,
bypass_reason: None,
};
storage.log_request(&metadata).await.unwrap();
}
storage
});
group.bench_function("query_logs_by_time", |b| {
b.to_async(&rt).iter(|| async {
let start_ts = chrono::Utc::now().timestamp() - 3600;
let end_ts = chrono::Utc::now().timestamp();
storage
.query_logs(black_box(start_ts), black_box(end_ts), black_box(100))
.await
.unwrap()
})
});
group.bench_function("query_logs_by_host", |b| {
b.to_async(&rt).iter(|| async {
storage
.query_logs_by_host(black_box("host5.example.com"), black_box(100))
.await
.unwrap()
})
});
group.bench_function("count_logs", |b| {
b.to_async(&rt)
.iter(|| async { storage.count_logs().await.unwrap() })
});
group.finish();
}
fn bench_end_to_end_overhead(c: &mut Criterion) {
let mut group = c.benchmark_group("end_to_end");
let request_data =
b"GET /payment?card=4532-1234-5678-9010 HTTP/1.1\r\nHost: example.com\r\n\r\n";
let response_data =
b"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{\"status\":\"ok\"}";
group.bench_function("parse_request_response_redact", |b| {
b.iter(|| {
let req = parse_http1_request(black_box(request_data)).unwrap();
let resp = parse_http1_response(black_box(response_data)).unwrap();
let mut metadata = RequestMetadata {
timestamp: chrono::Utc::now().timestamp(),
method: req.method.clone(),
host: "example.com".to_string(),
port: 443,
path: req.path.clone(),
http_version: req.version.clone(),
status_code: Some(resp.status_code),
request_size: request_data.len(),
response_size: response_data.len(),
duration_ms: 100,
tls_version: Some("TLS 1.3".to_string()),
mitm_applied: true,
bypass_reason: None,
};
metadata.path = PiiRedactor::redact(&metadata.path);
black_box(metadata)
})
});
group.finish();
}
criterion_group!(
benches,
bench_http_request_parsing,
bench_http_response_parsing,
bench_pii_redaction,
bench_sampling,
bench_sqlite_logging,
bench_sqlite_queries,
bench_end_to_end_overhead
);
criterion_main!(benches);