WebSocket
Learn the possible WebSocket operations with Gatling: connect, close, send
WebSocket 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")
If you set an explicit name for the WebSocket, you’ll have to make it explicit for every other WebSocket actions you’ll define later in the scenario.
Of course, this step is not required if you deal with one single WebSocket per virtual user.
Connect
The first thing is to connect a WebSocket:
connect(url: Expression[String])
For example:
exec(ws("Connect WS").connect("/room/chat?username=steph"))
//#wsConnect
//#subprotocol
exec(ws("Connect WS").connect("/room/chat?username=steph").subprotocol("custom"))
//#subprotocol
//#onConnected
exec(ws("Connect WS").connect("/room/chat?username=steph")
.onConnected(
exec(ws("Perform auth")
.sendText("Some auth token"))
.pause(1)
))
//#onConnected
//#close
exec(ws("Close WS").close)
//#close
//#sendText
exec(ws("Message")
.sendText("""{"text": "Hello, I'm ${id} and this is message ${i}!"}"""))
//#sendText
//#create-single-check
val myCheck = ws.checkTextMessage("checkName")
.check(regex("hello (.*)").saveAs("name"))
//#create-single-check
//#create-multiple-checks
ws.checkTextMessage("checkName")
.check(
jsonPath("$.code").ofType[Int].is(1).saveAs("code"),
jsonPath("$.message").is("OK")
)
//#create-multiple-checks
//#matching
ws.checkTextMessage("checkName")
.matching(jsonPath("$.uuid").is("${correlation}"))
.check(jsonPath("$.code").ofType[Int].is(1))
//#matching
//#check-from-connect
exec(ws("Connect").connect("/foo").await(30 seconds)(myCheck))
//#check-from-connect
//#check-from-message
exec(ws("Send").sendText("hello").await(30 seconds)(myCheck))
//#check-from-message
val myCheck1 = myCheck
val myCheck2 = myCheck
//#check-single-sequence
// expecting 2 messages
// 1st message will be validated against myCheck1
// 2nd message will be validated against myCheck2
// whole sequence must complete withing 30 seconds
exec(
ws("Send")
.sendText("hello")
.await(30 seconds)(myCheck1, myCheck2)
)
//#check-single-sequence
//#check-multiple-sequence
// expecting 2 messages
// 1st message will be validated against myCheck1
// 2nd message will be validated against myCheck2
// both sequences must complete withing 15 seconds
// 2nd sequence will start after 1st one completes
exec(
ws("Send")
.sendText("hello")
.await(15 seconds)(myCheck1)
.await(15 seconds)(myCheck2)
)
//#check-multiple-sequence
//#check-matching
exec(
ws("Send")
.sendText("hello")
.await(1 second)(
ws.checkTextMessage("checkName")
.matching(jsonPath("$.uuid").is("${correlation}"))
.check(jsonPath("$.code").ofType[Int].is(1))
)
)
//#check-matching
//#chatroom-example
val httpProtocol = http
.baseUrl("http://localhost:9000")
.acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
.doNotTrackHeader("1")
.acceptLanguageHeader("en-US,en;q=0.5")
.acceptEncodingHeader("gzip, deflate")
.userAgentHeader("Gatling2")
.wsBaseUrl("ws://localhost:9000")
val scn = scenario("WebSocket")
.exec(http("Home").get("/"))
.pause(1)
.exec(session => session.set("id", "Steph" + session.userId))
.exec(http("Login").get("/room?username=${id}"))
.pause(1)
.exec(ws("Connect WS").connect("/room/chat?username=${id}"))
.pause(1)
.repeat(2, "i") {
exec(
ws("Say Hello WS")
.sendText("""{"text": "Hello, I'm ${id} and this is message ${i}!"}""")
.await(30 seconds)(
ws.checkTextMessage("checkName").check(regex(".*I'm still alive.*"))
)
).pause(1)
}
.exec(ws("Close WS").close)
//#chatroom-example
}
You can specify a subprotocol:
exec(ws("Connect WS").connect("/room/chat?username=steph").subprotocol("custom"))
You can define a chain of actions to be performed after (re-)connecting with onConnected
:
exec(ws("Connect WS").connect("/room/chat?username=steph")
.onConnected(
exec(ws("Perform auth")
.sendText("Some auth token"))
.pause(1)
))
Close
When you’re done with a WebSocket, you can close it:
close
For example:
exec(ws("Close WS").close)
Send a Message
You may send text or binary messages:
sendText(text: Expression[String])
sendBytes(bytes: Expression[Array[Byte]])
For example:
exec(ws("Message")
.sendText("""{"text": "Hello, I'm ${id} and this is message ${i}!"}"""))
Server Messages: Checks
Gatling currently only supports blocking checks that will waiting until receiving expected message or timing out.
Set a Check
You can set a check right after connecting:
exec(ws("Connect").connect("/foo").await(30 seconds)(myCheck))
Or you can set a check right after sending a message to the server:
exec(ws("Send").sendText("hello").await(30 seconds)(myCheck))
You can set multiple checks sequentially. Each one will expect one single frame.
You can configure multiple checks in a single sequence:
// expecting 2 messages
// 1st message will be validated against myCheck1
// 2nd message will be validated against myCheck2
// whole sequence must complete withing 30 seconds
exec(
ws("Send")
.sendText("hello")
.await(30 seconds)(myCheck1, myCheck2)
)
You can also configure multiple check sequences with different timeouts:
// expecting 2 messages
// 1st message will be validated against myCheck1
// 2nd message will be validated against myCheck2
// both sequences must complete withing 15 seconds
// 2nd sequence will start after 1st one completes
exec(
ws("Send")
.sendText("hello")
.await(15 seconds)(myCheck1)
.await(15 seconds)(myCheck2)
)
Create a check
You can create checks for text and binary frames with checkTextMessage
and checkBinaryMessage
.
You can use almost all the same check criteria as for HTTP requests.
val myCheck = ws.checkTextMessage("checkName")
.check(regex("hello (.*)").saveAs("name"))
You can have multiple criteria for a given message:
ws.checkTextMessage("checkName")
.check(
jsonPath("$.code").ofType[Int].is(1).saveAs("code"),
jsonPath("$.message").is("OK")
)
Matching messages
You can define matching
criteria to filter messages you want to check.
Matching criterion is a standard check, except it doesn’t take saveAs
.
Non matching messages will be ignored.
ws.checkTextMessage("checkName")
.matching(jsonPath("$.uuid").is("${correlation}"))
.check(jsonPath("$.code").ofType[Int].is(1))
Configuration
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
Example
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 httpProtocol = http
.baseUrl("http://localhost:9000")
.acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
.doNotTrackHeader("1")
.acceptLanguageHeader("en-US,en;q=0.5")
.acceptEncodingHeader("gzip, deflate")
.userAgentHeader("Gatling2")
.wsBaseUrl("ws://localhost:9000")
val scn = scenario("WebSocket")
.exec(http("Home").get("/"))
.pause(1)
.exec(session => session.set("id", "Steph" + session.userId))
.exec(http("Login").get("/room?username=${id}"))
.pause(1)
.exec(ws("Connect WS").connect("/room/chat?username=${id}"))
.pause(1)
.repeat(2, "i") {
exec(
ws("Say Hello WS")
.sendText("""{"text": "Hello, I'm ${id} and this is message ${i}!"}""")
.await(30 seconds)(
ws.checkTextMessage("checkName").check(regex(".*I'm still alive.*"))
)
).pause(1)
}
.exec(ws("Close WS").close)