index.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*!
  2. * on-headers
  3. * Copyright(c) 2014 Douglas Christopher Wilson
  4. * MIT Licensed
  5. */
  6. 'use strict'
  7. /**
  8. * Module exports.
  9. * @public
  10. */
  11. module.exports = onHeaders
  12. var http = require('http')
  13. // older node versions don't have appendHeader
  14. var isAppendHeaderSupported = typeof http.ServerResponse.prototype.appendHeader === 'function'
  15. var set1dArray = isAppendHeaderSupported ? set1dArrayWithAppend : set1dArrayWithSet
  16. /**
  17. * Create a replacement writeHead method.
  18. *
  19. * @param {function} prevWriteHead
  20. * @param {function} listener
  21. * @private
  22. */
  23. function createWriteHead (prevWriteHead, listener) {
  24. var fired = false
  25. // return function with core name and argument list
  26. return function writeHead (statusCode) {
  27. // set headers from arguments
  28. var args = setWriteHeadHeaders.apply(this, arguments)
  29. // fire listener
  30. if (!fired) {
  31. fired = true
  32. listener.call(this)
  33. // pass-along an updated status code
  34. if (typeof args[0] === 'number' && this.statusCode !== args[0]) {
  35. args[0] = this.statusCode
  36. args.length = 1
  37. }
  38. }
  39. return prevWriteHead.apply(this, args)
  40. }
  41. }
  42. /**
  43. * Execute a listener when a response is about to write headers.
  44. *
  45. * @param {object} res
  46. * @return {function} listener
  47. * @public
  48. */
  49. function onHeaders (res, listener) {
  50. if (!res) {
  51. throw new TypeError('argument res is required')
  52. }
  53. if (typeof listener !== 'function') {
  54. throw new TypeError('argument listener must be a function')
  55. }
  56. res.writeHead = createWriteHead(res.writeHead, listener)
  57. }
  58. /**
  59. * Set headers contained in array on the response object.
  60. *
  61. * @param {object} res
  62. * @param {array} headers
  63. * @private
  64. */
  65. function setHeadersFromArray (res, headers) {
  66. if (headers.length && Array.isArray(headers[0])) {
  67. // 2D
  68. set2dArray(res, headers)
  69. } else {
  70. // 1D
  71. if (headers.length % 2 !== 0) {
  72. throw new TypeError('headers array is malformed')
  73. }
  74. set1dArray(res, headers)
  75. }
  76. }
  77. /**
  78. * Set headers contained in object on the response object.
  79. *
  80. * @param {object} res
  81. * @param {object} headers
  82. * @private
  83. */
  84. function setHeadersFromObject (res, headers) {
  85. var keys = Object.keys(headers)
  86. for (var i = 0; i < keys.length; i++) {
  87. var k = keys[i]
  88. if (k) res.setHeader(k, headers[k])
  89. }
  90. }
  91. /**
  92. * Set headers and other properties on the response object.
  93. *
  94. * @param {number} statusCode
  95. * @private
  96. */
  97. function setWriteHeadHeaders (statusCode) {
  98. var length = arguments.length
  99. var headerIndex = length > 1 && typeof arguments[1] === 'string'
  100. ? 2
  101. : 1
  102. var headers = length >= headerIndex + 1
  103. ? arguments[headerIndex]
  104. : undefined
  105. this.statusCode = statusCode
  106. if (Array.isArray(headers)) {
  107. // handle array case
  108. setHeadersFromArray(this, headers)
  109. } else if (headers) {
  110. // handle object case
  111. setHeadersFromObject(this, headers)
  112. }
  113. // copy leading arguments
  114. var args = new Array(Math.min(length, headerIndex))
  115. for (var i = 0; i < args.length; i++) {
  116. args[i] = arguments[i]
  117. }
  118. return args
  119. }
  120. function set2dArray (res, headers) {
  121. var key
  122. for (var i = 0; i < headers.length; i++) {
  123. key = headers[i][0]
  124. if (key) {
  125. res.setHeader(key, headers[i][1])
  126. }
  127. }
  128. }
  129. function set1dArrayWithAppend (res, headers) {
  130. for (var i = 0; i < headers.length; i += 2) {
  131. res.removeHeader(headers[i])
  132. }
  133. var key
  134. for (var j = 0; j < headers.length; j += 2) {
  135. key = headers[j]
  136. if (key) {
  137. res.appendHeader(key, headers[j + 1])
  138. }
  139. }
  140. }
  141. function set1dArrayWithSet (res, headers) {
  142. var key
  143. for (var i = 0; i < headers.length; i += 2) {
  144. key = headers[i]
  145. if (key) {
  146. res.setHeader(key, headers[i + 1])
  147. }
  148. }
  149. }