Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Socket.io "Invalid frame header" error with independent websocket server

Is there a way to have a separate websocket server work alongside socket.io on a different path?

let http = require('http');
let express = require('express');
let socketio = require('socket.io');
let websocket = require('ws');

let httpServer = http.createServer();

let expressApp = express();
httpServer.on('request', expressApp);

let socketioServer = socketio(httpServer, { path: '/aaaaa/socket.io/' });
socketioServer.of('/').on('connect', () => {});

let websocketServer = new websocket.Server({ server: httpServer, path: '/aaaaa/graphql' });

httpServer.listen(2233, () => console.log('started'));

The behavior I'm seeing is that, when a separate websocket server is created, socket.io still functions properly, but will not uprade connections to a websocket and fails with the error (from chrome):

WebSocket connection to 'ws://localhost:2233/aaaaa/socket.io/?EIO=3&transport=websocket&sid=fx4pOT0cegz65JMCAAAB' failed: Invalid frame header

To be clear, if the websocket server line is omitted, socket.io works properly.

My specific use-case is that a websocket server is created by the apollo-server-express package when subscriptions are enabled. Is there a way to have socket.io configured in a more friendly way? Or, I believe I can supply a websocket server for apollo to use instead of creating one... how would I create that?

Package versions for reproduction:

node       8.11.1
express    4.16.4
socket.io  2.1.1
ws         6.1.0
like image 703
Trevor Wilson Avatar asked Nov 06 '18 22:11

Trevor Wilson


Video Answer


1 Answers

In case this helps anyone else, here's my derived solution:

let [socketioUpgradeListener, apolloUpgradeListener] = httpServer.listeners('upgrade').slice(0);
httpServer.removeAllListeners('upgrade');
httpServer.on('upgrade', (req, socket, head) => {
  const pathname = url.parse(req.url).pathname;
  if (pathname == '/aaaaa/socket.io/')
    socketioUpgradeListener(req, socket, head);
  else if (pathname == '/aaaaa/graphql')
    apolloUpgradeListener(req, socket, head);
  else
    socket.destroy();
});

Was a bit annoying because both libraries had already fully initialized their websocket servers, with plenty of event listeners, before I could mess with them. However, I could pick out the 'upgrade' listeners and delegate them manually. Of course this isn't perfect since it is sensitive to initialization order and new listeners, but it is adequate for my use-case.

If there's any glaring flaws with this solution or any other nuances with websocket server delegation, please let me know.

like image 128
Trevor Wilson Avatar answered Sep 21 '22 13:09

Trevor Wilson