Skip to content
This repository has been archived by the owner on Apr 14, 2023. It is now read-only.

Wrap ws in order to support multiple websocket listeners on('upgrade') #751

Closed
andsens opened this issue Apr 5, 2020 · 2 comments
Closed

Comments

@andsens
Copy link

andsens commented Apr 5, 2020

In order to support more than one websocket on the same server, I currently need to wrap httpServer so that ws doesn't immediately grab the socket and then passes it to SubscriptionServer, which in turn immediately aborts the handshake if it isn't a graphql subscription.
Currently my code to handle that issue, looks like this:

const serverProxy = new EventEmitter();
server.on('listening', serverProxy.emit.bind(serverProxy));
server.on('error', serverProxy.emit.bind(serverProxy));
server.on('upgrade', (req: IncomingMessage, ...rest: any[]) => {
  if (req.url === paths.graphqlSubscriptions) {
    serverProxy.emit('upgrade', req, ...rest);
  }
});
apollo.installSubscriptionHandlers(serverProxy as Server);

This is rather janky. It would be nice if this handling could happen in the SubscriptionServer instead.

This means that this section:

// Init and connect WebSocket server to http
this.wsServer = new WebSocket.Server(socketOptionsOrServer || {});

... would look more like this (grabbed from the examples of ws):

// Init and connect WebSocket server to http
if(socketOptionsOrServer) {
    this.wsServer = new WebSocket.Server({ noServer: true });
    server.on('upgrade', function upgrade(request, socket, head) {
      const pathname = url.parse(request.url).pathname;
      if (pathname === this.pathname) {
        this.wsServer.handleUpgrade(request, socket, head, (ws) => {
          this.wsServer.emit('connection', ws, request);
        });
      }
    });
} else {
    this.wsServer = new WebSocket.Server({});
}

The only thing missing is the pathname which doesn't seem to be passed to SubscriptionServer right now.

For more see websockets/ws#1726.

@a-tokyo
Copy link

a-tokyo commented Aug 15, 2020

Any news on this? 🚀

@a-tokyo
Copy link

a-tokyo commented Aug 15, 2020

Note for future developers not using apollo server:

This function takes in your normal http server and wraps it with @andsens solution to support more than one websocket on the same server.

Code example using normal http server:

/**
 * Adaptor between our httpServer and gql Subscription server
 * 
 * This gets socket.io to work alongside gqls
 * In order to support more than one websocket on the same server, we need to wrap httpServer so that ws doesn't immediately grab the socket and then passes it to SubscriptionServer, which in turn immediately aborts the handshake if it isn't a graphql subscription.
 * 
 * more info: https://github.com/apollographql/subscriptions-transport-ws/issues/751
 */
const adaptServerToGqls = (server: Server, gqlsEndpoint: string = '/graphql'): Server => {
  const gqlsServerProxy = new EventEmitter();
  server.on('listening', gqlsServerProxy.emit.bind(gqlsServerProxy));
  server.on('error', gqlsServerProxy.emit.bind(gqlsServerProxy));
  server.on('upgrade', (req: IncomingMessage, ...rest: any[]) => {
    if (req.url === gqlsEndpoint) {
      gqlsServerProxy.emit('upgrade', req, ...rest);
    }
  });
  return gqlsServerProxy;
};

Then when you pass the server option to SubscriptionServer simply wrap it with the above function. eg:

new SubscriptionServer(
  {
    execute,
    subscribe,
    schema,
  },
  {
    server: adaptServerToGqls(httpServer, GRAPHQL_ENDPOINT),
    path: GRAPHQL_ENDPOINT,
  },
);

Code example using apollo server: (quoted from issue description)

const serverProxy = new EventEmitter();
server.on('listening', serverProxy.emit.bind(serverProxy));
server.on('error', serverProxy.emit.bind(serverProxy));
server.on('upgrade', (req: IncomingMessage, ...rest: any[]) => {
if (req.url === paths.graphqlSubscriptions) {
serverProxy.emit('upgrade', req, ...rest);
}
});
apollo.installSubscriptionHandlers(serverProxy as Server);

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants