Path: blob/master/node_modules/@hapi/boom/lib/index.js
1126 views
'use strict';12const Hoek = require('@hapi/hoek');345const internals = {6codes: new Map([7[100, 'Continue'],8[101, 'Switching Protocols'],9[102, 'Processing'],10[200, 'OK'],11[201, 'Created'],12[202, 'Accepted'],13[203, 'Non-Authoritative Information'],14[204, 'No Content'],15[205, 'Reset Content'],16[206, 'Partial Content'],17[207, 'Multi-Status'],18[300, 'Multiple Choices'],19[301, 'Moved Permanently'],20[302, 'Moved Temporarily'],21[303, 'See Other'],22[304, 'Not Modified'],23[305, 'Use Proxy'],24[307, 'Temporary Redirect'],25[400, 'Bad Request'],26[401, 'Unauthorized'],27[402, 'Payment Required'],28[403, 'Forbidden'],29[404, 'Not Found'],30[405, 'Method Not Allowed'],31[406, 'Not Acceptable'],32[407, 'Proxy Authentication Required'],33[408, 'Request Time-out'],34[409, 'Conflict'],35[410, 'Gone'],36[411, 'Length Required'],37[412, 'Precondition Failed'],38[413, 'Request Entity Too Large'],39[414, 'Request-URI Too Large'],40[415, 'Unsupported Media Type'],41[416, 'Requested Range Not Satisfiable'],42[417, 'Expectation Failed'],43[418, 'I\'m a teapot'],44[422, 'Unprocessable Entity'],45[423, 'Locked'],46[424, 'Failed Dependency'],47[425, 'Too Early'],48[426, 'Upgrade Required'],49[428, 'Precondition Required'],50[429, 'Too Many Requests'],51[431, 'Request Header Fields Too Large'],52[451, 'Unavailable For Legal Reasons'],53[500, 'Internal Server Error'],54[501, 'Not Implemented'],55[502, 'Bad Gateway'],56[503, 'Service Unavailable'],57[504, 'Gateway Time-out'],58[505, 'HTTP Version Not Supported'],59[506, 'Variant Also Negotiates'],60[507, 'Insufficient Storage'],61[509, 'Bandwidth Limit Exceeded'],62[510, 'Not Extended'],63[511, 'Network Authentication Required']64])65};666768exports.Boom = class extends Error {6970constructor(message, options = {}) {7172if (message instanceof Error) {73return exports.boomify(Hoek.clone(message), options);74}7576const { statusCode = 500, data = null, ctor = exports.Boom } = options;77const error = new Error(message ? message : undefined); // Avoids settings null message78Error.captureStackTrace(error, ctor); // Filter the stack to our external API79error.data = data;80const boom = internals.initialize(error, statusCode);8182Object.defineProperty(boom, 'typeof', { value: ctor });8384if (options.decorate) {85Object.assign(boom, options.decorate);86}8788return boom;89}9091static [Symbol.hasInstance](instance) {9293if (this === exports.Boom) {94return exports.isBoom(instance);95}9697// Cannot use 'instanceof' as it creates infinite recursion9899return this.prototype.isPrototypeOf(instance);100}101};102103104exports.isBoom = function (err, statusCode) {105106return err instanceof Error && !!err.isBoom && (!statusCode || err.output.statusCode === statusCode);107};108109110exports.boomify = function (err, options) {111112Hoek.assert(err instanceof Error, 'Cannot wrap non-Error object');113114options = options || {};115116if (options.data !== undefined) {117err.data = options.data;118}119120if (options.decorate) {121Object.assign(err, options.decorate);122}123124if (!err.isBoom) {125return internals.initialize(err, options.statusCode || 500, options.message);126}127128if (options.override === false || // Defaults to true129!options.statusCode && !options.message) {130131return err;132}133134return internals.initialize(err, options.statusCode || err.output.statusCode, options.message);135};136137138// 4xx Client Errors139140exports.badRequest = function (message, data) {141142return new exports.Boom(message, { statusCode: 400, data, ctor: exports.badRequest });143};144145146exports.unauthorized = function (message, scheme, attributes) { // Or (message, wwwAuthenticate[])147148const err = new exports.Boom(message, { statusCode: 401, ctor: exports.unauthorized });149150// function (message)151152if (!scheme) {153return err;154}155156// function (message, wwwAuthenticate[])157158if (typeof scheme !== 'string') {159err.output.headers['WWW-Authenticate'] = scheme.join(', ');160return err;161}162163// function (message, scheme, attributes)164165let wwwAuthenticate = `${scheme}`;166167if (attributes ||168message) {169170err.output.payload.attributes = {};171}172173if (attributes) {174if (typeof attributes === 'string') {175wwwAuthenticate += ' ' + Hoek.escapeHeaderAttribute(attributes);176err.output.payload.attributes = attributes;177}178else {179wwwAuthenticate += ' ' + Object.keys(attributes).map((name) => {180181let value = attributes[name];182if (value === null ||183value === undefined) {184185value = '';186}187188err.output.payload.attributes[name] = value;189return `${name}="${Hoek.escapeHeaderAttribute(value.toString())}"`;190})191.join(', ');192}193}194195if (message) {196if (attributes) {197wwwAuthenticate += ',';198}199200wwwAuthenticate += ` error="${Hoek.escapeHeaderAttribute(message)}"`;201err.output.payload.attributes.error = message;202}203else {204err.isMissing = true;205}206207err.output.headers['WWW-Authenticate'] = wwwAuthenticate;208return err;209};210211212exports.paymentRequired = function (message, data) {213214return new exports.Boom(message, { statusCode: 402, data, ctor: exports.paymentRequired });215};216217218exports.forbidden = function (message, data) {219220return new exports.Boom(message, { statusCode: 403, data, ctor: exports.forbidden });221};222223224exports.notFound = function (message, data) {225226return new exports.Boom(message, { statusCode: 404, data, ctor: exports.notFound });227};228229230exports.methodNotAllowed = function (message, data, allow) {231232const err = new exports.Boom(message, { statusCode: 405, data, ctor: exports.methodNotAllowed });233234if (typeof allow === 'string') {235allow = [allow];236}237238if (Array.isArray(allow)) {239err.output.headers.Allow = allow.join(', ');240}241242return err;243};244245246exports.notAcceptable = function (message, data) {247248return new exports.Boom(message, { statusCode: 406, data, ctor: exports.notAcceptable });249};250251252exports.proxyAuthRequired = function (message, data) {253254return new exports.Boom(message, { statusCode: 407, data, ctor: exports.proxyAuthRequired });255};256257258exports.clientTimeout = function (message, data) {259260return new exports.Boom(message, { statusCode: 408, data, ctor: exports.clientTimeout });261};262263264exports.conflict = function (message, data) {265266return new exports.Boom(message, { statusCode: 409, data, ctor: exports.conflict });267};268269270exports.resourceGone = function (message, data) {271272return new exports.Boom(message, { statusCode: 410, data, ctor: exports.resourceGone });273};274275276exports.lengthRequired = function (message, data) {277278return new exports.Boom(message, { statusCode: 411, data, ctor: exports.lengthRequired });279};280281282exports.preconditionFailed = function (message, data) {283284return new exports.Boom(message, { statusCode: 412, data, ctor: exports.preconditionFailed });285};286287288exports.entityTooLarge = function (message, data) {289290return new exports.Boom(message, { statusCode: 413, data, ctor: exports.entityTooLarge });291};292293294exports.uriTooLong = function (message, data) {295296return new exports.Boom(message, { statusCode: 414, data, ctor: exports.uriTooLong });297};298299300exports.unsupportedMediaType = function (message, data) {301302return new exports.Boom(message, { statusCode: 415, data, ctor: exports.unsupportedMediaType });303};304305306exports.rangeNotSatisfiable = function (message, data) {307308return new exports.Boom(message, { statusCode: 416, data, ctor: exports.rangeNotSatisfiable });309};310311312exports.expectationFailed = function (message, data) {313314return new exports.Boom(message, { statusCode: 417, data, ctor: exports.expectationFailed });315};316317318exports.teapot = function (message, data) {319320return new exports.Boom(message, { statusCode: 418, data, ctor: exports.teapot });321};322323324exports.badData = function (message, data) {325326return new exports.Boom(message, { statusCode: 422, data, ctor: exports.badData });327};328329330exports.locked = function (message, data) {331332return new exports.Boom(message, { statusCode: 423, data, ctor: exports.locked });333};334335336exports.failedDependency = function (message, data) {337338return new exports.Boom(message, { statusCode: 424, data, ctor: exports.failedDependency });339};340341exports.tooEarly = function (message, data) {342343return new exports.Boom(message, { statusCode: 425, data, ctor: exports.tooEarly });344};345346347exports.preconditionRequired = function (message, data) {348349return new exports.Boom(message, { statusCode: 428, data, ctor: exports.preconditionRequired });350};351352353exports.tooManyRequests = function (message, data) {354355return new exports.Boom(message, { statusCode: 429, data, ctor: exports.tooManyRequests });356};357358359exports.illegal = function (message, data) {360361return new exports.Boom(message, { statusCode: 451, data, ctor: exports.illegal });362};363364365// 5xx Server Errors366367exports.internal = function (message, data, statusCode = 500) {368369return internals.serverError(message, data, statusCode, exports.internal);370};371372373exports.notImplemented = function (message, data) {374375return internals.serverError(message, data, 501, exports.notImplemented);376};377378379exports.badGateway = function (message, data) {380381return internals.serverError(message, data, 502, exports.badGateway);382};383384385exports.serverUnavailable = function (message, data) {386387return internals.serverError(message, data, 503, exports.serverUnavailable);388};389390391exports.gatewayTimeout = function (message, data) {392393return internals.serverError(message, data, 504, exports.gatewayTimeout);394};395396397exports.badImplementation = function (message, data) {398399const err = internals.serverError(message, data, 500, exports.badImplementation);400err.isDeveloperError = true;401return err;402};403404405internals.initialize = function (err, statusCode, message) {406407const numberCode = parseInt(statusCode, 10);408Hoek.assert(!isNaN(numberCode) && numberCode >= 400, 'First argument must be a number (400+):', statusCode);409410err.isBoom = true;411err.isServer = numberCode >= 500;412413if (!err.hasOwnProperty('data')) {414err.data = null;415}416417err.output = {418statusCode: numberCode,419payload: {},420headers: {}421};422423Object.defineProperty(err, 'reformat', { value: internals.reformat, configurable: true });424425if (!message &&426!err.message) {427428err.reformat();429message = err.output.payload.error;430}431432if (message) {433const props = Object.getOwnPropertyDescriptor(err, 'message') || Object.getOwnPropertyDescriptor(Object.getPrototypeOf(err), 'message');434Hoek.assert(!props || props.configurable && !props.get, 'The error is not compatible with boom');435436err.message = message + (err.message ? ': ' + err.message : '');437err.output.payload.message = err.message;438}439440err.reformat();441return err;442};443444445internals.reformat = function (debug = false) {446447this.output.payload.statusCode = this.output.statusCode;448this.output.payload.error = internals.codes.get(this.output.statusCode) || 'Unknown';449450if (this.output.statusCode === 500 && debug !== true) {451this.output.payload.message = 'An internal server error occurred'; // Hide actual error from user452}453else if (this.message) {454this.output.payload.message = this.message;455}456};457458459internals.serverError = function (message, data, statusCode, ctor) {460461if (data instanceof Error &&462!data.isBoom) {463464return exports.boomify(data, { statusCode, message });465}466467return new exports.Boom(message, { statusCode, data, ctor });468};469470471