use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
use fetchttp::*;
use std::hint::black_box;
use std::time::Duration;
use tokio::runtime::Runtime;
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
fn create_runtime() -> Runtime {
Runtime::new().unwrap()
}
async fn setup_mock_server() -> MockServer {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/bench"))
.respond_with(ResponseTemplate::new(200).set_body_string("benchmark response"))
.mount(&mock_server)
.await;
Mock::given(method("POST"))
.and(path("/bench"))
.respond_with(ResponseTemplate::new(201).set_body_string("created"))
.mount(&mock_server)
.await;
Mock::given(method("POST"))
.and(path("/json"))
.respond_with(
ResponseTemplate::new(200)
.set_body_string(r#"{"status": "success"}"#)
.insert_header("content-type", "application/json"),
)
.mount(&mock_server)
.await;
let large_body = "x".repeat(1024 * 100); Mock::given(method("GET"))
.and(path("/large"))
.respond_with(ResponseTemplate::new(200).set_body_string(large_body))
.mount(&mock_server)
.await;
mock_server
}
fn bench_headers_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("headers");
group.bench_function("create", |b| b.iter(|| black_box(Headers::new())));
group.bench_function("set_single", |b| {
b.iter(|| {
let mut headers = Headers::new();
headers.set("content-type", "application/json").unwrap();
black_box(())
})
});
group.bench_function("set_multiple", |b| {
b.iter(|| {
let mut headers = Headers::new();
headers.set("content-type", "application/json").unwrap();
headers.set("accept", "application/json").unwrap();
headers.set("user-agent", "fetchttp/0.1.0").unwrap();
headers.set("authorization", "Bearer token").unwrap();
headers.set("x-custom", "value").unwrap();
black_box(())
})
});
group.bench_function("get", |b| {
let mut headers = Headers::new();
headers.set("content-type", "application/json").unwrap();
b.iter(|| black_box(headers.get("content-type").unwrap()))
});
group.bench_function("has", |b| {
let mut headers = Headers::new();
headers.set("content-type", "application/json").unwrap();
b.iter(|| black_box(headers.has("content-type").unwrap()))
});
group.bench_function("append", |b| {
let mut headers = Headers::new();
headers.set("accept", "application/json").unwrap();
b.iter(|| {
let mut h = headers.clone();
h.append("accept", "text/plain").unwrap();
black_box(())
})
});
group.finish();
}
fn bench_body_operations(c: &mut Criterion) {
let rt = create_runtime();
let mut group = c.benchmark_group("body");
group.bench_function("create_text", |b| {
b.iter(|| black_box(ReadableStream::from_text("Hello, World!")))
});
group.bench_function("create_bytes", |b| {
let data = vec![0u8; 1024];
b.iter(|| black_box(ReadableStream::from_bytes(bytes::Bytes::from(data.clone()))))
});
group.bench_function("create_json", |b| {
let value = serde_json::json!({"key": "value", "number": 42});
b.iter(|| black_box(ReadableStream::from_json(&value)))
});
group.bench_function("text_consumption", |b| {
b.to_async(&rt).iter(|| async {
let stream = ReadableStream::from_text("Hello, World!");
black_box(stream.text().await.unwrap())
})
});
group.bench_function("json_consumption", |b| {
let value = serde_json::json!({"key": "value", "number": 42});
b.to_async(&rt).iter(|| async {
let stream = ReadableStream::from_json(&value);
let result: serde_json::Value = stream.json().await.unwrap();
black_box(result)
})
});
group.bench_function("bytes_consumption", |b| {
let data = vec![0u8; 1024];
b.to_async(&rt).iter(|| async {
let stream = ReadableStream::from_bytes(bytes::Bytes::from(data.clone()));
black_box(stream.array_buffer().await.unwrap())
})
});
group.finish();
}
fn bench_request_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("request");
group.bench_function("create_simple", |b| {
b.iter(|| black_box(Request::new("https://example.com", None).unwrap()))
});
group.bench_function("create_with_headers", |b| {
b.iter(|| {
let mut headers = Headers::new();
headers.set("content-type", "application/json").unwrap();
headers.set("accept", "application/json").unwrap();
let mut init = RequestInit::new();
init.headers = Some(headers);
black_box(Request::new("https://example.com", Some(init)).unwrap())
})
});
group.bench_function("create_with_body", |b| {
b.iter(|| {
let mut init = RequestInit::new();
init.method = Some("POST".to_string());
init.body = Some(ReadableStream::from_text("test body"));
black_box(Request::new("https://example.com", Some(init)).unwrap())
})
});
group.bench_function("create_full_init", |b| {
b.iter(|| {
let mut headers = Headers::new();
headers.set("content-type", "application/json").unwrap();
headers.set("accept", "application/json").unwrap();
headers.set("user-agent", "fetchttp/0.1.0").unwrap();
let mut init = RequestInit::new();
init.method = Some("POST".to_string());
init.headers = Some(headers);
init.body = Some(ReadableStream::from_text("test body"));
init.mode = Some(RequestMode::Cors);
init.credentials = Some(RequestCredentials::Include);
black_box(Request::new("https://example.com", Some(init)).unwrap())
})
});
group.finish();
}
fn bench_response_creation(c: &mut Criterion) {
let mut group = c.benchmark_group("response");
group.bench_function("create_simple", |b| {
b.iter(|| black_box(Response::new(None, None).unwrap()))
});
group.bench_function("create_with_headers", |b| {
b.iter(|| {
let mut headers = Headers::new();
headers.set("content-type", "application/json").unwrap();
headers.set("x-custom", "value").unwrap();
let mut init = ResponseInit::new();
init.headers = Some(headers);
black_box(Response::new(None, Some(init)).unwrap())
})
});
group.bench_function("create_with_body", |b| {
b.iter(|| {
let body = ReadableStream::from_text("response body");
black_box(Response::new(Some(body), None).unwrap())
})
});
group.bench_function("error", |b| b.iter(|| black_box(Response::error())));
group.bench_function("redirect", |b| {
b.iter(|| black_box(Response::redirect("https://example.com", Some(302)).unwrap()))
});
group.finish();
}
fn bench_fetch_operations(c: &mut Criterion) {
let rt = create_runtime();
let mock_server = rt.block_on(setup_mock_server());
let base_url = mock_server.uri();
let mut group = c.benchmark_group("fetch");
group.measurement_time(Duration::from_secs(30));
group.sample_size(100);
group.bench_function("get_request", |b| {
let url = format!("{}/bench", base_url);
b.to_async(&rt).iter(|| async {
let response = fetch(&url, None).await.unwrap();
black_box(response.text().await.unwrap())
})
});
group.bench_function("post_request", |b| {
let url = format!("{}/bench", base_url);
b.to_async(&rt).iter(|| async {
let mut init = RequestInit::new();
init.method = Some("POST".to_string());
init.body = Some(ReadableStream::from_text("test data"));
let response = fetch(&url, Some(init)).await.unwrap();
black_box(response.text().await.unwrap())
})
});
group.bench_function("json_request", |b| {
let url = format!("{}/json", base_url);
b.to_async(&rt).iter(|| async {
let data = serde_json::json!({"key": "value"});
let mut init = RequestInit::new();
init.method = Some("POST".to_string());
init.body = Some(ReadableStream::from_json(&data));
let response = fetch(&url, Some(init)).await.unwrap();
let result: serde_json::Value = response.json().await.unwrap();
black_box(result)
})
});
group.bench_function("large_response", |b| {
let url = format!("{}/large", base_url);
b.to_async(&rt).iter(|| async {
let response = fetch(&url, None).await.unwrap();
black_box(response.text().await.unwrap())
})
});
for concurrent_requests in [1, 5, 10, 20].iter() {
group.bench_with_input(
BenchmarkId::new("concurrent_requests", concurrent_requests),
concurrent_requests,
|b, &concurrent_requests| {
let url = format!("{}/bench", base_url);
b.to_async(&rt).iter(|| async {
let futures: Vec<_> = (0..concurrent_requests)
.map(|_| {
let url_clone = url.clone();
async move { fetch(&url_clone, None).await }
})
.collect();
let responses = futures::future::join_all(futures).await;
for response in responses {
black_box(response.unwrap().text().await.unwrap());
}
})
},
);
}
group.finish();
}
fn bench_memory_usage(c: &mut Criterion) {
let mut group = c.benchmark_group("memory");
group.bench_function("headers_memory_100", |b| {
b.iter(|| {
let mut headers_vec = Vec::new();
for i in 0..100 {
let mut headers = Headers::new();
headers
.set(&format!("header-{}", i), &format!("value-{}", i))
.unwrap();
headers_vec.push(headers);
}
black_box(headers_vec)
})
});
group.bench_function("requests_memory_100", |b| {
b.iter(|| {
let mut requests_vec = Vec::new();
for i in 0..100 {
let request = Request::new(&format!("https://example.com/{}", i), None).unwrap();
requests_vec.push(request);
}
black_box(requests_vec)
})
});
group.bench_function("responses_memory_100", |b| {
b.iter(|| {
let mut responses_vec = Vec::new();
for i in 0..100 {
let response = Response::new(
Some(ReadableStream::from_text(&format!("body-{}", i))),
None,
)
.unwrap();
responses_vec.push(response);
}
black_box(responses_vec)
})
});
group.finish();
}
criterion_group!(
benches,
bench_headers_operations,
bench_body_operations,
bench_request_creation,
bench_response_creation,
bench_fetch_operations,
bench_memory_usage
);
criterion_main!(benches);