You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
433 lines
11 KiB
433 lines
11 KiB
'use strict'; |
|
|
|
// Load modules |
|
|
|
const Hoek = require('hoek'); |
|
|
|
|
|
// Declare internals |
|
|
|
const internals = { |
|
STATUS_CODES: Object.setPrototypeOf({ |
|
'100': 'Continue', |
|
'101': 'Switching Protocols', |
|
'102': 'Processing', |
|
'200': 'OK', |
|
'201': 'Created', |
|
'202': 'Accepted', |
|
'203': 'Non-Authoritative Information', |
|
'204': 'No Content', |
|
'205': 'Reset Content', |
|
'206': 'Partial Content', |
|
'207': 'Multi-Status', |
|
'300': 'Multiple Choices', |
|
'301': 'Moved Permanently', |
|
'302': 'Moved Temporarily', |
|
'303': 'See Other', |
|
'304': 'Not Modified', |
|
'305': 'Use Proxy', |
|
'307': 'Temporary Redirect', |
|
'400': 'Bad Request', |
|
'401': 'Unauthorized', |
|
'402': 'Payment Required', |
|
'403': 'Forbidden', |
|
'404': 'Not Found', |
|
'405': 'Method Not Allowed', |
|
'406': 'Not Acceptable', |
|
'407': 'Proxy Authentication Required', |
|
'408': 'Request Time-out', |
|
'409': 'Conflict', |
|
'410': 'Gone', |
|
'411': 'Length Required', |
|
'412': 'Precondition Failed', |
|
'413': 'Request Entity Too Large', |
|
'414': 'Request-URI Too Large', |
|
'415': 'Unsupported Media Type', |
|
'416': 'Requested Range Not Satisfiable', |
|
'417': 'Expectation Failed', |
|
'418': 'I\'m a teapot', |
|
'422': 'Unprocessable Entity', |
|
'423': 'Locked', |
|
'424': 'Failed Dependency', |
|
'425': 'Unordered Collection', |
|
'426': 'Upgrade Required', |
|
'428': 'Precondition Required', |
|
'429': 'Too Many Requests', |
|
'431': 'Request Header Fields Too Large', |
|
'451': 'Unavailable For Legal Reasons', |
|
'500': 'Internal Server Error', |
|
'501': 'Not Implemented', |
|
'502': 'Bad Gateway', |
|
'503': 'Service Unavailable', |
|
'504': 'Gateway Time-out', |
|
'505': 'HTTP Version Not Supported', |
|
'506': 'Variant Also Negotiates', |
|
'507': 'Insufficient Storage', |
|
'509': 'Bandwidth Limit Exceeded', |
|
'510': 'Not Extended', |
|
'511': 'Network Authentication Required' |
|
}, null) |
|
}; |
|
|
|
|
|
exports.wrap = function (error, statusCode, message) { |
|
|
|
Hoek.assert(error instanceof Error, 'Cannot wrap non-Error object'); |
|
Hoek.assert(!error.isBoom || (!statusCode && !message), 'Cannot provide statusCode or message with boom error'); |
|
|
|
return (error.isBoom ? error : internals.initialize(error, statusCode || 500, message)); |
|
}; |
|
|
|
|
|
exports.create = function (statusCode, message, data) { |
|
|
|
return internals.create(statusCode, message, data, exports.create); |
|
}; |
|
|
|
|
|
internals.create = function (statusCode, message, data, ctor) { |
|
|
|
if (message instanceof Error) { |
|
if (data) { |
|
message.data = data; |
|
} |
|
return exports.wrap(message, statusCode); |
|
} |
|
|
|
const error = new Error(message ? message : undefined); // Avoids settings null message |
|
Error.captureStackTrace(error, ctor); // Filter the stack to our external API |
|
error.data = data || null; |
|
internals.initialize(error, statusCode); |
|
return error; |
|
}; |
|
|
|
|
|
internals.initialize = function (error, statusCode, message) { |
|
|
|
const numberCode = parseInt(statusCode, 10); |
|
Hoek.assert(!isNaN(numberCode) && numberCode >= 400, 'First argument must be a number (400+):', statusCode); |
|
|
|
error.isBoom = true; |
|
error.isServer = numberCode >= 500; |
|
|
|
if (!error.hasOwnProperty('data')) { |
|
error.data = null; |
|
} |
|
|
|
error.output = { |
|
statusCode: numberCode, |
|
payload: {}, |
|
headers: {} |
|
}; |
|
|
|
error.reformat = internals.reformat; |
|
error.reformat(); |
|
|
|
if (!message && |
|
!error.message) { |
|
|
|
message = error.output.payload.error; |
|
} |
|
|
|
if (message) { |
|
error.message = (message + (error.message ? ': ' + error.message : '')); |
|
} |
|
|
|
return error; |
|
}; |
|
|
|
|
|
internals.reformat = function () { |
|
|
|
this.output.payload.statusCode = this.output.statusCode; |
|
this.output.payload.error = internals.STATUS_CODES[this.output.statusCode] || 'Unknown'; |
|
|
|
if (this.output.statusCode === 500) { |
|
this.output.payload.message = 'An internal server error occurred'; // Hide actual error from user |
|
} |
|
else if (this.message) { |
|
this.output.payload.message = this.message; |
|
} |
|
}; |
|
|
|
|
|
// 4xx Client Errors |
|
|
|
exports.badRequest = function (message, data) { |
|
|
|
return internals.create(400, message, data, exports.badRequest); |
|
}; |
|
|
|
|
|
exports.unauthorized = function (message, scheme, attributes) { // Or function (message, wwwAuthenticate[]) |
|
|
|
const err = internals.create(401, message, undefined, exports.unauthorized); |
|
|
|
if (!scheme) { |
|
return err; |
|
} |
|
|
|
let wwwAuthenticate = ''; |
|
|
|
if (typeof scheme === 'string') { |
|
|
|
// function (message, scheme, attributes) |
|
|
|
wwwAuthenticate = scheme; |
|
|
|
if (attributes || message) { |
|
err.output.payload.attributes = {}; |
|
} |
|
|
|
if (attributes) { |
|
if (typeof attributes === 'string') { |
|
wwwAuthenticate = wwwAuthenticate + ' ' + Hoek.escapeHeaderAttribute(attributes); |
|
err.output.payload.attributes = attributes; |
|
} |
|
else { |
|
const names = Object.keys(attributes); |
|
for (let i = 0; i < names.length; ++i) { |
|
const name = names[i]; |
|
if (i) { |
|
wwwAuthenticate = wwwAuthenticate + ','; |
|
} |
|
|
|
let value = attributes[name]; |
|
if (value === null || |
|
value === undefined) { // Value can be zero |
|
|
|
value = ''; |
|
} |
|
wwwAuthenticate = wwwAuthenticate + ' ' + name + '="' + Hoek.escapeHeaderAttribute(value.toString()) + '"'; |
|
err.output.payload.attributes[name] = value; |
|
} |
|
} |
|
} |
|
|
|
|
|
if (message) { |
|
if (attributes) { |
|
wwwAuthenticate = wwwAuthenticate + ','; |
|
} |
|
wwwAuthenticate = wwwAuthenticate + ' error="' + Hoek.escapeHeaderAttribute(message) + '"'; |
|
err.output.payload.attributes.error = message; |
|
} |
|
else { |
|
err.isMissing = true; |
|
} |
|
} |
|
else { |
|
|
|
// function (message, wwwAuthenticate[]) |
|
|
|
const wwwArray = scheme; |
|
for (let i = 0; i < wwwArray.length; ++i) { |
|
if (i) { |
|
wwwAuthenticate = wwwAuthenticate + ', '; |
|
} |
|
|
|
wwwAuthenticate = wwwAuthenticate + wwwArray[i]; |
|
} |
|
} |
|
|
|
err.output.headers['WWW-Authenticate'] = wwwAuthenticate; |
|
|
|
return err; |
|
}; |
|
|
|
|
|
exports.paymentRequired = function (message, data) { |
|
|
|
return internals.create(402, message, data, exports.paymentRequired); |
|
}; |
|
|
|
|
|
exports.forbidden = function (message, data) { |
|
|
|
return internals.create(403, message, data, exports.forbidden); |
|
}; |
|
|
|
|
|
exports.notFound = function (message, data) { |
|
|
|
return internals.create(404, message, data, exports.notFound); |
|
}; |
|
|
|
|
|
exports.methodNotAllowed = function (message, data, allow) { |
|
|
|
const err = internals.create(405, message, data, exports.methodNotAllowed); |
|
|
|
if (typeof allow === 'string') { |
|
allow = [allow]; |
|
} |
|
|
|
if (Array.isArray(allow)) { |
|
err.output.headers.Allow = allow.join(', '); |
|
} |
|
|
|
return err; |
|
}; |
|
|
|
|
|
exports.notAcceptable = function (message, data) { |
|
|
|
return internals.create(406, message, data, exports.notAcceptable); |
|
}; |
|
|
|
|
|
exports.proxyAuthRequired = function (message, data) { |
|
|
|
return internals.create(407, message, data, exports.proxyAuthRequired); |
|
}; |
|
|
|
|
|
exports.clientTimeout = function (message, data) { |
|
|
|
return internals.create(408, message, data, exports.clientTimeout); |
|
}; |
|
|
|
|
|
exports.conflict = function (message, data) { |
|
|
|
return internals.create(409, message, data, exports.conflict); |
|
}; |
|
|
|
|
|
exports.resourceGone = function (message, data) { |
|
|
|
return internals.create(410, message, data, exports.resourceGone); |
|
}; |
|
|
|
|
|
exports.lengthRequired = function (message, data) { |
|
|
|
return internals.create(411, message, data, exports.lengthRequired); |
|
}; |
|
|
|
|
|
exports.preconditionFailed = function (message, data) { |
|
|
|
return internals.create(412, message, data, exports.preconditionFailed); |
|
}; |
|
|
|
|
|
exports.entityTooLarge = function (message, data) { |
|
|
|
return internals.create(413, message, data, exports.entityTooLarge); |
|
}; |
|
|
|
|
|
exports.uriTooLong = function (message, data) { |
|
|
|
return internals.create(414, message, data, exports.uriTooLong); |
|
}; |
|
|
|
|
|
exports.unsupportedMediaType = function (message, data) { |
|
|
|
return internals.create(415, message, data, exports.unsupportedMediaType); |
|
}; |
|
|
|
|
|
exports.rangeNotSatisfiable = function (message, data) { |
|
|
|
return internals.create(416, message, data, exports.rangeNotSatisfiable); |
|
}; |
|
|
|
|
|
exports.expectationFailed = function (message, data) { |
|
|
|
return internals.create(417, message, data, exports.expectationFailed); |
|
}; |
|
|
|
|
|
exports.teapot = function (message, data) { |
|
|
|
return internals.create(418, message, data, exports.teapot); |
|
}; |
|
|
|
|
|
exports.badData = function (message, data) { |
|
|
|
return internals.create(422, message, data, exports.badData); |
|
}; |
|
|
|
|
|
exports.locked = function (message, data) { |
|
|
|
return internals.create(423, message, data, exports.locked); |
|
}; |
|
|
|
|
|
exports.preconditionRequired = function (message, data) { |
|
|
|
return internals.create(428, message, data, exports.preconditionRequired); |
|
}; |
|
|
|
|
|
exports.tooManyRequests = function (message, data) { |
|
|
|
return internals.create(429, message, data, exports.tooManyRequests); |
|
}; |
|
|
|
|
|
exports.illegal = function (message, data) { |
|
|
|
return internals.create(451, message, data, exports.illegal); |
|
}; |
|
|
|
|
|
// 5xx Server Errors |
|
|
|
exports.internal = function (message, data, statusCode) { |
|
|
|
return internals.serverError(message, data, statusCode, exports.internal); |
|
}; |
|
|
|
|
|
internals.serverError = function (message, data, statusCode, ctor) { |
|
|
|
let error; |
|
if (data instanceof Error) { |
|
error = exports.wrap(data, statusCode, message); |
|
} |
|
else { |
|
error = internals.create(statusCode || 500, message, undefined, ctor); |
|
error.data = data; |
|
} |
|
|
|
return error; |
|
}; |
|
|
|
|
|
exports.notImplemented = function (message, data) { |
|
|
|
return internals.serverError(message, data, 501, exports.notImplemented); |
|
}; |
|
|
|
|
|
exports.badGateway = function (message, data) { |
|
|
|
return internals.serverError(message, data, 502, exports.badGateway); |
|
}; |
|
|
|
|
|
exports.serverUnavailable = function (message, data) { |
|
|
|
return internals.serverError(message, data, 503, exports.serverUnavailable); |
|
}; |
|
|
|
|
|
exports.gatewayTimeout = function (message, data) { |
|
|
|
return internals.serverError(message, data, 504, exports.gatewayTimeout); |
|
}; |
|
|
|
|
|
exports.badImplementation = function (message, data) { |
|
|
|
const err = internals.serverError(message, data, 500, exports.badImplementation); |
|
err.isDeveloperError = true; |
|
return err; |
|
};
|
|
|