Skip to content

Commit

Permalink
feat: add abort signal to body.dump() (#1993)
Browse files Browse the repository at this point in the history
  • Loading branch information
debadree25 authored Mar 10, 2023
1 parent 0d2f4f1 commit 65eea9b
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 2 deletions.
20 changes: 18 additions & 2 deletions lib/api/readable.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

const assert = require('assert')
const { Readable } = require('stream')
const { RequestAbortedError, NotSupportedError } = require('../core/errors')
const { RequestAbortedError, NotSupportedError, InvalidArgumentError } = require('../core/errors')
const util = require('../core/util')
const { ReadableStreamFrom, toUSVString } = require('../core/util')

Expand Down Expand Up @@ -146,15 +146,31 @@ module.exports = class BodyReadable extends Readable {

async dump (opts) {
let limit = opts && Number.isFinite(opts.limit) ? opts.limit : 262144
const signal = opts && opts.signal
const abortFn = () => {
this.destroy()
}
if (signal) {
if (typeof signal !== 'object' || !('aborted' in signal)) {
throw new InvalidArgumentError('signal must be an AbortSignal')
}
util.throwIfAborted(signal)
signal.addEventListener('abort', abortFn, { once: true })
}
try {
for await (const chunk of this) {
util.throwIfAborted(signal)
limit -= Buffer.byteLength(chunk)
if (limit < 0) {
return
}
}
} catch {
// Do nothing...
util.throwIfAborted(signal)
} finally {
if (signal) {
signal.removeEventListener('abort', abortFn)
}
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions lib/core/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,20 @@ function isFormDataLike (object) {
)
}

function throwIfAborted (signal) {
if (!signal) { return }
if (typeof signal.throwIfAborted === 'function') {
signal.throwIfAborted()
} else {
if (signal.aborted) {
// DOMException not available < v17.0.0
const err = new Error('The operation was aborted')
err.name = 'AbortError'
throw err
}
}
}

const kEnumerableProperty = Object.create(null)
kEnumerableProperty.enumerable = true

Expand Down Expand Up @@ -426,6 +440,7 @@ module.exports = {
getSocketInfo,
isFormDataLike,
buildURL,
throwIfAborted,
nodeMajor,
nodeMinor,
nodeHasAutoSelectFamily: nodeMajor > 18 || (nodeMajor === 18 && nodeMinor >= 13)
Expand Down
34 changes: 34 additions & 0 deletions test/client-request.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* globals AbortController */

'use strict'

const { test } = require('tap')
Expand Down Expand Up @@ -41,6 +43,38 @@ test('request dump', (t) => {
})
})

test('request dump with abort signal', (t) => {
t.plan(2)
const server = createServer((req, res) => {
res.write('hello')
})
t.teardown(server.close.bind(server))

server.listen(0, () => {
const client = new Client(`http://localhost:${server.address().port}`)
t.teardown(client.destroy.bind(client))

client.request({
path: '/',
method: 'GET'
}, (err, { body }) => {
t.error(err)
let ac
if (!global.AbortController) {
const { AbortController } = require('abort-controller')
ac = new AbortController()
} else {
ac = new AbortController()
}
body.dump({ signal: ac.signal }).catch((err) => {
t.equal(err.name, 'AbortError')
server.close()
})
ac.abort()
})
})
})

test('request abort before headers', (t) => {
t.plan(6)

Expand Down

0 comments on commit 65eea9b

Please sign in to comment.