diff --git a/packages/engine.io/lib/server.ts b/packages/engine.io/lib/server.ts index c4f2c30d4..293c74197 100644 --- a/packages/engine.io/lib/server.ts +++ b/packages/engine.io/lib/server.ts @@ -669,13 +669,17 @@ export abstract class BaseServer extends EventEmitter { * * @see https://nodejs.org/api/http.html#class-httpserverresponse */ -class WebSocketResponse { +class WebSocketResponse extends EventEmitter { constructor( readonly req, readonly socket: Duplex, ) { + super(); // temporarily store the response headers on the req object (see the "headers" event) req[kResponseHeaders] = {}; + // some middlewares (like pino-http) rely on the "close" event to know when the response is done, which the + // underlying socket does not expose by default + socket.once("close", () => this.emit("close")); } public setHeader(name: string, value: any) { @@ -695,6 +699,7 @@ class WebSocketResponse { public writeHead() {} public end() { + this.emit("finish"); // we could return a proper error code, but the WebSocket client will emit an "error" event anyway. this.socket.destroy(); } diff --git a/packages/engine.io/test/middlewares.js b/packages/engine.io/test/middlewares.js index 60e1cc6b2..6e9771199 100644 --- a/packages/engine.io/test/middlewares.js +++ b/packages/engine.io/test/middlewares.js @@ -56,6 +56,29 @@ describe("middlewares", () => { }); }); + it("should expose EventEmitter methods on the response object during upgrade (regression for pino-http)", (done) => { + const engine = listen((port) => { + engine.use((req, res, next) => { + expect(res.on).to.be.a("function"); + res.on("close", () => { + if (engine.httpServer) { + engine.httpServer.close(); + } + done(); + }); + next(); + }); + + const socket = new WebSocket( + `ws://localhost:${port}/engine.io/?EIO=4&transport=websocket`, + ); + + socket.on("open", () => { + socket.close(); + }); + }); + }); + it("should apply all middlewares in order", (done) => { const engine = listen((port) => { let count = 0;