Skip to content

Commit

Permalink
Merge #1739 into Got 12
Browse files Browse the repository at this point in the history
  • Loading branch information
szmarczak committed Jul 1, 2021
1 parent 1e1e506 commit f27e8d3
Show file tree
Hide file tree
Showing 14 changed files with 73 additions and 23 deletions.
1 change: 1 addition & 0 deletions source/as-promise/types.ts
Expand Up @@ -15,6 +15,7 @@ export class CancelError extends RequestError {
constructor(request: Request) {
super('Promise was canceled', {}, request);
this.name = 'CancelError';
this.code = 'ERR_CANCELED';
}

get isCanceled() {
Expand Down
2 changes: 1 addition & 1 deletion source/core/calculate-retry-delay.ts
Expand Up @@ -18,7 +18,7 @@ const calculateRetryDelay: Returns<RetryFunction, number> = ({
}

const hasMethod = retryOptions.methods.includes(error.options.method);
const hasErrorCode = retryOptions.errorCodes.includes(error.code!);
const hasErrorCode = retryOptions.errorCodes.includes(error.code);
const hasStatusCode = error.response && retryOptions.statusCodes.includes(error.response.statusCode);
if (!hasMethod || (!hasErrorCode && !hasStatusCode)) {
return 0;
Expand Down
9 changes: 7 additions & 2 deletions source/core/errors.ts
Expand Up @@ -19,7 +19,7 @@ Contains a `code` property with error class code, like `ECONNREFUSED`.
export class RequestError extends Error {
input?: string;

code?: string;
code: string;
stack!: string;
declare readonly options: Options;
readonly response?: Response;
Expand All @@ -31,7 +31,7 @@ export class RequestError extends Error {
Error.captureStackTrace(this, this.constructor);

this.name = 'RequestError';
this.code = error.code;
this.code = error.code ?? 'ERR_GOT_REQUEST_ERROR';
this.input = (error as any).input;

if (isRequest(self)) {
Expand Down Expand Up @@ -80,6 +80,7 @@ export class MaxRedirectsError extends RequestError {
constructor(request: Request) {
super(`Redirected ${request.options.maxRedirects} times. Aborting.`, {}, request);
this.name = 'MaxRedirectsError';
this.code = 'ERR_TOO_MANY_REDIRECTS';
}
}

Expand All @@ -95,6 +96,7 @@ export class HTTPError extends RequestError {
constructor(response: PlainResponse) {
super(`Response code ${response.statusCode} (${response.statusMessage!})`, {}, response.request);
this.name = 'HTTPError';
this.code = 'ERR_NON_2XX_3XX_RESPONSE';
}
}

Expand All @@ -108,6 +110,7 @@ export class CacheError extends RequestError {
constructor(error: Error, request: Request) {
super(error.message, error, request);
this.name = 'CacheError';
this.code = this.code === 'ERR_GOT_REQUEST_ERROR' ? 'ERR_CACHE_ACCESS' : this.code;
}
}

Expand All @@ -120,6 +123,7 @@ export class UploadError extends RequestError {
constructor(error: Error, request: Request) {
super(error.message, error, request);
this.name = 'UploadError';
this.code = this.code === 'ERR_GOT_REQUEST_ERROR' ? 'ERR_UPLOAD' : this.code;
}
}

Expand Down Expand Up @@ -151,6 +155,7 @@ export class ReadError extends RequestError {
constructor(error: Error, request: Request) {
super(error.message, error, request);
this.name = 'ReadError';
this.code = this.code === 'ERR_GOT_REQUEST_ERROR' ? 'ERR_READING_RESPONSE_STREAM' : this.code;
}
}

Expand Down
5 changes: 4 additions & 1 deletion source/core/options.ts
Expand Up @@ -1166,7 +1166,10 @@ export default class Options {
}

if (url.protocol !== 'http:' && url.protocol !== 'https:') {
throw new Error(`Unsupported protocol: ${url.protocol}`);
const error: NodeJS.ErrnoException = new Error(`Unsupported protocol: ${url.protocol}`);
error.code = 'ERR_UNSUPPORTED_PROTOCOL';

throw error;
}

if (this._internals.username) {
Expand Down
1 change: 1 addition & 0 deletions source/core/response.ts
Expand Up @@ -123,6 +123,7 @@ export class ParseError extends RequestError {

super(`${error.message} in "${options.url!.toString()}"`, error, response.request);
this.name = 'ParseError';
this.code = 'ERR_BODY_PARSE_FAILURE';
}
}

Expand Down
5 changes: 4 additions & 1 deletion test/cache.ts
Expand Up @@ -121,7 +121,10 @@ test('cache error throws `CacheError`', withServer, async (t, server, got) => {
const cache = {};

// @ts-expect-error Error tests
await t.throwsAsync(got({cache}), {instanceOf: CacheError});
await t.throwsAsync(got({cache}), {
instanceOf: CacheError,
code: 'ERR_CACHE_ACCESS'
});
});

test('doesn\'t cache response when received HTTP error', withServer, async (t, server, got) => {
Expand Down
30 changes: 24 additions & 6 deletions test/cancel.ts
Expand Up @@ -78,7 +78,10 @@ test.serial('does not retry after cancelation', withServerAndFakeTimers, async (
gotPromise.cancel();
});

await t.throwsAsync(gotPromise, {instanceOf: CancelError});
await t.throwsAsync(gotPromise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
});
await t.notThrowsAsync(promise, 'Request finished instead of aborting.');
});

Expand All @@ -105,7 +108,10 @@ test.serial('cleans up request timeouts', withServer, async (t, server, got) =>
}
});

await t.throwsAsync(gotPromise, {instanceOf: CancelError});
await t.throwsAsync(gotPromise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
});

// Wait for unhandled errors
await delay(40);
Expand All @@ -127,7 +133,10 @@ test.serial('cancels in-progress request', withServerAndFakeTimers, async (t, se
body.push(null);
});

await t.throwsAsync(gotPromise, {instanceOf: CancelError});
await t.throwsAsync(gotPromise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
});
await t.notThrowsAsync(promise, 'Request finished instead of aborting.');
});

