opusdev/vector-similarity-api
1
1/*!2 * on-finished3 * Copyright(c) 2013 Jonathan Ong4 * Copyright(c) 2014 Douglas Christopher Wilson5 * MIT Licensed6 */7 8'use strict'9 10/**11 * Module exports.12 * @public13 */14 15module.exports = onFinished16module.exports.isFinished = isFinished17 18/**19 * Module dependencies.20 * @private21 */22 23var asyncHooks = tryRequireAsyncHooks()24var first = require('ee-first')25 26/**27 * Variables.28 * @private29 */30 31/* istanbul ignore next */32var defer = typeof setImmediate === 'function'33 ? setImmediate34 : function (fn) { process.nextTick(fn.bind.apply(fn, arguments)) }35 36/**37 * Invoke callback when the response has finished, useful for38 * cleaning up resources afterwards.39 *40 * @param {object} msg41 * @param {function} listener42 * @return {object}43 * @public44 */45 46function onFinished (msg, listener) {47 if (isFinished(msg) !== false) {48 defer(listener, null, msg)49 return msg50 }51 52 // attach the listener to the message53 attachListener(msg, wrap(listener))54 55 return msg56}57 58/**59 * Determine if message is already finished.60 *61 * @param {object} msg62 * @return {boolean}63 * @public64 */65 66function isFinished (msg) {67 var socket = msg.socket68 69 if (typeof msg.finished === 'boolean') {70 // OutgoingMessage71 return Boolean(msg.finished || (socket && !socket.writable))72 }73 74 if (typeof msg.complete === 'boolean') {75 // IncomingMessage76 return Boolean(msg.upgrade || !socket || !socket.readable || (msg.complete && !msg.readable))77 }78 79 // don't know80 return undefined81}82 83/**84 * Attach a finished listener to the message.85 *86 * @param {object} msg87 * @param {function} callback88 * @private89 */90 91function attachFinishedListener (msg, callback) {92 var eeMsg93 var eeSocket94 var finished = false95 96 function onFinish (error) {97 eeMsg.cancel()98 eeSocket.cancel()99 100 finished = true101 callback(error)102 }103 104 // finished on first message event105 eeMsg = eeSocket = first([[msg, 'end', 'finish']], onFinish)106 107 function onSocket (socket) {108 // remove listener109 msg.removeListener('socket', onSocket)110 111 if (finished) return112 if (eeMsg !== eeSocket) return113 114 // finished on first socket event115 eeSocket = first([[socket, 'error', 'close']], onFinish)116 }117 118 if (msg.socket) {119 // socket already assigned120 onSocket(msg.socket)121 return122 }123 124 // wait for socket to be assigned125 msg.on('socket', onSocket)126 127 if (msg.socket === undefined) {128 // istanbul ignore next: node.js 0.8 patch129 patchAssignSocket(msg, onSocket)130 }131}132 133/**134 * Attach the listener to the message.135 *136 * @param {object} msg137 * @return {function}138 * @private139 */140 141function attachListener (msg, listener) {142 var attached = msg.__onFinished143 144 // create a private single listener with queue145 if (!attached || !attached.queue) {146 attached = msg.__onFinished = createListener(msg)147 attachFinishedListener(msg, attached)148 }149 150 attached.queue.push(listener)151}152 153/**154 * Create listener on message.155 *156 * @param {object} msg157 * @return {function}158 * @private159 */160 161function createListener (msg) {162 function listener (err) {163 if (msg.__onFinished === listener) msg.__onFinished = null164 if (!listener.queue) return165 166 var queue = listener.queue167 listener.queue = null168 169 for (var i = 0; i < queue.length; i++) {170 queue[i](err, msg)171 }172 }173 174 listener.queue = []175 176 return listener177}178 179/**180 * Patch ServerResponse.prototype.assignSocket for node.js 0.8.181 *182 * @param {ServerResponse} res183 * @param {function} callback184 * @private185 */186 187// istanbul ignore next: node.js 0.8 patch188function patchAssignSocket (res, callback) {189 var assignSocket = res.assignSocket190 191 if (typeof assignSocket !== 'function') return192 193 // res.on('socket', callback) is broken in 0.8194 res.assignSocket = function _assignSocket (socket) {195 assignSocket.call(this, socket)196 callback(socket)197 }198}199 200/**201 * Try to require async_hooks202 * @private203 */204 205function tryRequireAsyncHooks () {206 try {207 return require('async_hooks')208 } catch (e) {209 return {}210 }211}212 213/**214 * Wrap function with async resource, if possible.215 * AsyncResource.bind static method backported.216 * @private217 */218 219function wrap (fn) {220 var res221 222 // create anonymous resource223 if (asyncHooks.AsyncResource) {224 res = new asyncHooks.AsyncResource(fn.name || 'bound-anonymous-fn')225 }226 227 // incompatible node.js228 if (!res || !res.runInAsyncScope) {229 return fn230 }231 232 // return bound function233 return res.runInAsyncScope.bind(res, fn, null)234}235 