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.
		
		
		
		
		
			
		
			
				
					
					
						
							290 lines
						
					
					
						
							6.2 KiB
						
					
					
				
			
		
		
	
	
							290 lines
						
					
					
						
							6.2 KiB
						
					
					
				/*!
 | 
						|
 * 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
 | 
						|
}
 |