Skip to content

fix(eio): do not access the uWS request after an async middleware - #5579

Open
b84330808 wants to merge 1 commit into
socketio:mainfrom
b84330808:fix/uws-async-middleware
Open

b84330808 wants to merge 1 commit into
socketio:mainfrom
b84330808:fix/uws-async-middleware

Conversation

@b84330808

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

With the uWebSockets.js server (uServer), the HttpRequest object must not be used once the route handler has returned. When a middleware or the allowRequest() option completes asynchronously, the request is still accessed afterwards, and the connection fails with:

uWS.HttpRequest must not be accessed after await or route handler return

  • WebSocket upgrade: req.getHeader("sec-websocket-*") is called in the verify callback (and the response is written outside of cork(), which logs "uWS.HttpResponse writes must be made from within a corked callback")
  • HTTP long-polling: req.getMethod() is called in Polling.onRequest()

This happens for example with express-session when the client already has a session cookie (the store lookup is asynchronous), which matches the reconnection scenario in #5330.

New behavior

  • the Sec-WebSocket-Key/Protocol/Extensions headers are read synchronously, and the status, headers and upgrade() call are made within res.cork()
  • the polling transport uses req.method (already set in prepare()) instead of req.getMethod()

Two tests were added in test/middlewares.js with a middleware that calls next() in a setTimeout(). They fail with EIO_WS_ENGINE=uws before this change and pass after it (they pass with the default engine both before and after).

Other information (e.g. related issues)

Related: #5330 (the reporter describes the same two changes in handleUpgrade).

Not addressed here: a long-polling POST request still loses its body when a middleware is asynchronous, since res.onData() is only attached after the middlewares have run. Buffering the body earlier looked like a bigger change, so I left it out.

I used Claude Code to write this change.

With the uWebSockets.js server, the HttpRequest object must not be used
once the route handler has returned. When a middleware (for example
express-session with an existing session) or the allowRequest() option
completes asynchronously, the handshake failed with:

> uWS.HttpRequest must not be accessed after await or route handler return

- for WebSocket upgrades, the Sec-WebSocket-* headers are now read
  synchronously, and the response is written within a cork() callback
- for HTTP long-polling, the method is now read from the prepared
  request.method attribute instead of req.getMethod()

Related: socketio#5330

Co-Authored-By: Claude Opus 5.5 <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.

1 participant