Skip to content

Commit e265c6d

Browse files
http: add req.signal to IncomingMessage
1 parent 86282b5 commit e265c6d

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

lib/_http_incoming.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ const {
2929

3030
const { Readable, finished } = require('stream');
3131

32+
const { AbortController } = require('internal/abort_controller');
33+
3234
const kHeaders = Symbol('kHeaders');
3335
const kHeadersDistinct = Symbol('kHeadersDistinct');
3436
const kHeadersCount = Symbol('kHeadersCount');
3537
const kTrailers = Symbol('kTrailers');
3638
const kTrailersDistinct = Symbol('kTrailersDistinct');
3739
const kTrailersCount = Symbol('kTrailersCount');
40+
const kAbortController = Symbol('kAbortController');
3841

3942
function readStart(socket) {
4043
if (socket && !socket._paused && socket.readable)
@@ -90,6 +93,7 @@ function IncomingMessage(socket) {
9093
// Flag for when we decide that this message cannot possibly be
9194
// read by the user, so there's no point continuing to handle it.
9295
this._dumped = false;
96+
this[kAbortController] = undefined;
9397
}
9498
ObjectSetPrototypeOf(IncomingMessage.prototype, Readable.prototype);
9599
ObjectSetPrototypeOf(IncomingMessage, Readable);
@@ -184,6 +188,28 @@ ObjectDefineProperty(IncomingMessage.prototype, 'trailersDistinct', {
184188
},
185189
});
186190

191+
ObjectDefineProperty(IncomingMessage.prototype, 'signal', {
192+
__proto__: null,
193+
configurable: true,
194+
get: function() {
195+
if (this[kAbortController] === undefined) {
196+
const ac = new AbortController();
197+
this[kAbortController] = ac;
198+
if (this.destroyed) {
199+
ac.abort();
200+
} else {
201+
this.once('close', function() {
202+
ac.abort();
203+
});
204+
this.once('abort', function() {
205+
ac.abort();
206+
});
207+
}
208+
}
209+
return this[kAbortController].signal;
210+
},
211+
});
212+
187213
IncomingMessage.prototype.setTimeout = function setTimeout(msecs, callback) {
188214
if (callback)
189215
this.on('timeout', callback);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const http = require('http');
6+
7+
// Test 1: req.signal is an AbortSignal and aborts on 'close'
8+
{
9+
const server = http.createServer(common.mustCall((req, res) => {
10+
assert.ok(req.signal instanceof AbortSignal);
11+
assert.strictEqual(req.signal.aborted, false);
12+
req.signal.onabort = common.mustCall(() => {
13+
assert.strictEqual(req.signal.aborted, true);
14+
});
15+
res.destroy();
16+
}));
17+
server.listen(0, common.mustCall(() => {
18+
http.get({ port: server.address().port }, () => {}).on('error', () => {
19+
server.close();
20+
});
21+
}));
22+
}
23+
24+
// Test 2: req.signal is aborted if accessed after destroy
25+
{
26+
const req = new http.IncomingMessage(null);
27+
req.destroy();
28+
assert.strictEqual(req.signal.aborted, true);
29+
}
30+
31+
// Test 3: Multiple accesses return the same signal
32+
{
33+
const req = new http.IncomingMessage(null);
34+
assert.strictEqual(req.signal, req.signal);
35+
}
36+
37+
// Test 4: req.signal aborts when 'abort' event is emitted on req
38+
{
39+
const req = new http.IncomingMessage(null);
40+
const signal = req.signal;
41+
assert.strictEqual(signal.aborted, false);
42+
req.emit('abort');
43+
assert.strictEqual(signal.aborted, true);
44+
}

0 commit comments

Comments
 (0)