Skip to content

Node reference ​

Every kind of node an experiment can hold, in the groups of the add menu: actions, waits, emulation, faults, data, checks and flow. How to add and wire them is in The editor; signallab nodes prints the same catalogue as JSON, for scripts and assistants (The command line).

Reading this page ​

Each node has a table of its fields:

  • Field is the name in the properties pane; In the file is the key in the experiment's JSON.
  • Default is what a node gets when you add it in the editor. Where a file may leave a key out, the value it then takes is given as if absent; other keys are required in a file.
  • Templates: yes — the field takes {{templates}}: parameters, variables set earlier, secrets and generators, resolved as the step runs (Data and templates). Parameters only — it is opened before the first step, when only parameters are known. No — the value is taken as written.

Times are in milliseconds. Limits are checked before a run starts; a field out of range keeps the experiment from running and is shown on the node.

A node in a file ​

In an experiment file, a node is an object with an id (unique in the experiment), its type, its place on the canvas (x, y, zero or more), its fields, and the settings it uses (retry, repeat, load, left out when off). A wire is an edge from one node's output (port, next if absent) to another node:

json
{
  "nodes": [
    { "id": "start", "type": "start", "x": 40, "y": 80 },
    { "id": "ping", "type": "udp", "x": 270, "y": 80, "target": "127.0.0.1:9000", "text": "PING",
      "retry": { "attempts": 3, "delay_ms": 500, "backoff": "fixed" } },
    { "id": "end", "type": "end", "x": 500, "y": 80 }
  ],
  "edges": [
    { "from": "start", "to": "ping", "port": "next" },
    { "from": "ping", "to": "end", "port": "next" }
  ]
}

The examples below show one node each, as a file holds it.

Settings shared by many nodes ​

These are switched on in the lower part of a node's properties. Which node takes which is listed under each node.

SettingTakes itWhat it does
RetryNodes that send or listen: HTTP request, TCP message, OSC message, UDP datagram, MQTT publish, WebSocket connect, WebSocket send, and every waitTries again when the step fails
RepeatNodes that send: HTTP request, TCP message, OSC message, UDP datagram, MQTT publish, WebSocket sendSends again and again, a number of times or for a time
LoadHTTP requestSends the request on a load profile, measured and judged by thresholds
Waiting for a replyOSC message, UDP datagramSends and waits for the answer in the same step

Retry ​

retry on failure: when the step fails — no connection, a timeout, a wait with nothing matching — it pauses and runs again. Each failed attempt is a row in the timeline; the step fails when the last attempt does. A template that cannot be resolved is not retried. Stop also ends a pause.

FieldIn the fileWhatDefault and limits
Attemptsretry.attemptsAttempts in all, the first included3; 2–10 in the editor (a file may also say 1)
Pause, msretry.delay_msThe pause before the second attempt500; 0–60 000
Pausesretry.backoffthe same (fixed): the same pause each time; doubling (exponential): twice as long after each failurefixed (also if absent)

No pause is longer than 60 seconds, however it doubles. A wait whose Timeout output has a wire does not fail on a timeout — it leaves through that output — so it is not retried then.

Repeat ​

repeat sending: the node sends again and again — a heartbeat, a poll, a steady stream — without a loop in the graph. Each send reads its templates afresh ({{counter}} is its number, {{now}} its time), and Retry, when on, applies to each send. The step passes when every send did; a send that fails for good fails the step. The timeline reports progress at most once a second.

FieldIn the fileWhatDefault and limits
Repeatrepeat.untila number of times (count) or for a time (duration)count (also if absent)
Timesrepeat.countSends in all, the first included10 (also if absent); 2–10 000
For, msrepeat.duration_msHow long to keep sending, from the first send10 000 (also if absent); 1–300 000
Every, msrepeat.interval_msThe pause between two sends1 000; 10–60 000; required in a file
Jitter, msrepeat.jitter_msEach pause up to this much longer, drawn from the run's seed0 (also if absent); 0–60 000

The repeats must fit in a run's 300 seconds, and for a time must need fewer than 10 000 sends (its time divided by the interval).

Load ​

send under load, on an HTTP request only: the request is sent on a profile — a constant rate, a ramp, steps, a spike or random arrivals — with up to 512 in flight at once (32 by default), and measured: latencies, errors, the rate achieved. Thresholds decide whether the step passes. Load replaces Repeat and Retry (a failed request is counted, not tried again), and leaves no response for the checks after it. Its fields and results are in Load testing.

