Bondy Connect
Skip to content

HTTP Transports: Longpoll & SSE ​

Bondy provides two HTTP-based transports for WAMP sessions, enabling WAMP communication in environments where WebSocket connections are unavailable or impractical.

Overview ​

HTTP Transports

HTTP transports allow clients to establish WAMP sessions using standard HTTP requests instead of WebSocket connections. They provide the same WAMP semantics (RPC and Pub/Sub) through a request/response interface combined with either long-polling or Server-Sent Events for server-to-client messaging.

Both transports support cookie-based authentication, designed to work with OIDC authentication: after a user authenticates via OIDC, the browser sends the resulting ticket cookie with every transport request, so the web login carries over to the WAMP session without a separate authentication step.

When to Use HTTP Transports ​

TransportBest For
WebSocketFull-duplex, low-latency WAMP communication (preferred when available)
LongpollEnvironments that block WebSocket (corporate proxies, restrictive firewalls)
SSEBrowser-based apps that need efficient server push with HTTP/2 multiplexing

Long Polling ​

The Longpoll transport uses a request/response cycle to exchange WAMP messages. The client sends messages via POST and receives messages by issuing blocking POST requests that the server holds open until a message is available or a timeout occurs.

Protocol ​

The Longpoll transport uses the wamp.2.json sub-protocol. All WAMP messages are JSON-encoded.

Session Lifecycle ​

Client                          Bondy
  │                               │
  │  POST /wamp/longpoll/open     │
  │  {"protocols":["wamp.2.json"]}│
  │──────────────────────────────>│
  │  200 {"transport":"<id>"}     │
  │<──────────────────────────────│
  │                               │
  │  POST /<id>/send              │
  │  [HELLO, ...]                 │
  │──────────────────────────────>│
  │  202 Accepted                 │
  │<──────────────────────────────│
  │                               │
  │  POST /<id>/receive           │
  │  (blocks until message ready) │
  │──────────────────────────────>│
  │  200 [WELCOME, ...]           │
  │<──────────────────────────────│
  │                               │
  │  ... send/receive cycle ...   │
  │                               │
  │  POST /<id>/close             │
  │──────────────────────────────>│
  │  202 Accepted                 │
  │<──────────────────────────────│

Endpoints ​

RouteMethodDescription
/wamp/longpoll/openPOSTCreate a new transport session
/wamp/longpoll/:transport_id/sendPOSTSend a WAMP message
/wamp/longpoll/:transport_id/receivePOSTLong-poll for a WAMP message
/wamp/longpoll/:transport_id/closePOSTTerminate the transport session

All endpoints also accept OPTIONS for CORS preflight.

Open ​

Creates a new transport session.

bash
curl -X POST "https://api.example.com/wamp/longpoll/open" \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: <csrf_token>" \
  -b "bondy_ticket_com.example.myrealm=<jwt>; bondy_csrf_com.example.myrealm=<csrf_token>" \
  -d '{"protocols": ["wamp.2.json"]}'
