sondahub

sondahub / STOMP test server

STOMP test server

STOMP 1.0, 1.1 and 1.2 straight over WebSocket, with RabbitMQ’s destinations and a broker that is already busy: live topics from the hub’s activity, queues that hold the last minute of it, an orders queue that moves what you send, and an rpc queue that answers through a temp-queue. Over TCP too, through the bridge, on the same broker as AMQP.

Free and open: no key, no account. Each WebSocket connection gets a broker of its own.

Connect

WebSocket
wss://api.sondahub.com/stomp
Subprotocol
v12.stomp, v11.stomp or v10.stomp
Over TCP
through the bridge: node bridge.mjs amqp, then localhost:61713
STOMP
1.0, 1.1 and 1.2; heart-beats 10000,10000
Login
none (guest), or guest/guest, sonda/sondahub, viewer/viewer

Each WebSocket connection gets a broker of its own, already busy: the hub’s activity on amq.topic, a queue per API holding the last minute of it, an orders queue that takes orders through their life, and an rpc queue that answers. Over TCP the bridge serves STOMP on the same broker as AMQP.

JavaScript (@stomp/stompjs)
import { Client } from '@stomp/stompjs'

const client = new Client({ brokerURL: 'wss://api.sondahub.com/stomp' })
client.onConnect = () => {
  client.subscribe('/topic/fleet.telemetry', (m) => console.log(JSON.parse(m.body)))
  client.publish({ destination: '/queue/orders', body: JSON.stringify({ customer_id: 7, total: 42.5 }) })
}
client.activate()
Frames by hand, from any WebSocket client (^@ is the NUL byte; a message holding one whole frame may leave it off)
CONNECT
accept-version:1.2
host:sondahub
heart-beat:10000,10000

^@
SUBSCRIBE
id:0
destination:/topic/fleet.telemetry
ack:auto

^@
SEND
destination:/queue/orders
content-type:application/json
receipt:1

{"customer_id":7,"total":42.5}^@

In LockFlare Sonda: a STOMP item pointed at localhost:61713 with the bridge running — or a WebSocket request to wss://api.sondahub.com/stomp, then each frame as a message of its own, CONNECT first.

Destinations

DestinationWhat it does
/queue/nameSEND to the queue (declared if it is not there); SUBSCRIBE reads it — store, fleet and the other API queues, orders, rpc, yours.
/amq/queue/nameThe same, but the queue must exist: no declaring.
/topic/keySEND publishes to amq.topic with the key; SUBSCRIBE binds a queue of its own with the key as a pattern — /topic/fleet.telemetry, /topic/store.*, /topic/#.
/exchange/name/keySEND publishes to the exchange with the key; SUBSCRIBE binds a queue of its own to the exchange with the key.
/temp-queue/nameIn reply-to: a private reply queue, made when first used; the replies arrive with subscription:/temp-queue/name. The reply-to the other side sees is /reply-queue/….

What is there from the start: the queues store, fleet, bank, social, helpdesk, flights, identity, orders, orders.dead, rpc, and on /topic/ the routing keys store.orders, store.inventory, fleet.telemetry, fleet.devices, fleet.alerts, bank.fx and the rest — all of them.

Messages

SUBSCRIBE with ack:auto (the default), client (an ACK covers the messages before it on that subscription) or client-individual, and prefetch-count to hold the rest back until you ACK. NACK requeues — the message comes again with redelivered:true — or, with requeue:false, dead-letters it when its queue has a dead-letter exchange. Every frame takes a receipt header; BEGIN, COMMIT and ABORT group SENDs and ACKs; DISCONNECT with a receipt closes cleanly.

MESSAGE headerWhat it says
subscription, destination, message-idWhich subscription, where the message was published (/topic/store.orders, /queue/name, /exchange/name/key), and its id.
ackOn a client or client-individual subscription (STOMP 1.2): the id to ACK or NACK it by.
redeliveredtrue when it was requeued and comes again.
content-type, content-lengthAs published.
correlation-id, reply-to, expiration, persistent, priority, amqp-message-id, timestamp, type, user-id, app-idThe AMQP properties, when the message has them.
anything elseThe message’s own headers.

A SEND’s own headers travel with the message as headers, and its content-type, correlation-id, reply-to, expiration, persistent and priority as the AMQP properties an AMQP consumer reads.

Orders and RPC

SEND an order to /queue/orders and watch /topic/store.orders: paid two seconds later, shipped at five, delivered at nine. SEND {"method":"store.getProduct","params":{"id":1}} to /queue/rpc with reply-to:/temp-queue/answers, and the answer arrives on the subscription /temp-queue/answers with your correlation-id.

Users

UserMay
guest
password guest
Everything: declare, bind, publish, consume, delete. The user every client tries first.
sonda
password sondahub
Everything: declare, bind, publish, consume, delete.
viewer
password viewer
Reads: consume, get, declare a server-named queue and bind it. Publishing and declaring named queues or exchanges are refused with 403 ACCESS_REFUSED.
Anyone else
or a wrong password
AMQP: connection.close 403 ACCESS_REFUSED, as RabbitMQ answers. STOMP: an ERROR frame, then the connection closes.

Questions

Does it need the bridge?

Not over WebSocket: connect to wss://api.sondahub.com/stomp with the subprotocol v12.stomp (or v11.stomp, v10.stomp) and send CONNECT. Over TCP, yes: node bridge.mjs amqp opens localhost:61713, on the same broker as AMQP.

Do my messages reach other people?

No. Over WebSocket each connection has a broker of its own: what you send reaches your own subscriptions. Through the bridge, the clients of one bridge share one broker — STOMP and AMQP alike — and nobody else sees it.

Which clients work?

Any STOMP 1.0, 1.1 or 1.2 client — over WebSocket, or over TCP through the bridge. The destinations are RabbitMQ’s, so code written for RabbitMQ’s STOMP plugin points here unchanged.