Design notes
Pipeline
.http text --parser--> ParsedFile --scope/order--> dependency graph --runner--> Results --report--> output
- Parse (
parser.py): split on###, then per block read file variables, directives, request line, headers, body, and??assertions. Every error carries the 1-based line number of the offending line. Assertion syntax is validated here too (assertions.parse), so a typo never reaches the network. - Order (
scope.py): scan every templated string of every request (URL, headers, body, parts, assertions) for{{name.response...}}references. Plain variables are followed transitively, so@auth = Bearer {{login.response.body.$.token}}makes every request that uses{{auth}}depend onlogin.# @afteradds explicit edges. - Select (
runner.select):--onlypicks requests by name, title or tag withfnmatchglobs, then adds the transitive dependencies so a selected request can always resolve its references. - Run (
runner.py): resolve templates, build the body (build.py), send with httpx, evaluate assertions, store the response in the scope so later requests can reference it. - Report: terminal rendering, saved responses, JUnit, JSON, Markdown.
Variable resolution
Lookup order: --var first, then file variables, then the selected environment. Values may themselves contain templates and are resolved recursively with a stack, so @a = {{b}} / @b = {{a}} raises variable cycle: a -> b -> a instead of recursing forever.
Worked example:
@baseUrl = http://h
@auth = Bearer {{login.response.body.$.token}}
### login
POST {{baseUrl}}/login
### me
GET {{baseUrl}}/me
Authorization: {{auth}}
Dependencies: me -> {login} (through auth). Execution order is login, me, even if me were written first in the file.
Ordering and cycles
The graph is checked with a depth-first search that tracks the current path; the first back edge reports the cycle, for example dependency cycle: a -> b -> a. A request referencing itself is a one-node cycle. References to unknown names are reported with the file and line of the referencing request.
The topological order is stable: requests keep file order except where a dependency must move earlier. Sequential runs (-j 1, the default) execute in that order.
Concurrency (-j N)
A request is submitted to a thread pool once none of its dependencies is still pending or running. Independent requests overlap; chains stay sequential. Requests with no declared relationship may run in any order, so state-changing sequences that rely on file order (delete, then check it is gone) need # @after. The cookie jar is shared by the whole run, so cookies set by one request are visible to requests that start after it.
If a request fails or is skipped its dependents are still attempted; they fail with a precise message (request 'login' has no response) rather than being silently dropped.
Secret masking
Masker hides values in anything that is printed or saved: the values from the private env file and every {{$env.X}} value (replaced by ****), header names that look sensitive (auth, token, secret, password, api-key, cookie), and JSON keys containing password, secret, token or api_key. The real values are only used for the actual request.
Retries, timeouts, redirects
--retries N retries transport errors and 502/503/504 with exponential backoff (0.2 s, 0.4 s, ...). A # @timeout directive overrides --timeout for one request. Redirects are followed unless --no-redirect or # @no-redirect. trust_env is off for httpx so proxy and CA environment variables do not change behaviour silently; use --proxy and --insecure explicitly.
curl and HAR
restbook curl resolves a request in dry mode (response references stay as {{...}} placeholders, since nothing was sent) and prints one command. Multipart requests use -F, file bodies --data-binary @file, and -L is emitted unless the request has # @no-redirect. import-curl parses a command with shlex and supports -X -H -d/--data* -F -u -A -b -e -L -I -m --url; -d without a content type gets curl's default (form-encoded) or JSON when the data looks like JSON. import-har converts each HAR entry, dropping HTTP/2 pseudo headers, Host and Content-Length. The exported curl command re-imports to the same method, URL, headers and body (covered by round-trip tests).
Exit codes
0 all requests passed (skipped requests do not count as failures), 1 at least one failed assertion or request error, 2 usage, parse, environment or dependency errors (nothing was sent).