Version: 0.32.0This documentation is for Spitfire 0.32.0.
HTTP, WebSocket, SSE and GraphQL
HTTP(S), WebSocket, Server-Sent Events (SSE) and GraphQL steps carry their address in the step itself, so they need no connection. A connection is used only when the server requires a client certificate (mTLS) or you need to trust a private CA.
HTTP
What it does
Sends requests to a REST, JSON, XML or form-based web service like a real user: method, URL, headers, query parameters and body. You can extract a value from a response (e.g. a login token) to use in later steps, and verify responses with checks on status, latency, a JSON field, XPath, a header and more.
When to use it
- To measure the HTTP endpoints of your web application, API gateway or microservice under load.
- To chain a user flow such as login → list → add to cart → pay.
- To run tests that came from a browser recording (HAR) or an API document (see Importing).
Step by step
- On the Tests page open the test and switch to the editor (or New test).
- In the Scenarios & steps tab click Add step.
- Give it a meaningful Step name (e.g. "My orders"). The name appears in the result tables.
- Keep HTTP as the Protocol.
- Enter the Method (GET, POST, PUT, PATCH, DELETE…) and the URL. It must be a full
http://orhttps://address; to keep the common part in a variable, write{{base}}/api/ordersand definebasein the General & variables tab. - Add Headers (e.g.
Authorization: Bearer {{token}}) and Query parameters if needed (Add header, Add parameter). - Choose the Body type: None, JSON, XML, Form (urlencoded), Form (multipart / files) or Raw. Content-Type is set from the body type unless you set it.
- If the request creates, updates or deletes data on the target, tick This request changes data on the target. Every run of a test with such a step asks for an extra confirmation before it starts.
- Add checks in the Checks tab (e.g. Status = 200, Latency (ms) < 500).
- To take a value from the response, use the Extract tab (e.g. JSONPath
$.token→token). - For steps that should run once per virtual user (VU), such as a login, tick Run once per VU.
- Send a single iteration with Try it at the top of the editor to see the request and the response, then Save.
Example
Two steps that log in once to get a token, then fetch the order list with that token on every iteration:
[
{"id": "login", "name": "Login", "once": true,
"request": {"method": "POST", "url": "{{base}}/api/login",
"body": {"type": "json", "content": "{\"user\": \"vu{{__VU}}\", \"password\": \"{{password}}\"}"}},
"extract": [{"var": "token", "from": "jsonpath", "expr": "$.token"}],
"checks": [{"type": "status", "value": 200}]},
{"id": "orders", "name": "My orders",
"request": {"url": "{{base}}/api/orders",
"headers": [{"key": "Authorization", "value": "Bearer {{token}}"}]},
"checks": [{"type": "status", "value": 200}, {"type": "latency", "op": "lt", "value": 500}]}
]Spitfire has no separate "authentication" section; a credential is a header. For a Bearer token, extract token from a login step as above and use it in the Authorization header. For a fixed API key, keep it in a variable and write {{apiKey}} in the header.
Test-wide HTTP options
The editor's Options tab applies to every HTTP request in the test:
| Option | Default | Description |
|---|---|---|
| Request timeout | 30s | The longest a request may take. Beyond it the request counts as a timeout error. |
| HTTP version | Automatic (HTTP/2 when available) | HTTP/1.1 only turns HTTP/2 off. HTTP/3 (QUIC, UDP) sends HTTP and GraphQL steps over QUIC (for CDNs and servers with HTTP/3 on); when the server does not offer HTTP/3 on UDP 443 the requests fail, with no fallback to HTTP/2. In QUIC, connecting and TLS are one handshake: http_req_connecting and http_req_tls show the same time. A proxy and Never reuse connections do not apply to HTTP/3. WebSocket and SSE stay on TCP. |
| Max redirects | 10 | The most 3xx redirects followed. |
| Skip TLS certificate verification | off | Turns certificate verification off for this test's HTTP requests only; for self-signed test environments. When on, saving shows the warning TLS certificate verification is disabled. |
| Cookies | Each VU keeps its session (recommended) | Each VU keeps its own cookies, like a browser. Off sends only a step's own Cookie header. |
| Never reuse connections | off | Every request opens a new TCP/TLS connection. Can exhaust source ports under high load. |
| Close connections between iterations | off | Every iteration connects like a new user. |
| Don't keep response bodies | off | Saves memory; steps that extract or check the body are not affected. |
| Failed request samples | on | Keeps 2 KB of the body of a few failed responses. Turn it off if responses must not be stored. |
Client certificate with mTLS
If the server requires a client certificate (mTLS), or its certificate is signed by a private CA:
- Click Connections → Add connection and pick Client certificate (mTLS) as the Type.
- Give it a Name (e.g.
partner-mtls). - Fill Client certificate (PEM), Client key (PEM, stored encrypted) and, if needed, CA certificate. At least the certificate or the CA is required. Set Server name (SNI) when the certificate's name differs from the address.
- Save. Saving checks that the certificate and the key match; Test checks the certificate's expiry.
- In the test editor, pick this connection in the HTTP (or WebSocket / SSE) step's Client certificate (mTLS) field. The default is None.
{"id": "pay", "name": "Payment (mTLS)", "connection": "partner-mtls",
"request": {"method": "POST", "url": "https://partner.example.com/pay",
"body": {"type": "json", "content": "{\"amount\": 10}"}, "modifiesData": true}}The key never enters the test definition; the step carries only the connection's name.
Metrics it produces
| Metric | Meaning |
|---|---|
reqs |
Requests sent |
req_duration |
Total request time (ms); p50/p95/p99 come from this metric |
req_failed |
Rate of failed requests (connection errors, timeouts, 4xx/5xx) |
http_req_connecting |
Time to set up the TCP connection |
http_req_tls_handshaking |
TLS handshake time |
http_req_waiting |
Time waiting for the first byte (TTFB) |
data_sent, data_received |
Data sent and received |
checks |
Rate of passed checks |
Threshold example: {"metric": "req_duration", "filter": {"step": "orders"}, "expr": "p(95)<500"}.
WebSocket
What it does
Connects to a WebSocket server (chat, live notifications, a game server…), sends messages and waits for the messages the server pushes. Each VU keeps one connection per URL, like a browser tab: the first WebSocket step connects, later steps reuse that connection.
When to use it
When your server holds long-lived connections and you want to know how many concurrent connections, what message latency or what push rate it can handle.
Step by step
- Add step → pick WebSocket as the Protocol.
- Write the URL with
ws://orwss://. - Pick an Action:
- Send and wait for the reply: sends the message and waits for the reply; the latency runs from sending to the reply, and the reply goes to the checks.
- Send: sends the message without waiting for a reply.
- Wait for a message: waits for the next message the server pushes. Messages that arrive between steps are buffered.
- Close the connection: closes the connection; the next WebSocket step reconnects.
- Type the text to send in Message. To send a binary frame, tick Message is base64 (sent as a binary frame) and write base64.
- To wait for a specific reply, fill Text the awaited message contains; messages without it are skipped.
- If the Wait (default 10s) runs out, the step fails.
- If the handshake needs headers or a subprotocol, expand Handshake headers and subprotocol (Subprotocols (comma-separated)).
- If the messages change data, tick These messages change data on the target; the editor shows it as the warning WebSocket messages change data on the target on every iteration.
{"id": "ws-ping", "name": "WS ping", "protocol": "ws",
"ws": {"action": "request", "url": "wss://chat.example.com/ws",
"message": "{\"type\": \"ping\"}", "match": "pong", "wait": "5s"}}Metrics it produces
req_duration (from sending to the reply or the awaited message), req_failed, and, on the step that connected, ws_connecting (WebSocket handshake time).
SSE
What it does
Opens a Server-Sent Events stream (GET, Accept: text/event-stream) and closes it once the given number of events arrived. It tests one-way live streams such as prices, scores or notifications.
Step by step
- Add step → pick Server-Sent Events as the Protocol.
- Write the stream's URL.
- To count only certain events, fill Event type (e.g.
price) and/or Text in the data. - The stream closes once Events (default 1) events arrived.
- If the Wait (default 30s) runs out, the step fails.
{"id": "prices", "name": "Price stream", "protocol": "sse",
"sse": {"url": "https://api.example.com/events", "event": "price", "count": 5, "wait": "30s"}}The last event's data goes to the checks; its type and id are in the Sse-Event / Sse-Id headers.
Metrics it produces
req_duration (to the last awaited event), req_failed and sse_time_to_first_event (from opening the stream to the first event).
GraphQL
What it does
Sends one operation to a GraphQL endpoint: POST {query, variables, operationName}. GraphQL servers often report failures with HTTP 200 and an errors field in the response, so Spitfire fails the step whenever errors is not empty. The first error message shows in the run page's error samples.
When to use it
- Your API is GraphQL and you want timings, error rate and capacity per query or mutation. Each operation is its own step, so metrics are split by operation.
Step by step
- Add step → choose GraphQL as the Protocol.
- Enter the endpoint URL (e.g.
{{base}}/graphql). If it needs authentication, addAuthorization: Bearer {{token}}under Headers. - Click Load schema: Spitfire sends the introspection query with the step's URL, headers and the test's variables. Picking a field from Pick an operation from the schema fills in the query, example variables and the operation name (required arguments become variables; sub-fields are selected two levels deep).
- Replace the example values in Variables (JSON) with real ones; templates work:
{"id": "{{userId}}"}. - If the document has more than one operation, set the Operation name.
- If partial results (
errorsnext todata) are expected, tick Don't fail responses with errors and verify them with checks.
{"id": "user", "name": "user", "protocol": "graphql",
"graphql": {"url": "{{base}}/graphql",
"headers": [{"key": "Authorization", "value": "Bearer {{token}}"}],
"query": "query User($id: ID!) { user(id: $id) { id name } }",
"variables": "{\"id\": \"{{userId}}\"}",
"operationName": "User"},
"checks": [{"type": "jsonPath", "path": "$.data.user.id", "op": "exists"}]}The response goes to checks and extractors as JSON: use paths like $.data.user.name.
Mutations: a document with a mutation counts as changing data on the target. The editor marks the step, and runs, tries and schedules need the write confirmation (like This request changes data on the target for HTTP). Subscriptions are not supported (they need a stream); validation refuses them.
Generating a test from the schema: in Tests → Import API / HAR, enter the endpoint URL under From a URL. When the address does not return a document, Spitfire asks for introspection and offers one step per query and mutation field. You can also upload an introspection result (JSON) as a file. Details: Import.
Metrics
The same as HTTP: req_duration, req_failed (HTTP errors and GraphQL errors), http_req_waiting, http_req_connecting, http_req_tls. A VU's HTTP and GraphQL steps share its connections.
Common problems
Symptom: High req_failed, error type "connection refused".
Cause: The target port is closed, the service is down, or the runner cannot reach it.
Fix: Check the URL's host and port; try curl -v <url> from the runner machine.
Symptom: x509: certificate signed by unknown authority.
Cause: The target is signed by a private CA.
Fix: Put the CA in the CA certificate field of a Client certificate (mTLS) connection and pick it in the step. Only in a test environment may you temporarily use Options → Skip TLS certificate verification.
Symptom: The server answers 400 No required SSL certificate was sent or the handshake fails.
Cause: The server requires mTLS and the step presents no certificate.
Fix: Follow Client certificate with mTLS.
Symptom: 401/403 errors that grow after the first iterations. Cause: The token expires, or the login step runs only once. Fix: Make the token outlive the run, or untick Run once per VU on the login step.
Symptom: Starting a run shows This test modifies data; confirm to continue. Cause: An HTTP step has This request changes data on the target ticked (or the test has an approved SQL, MongoDB or Redis write). Fix: Tick I understand this test changes real data; start it in the run dialog. This is a safety measure; make sure you target the right environment.
Symptom: A WebSocket Wait for a message step times out. Cause: The expected text never arrives, or the message was consumed by an earlier step. Fix: Check Text the awaited message contains; raise the Wait; use Try it to see what arrives.
Symptom: A GraphQL step fails although it gets HTTP 200.
Cause: The response has errors (authorization, validation, a missing record…).
Fix: Read the first error message in the run page's error samples; use Try to see the whole response. If the error is expected, tick Don't fail responses with errors.
Symptom: Load schema says "Could not get the schema". Cause: Introspection is turned off on the server (common in production), or headers are missing. Fix: Turn introspection on in the test environment or write the query by hand; add the authentication header.

