123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- /*!
- * finalhandler
- * Copyright(c) 2014-2015 Douglas Christopher Wilson
- * MIT Licensed
- */
- 'use strict'
- /**
- * Module dependencies.
- * @private
- */
- var debug = require('debug')('finalhandler')
- var escapeHtml = require('escape-html')
- var http = require('http')
- var onFinished = require('on-finished')
- var unpipe = require('unpipe')
- /**
- * Module variables.
- * @private
- */
- /* istanbul ignore next */
- var defer = typeof setImmediate === 'function'
- ? setImmediate
- : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
- var isFinished = onFinished.isFinished
- /**
- * Module exports.
- * @public
- */
- module.exports = finalhandler
- /**
- * Create a function to handle the final response.
- *
- * @param {Request} req
- * @param {Response} res
- * @param {Object} [options]
- * @return {Function}
- * @public
- */
- function finalhandler(req, res, options) {
- var opts = options || {}
- // get environment
- var env = opts.env || process.env.NODE_ENV || 'development'
- // get error callback
- var onerror = opts.onerror
- return function (err) {
- var status = res.statusCode
- // ignore 404 on in-flight response
- if (!err && res._header) {
- debug('cannot 404 after headers sent')
- return
- }
- // unhandled error
- if (err) {
- // respect err.statusCode
- if (err.statusCode) {
- status = err.statusCode
- }
- // respect err.status
- if (err.status) {
- status = err.status
- }
- // default status code to 500
- if (!status || status < 400) {
- status = 500
- }
- // production gets a basic error message
- var msg = env === 'production'
- ? http.STATUS_CODES[status]
- : err.stack || err.toString()
- msg = escapeHtml(msg)
- .replace(/\n/g, '<br>')
- .replace(/ /g, ' ') + '\n'
- } else {
- status = 404
- msg = 'Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl || req.url) + '\n'
- }
- debug('default %s', status)
- // schedule onerror callback
- if (err && onerror) {
- defer(onerror, err, req, res)
- }
- // cannot actually respond
- if (res._header) {
- return req.socket.destroy()
- }
- send(req, res, status, msg)
- }
- }
- /**
- * Send response.
- *
- * @param {IncomingMessage} req
- * @param {OutgoingMessage} res
- * @param {number} status
- * @param {string} body
- * @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()
- }
|