On this page

HTTP Protocol Reference

Interface declaration:

api = service("api",
    interface("public", "http", 8080),
    ...
)

Methods

get(path="", headers={})

Send an HTTP GET request.

resp = api.public.get(path="/users/1")
resp = api.get(path="/users/1")  # shorthand when service has one HTTP interface

resp = api.get(path="/admin", headers={"Authorization": "Bearer token123"})

post(path="", body="", headers={})

Send an HTTP POST request.

resp = api.post(path="/users", body='{"name":"alice"}')
resp = api.post(path="/upload", body="raw data", headers={"Content-Type": "text/plain"})

put(path="", body="", headers={})

Send an HTTP PUT request.

resp = api.put(path="/users/1", body='{"name":"bob"}')

delete(path="", headers={})

Send an HTTP DELETE request.

resp = api.delete(path="/users/1")

patch(path="", body="", headers={})

Send an HTTP PATCH request.

resp = api.patch(path="/users/1", body='{"name":"charlie"}')

Response Object

All methods return a response with:

FieldTypeDescription
.statusintHTTP status code (200, 404, 500, etc.)
.bodystringRaw response body
.datadict/listAuto-decoded JSON (if body is valid JSON)
.okboolTrue if status is 2xx
.duration_msintRequest duration in milliseconds
.headersdictResponse headers
resp = api.get(path="/users")
assert_eq(resp.status, 200)
assert_true(resp.ok)
print(resp.data)          # [{"id": 1, "name": "alice"}, ...]
print(resp.data[0]["name"])  # "alice"
print(resp.duration_ms)   # 12

Fault Rules

Protocol-level faults use fault_assumption() with the interface as target:

response(method=, path=, status=, body=)

Return a custom HTTP response without forwarding to the service.

rate_limited = fault_assumption("rate_limited",
    target = api.public,
    rules = [response(method="POST", path="/orders*", status=429,
                      body='{"error":"too many requests"}')],
)
ParameterTypeDescription
methodstringHTTP method glob ("POST", "GET", "*")
pathstringPath glob ("/orders*", "/api/v1/*")
statusintHTTP status code to return
bodystringResponse body to return

error(method=, path=, status=, message=)

Same as response() — returns an error response.

server_error = fault_assumption("server_error",
    target = api.public,
    rules = [error(path="/health", status=500, message="internal error")],
)

delay(method=, path=, delay=)

Delay matching requests, then forward normally.

slow_search = fault_assumption("slow_search",
    target = api.public,
    rules = [delay(path="/search*", delay="2s")],
)

drop(method=, path=)

Close the connection without responding.

drop_uploads = fault_assumption("drop_uploads",
    target = api.public,
    rules = [drop(method="POST", path="/upload*")],
)

Seed / Reset Patterns

HTTP services are typically stateless — seed and reset are usually not needed. For services that cache state in memory:

api = service("api",
    interface("public", "http", 8080),
    build = "./api",
    reuse = True,
    reset = lambda: api.post(path="/admin/reset"),  # app-specific reset endpoint
)

Event Sources

HTTP services don’t have a native event source. Use stdout to capture application logs:

api = service("api",
    interface("public", "http", 8080),
    build = "./api",
    observe = [stdout(decoder=json_decoder())],
)