Including the node_modules folder for socket.io code.
[KiwiIRC.git] / node / node_modules / socket.io / lib / namespace.js
1 /**
2 * Module dependencies.
3 */
4
5 var Socket = require('./socket')
6 , EventEmitter = process.EventEmitter
7 , parser = require('./parser')
8 , util = require('./util');
9
10 /**
11 * Exports the constructor.
12 */
13
14 exports = module.exports = SocketNamespace;
15
16 /**
17 * Constructor.
18 *
19 * @api public.
20 */
21
22 function SocketNamespace (mgr, name) {
23 this.manager = mgr;
24 this.name = name || '';
25 this.sockets = {};
26 this.auth = false;
27 this.setFlags();
28 };
29
30 /**
31 * Inherits from EventEmitter.
32 */
33
34 SocketNamespace.prototype.__proto__ = EventEmitter.prototype;
35
36 /**
37 * Copies emit since we override it
38 *
39 * @api private
40 */
41
42 SocketNamespace.prototype.$emit = EventEmitter.prototype.emit;
43
44 /**
45 * Retrieves all clients as Socket instances as an array.
46 *
47 * @api public
48 */
49
50 SocketNamespace.prototype.clients = function (room) {
51 var room = this.name + (room !== undefined ?
52 '/' + room : '');
53
54 if (!this.manager.rooms[room]) {
55 return [];
56 }
57
58 return this.manager.rooms[room].map(function (id) {
59 return this.socket(id);
60 }, this);
61 };
62
63 /**
64 * Access logger interface.
65 *
66 * @api public
67 */
68
69 SocketNamespace.prototype.__defineGetter__('log', function () {
70 return this.manager.log;
71 });
72
73 /**
74 * Access store.
75 *
76 * @api public
77 */
78
79 SocketNamespace.prototype.__defineGetter__('store', function () {
80 return this.manager.store;
81 });
82
83 /**
84 * JSON message flag.
85 *
86 * @api public
87 */
88
89 SocketNamespace.prototype.__defineGetter__('json', function () {
90 this.flags.json = true;
91 return this;
92 });
93
94 /**
95 * Volatile message flag.
96 *
97 * @api public
98 */
99
100 SocketNamespace.prototype.__defineGetter__('volatile', function () {
101 this.flags.volatile = true;
102 return this;
103 });
104
105 /**
106 * Overrides the room to relay messages to (flag)
107 *
108 * @api public
109 */
110
111 SocketNamespace.prototype.in = 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 });
175 };
176
177 /**
178 * Emits to everyone (override)
179 *
180 * @api private
181 */
182
183 SocketNamespace.prototype.emit = function (name) {
184 if (name == 'newListener') {
185 return this.$emit.apply(this, arguments);
186 }
187
188 return this.packet({
189 type: 'event'
190 , name: name
191 , args: util.toArray(arguments).slice(1)
192 });
193 };
194
195 /**
196 * Retrieves or creates a write-only socket for a client, unless specified.
197 *
198 * @param {Boolean} whether the socket will be readable when initialized
199 * @api private
200 */
201
202 SocketNamespace.prototype.socket = function (sid, readable) {
203 if (!this.sockets[sid]) {
204 this.sockets[sid] = new Socket(this.manager, sid, this, readable);
205 }
206
207 return this.sockets[sid];
208 };
209
210 /**
211 * Sets authorization for this namespace
212 *
213 * @api public
214 */
215
216 SocketNamespace.prototype.authorization = function (fn) {
217 this.auth = fn;
218 return this;
219 };
220
221 /**
222 * Called when a socket disconnects entirely.
223 *
224 * @api private
225 */
226
227 SocketNamespace.prototype.handleDisconnect = function (sid, reason) {
228 if (this.sockets[sid] && this.sockets[sid].readable) {
229 this.sockets[sid].onDisconnect(reason);
230 }
231 };
232
233 /**
234 * Performs authentication.
235 *
236 * @param Object client request data
237 * @api private
238 */
239
240 SocketNamespace.prototype.authorize = function (data, fn) {
241 if (this.auth) {
242 var self = this;
243
244 this.auth.call(this, data, function (err, authorized) {
245 self.log.debug('client ' +
246 (authorized ? '' : 'un') + 'authorized for ' + self.name);
247 fn(err, authorized);
248 });
249 } else {
250 this.log.debug('client authorized for ' + this.name);
251 fn(null, true);
252 }
253
254 return this;
255 };
256
257 /**
258 * Handles a packet.
259 *
260 * @api private
261 */
262
263 SocketNamespace.prototype.handlePacket = function (sessid, packet) {
264 var socket = this.socket(sessid)
265 , dataAck = packet.ack == 'data'
266 , self = this;
267
268 function ack () {
269 self.log.debug('sending data ack packet');
270 socket.packet({
271 type: 'ack'
272 , args: util.toArray(arguments)
273 , ackId: packet.id
274 });
275 };
276
277 function error (err) {
278 self.log.warn('handshake error ' + err + ' for ' + self.name);
279 socket.packet({ type: 'error', reason: err });
280 };
281
282 function connect () {
283 self.manager.onJoin(sessid, self.name);
284 self.store.publish('join', sessid, self.name);
285
286 // packet echo
287 socket.packet({ type: 'connect' });
288
289 // emit connection event
290 self.$emit('connection', socket);
291 };
292
293 switch (packet.type) {
294 case 'connect':
295 if (packet.endpoint == '') {
296 connect();
297 } else {
298 var manager = this.manager
299 , handshakeData = manager.handshaken[sessid];
300
301 this.authorize(handshakeData, function (err, authorized, newData) {
302 if (err) return error(err);
303
304 if (authorized) {
305 manager.onHandshake(sessid, newData || handshakeData);
306 self.store.publish('handshake', sessid, newData || handshakeData);
307 connect();
308 } else {
309 error('unauthorized');
310 }
311 });
312 }
313 break;
314
315 case 'ack':
316 if (socket.acks[packet.ackId]) {
317 socket.acks[packet.ackId].apply(socket, packet.args);
318 } else {
319 this.log.info('unknown ack packet');
320 }
321 break;
322
323 case 'event':
324 var params = [packet.name].concat(packet.args);
325
326 if (dataAck)
327 params.push(ack);
328
329 socket.$emit.apply(socket, params);
330 break;
331
332 case 'disconnect':
333 this.manager.onLeave(sessid, this.name);
334 this.store.publish('leave', sessid, this.name);
335
336 socket.$emit('disconnect', packet.reason || 'packet');
337 break;
338
339 case 'json':
340 case 'message':
341 var params = ['message', packet.data];
342
343 if (dataAck)
344 params.push(ack);
345
346 socket.$emit.apply(socket, params);
347 };
348 };