cloudscraper/errors.js
2019-03-08 08:31:51 -06:00

131 lines
4.1 KiB
JavaScript

'use strict';
// The purpose of this library:
// 1. Have errors consistent with request/promise-core
// 2. Prevent request/promise core from wrapping our errors
// 3. Create descriptive errors.
// There are two differences between these errors and the originals.
// 1. There is a non-enumerable errorType attribute.
// 2. The error constructor is hidden from the stacktrace.
var EOL = require('os').EOL;
var original = require('request-promise-core/errors');
var http = require('http');
var BUG_REPORT = format([
'### Cloudflare may have changed their technique, or there may be a bug.',
'### Bug Reports: https://github.com/codemanki/cloudscraper/issues',
'### Check the detailed exception message that follows for the cause.'
]);
var ERROR_CODES = {
// Non-standard 5xx server error HTTP status codes
'522': 'Connection timed out',
'521': 'Web server is down',
'520': 'Web server is returning an unknown error',
'523': 'Origin is unreachable',
'524': 'A timeout occurred',
'525': 'SSL handshake failed',
'526': 'Invalid SSL certificate',
// Other codes
'1000': 'DNS points to prohibited IP',
'1001': 'DNS resolution error',
'1002': 'Restricted or DNS points to Prohibited IP',
'1003': 'Access Denied: Direct IP Access Not Allowed',
'1004': 'Host Not Configured to Serve Web Traffic',
'1010': 'The owner of this website has banned your access based on your browser\'s signature',
'1011': 'Access Denied (Hotlinking Denied)',
'1012': 'Access Denied',
'1013': 'HTTP hostname and TLS SNI hostname mismatch',
'1016': 'Origin DNS error'
};
ERROR_CODES[1006] =
ERROR_CODES[1007] =
ERROR_CODES[1008] = 'Access Denied: Your IP address has been banned';
var OriginalError = original.RequestError;
var RequestError = create('RequestError', 0);
var CaptchaError = create('CaptchaError', 1);
// errorType 4 is a CloudflareError so this constructor is reused.
var CloudflareError = create('CloudflareError', 2, function (error) {
if (!isNaN(error.cause)) {
var description = ERROR_CODES[error.cause] || http.STATUS_CODES[error.cause];
if (description) {
error.message = error.cause + ', ' + description;
}
}
});
var ParserError = create('ParserError', 3, function (error) {
error.message = BUG_REPORT + error.message;
});
// The following errors originate from promise-core and it's dependents.
// Give them an errorType for consistency.
original.StatusCodeError.prototype.errorType = 5;
original.TransformError.prototype.errorType = 6;
// This replaces the RequestError for all libraries using request/promise-core
// and prevents silent failure.
Object.defineProperty(original, 'RequestError', {
configurable: true,
enumerable: true,
writable: true,
value: RequestError
});
// Export our custom errors along with StatusCodeError, etc.
Object.assign(module.exports, original, {
RequestError: RequestError,
CaptchaError: CaptchaError,
ParserError: ParserError,
CloudflareError: CloudflareError
});
function create(name, errorType, customize) {
function CustomError(cause, options, response) {
// This prevents nasty things e.g. `error.cause.error` and
// is why replacing the original RequestError is necessary.
if (cause instanceof OriginalError) {
return cause;
}
OriginalError.apply(this, arguments);
// Change the name to match this constructor
this.name = name;
if (typeof customize === 'function') {
customize(this);
}
if (Error.captureStackTrace) { // required for non-V8 environments
// Provide a proper stack trace that hides this constructor
Error.captureStackTrace(this, CustomError);
}
}
CustomError.prototype = Object.create(OriginalError.prototype);
CustomError.prototype.constructor = CustomError;
// Keeps things stealthy by defining errorType on the prototype.
// This makes it non-enumerable and safer to add.
CustomError.prototype.errorType = errorType;
Object.setPrototypeOf(CustomError, Object.getPrototypeOf(OriginalError));
Object.defineProperty(CustomError, 'name', {
configurable: true,
value: name
});
return CustomError;
}
function format(lines) {
return EOL + lines.join(EOL) + EOL + EOL;
}