Waiting for a reply ​

wait for a reply, on an OSC message or a UDP datagram: the message is sent from the port the reply is awaited on, so a device that answers the sender is heard, and the step passes only when a matching reply arrives in time. No reply fails the step — Retry sends again. The reply is stored in a variable, as a wait's is.

FieldIn the fileWhatDefault and limitsTemplates
Reply on (IP:port)reply.bindIP:port to send from and listen on; port 0 takes any free port0.0.0.0:0No
Reply address pattern (OSC)reply.addressThe reply's address pattern, as in Wait for OSC/*Yes
Argument rules (OSC)reply.argsArgument rules, as in Wait for OSCnone; at most 16Values: yes
Reply payload (UDP)reply.modeany, contains, regex or hex — see Payload matchingany (also if absent)No
Pattern (UDP)reply.patternWhat the reply must contain or matchempty; required unless anyYes
Timeout, msreply.timeout_msHow long to wait2 000 (also if absent); 1–120 000No
Reply variablereply.variableThe variable the reply is stored inreply (also if absent)No

The reply's port is opened before the first step, like a wait's.

Actions ​

Nodes that send. A wait after an action counts messages from the moment the action started.

HTTP request ​

Sends one HTTP request and keeps the response for the checks, branches and Extract value nodes after it.

FieldIn the fileWhatDefault and limitsTemplates
Methodrequest.methodGET, HEAD, POST, PUT, PATCH, DELETE or OPTIONS (a file may name any method)GETNo
URLrequest.urlAn http:// or https:// URLhttp://127.0.0.1:8080/Yes
Timeout (ms)request.timeout_msFor the whole exchange4 000 (10 000 if absent); 1–120 000No
Request headersrequest.headers[[name, value], …]; a row with an empty name is skippednoneYes, names and values
Bodyrequest.bodyText, or null for nonenullYes
Authenticationrequest.authNone, Basic, Bearer token or Digest, with User name and Password, or TokennoneYes
  • Any answer passes the step, 404 and 500 included: check the status with HTTP status or branch on it with Status branch. A request that gets no answer — refused, a timeout, a name that does not resolve, a certificate that is not trusted — fails the step.
  • Redirects are followed, ten at most. https:// certificates are verified.
  • The response body is kept up to 256 KiB for the checks; a larger body is cut there (the checks say so when what they look for may be past the cut).
  • Digest answers the server's 401 challenge and sends the request again. The credentials go only into the request: steps, reports and the Inspector never show the Authorization header. Write a password as {{secret.NAME}}.
  • While the experiment keeps cookies (on by default, under Parameters), what servers set is sent back with the run's later requests to them.

Outputs: Output. Settings: Retry, Repeat, Load.

json
{ "id": "cue", "type": "http", "x": 270, "y": 80,
  "request": { "method": "POST", "url": "{{api}}/cue", "headers": [["Content-Type", "application/json"]],
               "body": "{\"cue\": 1}", "timeout_ms": 5000,
               "auth": { "scheme": "bearer", "token": "{{secret.API_TOKEN}}" } } }

See also HTTP.

TCP message ​

Connects to a host over TCP, writes the payload, waits up to 250 ms for the first bytes of an answer (it reads at most 1 024 bytes, once) and closes the connection. The answer's size is reported, not checked.

In the Inspector the step is two tcp frames with the source experiment: the payload written and, when one came, the answer read. Secrets in use are masked in both, as in any frame.

FieldIn the fileWhatDefault and limitsTemplates
HosthostA host name or an IP address127.0.0.1Yes
Portport9000; 1–65 535No
Timeout (ms)timeout_msFor connecting, writing and the answer together4 000 (also if absent); 1–120 000No
PayloadpayloadThe text written once connected, as UTF-8helloYes

The step fails when the connection is refused, the name does not resolve or the time runs out. Outputs: Output. Settings: Retry, Repeat. Send now connects and writes the payload once, and the node's result says how many bytes were sent and came back.

json
{ "id": "go", "type": "tcp", "x": 270, "y": 80, "host": "127.0.0.1", "port": 5000, "payload": "GO\r\n", "timeout_ms": 2000 }

OSC message ​

Sends one OSC 1.0 message over UDP.