json
{
    "protocol": "wamp.2.json",
    "transport": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

If a bondy_ticket_<realm> cookie is present and valid, the ticket claims are stored in the transport session and used for cookie-based authentication.

Send ​

Sends a JSON-encoded WAMP message.

bash
curl -X POST "https://api.example.com/wamp/longpoll/<transport_id>/send" \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: <csrf_token>" \
  -b "bondy_ticket_com.example.myrealm=<jwt>; bondy_csrf_com.example.myrealm=<csrf_token>" \
  -d '[1, "com.example.myrealm", {"authmethods": ["cookie"], "roles": {"caller": {}, "subscriber": {}}}]'

Returns 202 Accepted on success, 404 if the transport is not found. Bondy refuses a body larger than 4 MiB with 413.

Receive ​

Blocks until a WAMP message is available or the timeout expires.

Returns 200 with the JSON-encoded WAMP message, or 204 No Content on timeout.

INFO

The listener's longpoll.poll_timeout sets how long Bondy holds a receive request, and defaults to 30 seconds. A client sends a new receive request immediately after each response, which keeps the long-polling loop running.

Close ​

Terminates the transport session and the associated WAMP session.

Returns 202 Accepted.

Server-Sent Events (SSE) ​

The SSE transport uses POST endpoints for client-to-server messages (same as Longpoll) and a persistent SSE connection for server-to-client messages. This provides more efficient server push compared to long-polling.

Protocol ​

The SSE transport uses the wamp.2.json.sse sub-protocol.

Session Lifecycle ​

Client                          Bondy
  │                               │
  │  POST /wamp/sse/open          │
  │  {"protocols":["wamp.2.json.sse"]}
  │──────────────────────────────>│
  │  200 {"transport":"<id>"}     │
  │<──────────────────────────────│
  │                               │
  │  GET /<id>/receive            │
  │──────────────────────────────>│
  │  200 text/event-stream        │
  │<──────────── SSE stream ──────│
  │                               │
  │  POST /<id>/send              │
  │  [HELLO, ...]                 │
  │──────────────────────────────>│
  │  202 Accepted                 │
  │<──────────────────────────────│
  │                               │
  │  event: wamp                  │
  │  data: [WELCOME, ...]         │
  │<──────────────────────────────│
  │                               │
  │  ... send/receive cycle ...   │
  │                               │
  │  POST /<id>/close             │
  │──────────────────────────────>│
  │  202 Accepted                 │
  │<──────────────────────────────│

Endpoints ​

RouteMethodDescription
/wamp/sse/openPOSTCreate a new transport session
/wamp/sse/:transport_id/sendPOSTSend a WAMP message
/wamp/sse/:transport_id/receiveGETOpen an SSE event stream
/wamp/sse/:transport_id/closePOSTTerminate the transport session

All endpoints also accept OPTIONS for CORS preflight.

Open ​

Identical to the Longpoll /open endpoint, but the protocol is wamp.2.json.sse:

bash
curl -X POST "https://api.example.com/wamp/sse/open" \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: <csrf_token>" \
  -b "bondy_ticket_com.example.myrealm=<jwt>; bondy_csrf_com.example.myrealm=<csrf_token>" \
  -d '{"protocols": ["wamp.2.json.sse"]}'
json
{
    "protocol": "wamp.2.json.sse",
    "transport": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

Send ​

Identical to the Longpoll /send endpoint.

Receive (SSE Stream) ​

Opens a persistent SSE connection. Unlike Longpoll, this is a GET request that returns a text/event-stream response.

bash
curl -N "https://api.example.com/wamp/sse/<transport_id>/receive" \
  -H "Accept: text/event-stream" \
  -b "bondy_ticket_com.example.myrealm=<jwt>"

WAMP messages arrive as SSE events:

event: wamp
data: [2, 1, {}]

event: wamp
data: [36, 1234, 5678, {}, ["Hello, world!"]]

Special events:

  • event: transport_error with data: session_terminated is sent when the transport session ends.
  • Keepalive comments (: keepalive) are sent at the listener's sse.ping.interval, 20 seconds by default, to prevent connection timeout.

Close ​

Identical to the Longpoll /close endpoint.

When a client opens a transport session (/open) and the request includes a valid bondy_ticket_<realm> cookie, Bondy uses cookie-based authentication to establish the WAMP session.

How It Works ​

  1. During /open, Bondy reads the bondy_ticket_<realm> cookie, verifies the JWT signature, and stores the ticket claims in the transport session.

  2. The client sends a WAMP HELLO message with authmethods: ["cookie"]:

    json
    [1, "com.example.myrealm", {
        "authmethods": ["cookie"],
        "roles": {
            "caller": {},
            "subscriber": {}
        }
    }]
  3. Bondy detects the stored auth claims and authenticates the session using the cookie auth method — no challenge is issued, and a WELCOME message is returned immediately.

  4. On subsequent /send and /receive requests, Bondy re-validates the ticket cookie to ensure the session remains authorized.

Important

The realm's authmethods configuration has to include both "oidcrp" and "cookie" for the OIDC-to-WAMP flow to work, and a source must allow cookie from the client's address:

json
{
    "uri": "com.example.myrealm",
    "authmethods": ["cryptosign", "wampcra", "ticket", "oidcrp", "cookie"]
}

CSRF Protection ​

CSRF validation is enforced on the following endpoints:

EndpointCSRF Required
/openYes
/sendYes
/receiveNo
/closeYes

The /receive endpoint is exempt because it is either a blocking read (Longpoll) or a GET SSE stream, neither of which can be exploited via CSRF.

To pass CSRF validation, include the X-CSRF-Token header with the value of the bondy_csrf_<realm> cookie:

javascript
const csrfToken = document.cookie
    .split('; ')
    .find(c => c.startsWith('bondy_csrf_com.example.myrealm='))
    ?.split('=')[1];

fetch('/wamp/longpoll/open', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': csrfToken
    },
    credentials: 'include',
    body: JSON.stringify({ protocols: ['wamp.2.json'] })
});

Error Responses ​

All transport endpoints return JSON error bodies:

StatusErrorDescription
400no_supported_protocolThe protocols array in /open doesn't contain a supported protocol
400invalid_jsonThe request body is not valid JSON
400method_not_allowedThe request uses a method the endpoint does not accept
400message_errorThe body of a /send request is not a valid WAMP message
401unauthorizedThe ticket cookie is missing, invalid, or doesn't match the session
403csrf_validation_failedThe X-CSRF-Token header is missing or doesn't match the CSRF cookie
404transport_not_foundThe transport_id doesn't correspond to an active session
413payload_too_largeLong-poll only: the request body is larger than 4 MiB

The two refusals of /open, no_supported_protocol and invalid_json, carry only the error key:

json
{
    "error": "invalid_json"
}

Every other error carries the standard error payload plus an error key that holds the short name above. A client can branch on the status or on the error key.

Transport Session Lifecycle ​

Each HTTP transport session is backed by a persistent server-side process that:

  • Holds the WAMP protocol state (session, subscriptions, registrations)
  • Buffers outbound WAMP messages until the client polls for them
  • Times out after prolonged inactivity (default: 1 hour)
  • Cleans up the WAMP session when the transport process terminates

Inactivity Timeout

If no client requests arrive within wamp.http_transport.idle_timeout, Bondy terminates the transport session and its WAMP session, and discards the queued messages of that transport. Each request resets the timeout. For SSE, an active SSE stream connection prevents the inactivity timeout from firing.

The outbound queue ​

A long-poll client is not connected at all between polls, and an SSE stream can drop and reconnect while the session survives. Messages for such a session therefore wait in a bounded queue per transport. The node-wide wamp.http_transport.queue.* keys bound it; WebSocket and RawSocket sessions never use it.

  • When a new message would exceed max_messages or max_bytes, Bondy evicts the oldest entry of that transport to make room. Bondy applies no other eviction policy.
  • A message older than message_ttl is not delivered: a poll skips it and the periodic sweep removes it.
  • The router's messages and the synchronous replies to the client's own messages, such as an inline ERROR, share one queue, in the order they were produced.

When the router ends a session with ABORT or GOODBYE, Bondy queues the terminal message, hands it to a long-poll request that is waiting at that moment, and then closes the session. A long-poll client that is between polls at that moment does not receive the terminal message: its next poll answers 404 with transport_not_found. A poll that is waiting when the session dies gets the same answer.