index.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /*!
  2. * finalhandler
  3. * Copyright(c) 2014-2015 Douglas Christopher Wilson
  4. * MIT Licensed
  5. */
  6. 'use strict'
  7. /**
  8. * Module dependencies.
  9. * @private
  10. */
  11. var debug = require('debug')('finalhandler')
  12. var escapeHtml = require('escape-html')
  13. var http = require('http')
  14. var onFinished = require('on-finished')
  15. var unpipe = require('unpipe')
  16. /**
  17. * Module variables.
  18. * @private
  19. */
  20. /* istanbul ignore next */
  21. var defer = typeof setImmediate === 'function'
  22. ? setImmediate
  23. : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
  24. var isFinished = onFinished.isFinished
  25. /**
  26. * Module exports.
  27. * @public
  28. */
  29. module.exports = finalhandler
  30. /**
  31. * Create a function to handle the final response.
  32. *
  33. * @param {Request} req
  34. * @param {Response} res
  35. * @param {Object} [options]
  36. * @return {Function}
  37. * @public
  38. */
  39. function finalhandler(req, res, options) {
  40. var opts = options || {}
  41. // get environment
  42. var env = opts.env || process.env.NODE_ENV || 'development'
  43. // get error callback
  44. var onerror = opts.onerror
  45. return function (err) {
  46. var status = res.statusCode
  47. // ignore 404 on in-flight response
  48. if (!err && res._header) {
  49. debug('cannot 404 after headers sent')
  50. return
  51. }
  52. // unhandled error
  53. if (err) {
  54. // respect err.statusCode
  55. if (err.statusCode) {
  56. status = err.statusCode
  57. }
  58. // respect err.status
  59. if (err.status) {
  60. status = err.status
  61. }
  62. // default status code to 500
  63. if (!status || status < 400) {
  64. status = 500
  65. }
  66. // production gets a basic error message
  67. var msg = env === 'production'
  68. ? http.STATUS_CODES[status]
  69. : err.stack || err.toString()
  70. msg = escapeHtml(msg)
  71. .replace(/\n/g, '<br>')
  72. .replace(/ /g, ' &nbsp;') + '\n'
  73. } else {
  74. status = 404
  75. msg = 'Cannot ' + escapeHtml(req.method) + ' ' + escapeHtml(req.originalUrl || req.url) + '\n'
  76. }
  77. debug('default %s', status)
  78. // schedule onerror callback
  79. if (err && onerror) {
  80. defer(onerror, err, req, res)
  81. }
  82. // cannot actually respond
  83. if (res._header) {
  84. return req.socket.destroy()
  85. }
  86. send(req, res, status, msg)
  87. }
  88. }
  89. /**
  90. * Send response.
  91. *
  92. * @param {IncomingMessage} req
  93. * @param {OutgoingMessage} res
  94. * @param {number} status
  95. * @param {string} body
  96. * @private
  97. */
  98. function send(req, res, status, body) {
  99. function write() {
  100. res.statusCode = status
  101. // security header for content sniffing
  102. res.setHeader('X-Content-Type-Options', 'nosniff')
  103. // standard headers
  104. res.setHeader('Content-Type', 'text/html; charset=utf-8')
  105. res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'))
  106. if (req.method === 'HEAD') {
  107. res.end()
  108. return
  109. }
  110. res.end(body, 'utf8')
  111. }
  112. if (isFinished(req)) {
  113. write()
  114. return
  115. }
  116. // unpipe everything from the request
  117. unpipe(req)
  118. // flush the request
  119. onFinished(req, write)
  120. req.resume()
  121. }