Skip to main content

BenchmarkBuilder

Struct BenchmarkBuilder 

Source
pub struct BenchmarkBuilder { /* private fields */ }
Expand description

Builder for configuring and running benchmarks.

BenchmarkBuilder provides a fluent API for configuring all aspects of an HTTP benchmark, including target URL, concurrency, duration, rate limiting, custom request generation, and hooks.

§Examples

Basic benchmark:

let results = Benchmark::builder()
    .url("http://localhost:3000")
    .concurrency(50)
    .duration(Duration::from_secs(10))
    .build()?
    .run()
    .await?;

With rate limiting:

let results = Benchmark::builder()
    .url("http://localhost:3000")
    .rate(1000)  // 1000 req/s
    .duration(Duration::from_secs(30))
    .build()?
    .run()
    .await?;

Implementations§

Source§

impl BenchmarkBuilder

Source

pub fn new() -> Self

Create a new builder with default settings.

§Default Values
  • concurrency: 10
  • stop_condition: Infinite (runs until interrupted)
  • timeout: 30 seconds
  • max_retries: 3
§Examples
use httpress::BenchmarkBuilder;

let builder = BenchmarkBuilder::new();
Source

pub fn url(self, url: &str) -> Self

Set the target URL (required unless using request_fn).

The URL must be a valid HTTP or HTTPS URL.

§Constraints
  • Cannot be used together with request_fn
  • Exactly one of url() or request_fn() must be specified
§Examples
let builder = Benchmark::builder()
    .url("http://localhost:3000/api/endpoint");
Source

pub fn method(self, method: HttpMethod) -> Self

Set the HTTP method (default: GET).

Specifies which HTTP method to use for requests.

§Constraints

Cannot be used with request_fn (use RequestConfig instead).

§Examples
let builder = Benchmark::builder()
    .url("http://localhost:3000")
    .method(HttpMethod::Post);
Source

pub fn concurrency(self, n: usize) -> Self

Set the number of concurrent connections (default: 10).

This determines how many workers will send requests in parallel.

§Examples
let builder = Benchmark::builder()
    .url("http://localhost:3000")
    .concurrency(100);  // 100 concurrent workers
Source

pub fn duration(self, d: Duration) -> Self

Set the test duration.

The benchmark will run for the specified duration, then stop.

§Constraints

Cannot be used together with requests. If neither duration nor requests is specified, the benchmark runs infinitely until interrupted (Ctrl+C).

§Examples
let builder = Benchmark::builder()
    .url("http://localhost:3000")
    .duration(Duration::from_secs(30));  // Run for 30 seconds
Source

pub fn requests(self, n: usize) -> Self

Set the total number of requests.

The benchmark will stop after this many requests have been sent (across all workers).

§Constraints

Cannot be used together with duration. If neither requests nor duration is specified, the benchmark runs infinitely until interrupted (Ctrl+C).

§Examples
let builder = Benchmark::builder()
    .url("http://localhost:3000")
    .requests(10000);  // Send 10,000 requests total
Source

pub fn rate(self, rps: u64) -> Self

Set a fixed target rate in requests per second.

The benchmark will attempt to maintain this constant rate across all workers.

§Constraints

Cannot be used together with rate_fn.

§Examples
let builder = Benchmark::builder()
    .url("http://localhost:3000")
    .rate(1000)  // 1000 requests per second
    .duration(Duration::from_secs(30));
Source

pub fn rate_fn<F>(self, f: F) -> Self
where F: Fn(RateContext) -> f64 + Send + Sync + 'static,

Set a dynamic rate function.

Provides a function that calculates the target rate dynamically based on runtime benchmark state (elapsed time, request counts, etc.). This enables rate ramping, adaptive rate control, or scheduled rate changes.

§Constraints

Cannot be used together with rate.

§Examples
let results = Benchmark::builder()
    .url("http://localhost:3000")
    .rate_fn(|ctx: RateContext| {
        // Ramp from 100 to 1000 req/s over 10 seconds
        let progress = (ctx.elapsed.as_secs_f64() / 10.0).min(1.0);
        100.0 + (900.0 * progress)
    })
    .duration(Duration::from_secs(30))
    .build()?
    .run()
    .await?;
Source

pub fn header(self, key: &str, value: &str) -> Self

Add an HTTP header.

Can be called multiple times to add multiple headers.

§Constraints

Cannot be used with request_fn (use RequestConfig instead).

§Examples
let builder = Benchmark::builder()
    .url("http://localhost:3000")
    .header("Content-Type", "application/json")
    .header("Authorization", "Bearer token123");
Source

pub fn body(self, body: &str) -> Self

