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