fastify/fastify-websocket

How to handle ``on("message"`` for both binary and json messages

marwie opened this issue · 8 comments

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the issue has not already been raised

Issue

Hello,

I'm having an issue where I'm sending both json strings as well as flatbuffer/uin8 arrays to my websocket connection.

In on("message" I'm calling toString but that doesnt work for the Uint8Arrays being sent. How should I handle both message types / is there a flag to enable?

Current version: 4.2.2

error when calling toString on a message that is actually an Uint8Array

SyntaxError: Unexpected token  in JSON at position 0
    at JSON.parse (<anonymous>)
    at C:\git\needle-tiny-networking-package\packages\websocket\src\networking.js:52:28
    at WebSocket.<anonymous> (C:\git\needle-tiny-networking-package\packages\websocket\src\proxy.js:31:17)
    at WebSocket.emit (events.js:388:22)
    at Receiver.receiverOnMessage (C:\git\needle-tiny-networking-package\packages\websocket\node_modules\fastify-websocket\node_modules\ws\lib\websocket.js:1137:20)
    at Receiver.emit (events.js:376:20)
    at Receiver.dataMessage (C:\git\needle-tiny-networking-package\packages\websocket\node_modules\fastify-websocket\node_modules\ws\lib\receiver.js:513:14)
    at Receiver.getData (C:\git\needle-tiny-networking-package\packages\websocket\node_modules\fastify-websocket\node_modules\ws\lib\receiver.js:446:17)
    at Receiver.startLoop (C:\git\needle-tiny-networking-package\packages\websocket\node_modules\fastify-websocket\node_modules\ws\lib\receiver.js:148:22)
    at Receiver._write (C:\git\needle-tiny-networking-package\packages\websocket\node_modules\fastify-websocket\node_modules\ws\lib\receiver.js:83:10)
    at writeOrBuffer (internal/streams/writable.js:358:12)
    at Receiver.Writable.write (internal/streams/writable.js:303:10)
    at Socket.socketOnData (C:\git\needle-tiny-networking-package\packages\websocket\node_modules\fastify-websocket\node_modules\ws\lib\websocket.js:1231:35)
    at Socket.emit (events.js:376:20)
    at addChunk (internal/streams/readable.js:309:12)
    at readableAddChunk (internal/streams/readable.js:284:9)

Ok found a workround - instead of subscribing to connection.socket.on("message i'm now setting a callback receiver to connection.socket.onmessage and pass evt.data through:

this.ws.socket.onmessage = (evt) => {
                callback(evt.data);
            };

That being said: info on if there is a better/official/recommended way on doing this correctly would be appreciated still :)

I think no matter what you're going to need to do some detection in your application to figure out which message type it is -- fastify-websocket can only just hand you the data that was sent from the client. With your workaround, don't you still need to do a check somehow to figure out which type it is, and then only try to JSON parse if it is a string?

Yes - the check somehow is where I didnt know how.

When using the evt.data like above it is either a string or a buffer so I can just pass it on to be handled in my core (where a buffer is always treated as a flatbuffer message right now) - but when I was using connection.on("message", data => I didnt know how to check wether the data buffer was a actually json string so I can call toString safely or a flatbuffer byte array.

fastify-websocket just uses the underlying ws library's Connection and WebSocket objects -- we don't do anything to the message data as it comes through. See https://github.com/fastify/fastify-websocket/blob/master/index.js#L61 for where we create the objects you're working with. I am pretty sure that connection.socket.onmessage and connection.socket.on("message will actually get passed the exact same data, and that regardless of which one you use you'll have to do an instanceof Buffer check or something like that to figure out which type of data you're dealing with. Regardless, I think fastify-websocket has no opinion on what data you should pump through the websocket and doesn't touch it, so I don't think this is a flag or something we'd add to the library if that makes sense. Not even really sure what flag you might expect. Gonna close this but if you think the library is messing with your data feel free to reopen.

What I mean is this:

image

 this.ws.socket.on("message", (data) => {
     console.log("ws.socket.on", data);
 });
this.ws.socket.onmessage = evt => {
     console.log("ws.socket.onmessage", evt.data);
};

As you can see in one event I'm getting a json string and in the other event im getting a buffer object (for the same message event)

socket.onmessage is made for Web API compatibility, so it returns the Event like object.
https://github.com/websockets/ws/blob/master/lib/event-target.js#L193-L200

socket.on('message') is the actual Node EventEmitter implementation. It receive the raw data (e.g. Buffer, ArrayBuffer, etc).

The ws document already give you the hint on how to handle your case.

socket.on('message', function(data, isBinary) {
  // it is sent with binary
  if(isBinary) {
    const buf = Buffer.from(data)
  } else {
    const json = JSON.parse(Buffer.from(data).toString())
  }
})

@climba03003 thanks for explaining! that's probably what I was missing :)