Skip to content

fix(eio): expose EventEmitter interface on WebSocket upgrade response#5526

Open
nabsei wants to merge 1 commit into
socketio:mainfrom
nabsei:fix/eio-websocket-response-eventemitter
Open

fix(eio): expose EventEmitter interface on WebSocket upgrade response#5526
nabsei wants to merge 1 commit into
socketio:mainfrom
nabsei:fix/eio-websocket-response-eventemitter

Conversation

@nabsei

@nabsei nabsei commented Jul 14, 2026

Copy link
Copy Markdown

The kind of change this PR does introduce

  • a bug fix
  • a new feature
  • an update to the documentation
  • a code change that improves performance
  • other

Current behavior

Fixes #5072. During a WebSocket upgrade, engine.io applies middlewares against a WebSocketResponse object that only implements a small subset of http.ServerResponse (setHeader/getHeader/removeHeader/write/writeHead/end). It does not implement EventEmitter, so any middleware that calls res.on(...) — for example pino-http, which listens for "close" to know when the request finished — crashes with TypeError: res.on is not a function.

New behavior

WebSocketResponse now extends EventEmitter and emits "close" when the underlying socket closes, and "finish" when end() is called, mirroring the events a real http.ServerResponse emits. Added a regression test in test/middlewares.js that registers a res.on("close", ...) listener during a WebSocket upgrade and asserts it fires.

Other information (e.g. related issues)

Parts of this fix (root-cause analysis and implementation) were produced with the help of Claude Code — credited via the Co-authored-by trailer on the commit. I reviewed and understood the change before opening this PR.

Verification performed:

  • Added unit test passes, along with the full existing middlewares.js/server.js/engine.io.js suite (175 passing, 2 pre-existing unrelated pending) — no regressions.
  • Reproduced the exact bug report with the real pino-http package against an unpatched build (crashes with res.on is not a function), then confirmed the same script completes cleanly with this fix applied.
  • prettier --check passes on the changed files.

WebSocketResponse (the fake response object passed to middlewares
during a WebSocket upgrade) did not implement EventEmitter, so any
middleware calling res.on(...) — e.g. pino-http listening for "close"
to know when the request finished — crashed with "res.on is not a
function".

Make WebSocketResponse extend EventEmitter and emit "close" when the
underlying socket closes, and "finish" when end() is called, mirroring
the events a real http.ServerResponse emits.

Fixes socketio#5072

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Log with pino-http middleware raises TypeError: res.on is not a function

1 participant