Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored AxiosError to be a constructor; Refactored Cancel [error] to be an AxiosError subclass; #3645

Merged
merged 1 commit into from Mar 10, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 22 additions & 1 deletion index.d.ts
Expand Up @@ -121,13 +121,34 @@ export interface AxiosResponse<T = unknown, D = any> {
request?: any;
}

export interface AxiosError<T = unknown, D = any> extends Error {
export class AxiosError<T = unknown, D = any> extends Error {
constructor(
message?: string,
code?: string,
config?: AxiosRequestConfig<D>,
request?: any,
response?: AxiosResponse<T, D>
);
config: AxiosRequestConfig<D>;
code?: string;
request?: any;
response?: AxiosResponse<T, D>;
isAxiosError: boolean;
status?: string;
toJSON: () => object;
static readonly ERR_FR_TOO_MANY_REDIRECTS = "ERR_FR_TOO_MANY_REDIRECTS";
static readonly ERR_BAD_OPTION_VALUE = "ERR_BAD_OPTION_VALUE";
static readonly ERR_BAD_OPTION = "ERR_BAD_OPTION";
static readonly ERR_NETWORK = "ERR_NETWORK";
static readonly ERR_DEPRECATED = "ERR_DEPRECATED";
static readonly ERR_BAD_RESPONSE = "ERR_BAD_RESPONSE";
static readonly ERR_BAD_REQUEST = "ERR_BAD_REQUEST";
static readonly ERR_CANCELED = "ERR_CANCELED";
static readonly ECONNABORTED = "ECONNABORTED";
static readonly ETIMEDOUT = "ETIMEDOUT";
}

export class CanceledError<T> extends AxiosError<T> {
}

export interface AxiosPromise<T = unknown> extends Promise<AxiosResponse<T>> {
Expand Down
30 changes: 15 additions & 15 deletions lib/adapters/http.js
Expand Up @@ -11,10 +11,9 @@ var httpsFollow = require('follow-redirects').https;
var url = require('url');
var zlib = require('zlib');
var VERSION = require('./../env/data').version;
var createError = require('../core/createError');
var enhanceError = require('../core/enhanceError');
var defaults = require('../defaults');
var Cancel = require('../cancel/Cancel');
var AxiosError = require('../core/AxiosError');
var CanceledError = require('../cancel/CanceledError');

var isHttps = /https:?/;

Expand Down Expand Up @@ -93,8 +92,9 @@ module.exports = function httpAdapter(config) {
} else if (utils.isString(data)) {
data = Buffer.from(data, 'utf-8');
} else {
return reject(createError(
return reject(new AxiosError(
'Data after transformation must be a string, an ArrayBuffer, a Buffer, or a Stream',
AxiosError.ERR_BAD_REQUEST,
config
));
}
Expand Down Expand Up @@ -270,14 +270,14 @@ module.exports = function httpAdapter(config) {
// make sure the content length is not over the maxContentLength if specified
if (config.maxContentLength > -1 && totalResponseBytes > config.maxContentLength) {
stream.destroy();
reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded',
config, null, lastRequest));
reject(new AxiosError('maxContentLength size of ' + config.maxContentLength + ' exceeded',
AxiosError.ERR_BAD_RESPONSE, config, lastRequest));
}
});

stream.on('error', function handleStreamError(err) {
if (req.aborted) return;
reject(enhanceError(err, config, null, lastRequest));
reject(AxiosError.from(err, null, config, lastRequest));
});

stream.on('end', function handleStreamEnd() {
Expand All @@ -297,8 +297,8 @@ module.exports = function httpAdapter(config) {

// Handle errors
req.on('error', function handleRequestError(err) {
if (req.aborted && err.code !== 'ERR_FR_TOO_MANY_REDIRECTS') return;
reject(enhanceError(err, config, null, req));
if (req.aborted && err.code !== AxiosError.ERR_FR_TOO_MANY_REDIRECTS) return;
reject(AxiosError.from(err, null, config, req));
});

// Handle request timeout
Expand All @@ -307,10 +307,10 @@ module.exports = function httpAdapter(config) {
var timeout = parseInt(config.timeout, 10);

if (isNaN(timeout)) {
reject(createError(
reject(new AxiosError(
'error trying to parse `config.timeout` to int',
AxiosError.ERR_BAD_OPTION_VALUE,
config,
'ERR_PARSE_TIMEOUT',
req
));

Expand All @@ -325,10 +325,10 @@ module.exports = function httpAdapter(config) {
req.setTimeout(timeout, function handleRequestTimeout() {
req.abort();
var transitional = config.transitional || defaults.transitional;
reject(createError(
reject(new AxiosError(
'timeout of ' + timeout + 'ms exceeded',
transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
config,
transitional.clarifyTimeoutError ? 'ETIMEDOUT' : 'ECONNABORTED',
req
));
});
Expand All @@ -341,7 +341,7 @@ module.exports = function httpAdapter(config) {
if (req.aborted) return;

req.abort();
reject(!cancel || (cancel && cancel.type) ? new Cancel('canceled') : cancel);
reject(!cancel || (cancel && cancel.type) ? new CanceledError() : cancel);
};

config.cancelToken && config.cancelToken.subscribe(onCanceled);
Expand All @@ -354,7 +354,7 @@ module.exports = function httpAdapter(config) {
// Send the request
if (utils.isStream(data)) {
data.on('error', function handleStreamError(err) {
reject(enhanceError(err, config, null, req));
reject(AxiosError.from(err, config, null, req));
}).pipe(req);
} else {
req.end(data);
Expand Down
14 changes: 7 additions & 7 deletions lib/adapters/xhr.js
Expand Up @@ -7,9 +7,9 @@ var buildURL = require('./../helpers/buildURL');
var buildFullPath = require('../core/buildFullPath');
var parseHeaders = require('./../helpers/parseHeaders');
var isURLSameOrigin = require('./../helpers/isURLSameOrigin');
var createError = require('../core/createError');
var defaults = require('../defaults');
var Cancel = require('../cancel/Cancel');
var AxiosError = require('../core/AxiosError');
var CanceledError = require('../cancel/CanceledError');

module.exports = function xhrAdapter(config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
Expand Down Expand Up @@ -104,7 +104,7 @@ module.exports = function xhrAdapter(config) {
return;
}

reject(createError('Request aborted', config, 'ECONNABORTED', request));
reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request));

// Clean up request
request = null;
Expand All @@ -114,7 +114,7 @@ module.exports = function xhrAdapter(config) {
request.onerror = function handleError() {
// Real errors are hidden from us by the browser
// onerror should only fire if it's a network error
reject(createError('Network Error', config, null, request));
reject(new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request, request));

// Clean up request
request = null;
Expand All @@ -127,10 +127,10 @@ module.exports = function xhrAdapter(config) {
if (config.timeoutErrorMessage) {
timeoutErrorMessage = config.timeoutErrorMessage;
}
reject(createError(
reject(new AxiosError(
timeoutErrorMessage,
transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
config,
transitional.clarifyTimeoutError ? 'ETIMEDOUT' : 'ECONNABORTED',
request));

// Clean up request
Expand Down Expand Up @@ -191,7 +191,7 @@ module.exports = function xhrAdapter(config) {
if (!request) {
return;
}
reject(!cancel || (cancel && cancel.type) ? new Cancel('canceled') : cancel);
reject(!cancel || (cancel && cancel.type) ? new CanceledError() : cancel);
request.abort();
request = null;
};
Expand Down
8 changes: 7 additions & 1 deletion lib/axios.js
Expand Up @@ -37,11 +37,17 @@ var axios = createInstance(defaults);
axios.Axios = Axios;

// Expose Cancel & CancelToken
axios.Cancel = require('./cancel/Cancel');
axios.CanceledError = require('./cancel/CanceledError');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
axios.VERSION = require('./env/data').version;

// Expose AxiosError class
axios.AxiosError = require('../lib/core/AxiosError');

// alias for CanceledError for backward compatibility
axios.Cancel = axios.CanceledError;

// Expose all/spread
axios.all = function all(promises) {
return Promise.all(promises);
Expand Down
19 changes: 0 additions & 19 deletions lib/cancel/Cancel.js

This file was deleted.

6 changes: 3 additions & 3 deletions lib/cancel/CancelToken.js
@@ -1,6 +1,6 @@
'use strict';

var Cancel = require('./Cancel');
var CanceledError = require('./CanceledError');

/**
* A `CancelToken` is an object that can be used to request cancellation of an operation.
Expand Down Expand Up @@ -56,13 +56,13 @@ function CancelToken(executor) {
return;
}

token.reason = new Cancel(message);
token.reason = new CanceledError(message);
resolvePromise(token.reason);
});
}

/**
* Throws a `Cancel` if cancellation has been requested.
* Throws a `CanceledError` if cancellation has been requested.
*/
CancelToken.prototype.throwIfRequested = function throwIfRequested() {
if (this.reason) {
Expand Down
22 changes: 22 additions & 0 deletions lib/cancel/CanceledError.js
@@ -0,0 +1,22 @@
'use strict';

var AxiosError = require('../core/AxiosError');
var utils = require('../utils');

/**
* A `CanceledError` is an object that is thrown when an operation is canceled.
*
* @class
* @param {string=} message The message.
*/
function CanceledError(message) {
// eslint-disable-next-line no-eq-null,eqeqeq
AxiosError.call(this, message == null ? 'canceled' : message, AxiosError.ERR_CANCELED);
this.name = 'CanceledError';
}

utils.inherits(CanceledError, AxiosError, {
__CANCEL__: true
});

module.exports = CanceledError;
86 changes: 86 additions & 0 deletions lib/core/AxiosError.js
@@ -0,0 +1,86 @@
'use strict';

var utils = require('../utils');

/**
* Create an Error with the specified message, config, error code, request and response.
*
* @param {string} message The error message.
* @param {string} [code] The error code (for example, 'ECONNABORTED').
* @param {Object} [config] The config.
* @param {Object} [request] The request.
* @param {Object} [response] The response.
* @returns {Error} The created error.
*/
function AxiosError(message, code, config, request, response) {
Error.call(this);
this.message = message;
this.name = 'AxiosError';
code && (this.code = code);
config && (this.config = config);
request && (this.request = request);
response && (this.response = response);
}

utils.inherits(AxiosError, Error, {
toJSON: function toJSON() {
return {
// Standard
message: this.message,
name: this.name,
// Microsoft
description: this.description,
number: this.number,
// Mozilla
fileName: this.fileName,
lineNumber: this.lineNumber,
columnNumber: this.columnNumber,
stack: this.stack,
// Axios
config: this.config,
code: this.code,
status: this.response && this.response.status ? this.response.status : null
};
}
});

var prototype = AxiosError.prototype;
var descriptors = {};

[
'ERR_BAD_OPTION_VALUE',
'ERR_BAD_OPTION',
'ECONNABORTED',
'ETIMEDOUT',
'ERR_NETWORK',
'ERR_FR_TOO_MANY_REDIRECTS',
'ERR_DEPRECATED',
'ERR_BAD_RESPONSE',
'ERR_BAD_REQUEST',
'ERR_CANCELED'
// eslint-disable-next-line func-names
].forEach(function(code) {
descriptors[code] = {value: code};
});

Object.defineProperties(AxiosError, descriptors);
Object.defineProperty(prototype, 'isAxiosError', {value: true});

// eslint-disable-next-line func-names
AxiosError.from = function(error, code, config, request, response, customProps) {
var axiosError = Object.create(prototype);

utils.toFlatObject(error, axiosError, function filter(obj) {
return obj !== Error.prototype;
});

AxiosError.call(axiosError, error.message, code, config, request, response);

axiosError.name = error.name;

customProps && Object.assign(axiosError, customProps);

return axiosError;
};

module.exports = AxiosError;
18 changes: 0 additions & 18 deletions lib/core/createError.js

This file was deleted.

6 changes: 3 additions & 3 deletions lib/core/dispatchRequest.js
Expand Up @@ -4,18 +4,18 @@ var utils = require('./../utils');
var transformData = require('./transformData');
var isCancel = require('../cancel/isCancel');
var defaults = require('../defaults');
var Cancel = require('../cancel/Cancel');
var CanceledError = require('../cancel/CanceledError');

/**
* Throws a `Cancel` if cancellation has been requested.
* Throws a `CanceledError` if cancellation has been requested.
*/
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}

if (config.signal && config.signal.aborted) {
throw new Cancel('canceled');
throw new CanceledError();
}
}

Expand Down