This article will hopefully save you some of the trouble I had, when I tried to use Gatling for Socket.IO sockets. Gatling has support for web sockets but one has to do a lot of research and workarounds in order to make it work with Socket.IO sockets.

What are Gatling and Socket.IO?

Gatling is a powerful load-testing solution for applications, APIs, and microservices (https://gatling.io/).

Socket.IO is a library that enables low-latency, bidirectional and event-based communication between a client and a server (https://socket.io/docs/v4/). The Socket.IO connection can be established with different low-level transports and one of them are web sockets. Socket.IO uses the web socket protocol as one of the underlying protocols for bidirectional communication.

Load testing Socket.IO

Back to our problem…

The Socket.IO server

I prefer to always work with minimal examples when I test something new. I want to avoid the complexity of a large problem getting into the way of understanding how to solve the technical issues. That’s why the working example is very simple:

  • The Socket.IO server responds to the special connection event.
  • On a successful connection:
    • the socket accepts messages with the message event and the message content. On the reception of the message it logs the message and then emits the message using a broadcast event.
    • Finally, the socket accepts the special disconnect event with a log message.

This is the implementation of the server in JavaScript:

const { Server } = require("socket.io");
const cors = require("cors");

const io = new Server({
  transports: ["websocket", "polling"],
  cors: {
    origin: "*",
    methods: ["GET", "POST"],
  },
});

io.on("connection", (socket) => {
  console.log("a user connected to the socket.io server: " + socket.id);

  socket.on("message", (msg) => {
    console.log("message: " + msg);
    io.emit("broadcast", "they say: " + msg);
  });

  socket.on("disconnect", () => {
    console.log("user disconnected: " + socket.id);
  });
});

io.listen(3000);

The only required dependencies are:

npm i socket.io cors

Back to Gatling:

Gatling websocket simulation

Following the Gatling documentation for Web Sockets: https://gatling.io/docs/gatling/reference/current/http/websocket/ I created a simple simulation:

package computerdatabase;

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

import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;

public class WebsocketExampleSimulation extends Simulation {

    HttpProtocolBuilder httpProtocol = http
            .wsBaseUrl("ws://localhost:3000");

    ScenarioBuilder scene = scenario("WebSocket")
            .exec(ws("Connect WS").connect("/"))
            .pause(1)
            .exec(ws("Say hi")
                    .sendText("message Hi")
                    .await(30).on(
                            ws.checkTextMessage("checkMessage")
                                    .check(regex(".*they say*"))))
            .pause(1)
            .exec(ws("Close WS").close());

    {
        setUp(scene.injectOpen(
                atOnceUsers(1))
                .protocols(httpProtocol));
    }
}

… but nothing works the first time:

>>>>>n>cdrGCSjCCaloa.lmlsonyiiaohRbnE.exseeaehrInedqlcirOtr:utoEeWerxicSjsWscso:.tSesnispunC.teelIidciOoteEntsnx:etcxiePtsiprstefrsimreuoaaaentmcd:uehrecPebdlruoectsmleaostseouerrrvdeeerrclhboausstecWleobsSeodcktehtewWaesbSaolcr(((keOOOeaKKKtd===y000aKKK111OOO===(((211333333...333333%%%)))

Well in this particular case, nothing worked after several hours of all kinds of attempts (which I will not present) until I managed to figure out all the pieces of the puzzle. And this is the reason I want to share with you my findings.

Testing with Postman

To make sure that my server is working properly I used Postman and I created a new Socket.IO connection.

Note

Well, in reality I did many other things before trying out Postman and this is one of the major mistakes that I have done. I should have used a trusted tool to make sure that my server is working instead of trying things around.

When I created a new Socket.IO client in Postman I could successfully connect to the Socket.IO server, send and receive messages. But the Gatling simulation could not even connect to the server.

Gatling URL for Socket.IO connections

So my first investigation involved finding out how to connect to the Socket.IO server. Searching for examples, I discovered a question in a forum (https://community.gatling.io/t/checking-socket-io-messages/7350) using the syntax:

.exec(ws("Connect WS").connect("/socket.io/?EIO=4&transport=websocket"))

This time the Gatling output was:

>>>>>>crGCScWCaloahelsonyebohRbncESseeaehkroedqlciMrc:uteokWesreSsWsst:tSasgcCerlaisehnetdiwshsiuleedwcaliotsiengorfdoerrcbhuetckW:ebSocketwasalr((((eOOOOaKKKKd====y2110KKKK11OOOO====((10015500..0000%%))

Gatling reported that Connect WS received an OK response. Even the Say hi received and OK response. But on the server side there was no log message for the connection or the reception of the message.

Logging the Socket.IO server connection with Postman as a client

This is the part that took me the longest to figure out and I managed to solve it only after turning on the debug messages on the server side and trying the successful connection and transfer of messages with Postman.

On a terminal, I started the server with:

DEBUG="*" node server.js
ssssoooocccckkkkeeeetttt....iiiioooo::::sssseeeerrrrvvvveeeerrrriccanrrtieettaaaittcaiihlnniiggnzgihentncgtglpiinneasenme.terisvospeearircnveasintn/dagn+bcr0ieemnqsdwiihntaghndtolopetr3s0+0{20"mts+r2amnssports":["websocket","polling"],"cors":{"origin":"*","methods":["GET","POST"]},"cleanupEmptyChildNamespaces":false,"path":"/socket.io"}+3ms

And then I connected with Postman. The debug messages are:

aeeeeeeeeeeseessssssseeeunnnnnnnnnnonnooooooonnnsggggggggggcggcccccccgggeiiiiiiiiiikiikkkkkkkiiirnnnnnnnnnnenneeeeeeennneeeeeeeeeeteettttttteeec:::::::.::.......:::oawhtsssswtiwsiiiiiiisswnpraroooosrosoooooooooosnpinacccca:c-::::--cceltdnkkkkwnsrkpcnssppkkwcyisseeeerseeealaooaaeertinhpttttiprctrimccrrttiengaotoveseekksstdgkrrrsfireirenseeeesfihiteeelntrvertpttrrelntmenaanugecanugoiagrdddssidedccsjeedsddeyyih"enieoeoonnih"tdecaSSni0tc"vcnciccni4hlrldttgn{to4eonaknoogn0eesiyaag"im0ddededdg{w:eSttpsni"ecdtriep"santeeabignpdtiondabsor{tacudg+aincogcuice"tuukf"r2c0ngom{kfdkA"eppef:ecmkgnp"ef"enc_ddte"qoseasnJatte:t°c9uaar_untstoencyr".1eSptt"9enoccYkp"JisxdeeotSsem{ktOeemtno+sGaddpoxtce"neeft"eoY0-dteGtstatdD:sOsmCReffntd+isymW{0stfeso_drr"rR3oapet-v",arDrn2ooa_mngesopt"gaWvtRfmm(n2se"pwdydenverer{sRw:anrmpa"sproMouo"pei+0csi1etpd:lDmnpsoMt4,eptz"a(om-YdeirDhm"ir:"0r1JA5uendtYsn//nC0:{tznlbnfi"5isgE,{"rYlAdin:+bdp++A""s+COoAeng"0A"00pAdsi0EfwAfe_mA_:mmaAaidmAD-Aidt9sA9"sscBtd"sAWO"noSAS/ka":Avretx"x"e+":"Bpi+dooG,G}t0:"J"dg3pd"dm{Jn}mimtoeRuR++s"nY"1nsopn_p_00sYOz"e2g2mmiOf+r:on+RrRssdfD4C"pi1eae"DWmE*enmMdM:WvsA"ngsDeD"vpA}YsYJpdA(+5"5ndmB+w0b:bYm12emA[AO1zmbsA]AfzrssA,ADrCoA"AWCEc"pvEAk,i+pAAe"n1dAAtugmmAB)pI1B"gnz"}+rtr})0aeC,mdrE"+sevAn3saAsm"lAps:"B"[:":]2}",5,/"0""p0n}i0sn,pag""sIp:ni"0tn/{eg""rT}sviiam+dle2""om::us"2tJ5"n0:Y02O00f,0D"0Wp0vi,pn"dgmmTa1ixzmPreaCoyEulAtoA"aA:dB2""0:}0100+001,0m"0sm0a0x}P"ay+l0omasd":1000000})+0ms

At the bottom I could see the log message from my server, indicating a successful connection. The interesting messages are those starting with engine:ws:

eeennngggiiinnneee:::wwwssswrwrericitetiiinvngegd""0"4{40"0{s""isd+i"2d