I'm trying to loadtest my solution using jmeter. Since I use namespaces I need to know how I can connect to the specific namespace in order to send my messages.
I have been using google chrome devtools in order to figure out how the connection to a specific namespace is established, but somehow I can't find it.
Our loadtest currently fails as soon as we try to emit on a namespace.
On the server I can see the code for the namespace is never executed, which means we actually never connect.
Does anybody know how to connect to the namespace using httprequest or websocket?
Or is this completely handled in the javascript, which makes this actually completely untestable?
Same issue on my side.
Like Marco, I use JMeter with the WebSocketSampler plugin ( https://github.com/maciejzaleski/JMeter-WebSocketSampler ).
Can someone help us?
Socket.IO is not just websocket or http, but has own protocols on top on them, thus I think using JMeter is not easy (or impossible).
Please refer the following for protocols.
Thank you @nkzawa for your answer. What we did is generating exactly the same requests as you will see in the chrome devtools. We are aware of this protocol and also used it to do the actual socket connection. However as soon as I send a message to my namespace our jMeter script disconnects. We know it is most probably caused by the fact that the server never really connects to the namespace when we do exactly the same requests as you can see in the google devtools network tab.
So what we do very simply.
GET HTTP/socket.io/?EIO=3&transport=polling&t=1417083573418-0
OPTIONS HTTP /socket.io/?EIO=3&transport=polling&t=1417083577725-1&sid=vgBVmF_jpLhlexsQAAAB
GET HTTP /socket.io/?EIO=3&transport=polling&t=1417083577728-2&sid=vgBVmF_jpLhlexsQAAAB
GET WS /socket.io/?EIO=3&transport=websocket&sid=vgBVmF_jpLhlexsQAAAB
POST HTTP /socket.io/?EIO=3&transport=polling&t=1417083577725-1&sid=vgBVmF_jpLhlexsQAAAB
This establishes the websocket connection...
On the socket we have the following messages in Chrome devtools which we do exactly the same in jMeter: (+ send - receive)
+ 3
+ 2
+ 3
+ 2
- 43/notifications,0[{"successfull": true}]
+ 42/notifications,0["listenNotifications", {"userId":6}]
+ 5
- probe3
On the server this result in following when using the webpage:
Websocket connected
Namespace notifications connected
Notifications: Received message listen for notifications
However when we do the same using jMeter the following happens on the server:
Websocket connected
Websocket disconnected **This happens as soon as we sent the message to start listening for notifications**
In the past we have been using socket.io 0.9 which we where perfectly able to test using jmeter by sending the data as defined in the protocol for socket.io 0.9. However at that point in time we didn't had any namespaces.
Hope this clarifies a little bit more. Looking forward to an answer.
Can you enable debug logs (DEBUG=* node server
), and post a dump of server logs?
Log when using the webpage
socket.io:server initializing namespace / +0ms
socket.io:server initializing namespace /presence +101ms
socket.io:server initializing namespace /notifications +2ms
socket.io:server creating http server and binding to 3000 +2ms
socket.io:server creating engine.io instance with opts {"path":"/socket.io"} +3ms
socket.io:server attaching client serving req handler +3ms
engine intercepting request for path "/socket.io/" +0ms
engine handling "GET" http request "/socket.io/?EIO=3&transport=polling&t=1417090905372-8" +1ms
engine handshaking client "TYQy83VYbEKgWSc3AAAA" +4ms
engine:socket sending packet "open" ({"sid":"TYQy83VYbEKgWSc3AAAA","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":60000}) +1ms
engine:polling setting request +13ms
engine:socket flushing buffer to transport +0ms
engine:polling writing " �0{"sid":"TYQy83VYbEKgWSc3AAAA","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":60000}" +2ms
engine:socket executing batch send callback +18ms
socket.io:server incoming connection with id TYQy83VYbEKgWSc3AAAA +62ms
socket.io:client connecting to namespace / +0ms
socket.io:namespace adding socket to nsp / +0ms
engine intercepting request for path "/socket.io/" +31ms
engine handling "GET" http request "/socket.io/?EIO=3&transport=polling&t=1417090906214-10&sid=TYQy83VYbEKgWSc3AAAA" +0ms
engine setting new request for existing client +1ms
engine:polling setting request +0ms
socket.io:socket socket connected - writing packet +0ms
socket.io:socket joining room TYQy83VYbEKgWSc3AAAA +0ms
socket.io:client writing packet {"type":0,"nsp":"/"} +56ms
socket.io-parser encoding packet {"type":0,"nsp":"/"} +0ms
socket.io-parser encoded {"type":0,"nsp":"/"} as 0 +0ms
engine:socket sending packet "message" (0) +43ms
engine:socket flushing buffer to transport +1ms
engine:polling writing "�40" +0ms
socket.io:socket joined room TYQy83VYbEKgWSc3AAAA +2ms
engine intercepting request for path "/socket.io/" +2ms
engine handling "OPTIONS" http request "/socket.io/?EIO=3&transport=polling&t=1417090906212-9&sid=TYQy83VYbEKgWSc3AAAA" +1ms
engine setting new request for existing client +1ms
engine upgrading existing transport +9ms
engine:socket might upgrade socket transport from "polling" to "websocket" +1ms
engine:ws received "2probe" +2ms
engine:ws writing "3probe" +1ms
engine intercepting request for path "/socket.io/" +1ms
engine handling "POST" http request "/socket.io/?EIO=3&transport=polling&t=1417090906212-9&sid=TYQy83VYbEKgWSc3AAAA" +1ms
engine setting new request for existing client +0ms
engine:polling received "�40/notifications" +1ms
engine:socket packet +20ms
socket.io-parser decoded 0/notifications as {"type":0,"nsp":"/notifications"} +42ms
socket.io:client connecting to namespace /notifications +42ms
socket.io:namespace adding socket to nsp /notifications +98ms
socket.io:socket socket connected - writing packet +44ms
socket.io:socket joining room TYQy83VYbEKgWSc3AAAA +0ms
socket.io:client writing packet {"type":0,"nsp":"/notifications"} +3ms
socket.io-parser encoding packet {"type":0,"nsp":"/notifications"} +3ms
socket.io-parser encoded {"type":0,"nsp":"/notifications"} as 0/notifications +1ms
engine:socket sending packet "message" (0/notifications) +5ms
socket.io:socket joined room TYQy83VYbEKgWSc3AAAA +2ms
engine intercepting request for path "/socket.io/" +2ms
engine handling "GET" http request "/socket.io/?EIO=3&transport=polling&t=1417090906303-11&sid=TYQy83VYbEKgWSc3AAAA" +0ms
engine setting new request for existing client +1ms
engine:polling setting request +0ms
engine:socket flushing buffer to transport +0ms
engine:polling writing "�40/notifications" +0ms
engine:socket executing batch send callback +0ms
engine:ws received "5" +20ms
engine:socket got upgrade packet - upgrading +0ms
engine:ws received "42/notifications,1["listenNotifications",{"userId":6}]" +1s
engine:socket packet +0ms
socket.io-parser decoded 2/notifications,1["listenNotifications",{"userId":6}] as {"type":2,"nsp":"/notifications","id":1,"data":["listenNotifications",{"userId":6}]} +1s
socket.io:socket got packet {"type":2,"nsp":"/notifications","id":1,"data":["listenNotifications",{"userId":6}]} +1s
socket.io:socket emitting event ["listenNotifications",{"userId":6}] +0ms
socket.io:socket attaching ack callback to event +1ms
socket.io:socket sending ack [{"successfull":true,"userId":6,"count":null}] +5ms
socket.io:client writing packet {"id":1,"type":3,"data":[{"successfull":true,"userId":6,"count":null}],"nsp":"/notifications"} +1s
socket.io-parser encoding packet {"id":1,"type":3,"data":[{"successfull":true,"userId":6,"count":null}],"nsp":"/notifications"} +7ms
socket.io-parser encoded {"id":1,"type":3,"data":[{"successfull":true,"userId":6,"count":null}],"nsp":"/notifications"} as 3/notifications,1[{"successfull":true,"userId":6,"count":null}] +1ms
engine:socket sending packet "message" (3/notifications,1[{"successfull":true,"userId":6,"count":null}]) +9ms
engine:socket flushing buffer to transport +0ms
engine:ws writing "43/notifications,1[{"successfull":true,"userId":6,"count":null}]" +2ms
engine:socket executing batch send callback +2ms
socket.io-redis ignore different namespace /notifications <=> / +0ms
socket.io-redis ignore different namespace /notifications <=> /presence +12ms
socket.io-parser encoding packet {"type":2,"data":["notify",{"userId":6,"count":4}],"nsp":"/notifications"} +0ms
socket.io-parser encoded {"type":2,"data":["notify",{"userId":6,"count":4}],"nsp":"/notifications"} as 2/notifications,["notify",{"userId":6,"count":4}] +9ms
socket.io-redis ignore different namespace /notifications <=> / +11ms
socket.io-redis ignore different namespace /notifications <=> /presence +0ms
socket.io-parser encoding packet {"type":2,"data":["notify",{"userId":6,"count":4}],"nsp":"/notifications"} +1ms
socket.io-parser encoded {"type":2,"data":["notify",{"userId":6,"count":4}],"nsp":"/notifications"} as 2/notifications,["notify",{"userId":6,"count":4}] +2ms
socket.io:client writing packet ["2/notifications,[\"notify\",{\"userId\":6,\"count\":4}]"] +39ms
engine:socket sending packet "message" (2/notifications,["notify",{"userId":6,"count":4}]) +35ms
engine:socket flushing buffer to transport +0ms
engine:ws writing "42/notifications,["notify",{"userId":6,"count":4}]" +0ms
socket.io-redis ignore different namespace /notifications <=> / +4ms
socket.io-redis ignore different namespace /notifications <=> /presence +0ms
socket.io-parser encoding packet {"type":2,"data":["notify",{"userId":6,"count":4}],"nsp":"/notifications"} +2ms
socket.io-parser encoded {"type":2,"data":["notify",{"userId":6,"count":4}],"nsp":"/notifications"} as 2/notifications,["notify",{"userId":6,"count":4}] +0ms
socket.io-redis ignore different namespace /notifications <=> / +0ms
socket.io-redis ignore different namespace /notifications <=> /presence +1ms
socket.io-parser encoding packet {"type":2,"data":["notify",{"userId":6,"count":4}],"nsp":"/notifications"} +1ms
socket.io-parser encoded {"type":2,"data":["notify",{"userId":6,"count":4}],"nsp":"/notifications"} as 2/notifications,["notify",{"userId":6,"count":4}] +1ms
socket.io:client writing packet ["2/notifications,[\"notify\",{\"userId\":6,\"count\":4}]"] +4ms
engine:socket sending packet "message" (2/notifications,["notify",{"userId":6,"count":4}]) +3ms
engine:socket flushing buffer to transport +0ms
engine:ws writing "42/notifications,["notify",{"userId":6,"count":4}]" +0ms
engine:ws received "2" +22s
engine:socket packet +1ms
engine:socket got ping +0ms
engine:socket sending packet "pong" (undefined) +0ms
engine:socket flushing buffer to transport +0ms
engine:ws writing "3" +0ms
engine:ws received "2" +3s
engine:socket packet +0ms
engine:socket got ping +1ms
engine:socket sending packet "pong" (undefined) +0ms
engine:socket flushing buffer to transport +0ms
engine:ws writing "3" +0ms
When using jMeter
socket.io:server initializing namespace / +0ms
socket.io:server initializing namespace /presence +98ms
socket.io:server initializing namespace /notifications +2ms
socket.io:server creating http server and binding to 3000 +2ms
socket.io:server creating engine.io instance with opts {"path":"/socket.io"} +2ms
socket.io:server attaching client serving req handler +6ms
socket.io-redis ignore different namespace /notifications <=> / +0ms
socket.io-redis ignore different namespace /notifications <=> /presence +0ms
socket.io-parser encoding packet {"type":2,"data":["notify",{"userId":6,"count":null}],"nsp":"/notifications"} +0ms
socket.io-parser encoded {"type":2,"data":["notify",{"userId":6,"count":null}],"nsp":"/notifications"} as 2/notifications,["notify",{"userId":6,"count":null}] +1ms
socket.io-redis ignore different namespace /notifications <=> / +6ms
socket.io-redis ignore different namespace /notifications <=> /presence +0ms
socket.io-parser encoding packet {"type":2,"data":["notify",{"userId":6,"count":4}],"nsp":"/notifications"} +4ms
socket.io-parser encoded {"type":2,"data":["notify",{"userId":6,"count":4}],"nsp":"/notifications"} as 2/notifications,["notify",{"userId":6,"count":4}] +0ms
engine intercepting request for path "/socket.io/" +0ms
engine handling "GET" http request "/socket.io/?EIO=3&transport=polling&t=1417092200015-0" +1ms
engine handshaking client "OkhjEQyA3sz4x-rGAAAA" +4ms
engine:socket sending packet "open" ({"sid":"OkhjEQyA3sz4x-rGAAAA","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":60000}) +0ms
engine:polling setting request +2ms
engine:socket flushing buffer to transport +0ms
engine:polling writing " �0{"sid":"OkhjEQyA3sz4x-rGAAAA","upgrades":["websocket"],"pingInterval":25000,"pingTimeout":60000}" +2ms
engine:socket executing batch send callback +3ms
socket.io:server incoming connection with id OkhjEQyA3sz4x-rGAAAA +3s
socket.io:client connecting to namespace / +0ms
socket.io:namespace adding socket to nsp / +0ms
engine intercepting request for path "/socket.io/" +30ms
engine handling "OPTIONS" http request "/socket.io/?EIO=3&transport=polling&t=1417092200035-1&sid=OkhjEQyA3sz4x-rGAAAA" +0ms
engine setting new request for existing client +0ms
socket.io:socket socket connected - writing packet +0ms
socket.io:socket joining room OkhjEQyA3sz4x-rGAAAA +1ms
socket.io:client writing packet {"type":0,"nsp":"/"} +37ms
socket.io-parser encoding packet {"type":0,"nsp":"/"} +0ms
socket.io-parser encoded {"type":0,"nsp":"/"} as 0 +1ms
engine:socket sending packet "message" (0) +11ms
socket.io:socket joined room OkhjEQyA3sz4x-rGAAAA +2ms
engine intercepting request for path "/socket.io/" +1ms
engine handling "GET" http request "/socket.io/?EIO=3&transport=polling&t=1417092200067-2&sid=OkhjEQyA3sz4x-rGAAAA" +0ms
engine setting new request for existing client +1ms
engine:polling setting request +0ms
engine:socket flushing buffer to transport +1ms
engine:polling writing "�40" +1ms
engine:socket executing batch send callback +1ms
engine upgrading existing transport +66ms
engine:socket might upgrade socket transport from "polling" to "websocket" +0ms
engine:ws received "2probe" +23ms
engine:ws writing "3probe" +1ms
engine intercepting request for path "/socket.io/" +4ms
engine handling "GET" http request "/socket.io/?EIO=3&transport=polling&t=1417092200174-3&sid=OkhjEQyA3sz4x-rGAAAA" +0ms
engine setting new request for existing client +1ms
engine:polling setting request +0ms
engine:socket writing a noop packet to polling for fast upgrade +97ms
engine:polling writing "�6" +1ms
engine:ws received "40/notifications" +3ms
engine:ws closing +1ms
engine:socket client did not complete upgrade - closing transport +10s
Whoaaaaa.... Amazing... I just figured it out.... @nkzawa you made my day :-)
Since we do the 40/notifications
on the websocket instead of polling I have to switch this call with the call sending 5
.
5
will complete the websocket upgrade. Maybe it is a good to make this more clear in the protocol docs.
So to answer the initial question: How to connect to a namespace...
Simply send 40/namespacename
To do a successfull upgrade make sure the first message sent on the socket equals 5 to complete the upgrade.
Then you are free to send any next messages on the socket using the protocol definition as defined in
https://github.com/Automattic/socket.io-protocol
@marcofranssen Great :smile: Please share what you did if possible. It would be helpful.
I switched the 2 webwsocket calls we had in jmeter.
first send 5 on the websocket
then send 40/namespacename on the websocket
before we had it the other way arround. I figured out when I was examining the log I posted over here.
Also opened a ticket in the repo explaining the protocol since it could be more clarified over there. https://github.com/Automattic/socket.io-protocol/issues/12
I'm not entirely sure what you mean by Simply send 40/namespacename
Is this in the path? If it's the path does should it be /40/namespace
?
I'm just not entirely sure what you mean by sending 5
either. I realize this is what is necessary for the socket upgrade
. just not sure how you're sending it.
OK, after reverse engineering socket.io(v0.9.17), here are the steps to connect:
1: connect to socket in order to get an Socket ID:
sampler
: HTTP sampler
method
: get
path
: /socket.io/1/websocket
2: use a Regular Expression Extractor post processor
field
: Body
Reference Name
: socketId
Regular Expression
: (.+?):60:60:
template
: $1$
match num
: $1$
3: upgrade to socket using the socketId variable we just created with any params your server expects
sampler
: websocket sampler
path
: /socket.io/1/websocket/${socketId}
streaming connection
: true
4: connect to a namespace
(optional)
sampler
: websocket sampler
Request Data
: 1::/path/to/namespace
5: send payload to server
sampler
: websocket sampler
request data
: 5::/path/to/namespace:{"name":"tacos","args":["cheese", "salsa", "carne asada"]}
6: ping server to keep connection alive
sampler
: websocket sampler
request data
: 2::probe
If you are looping this multiple times, make sure to put the connect to namespace in a once only
logic controller, otherwise, it will cause your server to send responses multiple times.
Hi bjoshuanoah ,
I was using the steps same what you just mentioned. My http requets is going fine an di am able to retrieve the id. but the connection is not made. and i get the following error.
[Execution Flow]
[Variables]
[Problems]
Response headers:
SampleResult fields:
ContentType:
DataEncoding: UTF-8
Alls well that ends well. I figured out every puzzel and its working for me. created a test with the plugin and all errors are not gone. sending 3 requests and in between 1 ping lifesign request to keep the connection alive.
Hello @bjoshuanoah ,
I need to know about send payload to server.
I sent request data 2probe , it response 3probe
Now ,
I tried to sent event , but not response
Do you have example other than 5::/path/to/namespace:{"name":"tacos","args":["cheese", "salsa", "carne asada"]}
Thank you
Most helpful comment
OK, after reverse engineering socket.io(v0.9.17), here are the steps to connect:
1: connect to socket in order to get an Socket ID:
sampler
: HTTP samplermethod
: getpath
: /socket.io/1/websocket2: use a Regular Expression Extractor post processor
field
: BodyReference Name
: socketIdRegular Expression
: (.+?):60:60:template
: $1$match num
: $1$3: upgrade to socket using the socketId variable we just created with any params your server expects
sampler
: websocket samplerpath
: /socket.io/1/websocket/${socketId}streaming connection
: true4: connect to a
namespace
(optional)sampler
: websocket samplerRequest Data
: 1::/path/to/namespace5: send payload to server
sampler
: websocket samplerrequest data
: 5::/path/to/namespace:{"name":"tacos","args":["cheese", "salsa", "carne asada"]}6: ping server to keep connection alive
sampler
: websocket samplerrequest data
: 2::probeIf you are looping this multiple times, make sure to put the connect to namespace in a
once only
logic controller, otherwise, it will cause your server to send responses multiple times.