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.

191 lines
3.5 KiB

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