Closed
Description
Is your feature request related to a problem? Please describe.
A common Express middleware needs an interface like (req, res, next)
. A common example would be helmet()
which adds securtiy headers in responses.
But the new headers introduced in https://socket.io/blog/socket-io-4-1-0/#add-a-way-to-customize-the-response-headers does not get the res
in the callback:
io.engine.on("initial_headers", (headers, req) => {
headers["test"] = "123";
headers["set-cookie"] = "mycookie=456";
});
io.engine.on("headers", (headers, req) => {
headers["test"] = "789";
});
This means we cannot use common Express middleware to add headers properly.
Describe the solution you'd like
I would have wanted this:
io.engine.on("initial_headers", (headers, req, res) => {
helmet()(req, res);
});
io.engine.on("headers", (headers, req) => {
helmet()(req, res);
});
Describe alternatives you've considered
Our current solution is this which is a hack we want to replace with the new solution of io.engine.on("initial_headers"
and io.engine.on("headers"
/**
* Attach the socket.io server to an HTTP(S) server.
*
* @param {http.Server|https.Server} server - Server to attach to
*/
function attachSocketIoToServer(server) {
const socketIoPath = `socket.io`;
io.attach(server, {
serveClient: false,
path: socketIoPath,
});
// Workarounds to add secure headers to socket.io responses. Different
// workarounds are needed for polling and websocket transports.
//
// 1. Polling transport: When attaching to a server, socket.io removes all
// other listeners of the 'request' event and adds itself. If the path
// matches the socket.io path it handles the request, otherwise it calls
// the other listeners itself.
//
// By doing the same thing, we can use Helmet to add the headers to the
// response before socket.io gets involved.
if (server.listeners('request').length !== 1) {
throw new Error('Unexpected number of "request" listeners on server');
}
const socketIoListener = server.listeners('request')[0];
server.removeAllListeners('request');
server.on('request', (req, res) => {
// @ts-ignore -- Fault in Node.js types, see https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/54920
if (req.url.startsWith(socketIoPath)) {
helmet(getHelmetOptions())(req, res, () => {
socketIoListener(req, res);
});
} else {
socketIoListener(req, res);
}
});
// 2. Websocket transport: The underlying 'ws' package emits a 'headers' event
// when the headers are ready, which is meant to be used for inspecting or
// modifying the headers before they are sent.
//
// We hook into this after the server has begun listening, since engine.io
// recreates the ws object at that point.
server.on('listening', () => {
io.eio.ws.on('headers', (headers) => {
headers.push(
'something something'
);
});
});
}