scanclient 0.1.0

High-performance, observable HTTP client for Santh security scanners
Documentation

scanclient — HTTP client tuned for security scanning

License Tests Crates.io

Why

Security scanners perform huge numbers of HTTP requests under strict resource constraints and retry semantics. scanclient gives one production-oriented client with timeout, retry, proxy, rate limiting, and request observability baked into the crate.

It wraps reqwest but keeps a small, scan-specific API focused on scan workloads: easy defaults, bounded retries, response helpers, and typed errors.

Quick Start

use scanclient::{HttpConfig, ScanClient};

#[tokio::main]
async fn main() -> scanclient::Result<()> {
    let mut config = HttpConfig::default();
    config.timeout_secs = 5;
    config.max_retries = 2;

    let client = ScanClient::from_config(config)?;
    let response = client.get("https://example.com").await?;

    println!("status={}", response.status());
    if let Ok(body) = response.body_text() {
        println!("len={}", body.len());
    }
    Ok(())
}

Features

  • Thin ScanClient API (get, head, post, request, execute).
  • Configurable retries with exponential backoff and idempotent status retry policy.
  • Optional rate limiting and proxy support.
  • Request/response helpers (body_text, contains, header lookup).
  • TOML load/parse for client configuration.

TOML Configuration

Use HttpConfig::from_toml or HttpConfig::load.

timeout_secs = 10
max_retries = 3
retry_delay_ms = 250
max_redirects = 5
user_agent = "SanthScanner/1.0"
tls_verify = false
rate_limit_per_sec = 8

[custom_headers]
X-Scanner = "santh"
use scanclient::{HttpConfig, ScanClient};

let cfg = HttpConfig::from_toml(r#"timeout_secs=12\nrate_limit_per_sec=5\n"#).unwrap();
let _client = ScanClient::from_config(cfg);

API Overview

  • HttpConfig: configuration model.
  • ScanClient::from_config: create client.
  • ScanClient::get, head, post, request, execute.
  • ScanResponse: typed response wrapper (status, headers, body_*, contains).
  • Error, Result: crate-level error handling.

Examples

1) Submit a POST and inspect response

use scanclient::{HttpConfig, ScanClient};

#[tokio::main]
async fn main() -> scanclient::Result<()> {
    let client = ScanClient::from_config(HttpConfig::default())?;
    let response = client.post("https://httpbin.org/post", "a=b&c=d").await?;

    println!("status={} contains=a={}", response.status(), response.contains("args"));
    Ok(())
}

2) Reuse a custom request with method + execute

use reqwest::Method;
use scanclient::{HttpConfig, ScanClient};

#[tokio::main]
async fn main() -> scanclient::Result<()> {
    let client = ScanClient::from_config(HttpConfig::default())?;
    let builder = client.request(Method::HEAD, "https://example.com");
    let response = client.execute(builder).await?;
    println!("ok={}", response.status().is_success());
    Ok(())
}

3) Load settings from TOML in scanner bootstrap

use scanclient::HttpConfig;

let cfg = HttpConfig::from_toml(r#"
user_agent = "MyScanner/2.0"
timeout_secs = 8
max_redirects = 2
"#).unwrap();
println!("ua={}", cfg.user_agent);

Traits

scanclient does not define custom traits in the public API.

Related Crates

License

MIT, Corum Collective LLC

Docs: https://docs.rs/scanclient

Santh ecosystem: https://santh.io