Skip to Content
ReferenceActionsWEBSOCKET

WebSocket Action

The WebSocket action connects to a WebSocket server, sends and receives messages in the order a step lists them, and returns what it received.

It is an external action, published in mozership/probe-websocket . Probe downloads it the first time a workflow uses it, and runs the executable whose SHA-256 the action.yml at the pinned commit names. It needs Probe v1.17.0 or later, and v1.21.0 or later to run it under a guard and to have probe check check its with.

Basic Syntax

A step pins the action by a full commit SHA. The notes of each release  start with the uses line to copy.

steps: - name: Subscribe and get an update uses: github.com/mozership/probe-websocket@a159244b9ab3f5ec4c61782af1e711bce705c8a8 # v0.1.0 with: url: wss://stream.example.com/ws headers: Authorization: "Bearer {{vars.token}}" messages: - send: {type: subscribe, channel: ticker} - receive: match: {type: subscribed} - receive: count: 3 match: {type: update} test: res.code == 101 && len(res.messages) == 4 && res.messages[3].data.price > 0

Every step opens a new connection, runs its messages in order, and closes it, so a subscription does not carry over to the next step. Put a whole exchange in one step.

Parameters

ParameterTypeRequiredDefaultDescription
urlStringYes-The server, ws or wss
headersObjectNo-Handshake request headers, such as Authorization. They override User-Agent: probe-websocket/<version>
subprotocolsListNo-The subprotocols to offer, in order of preference
messagesListNo-What to send and receive, in order. See Messages. Without it, the step connects and closes
timeoutDurationNo30sTime limit for the whole step, from the handshake to the close, as 10s or a number of seconds. 0 removes it

A key of with that is not one of these fails the step before anything is sent. Its action.yml declares them as params, so probe check reports such a key with its line.

Messages

Each entry of messages has exactly one of these keys.

KeyValueWhat it does
sendString, or any other valueSends a text message: a string as it is, anything else as JSON
send_binaryBase64 stringSends the decoded bytes as a binary message
receiveObject, or nothingWaits for messages and keeps them in res.messages. Without options, it takes the next message

receive takes these options.

OptionTypeDescription
countIntegerTakes this many messages. Defaults to 1
until_closeBooleanTakes every message until the server closes the connection. It cannot be used with count, and the entry must be the last one
matchAnyTakes only the messages whose data contains it, and skips the others. An object matches an object that has each of its keys with a matching value, a list matches a list of as many matching entries, and anything else matches an equal value, numbers by value

With match, count is how many matching messages to take. A skipped message is not kept.

Response Object

PropertyTypeDescription
res.codeIntegerHTTP status code of the handshake, 101 when the connection was upgraded
res.statusStringHTTP status line, such as "101 Switching Protocols"
res.headersObjectHandshake response headers, keyed by canonical name
res.subprotocolStringThe subprotocol the server chose, or empty
res.messagesArrayThe messages the receive entries took, in order
res.closeObjectThe code and reason the server closed the connection with, or null when it did not
res.errorStringWhy the messages stopped before the last entry, or empty when every entry was done
reqObjectThe url, headers, subprotocols and messages that were used
rtDurationTime from the handshake to the close
statusInteger0 when the connection was upgraded and every entry was done; 1 otherwise

Each message in res.messages has these properties.

PropertyTypeDescription
typeString"text" or "binary"
dataAnyA text message parsed as JSON when it is JSON, otherwise the string. A binary message as base64
rawStringThe unparsed text, or the base64 of a binary message

Once the server answers the handshake, what happens is a result, so a test can assert on a 401 handshake, a close before the expected message, or a message that never came. The messages stop at the first entry that cannot be done, and res.error says which, such as messages[1] took 2 of 3 messages: timed out after 10s. Only a handshake that gets no answer, such as a refused connection or a timeout, fails the step as an error.

When every entry is done and the server has not closed the connection, the action closes it with 1000. A message larger than 16 MiB ends the exchange.

steps: - name: The feed sends two updates, then closes uses: github.com/mozership/probe-websocket@a159244b9ab3f5ec4c61782af1e711bce705c8a8 # v0.1.0 with: url: wss://stream.example.com/replay messages: - receive: until_close: true match: {type: update} test: len(res.messages) == 2 && res.close.code == 1000

Under a Guard

The action keeps to the guard of the run, and its action.yml declares guard: [read-only, allow-host], so Probe runs it under either without --allow-action. A step it refuses fails with the kind refused before connecting.

  • Under --read-only, only a step that receives is run. What a message does on the server cannot be told, so a step with a send or a send_binary entry is refused. A server that needs a message before it sends anything, such as a subscribe, has to be allowed with --allow-action.
  • Under --allow-host, the host of url, and of each redirect of the handshake, must be one the run allows. A URL without a port is taken at the port of its scheme: 80 for ws and 443 for wss.

See Guard.

See Also

  • External Actions - How Probe resolves and checks an external action
  • HTTP - Requests over plain HTTP
  • GraphQL - GraphQL queries over HTTP
Updated at