Set the request body.

§Constraints

Cannot be used with request_fn (use RequestConfig instead).

§Examples
let builder = Benchmark::builder()
    .url("http://localhost:3000/api/users")
    .method(HttpMethod::Post)
    .header("Content-Type", "application/json")
    .body(r#"{"name": "John Doe"}"#);
Source

pub fn timeout(self, d: Duration) -> Self

Set the request timeout (default: 30s).

Requests that take longer than this duration will be cancelled and counted as failed.

§Examples
let builder = Benchmark::builder()
    .url("http://localhost:3000")
    .timeout(Duration::from_secs(10));  // 10 second timeout
Source

pub fn request_fn<F>(self, f: F) -> Self
where F: Fn(RequestContext) -> RequestConfig + Send + Sync + 'static,

Set a custom request generator function.

Provides a function that generates a unique RequestConfig for each request based on RequestContext. This enables dynamic request patterns like URL rotation, varying HTTP methods, or conditional headers/bodies.

§Constraints
  • Cannot be used together with url
  • Cannot be used with method, header, or body
  • Exactly one of url() or request_fn() must be specified
§Examples
let results = Benchmark::builder()
    .request_fn(|ctx: RequestContext| {
        // Rotate through 100 different user IDs
        let user_id = ctx.request_number % 100;

        RequestConfig {
            url: format!("http://localhost:3000/user/{}", user_id),
            method: HttpMethod::Get,
            headers: HashMap::new(),
            body: None,
        }
    })
    .concurrency(10)
    .requests(1000)
    .build()?
    .run()
    .await?;
Source

pub fn before_request<F>(self, f: F) -> Self
where F: Fn(BeforeRequestContext) -> HookAction + Send + Sync + 'static,

Add a before-request hook.

Before-request hooks are called before sending each HTTP request. They can be used to implement circuit breakers, conditional request execution, or custom metrics. Multiple hooks can be added and will be executed in order.

The hook receives BeforeRequestContext and returns HookAction to control whether the request should proceed.

§Examples
let results = Benchmark::builder()
    .url("http://localhost:3000")
    .before_request(|ctx: BeforeRequestContext| {
        // Implement circuit breaker
        let failure_rate = ctx.failed_requests as f64 / ctx.total_requests.max(1) as f64;
        if failure_rate > 0.5 && ctx.total_requests > 100 {
            HookAction::Abort
        } else {
            HookAction::Continue
        }
    })
    .requests(1000)
    .build()?
    .run()
    .await?;
Source

pub fn after_request<F>(self, f: F) -> Self
where F: Fn(AfterRequestContext) -> HookAction + Send + Sync + 'static,

Add an after-request hook.

After-request hooks are called after each HTTP request completes (or fails). They can be used for custom metrics collection, retry logic, or conditional behavior based on response. Multiple hooks can be added and will be executed in order.

The hook receives AfterRequestContext with latency and status information, and returns HookAction to control retry behavior.

§Examples
let slow_count = Arc::new(Mutex::new(0));
let slow_count_clone = slow_count.clone();

let results = Benchmark::builder()
    .url("http://localhost:3000")
    .after_request(move |ctx: AfterRequestContext| {
        // Track slow requests
        if ctx.latency > Duration::from_millis(100) {
            *slow_count_clone.lock().unwrap() += 1;
        }

        // Retry on 5xx errors
        if let Some(status) = ctx.status {
            if status >= 500 {
                return HookAction::Retry;
            }
        }
        HookAction::Continue
    })
    .max_retries(3)
    .requests(1000)
    .build()?
    .run()
    .await?;
Source

pub fn max_retries(self, n: usize) -> Self

Set maximum number of retries when hooks return Retry (default: 3).

When a hook returns HookAction::Retry, the request will be retried up to this many times. After exceeding this limit, the request is marked as failed.

§Examples
let builder = Benchmark::builder()
    .url("http://localhost:3000")
    .max_retries(5);  // Retry up to 5 times
Source

pub fn build(self) -> Result<Benchmark>

Build the benchmark.

Validates the configuration and constructs a Benchmark ready to run.

§Errors

Returns Error::InvalidConfig if:

  • Both url() and request_fn() are specified
  • Neither url() nor request_fn() is specified
  • Both rate() and rate_fn() are specified
  • method(), header(), or body() is used with request_fn()
§Examples
let benchmark = Benchmark::builder()
    .url("http://localhost:3000")
    .concurrency(50)
    .duration(Duration::from_secs(10))
    .build()?;  // Returns Result<Benchmark>

let results = benchmark.run().await?;

Trait Implementations§

Source§

impl Default for BenchmarkBuilder

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more