FieldIn the fileWhatDefault and limitsTemplates
Target host:porttargetIP:port or host:port; a host name is looked up when the step sends, its IPv4 address taken when it has one127.0.0.1:9000Yes
OSC addressaddressStarts with //testYes
Argumentsargs[{ "type", "value" }, …] — int, float, str, long, double, bool, blob (bytes), nil (no value)noneText (str) values: yes
wait for a replyreplyOptional: send and wait for the answer — see Waiting for a replyoff

Outputs: Output; with a reply expected, it is followed only when the reply came. Settings: Retry, Repeat, a reply. ⚡ Route through impairment in its properties puts an Impairment in front of it.

json
{ "id": "fader", "type": "osc", "x": 270, "y": 80, "target": "{{device}}", "address": "/fader/1",
  "args": [{ "type": "float", "value": 0.75 }] }

See also OSC.

UDP datagram ​

Sends a text payload as one UDP datagram to one or more targets.

FieldIn the fileWhatDefault and limitsTemplates
Target host:porttargetIP:port or host:port; several separated by commas, semicolons or new lines each get the datagram. A host name is looked up when the step sends, its IPv4 address taken when it has one127.0.0.1:9000Yes
PayloadtextThe payload, as UTF-8hello; at most 65 507 bytesYes
wait for a replyreplyOptional: send and wait for the answer — see Waiting for a replyoff

The step fails if any target cannot be reached. Outputs: Output. Settings: Retry, Repeat, a reply.

json
{ "id": "ping", "type": "udp", "x": 270, "y": 80, "target": "{{device}}", "text": "PING {{run.id}}",
  "reply": { "bind": "0.0.0.0:0", "mode": "contains", "pattern": "PONG", "timeout_ms": 1000, "variable": "pong" } }

MQTT publish ​

Connects to an MQTT broker, publishes one message and disconnects. The connection is MQTT 3.1.1 over plain TCP, with a clean session and no user name or password. Connecting, publishing and the broker's acknowledgement must all happen within 15 seconds.

