Bondy Connect
Skip to content

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 GatewayHTTP Connector
DirectionHTTP → WAMPWAMP → HTTP
InputIncoming HTTP requestIncoming WAMP Call
OutputWAMP call result as HTTP responseOutgoing HTTP request result as WAMP response
Use caseExpose WAMP services as REST APIsConsume REST APIs from WAMP clients
ConfigurationJSON API Specificationbondy.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 / Error

Step-by-step ​

  1. Custom headers — If the call kwargs contain a _headers key, its value (a map of header name → value) is extracted and merged with the default Content-Type and Accept headers. The _headers key is removed before further processing.

  2. 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 immediate wamp.error.invalid_argument error.

  3. 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)
  4. 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.

  5. HTTP request — The request is sent to the upstream service with configurable retries and exponential backoff on connection failures. The procedure's timeout bounds the whole call, retries included.

  6. 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=lines

POST, 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:

json
{
    "_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, or none encoding)
  • 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_in value 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 from Basic base64(user:pass)), or basic_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 StatusWAMP Error URI
3xxbondy.error.bad_gateway
400wamp.error.invalid_argument
401wamp.error.not_authorized
403wamp.error.not_authorized
404bondy.error.not_found
408wamp.error.timeout
422wamp.error.invalid_argument
429bondy.error.too_many_requests
4xx (other)wamp.error.invalid_argument
502bondy.error.bad_gateway
503bondy.error.bad_gateway
504wamp.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:

ConditionWAMP Error URIstatus
The connect, the pool checkout or the response outlasts the procedure's timeoutbondy.error.gateway_timeout504
The service's connection pool is downwamp.error.unavailable503
The service's secrets are not resolved yet, or the auth token fetch failsbondy.error.unavailable503
Any other transport failurebondy.error.bad_gateway502

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):

json
{
    "status": 200,
    "body": {"id": "INV-001", "amount": 1500}
}

Error (non-2xx):

The WAMP error kwargs contain the HTTP status and response body:

json
{
    "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:

  1. Invalidates the cached auth token
  2. Fetches a fresh token from the auth provider
  3. Retries the request once with the new token

If the retry also fails, the error response is returned normally.