Expand All @@ -147,7 +156,10 @@ test.serial('cancels in-progress request with timeout', withServerAndFakeTimers,
body.push(null);
});

await t.throwsAsync(gotPromise, {instanceOf: CancelError});
await t.throwsAsync(gotPromise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
});
await t.notThrowsAsync(promise, 'Request finished instead of aborting.');
});

Expand Down Expand Up @@ -227,7 +239,10 @@ test.serial('throws on incomplete (canceled) response - promise #2', withServer,
promise.cancel();
}, 500);

await t.throwsAsync(promise, {instanceOf: CancelError});
await t.throwsAsync(promise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
});
});

test.serial('throws on incomplete (canceled) response - stream', withServerAndFakeTimers, async (t, server, got, clock) => {
Expand Down Expand Up @@ -255,5 +270,8 @@ test('throws when canceling cached request', withServer, async (t, server, got)
const promise = got({cache});
promise.cancel();

await t.throwsAsync(promise, {instanceOf: CancelError});
await t.throwsAsync(promise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
});
});
9 changes: 6 additions & 3 deletions test/error.ts
Expand Up @@ -27,7 +27,7 @@ test('properties', withServer, async (t, server, got) => {
t.truthy(error.options);
t.true({}.propertyIsEnumerable.call(error, 'options'));
t.false({}.propertyIsEnumerable.call(error, 'response'));
t.is(error.code, undefined);
t.is(error.code, 'ERR_NON_2XX_3XX_RESPONSE');
t.is(error.message, 'Response code 404 (Not Found)');
t.deepEqual(error.options.url, url);
t.is(error.response.headers.connection, 'close');
Expand All @@ -40,6 +40,7 @@ test('catches dns errors', async t => {
t.regex(error.message, /ENOTFOUND|EAI_AGAIN/);
t.is((error.options.url as URL).host, 'doesntexist');
t.is(error.options.method, 'GET');
t.is(error.code, 'ENOTFOUND');
});

test('`options.body` form error message', async t => {
Expand Down Expand Up @@ -132,7 +133,8 @@ test('`http.request` error', async t => {
}
}), {
instanceOf: RequestError,
message: 'The header content contains invalid characters'
message: 'The header content contains invalid characters',
code: 'ERR_GOT_REQUEST_ERROR'
});
});

Expand Down Expand Up @@ -236,7 +238,8 @@ test('promise does not hang on timeout on HTTP error', withServer, async (t, ser
request: 100
}
}), {
instanceOf: TimeoutError
instanceOf: TimeoutError,
code: 'ETIMEDOUT'
});
});

Expand Down
6 changes: 4 additions & 2 deletions test/http.ts
Expand Up @@ -93,7 +93,8 @@ test('doesn\'t throw if `options.throwHttpErrors` is false', withServer, async (
test('invalid protocol throws', async t => {
await t.throwsAsync(got('c:/nope.com').json(), {
instanceOf: RequestError,
message: 'Unsupported protocol: c:'
message: 'Unsupported protocol: c:',
code: 'ERR_UNSUPPORTED_PROTOCOL'
});
});

Expand Down Expand Up @@ -226,7 +227,8 @@ test('throws an error if the server aborted the request', withServer, async (t,
});

const error = await t.throwsAsync<ReadError>(got(''), {
message: 'The server aborted pending request'
message: 'The server aborted pending request',
code: 'ECONNRESET'
});

t.truthy(error.response.retryCount);
Expand Down
3 changes: 2 additions & 1 deletion test/post.ts
Expand Up @@ -334,7 +334,8 @@ test('throws on upload error', withServer, async (t, server, got) => {
}
})), {
instanceOf: UploadError,
message
message,
code: 'ERR_UPLOAD'
});
});

