HTTP Connector
The HTTP Connector bridges WAMP RPC procedures to upstream HTTP/REST services, allowing WAMP clients to call registered procedures that are transparently translated into HTTP requests.
Overview
HTTP Connector
The HTTP Connector is a Bondy subsystem that registers WAMP procedures and, when called, translates the call arguments into an outgoing HTTP request to an upstream service. The HTTP response is then mapped back to a WAMP result or error. It enables WAMP clients to interact with external REST APIs without any HTTP-specific code.
The HTTP Connector is the inverse of the HTTP API Gateway:
| HTTP API Gateway | HTTP Connector | |
|---|---|---|
| Direction | HTTP → WAMP | WAMP → HTTP |
| Input | Incoming HTTP request | Incoming WAMP Call |
| Output | WAMP call result as HTTP response | Outgoing HTTP request result as WAMP response |
| Use case | Expose WAMP services as REST APIs | Consume REST APIs from WAMP clients |
| Configuration | JSON API Specification | bondy.conf service definitions |
How It Works
WAMP Call(KWArgs)
│
├─ 1. Extract custom headers from KWArgs["_headers"]
├─ 2. Interpolate path template variables (consumed from KWArgs)
├─ 3. Route remaining KWArgs by HTTP method:
│ GET / DELETE / HEAD → query parameters
│ POST / PUT / PATCH → JSON request body
├─ 4. Acquire auth token (via token cache)
├─ 5. Apply auth to headers/URL
└─ 6. HTTP request (with retries + auth retry on 401/403)
│
└─ HTTP Response → WAMP Result / ErrorStep-by-step
Custom headers — If the call kwargs contain a
_headerskey, its value (a map of header name → value) is extracted and merged with the defaultContent-TypeandAcceptheaders. The_headerskey is removed before further processing.Path interpolation — The procedure's path template (e.g.
/invoices/{{id}}) is evaluated against the remaining kwargs. Matched keys are consumed — they are removed from kwargs after substitution. Missing template variables cause an immediatewamp.error.invalid_argumenterror.KWArgs routing — The remaining kwargs (after path interpolation) are routed based on the HTTP method:
- GET, DELETE, HEAD → URL query parameters (body is empty)
- POST, PUT, PATCH → JSON request body (no query parameters)
Authentication — A token is acquired from the per-service token cache. If the cache is empty, the connector fetches a token from the configured auth endpoint. Tokens are cached with preemptive refresh.
HTTP request — The request is sent to the upstream service with configurable retries and exponential backoff on connection failures. The procedure's
timeoutbounds the whole call, retries included.Response mapping — The HTTP response is mapped back to a WAMP result (2xx) or error (non-2xx).
Architecture
The HTTP Connector runs as a separate OTP application (bondy_http_connector) started automatically by Bondy after the public listeners are ready. If no services are configured, the subsystem remains idle with no resource overhead.
Each configured service gets:
- A dedicated connection pool — A hackney HTTP connection pool with configurable size, timeouts, and health checks.
- A token cache — Per-service token caching with preemptive refresh and thundering herd protection. The cache uses a lock-free ETS read path for the hot path and serializes fetches through a gen_server to prevent concurrent duplicate token requests.
- One or more WAMP callees — Each service/realm pair spawns a process that registers the configured procedures with the Bondy Dealer.
Lazy Startup
The HTTP Connector only starts supervision tree children when services are configured. An empty configuration results in zero overhead.
Request Routing
Path Template Variables
Path templates use {{var}} placeholders that are filled from the call kwargs. Matched keys are consumed — they do not appear in the query string or request body.
Template: /orgs/{{org}}/invoices/{{id}}
KWArgs: {"org": "acme", "id": "42", "status": "paid"}
Result: /orgs/acme/invoices/42
Remaining: {"status": "paid"}GET, DELETE, HEAD
Remaining kwargs become URL query parameters. The request body is always empty.
Procedure: com.billing.get_invoice (GET /invoices/{{id}})
KWArgs: {"id": "INV-001", "expand": "lines"}
→ GET /invoices/INV-001?expand=linesPOST, PUT, PATCH
Remaining kwargs become the JSON request body. No query parameters are appended.
Procedure: com.billing.create_invoice (POST /invoices)
KWArgs: {"customer": "cust-42", "amount": 2500}
→ POST /invoices
Content-Type: application/json
{"customer":"cust-42","amount":2500}Custom Headers
Pass a _headers key in kwargs to inject custom HTTP headers:
{
"_headers": {"X-Request-ID": "req-42", "X-Tenant": "acme"},
"id": "INV-001"
}The _headers map is extracted first and merged with the default headers. It does not appear in query params or the request body.
Authentication
The HTTP Connector supports pluggable authentication for upstream services. The built-in generic module provides a declarative configuration for common patterns.
Token Acquisition
The connector can acquire tokens from an OAuth2 token endpoint (or any HTTP endpoint that returns a token). You configure:
- The HTTP method and URL for the token request
- Request body key-value pairs (supports
form,json, ornoneencoding) - Optional HTTP Basic authentication on the token request itself
- The JSON path to extract the token from the response
- The JSON path to extract the token TTL
Token Placement
Once acquired, the token is applied to each upstream request using one of:
- Header (default) — e.g.
Authorization: Bearer <token> - Query parameter — e.g.
?api_key=<token>
The format is configurable with a {{token}} placeholder.
Variable Interpolation
All auth-related URLs, headers, body values, and basic auth credentials support {{var}} interpolation from a configurable variable map. This allows you to keep credentials in one place and reference them throughout the auth configuration.
Token Cache
Tokens are cached per-service with:
- Configurable TTL — Defaults to 1 hour, or uses the
expires_invalue from the token response. - Preemptive refresh — Tokens are refreshed in the background before they expire (default: 1 minute before expiry).
- Thundering herd protection — Concurrent token requests from multiple callers are serialized; only one fetch is performed, and all callers receive the result.
- Auth rejection retry — If an upstream returns 401 or 403, the cached token is invalidated, a fresh token is fetched, and the request is retried once.
Secret Management
For production deployments, you can store credentials in an external secrets provider instead of the bondy.conf file. The HTTP Connector supports AWS Secrets Manager.
Secrets are resolved at startup and mapped into the auth variable bindings (auth.vars), overriding any static values. Each secret variable specifies:
- The JSON field name in the secret
- An optional transform:
none(raw value),basic_username(extract username fromBasic base64(user:pass)), orbasic_password(extract password)
Resilient Startup
If secret resolution fails at startup (e.g. AWS is unreachable), the service is registered immediately but marked as "not ready". WAMP calls receive a bondy.error.unavailable error with status 503. Secret resolution is retried in the background with exponential backoff until it succeeds.
Error Handling
HTTP Status to WAMP Error Mapping
| HTTP Status | WAMP Error URI |
|---|---|
| 3xx | bondy.error.bad_gateway |
| 400 | wamp.error.invalid_argument |
| 401 | wamp.error.not_authorized |
| 403 | wamp.error.not_authorized |
| 404 | bondy.error.not_found |
| 408 | wamp.error.timeout |
| 422 | wamp.error.invalid_argument |
| 429 | bondy.error.too_many_requests |
| 4xx (other) | wamp.error.invalid_argument |
| 502 | bondy.error.bad_gateway |
| 503 | bondy.error.bad_gateway |
| 504 | wamp.error.timeout |
| 5xx (other) | bondy.error.bad_gateway |
Failures Without an Upstream Response
A call can fail before the upstream sends a response. The connector answers it with an error of its own:
| Condition | WAMP Error URI | status |
|---|---|---|
The connect, the pool checkout or the response outlasts the procedure's timeout | bondy.error.gateway_timeout | 504 |
| The service's connection pool is down | wamp.error.unavailable | 503 |
| The service's secrets are not resolved yet, or the auth token fetch fails | bondy.error.unavailable | 503 |
| Any other transport failure | bondy.error.bad_gateway | 502 |
The error kwargs carry the status and a message. When the connector itself fails, the router answers the call with bondy.error.internal_error.
The HTTP API Gateway maps each of these URIs to the HTTP status in the table. So an HTTP client that reaches the connector through the gateway receives the same status.
WAMP Return Format
Success (2xx):
{
"status": 200,
"body": {"id": "INV-001", "amount": 1500}
}Error (non-2xx):
The WAMP error kwargs contain the HTTP status and response body:
{
"status": 404,
"body": {"error": "not found"}
}Retries
Bondy retries a request when it fails at the transport level, for example a refused connection. After an HTTP response, it retries only a 401 or 403, once, with a fresh token (see Auth Rejection Auto-Retry). It does not retry while the service's connection pool is marked down.
The procedure's timeout is one deadline for the whole call, measured from its start. Each attempt gets only the time left before the deadline. Bondy skips a retry whose wait would end past the deadline, and the retry after a token refresh shares the same deadline. So a request that timed out is not retried.
Before each retry Bondy waits a random time with jittered exponential backoff. The first wait is between 50 ms and 150 ms. Each later wait is drawn from the previous one, and no wait exceeds 200 ms. The wait is kept short because the connector runs inside the caller's dispatch path, so a long sleep would delay other calls queued behind it.
Default: 3 retries, 30 second timeout. Both are configurable per service.
Auth Rejection Auto-Retry
When the upstream returns 401 or 403, the connector automatically:
- Invalidates the cached auth token
- Fetches a fresh token from the auth provider
- Retries the request once with the new token
If the retry also fails, the error response is returned normally.