Proxying WebSockets with Express and node-http-proxy

Search for a command to run...

No comments yet. Be the first to comment.
Hello. I'm writing a Lambda "tick" function to simulate the universe for my game. It's a different architecture than my previous idea of maintaining a 24/7 server, which simulates a busy real-time game loop. I wanted to give it a try. In this post, I...

A small tool that lets you put a "marker" on a song and have a button to recall that position with a click.

Hey there! I had a blast participating in the Appwrite hackathon and I want to share my journey with you. So, buckle up and get ready for some timer magic! Team Details Okay, before we dive in, let's get to know the team (well, it's just me actually)...

Hello. I've decided to try something for the game I'm developing. The game is a long-running online solo/coop space strategy game with resource balancing and 4x elements, but that's not the subject of this post. I decided to separate game logic code ...

Hello. For this one, I ended up using Hammer.js. I did try Interact.js which is definitely newer and more updated, but that just... well... didn't work nicely. Judging by the examples on both libraries' pages you can see if the core functionality of ...

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.
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.