Initial commit
[yaffs-website] / node_modules / on-finished / index.js
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 }