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