|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
'use strict' |
|
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = onFinished |
|
module.exports.isFinished = isFinished |
|
|
|
|
|
|
|
|
|
|
|
|
|
var asyncHooks = tryRequireAsyncHooks() |
|
var first = require('ee-first') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var defer = typeof setImmediate === 'function' |
|
? setImmediate |
|
: function (fn) { process.nextTick(fn.bind.apply(fn, arguments)) } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function onFinished (msg, listener) { |
|
if (isFinished(msg) !== false) { |
|
defer(listener, null, msg) |
|
return msg |
|
} |
|
|
|
|
|
attachListener(msg, wrap(listener)) |
|
|
|
return msg |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isFinished (msg) { |
|
var socket = msg.socket |
|
|
|
if (typeof msg.finished === 'boolean') { |
|
|
|
return Boolean(msg.finished || (socket && !socket.writable)) |
|
} |
|
|
|
if (typeof msg.complete === 'boolean') { |
|
|
|
return Boolean(msg.upgrade || !socket || !socket.readable || (msg.complete && !msg.readable)) |
|
} |
|
|
|
|
|
return undefined |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function attachFinishedListener (msg, callback) { |
|
var eeMsg |
|
var eeSocket |
|
var finished = false |
|
|
|
function onFinish (error) { |
|
eeMsg.cancel() |
|
eeSocket.cancel() |
|
|
|
finished = true |
|
callback(error) |
|
} |
|
|
|
|
|
eeMsg = eeSocket = first([[msg, 'end', 'finish']], onFinish) |
|
|
|
function onSocket (socket) { |
|
|
|
msg.removeListener('socket', onSocket) |
|
|
|
if (finished) return |
|
if (eeMsg !== eeSocket) return |
|
|
|
|
|
eeSocket = first([[socket, 'error', 'close']], onFinish) |
|
} |
|
|
|
if (msg.socket) { |
|
|
|
onSocket(msg.socket) |
|
return |
|
} |
|
|
|
|
|
msg.on('socket', onSocket) |
|
|
|
if (msg.socket === undefined) { |
|
|
|
patchAssignSocket(msg, onSocket) |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function attachListener (msg, listener) { |
|
var attached = msg.__onFinished |
|
|
|
|
|
if (!attached || !attached.queue) { |
|
attached = msg.__onFinished = createListener(msg) |
|
attachFinishedListener(msg, attached) |
|
} |
|
|
|
attached.queue.push(listener) |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function createListener (msg) { |
|
function listener (err) { |
|
if (msg.__onFinished === listener) msg.__onFinished = null |
|
if (!listener.queue) return |
|
|
|
var queue = listener.queue |
|
listener.queue = null |
|
|
|
for (var i = 0; i < queue.length; i++) { |
|
queue[i](err, msg) |
|
} |
|
} |
|
|
|
listener.queue = [] |
|
|
|
return listener |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function patchAssignSocket (res, callback) { |
|
var assignSocket = res.assignSocket |
|
|
|
if (typeof assignSocket !== 'function') return |
|
|
|
|
|
res.assignSocket = function _assignSocket (socket) { |
|
assignSocket.call(this, socket) |
|
callback(socket) |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function tryRequireAsyncHooks () { |
|
try { |
|
return require('async_hooks') |
|
} catch (e) { |
|
return {} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function wrap (fn) { |
|
var res |
|
|
|
|
|
if (asyncHooks.AsyncResource) { |
|
res = new asyncHooks.AsyncResource(fn.name || 'bound-anonymous-fn') |
|
} |
|
|
|
|
|
if (!res || !res.runInAsyncScope) { |
|
return fn |
|
} |
|
|
|
|
|
return res.runInAsyncScope.bind(res, fn, null) |
|
} |
|
|