UNPKG

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