|
|
/*! * http-errors * Copyright(c) 2014 Jonathan Ong * Copyright(c) 2016 Douglas Christopher Wilson * MIT Licensed */
'use strict'
/** * Module dependencies. * @private */
var deprecate = require('depd')('http-errors') var setPrototypeOf = require('setprototypeof') var statuses = require('statuses') var inherits = require('inherits') var toIdentifier = require('toidentifier')
/** * Module exports. * @public */
module.exports = createError module.exports.HttpError = createHttpErrorConstructor() module.exports.isHttpError = createIsHttpErrorFunction(module.exports.HttpError)
// Populate exports for all constructors
populateConstructorExports(module.exports, statuses.codes, module.exports.HttpError)
/** * Get the code class of a status code. * @private */
function codeClass (status) { return Number(String(status).charAt(0) + '00') }
/** * Create a new HTTP Error. * * @returns {Error} * @public */
function createError () { // so much arity going on ~_~
var err var msg var status = 500 var props = {} for (var i = 0; i < arguments.length; i++) { var arg = arguments[i] var type = typeof arg if (type === 'object' && arg instanceof Error) { err = arg status = err.status || err.statusCode || status } else if (type === 'number' && i === 0) { status = arg } else if (type === 'string') { msg = arg } else if (type === 'object') { props = arg } else { throw new TypeError('argument #' + (i + 1) + ' unsupported type ' + type) } }
if (typeof status === 'number' && (status < 400 || status >= 600)) { deprecate('non-error status code; use only 4xx or 5xx status codes') }
if (typeof status !== 'number' || (!statuses.message[status] && (status < 400 || status >= 600))) { status = 500 }
// constructor
var HttpError = createError[status] || createError[codeClass(status)]
if (!err) { // create error
err = HttpError ? new HttpError(msg) : new Error(msg || statuses.message[status]) Error.captureStackTrace(err, createError) }
if (!HttpError || !(err instanceof HttpError) || err.status !== status) { // add properties to generic error
err.expose = status < 500 err.status = err.statusCode = status }
for (var key in props) { if (key !== 'status' && key !== 'statusCode') { err[key] = props[key] } }
return err }
/** * Create HTTP error abstract base class. * @private */
function createHttpErrorConstructor () { function HttpError () { throw new TypeError('cannot construct abstract class') }
inherits(HttpError, Error)
return HttpError }
/** * Create a constructor for a client error. * @private */
function createClientErrorConstructor (HttpError, name, code) { var className = toClassName(name)
function ClientError (message) { // create the error object
var msg = message != null ? message : statuses.message[code] var err = new Error(msg)
// capture a stack trace to the construction point
Error.captureStackTrace(err, ClientError)
// adjust the [[Prototype]]
setPrototypeOf(err, ClientError.prototype)
// redefine the error message
Object.defineProperty(err, 'message', { enumerable: true, configurable: true, value: msg, writable: true })
// redefine the error name
Object.defineProperty(err, 'name', { enumerable: false, configurable: true, value: className, writable: true })
return err }
inherits(ClientError, HttpError) nameFunc(ClientError, className)
ClientError.prototype.status = code ClientError.prototype.statusCode = code ClientError.prototype.expose = true
return ClientError }
/** * Create function to test is a value is a HttpError. * @private */
function createIsHttpErrorFunction (HttpError) { return function isHttpError (val) { if (!val || typeof val !== 'object') { return false }
if (val instanceof HttpError) { return true }
return val instanceof Error && typeof val.expose === 'boolean' && typeof val.statusCode === 'number' && val.status === val.statusCode } }
/** * Create a constructor for a server error. * @private */
function createServerErrorConstructor (HttpError, name, code) { var className = toClassName(name)
function ServerError (message) { // create the error object
var msg = message != null ? message : statuses.message[code] var err = new Error(msg)
// capture a stack trace to the construction point
Error.captureStackTrace(err, ServerError)
// adjust the [[Prototype]]
setPrototypeOf(err, ServerError.prototype)
// redefine the error message
Object.defineProperty(err, 'message', { enumerable: true, configurable: true, value: msg, writable: true })
// redefine the error name
Object.defineProperty(err, 'name', { enumerable: false, configurable: true, value: className, writable: true })
return err }
inherits(ServerError, HttpError) nameFunc(ServerError, className)
ServerError.prototype.status = code ServerError.prototype.statusCode = code ServerError.prototype.expose = false
return ServerError }
/** * Set the name of a function, if possible. * @private */
function nameFunc (func, name) { var desc = Object.getOwnPropertyDescriptor(func, 'name')
if (desc && desc.configurable) { desc.value = name Object.defineProperty(func, 'name', desc) } }
/** * Populate the exports object with constructors for every error class. * @private */
function populateConstructorExports (exports, codes, HttpError) { codes.forEach(function forEachCode (code) { var CodeError var name = toIdentifier(statuses.message[code])
switch (codeClass(code)) { case 400: CodeError = createClientErrorConstructor(HttpError, name, code) break case 500: CodeError = createServerErrorConstructor(HttpError, name, code) break }
if (CodeError) { // export the constructor
exports[code] = CodeError exports[name] = CodeError } }) }
/** * Get a class name from a name identifier. * @private */
function toClassName (name) { return name.substr(-5) !== 'Error' ? name + 'Error' : name }
|