3 * Copyright(c) 2014-2015 Douglas Christopher Wilson
10 * Module dependencies.
14 var debug = require('debug')('finalhandler')
15 var escapeHtml = require('escape-html')
16 var http = require('http')
17 var onFinished = require('on-finished')
18 var unpipe = require('unpipe')
25 /* istanbul ignore next */
26 var defer = typeof setImmediate === 'function'
28 : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
29 var isFinished = onFinished.isFinished
36 module.exports = finalhandler
39 * Create a function to handle the final response.
41 * @param {Request} req
42 * @param {Response} res
43 * @param {Object} [options]
48 function finalhandler(req, res, options) {
49 var opts = options || {}
52 var env = opts.env || process.env.NODE_ENV || 'development'
55 var onerror = opts.onerror
57 return function (err) {
58 var status = res.statusCode
60 // ignore 404 on in-flight response
61 if (!err && res._header) {
62 debug('cannot 404 after headers sent')
68 // respect err.statusCode
70 status = err.statusCode
78 // default status code to 500
79 if (!status || status < 400) {
83 // production gets a basic error message
84 var msg = env === 'production'
85 ? http.STATUS_CODES[status]
86 : err.stack || err.toString()
88 .replace(/\n/g, '<br>')
89 .replace(/ /g, ' ') + '\n'
92 msg = 'Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl || req.url) + '\n'
95 debug('default %s', status)
97 // schedule onerror callback
99 defer(onerror, err, req, res)
102 // cannot actually respond
104 return req.socket.destroy()
107 send(req, res, status, msg)
114 * @param {IncomingMessage} req
115 * @param {OutgoingMessage} res
116 * @param {number} status
117 * @param {string} body
121 function send(req, res, status, body) {
123 res.statusCode = status
125 // security header for content sniffing
126 res.setHeader('X-Content-Type-Options', 'nosniff')
129 res.setHeader('Content-Type', 'text/html; charset=utf-8')
130 res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'))
132 if (req.method === 'HEAD') {
137 res.end(body, 'utf8')
140 if (isFinished(req)) {
145 // unpipe everything from the request
149 onFinished(req, write)