|
@@ -23,11 +23,11 @@ async function fetchJson(url, opts = {}) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async function fetchJsonWithResponse(url, opts = {}) {
|
|
async function fetchJsonWithResponse(url, opts = {}) {
|
|
|
- const { fetchOpts } = parseOpts(opts)
|
|
|
|
|
|
|
+ const { fetchOpts, detachSignal } = parseOpts(opts)
|
|
|
fetchOpts.headers = fetchOpts.headers ?? {}
|
|
fetchOpts.headers = fetchOpts.headers ?? {}
|
|
|
fetchOpts.headers.Accept = fetchOpts.headers.Accept ?? 'application/json'
|
|
fetchOpts.headers.Accept = fetchOpts.headers.Accept ?? 'application/json'
|
|
|
|
|
|
|
|
- const response = await performRequest(url, fetchOpts)
|
|
|
|
|
|
|
+ const response = await performRequest(url, fetchOpts, detachSignal)
|
|
|
if (!response.ok) {
|
|
if (!response.ok) {
|
|
|
const body = await maybeGetResponseBody(response)
|
|
const body = await maybeGetResponseBody(response)
|
|
|
throw new RequestFailedError(url, opts, response, body)
|
|
throw new RequestFailedError(url, opts, response, body)
|
|
@@ -53,8 +53,8 @@ async function fetchStream(url, opts = {}) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async function fetchStreamWithResponse(url, opts = {}) {
|
|
async function fetchStreamWithResponse(url, opts = {}) {
|
|
|
- const { fetchOpts, abortController } = parseOpts(opts)
|
|
|
|
|
- const response = await performRequest(url, fetchOpts)
|
|
|
|
|
|
|
+ const { fetchOpts, abortController, detachSignal } = parseOpts(opts)
|
|
|
|
|
+ const response = await performRequest(url, fetchOpts, detachSignal)
|
|
|
|
|
|
|
|
if (!response.ok) {
|
|
if (!response.ok) {
|
|
|
const body = await maybeGetResponseBody(response)
|
|
const body = await maybeGetResponseBody(response)
|
|
@@ -76,8 +76,8 @@ async function fetchStreamWithResponse(url, opts = {}) {
|
|
|
* @throws {RequestFailedError} if the response has a failure status code
|
|
* @throws {RequestFailedError} if the response has a failure status code
|
|
|
*/
|
|
*/
|
|
|
async function fetchNothing(url, opts = {}) {
|
|
async function fetchNothing(url, opts = {}) {
|
|
|
- const { fetchOpts } = parseOpts(opts)
|
|
|
|
|
- const response = await performRequest(url, fetchOpts)
|
|
|
|
|
|
|
+ const { fetchOpts, detachSignal } = parseOpts(opts)
|
|
|
|
|
+ const response = await performRequest(url, fetchOpts, detachSignal)
|
|
|
if (!response.ok) {
|
|
if (!response.ok) {
|
|
|
const body = await maybeGetResponseBody(response)
|
|
const body = await maybeGetResponseBody(response)
|
|
|
throw new RequestFailedError(url, opts, response, body)
|
|
throw new RequestFailedError(url, opts, response, body)
|
|
@@ -108,9 +108,9 @@ async function fetchRedirect(url, opts = {}) {
|
|
|
* @throws {RequestFailedError} if the response has a non redirect status code or missing Location header
|
|
* @throws {RequestFailedError} if the response has a non redirect status code or missing Location header
|
|
|
*/
|
|
*/
|
|
|
async function fetchRedirectWithResponse(url, opts = {}) {
|
|
async function fetchRedirectWithResponse(url, opts = {}) {
|
|
|
- const { fetchOpts } = parseOpts(opts)
|
|
|
|
|
|
|
+ const { fetchOpts, detachSignal } = parseOpts(opts)
|
|
|
fetchOpts.redirect = 'manual'
|
|
fetchOpts.redirect = 'manual'
|
|
|
- const response = await performRequest(url, fetchOpts)
|
|
|
|
|
|
|
+ const response = await performRequest(url, fetchOpts, detachSignal)
|
|
|
if (response.status < 300 || response.status >= 400) {
|
|
if (response.status < 300 || response.status >= 400) {
|
|
|
const body = await maybeGetResponseBody(response)
|
|
const body = await maybeGetResponseBody(response)
|
|
|
throw new RequestFailedError(url, opts, response, body)
|
|
throw new RequestFailedError(url, opts, response, body)
|
|
@@ -142,8 +142,8 @@ async function fetchString(url, opts = {}) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async function fetchStringWithResponse(url, opts = {}) {
|
|
async function fetchStringWithResponse(url, opts = {}) {
|
|
|
- const { fetchOpts } = parseOpts(opts)
|
|
|
|
|
- const response = await performRequest(url, fetchOpts)
|
|
|
|
|
|
|
+ const { fetchOpts, detachSignal } = parseOpts(opts)
|
|
|
|
|
+ const response = await performRequest(url, fetchOpts, detachSignal)
|
|
|
if (!response.ok) {
|
|
if (!response.ok) {
|
|
|
const body = await maybeGetResponseBody(response)
|
|
const body = await maybeGetResponseBody(response)
|
|
|
throw new RequestFailedError(url, opts, response, body)
|
|
throw new RequestFailedError(url, opts, response, body)
|
|
@@ -178,13 +178,14 @@ function parseOpts(opts) {
|
|
|
|
|
|
|
|
const abortController = new AbortController()
|
|
const abortController = new AbortController()
|
|
|
fetchOpts.signal = abortController.signal
|
|
fetchOpts.signal = abortController.signal
|
|
|
|
|
+ let detachSignal = () => {}
|
|
|
if (opts.signal) {
|
|
if (opts.signal) {
|
|
|
- abortOnSignal(abortController, opts.signal)
|
|
|
|
|
|
|
+ detachSignal = abortOnSignal(abortController, opts.signal)
|
|
|
}
|
|
}
|
|
|
if (opts.body instanceof Readable) {
|
|
if (opts.body instanceof Readable) {
|
|
|
abortOnDestroyedRequest(abortController, fetchOpts.body)
|
|
abortOnDestroyedRequest(abortController, fetchOpts.body)
|
|
|
}
|
|
}
|
|
|
- return { fetchOpts, abortController }
|
|
|
|
|
|
|
+ return { fetchOpts, abortController, detachSignal }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function setupJsonBody(fetchOpts, json) {
|
|
function setupJsonBody(fetchOpts, json) {
|
|
@@ -208,6 +209,9 @@ function abortOnSignal(abortController, signal) {
|
|
|
abortController.abort(signal.reason)
|
|
abortController.abort(signal.reason)
|
|
|
}
|
|
}
|
|
|
signal.addEventListener('abort', listener)
|
|
signal.addEventListener('abort', listener)
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ signal.removeEventListener('abort', listener)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
function abortOnDestroyedRequest(abortController, stream) {
|
|
function abortOnDestroyedRequest(abortController, stream) {
|
|
@@ -226,11 +230,12 @@ function abortOnDestroyedResponse(abortController, response) {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-async function performRequest(url, fetchOpts) {
|
|
|
|
|
|
|
+async function performRequest(url, fetchOpts, detachSignal) {
|
|
|
let response
|
|
let response
|
|
|
try {
|
|
try {
|
|
|
response = await fetch(url, fetchOpts)
|
|
response = await fetch(url, fetchOpts)
|
|
|
} catch (err) {
|
|
} catch (err) {
|
|
|
|
|
+ detachSignal()
|
|
|
if (fetchOpts.body instanceof Readable) {
|
|
if (fetchOpts.body instanceof Readable) {
|
|
|
fetchOpts.body.destroy()
|
|
fetchOpts.body.destroy()
|
|
|
}
|
|
}
|
|
@@ -239,6 +244,7 @@ async function performRequest(url, fetchOpts) {
|
|
|
method: fetchOpts.method ?? 'GET',
|
|
method: fetchOpts.method ?? 'GET',
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
+ response.body.on('close', detachSignal)
|
|
|
if (fetchOpts.body instanceof Readable) {
|
|
if (fetchOpts.body instanceof Readable) {
|
|
|
response.body.on('close', () => {
|
|
response.body.on('close', () => {
|
|
|
if (!fetchOpts.body.readableEnded) {
|
|
if (!fetchOpts.body.readableEnded) {
|