| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 6e734a8 commit 524b713
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2326,6 +2326,9 @@ This can be overridden for servers and client requests by passing the | |||
| 2326 | 2326 | <!-- YAML | |
| 2327 | 2327 | added: v0.3.6 | |
| 2328 | 2328 | changes: | |
| 2329 | + - version: REPLACEME | ||
| 2330 | + pr-url: https://github.com/nodejs/node/pull/36048 | ||
| 2331 | + description: It is possible to abort a request with an AbortSignal. | ||
| 2329 | 2332 | - version: | |
| 2330 | 2333 | - v13.8.0 | |
| 2331 | 2334 | - v12.15.0 | |
@@ -2393,6 +2396,8 @@ changes: | |||
| 2393 | 2396 | or `port` is specified, those specify a TCP Socket). | |
| 2394 | 2397 | * `timeout` {number}: A number specifying the socket timeout in milliseconds. | |
| 2395 | 2398 | This will set the timeout before the socket is connected. | |
| 2399 | + * `signal` {AbortSignal}: An AbortSignal that may be used to abort an ongoing | ||
| 2400 | + request. | ||
| 2396 | 2401 | * `callback` {Function} | |
| 2397 | 2402 | * Returns: {http.ClientRequest} | |
| 2398 | 2403 | ||
@@ -2580,6 +2585,10 @@ events will be emitted in the following order: | |||
| 2580 | 2585 | Setting the `timeout` option or using the `setTimeout()` function will | |
| 2581 | 2586 | not abort the request or do anything besides add a `'timeout'` event. | |
| 2582 | 2587 | ||
| 2588 | + Passing an `AbortSignal` and then calling `abort` on the corresponding | ||
| 2589 | + `AbortController` will behave the same way as calling `.destroy()` on the | ||
| 2590 | + request itself. | ||
| 2591 | + | ||
| 2583 | 2592 | ## `http.validateHeaderName(name)` | |
| 2584 | 2593 | <!-- YAML | |
| 2585 | 2594 | added: v14.3.0 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -95,7 +95,7 @@ rules: | |||
| 95 | 95 | - selector: "ThrowStatement > CallExpression[callee.name=/Error$/]" | |
| 96 | 96 | message: "Use new keyword when throwing an Error." | |
| 97 | 97 | # Config specific to lib | |
| 98 | - - selector: "NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError)$/])" | ||
| 98 | + - selector: "NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError)$/])" | ||
| 99 | 99 | message: "Use an error exported by the internal/errors module." | |
| 100 | 100 | - selector: "CallExpression[callee.object.name='Error'][callee.property.name='captureStackTrace']" | |
| 101 | 101 | message: "Please use `require('internal/errors').hideStackFrames()` instead." | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -51,15 +51,18 @@ const { Buffer } = require('buffer'); | |||
| 51 | 51 | const { defaultTriggerAsyncIdScope } = require('internal/async_hooks'); | |
| 52 | 52 | const { URL, urlToOptions, searchParamsSymbol } = require('internal/url'); | |
| 53 | 53 | const { kOutHeaders, kNeedDrain } = require('internal/http'); | |
| 54 | - const { connResetException, codes } = require('internal/errors'); | ||
| 54 | + const { AbortError, connResetException, codes } = require('internal/errors'); | ||
| 55 | 55 | const { | |
| 56 | 56 | ERR_HTTP_HEADERS_SENT, | |
| 57 | 57 | ERR_INVALID_ARG_TYPE, | |
| 58 | 58 | ERR_INVALID_HTTP_TOKEN, | |
| 59 | 59 | ERR_INVALID_PROTOCOL, | |
| 60 | 60 | ERR_UNESCAPED_CHARACTERS | |
| 61 | 61 | } = codes; | |
| 62 | - const { validateInteger } = require('internal/validators'); | ||
| 62 | + const { | ||
| 63 | + validateInteger, | ||
| 64 | + validateAbortSignal, | ||
| 65 | + } = require('internal/validators'); | ||
| 63 | 66 | const { getTimerDuration } = require('internal/timers'); | |
| 64 | 67 | const { | |
| 65 | 68 | DTRACE_HTTP_CLIENT_REQUEST, | |
@@ -169,6 +172,15 @@ function ClientRequest(input, options, cb) { | |||
| 169 | 172 | if (options.timeout !== undefined) | |
| 170 | 173 | this.timeout = getTimerDuration(options.timeout, 'timeout'); | |
| 171 | 174 | ||
| 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 | + } | ||
| 172 | 184 | let method = options.method; | |
| 173 | 185 | const methodIsString = (typeof method === 'string'); | |
| 174 | 186 | if (method !== null && method !== undefined && !methodIsString) { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -704,6 +704,16 @@ const fatalExceptionStackEnhancers = { | |||
| 704 | 704 | } | |
| 705 | 705 | }; | |
| 706 | 706 | ||
| 707 | + // Node uses an AbortError that isn't exactly the same as the DOMException | ||
| 708 | + // to make usage of the error in userland and readable-stream easier. | ||
| 709 | + // It is a regular error with `.code` and `.name`. | ||
| 710 | + class AbortError extends Error { | ||
| 711 | + constructor() { | ||
| 712 | + super('The operation was aborted'); | ||
| 713 | + this.code = 'ABORT_ERR'; | ||
| 714 | + this.name = 'AbortError'; | ||
| 715 | + } | ||
| 716 | + } | ||
| 707 | 717 | module.exports = { | |
| 708 | 718 | addCodeToName, // Exported for NghttpError | |
| 709 | 719 | codes, | |
@@ -718,6 +728,7 @@ module.exports = { | |||
| 718 | 728 | uvException, | |
| 719 | 729 | uvExceptionWithHostPort, | |
| 720 | 730 | SystemError, | |
| 731 | + AbortError, | ||
| 721 | 732 | // This is exported only to facilitate testing. | |
| 722 | 733 | E, | |
| 723 | 734 | kNoOverride, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,3 +1,4 @@ | |||
| 1 | + // Flags: --experimental-abortcontroller | ||
| 1 | 2 | 'use strict'; | |
| 2 | 3 | const common = require('../common'); | |
| 3 | 4 | const http = require('http'); | |
@@ -52,8 +53,7 @@ const assert = require('assert'); | |||
| 52 | 53 | { | |
| 53 | 54 | // destroy | |
| 54 | 55 | ||
| 55 | - const server = http.createServer(common.mustNotCall((req, res) => { | ||
| 56 | - })); | ||
| 56 | + const server = http.createServer(common.mustNotCall()); | ||
| 57 | 57 | ||
| 58 | 58 | server.listen(0, common.mustCall(() => { | |
| 59 | 59 | const options = { port: server.address().port }; | |
@@ -69,3 +69,26 @@ const assert = require('assert'); | |||
| 69 | 69 | assert.strictEqual(req.destroyed, true); | |
| 70 | 70 | })); | |
| 71 | 71 | } | |
| 72 | + | ||
| 73 | + | ||
| 74 | + { | ||
| 75 | + // Destroy with AbortSignal | ||
| 76 | + | ||
| 77 | + const server = http.createServer(common.mustNotCall()); | ||
| 78 | + const controller = new AbortController(); | ||
| 79 | + | ||
| 80 | + server.listen(0, common.mustCall(() => { | ||
| 81 | + const options = { port: server.address().port, signal: controller.signal }; | ||
| 82 | + const req = http.get(options, common.mustNotCall()); | ||
| 83 | + req.on('error', common.mustCall((err) => { | ||
| 84 | + assert.strictEqual(err.code, 'ABORT_ERR'); | ||
| 85 | + assert.strictEqual(err.name, 'AbortError'); | ||
| 86 | + server.close(); | ||
| 87 | + })); | ||
| 88 | + assert.strictEqual(req.aborted, false); | ||
| 89 | + assert.strictEqual(req.destroyed, false); | ||
| 90 | + controller.abort(); | ||
| 91 | + assert.strictEqual(req.aborted, false); | ||
| 92 | + assert.strictEqual(req.destroyed, true); | ||
| 93 | + })); | ||
| 94 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments