'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.boomify = function (error, options) { Hoek.assert(error instanceof Error, 'Cannot wrap non-Error object'); options = options || {}; if (!error.isBoom) { return internals.initialize(error, options.statusCode || 500, options.message); } if (options.override === false || // Defaults to true (!options.statusCode && !options.message)) { return error; } return internals.initialize(error, options.statusCode || error.output.statusCode, options.message); }; 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); error.typeof = ctor; 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; if (!message && !error.message) { error.reformat(); message = error.output.payload.error; } if (message) { error.message = (message + (error.message ? ': ' + error.message : '')); error.output.payload.message = error.message; } error.reformat(); 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) { if (data instanceof Error && !data.isBoom) { return exports.wrap(data, statusCode, message); } const 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; };