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 };