mockinx - compliant web server, broken on purpose
[MO-keen-x]: match + reply + serve + chaos = what you get back.
Codeless, easy config: OK · errors · CRUD · parallel · fast · slow · trickle · drop
Rules use yttp conventions for HTTP requests and responses.
|
| # → {"name": "Owl", "price": 5.99}
Reply rule
A reply rule encompasses four aspects:
match: # what to match (method, URI)
reply: # what to respond (status, payload)
serve: # how to serve it (pace, drop, limits)
chaos: # what can go wrong (probability → override)
match
match: # GET /api/data
match: # POST /api/data
match: # any method, /api/data
match: _ # match everything
reply
Reply is polymorphic — object, array, or crud.
yttp conventions are used: s: h: b:, shortcuts.
mockinx directives also use ! suffix to distinguish from literal data.
# static reply (content-type inferred from body; explicit h: overrides)
reply:
# status only
reply:
# generated body (rand!, pattern! are mockinx directives)
reply:
reply:
# wrong content-type (malformed response)
reply:
# sequence — array of replies, cycled per connection
reply:
-
-
# crud — in-memory REST resource
reply:
reply: # auto-increment IDs
reply: # UUID IDs
reply: # no data, id: "id", inc
# reflect — echo request back as JSON (for testing)
reply: # {i: {m, h, u, q}} (no body)
reply: # selected fields
reply: # include body (explicit only)
# fields: i.m (method), i.h (headers), i.u (path), i.q (query), i.b (body)
# file — serve body from a file (re-read on each request)
reply:
serve
How the response is served — delivery shaping and operational constraints:
serve:
# delivery shaping
pace: 5s # pacing: duration target (auto-chunked)
pace: 1kb@100ms # pacing: 1kb chunks at every 100ms
pace: 10kb/s # pacing: bandwidth cap
drop: 2kb # kill connection after N bytes
drop: 1s # kill connection after N time
hang: 10kb # stop sending after N bytes, keep connection open
hang: 1s # stop sending after N time, keep connection open
first_byte: 2s # time to first byte (delay)
# operational constraints (connections, rate per second)
conn:
conn:
conn:
rps:
timeout: 30s
Any scalar value supports jitter via ranges (min..max or value..percent):
serve:
pace: 4s..6s # random timespan
pace: 512b..2kb@50ms..150ms # random chunk size and sending interval
pace: 10kb/s..20% # random bandwidth 8kb/s..12kb/s
drop: 1kb..4kb # drop conn anywhere in that byte range
first_byte: 1s..10% # 900ms..1.1s
chaos
Probabilistic overrides for reply and/or serve. Each entry has a percentage p
and reply/serve overrides. Unspecified fields inherit from the rule's defaults.
# p is a percentage — unmatched remainder uses rule defaults
chaos:
- # 0.1% error
- # 0.05% drop after 1kb
- # 7% crawl
# remaining 92.85% normal (0 padding added for readability)
Examples
# Slow API with concurrency limit
|
# Large download, throttled, drops mid-stream
|
# Flaky auth endpoint
|
# CRUD resource with latency
|
# Mostly fine, occasional errors and slow responses
|
Managing rules: /_mx
# POST — append rules (single or array)
|
# GET — list active rules (most recent first)
|
# PUT — replace all rules (atomic reset + load)
|
# PUT with empty array — clear all rules
|
Rules are priority-ordered. Later rules take precedence.
Managing rules: config file
# rules.yaml — load with: mockinx 9999 -c rules.yaml
# see: ./tests/fixtures/rules.yaml
# equivalent to posting at /_mx - see yurl examples above.
# simple static reply
- match:
reply:
# CRUD resource
- match:
reply:
crud!:
data:
-
-
# slow endpoint with concurrency limit
- match:
reply:
serve:
# /toys/6 is flaky — overrides the CRUD rule (later = higher priority)
- match:
reply:
serve:
chaos:
-
-
Load with: mockinx -c rules.yaml
Performance
Zero overhead — mockinx matches raw axum throughput:
req/sec latency
raw TCP (no parsing) 194k 496µs
axum (baseline) 191k 502µs
mockinx (rule match) 190k 499µs
Run ./benches/run.sh (requires wrk).
Tech
Rust, axum, tokio. Single binary.