High-performance HTTP / SIP / WebSocket load testing tool written in Rust.
Async I/O (Tokio), HdrHistogram latency, pluggable load models, multi-step scenarios, distributed workers, and a local Web UI — one binary, no runtime deps.
- HTTP/1.1 · HTTP/2 · experimental HTTP/3 — connection pooling, proxy (HTTP/SOCKS5), retries, dry-run
- Load models — closed-loop, constant RPS, ramp, burst, step, peak, sawtooth, custom wave
- Scenarios — multi-step flows with JSONPath / regex extraction, think time, data sources (CSV/JSON/faker)
- SIP / VoIP — CPS + concurrent calls, RTP stats, answer/fail metrics
- WebSocket · HAR record/replay · experimental gRPC
- Reports — TUI, JSON, HTML + PNG charts, Prometheus, Markdown, CSV
- Web UI — browser dashboard with live RPS / P99 via SSE
- Distributed — worker agents + coordinator (HdrHistogram merge for accurate global percentiles)
- Scripting — Lua or WASM per-request transforms
# From this repo
cargo install --path .
# Or build a release binary
cargo build --release
./target/release/lingedge --helpRequires a recent Rust toolchain (edition = "2021").
# Local Web UI
lingedge ui
# → http://127.0.0.1:8787/
# Closed-loop (fixed concurrency)
lingedge GET https://api.example.com/users -c 50 -d 30s
# Constant rate (open-loop)
lingedge GET https://api.example.com/users --rate 1000 -d 60s
# Ramp + TUI + multi-format export
lingedge GET https://api.example.com/users \
--ramp-start 10 --ramp-end 500 --ramp-up 20s -d 60s \
--tui -o ./results/run --format all
# Config file
lingedge run --config examples/burst.yaml
# SIP / VoIP (simulate without a real UA)
lingedge sip 'sip:bob@127.0.0.1:5060' --simulate -o ./results/voip --format all| Command | Description |
|---|---|
lingedge ui |
Local Web UI console |
lingedge GET/POST/... <URL> |
Quick HTTP load test |
lingedge run --config <FILE> |
YAML / JSON / TOML config |
lingedge sip <URI> |
SIP / VoIP CPS load test |
lingedge bench <URL> |
Fixed-iteration latency bench |
lingedge ws <URL> |
WebSocket load test |
lingedge grpc --endpoint ... |
Experimental gRPC-over-HTTP |
lingedge record |
HTTP forward-proxy → HAR |
lingedge replay --har <FILE> |
HAR multi-entry replay |
lingedge compare <A> <B> |
Diff two JSON reports |
lingedge worker |
Distributed worker agent |
lingedge dist |
Distributed coordinator |
lingedge ui --bind 127.0.0.1:8787
# open http://127.0.0.1:8787/ (--no-open to skip browser)Configure URL and load model in the form, Start to run, watch live charts via SSE. Default bind is loopback only.
# On each worker host (optional auth + TLS)
lingedge worker --bind 0.0.0.0:9800 --token "$LINGEDGE_TOKEN" \
--tls-cert ./cert.pem --tls-key ./key.pem
# Coordinator
lingedge dist --config examples/quick.yaml \
--workers 10.0.0.1:9800,10.0.0.2:9800 \
--token "$LINGEDGE_TOKEN" --tls --insecure
# Or via run
lingedge run --config examples/quick.yaml \
--workers 10.0.0.1:9800 --workers 10.0.0.2:9800The coordinator health-checks workers, streams live progress (/v1/progress), retries with exponential backoff, and merges raw HdrHistograms for accurate global percentiles.
# Scenario / SIP fan-out
lingedge run -c examples/scenario.yaml --workers host1:9800,host2:9800 \
--progress-out ./progress.jsonl
lingedge sip 'sip:bob@127.0.0.1:5060' --simulate --workers 127.0.0.1:9800# Lua: export on_request(ctx) → { url?, body?, headers? } | nil
lingedge GET https://api.example.com/x \
--script examples/scripts/on_request.lua -c 5 -d 5s
# WASM: export alloc / on_request (JSON in → packed ptr+len out)
lingedge GET https://api.example.com/x --script ./hook.wasm -c 5 -d 5s| Flag | Meaning |
|---|---|
--script <FILE> |
Lua (.lua) or WASM (.wasm) per-request transform |
--workers host:port |
Fan-out to remote workers (repeatable) |
--dry-run |
Print plan, send nothing |
--proxy <URL> |
HTTP / SOCKS5 proxy |
--retries N |
Exponential backoff retries |
--slow-threshold 500ms |
Slow-request counter |
--metrics-addr 127.0.0.1:9100 |
Live Prometheus /metrics |
-v / --verbose |
Debug logs |
--format json,html,png,prom,md,csv,all |
Report formats |
-o <DIR> |
Output directory for reports |
Set load.model in config (or use CLI flags for closed / constant / ramp):
| Model | Behavior |
|---|---|
closed |
Fixed concurrency (closed-loop) |
constant |
Fixed arrival rate (open-loop RPS) |
ramp |
Linear ramp-up / hold |
burst |
Periodic spikes |
step |
Stair-step concurrency or rate |
peak |
Short peak then settle |
sawtooth |
Cyclic rise and fall |
wave |
Custom waveform points |
See examples/burst.yaml, examples/step.yaml, examples/peak.yaml, examples/quick.yaml.
# examples/scenario.yaml
load:
concurrency: 5
duration: 10s
scenario:
name: login_then_me
steps:
- type: request
method: POST
url: http://127.0.0.1:8080/login
body: '{"user":"demo"}'
extract:
token:
type: json_path
path: /token
- type: think
duration: 50ms
- type: request
method: GET
url: http://127.0.0.1:8080/me
headers:
Authorization: "Bearer ${token}"| Format | Notes |
|---|---|
| Terminal | Summary after each run |
| TUI | Live dashboard (--tui) |
| JSON | Machine-readable, for compare |
| HTML + PNG | Charts under <out>_charts/ |
| Prometheus | Textfile and/or --metrics-addr |
| Markdown / CSV | Lightweight exports |
| Web UI | Live SSE charts |
| Module | Responsibility |
|---|---|
cli/ |
Clap parsing, subcommand dispatch |
config/ |
YAML / JSON / TOML loading |
http/ |
Reqwest client, pool, request builder |
load/ |
Pluggable LoadModel strategies |
scenario/ |
Multi-step engine + extraction |
data/ |
CSV / JSON sources, templates, faker |
metrics/ |
HdrHistogram, counters, VoIP / system |
report/ |
TUI, JSON/HTML/PNG/Prom/MD/CSV |
ui/ |
Local Web UI + SSE |
protocol/ |
WebSocket, gRPC, HAR, SIP/RTP, record |
distributed/ |
Worker + coordinator |
script/ |
Lua / WASM hooks |
Design notes: see DESIGN.md. Agent conventions: see AGENTS.md.
cargo test
cargo clippy
cargo fmt --check
cargo run -- ui --no-open
cargo run -- GET http://127.0.0.1:8080/ -c 10 -d 5s --dry-runOptional experimental HTTP/3 (QUIC via reqwest + quinn):
# .cargo/config.toml already sets --cfg reqwest_unstable for this repo
cargo build --release --features http3
./target/release/lingedge GET https://cloudflare-quic.com/ --http3 -c 10 -d 5sMIT