amis-rpc-design/node_modules/errorhandler/index.js

199 lines
4.5 KiB
JavaScript
Raw Normal View History

2023-10-07 19:42:30 +08:00
/*!
* errorhandler
* Copyright(c) 2010 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed
*/
'use strict'
/**
* Module dependencies.
* @private
*/
var accepts = require('accepts')
var escapeHtml = require('escape-html')
var fs = require('fs')
var path = require('path')
var util = require('util')
/**
* Module variables.
* @private
*/
var DOUBLE_SPACE_REGEXP = /\x20{2}/g
var NEW_LINE_REGEXP = /\n/g
var STYLESHEET = fs.readFileSync(path.join(__dirname, '/public/style.css'), 'utf8')
var TEMPLATE = fs.readFileSync(path.join(__dirname, '/public/error.html'), 'utf8')
var inspect = util.inspect
var toString = Object.prototype.toString
/* istanbul ignore next */
var defer = typeof setImmediate === 'function'
? setImmediate
: function (fn) { process.nextTick(fn.bind.apply(fn, arguments)) }
/**
* Error handler:
*
* Development error handler, providing stack traces
* and error message responses for requests accepting text, html,
* or json.
*
* Text:
*
* By default, and when _text/plain_ is accepted a simple stack trace
* or error message will be returned.
*
* JSON:
*
* When _application/json_ is accepted, connect will respond with
* an object in the form of `{ "error": error }`.
*
* HTML:
*
* When accepted connect will output a nice html stack trace.
*
* @return {Function}
* @api public
*/
exports = module.exports = function errorHandler (options) {
// get environment
var env = process.env.NODE_ENV || 'development'
// get options
var opts = options || {}
// get log option
var log = opts.log === undefined
? env !== 'test'
: opts.log
if (typeof log !== 'function' && typeof log !== 'boolean') {
throw new TypeError('option log must be function or boolean')
}
// default logging using console.error
if (log === true) {
log = logerror
}
return function errorHandler (err, req, res, next) {
// respect err.statusCode
if (err.statusCode) {
res.statusCode = err.statusCode
}
// respect err.status
if (err.status) {
res.statusCode = err.status
}
// default status code to 500
if (res.statusCode < 400) {
res.statusCode = 500
}
// log the error
var str = stringify(err)
if (log) {
defer(log, err, str, req, res)
}
// cannot actually respond
if (res._header) {
return req.socket.destroy()
}
// negotiate
var accept = accepts(req)
var type = accept.type('html', 'json', 'text')
// Security header for content sniffing
res.setHeader('X-Content-Type-Options', 'nosniff')
// html
if (type === 'html') {
var isInspect = !err.stack && String(err) === toString.call(err)
var errorHtml = !isInspect
? escapeHtmlBlock(str.split('\n', 1)[0] || 'Error')
: 'Error'
var stack = !isInspect
? String(str).split('\n').slice(1)
: [str]
var stackHtml = stack
.map(function (v) { return '<li>' + escapeHtmlBlock(v) + '</li>' })
.join('')
var body = TEMPLATE
.replace('{style}', STYLESHEET)
.replace('{stack}', stackHtml)
.replace('{title}', escapeHtml(exports.title))
.replace('{statusCode}', res.statusCode)
.replace(/\{error\}/g, errorHtml)
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(body)
// json
} else if (type === 'json') {
var error = { message: err.message, stack: err.stack }
for (var prop in err) error[prop] = err[prop]
var json = JSON.stringify({ error: error }, null, 2)
res.setHeader('Content-Type', 'application/json; charset=utf-8')
res.end(json)
// plain text
} else {
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end(str)
}
}
}
/**
* Template title, framework authors may override this value.
*/
exports.title = 'Connect'
/**
* Escape a block of HTML, preserving whitespace.
* @api private
*/
function escapeHtmlBlock (str) {
return escapeHtml(str)
.replace(DOUBLE_SPACE_REGEXP, ' &nbsp;')
.replace(NEW_LINE_REGEXP, '<br>')
}
/**
* Stringify a value.
* @api private
*/
function stringify (val) {
var stack = val.stack
if (stack) {
return String(stack)
}
var str = String(val)
return str === toString.call(val)
? inspect(val)
: str
}
/**
* Log error to console.
* @api private
*/
function logerror (err, str) {
console.error(str || err.stack)
}