FieldIn the fileWhatDefault and limitsTemplates
Broker hosthostThe broker's host name or address127.0.0.1Yes
Portport1883; 1–65 535No
TopictopicNo wildcards (+, #)lab/testYes
PayloadpayloadThe message, as texthelloYes
QoSqos0, 1 or 20No
Retain messageretaintrue: the broker keeps it as the topic's valuefalseNo

All six keys are required in a file. The step fails when the broker cannot be reached or refuses the connection or the message. Outputs: Output. Settings: Retry, Repeat.

json
{ "id": "light", "type": "mqtt", "x": 270, "y": 80, "host": "{{broker}}", "port": 1883,
  "topic": "lab/light/1/set", "payload": "on", "qos": 1, "retain": false }

See also MQTT.

WebSocket connect ​

Opens a WebSocket for the rest of the run, or until a WebSocket close. What arrives from then on is kept for the Wait for WebSocket steps on it. The URL and headers are resolved when the step runs, so a token extracted earlier can be in them. Run again — in a Loop — it closes its previous connection first and opens a new one. When the run ends, in any way, its connections are closed with a close frame.

FieldIn the fileWhatDefault and limitsTemplates
URLurlA ws:// or wss:// URLws://127.0.0.1:9001/Yes
Request headersheaders[[name, value], …] sent with the upgrade requestnoneYes, names and values
SubprotocolsprotocolsSubprotocols to offer, in order of preference; the server picks onenoneNo
Timeout (ms)timeout_msFor connecting and the upgrade5 000 (10 000 if absent); 1–120 000No

wss:// trusts the same certificates as https://. The step fails when the connection or the upgrade fails; the server's status is in the reason. Outputs: Output. Settings: Retry (not Repeat).

json
{ "id": "socket", "type": "ws_connect", "x": 270, "y": 80, "url": "ws://127.0.0.1:9001/chat",
  "headers": [["Authorization", "Bearer {{token}}"]], "protocols": ["chat.v1"], "timeout_ms": 5000 }

See also WebSocket.

WebSocket send ​

Sends one message on the connection a WebSocket connect opened.

FieldIn the fileWhatDefault and limitsTemplates
ConnectionconnectionThe id of a WebSocket connect node of this experimentthe first oneNo
FormatbinaryText (false), or Binary (hex) (true): the payload is bytes written as hex, de ad be effalse (also if absent)No
PayloadtextThe messagehello; at most 16 MiBYes

The connect must come before the send on its path; a send whose connection is not open fails. Answers count from the moment the message is written. Outputs: Output. Settings: Retry, Repeat.

json
{ "id": "hello", "type": "ws_send", "x": 500, "y": 80, "connection": "socket",
  "text": "{\"type\":\"ping\",\"id\":\"{{uuid}}\"}", "binary": false }

WebSocket close ​

Closes a connection with a close handshake. The timeline says who closed it: this step, the server earlier (with its code), or a connection that had broken.

FieldIn the fileWhatDefault and limitsTemplates
ConnectionconnectionThe id of a WebSocket connect nodethe first oneNo
Close codecode1000 (normal), or 3000–4999 for an application's own1000 (also if absent)No
ReasonreasonSent with the codeempty; at most 123 bytes, after templatesYes

Outputs: Output. No settings.

json
{ "id": "bye", "type": "ws_close", "x": 960, "y": 80, "connection": "socket", "code": 1000, "reason": "done" }

Log marker ​

Writes a line into the timeline and the report — a checkpoint, or the values a run reached.

FieldIn the fileWhatDefault and limitsTemplates
MessagemessageThe textCheck point; at most 10 000 charactersYes

Outputs: Output. No settings.

json
{ "id": "ready", "type": "log", "x": 500, "y": 80, "message": "device {{device}} ready" }

Waits ​

The Observe group: nodes that wait for something to arrive. They share these rules:

  • They listen from the start of the run. A wait's port or broker subscription is opened before the first step, so a device that answers faster than the next step begins is not missed. Two waits on the same address share one socket.
  • They count from the latest action on their branch. A message that arrived before the branch's last request is not an answer to it; before any action, everything since the run started counts.
  • The first matching message is taken. A message one wait took is not seen by another.
  • Matched or Timeout. On a match the message is stored in the wait's variable and the flow follows Matched. When the time runs out, it follows Timeout if that output has a wire; otherwise the step fails, saying how many other messages arrived.
  • Each socket keeps the latest 1 024 messages (and 64 MiB); older ones are dropped, and a timeout says how many were.
  • Listen now listens with that one step, from now on.

Outputs: Matched (required), Timeout (optional). Settings: Retry.

Payload matching ​

Wait for UDP, Wait for MQTT, Wait for WebSocket and a UDP reply choose how the payload must look:

OptionIn the fileMatches when the payload
Any datagramanyis anything
Contains textcontainsread as UTF-8 text, contains the pattern (case-sensitive)
Matches regexregexread as UTF-8 text, matches the regular expression
Contains bytes (hex)hexcontains the bytes, written as hex pairs: de ad be ef, deadbeef, 0xde,0xad, DE:AD

The matched message is stored as an object. Later steps read its fields as {{reply.text}} (with the variable's name in place of reply):

FieldWhat
textThe payload as text
hex, bytesThe payload in hex (its first 1 024 bytes), and its size in bytes
matchWhat matched: the text, the regular expression's first group (or the whole match), or the bytes
fromThe sender's IP:port
msMilliseconds from the branch's latest action (or the start of the run) to the message
topicWait for MQTT: the topic it was published to
json, kindWait for WebSocket: the message parsed as JSON (null when it is not), and text or binary

Wait for OSC ​

Waits for an OSC message whose address matches a pattern and whose arguments meet every rule. In a bundle, the first message that matches is the one taken.

FieldIn the fileWhatDefault and limitsTemplates
Listen on (IP:port)bindIP:port to listen on; 0.0.0.0 for every network card127.0.0.1:9001No
Address patternaddress* any characters, ? one, [0-9] a set ([!0-9] outside it), {ping,pong} either; wildcards stay within one / segment/pong; at most 512 charactersYes
Argument rulesargs[{ "index", "op", "value" }, …]: argument index compared with value by op (Comparisons); all must holdnone; at most 16, index 0–63Values: yes
Timeout, mstimeout_ms2 000 (also if absent); 1–120 000No
Reply variablevariableWhere the message is storedreply (also if absent)No

An argument compares as text: numbers as written, strings without quotes, true/false, a blob in hex. A rule on an argument the message does not have does not hold. The stored message has address, args ({{reply.args[0]}}), from and ms.

json
{ "id": "status", "type": "wait_osc", "x": 500, "y": 80, "bind": "0.0.0.0:9001", "address": "/status",
  "args": [{ "index": 0, "op": "eq", "value": "ready" }], "timeout_ms": 5000, "variable": "reply" }

Wait for UDP ​

Waits for a UDP datagram whose payload matches.

FieldIn the fileWhatDefault and limitsTemplates
Listen on (IP:port)bindIP:port to listen on127.0.0.1:9001No
PayloadmodeSee Payload matchingcontains (any if absent)No
PatternpatternWhat the payload must contain or matchpong; required unless anyYes
Timeout, mstimeout_ms2 000 (also if absent); 1–120 000No
Reply variablevariablereply (also if absent)No
json
{ "id": "ready", "type": "wait_udp", "x": 500, "y": 80, "bind": "0.0.0.0:9002", "mode": "contains",
  "pattern": "READY", "timeout_ms": 5000, "variable": "reply" }

Wait for MQTT ​

Waits for a message published to a topic at a broker, whose payload matches. The run connects and subscribes before its first step. Retained messages the broker replays on subscribing are ignored: only what is published after the run started counts.

FieldIn the fileWhatDefault and limitsTemplates
Broker hosthostThe broker127.0.0.1Parameters only
Portport1883; 1–65 535No
Topic filtertopicA filter: + is any one level, # everything below (last only)lab/#Parameters only
PayloadmodeSee Payload matchingany (also if absent)No
Patternpatternempty; required unless anyYes
Timeout, mstimeout_ms2 000 (also if absent); 1–120 000No
Reply variablevariablereply (also if absent)No
json
{ "id": "state", "type": "wait_mqtt", "x": 500, "y": 80, "host": "{{broker}}", "port": 1883,
  "topic": "lab/+/state", "mode": "contains", "pattern": "on", "timeout_ms": 5000, "variable": "reply" }

Wait for HTTP request ​

Waits for an HTTP request — a webhook, a callback — to the run's Emulator on that address, or, when the run has no HTTP emulator there, to a listener of the run's own that answers every request with 204. The request must match the method, the path and every condition.

FieldIn the fileWhatDefault and limitsTemplates
Listen on (IP:port)bindIP:port127.0.0.1:18080 — where a new Emulator listensNo
MethodmethodA method, or Any (ANY); GET also takes HEADANY (also if absent)No
Pathpath/hooks/:name names a segment ({{request.params.name}}); a final /* takes the rest/* (also if absent); at most 512 charactersYes
Conditionswhen[{ "on", "name", "op", "value" }, …] on a header, a query parameter, the body or a json path; every one must holdnone; at most 16Yes, names and values
Timeout, mstimeout_ms5 000 (2 000 if absent); 1–120 000No
Reply variablevariablerequest (also if absent)No

The stored request has method, path, query, headers, body, json, params, from and ms: {{request.json.event}}, {{request.headers.x-key}}.

json
{ "id": "hook", "type": "wait_http", "x": 500, "y": 80, "bind": "127.0.0.1:18081", "method": "POST",
  "path": "/hooks/:name", "when": [{ "on": "json", "name": "$.event", "op": "eq", "value": "deploy" }],
  "timeout_ms": 5000, "variable": "request" }

Wait for WebSocket ​

Waits for a message on the connection a WebSocket connect opened, whose payload matches. Messages since the latest action on the branch count — the connect itself, a send, or any other request.

FieldIn the fileWhatDefault and limitsTemplates
ConnectionconnectionThe id of a WebSocket connect nodethe first oneNo
PayloadmodeSee Payload matchingany (also if absent)No
Patternpatternempty; required unless anyYes
Timeout, mstimeout_ms2 000 (also if absent); 1–120 000No
Reply variablevariablereply (also if absent)No

A JSON message is readable field by field: {{reply.json.type}}. The connect must come before the wait on its path.

json
{ "id": "pong", "type": "wait_ws", "x": 730, "y": 80, "connection": "socket", "mode": "contains",
  "pattern": "pong", "timeout_ms": 3000, "variable": "reply" }

Emulation ​

Emulator ​

Plays a dependency — an HTTP API, an OSC, UDP or TCP device, an MQTT broker — for the whole run. It opens before the first step and answers until the run ends; in the flow the step passes at once. What it received is counted, rule by rule, in the run's report.

FieldIn the fileWhatDefault
Edit…emulatorThe emulator: name, bind (IP:port), protocol (http, osc, udp, tcp, mqtt), its routes or rules, and an optional outageAn HTTP API named API on 127.0.0.1:18080 answering /health

The properties show what it plays in one line. Edit… opens its rules, the same editor as the Emulators screen; To the library keeps a copy in the emulator library, and From the library replaces this one with a copy from it. The rules — routes, responses, faults, outages — are described there.

  • An HTTP emulator is also what a Wait for HTTP request on its address reads; an OSC or UDP emulator shares its port with the run's waits there.
  • Two emulators of one transport cannot share a port in a run.
  • Emulator down/up takes it down and brings it back.

Outputs: Output. No settings.

json
{ "id": "api", "type": "emulator", "x": 270, "y": 80,
  "emulator": { "name": "Orders API", "bind": "127.0.0.1:18080", "protocol": "http",
    "routes": [{ "method": "GET", "path": "/orders/:id", "order": "sequence",
                 "responses": [{ "status": 503 }, { "status": 200, "body": "{\"id\":\"{{request.params.id}}\"}" }] }] } }

Faults ​

Nodes that break things on cue. A branch of Delay nodes and these beside the traffic reads as a schedule; Faults on a schedule shows how.

Impairment ​

An impairment relay for the whole run: the system under test sends to (or connects to) Listen on instead of the real target; the relay forwards to Forward to, and the replies come back the same way, impaired by the profile. It opens before the first step and closes when the run ends, in any way, so nothing stays impaired; in the flow the step passes at once. Every decision draws from the run's seed: the same seed and the same traffic meet the same fate.

FieldIn the fileWhatDefault and limitsTemplates
Listen onlistenIP:port the system under test sends to127.0.0.1:9010Parameters only
Forward totargetIP:port of the real destination, or host:port — a host name is looked up when the run starts, and a name that cannot be found stops the run at this node127.0.0.1:9000Parameters only
ProtocolprotocolUDP (udp): each datagram meets its own fate; TCP (tcp): each connection is joined to one of its own to the target, and both streams are impairedUDP (udp if absent)No
Preset and the values under itprofileWhat the relay does to the traffic — see The profileLAN (no impairment if absent)No

A relay's listening address cannot be another socket of the run, and relays may not forward to each other in a circle. A target given by name is followed once it is looked up, so a circle through a name stops the run as it starts. The report counts each phase of a relay apart.

Outputs: Output. No settings.

json
{ "id": "relay", "type": "impairment", "x": 270, "y": 80, "listen": "127.0.0.1:9010", "target": "{{device}}",
  "profile": { "name": "lan", "latency_ms": 1, "jitter_ms": 1 } }

The profile ​

A preset chip — LAN, Busy Wi-Fi, 4G, Satellite, Intermittent, Offline — fills in every value; change any of them after. A relay reads only the values of its protocol; in a file every key may be left out (zero, off).

FieldIn the fileWhatLimitsProtocol
—nameA label for the timeline and the report: a preset's key (lan, wifi, 4g, satellite, intermittent, offline) or your ownat most 60 charactersboth
Offline — nothing gets throughofflineNothing gets throughtrue / falseboth
Latencylatency_msDelay added to every packet, or chunk of a stream0–60 000 (the slider goes to 1 000)both
Jitterjitter_msA random extra delay up to this much; a TCP stream stays in order0–60 000 (the slider goes to 500)both
Bandwidth, kbit/srate_kbpsA bandwidth limit, 0 for none. UDP: past a second of queue, datagrams are dropped as throttled; TCP: the sender is slowed down, nothing is dropped0, or 8–10 000 000both
Packet losslossThe chance a datagram is dropped0–1 (the slider shows %)UDP
Burst loss, Burst length, datagramsburst_start, burst_lengthThe chance a burst of loss starts, and how many datagrams it lasts on average0–1; 1–1 000 when bursts are onUDP
DuplicationduplicateThe chance a datagram is sent twice0–1UDP
CorruptioncorruptThe chance one bit of a datagram is flipped0–1UDP
ReorderingreorderThe chance a datagram is held back, so later ones overtake it0–1UDP
Connection resetresetThe chance a chunk of a stream resets its connection instead — both sides get a reset0–1TCP
Half-openstallThe chance a chunk leaves its connection half-open: nothing more goes through either way, and neither side is told0–1TCP

More on relays, presets and what they model on the Impairment page.

Change impairment ​

Switches one of the run's Impairment nodes to another profile from this step on, without dropping its port. The phase so far is closed and counted in the report.

FieldIn the fileWhatDefaultTemplates
ImpairmentrelayThe id of an Impairment node of this experimentthe first oneNo
Preset and the values under itprofileWhat it impairs with from now on — see The profile; the relay reads the values of its own protocolOffline (no impairment if absent)No

The step fails if the relay is not running — it failed to relay, say. Outputs: Output. No settings.

json
{ "id": "cut", "type": "impairment_change", "x": 730, "y": 200, "relay": "relay",
  "profile": { "name": "offline", "offline": true } }

Emulator down/up ​

Takes one of the run's emulators down, or brings it back up. While it is down an HTTP emulator answers as While down says; a TCP device and an MQTT broker drop their connections and refuse new ones; OSC and UDP devices answer nothing. Up again, the emulator follows its own outage schedule, if it has one.

FieldIn the fileWhatDefault
EmulatoremulatorThe id of an Emulator node of this experimentthe first one
StatedownDown (true) or Up (false)down (false if absent)
While downfaultHTTP only: 503 Unavailable (unavailable), Close the connection (reset: the connection is closed without an answer) or No answer (timeout: the request is held until the client gives up, 120 s at most)unavailable (also if absent)

Outputs: Output. No settings. Nothing is templated.

json
{ "id": "down", "type": "emulator_state", "x": 500, "y": 200, "emulator": "api", "down": true, "fault": "unavailable" }

Data ​

Extract value ​

Saves a part of the latest HTTP response on its path as a variable, for later fields ({{token}}), checks and branches. An HTTP request must come before it on every path. Clicking a value in a Send now response adds one for you.

FieldIn the fileWhatDefault and limitsTemplates
VariablevariableThe name: letters, digits and _, not starting with a digit, not a reserved word, not a parameter's nametokenNo
Take fromfromJSON field (json), Header (header), Status code (status), Whole body (body) or Regular expression (regex)jsonNo
JSON path, Header name or Pattern (group 1 if present)exprA JSON path ($.data.token, $.items[0], $["first name"]), a header name (any case), or a regular expression — its first group, or the whole match$.token; not used for status and bodyNo

The step fails when there is nothing to take: the body is not JSON, the path or header is missing, the expression does not match, or — for a JSON field or the whole body — the body was longer than the 256 KiB kept. A status is stored as a number; the rest as text, or as the JSON value found. Outputs: Output. No settings.

json
{ "id": "token", "type": "extract", "x": 500, "y": 80, "variable": "token", "from": "json", "expr": "$.data.token" }

More on variables in Data and templates.

Checks ​

A check passes, or fails the run. The four response checks read the latest HTTP response on their path, so an HTTP request — not one under load — must come before them on every path.

HTTP status ​

Passes when the latest response's status is exactly the one given.

FieldIn the fileWhatDefault and limitsTemplates
Expected statusstatus200; 100–599No

Outputs: Output. No settings.

json
{ "id": "ok", "type": "assert_status", "x": 500, "y": 80, "status": 200 }

Response text ​

Passes when the latest response's body contains the text, exactly (case included). Only the first 256 KiB of a body are kept: text not found in a body that was cut fails with that reason.

FieldIn the fileWhatDefault and limitsTemplates
Contains textcontainsok; requiredYes

Outputs: Output. No settings.

json
{ "id": "ready", "type": "assert_body", "x": 500, "y": 80, "contains": "ready" }

Response header ​

Passes when the latest response has the header and its value contains the text. The header's name is matched in any case; the value exactly.

FieldIn the fileWhatDefault and limitsTemplates
Header namenamecontent-type; requiredYes
Contains textcontainsWhat its value must contain; empty: the header only has to be thereapplication/jsonYes

Outputs: Output. No settings.

json
{ "id": "json", "type": "assert_header", "x": 500, "y": 80, "name": "Content-Type", "contains": "json" }

Response time ​

Passes when the latest response took at most this long, from sending the request to the end of its body.

FieldIn the fileWhatDefault and limitsTemplates
Maximum time, msmax_ms1 000; 1–120 000No

Outputs: Output. No settings.

json
{ "id": "fast", "type": "assert_latency", "x": 500, "y": 80, "max_ms": 250 }

Check value ​

Compares a value — usually a variable, written as a template — with an expected one, and passes when the comparison holds.

FieldIn the fileWhatDefaultTemplates
ValuevalueWhat is compared: {{token}}, {{reply.args[0]}}{{token}}Yes
ConditionopSee Comparisonsis not emptyNo
ExpectedexpectedNot used by is empty and is not emptyempty (also if absent)Yes

Outputs: Output. No settings.

json
{ "id": "state", "type": "assert_value", "x": 730, "y": 80, "value": "{{state}}", "op": "eq", "expected": "ready" }

Comparisons ​

Check value, Branch on value, the exit condition of a Loop, OSC argument rules and HTTP conditions compare the same way:

OptionIn the fileHolds when the value
equalseqequals the expected one — as numbers when both are numbers (200 = 200.0), else as exact text
does not equalnedoes not equal it, by the same rule
less than, at most, greater than, at leastlt, le, gt, geis less, at most, greater, at least — both must be numbers: otherwise a check, a branch or a Loop fails the step, and an argument rule or an HTTP condition does not hold
containscontainscontains the expected text
matches regexmatchesmatches the expected regular expression
is empty, is not emptyempty, not_emptyis empty (spaces count as empty) / is not

Flow ​

Nodes that decide where the run goes. More on branches, joins and loops in Flow.

Start ​

Where the run begins; every experiment has exactly one. It has no input and no fields. The timeline's first row gives the run's seed.

Outputs: Output, required. Several wires from it start parallel branches at once.

json
{ "id": "start", "type": "start", "x": 40, "y": 80 }

End ​

Where the run completes; every experiment has exactly one, and it has no outputs. Several branches may lead to it: the run passes once, after the last branch finished, and only if none failed. A run that never reaches End fails.

json
{ "id": "end", "type": "end", "x": 960, "y": 80 }

Delay ​

Waits a fixed time before the next step.

FieldIn the fileWhatDefault and limitsTemplates
Delay (ms)ms300; 0–60 000No

Outputs: Output. No settings. For longer waits, put several in a row or in a Loop.

json
{ "id": "pause", "type": "delay", "x": 500, "y": 80, "ms": 500 }

Status branch ​

Chooses Yes when the latest HTTP response has this status, else No. An HTTP request must come before it on every path.

FieldIn the fileWhatDefault and limitsTemplates
Expected statusstatus200; 100–599No

Outputs: Yes and No, both required. No settings.

json
{ "id": "branch", "type": "branch_status", "x": 500, "y": 80, "status": 200 }

Branch on value ​

Chooses Yes when a comparison holds, else No. Its fields and comparisons are those of Check value; a comparison that cannot be made (lt on text) fails the step.

FieldIn the fileWhatDefaultTemplates
ValuevalueWhat is compared{{token}}Yes
ConditionopequalsNo
Expectedexpectedempty (also if absent)Yes

Outputs: Yes and No, both required. No settings.

json
{ "id": "ok", "type": "branch_value", "x": 730, "y": 80, "value": "{{reply.args[0]}}", "op": "eq", "expected": "ok" }

Parallel branch ​

Runs what follows Branch 1 and Branch 2 at the same time, each branch with its own copy of the variables. Each output may have more wires for more branches. No fields.

Outputs: Branch 1 and Branch 2, both required.

json
{ "id": "split", "type": "fork", "x": 270, "y": 80 }

Join branches ​

Waits until every wire into it has been reached, then continues once, with the branches' variables merged — where two branches set the same variable, the one whose wire comes later in the file wins — and the latest HTTP response of the last of them that had one. No fields.

Only branches that all run meet here: a Join branches behind a Status branch, whose Yes and No never both happen, never continues; when no other path reaches End, the run fails at this node, saying how many branches it still waited for.

Outputs: Output, required.

Any other node with several wires into it runs once for each arrival.

json
{ "id": "joined", "type": "join", "x": 730, "y": 80 }

Loop ​

Runs the steps on Body — which lead back to it — again and again: at most a number of times, and, when it has an exit condition, until that holds.

FieldIn the fileWhatDefault and limitsTemplates
Iterations at mostmaxIterations at most5; 1–1 000No
stop early whenuntilOptional exit condition { "value", "op", "expected" }, as Check valueoffValue and expected: yes
  • The body always runs at least once. The exit condition is read after each iteration, so the body can set what it tests.
  • Done follows when the condition holds — or, without a condition, after the last iteration.
  • Limit follows when the iterations ran out before the condition held. Without a wire on it, that fails the step.
  • Inside the body, {{counter}} is the iteration's number.
  • A body runs as one branch: each output inside it has one wire; it holds no Start, End, Parallel branch, Join branches or other Loop; it is entered only through Body; and every wire in it leads on in the body or back to the loop.

Outputs: Body and Done (required), Limit (optional). No settings.

json
{ "id": "poll", "type": "loop", "x": 270, "y": 80, "max": 10,
  "until": { "value": "{{status.args[0]}}", "op": "eq", "expected": "ready" } }