twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

index.js (3686B)


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