Mercurial > hg > nodescore
comparison node_modules/socket.io/lib/namespace.js @ 76:0ae87af84e2f
added oscgroups
author | Rob Canning <rob@foo.net> |
---|---|
date | Sun, 13 Jul 2014 10:07:41 +0100 |
parents | 333afcfd3f3a |
children |
comparison
equal
deleted
inserted
replaced
75:3a2845e3156e | 76:0ae87af84e2f |
---|---|
1 | |
1 /** | 2 /** |
2 * Module dependencies. | 3 * Module dependencies. |
3 */ | 4 */ |
4 | 5 |
5 var Socket = require('./socket') | 6 var Socket = require('./socket'); |
6 , EventEmitter = process.EventEmitter | 7 var Emitter = require('events').EventEmitter; |
7 , parser = require('./parser') | 8 var parser = require('socket.io-parser'); |
8 , util = require('./util'); | 9 var debug = require('debug')('socket.io:namespace'); |
9 | 10 var hasBin = require('has-binary-data'); |
10 /** | 11 |
11 * Exports the constructor. | 12 /** |
12 */ | 13 * Module exports. |
13 | 14 */ |
14 exports = module.exports = SocketNamespace; | 15 |
15 | 16 module.exports = exports = Namespace; |
16 /** | 17 |
17 * Constructor. | 18 /** |
18 * | 19 * Blacklisted events. |
19 * @api public. | 20 */ |
20 */ | 21 |
21 | 22 exports.events = [ |
22 function SocketNamespace (mgr, name) { | 23 'connect', // for symmetry with client |
23 this.manager = mgr; | 24 'connection', |
24 this.name = name || ''; | 25 'newListener' |
25 this.sockets = {}; | 26 ]; |
26 this.auth = false; | 27 |
27 this.setFlags(); | 28 /** |
28 }; | 29 * Flags. |
29 | 30 */ |
30 /** | 31 |
31 * Inherits from EventEmitter. | 32 exports.flags = ['json']; |
32 */ | 33 |
33 | 34 /** |
34 SocketNamespace.prototype.__proto__ = EventEmitter.prototype; | 35 * `EventEmitter#emit` reference. |
35 | 36 */ |
36 /** | 37 |
37 * Copies emit since we override it. | 38 var emit = Emitter.prototype.emit; |
38 * | 39 |
39 * @api private | 40 /** |
40 */ | 41 * Namespace constructor. |
41 | 42 * |
42 SocketNamespace.prototype.$emit = EventEmitter.prototype.emit; | 43 * @param {Server} server instance |
43 | 44 * @param {Socket} name |
44 /** | 45 * @api private |
45 * Retrieves all clients as Socket instances as an array. | 46 */ |
46 * | 47 |
47 * @api public | 48 function Namespace(server, name){ |
48 */ | 49 this.name = name; |
49 | 50 this.server = server; |
50 SocketNamespace.prototype.clients = function (room) { | 51 this.sockets = []; |
51 var room = this.name + (room !== undefined ? | 52 this.connected = {}; |
52 '/' + room : ''); | 53 this.fns = []; |
53 | 54 this.ids = 0; |
54 if (!this.manager.rooms[room]) { | 55 this.acks = {}; |
55 return []; | 56 this.initAdapter(); |
57 } | |
58 | |
59 /** | |
60 * Inherits from `EventEmitter`. | |
61 */ | |
62 | |
63 Namespace.prototype.__proto__ = Emitter.prototype; | |
64 | |
65 /** | |
66 * Apply flags from `Socket`. | |
67 */ | |
68 | |
69 exports.flags.forEach(function(flag){ | |
70 Namespace.prototype.__defineGetter__(flag, function(){ | |
71 this.flags = this.flags || {}; | |
72 this.flags[flag] = true; | |
73 return this; | |
74 }); | |
75 }); | |
76 | |
77 /** | |
78 * Initializes the `Adapter` for this nsp. | |
79 * Run upon changing adapter by `Server#adapter` | |
80 * in addition to the constructor. | |
81 * | |
82 * @api private | |
83 */ | |
84 | |
85 Namespace.prototype.initAdapter = function(){ | |
86 this.adapter = new (this.server.adapter())(this); | |
87 }; | |
88 | |
89 /** | |
90 * Sets up namespace middleware. | |
91 * | |
92 * @return {Namespace} self | |
93 * @api public | |
94 */ | |
95 | |
96 Namespace.prototype.use = function(fn){ | |
97 this.fns.push(fn); | |
98 return this; | |
99 }; | |
100 | |
101 /** | |
102 * Executes the middleware for an incoming client. | |
103 * | |
104 * @param {Socket} socket that will get added | |
105 * @param {Function} last fn call in the middleware | |
106 * @api private | |
107 */ | |
108 | |
109 Namespace.prototype.run = function(socket, fn){ | |
110 var fns = this.fns.slice(0); | |
111 if (!fns.length) return fn(null); | |
112 | |
113 function run(i){ | |
114 fns[i](socket, function(err){ | |
115 // upon error, short-circuit | |
116 if (err) return fn(err); | |
117 | |
118 // if no middleware left, summon callback | |
119 if (!fns[i + 1]) return fn(null); | |
120 | |
121 // go on to next | |
122 run(i + 1); | |
123 }); | |
56 } | 124 } |
57 | 125 |
58 return this.manager.rooms[room].map(function (id) { | 126 run(0); |
59 return this.socket(id); | 127 }; |
60 }, this); | 128 |
61 }; | 129 /** |
62 | 130 * Targets a room when emitting. |
63 /** | 131 * |
64 * Access logger interface. | 132 * @param {String} name |
65 * | 133 * @return {Namespace} self |
66 * @api public | 134 * @api public |
67 */ | 135 */ |
68 | 136 |
69 SocketNamespace.prototype.__defineGetter__('log', function () { | 137 Namespace.prototype.to = |
70 return this.manager.log; | 138 Namespace.prototype['in'] = function(name){ |
71 }); | 139 this.rooms = this.rooms || []; |
72 | 140 if (!~this.rooms.indexOf(name)) this.rooms.push(name); |
73 /** | 141 return this; |
74 * Access store. | 142 }; |
75 * | 143 |
76 * @api public | 144 /** |
77 */ | 145 * Adds a new client. |
78 | 146 * |
79 SocketNamespace.prototype.__defineGetter__('store', function () { | 147 * @return {Socket} |
80 return this.manager.store; | 148 * @api private |
81 }); | 149 */ |
82 | 150 |
83 /** | 151 Namespace.prototype.add = function(client, fn){ |
84 * JSON message flag. | 152 debug('adding socket to nsp %s', this.name); |
85 * | 153 var socket = new Socket(this, client); |
86 * @api public | 154 var self = this; |
87 */ | 155 this.run(socket, function(err){ |
88 | 156 process.nextTick(function(){ |
89 SocketNamespace.prototype.__defineGetter__('json', function () { | 157 if ('open' == client.conn.readyState) { |
90 this.flags.json = true; | 158 if (err) return socket.error(err.data || err.message); |
91 return this; | 159 |
92 }); | 160 // track socket |
93 | 161 self.sockets.push(socket); |
94 /** | 162 |
95 * Volatile message flag. | 163 // it's paramount that the internal `onconnect` logic |
96 * | 164 // fires before user-set events to prevent state order |
97 * @api public | 165 // violations (such as a disconnection before the connection |
98 */ | 166 // logic is complete) |
99 | 167 socket.onconnect(); |
100 SocketNamespace.prototype.__defineGetter__('volatile', function () { | 168 if (fn) fn(); |
101 this.flags.volatile = true; | 169 |
102 return this; | 170 // fire user-set events |
103 }); | 171 self.emit('connect', socket); |
104 | 172 self.emit('connection', socket); |
105 /** | 173 } else { |
106 * Overrides the room to relay messages to (flag). | 174 debug('next called after client was closed - ignoring socket'); |
107 * | 175 } |
108 * @api public | 176 }); |
109 */ | |
110 | |
111 SocketNamespace.prototype.in = SocketNamespace.prototype.to = function (room) { | |
112 this.flags.endpoint = this.name + (room ? '/' + room : ''); | |
113 return this; | |
114 }; | |
115 | |
116 /** | |
117 * Adds a session id we should prevent relaying messages to (flag). | |
118 * | |
119 * @api public | |
120 */ | |
121 | |
122 SocketNamespace.prototype.except = function (id) { | |
123 this.flags.exceptions.push(id); | |
124 return this; | |
125 }; | |
126 | |
127 /** | |
128 * Sets the default flags. | |
129 * | |
130 * @api private | |
131 */ | |
132 | |
133 SocketNamespace.prototype.setFlags = function () { | |
134 this.flags = { | |
135 endpoint: this.name | |
136 , exceptions: [] | |
137 }; | |
138 return this; | |
139 }; | |
140 | |
141 /** | |
142 * Sends out a packet. | |
143 * | |
144 * @api private | |
145 */ | |
146 | |
147 SocketNamespace.prototype.packet = function (packet) { | |
148 packet.endpoint = this.name; | |
149 | |
150 var store = this.store | |
151 , log = this.log | |
152 , volatile = this.flags.volatile | |
153 , exceptions = this.flags.exceptions | |
154 , packet = parser.encodePacket(packet); | |
155 | |
156 this.manager.onDispatch(this.flags.endpoint, packet, volatile, exceptions); | |
157 this.store.publish('dispatch', this.flags.endpoint, packet, volatile, exceptions); | |
158 | |
159 this.setFlags(); | |
160 | |
161 return this; | |
162 }; | |
163 | |
164 /** | |
165 * Sends to everyone. | |
166 * | |
167 * @api public | |
168 */ | |
169 | |
170 SocketNamespace.prototype.send = function (data) { | |
171 return this.packet({ | |
172 type: this.flags.json ? 'json' : 'message' | |
173 , data: data | |
174 }); | 177 }); |
175 }; | 178 return socket; |
176 | 179 }; |
177 /** | 180 |
178 * Emits to everyone (override). | 181 /** |
179 * | 182 * Removes a client. Called by each `Socket`. |
180 * @api public | 183 * |
181 */ | 184 * @api private |
182 | 185 */ |
183 SocketNamespace.prototype.emit = function (name) { | 186 |
184 if (name == 'newListener') { | 187 Namespace.prototype.remove = function(socket){ |
185 return this.$emit.apply(this, arguments); | 188 var i = this.sockets.indexOf(socket); |
189 if (~i) { | |
190 this.sockets.splice(i, 1); | |
191 } else { | |
192 debug('ignoring remove for %s', socket.id); | |
186 } | 193 } |
187 | 194 }; |
188 return this.packet({ | 195 |
189 type: 'event' | 196 /** |
190 , name: name | 197 * Emits to all clients. |
191 , args: util.toArray(arguments).slice(1) | 198 * |
192 }); | 199 * @return {Namespace} self |
193 }; | 200 * @api public |
194 | 201 */ |
195 /** | 202 |
196 * Retrieves or creates a write-only socket for a client, unless specified. | 203 Namespace.prototype.emit = function(ev){ |
197 * | 204 if (~exports.events.indexOf(ev)) { |
198 * @param {Boolean} whether the socket will be readable when initialized | 205 emit.apply(this, arguments); |
199 * @api public | 206 } else { |
200 */ | 207 // set up packet object |
201 | 208 var args = Array.prototype.slice.call(arguments); |
202 SocketNamespace.prototype.socket = function (sid, readable) { | 209 var parserType = parser.EVENT; // default |
203 if (!this.sockets[sid]) { | 210 if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary |
204 this.sockets[sid] = new Socket(this.manager, sid, this, readable); | 211 |
212 var packet = { type: parserType, data: args }; | |
213 | |
214 if ('function' == typeof args[args.length - 1]) { | |
215 throw new Error('Callbacks are not supported when broadcasting'); | |
216 } | |
217 | |
218 this.adapter.broadcast(packet, { | |
219 rooms: this.rooms, | |
220 flags: this.flags | |
221 }); | |
222 | |
223 delete this.rooms; | |
224 delete this.flags; | |
205 } | 225 } |
206 | 226 return this; |
207 return this.sockets[sid]; | 227 }; |
208 }; | 228 |
209 | 229 /** |
210 /** | 230 * Sends a `message` event to all clients. |
211 * Sets authorization for this namespace. | 231 * |
212 * | 232 * @return {Namespace} self |
213 * @api public | 233 * @api public |
214 */ | 234 */ |
215 | 235 |
216 SocketNamespace.prototype.authorization = function (fn) { | 236 Namespace.prototype.send = |
217 this.auth = fn; | 237 Namespace.prototype.write = function(){ |
218 return this; | 238 var args = Array.prototype.slice.call(arguments); |
219 }; | 239 args.unshift('message'); |
220 | 240 this.emit.apply(this, args); |
221 /** | 241 return this; |
222 * Called when a socket disconnects entirely. | 242 }; |
223 * | |
224 * @api private | |
225 */ | |
226 | |
227 SocketNamespace.prototype.handleDisconnect = function (sid, reason, raiseOnDisconnect) { | |
228 if (this.sockets[sid] && this.sockets[sid].readable) { | |
229 if (raiseOnDisconnect) this.sockets[sid].onDisconnect(reason); | |
230 delete this.sockets[sid]; | |
231 } | |
232 }; | |
233 | |
234 /** | |
235 * Performs authentication. | |
236 * | |
237 * @param Object client request data | |
238 * @api private | |
239 */ | |
240 | |
241 SocketNamespace.prototype.authorize = function (data, fn) { | |
242 if (this.auth) { | |
243 var self = this; | |
244 | |
245 this.auth.call(this, data, function (err, authorized) { | |
246 self.log.debug('client ' + | |
247 (authorized ? '' : 'un') + 'authorized for ' + self.name); | |
248 fn(err, authorized); | |
249 }); | |
250 } else { | |
251 this.log.debug('client authorized for ' + this.name); | |
252 fn(null, true); | |
253 } | |
254 | |
255 return this; | |
256 }; | |
257 | |
258 /** | |
259 * Handles a packet. | |
260 * | |
261 * @api private | |
262 */ | |
263 | |
264 SocketNamespace.prototype.handlePacket = function (sessid, packet) { | |
265 var socket = this.socket(sessid) | |
266 , dataAck = packet.ack == 'data' | |
267 , manager = this.manager | |
268 , self = this; | |
269 | |
270 function ack () { | |
271 self.log.debug('sending data ack packet'); | |
272 socket.packet({ | |
273 type: 'ack' | |
274 , args: util.toArray(arguments) | |
275 , ackId: packet.id | |
276 }); | |
277 }; | |
278 | |
279 function error (err) { | |
280 self.log.warn('handshake error ' + err + ' for ' + self.name); | |
281 socket.packet({ type: 'error', reason: err }); | |
282 }; | |
283 | |
284 function connect () { | |
285 self.manager.onJoin(sessid, self.name); | |
286 self.store.publish('join', sessid, self.name); | |
287 | |
288 // packet echo | |
289 socket.packet({ type: 'connect' }); | |
290 | |
291 // emit connection event | |
292 self.$emit('connection', socket); | |
293 }; | |
294 | |
295 switch (packet.type) { | |
296 case 'connect': | |
297 if (packet.endpoint == '') { | |
298 connect(); | |
299 } else { | |
300 var handshakeData = manager.handshaken[sessid]; | |
301 | |
302 this.authorize(handshakeData, function (err, authorized, newData) { | |
303 if (err) return error(err); | |
304 | |
305 if (authorized) { | |
306 manager.onHandshake(sessid, newData || handshakeData); | |
307 self.store.publish('handshake', sessid, newData || handshakeData); | |
308 connect(); | |
309 } else { | |
310 error('unauthorized'); | |
311 } | |
312 }); | |
313 } | |
314 break; | |
315 | |
316 case 'ack': | |
317 if (socket.acks[packet.ackId]) { | |
318 socket.acks[packet.ackId].apply(socket, packet.args); | |
319 } else { | |
320 this.log.info('unknown ack packet'); | |
321 } | |
322 break; | |
323 | |
324 case 'event': | |
325 // check if the emitted event is not blacklisted | |
326 if (-~manager.get('blacklist').indexOf(packet.name)) { | |
327 this.log.debug('ignoring blacklisted event `' + packet.name + '`'); | |
328 } else { | |
329 var params = [packet.name].concat(packet.args); | |
330 | |
331 if (dataAck) { | |
332 params.push(ack); | |
333 } | |
334 | |
335 socket.$emit.apply(socket, params); | |
336 } | |
337 break; | |
338 | |
339 case 'disconnect': | |
340 this.manager.onLeave(sessid, this.name); | |
341 this.store.publish('leave', sessid, this.name); | |
342 | |
343 socket.$emit('disconnect', packet.reason || 'packet'); | |
344 break; | |
345 | |
346 case 'json': | |
347 case 'message': | |
348 var params = ['message', packet.data]; | |
349 | |
350 if (dataAck) | |
351 params.push(ack); | |
352 | |
353 socket.$emit.apply(socket, params); | |
354 }; | |
355 }; |