Hey @etnt!
I ran roadrunner against the built-in httpd, using the same load test we use for cowboy and elli. Two httpd setups: plain httpd (a small hand-written do/1, no extra deps) and httpd + httpd_router.
Median of 3 runs, 50 clients, HTTP/1.1, loopback, OTP 29 (req/s, higher is better):
| scenario | roadrunner | httpd | httpd + httpd_router |
|---|---|---|---|
| hello | 292k | 183k | 178k |
| json | 266k | 164k | 156k |
| echo | 253k | 149k | 138k |
| routing, 100 paths | 244k | 150k | 64k |
A few friendly notes:
- The built-in server is fast, and httpd_router adds almost no cost when there is just one route. Nice work.
- The slow part is matching many routes: httpd_router reads and scans its full route list on every request (
ets:tab2list/1), so it slows down with 100 routes. Keeping the routes ready instead of rebuilding them each time would help a lot here. - One tip if you test httpd yourself: by default it groups small TCP writes together (Nagle’s algorithm), which adds about 40ms to each keep-alive request on loopback. Turn it off with
{socket_type, {ip_comm, [{nodelay, true}]}}andhelloscenario goes from ~1k to ~180k req/s.
On features, the built-in httpd is HTTP/1.1 only, which is the trade-off for staying dependency-free:
| feature | roadrunner | httpd | httpd + httpd_router |
|---|---|---|---|
| HTTP/2 | yes | no | no |
| HTTP/3 | yes | no | no |
| WebSocket | yes | no | no |
| routing + path params | yes | manual | yes |
| extra deps | telemetry | none | httpd_router |
So if you want to stay dependency-free and only need HTTP/1.1, httpd is a solid pick, and httpd_router adds clean routing on top with almost no cost.
The numbers move around a bit (~15% each run), my machine was a little busy, and loopback (same machine) hides the real network cost, so take them as rough. I’m gonna add a full comparison in a PR shortly.
Cheers!