namespace.js (5954B)
1 2 /** 3 * Module dependencies. 4 */ 5 6 var Socket = require('./socket'); 7 var Emitter = require('events').EventEmitter; 8 var parser = require('socket.io-parser'); 9 var hasBin = require('has-binary2'); 10 var debug = require('debug')('socket.io:namespace'); 11 12 /** 13 * Module exports. 14 */ 15 16 module.exports = exports = Namespace; 17 18 /** 19 * Blacklisted events. 20 */ 21 22 exports.events = [ 23 'connect', // for symmetry with client 24 'connection', 25 'newListener' 26 ]; 27 28 /** 29 * Flags. 30 */ 31 32 exports.flags = [ 33 'json', 34 'volatile', 35 'local' 36 ]; 37 38 /** 39 * `EventEmitter#emit` reference. 40 */ 41 42 var emit = Emitter.prototype.emit; 43 44 /** 45 * Namespace constructor. 46 * 47 * @param {Server} server instance 48 * @param {Socket} name 49 * @api private 50 */ 51 52 function Namespace(server, name){ 53 this.name = name; 54 this.server = server; 55 this.sockets = {}; 56 this.connected = {}; 57 this.fns = []; 58 this.ids = 0; 59 this.rooms = []; 60 this.flags = {}; 61 this.initAdapter(); 62 } 63 64 /** 65 * Inherits from `EventEmitter`. 66 */ 67 68 Namespace.prototype.__proto__ = Emitter.prototype; 69 70 /** 71 * Apply flags from `Socket`. 72 */ 73 74 exports.flags.forEach(function(flag){ 75 Object.defineProperty(Namespace.prototype, flag, { 76 get: function() { 77 this.flags[flag] = true; 78 return this; 79 } 80 }); 81 }); 82 83 /** 84 * Initializes the `Adapter` for this nsp. 85 * Run upon changing adapter by `Server#adapter` 86 * in addition to the constructor. 87 * 88 * @api private 89 */ 90 91 Namespace.prototype.initAdapter = function(){ 92 this.adapter = new (this.server.adapter())(this); 93 }; 94 95 /** 96 * Sets up namespace middleware. 97 * 98 * @return {Namespace} self 99 * @api public 100 */ 101 102 Namespace.prototype.use = function(fn){ 103 if (this.server.eio && this.name === '/') { 104 debug('removing initial packet'); 105 delete this.server.eio.initialPacket; 106 } 107 this.fns.push(fn); 108 return this; 109 }; 110 111 /** 112 * Executes the middleware for an incoming client. 113 * 114 * @param {Socket} socket that will get added 115 * @param {Function} fn last fn call in the middleware 116 * @api private 117 */ 118 119 Namespace.prototype.run = function(socket, fn){ 120 var fns = this.fns.slice(0); 121 if (!fns.length) return fn(null); 122 123 function run(i){ 124 fns[i](socket, function(err){ 125 // upon error, short-circuit 126 if (err) return fn(err); 127 128 // if no middleware left, summon callback 129 if (!fns[i + 1]) return fn(null); 130 131 // go on to next 132 run(i + 1); 133 }); 134 } 135 136 run(0); 137 }; 138 139 /** 140 * Targets a room when emitting. 141 * 142 * @param {String} name 143 * @return {Namespace} self 144 * @api public 145 */ 146 147 Namespace.prototype.to = 148 Namespace.prototype.in = function(name){ 149 if (!~this.rooms.indexOf(name)) this.rooms.push(name); 150 return this; 151 }; 152 153 /** 154 * Adds a new client. 155 * 156 * @return {Socket} 157 * @api private 158 */ 159 160 Namespace.prototype.add = function(client, query, fn){ 161 debug('adding socket to nsp %s', this.name); 162 var socket = new Socket(this, client, query); 163 var self = this; 164 this.run(socket, function(err){ 165 process.nextTick(function(){ 166 if ('open' == client.conn.readyState) { 167 if (err) return socket.error(err.data || err.message); 168 169 // track socket 170 self.sockets[socket.id] = socket; 171 172 // it's paramount that the internal `onconnect` logic 173 // fires before user-set events to prevent state order 174 // violations (such as a disconnection before the connection 175 // logic is complete) 176 socket.onconnect(); 177 if (fn) fn(); 178 179 // fire user-set events 180 self.emit('connect', socket); 181 self.emit('connection', socket); 182 } else { 183 debug('next called after client was closed - ignoring socket'); 184 } 185 }); 186 }); 187 return socket; 188 }; 189 190 /** 191 * Removes a client. Called by each `Socket`. 192 * 193 * @api private 194 */ 195 196 Namespace.prototype.remove = function(socket){ 197 if (this.sockets.hasOwnProperty(socket.id)) { 198 delete this.sockets[socket.id]; 199 } else { 200 debug('ignoring remove for %s', socket.id); 201 } 202 }; 203 204 /** 205 * Emits to all clients. 206 * 207 * @return {Namespace} self 208 * @api public 209 */ 210 211 Namespace.prototype.emit = function(ev){ 212 if (~exports.events.indexOf(ev)) { 213 emit.apply(this, arguments); 214 return this; 215 } 216 // set up packet object 217 var args = Array.prototype.slice.call(arguments); 218 var packet = { 219 type: (this.flags.binary !== undefined ? this.flags.binary : hasBin(args)) ? parser.BINARY_EVENT : parser.EVENT, 220 data: args 221 }; 222 223 if ('function' == typeof args[args.length - 1]) { 224 throw new Error('Callbacks are not supported when broadcasting'); 225 } 226 227 var rooms = this.rooms.slice(0); 228 var flags = Object.assign({}, this.flags); 229 230 // reset flags 231 this.rooms = []; 232 this.flags = {}; 233 234 this.adapter.broadcast(packet, { 235 rooms: rooms, 236 flags: flags 237 }); 238 239 return this; 240 }; 241 242 /** 243 * Sends a `message` event to all clients. 244 * 245 * @return {Namespace} self 246 * @api public 247 */ 248 249 Namespace.prototype.send = 250 Namespace.prototype.write = function(){ 251 var args = Array.prototype.slice.call(arguments); 252 args.unshift('message'); 253 this.emit.apply(this, args); 254 return this; 255 }; 256 257 /** 258 * Gets a list of clients. 259 * 260 * @return {Namespace} self 261 * @api public 262 */ 263 264 Namespace.prototype.clients = function(fn){ 265 if(!this.adapter){ 266 throw new Error('No adapter for this namespace, are you trying to get the list of clients of a dynamic namespace?') 267 } 268 this.adapter.clients(this.rooms, fn); 269 // reset rooms for scenario: 270 // .in('room').clients() (GH-1978) 271 this.rooms = []; 272 return this; 273 }; 274 275 /** 276 * Sets the compress flag. 277 * 278 * @param {Boolean} compress if `true`, compresses the sending data 279 * @return {Socket} self 280 * @api public 281 */ 282 283 Namespace.prototype.compress = function(compress){ 284 this.flags.compress = compress; 285 return this; 286 }; 287 288 /** 289 * Sets the binary flag 290 * 291 * @param {Boolean} Encode as if it has binary data if `true`, Encode as if it doesnt have binary data if `false` 292 * @return {Socket} self 293 * @api public 294 */ 295 296 Namespace.prototype.binary = function (binary) { 297 this.flags.binary = binary; 298 return this; 299 };