http: add req.signal to IncomingMessage by akshatsrivastava11 · Pull Request #62541 · nodejs/node
Fixes: #62481
Summary
Adds a lazy signal getter to IncomingMessage that returns an
AbortSignal which aborts when the request is closed or aborted.
This mirrors the Web Fetch API's Request.signal and Deno's
request.signal.
Motivation
Currently, cancelling async work (DB queries, fetch calls) when a
client disconnects requires manual boilerplate in every request handler:
// before server.on('request', async (req, res) => { const ac = new AbortController(); req.on('close', () => ac.abort()); const data = await fetch('https://slow-api.com', { signal: ac.signal }); res.end(JSON.stringify(data)); });
With this change:
// after server.on('request', async (req, res) => { const data = await fetch('https://slow-api.com', { signal: req.signal }); res.end(JSON.stringify(data)); });
Design
Lazy initialization — AbortController is only created when
req.signal is first accessed. Zero overhead for handlers that
do not use it.
Single listener — listens on this.once('close') and
this.once('abort') rather than socket.once('close') directly,
since the request stream's close event fires in all teardown paths:
- socket close propagates through
_destroy()to reqclose - socketless
req.destroy()fires reqclosedirectly - client abort fires req
abortdirectly
Race condition — if .signal is accessed after req.destroy()
has already fired, this.destroyed is checked and the signal is
aborted immediately rather than registering a listener that would
never fire.
configurable: true — allows frameworks (Express, Fastify, Koa)
to override the property on their own subclasses.
Changes
lib/_http_incoming.js— addssignalgetter toIncomingMessagetest/parallel/test-http-request-signal.js— adds tests
Tests
req.signalis anAbortSignaland not aborted initially- signal aborts when
'abort'event fires - signal aborts when
'close'event fires (client disconnect) - signal is pre-aborted if accessed after
req.destroy()(race condition) - multiple accesses return the same signal instance (lazy init)
Fixes: #62481