Contributing to Roadrunner
View SourceSetup
See .tool-versions for the required Erlang/OTP and rebar3 versions
(install via mise or
asdf).
git clone https://github.com/arizona-framework/roadrunner.git
cd roadrunner
make compile
Workflow
One command covers day-to-day development:
make test(ormake precommit, orrebar3 precommit) — run before every commit and before pushing. Formats-check Erlang viaerlfmt, compiles, runs xref + hank + dialyzer, runs the eunit + Common Test (incl. PropEr) suites with cover, fails if line coverage drops below 100 %, and builds the docs. CI runs the same command.
make help lists every target. Common ones:
make compile/make doc/make fmtmake eunit/make ct/make dialyzer/make xref/make hank(individual stages)make bench-quick/make wrk2-quick/make h2spec/make autobahnmake clean/make distclean
You can also invoke rebar3 <task> directly (the Makefile is just a
thin wrapper over the same targets). See rebar.config for the full
alias list.
Diagnostic / conformance scripts under scripts/ (bench.escript,
h2spec.sh, autobahn.escript, redbot.escript, wrk2_bench.sh)
are run directly; each script's header documents its requirements.
Test surface
PropEr properties via ct_property_test cover the surfaces where
input-shape coverage matters: roadrunner_uri (percent round-trip +
encode shape), roadrunner_qs (round-trip), roadrunner_cookie
(adversarial robustness), roadrunner_http1 (parsers never-crash +
incremental-feed equivalence), roadrunner_conn_loop (random recv /
drain / stray inputs, clean exit + slot release), and request_id
consistency between request_start / request_stop telemetry.
Malformed-input corpus: roadrunner_http1_corpus_tests exercises
HTTP/1.1 patterns lifted from the
llhttp test corpus and the
canonical request-smuggling vectors documented by
PortSwigger.
Conformance harnesses (run on-demand, not part of make precommit):
scripts/h2spec.sh— HTTP/2 (RFC 9113 + RFC 7541). Strict 100 % pass.scripts/autobahn.escript— WebSocket (RFC 6455 + RFC 7692). Strict 100 % across the full Autobahn|Testsuite fuzzingclient matrix, no exclusions.scripts/redbot.escript— HTTP/1.1 response hygiene via REDbot.
Benchmarking notes
Two complementary load drivers ship in this repo:
scripts/bench.escript— closed-loop. Each worker sends a request, waits for the response, sends the next. Reports throughput + p50/p99 from per-request timing. Easy to set up, no external dependency, but tail latency under load is deflated by Coordinated Omission. Used for the per-scenario matrix indocs/bench_results.md.scripts/wrk2_bench.sh— open-loop, via wrk2 running in Docker (cylab/wrk2:latest). Issues requests at a fixed rate regardless of server response and reports Coordinated-Omission-corrected HdrHistogram percentiles. Output:docs/wrk2_results.md. Needsdockeron PATH andrebar3 as test compilealready done; the script pulls the image automatically.
Run a quick wrk2 sanity check:
./scripts/wrk2_bench.sh --quick --scenarios hello
The full matrix takes ~2 hours at --runs 1 --duration 30s and
~10 hours at the canonical --runs 3 --duration 60s. Run on a
quiet machine — system noise inflates tail percentiles.
Both drivers' internals (worker model, latency aggregation,
loader-as-bottleneck conditions) are documented in
docs/bench_internals.md.
License
Roadrunner is licensed under the Apache License Version 2.0, for all code.
Reporting a bug
Roadrunner is not perfect software and will be buggy.
Bugs can be reported via GitHub issues: bug report.
Some contributors and maintainers may be unpaid developers working on Roadrunner, in their own time, with limited resources. We ask for respect and understanding, and will provide the same back.
If your contribution is an actual bug fix, we ask you to include tests that, not only show the issue is solved, but help prevent future regressions related to it.
Requesting or implementing a feature
Before requesting or implementing a new feature, do the following:
- search, in existing issues (open or closed), whether the feature might already be in the works, or has already been rejected,
- make sure you're using the latest software release (or even the latest code, if you're going for bleeding edge).
If this is done, open up a GitHub issues: feature request.
We may discuss details with you regarding the implementation, and its inclusion within the project.
We try to have as many of Roadrunner's features tested as possible. Everything that a user can do, and is repeatable in any way, should be tested, to guarantee backwards compatible.
Submitting your changes
Code Style
- run
rebar3 fmt(also part of theprecommitgate) —erlfmtenforces no trailing whitespace, 4-space indentation, and a 100-character soft line limit - write small functions whenever possible, and use descriptive names for functions and variables
- comment tricky or non-obvious decisions made to explain their rationale
- prefer modern OTP idioms — sigils for binary literals (
~"..."), triple-quoted multi-line strings ("""..."""),maybeexpressions for nested case chains, body recursion (cons on the way out) overlists:reverse(Acc), binary keys for wire-derived data (nobinary_to_atomon parsed names)
Committing your changes
Merging to the main branch will usually be preceded by a squash.
Commit messages use a plain imperative subject line — no
feat:/fix:/chore: semantic prefixes. Subject only, no body
unless the change genuinely needs explanation (a one-line subject
beats a paragraph of preamble).
While it's OK (and expected) for your commit messages to relate the why of a given change, be aware that the final commit (the merge one) will be the PR title — so make it specific. This also helps automated changelog generation.
Pull requests and branching
All fixes to Roadrunner end up requiring a +1 from one or more of the project's maintainers.
During the review process, you may be asked to correct or edit a few things before a final rebase to merge things. Do send edits as individual commits to allow for gradual and partial reviews to be done by reviewers.
Credits
Roadrunner has been improved by many contributors!