Proxying WebSockets with Express and node-http-proxy

Hello. It took me more than 2 minutes to find the relevant info online so I decided to put it here for others for a quick reference. I am using Express and node-http-proxy to make a gateway that will also authenticate players for my game and then proxy their WebSocket traffic to the dedicated game server.

tl;dr: Express returns the Node HTTP server instance on which you can listen to an upgrade event and use the proxy's ws method.

Code-thing

I'll just share the whole thing:

import process from "process";
import express from "express";
import proxy from "http-proxy";
import cors from "cors";
import { logger } from "./logger.js";

import {
  ALLOWED_ORIGIN,
  IS_DEVELOPMENT,
  PORT,
  UPSTREAM_GAME_SERVER,
} from "./config.js";

const app = express();
app.disable("x-powered-by");

app.use(cors({ origin: ALLOWED_ORIGIN }));

app.get("/", (req, res) => {
  res.send({ hi: "hello" });
});

const gameProxy = proxy.createProxyServer({});

app.all("/game/*", (req, res) => {
  // This is for proxying regular traffic. The option { ws: true } did not work :shrug:
  gameProxy.web(req, res, { target: UPSTREAM_GAME_SERVER });
});

// This is probably a bad idea, I should check for IS_PRODUCTION for binding to 0.0.0.0
const HOST = IS_DEVELOPMENT ? "127.0.0.1" : "0.0.0.0";

const http = app.listen(PORT, HOST);

http.on("upgrade", (req, socket) => {
  // Papam! Proxy the WebSockets.
  gameProxy.ws(req, socket, { target: UPSTREAM_GAME_SERVER });
});

function shutdown(signal: NodeJS.Signals) {
  logger.info(`Caught signal ${signal}`);
  http.close(() => {
    logger.info("The HTTP server was closed");
  });
}

process.on("SIGINT", shutdown);
process.on("SIGTERM", shutdown);

logger.info(`Gateway proxy listening on port ${PORT}, host: ${HOST}`);
logger.info(`Proxying /game to ${UPSTREAM_GAME_SERVER}`);
logger.info(`Allowed origin: ${ALLOWED_ORIGIN}`);

Easy, right?

Some words about the game I'm developing: It's an online persistent-universe single player/cooperative gameplay 4X/resource-management space game targeting an audience that can't afford playing games for long sessions.

I want to separate the game server's logic from the authentication logic and all that, so creating a gateway server that authenticates players and proxies their traffic to the game server under a decorated route seems the way to go.