+++ /dev/null
-/*!
- * finalhandler
- * Copyright(c) 2014 Douglas Christopher Wilson
- * MIT Licensed
- */
-
-/**
- * Module dependencies.
- */
-
-var debug = require('debug')('finalhandler')
-var escapeHtml = require('escape-html')
-var http = require('http')
-var onFinished = require('on-finished')
-
-/**
- * Variables.
- */
-
-/* istanbul ignore next */
-var defer = typeof setImmediate === 'function'
- ? setImmediate
- : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
-var isFinished = onFinished.isFinished
-
-/**
- * Module exports.
- */
-
-module.exports = finalhandler
-
-/**
- * Final handler:
- *
- * @param {Request} req
- * @param {Response} res
- * @param {Object} [options]
- * @return {Function}
- * @api public
- */
-
-function finalhandler(req, res, options) {
- options = options || {}
-
- // get environment
- var env = options.env || process.env.NODE_ENV || 'development'
-
- // get error callback
- var onerror = options.onerror
-
- return function (err) {
- var msg
-
- // ignore 404 on in-flight response
- if (!err && res._header) {
- debug('cannot 404 after headers sent')
- return
- }
-
- // unhandled error
- if (err) {
- // default status code to 500
- if (!res.statusCode || res.statusCode < 400) {
- res.statusCode = 500
- }
-
- // respect err.status
- if (err.status) {
- res.statusCode = err.status
- }
-
- // production gets a basic error message
- var msg = env === 'production'
- ? http.STATUS_CODES[res.statusCode]
- : err.stack || err.toString()
- msg = escapeHtml(msg)
- .replace(/\n/g, '<br>')
- .replace(/ /g, ' ') + '\n'
- } else {
- res.statusCode = 404
- msg = 'Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl || req.url) + '\n'
- }
-
- debug('default %s', res.statusCode)
-
- // schedule onerror callback
- if (err && onerror) {
- defer(onerror, err, req, res)
- }
-
- // cannot actually respond
- if (res._header) {
- return req.socket.destroy()
- }
-
- send(req, res, res.statusCode, msg)
- }
-}
-
-/**
- * Send response.
- *
- * @param {IncomingMessage} req
- * @param {OutgoingMessage} res
- * @param {number} status
- * @param {string} body
- * @api private
- */
-
-function send(req, res, status, body) {
- function write() {
- res.statusCode = status
-
- // security header for content sniffing
- res.setHeader('X-Content-Type-Options', 'nosniff')
-
- // standard headers
- res.setHeader('Content-Type', 'text/html; charset=utf-8')
- res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'))
-
- if (req.method === 'HEAD') {
- res.end()
- return
- }
-
- res.end(body, 'utf8')
- }
-
- if (isFinished(req)) {
- write()
- return
- }
-
- // unpipe everything from the request
- unpipe(req)
-
- // flush the request
- onFinished(req, write)
- req.resume()
-}
-
-/**
- * Unpipe everything from a stream.
- *
- * @param {Object} stream
- * @api private
- */
-
-/* istanbul ignore next: implementation differs between versions */
-function unpipe(stream) {
- if (typeof stream.unpipe === 'function') {
- // new-style
- stream.unpipe()
- return
- }
-
- // Node.js 0.8 hack
- var listener
- var listeners = stream.listeners('close')
-
- for (var i = 0; i < listeners.length; i++) {
- listener = listeners[i]
-
- if (listener.name !== 'cleanup' && listener.name !== 'onclose') {
- continue
- }
-
- // invoke the listener
- listener.call(stream)
- }
-}