ws: websocket server unable to handle requests immediately after server startup.

  • I’ve searched for any related issues and avoided creating a duplicate issue.

  • I’ve checked to make sure this is the module with the issue.

Description

Server completes handshake with client websocket before being able to handle messages from client immediately after server startup.

Reproducible in:

version: 5.2.0

Node.js version(s):8.9.4

OS version(s): OSX 10.12.6 (MacOS High Sierra)

Steps to reproduce:

  1. mount express-ws on server
  2. have client continually try to connect to websocket server while server is starting.

Expected result:

Messages sent by client after handshake are handled by the server immediately after server startup.

Actual result:

Messages sent by client after handshake are never handled by the server (websocket is opened appropriately). The function connectionHandler defined in the app.ws(path, connectionHandler) is never called with the messages.

If client delays the sending of these messages by a small amount of time (around 50 milliseconds on my computer) the server handles the messages appropriately.

Attachments:


Server

server sample code

//import ws
const enableWs = require('express-ws')

//lots of extraneous code

//attach ws to server
enableWs(app, server)
app.ws('/ws', webSocketConnectionHandler) //see below

//more extraneous code

//tell the server to listen on port
server.listen(port, ()=> {
  console.log(`server listening on port ${port}`)
});

webSocketConnectionHandler

function wsConnectionHandler(ws, req) {

  //prevent websocket closing from crashing the server
  ws.on('close', () => {})

  //prevent websocket error from crashing the server
  ws.on('error', (error) => {})

  ws.on('message', message => {
    console.log(message) //verifying the message was received
    webSocketMessageHandler(message, getDefaultMessageHandlers(ws, req), ws) //handle message
  })
}

Client (not working, but should) - Java

//simplified a lot
class webSocket extends WebSocketClient {

  public webSocket(String url) {
    super(new URI(url), new Draft_6455(), getHeaders(), 0);
  }

  private boolean alreadyConnecting = false;

  //runs once websocket successfully opens (handshake successful)
  public void onOpen(ServerHandshake serverHandshake) {
    this.send("Hello World"); //this message is not handled by the server but should

    Timer timer = new Timer(true);
    timer.schedule(new TimerTask() { // delays sending of message over websocket for 50 milliseconds
        @Override
        public void run() {
            this.send("Hola Teirra"); //this message is handled by the server
            timer.cancel();
        }
    }, 50);
  }

  //once websocket closes or fails to initially connect, keep trying to reconnect until successful
  public void onClose() {
    if (alreadyConnecting) {
      return;
    }

    this.alreadyConnecting = true;

    Timer timer = new Timer(true);
    timer.scheduleAtFixedRate(new TimerTask() { //try to reconnect every second

      @Override
      public void run() {
          try {
              if (this.reconnectBlocking()) {
                  this.alreadyConnecting = false;
                  timer.cancel();
              }
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
        }
    }, 0, 1000);

  }
}
What happens:

The "Hello world" message is sent by the client after handshake completes and websocket successfully opens, however the "Hello world" message is never handled by the server. The "Hola Teirra" message is sent 50 milliseconds after the websocket successfully opens and the message is successfully handled by the server. I have verified that the client successfully sends both messages.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 1
  • Comments: 16 (8 by maintainers)

Most upvoted comments

The problem I noticed is that it seems the messages from the client are being sent before the message handler is added to the websocket in the server.

Does this mean something like this?

wss.on('connection', function (ws) {
  process.nextTick(function () {
    ws.on('message', function () {});
  });
});

If so then yes, you may miss messages if you don’t add a listener for the 'message' event immediately after (on the same tick) receiving the 'connection' event.

@Workruft the way the nodejs event loop works ensures that this won’t happen unless you do something asynchronous before making your ws.on(... calls.

So as long as you aren’t making those calls in a callback or after an await statement, you will be fine 😃

The connection is established asynchronously, which means it won’t be established until all synchronous code that’s already been queued up is completed. The ws.on calls are synchronous, so your example is fine, although I can completely understand why you might be confused.

So basically, .on() calls are only setting up state, nothing actually initiates until the script ends. Makes sense, gotcha, thanks for the explanation.

I have a highly related question to this one, so I’ll ask it here. In your examples for WS, I see this kind of code:

const WebSocket = require('ws');

const ws = new WebSocket('ws://www.host.com/path');

ws.on('open', function open() {
  ws.send('something');
});

ws.on('message', function incoming(data) {
  console.log(data);
});

However, this really bothers me, even if it’s sort of trivial: Couldn’t a connection technically be established before any of the event handler functions are registered?

I’m not really sure how the async nature of Node/JS works here, but it does seem that you can immediately chain .on() calls after creating the new WebSocket. Why aren’t the code examples doing this, and why not just have a .startListening() call instead?