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
			| 
											3 years ago
										 | /*! | ||
|  |  * 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 | ||
|  | } |