Skip to content

Commit

Permalink
Restore logic and fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jopemachine committed May 12, 2022
1 parent 9451678 commit 723b960
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 4 deletions.
27 changes: 26 additions & 1 deletion source/as-promise/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
RequestError,
HTTPError,
RetryError,
AbortError,
} from '../core/errors.js';
import Request from '../core/index.js';
import {parseBody, isResponseOk} from '../core/response.js';
Expand All @@ -22,13 +23,37 @@ const proxiedRequestEvents = [
'downloadProgress',
];

export default function asPromise<T>(firstRequest?: Request): CancelableRequest<T> {
// eslint-disable-next-line @typescript-eslint/naming-convention
const getDOMException = (errorMessage: string) => globalThis.DOMException === undefined
? new AbortError(errorMessage)
: new DOMException(errorMessage);

const getAbortedReason = (signal: AbortSignal) => {
// To do: Remove below any castings when '@types/node' targets 18
const reason = (signal as any).reason === undefined
? getDOMException('This operation was aborted.')
: (signal as any).reason;

return reason instanceof Error ? reason : getDOMException(reason);
};

export default function asPromise<T>(firstRequest?: Request, signal?: AbortSignal): CancelableRequest<T> {
let globalRequest: Request;
let globalResponse: Response;
let normalizedOptions: Options;
const emitter = new EventEmitter();

const promise = new PCancelable<T>((resolve, reject, onCancel) => {
if (signal) {
if (signal.aborted) {
reject(getAbortedReason(signal));
}

signal.addEventListener('abort', () => {
reject(getAbortedReason(signal));
});
}

onCancel(() => {
globalRequest.destroy();
});
Expand Down
12 changes: 12 additions & 0 deletions source/core/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,15 @@ export class RetryError extends RequestError {
this.code = 'ERR_RETRYING';
}
}

/**
An error to be thrown when the request is aborted by AbortController.
DOMException is thrown instead of this Error when DOMException is available.
*/
export class AbortError extends Error {
constructor(message: string) {
super();
this.name = 'AbortError';
this.message = message;
}
}
3 changes: 2 additions & 1 deletion source/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
TimeoutError,
UploadError,
CacheError,
AbortError,
} from './errors.js';
import type {PlainResponse} from './response.js';
import type {PromiseCookieJar, NativeRequestOptions, RetryOptions} from './options.js';
Expand Down Expand Up @@ -236,7 +237,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
}

this.options.signal?.addEventListener('abort', () => {
this.destroy(new Error('This operation was aborted.'));
this.destroy(new AbortError('This operation was aborted.'));
});

// Important! If you replace `body` in a handler with another stream, make sure it's readable first.
Expand Down
20 changes: 18 additions & 2 deletions source/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ const delay = async (ms: number) => new Promise(resolve => {

const isGotInstance = (value: Got | ExtendOptions): value is Got => is.function_(value);

const getSignal = (url: string | URL | OptionsInit | undefined, options?: OptionsInit): AbortSignal | undefined => {
let signal;

if (typeof url === 'object' && 'signal' in (url as OptionsInit)) {
signal = (url as OptionsInit).signal;
}

if (options?.signal) {
signal = options.signal;
}

return signal;
};

const aliases: readonly HTTPAlias[] = [
'get',
'post',
Expand Down Expand Up @@ -52,6 +66,8 @@ const create = (defaults: InstanceDefaults): Got => {
const request = new Request(url, options, defaultOptions);
let promise: CancelableRequest | undefined;

const signal = getSignal(url, options);

const lastHandler = (normalized: Options): GotReturn => {
// Note: `options` is `undefined` when `new Options(...)` fails
request.options = normalized;
Expand All @@ -63,7 +79,7 @@ const create = (defaults: InstanceDefaults): Got => {
}

if (!promise) {
promise = asPromise(request);
promise = asPromise(request, signal);
}

return promise;
Expand All @@ -77,7 +93,7 @@ const create = (defaults: InstanceDefaults): Got => {

if (is.promise(result) && !request.options.isStream) {
if (!promise) {
promise = asPromise(request);
promise = asPromise(request, signal);
}

if (result !== promise) {
Expand Down

0 comments on commit 723b960

Please sign in to comment.