You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

196 lines
3.6 KiB

4 years ago
  1. /*!
  2. * on-finished
  3. * Copyright(c) 2013 Jonathan Ong
  4. * Copyright(c) 2014 Douglas Christopher Wilson
  5. * MIT Licensed
  6. */
  7. 'use strict'
  8. /**
  9. * Module exports.
  10. * @public
  11. */
  12. module.exports = onFinished
  13. module.exports.isFinished = isFinished
  14. /**
  15. * Module dependencies.
  16. * @private
  17. */
  18. var first = require('ee-first')
  19. /**
  20. * Variables.
  21. * @private
  22. */
  23. /* istanbul ignore next */
  24. var defer = typeof setImmediate === 'function'
  25. ? setImmediate
  26. : function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
  27. /**
  28. * Invoke callback when the response has finished, useful for
  29. * cleaning up resources afterwards.
  30. *
  31. * @param {object} msg
  32. * @param {function} listener
  33. * @return {object}
  34. * @public
  35. */
  36. function onFinished(msg, listener) {
  37. if (isFinished(msg) !== false) {
  38. defer(listener, null, msg)
  39. return msg
  40. }
  41. // attach the listener to the message
  42. attachListener(msg, listener)
  43. return msg
  44. }
  45. /**
  46. * Determine if message is already finished.
  47. *
  48. * @param {object} msg
  49. * @return {boolean}
  50. * @public
  51. */
  52. function isFinished(msg) {
  53. var socket = msg.socket
  54. if (typeof msg.finished === 'boolean') {
  55. // OutgoingMessage
  56. return Boolean(msg.finished || (socket && !socket.writable))
  57. }
  58. if (typeof msg.complete === 'boolean') {
  59. // IncomingMessage
  60. return Boolean(msg.upgrade || !socket || !socket.readable || (msg.complete && !msg.readable))
  61. }
  62. // don't know
  63. return undefined
  64. }
  65. /**
  66. * Attach a finished listener to the message.
  67. *
  68. * @param {object} msg
  69. * @param {function} callback
  70. * @private
  71. */
  72. function attachFinishedListener(msg, callback) {
  73. var eeMsg
  74. var eeSocket
  75. var finished = false
  76. function onFinish(error) {
  77. eeMsg.cancel()
  78. eeSocket.cancel()
  79. finished = true
  80. callback(error)
  81. }
  82. // finished on first message event
  83. eeMsg = eeSocket = first([[msg, 'end', 'finish']], onFinish)
  84. function onSocket(socket) {
  85. // remove listener
  86. msg.removeListener('socket', onSocket)
  87. if (finished) return
  88. if (eeMsg !== eeSocket) return
  89. // finished on first socket event
  90. eeSocket = first([[socket, 'error', 'close']], onFinish)
  91. }
  92. if (msg.socket) {
  93. // socket already assigned
  94. onSocket(msg.socket)
  95. return
  96. }
  97. // wait for socket to be assigned
  98. msg.on('socket', onSocket)
  99. if (msg.socket === undefined) {
  100. // node.js 0.8 patch
  101. patchAssignSocket(msg, onSocket)
  102. }
  103. }
  104. /**
  105. * Attach the listener to the message.
  106. *
  107. * @param {object} msg
  108. * @return {function}
  109. * @private
  110. */
  111. function attachListener(msg, listener) {
  112. var attached = msg.__onFinished
  113. // create a private single listener with queue
  114. if (!attached || !attached.queue) {
  115. attached = msg.__onFinished = createListener(msg)
  116. attachFinishedListener(msg, attached)
  117. }
  118. attached.queue.push(listener)
  119. }
  120. /**
  121. * Create listener on message.
  122. *
  123. * @param {object} msg
  124. * @return {function}
  125. * @private
  126. */
  127. function createListener(msg) {
  128. function listener(err) {
  129. if (msg.__onFinished === listener) msg.__onFinished = null
  130. if (!listener.queue) return
  131. var queue = listener.queue
  132. listener.queue = null
  133. for (var i = 0; i < queue.length; i++) {
  134. queue[i](err, msg)
  135. }
  136. }
  137. listener.queue = []
  138. return listener
  139. }
  140. /**
  141. * Patch ServerResponse.prototype.assignSocket for node.js 0.8.
  142. *
  143. * @param {ServerResponse} res
  144. * @param {function} callback
  145. * @private
  146. */
  147. function patchAssignSocket(res, callback) {
  148. var assignSocket = res.assignSocket
  149. if (typeof assignSocket !== 'function') return
  150. // res.on('socket', callback) is broken in 0.8
  151. res.assignSocket = function _assignSocket(socket) {
  152. assignSocket.call(this, socket)
  153. callback(socket)
  154. }
  155. }