Server using Socket.IO
Socket.IO is not a WebSocket implementation. Although Socket.IO indeed uses WebSocket as a transport when possible, it adds some metadata to each packet: the packet type, the namespace and the ack id when a message acknowledgement is needed. That is why a WebSocket client will not be able to successfully connect to a Socket.IO server, and a Socket.IO client will not be able to connect to a WebSocket server (like ws://echo.websocket.org) either. Please see the protocol specification here.
Useing k6 ws module connect will throw an error:
ERRO[0000] GoError: websocket: bad handshake
Thanks for posting this issue! Socket.io support has been requested a few times, or rather people have asked questions about it. My previous response has been that we don't support it yet, but maybe writing some wrapper around out websocket support would do the trick - I just now realize how wrong I was ... :disappointed: I didn't understand that it was completely impossible for a normal websocket client to connect to a socket.io server... :man_facepalming:
So, we'd like to have socket.io support in k6, eventually... Unfortunately it's probably not going to be very high in our roadmap for the next several months :disappointed: . One of the reasons is that, in order to support such an asynchronous protocol properly (and to actually improve our current WebSocket support and pave the way for things like gRPC), we need to fully implement global per-VU event loops (https://github.com/loadimpact/k6/issues/882).
I also did a cursory search for a Go socket.io library and came up with https://github.com/googollee/go-socket.io . It claims to only support version 1.4 of the protocol, whereas the main site advertises 2.0...
Thanks for your reply. I temporary use other tools to test socket.io recently, it doesn't feel easy to use.
Hope to use k6's socket.io feature as soon as possible, then I will migrate the code to k6.
@na--
Hello
My server use Node.js socket.io, is there any way to test it?
Is it possible to use nodejs socket.io node_modules through webpack? Or there is no solution ? 馃槥
@lemon-li It seems that the socket.io client library could be bundled with Webpack (see here, assuming that still works), so you might be able to import it in k6, but as @na-- mentioned, you'll probably run into issues because the k6 JS runtime lacks a global event loop, which is needed for these async protocols.
Once that functionality is in k6, this might be a good candidate to implement as a plugin. So several things need to align before this is officially supported.
using pure WebSocket implementation to connect to socket.io server worked for me, the key here is to inspect in browser dev tools to see what is the actual url used to connect and what are the messages sent between client and server. You script should simulate this
e.g.
import ws from "k6/ws";
import { check } from "k6";
export default function () {
const { CHAT_WS } = __ENV;
const token =
"....token...";
const url = `${CHAT_WS}/socket.io/?token=${token}&tokenType=jwt&EIO=3&transport=websocket`;
var response = ws.connect(url, {}, function (socket) {
socket.on("open", function open() {
// console.log("connected");
// socket.send(Date.now());
socket.setInterval(function timeout() {
socket.ping();
console.log("Pinging every 5sec (setInterval test)");
}, 1000 * 5);
});
socket.on("message", function incoming(msg) {
// console.log(msg);
if (msg === "40") {
socket.send("40/chat?token=" + token + "&tokenType=jwt,");
}
if (msg === "40/chat") {
socket.send(
'42/chat,["ACTION_1",{"data": "x"}]'
);
}
if (msg && msg.startsWith("42/chat,")) {
const data = JSON.parse(msg.substring("42/chat,".length));
const action = data[0];
console.log(`received msg: ${action}`);
// waiting for specific action and responding to it
if (action === "ACTION_X") {
const chatMsg = `k6 hello, vu=${__VU}, iter=${__ITER}, ${Date.now().toString()}`;
socket.send(
`42/chat,["MESSAGE_ACTION",{"message": "${chatMsg}"}]`
);
}
}
});
socket.on("close", function close() {
console.log("disconnected");
});
socket.on("error", function (e) {
console.log("error", e);
if (e.error() != "websocket: close sent") {
console.log("An unexpected error occured: ", e.error());
}
});
socket.setTimeout(function () {
console.log("60 seconds passed, closing the socket");
socket.close();
}, 1000 * 60);
});
check(response, { "status is 101": (r) => r && r.status === 101 });
}
/chat is the custom namespace used in my case, it is empty for default namespace.
I have no idea what those 40 and 42 codes mean, it's just something socket.io adds internally
I wrote a minimal packet parser for socket.io/engine.io.
It does not support edge cases.
https://gist.github.com/wreulicke/e05b42ba79f42768a54f3b2a9cb7c416
And also, I tried to use socket.io-parser and engine.io-parser with some bundle tools for webpack/parcel/etc.
But, does not work for me...
(I don't try to use it with browserify. please let me know if working)
@na-- Any plans for support being added for socket.io?
@qwertynik There are no concrete plans for this, but have you tried the workarounds mentioned above?
If those don't work for you, you can try creating an xk6 extension for it, though you might run into issues with supporting the async workflows. See this article for an introduction.
@imiric
Have not tried the workarounds yet. Was looking for testing tools to test socket.io. Landed upon a couple of tools. Using jMeter for now.
Thanks for the link to extension development. It is highly unlikely that I will be building one at least in the near future.
Most helpful comment
using pure WebSocket implementation to connect to socket.io server worked for me, the key here is to inspect in browser dev tools to see what is the actual url used to connect and what are the messages sent between client and server. You script should simulate this
e.g.
/chatis the custom namespace used in my case, it is empty for default namespace.I have no idea what those
40and42codes mean, it's just something socket.io adds internally