Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8390f8a

Browse filesBrowse files
benjamingrcodebytere
authored andcommitted
http: add support for abortsignal to http.request
PR-URL: #36048 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Ricky Zhou <0x19951125@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 82f1cde commit 8390f8a
Copy full SHA for 8390f8a

File tree

Expand file treeCollapse file tree

5 files changed

+59
-5
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

5 files changed

+59
-5
lines changed
Open diff view settings
Collapse file

‎doc/api/http.md‎

Copy file name to clipboardExpand all lines: doc/api/http.md
+9Lines changed: 9 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -2336,6 +2336,9 @@ This can be overridden for servers and client requests by passing the
23362336
<!-- YAML
23372337
added: v0.3.6
23382338
changes:
2339+
- version: REPLACEME
2340+
pr-url: https://github.com/nodejs/node/pull/36048
2341+
description: It is possible to abort a request with an AbortSignal.
23392342
- version:
23402343
- v13.8.0
23412344
- v12.15.0
@@ -2403,6 +2406,8 @@ changes:
24032406
or `port` is specified, those specify a TCP Socket).
24042407
* `timeout` {number}: A number specifying the socket timeout in milliseconds.
24052408
This will set the timeout before the socket is connected.
2409+
* `signal` {AbortSignal}: An AbortSignal that may be used to abort an ongoing
2410+
request.
24062411
* `callback` {Function}
24072412
* Returns: {http.ClientRequest}
24082413

@@ -2596,6 +2601,10 @@ events will be emitted in the following order:
25962601
Setting the `timeout` option or using the `setTimeout()` function will
25972602
not abort the request or do anything besides add a `'timeout'` event.
25982603

2604+
Passing an `AbortSignal` and then calling `abort` on the corresponding
2605+
`AbortController` will behave the same way as calling `.destroy()` on the
2606+
request itself.
2607+
25992608
## `http.validateHeaderName(name)`
26002609
<!-- YAML
26012610
added: v14.3.0
Collapse file

‎lib/.eslintrc.yaml‎

Copy file name to clipboardExpand all lines: lib/.eslintrc.yaml
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ rules:
1919
- selector: "ThrowStatement > CallExpression[callee.name=/Error$/]"
2020
message: "Use new keyword when throwing an Error."
2121
# Config specific to lib
22-
- selector: "NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError)$/])"
22+
- selector: "NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError)$/])"
2323
message: "Use an error exported by the internal/errors module."
2424
- selector: "CallExpression[callee.object.name='Error'][callee.property.name='captureStackTrace']"
2525
message: "Please use `require('internal/errors').hideStackFrames()` instead."
Collapse file

‎lib/_http_client.js‎

Copy file name to clipboardExpand all lines: lib/_http_client.js
+14-2Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,18 @@ const { Buffer } = require('buffer');
5151
const { defaultTriggerAsyncIdScope } = require('internal/async_hooks');
5252
const { URL, urlToOptions, searchParamsSymbol } = require('internal/url');
5353
const { kOutHeaders, kNeedDrain } = require('internal/http');
54-
const { connResetException, codes } = require('internal/errors');
54+
const { AbortError, connResetException, codes } = require('internal/errors');
5555
const {
5656
ERR_HTTP_HEADERS_SENT,
5757
ERR_INVALID_ARG_TYPE,
5858
ERR_INVALID_HTTP_TOKEN,
5959
ERR_INVALID_PROTOCOL,
6060
ERR_UNESCAPED_CHARACTERS
6161
} = codes;
62-
const { validateInteger } = require('internal/validators');
62+
const {
63+
validateInteger,
64+
validateAbortSignal,
65+
} = require('internal/validators');
6366
const { getTimerDuration } = require('internal/timers');
6467
const {
6568
DTRACE_HTTP_CLIENT_REQUEST,
@@ -169,6 +172,15 @@ function ClientRequest(input, options, cb) {
169172
if (options.timeout !== undefined)
170173
this.timeout = getTimerDuration(options.timeout, 'timeout');
171174

175+
const signal = options.signal;
176+
if (signal) {
177+
validateAbortSignal(signal, 'options.signal');
178+
const listener = (e) => this.destroy(new AbortError());
179+
signal.addEventListener('abort', listener);
180+
this.once('close', () => {
181+
signal.removeEventListener('abort', listener);
182+
});
183+
}
172184
let method = options.method;
173185
const methodIsString = (typeof method === 'string');
174186
if (method !== null && method !== undefined && !methodIsString) {
Collapse file

‎lib/internal/errors.js‎

Copy file name to clipboardExpand all lines: lib/internal/errors.js
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,16 @@ const fatalExceptionStackEnhancers = {
727727
}
728728
};
729729

730+
// Node uses an AbortError that isn't exactly the same as the DOMException
731+
// to make usage of the error in userland and readable-stream easier.
732+
// It is a regular error with `.code` and `.name`.
733+
class AbortError extends Error {
734+
constructor() {
735+
super('The operation was aborted');
736+
this.code = 'ABORT_ERR';
737+
this.name = 'AbortError';
738+
}
739+
}
730740
module.exports = {
731741
addCodeToName, // Exported for NghttpError
732742
codes,
@@ -741,6 +751,7 @@ module.exports = {
741751
uvException,
742752
uvExceptionWithHostPort,
743753
SystemError,
754+
AbortError,
744755
// This is exported only to facilitate testing.
745756
E,
746757
kNoOverride,
Collapse file

‎test/parallel/test-http-client-abort-destroy.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-http-client-abort-destroy.js
+24-2Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ const assert = require('assert');
5252
{
5353
// destroy
5454

55-
const server = http.createServer(common.mustNotCall((req, res) => {
56-
}));
55+
const server = http.createServer(common.mustNotCall());
5756

5857
server.listen(0, common.mustCall(() => {
5958
const options = { port: server.address().port };
@@ -69,3 +68,26 @@ const assert = require('assert');
6968
assert.strictEqual(req.destroyed, true);
7069
}));
7170
}
71+
72+
73+
{
74+
// Destroy with AbortSignal
75+
76+
const server = http.createServer(common.mustNotCall());
77+
const controller = new AbortController();
78+
79+
server.listen(0, common.mustCall(() => {
80+
const options = { port: server.address().port, signal: controller.signal };
81+
const req = http.get(options, common.mustNotCall());
82+
req.on('error', common.mustCall((err) => {
83+
assert.strictEqual(err.code, 'ABORT_ERR');
84+
assert.strictEqual(err.name, 'AbortError');
85+
server.close();
86+
}));
87+
assert.strictEqual(req.aborted, false);
88+
assert.strictEqual(req.destroyed, false);
89+
controller.abort();
90+
assert.strictEqual(req.aborted, false);
91+
assert.strictEqual(req.destroyed, true);
92+
}));
93+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.