Expand Down
10 changes: 8 additions & 2 deletions test/promise.ts
Expand Up @@ -71,8 +71,14 @@ test('promise.json() can be called before a file stream body is open', withServe

const promise = got({body});
const checks = [
t.throwsAsync(promise, {instanceOf: CancelError}),
t.throwsAsync(promise.json(), {instanceOf: CancelError})
t.throwsAsync(promise, {
instanceOf: CancelError,
code: 'ERR_CANCELED'
}),
t.throwsAsync(promise.json(), {
instanceOf: CancelError,
code: 'ERR_CANCELED'
})
];

promise.cancel();
Expand Down
8 changes: 6 additions & 2 deletions test/redirects.ts
Expand Up @@ -84,6 +84,7 @@ test('throws on endless redirects - default behavior', withServer, async (t, ser
const error = await t.throwsAsync<MaxRedirectsError>(got(''), {message: 'Redirected 10 times. Aborting.'});

t.deepEqual(error.response.redirectUrls.map(x => String(x)), Array.from({length: 10}).fill(`${server.url}/`));
t.is(error.code, 'ERR_TOO_MANY_REDIRECTS');
});

test('custom `maxRedirects` option', withServer, async (t, server, got) => {
Expand All @@ -97,6 +98,7 @@ test('custom `maxRedirects` option', withServer, async (t, server, got) => {
const error = await t.throwsAsync<MaxRedirectsError>(got('', {maxRedirects: 5}), {message: 'Redirected 5 times. Aborting.'});

t.deepEqual(error.response.redirectUrls.map(x => String(x)), Array.from({length: 5}).fill(`${server.url}/`));
t.is(error.code, 'ERR_TOO_MANY_REDIRECTS');
});

test('searchParams are not breaking redirects', withServer, async (t, server, got) => {
Expand All @@ -123,7 +125,8 @@ test('redirects GET and HEAD requests', withServer, async (t, server, got) => {
});

await t.throwsAsync(got.get(''), {
instanceOf: MaxRedirectsError
instanceOf: MaxRedirectsError,
code: 'ERR_TOO_MANY_REDIRECTS'
});
});

Expand All @@ -136,7 +139,8 @@ test('redirects POST requests', withServer, async (t, server, got) => {
});

await t.throwsAsync(got.post({body: 'wow'}), {
instanceOf: MaxRedirectsError
instanceOf: MaxRedirectsError,
code: 'ERR_TOO_MANY_REDIRECTS'
});
});

Expand Down
5 changes: 4 additions & 1 deletion test/response-parse.ts
Expand Up @@ -98,6 +98,7 @@ test('wraps parsing errors', withServer, async (t, server, got) => {
const error = await t.throwsAsync<ParseError>(got({responseType: 'json'}), {instanceOf: ParseError});
t.true(error.message.includes((error.options.url as URL).hostname));
t.is((error.options.url as URL).pathname, '/');
t.is(error.code, 'ERR_BODY_PARSE_FAILURE');
});

test('parses non-200 responses', withServer, async (t, server, got) => {
Expand Down Expand Up @@ -134,6 +135,7 @@ test('parse errors have `response` property', withServer, async (t, server, got)

t.is(error.response.statusCode, 200);
t.is(error.response.body, '/');
t.is(error.code, 'ERR_BODY_PARSE_FAILURE');
});

test('sets correct headers', withServer, async (t, server, got) => {
Expand Down Expand Up @@ -184,7 +186,8 @@ test('shortcuts throw ParseErrors', withServer, async (t, server, got) => {

await t.throwsAsync(got('').json(), {
instanceOf: ParseError,
message: /^Unexpected token o in JSON at position 1 in/
message: /^Unexpected token o in JSON at position 1 in/,
code: 'ERR_BODY_PARSE_FAILURE'
});
});

Expand Down
2 changes: 1 addition & 1 deletion test/retry.ts
Expand Up @@ -188,7 +188,7 @@ test('custom error codes', async t => {
},
retry: {
calculateDelay: ({error}) => {
t.is(error.code! as typeof errorCode, errorCode);
t.is(error.code, errorCode);
return 0;
},
methods: [
Expand Down

0 comments on commit f27e8d3

Please sign in to comment.