creating a rust http testing client

This is an example of a rust http test client that i used to run test periodically. 


use clap::{Parser, ValueEnum};
use indicatif::{ProgressBar, ProgressStyle};
use reqwest::{Client, Method};
use std::collections::HashMap;
use tokio::time::{sleep, Duration}; // Added tokio sleep
use serde_json::Value;

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug)]
enum HttpMethod {
    Get,
    Post,
    Put,
}

#[derive(Parser, Debug)]
#[command(author, version, about = "A simple HTTP load tester")]
struct Config {
    /// HTTP Method to use
    #[arg(short, long, value_enum, default_value_t = HttpMethod::Get)]
    method: HttpMethod,

    /// Target URL endpoint
    #[arg(short, long)]
    url: String,

    /// Number of tests to run
    #[arg(short, long, default_value_t = 10)]
    count: u32,

    /// Delay between requests in milliseconds
    #[arg(short, long, default_value_t = 0)]
    delay: u64,

    /// JSON payload for POST/PUT requests (e.g., '{"key": "value"}')
    #[arg(short, long)]
    payload: Option<String>,

}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config::parse();
    let client = Client::new();


    // Pre-validate JSON payload if it exists
    let json_body: Option<Value> = if let Some(ref p) = config.payload {
        match serde_json::from_str(p) {
            Ok(v) => Some(v),
            Err(e) => {
                eprintln!("Error: Invalid JSON payload provided: {}", e);
                std::process::exit(1);
            }
        }
    } else {
        None
    };

    // Setup Progress Bar
    let pb = ProgressBar::new(config.count as u64);
    pb.set_style(ProgressStyle::default_bar()
        .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})")?
        .progress_chars("#>-"));

    let mut success_count = 0;
    let mut error_map: HashMap<u16, u32> = HashMap::new();

    for i in 0..config.count {
        let method = match config.method {
            HttpMethod::Get => Method::GET,
            HttpMethod::Post => Method::POST,
            HttpMethod::Put => Method::PUT,
        };

       

        if i > 0 && config.delay > 0 {
            sleep(Duration::from_millis(config.delay)).await;
        }


        // Build request and attach JSON if present
        let mut request_builder = client.request(method, &config.url);
        if let Some(ref body) = json_body {
            request_builder = request_builder.json(body);
        }
 
        match request_builder.send().await {
            Ok(response) => {
                let status = response.status().as_u16();
                if (200..300).contains(&status) {
                    success_count += 1;
                } else {
                    *error_map.entry(status).or_insert(0) += 1;
                }
            }
            Err(_) => {
                // Handle network/connection errors as 0
                *error_map.entry(0).or_insert(0) += 1;
            }
        }
        pb.inc(1);
    }

    pb.finish_with_message("Tests Complete");

    // --- Summary Report ---
    println!("\n--- Test Summary ---");
    println!("Total Requests: {}", config.count);
    println!("Success (20x):  {}", success_count);
   
    if !error_map.is_empty() {
        println!("Failures (grouped by status):");
        for (code, count) in error_map {
            let label = match code {
                0 => "Connection Error".to_string(),
                c if (400..500).contains(&c) => format!("{} (Client Error)", c),
                c if (500..600).contains(&c) => format!("{} (Server Error)", c),
                c => c.to_string(),
            };
            println!("  - {}: {}", label, count);
        }
    }

    Ok(())
}

Cargo.toml

[package]
name = "http-tester"
version = "0.1.0"
edition = "2024"

[dependencies]
clap = { version = "4.6.1", features = ["derive"] }
indicatif = "0.18.4"
reqwest = { version = "0.13.3", features = ["json"] }
serde_json = "1.0.149"
tokio = { version = "1.52.1", features = ["full"] }


Example code usage 


Running GET

cargo run -- --url https://www.google.com --count 10 --delay 1000

Running HTTP

cargo run -- --method post --url
https://www.google.com --count 10 --delay 1000
--payload '{"name": "rust-bot", "status": "active"}'






















Comments

Popular posts from this blog

vllm : Failed to infer device type

android studio kotlin source is null error

gemini cli getting file not defined error