Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions lib/fetch/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ const {
isBlobLike,
sameOrigin,
isCancelled,
isAborted
isAborted,
isErrorLike
} = require('./util')
const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
const assert = require('assert')
Expand Down Expand Up @@ -1854,7 +1855,7 @@ async function httpNetworkFetch (
timingInfo.decodedBodySize += bytes?.byteLength ?? 0

// 6. If bytes is failure, then terminate fetchParams’s controller.
if (bytes instanceof Error) {
if (isErrorLike(bytes)) {
fetchParams.controller.terminate(bytes)
return
}
Expand Down Expand Up @@ -1894,7 +1895,7 @@ async function httpNetworkFetch (
// 3. Otherwise, if stream is readable, error stream with a TypeError.
if (isReadable(stream)) {
fetchParams.controller.controller.error(new TypeError('terminated', {
cause: reason instanceof Error ? reason : undefined
cause: isErrorLike(reason) ? reason : undefined
}))
}
}
Expand Down
15 changes: 8 additions & 7 deletions lib/fetch/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ const {
isCancelled,
isAborted,
isBlobLike,
serializeJavascriptValueToJSONString
serializeJavascriptValueToJSONString,
isErrorLike
} = require('./util')
const {
redirectStatus,
Expand Down Expand Up @@ -347,15 +348,15 @@ function makeResponse (init) {
}

function makeNetworkError (reason) {
const isError = isErrorLike(reason)
return makeResponse({
type: 'error',
status: 0,
error:
reason instanceof Error
? reason
: new Error(reason ? String(reason) : reason, {
cause: reason instanceof Error ? reason : undefined
}),
error: isError
? reason
: new Error(reason ? String(reason) : reason, {
cause: isError ? reason : undefined
}),
aborted: reason && reason.name === 'AbortError'
})
}
Expand Down
10 changes: 9 additions & 1 deletion lib/fetch/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ function isFileLike (object) {
)
}

function isErrorLike (object) {
return object instanceof Error || (
object?.constructor?.name === 'Error' ||
object?.constructor?.name === 'DOMException'
)
}

// Check whether |statusText| is a ByteString and
// matches the Reason-Phrase token production.
// RFC 2616: https://tools.ietf.org/html/rfc2616
Expand Down Expand Up @@ -469,5 +476,6 @@ module.exports = {
makeIterator,
isValidHeaderName,
isValidHeaderValue,
hasOwn
hasOwn,
isErrorLike
}
44 changes: 44 additions & 0 deletions test/jest/instanceof-error.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use strict'

const { createServer } = require('http')
const { once } = require('events')

/* global expect, it, jest, AbortController */

// https://github.com/facebook/jest/issues/11607#issuecomment-899068995
jest.useRealTimers()

const runIf = (condition) => condition ? it : it.skip
const nodeMajor = Number(process.versions.node.split('.', 1)[0])

runIf(nodeMajor >= 16)('isErrorLike sanity check', () => {
const { isErrorLike } = require('../../lib/fetch/util')
const { DOMException } = require('../../lib/fetch/constants')
const error = new DOMException('')

// https://github.com/facebook/jest/issues/2549
expect(error instanceof Error).toBeFalsy()
expect(isErrorLike(error)).toBeTruthy()
})

runIf(nodeMajor >= 16)('Real use-case', async () => {
const { fetch } = require('../..')

const ac = new AbortController()
ac.abort()

const server = createServer((req, res) => {
res.end()
}).listen(0)

await once(server, 'listening')

const promise = fetch(`https://localhost:${server.address().port}`, {
signal: ac.signal
})

await expect(promise).rejects.toThrowError('The operation was aborted.')

server.close()
await once(server, 'close')
})