Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/gatling/gatling.io-doc/llms.txt

Use this file to discover all available pages before exploring further.

Gatling’s WebSocket support is an extension to the HTTP SDK, making ws a first-class citizen in your scenario alongside regular HTTP actions. Because WebSocket is a full-duplex protocol—messages can arrive from the server at any time, not only in response to a client request—Gatling models the WebSocket flow as a parallel branch within a scenario. The HTTP branch and the WebSocket branch run concurrently, each maintaining its own state; you can reconcile them by saving values captured in WebSocket checks back into the shared Session. WebSocket support was originally contributed by Andrew Duffy.

Multiple WebSockets Per User

When a virtual user needs more than one WebSocket connection simultaneously, assign a unique name to each and reference it in every subsequent action:
Java
ws("connect primary").wsName("primary").connect("/feed")
ws("connect secondary").wsName("secondary").connect("/events")

// later...
ws("send on primary").wsName("primary").sendText("ping")
ws("close primary").wsName("primary").close()
If you only open one WebSocket per user, the wsName call is optional and the default name is used automatically.

connect

Opening a WebSocket connection is the first step. The entry point is the HTTP URL of the WebSocket endpoint (the ws:// or wss:// scheme is determined by your HTTP protocol configuration).
Java
exec(ws("connect").connect("/chat"))
Scala
exec(ws("connect").connect("/chat"))

Subprotocols

Specify an application-layer subprotocol via the Sec-WebSocket-Protocol header:
Java
exec(ws("connect").connect("/chat").subprotocol("chat"))

onConnected Hook

Define a chain of actions to execute right after the WebSocket is (re-)connected. This is useful for sending an authentication frame or a subscription message immediately upon connection:
Java
exec(
  ws("connect")
    .connect("/feed")
    .onConnected(
      exec(ws("auth").sendText("{\"type\":\"auth\",\"token\":\"#{authToken}\"}"))
        .pause(1)
    )
)

close

Gracefully close the WebSocket once you no longer need it:
Java
exec(ws("close").close())

Sending Messages

WebSocket messages are sent as either text or binary frames.
Java
exec(ws("send message").sendText("{\"action\":\"subscribe\",\"channel\":\"#{channel}\"}"))
Supports body types: raw strings with EL, ElFileBody, PebbleStringBody, and PebbleFileBody.

Checks

Gatling WebSocket checks are blocking—the virtual user pauses and waits until a matching inbound frame arrives or the timeout expires.

Setting Checks

Checks can be attached at two points in the scenario flow:
Java
exec(
  ws("connect and check")
    .connect("/feed")
    .await(10).on(
      ws.checkTextMessage("welcome").check(
        jsonPath("$.type").is("welcome")
      )
    )
)

Single vs. Multiple Check Sequences

You can stack multiple checks in a single await call (each check expects one frame), or use multiple await blocks with different timeouts:
exec(ws("subscribe")
  .sendText("{\"cmd\":\"subscribe\"}")
  .await(10).on(
    ws.checkTextMessage("ack").check(jsonPath("$.status").is("ok")),
    ws.checkTextMessage("data").check(jsonPath("$.type").is("snapshot"))
  ))

Creating Checks

Use ws.checkTextMessage("checkName") or ws.checkBinaryMessage("checkName") to build a check definition. Almost all standard Gatling check criteria are available.
Java
// Single criterion
ws.checkTextMessage("price update")
  .check(jsonPath("$.price").saveAs("latestPrice"))

// Multiple criteria on the same frame
ws.checkTextMessage("order fill")
  .check(
    jsonPath("$.type").is("fill"),
    jsonPath("$.orderId").saveAs("filledOrderId"),
    jsonPath("$.quantity").ofInt().is(100)
  )

Silent Checks

Mark a check as silent to exclude it from reports regardless of outcome:
Java
ws.checkTextMessage("heartbeat").silent()
  .check(jsonPath("$.type").is("ping"))

Matching Messages

Use matching to filter which inbound frames a check should apply to. Non-matching frames are ignored. The matching criterion is a standard check expression that does not accept saveAs.
Java
ws.checkTextMessage("order update for my order")
  .matching(jsonPath("$.orderId").is("#{orderId}"))
  .check(jsonPath("$.status").saveAs("orderStatus"))

Processing Unmatched Messages

Messages that arrive but are not matched by any active check can be buffered and processed later. Enable buffering by setting wsUnmatchedInboundMessageBufferSize on the HTTP protocol:
Java
HttpProtocolBuilder httpProtocol = http
  .baseUrl("http://localhost:9000")
  .wsUnmatchedInboundMessageBufferSize(100);
The buffer is reset when a message is sent or processUnmatchedMessages is called. Call ws.processUnmatchedMessages on the ws DSL object (not on a named action):
Java
ws.processUnmatchedMessages((messages, session) -> {
  // messages sorted oldest-first
  // types: WsInboundMessage.Text, WsInboundMessage.Binary
  return session.set("bufferedCount", messages.size());
})
In JavaScript/TypeScript, use the helper type guards:
JavaScript
import { isWsInboundMessageBinary, isWsInboundMessageText } from "@gatling.io/http";

ws.processUnmatchedMessages((messages, session) => {
  const texts = messages.filter(isWsInboundMessageText);
  return session.set("textCount", texts.length);
});

HTTP Protocol Parameters for WebSocket

The following options are available on the HTTP protocol builder when using WebSocket:
Java
HttpProtocolBuilder httpProtocol = http
  .baseUrl("http://localhost:9000")
  .wsBaseUrl("ws://localhost:9000")            // optional: override base URL for ws
  .wsReconnect()                               // automatically reconnect on drop
  .wsMaxReconnects(3)                          // max reconnection attempts
  .wsAutoReplyTextFrame(text -> "pong")        // auto-reply to text frames (e.g. ping/pong)
  .wsAutoReplySocketIo4()                      // auto-handle Socket.IO v4 heartbeats
  .wsUnmatchedInboundMessageBufferSize(50);

Debugging

To inspect WebSocket traffic in detail, add the following logger to your Logback configuration:
logback-test.xml
<logger name="io.gatling.http.action.ws.fsm" level="DEBUG" />

Full Example

Java
import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;
import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;

public class WebSocketSimulation extends Simulation {

  HttpProtocolBuilder httpProtocol = http
    .baseUrl("http://localhost:9000")
    .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
    .wsUnmatchedInboundMessageBufferSize(20);

  ScenarioBuilder scn = scenario("WebSocket chat simulation")
    .exec(http("Home").get("/"))
    .pause(1)
    .exec(
      ws("connect WS").connect("/chat")
        .onConnected(
          exec(ws("identify").sendText("{\"user\":\"#{username}\"}"))
        )
    )
    .pause(1)
    .repeat(5).on(
      exec(
        ws("send message")
          .sendText("Hello #{username}!")
          .await(5).on(
            ws.checkTextMessage("message echo")
              .check(jsonPath("$.msg").exists())
          )
      )
      .pause(1)
    )
    .exec(ws("close WS").close());

  { setUp(scn.injectOpen(rampUsers(50).during(30))).protocols(httpProtocol); }
}

Build docs developers (and LLMs) love