Functional specs support was initially contributed by Andrew Duffy.

WebSocket support is an extension to the HTTP DSL, whose entry point is the ws(requestName: Expression[String]) method.

WebSocket protocol is very different from the HTTP one as the communication is 2 ways: both client-to-server and server-to-client, so the model is different from the HTTP request/response pair.

As a consequence, main HTTP branch and a WebSocket branch can exist in a Gatling scenario in a dissociated way, in parallel. When doing so, each flow branch has it’s own state, so a user might have to reconcile them, for example when capturing data from a WebSocket check and wanting this data to be available to the HTTP branch.

Common operations

If you want to deal with several WebSockets per virtual users, you have to give them a name and pass this name on each ws operation:

wsName(name: String)

For example:

ws("WS Operation").wsName("myCustomName")

Of course, this step is not required if you deal with one single WebSocket per virtual user.


The first thing is to open a WebSocket:

open(url: Expression[String])

For example:

exec(ws("Connect WS").open("/room/chat?username=steph"))


When you’re done with a WebSocket, you can close it:


For example:

exec(ws("Close WS").close)

Send a Message

You may send binary or text messages:

  • sendText(text: Expression[String])
  • sendBytes(bytes: Expression[Array[Byte]])

For example:

  .sendText("""{"text": "Hello, I'm ${id} and this is message ${i}!"}"""))

Server Messages: Checks

You deal with incoming messages with the check() method.

Gatling currently supports only one check at a time per WebSocket.

Set a Check

Checks can be set in 2 ways.

Either, when sending a message:


Or, from the main HTTP flow:

exec(ws("Set Check").check(myCheck))

If a check was already registered on the WebSocket at this time, it’s considered as failed and replaced with the new one.

Cancel a Check

One can decide to cancel a pending check:

exec(ws("Cancel Check").cancelCheck)

Build a Check

Now, to the matter at heart, how to build a WebSocket check.

Step 1: Blocking or non Blocking

The first thing is to decide if the main HTTP flow is blocked until the check completes or not.

wsListen creates a non blocking check: the main HTTP flow will go on and Gatling will listen for WebSocket incoming messages on the background.

wsAwait creates a blocking check: the main HTTP flow is blocked until the check completes.

Step 2: Set the Timeout

within(timeout: FiniteDuration)

Step 3: Exit condition

until(count: Int): the check will succeed as soon as Gatling has received the expected count of matching messages

expect(count: Int): Gatling will wait until the timeout and the check will succeed if it has received the expected count of matching messages

expect(range: Range): same as above, but use a range instead of a single expected count

Step 4: Matching condition

Websocket checks support the same kind of operations as for HTTP bodies:

regex(expression: Expression[String]): use a regular expression

jsonPath(path: Expression[String]): use JsonPath

jsonpJsonPath(path: Expression[String]): use JsonPath on a JSONP String

See HTTP counterparts for more details.

Step 5: Saving (optional)

Just like HTTP checks, you may save data into the virtual user’s session.

For example:

  ws("Send Message")
    .sendText("hello, I'm Stephane")
    .check(wsListen.within(30 seconds).until(1).regex("hello (.*)").saveAs("name"))


When using non blocking checks that save data, state is stored in a different flow than the main one.

So, you may have to reconcile the main flow state and the WebSocket flow one.

This can be done:

  • implicitly when performing an action on the WebSocket from the main flow, such as send a message to the server
  • explicitly with the reconciliate method.
exec(ws("Reconciliate states").reconciliate)


Websocket support introduces new HttpProtocol parameters:

wsBaseURL(url: String): similar to standard baseURL for HTTP, serves as root that will be prepended to all relative WebSocket urls

wsBaseURLs(urls: String*): similar to standard baseURLs for HTTP, serves as round-robin roots that will be prepended to all relative WebSocket urls

wsReconnect: automatically reconnect a WebSocket that would have been closed by someone else than the client.

wsMaxReconnects(max: Int): set a limit on the number of times a WebSocket will be automatically reconnected


Here’s an example that runs against Play 2.2‘s chatroom sample (beware that this sample is missing from Play 2.3 and above):

val httpConf = http
  .acceptEncodingHeader("gzip, deflate")

val scn = scenario("WebSocket")
  .exec(session => session.set("id", "Steph" + session.userId))
  .exec(ws("Connect WS").open("/room/chat?username=${id}"))
  .repeat(2, "i") {
    exec(ws("Say Hello WS")
      .sendText("""{"text": "Hello, I'm ${id} and this is message ${i}!"}""")
      .check(wsAwait.within(30).until(1).regex(".*I'm still alive.*")))
  .exec(ws("Close WS").close)