rc-web@69
|
1 /*!
|
rc-web@69
|
2 * socket.io-node
|
rc-web@69
|
3 * Copyright(c) 2011 LearnBoost <dev@learnboost.com>
|
rc-web@69
|
4 * MIT Licensed
|
rc-web@69
|
5 */
|
rc-web@69
|
6
|
rc-web@69
|
7 /**
|
rc-web@69
|
8 * Module dependencies.
|
rc-web@69
|
9 */
|
rc-web@69
|
10
|
rc-web@69
|
11 var fs = require('fs')
|
rc-web@69
|
12 , url = require('url')
|
rc-web@69
|
13 , tty = require('tty')
|
rc-web@69
|
14 , crypto = require('crypto')
|
rc-web@69
|
15 , util = require('./util')
|
rc-web@69
|
16 , store = require('./store')
|
rc-web@69
|
17 , client = require('socket.io-client')
|
rc-web@69
|
18 , transports = require('./transports')
|
rc-web@69
|
19 , Logger = require('./logger')
|
rc-web@69
|
20 , Socket = require('./socket')
|
rc-web@69
|
21 , MemoryStore = require('./stores/memory')
|
rc-web@69
|
22 , SocketNamespace = require('./namespace')
|
rc-web@69
|
23 , Static = require('./static')
|
rc-web@69
|
24 , EventEmitter = process.EventEmitter;
|
rc-web@69
|
25
|
rc-web@69
|
26 /**
|
rc-web@69
|
27 * Export the constructor.
|
rc-web@69
|
28 */
|
rc-web@69
|
29
|
rc-web@69
|
30 exports = module.exports = Manager;
|
rc-web@69
|
31
|
rc-web@69
|
32 /**
|
rc-web@69
|
33 * Default transports.
|
rc-web@69
|
34 */
|
rc-web@69
|
35
|
rc-web@69
|
36 var defaultTransports = exports.defaultTransports = [
|
rc-web@69
|
37 'websocket'
|
rc-web@69
|
38 , 'htmlfile'
|
rc-web@69
|
39 , 'xhr-polling'
|
rc-web@69
|
40 , 'jsonp-polling'
|
rc-web@69
|
41 ];
|
rc-web@69
|
42
|
rc-web@69
|
43 /**
|
rc-web@69
|
44 * Inherited defaults.
|
rc-web@69
|
45 */
|
rc-web@69
|
46
|
rc-web@69
|
47 var parent = module.parent.exports
|
rc-web@69
|
48 , protocol = parent.protocol
|
rc-web@69
|
49 , jsonpolling_re = /^\d+$/;
|
rc-web@69
|
50
|
rc-web@69
|
51 /**
|
rc-web@69
|
52 * Manager constructor.
|
rc-web@69
|
53 *
|
rc-web@69
|
54 * @param {HTTPServer} server
|
rc-web@69
|
55 * @param {Object} options, optional
|
rc-web@69
|
56 * @api public
|
rc-web@69
|
57 */
|
rc-web@69
|
58
|
rc-web@69
|
59 function Manager (server, options) {
|
rc-web@69
|
60 this.server = server;
|
rc-web@69
|
61 this.namespaces = {};
|
rc-web@69
|
62 this.sockets = this.of('');
|
rc-web@69
|
63 this.settings = {
|
rc-web@69
|
64 origins: '*:*'
|
rc-web@69
|
65 , log: true
|
rc-web@69
|
66 , store: new MemoryStore
|
rc-web@69
|
67 , logger: new Logger
|
rc-web@69
|
68 , static: new Static(this)
|
rc-web@69
|
69 , heartbeats: true
|
rc-web@69
|
70 , resource: '/socket.io'
|
rc-web@69
|
71 , transports: defaultTransports
|
rc-web@69
|
72 , authorization: false
|
rc-web@69
|
73 , blacklist: ['disconnect']
|
rc-web@69
|
74 , 'log level': 3
|
rc-web@69
|
75 , 'log colors': tty.isatty(process.stdout.fd)
|
rc-web@69
|
76 , 'close timeout': 60
|
rc-web@69
|
77 , 'heartbeat interval': 25
|
rc-web@69
|
78 , 'heartbeat timeout': 60
|
rc-web@69
|
79 , 'polling duration': 20
|
rc-web@69
|
80 , 'flash policy server': true
|
rc-web@69
|
81 , 'flash policy port': 10843
|
rc-web@69
|
82 , 'destroy upgrade': true
|
rc-web@69
|
83 , 'destroy buffer size': 10E7
|
rc-web@69
|
84 , 'browser client': true
|
rc-web@69
|
85 , 'browser client cache': true
|
rc-web@69
|
86 , 'browser client minification': false
|
rc-web@69
|
87 , 'browser client etag': false
|
rc-web@69
|
88 , 'browser client expires': 315360000
|
rc-web@69
|
89 , 'browser client gzip': false
|
rc-web@69
|
90 , 'browser client handler': false
|
rc-web@69
|
91 , 'client store expiration': 15
|
rc-web@69
|
92 , 'match origin protocol': false
|
rc-web@69
|
93 };
|
rc-web@69
|
94
|
rc-web@69
|
95 for (var i in options) {
|
rc-web@69
|
96 if (options.hasOwnProperty(i)) {
|
rc-web@69
|
97 this.settings[i] = options[i];
|
rc-web@69
|
98 }
|
rc-web@69
|
99 }
|
rc-web@69
|
100
|
rc-web@69
|
101 var self = this;
|
rc-web@69
|
102
|
rc-web@69
|
103 // default error handler
|
rc-web@69
|
104 server.on('error', function(err) {
|
rc-web@69
|
105 self.log.warn('error raised: ' + err);
|
rc-web@69
|
106 });
|
rc-web@69
|
107
|
rc-web@69
|
108 this.initStore();
|
rc-web@69
|
109
|
rc-web@69
|
110 this.on('set:store', function() {
|
rc-web@69
|
111 self.initStore();
|
rc-web@69
|
112 });
|
rc-web@69
|
113
|
rc-web@69
|
114 // reset listeners
|
rc-web@69
|
115 this.oldListeners = server.listeners('request').splice(0);
|
rc-web@69
|
116 server.removeAllListeners('request');
|
rc-web@69
|
117
|
rc-web@69
|
118 server.on('request', function (req, res) {
|
rc-web@69
|
119 self.handleRequest(req, res);
|
rc-web@69
|
120 });
|
rc-web@69
|
121
|
rc-web@69
|
122 server.on('upgrade', function (req, socket, head) {
|
rc-web@69
|
123 self.handleUpgrade(req, socket, head);
|
rc-web@69
|
124 });
|
rc-web@69
|
125
|
rc-web@69
|
126 server.on('close', function () {
|
rc-web@69
|
127 clearInterval(self.gc);
|
rc-web@69
|
128 });
|
rc-web@69
|
129
|
rc-web@69
|
130 server.once('listening', function () {
|
rc-web@69
|
131 self.gc = setInterval(self.garbageCollection.bind(self), 10000);
|
rc-web@69
|
132 });
|
rc-web@69
|
133
|
rc-web@69
|
134 for (var i in transports) {
|
rc-web@69
|
135 if (transports.hasOwnProperty(i)) {
|
rc-web@69
|
136 if (transports[i].init) {
|
rc-web@69
|
137 transports[i].init(this);
|
rc-web@69
|
138 }
|
rc-web@69
|
139 }
|
rc-web@69
|
140 }
|
rc-web@69
|
141
|
rc-web@69
|
142 // forward-compatibility with 1.0
|
rc-web@69
|
143 var self = this;
|
rc-web@69
|
144 this.sockets.on('connection', function (conn) {
|
rc-web@69
|
145 self.emit('connection', conn);
|
rc-web@69
|
146 });
|
rc-web@69
|
147
|
rc-web@69
|
148 this.sequenceNumber = Date.now() | 0;
|
rc-web@69
|
149
|
rc-web@69
|
150 this.log.info('socket.io started');
|
rc-web@69
|
151 };
|
rc-web@69
|
152
|
rc-web@69
|
153 Manager.prototype.__proto__ = EventEmitter.prototype
|
rc-web@69
|
154
|
rc-web@69
|
155 /**
|
rc-web@69
|
156 * Store accessor shortcut.
|
rc-web@69
|
157 *
|
rc-web@69
|
158 * @api public
|
rc-web@69
|
159 */
|
rc-web@69
|
160
|
rc-web@69
|
161 Manager.prototype.__defineGetter__('store', function () {
|
rc-web@69
|
162 var store = this.get('store');
|
rc-web@69
|
163 store.manager = this;
|
rc-web@69
|
164 return store;
|
rc-web@69
|
165 });
|
rc-web@69
|
166
|
rc-web@69
|
167 /**
|
rc-web@69
|
168 * Logger accessor.
|
rc-web@69
|
169 *
|
rc-web@69
|
170 * @api public
|
rc-web@69
|
171 */
|
rc-web@69
|
172
|
rc-web@69
|
173 Manager.prototype.__defineGetter__('log', function () {
|
rc-web@69
|
174 var logger = this.get('logger');
|
rc-web@69
|
175
|
rc-web@69
|
176 logger.level = this.get('log level') || -1;
|
rc-web@69
|
177 logger.colors = this.get('log colors');
|
rc-web@69
|
178 logger.enabled = this.enabled('log');
|
rc-web@69
|
179
|
rc-web@69
|
180 return logger;
|
rc-web@69
|
181 });
|
rc-web@69
|
182
|
rc-web@69
|
183 /**
|
rc-web@69
|
184 * Static accessor.
|
rc-web@69
|
185 *
|
rc-web@69
|
186 * @api public
|
rc-web@69
|
187 */
|
rc-web@69
|
188
|
rc-web@69
|
189 Manager.prototype.__defineGetter__('static', function () {
|
rc-web@69
|
190 return this.get('static');
|
rc-web@69
|
191 });
|
rc-web@69
|
192
|
rc-web@69
|
193 /**
|
rc-web@69
|
194 * Get settings.
|
rc-web@69
|
195 *
|
rc-web@69
|
196 * @api public
|
rc-web@69
|
197 */
|
rc-web@69
|
198
|
rc-web@69
|
199 Manager.prototype.get = function (key) {
|
rc-web@69
|
200 return this.settings[key];
|
rc-web@69
|
201 };
|
rc-web@69
|
202
|
rc-web@69
|
203 /**
|
rc-web@69
|
204 * Set settings
|
rc-web@69
|
205 *
|
rc-web@69
|
206 * @api public
|
rc-web@69
|
207 */
|
rc-web@69
|
208
|
rc-web@69
|
209 Manager.prototype.set = function (key, value) {
|
rc-web@69
|
210 if (arguments.length == 1) return this.get(key);
|
rc-web@69
|
211 this.settings[key] = value;
|
rc-web@69
|
212 this.emit('set:' + key, this.settings[key], key);
|
rc-web@69
|
213 return this;
|
rc-web@69
|
214 };
|
rc-web@69
|
215
|
rc-web@69
|
216 /**
|
rc-web@69
|
217 * Enable a setting
|
rc-web@69
|
218 *
|
rc-web@69
|
219 * @api public
|
rc-web@69
|
220 */
|
rc-web@69
|
221
|
rc-web@69
|
222 Manager.prototype.enable = function (key) {
|
rc-web@69
|
223 this.settings[key] = true;
|
rc-web@69
|
224 this.emit('set:' + key, this.settings[key], key);
|
rc-web@69
|
225 return this;
|
rc-web@69
|
226 };
|
rc-web@69
|
227
|
rc-web@69
|
228 /**
|
rc-web@69
|
229 * Disable a setting
|
rc-web@69
|
230 *
|
rc-web@69
|
231 * @api public
|
rc-web@69
|
232 */
|
rc-web@69
|
233
|
rc-web@69
|
234 Manager.prototype.disable = function (key) {
|
rc-web@69
|
235 this.settings[key] = false;
|
rc-web@69
|
236 this.emit('set:' + key, this.settings[key], key);
|
rc-web@69
|
237 return this;
|
rc-web@69
|
238 };
|
rc-web@69
|
239
|
rc-web@69
|
240 /**
|
rc-web@69
|
241 * Checks if a setting is enabled
|
rc-web@69
|
242 *
|
rc-web@69
|
243 * @api public
|
rc-web@69
|
244 */
|
rc-web@69
|
245
|
rc-web@69
|
246 Manager.prototype.enabled = function (key) {
|
rc-web@69
|
247 return !!this.settings[key];
|
rc-web@69
|
248 };
|
rc-web@69
|
249
|
rc-web@69
|
250 /**
|
rc-web@69
|
251 * Checks if a setting is disabled
|
rc-web@69
|
252 *
|
rc-web@69
|
253 * @api public
|
rc-web@69
|
254 */
|
rc-web@69
|
255
|
rc-web@69
|
256 Manager.prototype.disabled = function (key) {
|
rc-web@69
|
257 return !this.settings[key];
|
rc-web@69
|
258 };
|
rc-web@69
|
259
|
rc-web@69
|
260 /**
|
rc-web@69
|
261 * Configure callbacks.
|
rc-web@69
|
262 *
|
rc-web@69
|
263 * @api public
|
rc-web@69
|
264 */
|
rc-web@69
|
265
|
rc-web@69
|
266 Manager.prototype.configure = function (env, fn) {
|
rc-web@69
|
267 if ('function' == typeof env) {
|
rc-web@69
|
268 env.call(this);
|
rc-web@69
|
269 } else if (env == (process.env.NODE_ENV || 'development')) {
|
rc-web@69
|
270 fn.call(this);
|
rc-web@69
|
271 }
|
rc-web@69
|
272
|
rc-web@69
|
273 return this;
|
rc-web@69
|
274 };
|
rc-web@69
|
275
|
rc-web@69
|
276 /**
|
rc-web@69
|
277 * Initializes everything related to the message dispatcher.
|
rc-web@69
|
278 *
|
rc-web@69
|
279 * @api private
|
rc-web@69
|
280 */
|
rc-web@69
|
281
|
rc-web@69
|
282 Manager.prototype.initStore = function () {
|
rc-web@69
|
283 this.handshaken = {};
|
rc-web@69
|
284 this.connected = {};
|
rc-web@69
|
285 this.open = {};
|
rc-web@69
|
286 this.closed = {};
|
rc-web@69
|
287 this.rooms = {};
|
rc-web@69
|
288 this.roomClients = {};
|
rc-web@69
|
289
|
rc-web@69
|
290 var self = this;
|
rc-web@69
|
291
|
rc-web@69
|
292 this.store.subscribe('handshake', function (id, data) {
|
rc-web@69
|
293 self.onHandshake(id, data);
|
rc-web@69
|
294 });
|
rc-web@69
|
295
|
rc-web@69
|
296 this.store.subscribe('connect', function (id) {
|
rc-web@69
|
297 self.onConnect(id);
|
rc-web@69
|
298 });
|
rc-web@69
|
299
|
rc-web@69
|
300 this.store.subscribe('open', function (id) {
|
rc-web@69
|
301 self.onOpen(id);
|
rc-web@69
|
302 });
|
rc-web@69
|
303
|
rc-web@69
|
304 this.store.subscribe('join', function (id, room) {
|
rc-web@69
|
305 self.onJoin(id, room);
|
rc-web@69
|
306 });
|
rc-web@69
|
307
|
rc-web@69
|
308 this.store.subscribe('leave', function (id, room) {
|
rc-web@69
|
309 self.onLeave(id, room);
|
rc-web@69
|
310 });
|
rc-web@69
|
311
|
rc-web@69
|
312 this.store.subscribe('close', function (id) {
|
rc-web@69
|
313 self.onClose(id);
|
rc-web@69
|
314 });
|
rc-web@69
|
315
|
rc-web@69
|
316 this.store.subscribe('dispatch', function (room, packet, volatile, exceptions) {
|
rc-web@69
|
317 self.onDispatch(room, packet, volatile, exceptions);
|
rc-web@69
|
318 });
|
rc-web@69
|
319
|
rc-web@69
|
320 this.store.subscribe('disconnect', function (id) {
|
rc-web@69
|
321 self.onDisconnect(id);
|
rc-web@69
|
322 });
|
rc-web@69
|
323 };
|
rc-web@69
|
324
|
rc-web@69
|
325 /**
|
rc-web@69
|
326 * Called when a client handshakes.
|
rc-web@69
|
327 *
|
rc-web@69
|
328 * @param text
|
rc-web@69
|
329 */
|
rc-web@69
|
330
|
rc-web@69
|
331 Manager.prototype.onHandshake = function (id, data) {
|
rc-web@69
|
332 this.handshaken[id] = data;
|
rc-web@69
|
333 };
|
rc-web@69
|
334
|
rc-web@69
|
335 /**
|
rc-web@69
|
336 * Called when a client connects (ie: transport first opens)
|
rc-web@69
|
337 *
|
rc-web@69
|
338 * @api private
|
rc-web@69
|
339 */
|
rc-web@69
|
340
|
rc-web@69
|
341 Manager.prototype.onConnect = function (id) {
|
rc-web@69
|
342 this.connected[id] = true;
|
rc-web@69
|
343 };
|
rc-web@69
|
344
|
rc-web@69
|
345 /**
|
rc-web@69
|
346 * Called when a client opens a request in a different node.
|
rc-web@69
|
347 *
|
rc-web@69
|
348 * @api private
|
rc-web@69
|
349 */
|
rc-web@69
|
350
|
rc-web@69
|
351 Manager.prototype.onOpen = function (id) {
|
rc-web@69
|
352 this.open[id] = true;
|
rc-web@69
|
353
|
rc-web@69
|
354 if (this.closed[id]) {
|
rc-web@69
|
355 var self = this;
|
rc-web@69
|
356
|
rc-web@69
|
357 this.store.unsubscribe('dispatch:' + id, function () {
|
rc-web@69
|
358 var transport = self.transports[id];
|
rc-web@69
|
359 if (self.closed[id] && self.closed[id].length && transport) {
|
rc-web@69
|
360
|
rc-web@69
|
361 // if we have buffered messages that accumulate between calling
|
rc-web@69
|
362 // onOpen an this async callback, send them if the transport is
|
rc-web@69
|
363 // still open, otherwise leave them buffered
|
rc-web@69
|
364 if (transport.open) {
|
rc-web@69
|
365 transport.payload(self.closed[id]);
|
rc-web@69
|
366 self.closed[id] = [];
|
rc-web@69
|
367 }
|
rc-web@69
|
368 }
|
rc-web@69
|
369 });
|
rc-web@69
|
370 }
|
rc-web@69
|
371
|
rc-web@69
|
372 // clear the current transport
|
rc-web@69
|
373 if (this.transports[id]) {
|
rc-web@69
|
374 this.transports[id].discard();
|
rc-web@69
|
375 this.transports[id] = null;
|
rc-web@69
|
376 }
|
rc-web@69
|
377 };
|
rc-web@69
|
378
|
rc-web@69
|
379 /**
|
rc-web@69
|
380 * Called when a message is sent to a namespace and/or room.
|
rc-web@69
|
381 *
|
rc-web@69
|
382 * @api private
|
rc-web@69
|
383 */
|
rc-web@69
|
384
|
rc-web@69
|
385 Manager.prototype.onDispatch = function (room, packet, volatile, exceptions) {
|
rc-web@69
|
386 if (this.rooms[room]) {
|
rc-web@69
|
387 for (var i = 0, l = this.rooms[room].length; i < l; i++) {
|
rc-web@69
|
388 var id = this.rooms[room][i];
|
rc-web@69
|
389
|
rc-web@69
|
390 if (!~exceptions.indexOf(id)) {
|
rc-web@69
|
391 if (this.transports[id] && this.transports[id].open) {
|
rc-web@69
|
392 this.transports[id].onDispatch(packet, volatile);
|
rc-web@69
|
393 } else if (!volatile) {
|
rc-web@69
|
394 this.onClientDispatch(id, packet);
|
rc-web@69
|
395 }
|
rc-web@69
|
396 }
|
rc-web@69
|
397 }
|
rc-web@69
|
398 }
|
rc-web@69
|
399 };
|
rc-web@69
|
400
|
rc-web@69
|
401 /**
|
rc-web@69
|
402 * Called when a client joins a nsp / room.
|
rc-web@69
|
403 *
|
rc-web@69
|
404 * @api private
|
rc-web@69
|
405 */
|
rc-web@69
|
406
|
rc-web@69
|
407 Manager.prototype.onJoin = function (id, name) {
|
rc-web@69
|
408 if (!this.roomClients[id]) {
|
rc-web@69
|
409 this.roomClients[id] = {};
|
rc-web@69
|
410 }
|
rc-web@69
|
411
|
rc-web@69
|
412 if (!this.rooms[name]) {
|
rc-web@69
|
413 this.rooms[name] = [];
|
rc-web@69
|
414 }
|
rc-web@69
|
415
|
rc-web@69
|
416 if (!~this.rooms[name].indexOf(id)) {
|
rc-web@69
|
417 this.rooms[name].push(id);
|
rc-web@69
|
418 this.roomClients[id][name] = true;
|
rc-web@69
|
419 }
|
rc-web@69
|
420 };
|
rc-web@69
|
421
|
rc-web@69
|
422 /**
|
rc-web@69
|
423 * Called when a client leaves a nsp / room.
|
rc-web@69
|
424 *
|
rc-web@69
|
425 * @param private
|
rc-web@69
|
426 */
|
rc-web@69
|
427
|
rc-web@69
|
428 Manager.prototype.onLeave = function (id, room) {
|
rc-web@69
|
429 if (this.rooms[room]) {
|
rc-web@69
|
430 var index = this.rooms[room].indexOf(id);
|
rc-web@69
|
431
|
rc-web@69
|
432 if (index >= 0) {
|
rc-web@69
|
433 this.rooms[room].splice(index, 1);
|
rc-web@69
|
434 }
|
rc-web@69
|
435
|
rc-web@69
|
436 if (!this.rooms[room].length) {
|
rc-web@69
|
437 delete this.rooms[room];
|
rc-web@69
|
438 }
|
rc-web@69
|
439
|
rc-web@69
|
440 if (this.roomClients[id]) {
|
rc-web@69
|
441 delete this.roomClients[id][room];
|
rc-web@69
|
442 }
|
rc-web@69
|
443 }
|
rc-web@69
|
444 };
|
rc-web@69
|
445
|
rc-web@69
|
446 /**
|
rc-web@69
|
447 * Called when a client closes a request in different node.
|
rc-web@69
|
448 *
|
rc-web@69
|
449 * @api private
|
rc-web@69
|
450 */
|
rc-web@69
|
451
|
rc-web@69
|
452 Manager.prototype.onClose = function (id) {
|
rc-web@69
|
453 if (this.open[id]) {
|
rc-web@69
|
454 delete this.open[id];
|
rc-web@69
|
455 }
|
rc-web@69
|
456
|
rc-web@69
|
457 this.closed[id] = [];
|
rc-web@69
|
458
|
rc-web@69
|
459 var self = this;
|
rc-web@69
|
460
|
rc-web@69
|
461 this.store.subscribe('dispatch:' + id, function (packet, volatile) {
|
rc-web@69
|
462 if (!volatile) {
|
rc-web@69
|
463 self.onClientDispatch(id, packet);
|
rc-web@69
|
464 }
|
rc-web@69
|
465 });
|
rc-web@69
|
466 };
|
rc-web@69
|
467
|
rc-web@69
|
468 /**
|
rc-web@69
|
469 * Dispatches a message for a closed client.
|
rc-web@69
|
470 *
|
rc-web@69
|
471 * @api private
|
rc-web@69
|
472 */
|
rc-web@69
|
473
|
rc-web@69
|
474 Manager.prototype.onClientDispatch = function (id, packet) {
|
rc-web@69
|
475 if (this.closed[id]) {
|
rc-web@69
|
476 this.closed[id].push(packet);
|
rc-web@69
|
477 }
|
rc-web@69
|
478 };
|
rc-web@69
|
479
|
rc-web@69
|
480 /**
|
rc-web@69
|
481 * Receives a message for a client.
|
rc-web@69
|
482 *
|
rc-web@69
|
483 * @api private
|
rc-web@69
|
484 */
|
rc-web@69
|
485
|
rc-web@69
|
486 Manager.prototype.onClientMessage = function (id, packet) {
|
rc-web@69
|
487 if (this.namespaces[packet.endpoint]) {
|
rc-web@69
|
488 this.namespaces[packet.endpoint].handlePacket(id, packet);
|
rc-web@69
|
489 }
|
rc-web@69
|
490 };
|
rc-web@69
|
491
|
rc-web@69
|
492 /**
|
rc-web@69
|
493 * Fired when a client disconnects (not triggered).
|
rc-web@69
|
494 *
|
rc-web@69
|
495 * @api private
|
rc-web@69
|
496 */
|
rc-web@69
|
497
|
rc-web@69
|
498 Manager.prototype.onClientDisconnect = function (id, reason) {
|
rc-web@69
|
499 for (var name in this.namespaces) {
|
rc-web@69
|
500 if (this.namespaces.hasOwnProperty(name)) {
|
rc-web@69
|
501 this.namespaces[name].handleDisconnect(id, reason, typeof this.roomClients[id] !== 'undefined' &&
|
rc-web@69
|
502 typeof this.roomClients[id][name] !== 'undefined');
|
rc-web@69
|
503 }
|
rc-web@69
|
504 }
|
rc-web@69
|
505
|
rc-web@69
|
506 this.onDisconnect(id);
|
rc-web@69
|
507 };
|
rc-web@69
|
508
|
rc-web@69
|
509 /**
|
rc-web@69
|
510 * Called when a client disconnects.
|
rc-web@69
|
511 *
|
rc-web@69
|
512 * @param text
|
rc-web@69
|
513 */
|
rc-web@69
|
514
|
rc-web@69
|
515 Manager.prototype.onDisconnect = function (id, local) {
|
rc-web@69
|
516 delete this.handshaken[id];
|
rc-web@69
|
517
|
rc-web@69
|
518 if (this.open[id]) {
|
rc-web@69
|
519 delete this.open[id];
|
rc-web@69
|
520 }
|
rc-web@69
|
521
|
rc-web@69
|
522 if (this.connected[id]) {
|
rc-web@69
|
523 delete this.connected[id];
|
rc-web@69
|
524 }
|
rc-web@69
|
525
|
rc-web@69
|
526 if (this.transports[id]) {
|
rc-web@69
|
527 this.transports[id].discard();
|
rc-web@69
|
528 delete this.transports[id];
|
rc-web@69
|
529 }
|
rc-web@69
|
530
|
rc-web@69
|
531 if (this.closed[id]) {
|
rc-web@69
|
532 delete this.closed[id];
|
rc-web@69
|
533 }
|
rc-web@69
|
534
|
rc-web@69
|
535 if (this.roomClients[id]) {
|
rc-web@69
|
536 for (var room in this.roomClients[id]) {
|
rc-web@69
|
537 if (this.roomClients[id].hasOwnProperty(room)) {
|
rc-web@69
|
538 this.onLeave(id, room);
|
rc-web@69
|
539 }
|
rc-web@69
|
540 }
|
rc-web@69
|
541 delete this.roomClients[id]
|
rc-web@69
|
542 }
|
rc-web@69
|
543
|
rc-web@69
|
544 this.store.destroyClient(id, this.get('client store expiration'));
|
rc-web@69
|
545
|
rc-web@69
|
546 this.store.unsubscribe('dispatch:' + id);
|
rc-web@69
|
547
|
rc-web@69
|
548 if (local) {
|
rc-web@69
|
549 this.store.unsubscribe('message:' + id);
|
rc-web@69
|
550 this.store.unsubscribe('disconnect:' + id);
|
rc-web@69
|
551 }
|
rc-web@69
|
552 };
|
rc-web@69
|
553
|
rc-web@69
|
554 /**
|
rc-web@69
|
555 * Handles an HTTP request.
|
rc-web@69
|
556 *
|
rc-web@69
|
557 * @api private
|
rc-web@69
|
558 */
|
rc-web@69
|
559
|
rc-web@69
|
560 Manager.prototype.handleRequest = function (req, res) {
|
rc-web@69
|
561 var data = this.checkRequest(req);
|
rc-web@69
|
562
|
rc-web@69
|
563 if (!data) {
|
rc-web@69
|
564 for (var i = 0, l = this.oldListeners.length; i < l; i++) {
|
rc-web@69
|
565 this.oldListeners[i].call(this.server, req, res);
|
rc-web@69
|
566 }
|
rc-web@69
|
567
|
rc-web@69
|
568 return;
|
rc-web@69
|
569 }
|
rc-web@69
|
570
|
rc-web@69
|
571 if (data.static || !data.transport && !data.protocol) {
|
rc-web@69
|
572 if (data.static && this.enabled('browser client')) {
|
rc-web@69
|
573 this.static.write(data.path, req, res);
|
rc-web@69
|
574 } else {
|
rc-web@69
|
575 res.writeHead(200);
|
rc-web@69
|
576 res.end('Welcome to socket.io.');
|
rc-web@69
|
577
|
rc-web@69
|
578 this.log.info('unhandled socket.io url');
|
rc-web@69
|
579 }
|
rc-web@69
|
580
|
rc-web@69
|
581 return;
|
rc-web@69
|
582 }
|
rc-web@69
|
583
|
rc-web@69
|
584 if (data.protocol != protocol) {
|
rc-web@69
|
585 res.writeHead(500);
|
rc-web@69
|
586 res.end('Protocol version not supported.');
|
rc-web@69
|
587
|
rc-web@69
|
588 this.log.info('client protocol version unsupported');
|
rc-web@69
|
589 } else {
|
rc-web@69
|
590 if (data.id) {
|
rc-web@69
|
591 this.handleHTTPRequest(data, req, res);
|
rc-web@69
|
592 } else {
|
rc-web@69
|
593 this.handleHandshake(data, req, res);
|
rc-web@69
|
594 }
|
rc-web@69
|
595 }
|
rc-web@69
|
596 };
|
rc-web@69
|
597
|
rc-web@69
|
598 /**
|
rc-web@69
|
599 * Handles an HTTP Upgrade.
|
rc-web@69
|
600 *
|
rc-web@69
|
601 * @api private
|
rc-web@69
|
602 */
|
rc-web@69
|
603
|
rc-web@69
|
604 Manager.prototype.handleUpgrade = function (req, socket, head) {
|
rc-web@69
|
605 var data = this.checkRequest(req)
|
rc-web@69
|
606 , self = this;
|
rc-web@69
|
607
|
rc-web@69
|
608 if (!data) {
|
rc-web@69
|
609 if (this.enabled('destroy upgrade')) {
|
rc-web@69
|
610 socket.end();
|
rc-web@69
|
611 this.log.debug('destroying non-socket.io upgrade');
|
rc-web@69
|
612 }
|
rc-web@69
|
613
|
rc-web@69
|
614 return;
|
rc-web@69
|
615 }
|
rc-web@69
|
616
|
rc-web@69
|
617 req.head = head;
|
rc-web@69
|
618 this.handleClient(data, req);
|
rc-web@69
|
619 req.head = null;
|
rc-web@69
|
620 };
|
rc-web@69
|
621
|
rc-web@69
|
622 /**
|
rc-web@69
|
623 * Handles a normal handshaken HTTP request (eg: long-polling)
|
rc-web@69
|
624 *
|
rc-web@69
|
625 * @api private
|
rc-web@69
|
626 */
|
rc-web@69
|
627
|
rc-web@69
|
628 Manager.prototype.handleHTTPRequest = function (data, req, res) {
|
rc-web@69
|
629 req.res = res;
|
rc-web@69
|
630 this.handleClient(data, req);
|
rc-web@69
|
631 };
|
rc-web@69
|
632
|
rc-web@69
|
633 /**
|
rc-web@69
|
634 * Intantiantes a new client.
|
rc-web@69
|
635 *
|
rc-web@69
|
636 * @api private
|
rc-web@69
|
637 */
|
rc-web@69
|
638
|
rc-web@69
|
639 Manager.prototype.handleClient = function (data, req) {
|
rc-web@69
|
640 var socket = req.socket
|
rc-web@69
|
641 , store = this.store
|
rc-web@69
|
642 , self = this;
|
rc-web@69
|
643
|
rc-web@69
|
644 // handle sync disconnect xhrs
|
rc-web@69
|
645 if (undefined != data.query.disconnect) {
|
rc-web@69
|
646 if (this.transports[data.id] && this.transports[data.id].open) {
|
rc-web@69
|
647 this.transports[data.id].onForcedDisconnect();
|
rc-web@69
|
648 } else {
|
rc-web@69
|
649 this.store.publish('disconnect-force:' + data.id);
|
rc-web@69
|
650 }
|
rc-web@69
|
651 req.res.writeHead(200);
|
rc-web@69
|
652 req.res.end();
|
rc-web@69
|
653 return;
|
rc-web@69
|
654 }
|
rc-web@69
|
655
|
rc-web@69
|
656 if (!~this.get('transports').indexOf(data.transport)) {
|
rc-web@69
|
657 this.log.warn('unknown transport: "' + data.transport + '"');
|
rc-web@69
|
658 req.connection.end();
|
rc-web@69
|
659 return;
|
rc-web@69
|
660 }
|
rc-web@69
|
661
|
rc-web@69
|
662 var transport = new transports[data.transport](this, data, req)
|
rc-web@69
|
663 , handshaken = this.handshaken[data.id];
|
rc-web@69
|
664
|
rc-web@69
|
665 if (transport.disconnected) {
|
rc-web@69
|
666 // failed during transport setup
|
rc-web@69
|
667 req.connection.end();
|
rc-web@69
|
668 return;
|
rc-web@69
|
669 }
|
rc-web@69
|
670 if (handshaken) {
|
rc-web@69
|
671 if (transport.open) {
|
rc-web@69
|
672 if (this.closed[data.id] && this.closed[data.id].length) {
|
rc-web@69
|
673 transport.payload(this.closed[data.id]);
|
rc-web@69
|
674 this.closed[data.id] = [];
|
rc-web@69
|
675 }
|
rc-web@69
|
676
|
rc-web@69
|
677 this.onOpen(data.id);
|
rc-web@69
|
678 this.store.publish('open', data.id);
|
rc-web@69
|
679 this.transports[data.id] = transport;
|
rc-web@69
|
680 }
|
rc-web@69
|
681
|
rc-web@69
|
682 if (!this.connected[data.id]) {
|
rc-web@69
|
683 this.onConnect(data.id);
|
rc-web@69
|
684 this.store.publish('connect', data.id);
|
rc-web@69
|
685
|
rc-web@69
|
686 // flag as used
|
rc-web@69
|
687 delete handshaken.issued;
|
rc-web@69
|
688 this.onHandshake(data.id, handshaken);
|
rc-web@69
|
689 this.store.publish('handshake', data.id, handshaken);
|
rc-web@69
|
690
|
rc-web@69
|
691 // initialize the socket for all namespaces
|
rc-web@69
|
692 for (var i in this.namespaces) {
|
rc-web@69
|
693 if (this.namespaces.hasOwnProperty(i)) {
|
rc-web@69
|
694 var socket = this.namespaces[i].socket(data.id, true);
|
rc-web@69
|
695
|
rc-web@69
|
696 // echo back connect packet and fire connection event
|
rc-web@69
|
697 if (i === '') {
|
rc-web@69
|
698 this.namespaces[i].handlePacket(data.id, { type: 'connect' });
|
rc-web@69
|
699 }
|
rc-web@69
|
700 }
|
rc-web@69
|
701 }
|
rc-web@69
|
702
|
rc-web@69
|
703 this.store.subscribe('message:' + data.id, function (packet) {
|
rc-web@69
|
704 self.onClientMessage(data.id, packet);
|
rc-web@69
|
705 });
|
rc-web@69
|
706
|
rc-web@69
|
707 this.store.subscribe('disconnect:' + data.id, function (reason) {
|
rc-web@69
|
708 self.onClientDisconnect(data.id, reason);
|
rc-web@69
|
709 });
|
rc-web@69
|
710 }
|
rc-web@69
|
711 } else {
|
rc-web@69
|
712 if (transport.open) {
|
rc-web@69
|
713 transport.error('client not handshaken', 'reconnect');
|
rc-web@69
|
714 }
|
rc-web@69
|
715
|
rc-web@69
|
716 transport.discard();
|
rc-web@69
|
717 }
|
rc-web@69
|
718 };
|
rc-web@69
|
719
|
rc-web@69
|
720 /**
|
rc-web@69
|
721 * Generates a session id.
|
rc-web@69
|
722 *
|
rc-web@69
|
723 * @api private
|
rc-web@69
|
724 */
|
rc-web@69
|
725
|
rc-web@69
|
726 Manager.prototype.generateId = function () {
|
rc-web@69
|
727 var rand = new Buffer(15); // multiple of 3 for base64
|
rc-web@69
|
728 if (!rand.writeInt32BE) {
|
rc-web@69
|
729 return Math.abs(Math.random() * Math.random() * Date.now() | 0).toString()
|
rc-web@69
|
730 + Math.abs(Math.random() * Math.random() * Date.now() | 0).toString();
|
rc-web@69
|
731 }
|
rc-web@69
|
732 this.sequenceNumber = (this.sequenceNumber + 1) | 0;
|
rc-web@69
|
733 rand.writeInt32BE(this.sequenceNumber, 11);
|
rc-web@69
|
734 if (crypto.randomBytes) {
|
rc-web@69
|
735 crypto.randomBytes(12).copy(rand);
|
rc-web@69
|
736 } else {
|
rc-web@69
|
737 // not secure for node 0.4
|
rc-web@69
|
738 [0, 4, 8].forEach(function(i) {
|
rc-web@69
|
739 rand.writeInt32BE(Math.random() * Math.pow(2, 32) | 0, i);
|
rc-web@69
|
740 });
|
rc-web@69
|
741 }
|
rc-web@69
|
742 return rand.toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
|
rc-web@69
|
743 };
|
rc-web@69
|
744
|
rc-web@69
|
745 /**
|
rc-web@69
|
746 * Handles a handshake request.
|
rc-web@69
|
747 *
|
rc-web@69
|
748 * @api private
|
rc-web@69
|
749 */
|
rc-web@69
|
750
|
rc-web@69
|
751 Manager.prototype.handleHandshake = function (data, req, res) {
|
rc-web@69
|
752 var self = this
|
rc-web@69
|
753 , origin = req.headers.origin
|
rc-web@69
|
754 , headers = {
|
rc-web@69
|
755 'Content-Type': 'text/plain'
|
rc-web@69
|
756 };
|
rc-web@69
|
757
|
rc-web@69
|
758 function writeErr (status, message) {
|
rc-web@69
|
759 if (data.query.jsonp && jsonpolling_re.test(data.query.jsonp)) {
|
rc-web@69
|
760 res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
rc-web@69
|
761 res.end('io.j[' + data.query.jsonp + '](new Error("' + message + '"));');
|
rc-web@69
|
762 } else {
|
rc-web@69
|
763 res.writeHead(status, headers);
|
rc-web@69
|
764 res.end(message);
|
rc-web@69
|
765 }
|
rc-web@69
|
766 };
|
rc-web@69
|
767
|
rc-web@69
|
768 function error (err) {
|
rc-web@69
|
769 writeErr(500, 'handshake error');
|
rc-web@69
|
770 self.log.warn('handshake error ' + err);
|
rc-web@69
|
771 };
|
rc-web@69
|
772
|
rc-web@69
|
773 if (!this.verifyOrigin(req)) {
|
rc-web@69
|
774 writeErr(403, 'handshake bad origin');
|
rc-web@69
|
775 return;
|
rc-web@69
|
776 }
|
rc-web@69
|
777
|
rc-web@69
|
778 var handshakeData = this.handshakeData(data);
|
rc-web@69
|
779
|
rc-web@69
|
780 if (origin) {
|
rc-web@69
|
781 // https://developer.mozilla.org/En/HTTP_Access_Control
|
rc-web@69
|
782 headers['Access-Control-Allow-Origin'] = origin;
|
rc-web@69
|
783 headers['Access-Control-Allow-Credentials'] = 'true';
|
rc-web@69
|
784 }
|
rc-web@69
|
785
|
rc-web@69
|
786 this.authorize(handshakeData, function (err, authorized, newData) {
|
rc-web@69
|
787 if (err) return error(err);
|
rc-web@69
|
788
|
rc-web@69
|
789 if (authorized) {
|
rc-web@69
|
790 var id = self.generateId()
|
rc-web@69
|
791 , hs = [
|
rc-web@69
|
792 id
|
rc-web@69
|
793 , self.enabled('heartbeats') ? self.get('heartbeat timeout') || '' : ''
|
rc-web@69
|
794 , self.get('close timeout') || ''
|
rc-web@69
|
795 , self.transports(data).join(',')
|
rc-web@69
|
796 ].join(':');
|
rc-web@69
|
797
|
rc-web@69
|
798 if (data.query.jsonp && jsonpolling_re.test(data.query.jsonp)) {
|
rc-web@69
|
799 hs = 'io.j[' + data.query.jsonp + '](' + JSON.stringify(hs) + ');';
|
rc-web@69
|
800 res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
rc-web@69
|
801 } else {
|
rc-web@69
|
802 res.writeHead(200, headers);
|
rc-web@69
|
803 }
|
rc-web@69
|
804
|
rc-web@69
|
805 res.end(hs);
|
rc-web@69
|
806
|
rc-web@69
|
807 self.onHandshake(id, newData || handshakeData);
|
rc-web@69
|
808 self.store.publish('handshake', id, newData || handshakeData);
|
rc-web@69
|
809
|
rc-web@69
|
810 self.log.info('handshake authorized', id);
|
rc-web@69
|
811 } else {
|
rc-web@69
|
812 writeErr(403, 'handshake unauthorized');
|
rc-web@69
|
813 self.log.info('handshake unauthorized');
|
rc-web@69
|
814 }
|
rc-web@69
|
815 })
|
rc-web@69
|
816 };
|
rc-web@69
|
817
|
rc-web@69
|
818 /**
|
rc-web@69
|
819 * Gets normalized handshake data
|
rc-web@69
|
820 *
|
rc-web@69
|
821 * @api private
|
rc-web@69
|
822 */
|
rc-web@69
|
823
|
rc-web@69
|
824 Manager.prototype.handshakeData = function (data) {
|
rc-web@69
|
825 var connection = data.request.connection
|
rc-web@69
|
826 , connectionAddress
|
rc-web@69
|
827 , date = new Date;
|
rc-web@69
|
828
|
rc-web@69
|
829 if (connection.remoteAddress) {
|
rc-web@69
|
830 connectionAddress = {
|
rc-web@69
|
831 address: connection.remoteAddress
|
rc-web@69
|
832 , port: connection.remotePort
|
rc-web@69
|
833 };
|
rc-web@69
|
834 } else if (connection.socket && connection.socket.remoteAddress) {
|
rc-web@69
|
835 connectionAddress = {
|
rc-web@69
|
836 address: connection.socket.remoteAddress
|
rc-web@69
|
837 , port: connection.socket.remotePort
|
rc-web@69
|
838 };
|
rc-web@69
|
839 }
|
rc-web@69
|
840
|
rc-web@69
|
841 return {
|
rc-web@69
|
842 headers: data.headers
|
rc-web@69
|
843 , address: connectionAddress
|
rc-web@69
|
844 , time: date.toString()
|
rc-web@69
|
845 , query: data.query
|
rc-web@69
|
846 , url: data.request.url
|
rc-web@69
|
847 , xdomain: !!data.request.headers.origin
|
rc-web@69
|
848 , secure: data.request.connection.secure
|
rc-web@69
|
849 , issued: +date
|
rc-web@69
|
850 };
|
rc-web@69
|
851 };
|
rc-web@69
|
852
|
rc-web@69
|
853 /**
|
rc-web@69
|
854 * Verifies the origin of a request.
|
rc-web@69
|
855 *
|
rc-web@69
|
856 * @api private
|
rc-web@69
|
857 */
|
rc-web@69
|
858
|
rc-web@69
|
859 Manager.prototype.verifyOrigin = function (request) {
|
rc-web@69
|
860 var origin = request.headers.origin || request.headers.referer
|
rc-web@69
|
861 , origins = this.get('origins');
|
rc-web@69
|
862
|
rc-web@69
|
863 if (origin === 'null') origin = '*';
|
rc-web@69
|
864
|
rc-web@69
|
865 if (origins.indexOf('*:*') !== -1) {
|
rc-web@69
|
866 return true;
|
rc-web@69
|
867 }
|
rc-web@69
|
868
|
rc-web@69
|
869 if (origin) {
|
rc-web@69
|
870 try {
|
rc-web@69
|
871 var parts = url.parse(origin);
|
rc-web@69
|
872 parts.port = parts.port || 80;
|
rc-web@69
|
873 var ok =
|
rc-web@69
|
874 ~origins.indexOf(parts.hostname + ':' + parts.port) ||
|
rc-web@69
|
875 ~origins.indexOf(parts.hostname + ':*') ||
|
rc-web@69
|
876 ~origins.indexOf('*:' + parts.port);
|
rc-web@69
|
877 if (!ok) this.log.warn('illegal origin: ' + origin);
|
rc-web@69
|
878 return ok;
|
rc-web@69
|
879 } catch (ex) {
|
rc-web@69
|
880 this.log.warn('error parsing origin');
|
rc-web@69
|
881 }
|
rc-web@69
|
882 }
|
rc-web@69
|
883 else {
|
rc-web@69
|
884 this.log.warn('origin missing from handshake, yet required by config');
|
rc-web@69
|
885 }
|
rc-web@69
|
886 return false;
|
rc-web@69
|
887 };
|
rc-web@69
|
888
|
rc-web@69
|
889 /**
|
rc-web@69
|
890 * Handles an incoming packet.
|
rc-web@69
|
891 *
|
rc-web@69
|
892 * @api private
|
rc-web@69
|
893 */
|
rc-web@69
|
894
|
rc-web@69
|
895 Manager.prototype.handlePacket = function (sessid, packet) {
|
rc-web@69
|
896 this.of(packet.endpoint || '').handlePacket(sessid, packet);
|
rc-web@69
|
897 };
|
rc-web@69
|
898
|
rc-web@69
|
899 /**
|
rc-web@69
|
900 * Performs authentication.
|
rc-web@69
|
901 *
|
rc-web@69
|
902 * @param Object client request data
|
rc-web@69
|
903 * @api private
|
rc-web@69
|
904 */
|
rc-web@69
|
905
|
rc-web@69
|
906 Manager.prototype.authorize = function (data, fn) {
|
rc-web@69
|
907 if (this.get('authorization')) {
|
rc-web@69
|
908 var self = this;
|
rc-web@69
|
909
|
rc-web@69
|
910 this.get('authorization').call(this, data, function (err, authorized) {
|
rc-web@69
|
911 self.log.debug('client ' + authorized ? 'authorized' : 'unauthorized');
|
rc-web@69
|
912 fn(err, authorized);
|
rc-web@69
|
913 });
|
rc-web@69
|
914 } else {
|
rc-web@69
|
915 this.log.debug('client authorized');
|
rc-web@69
|
916 fn(null, true);
|
rc-web@69
|
917 }
|
rc-web@69
|
918
|
rc-web@69
|
919 return this;
|
rc-web@69
|
920 };
|
rc-web@69
|
921
|
rc-web@69
|
922 /**
|
rc-web@69
|
923 * Retrieves the transports adviced to the user.
|
rc-web@69
|
924 *
|
rc-web@69
|
925 * @api private
|
rc-web@69
|
926 */
|
rc-web@69
|
927
|
rc-web@69
|
928 Manager.prototype.transports = function (data) {
|
rc-web@69
|
929 var transp = this.get('transports')
|
rc-web@69
|
930 , ret = [];
|
rc-web@69
|
931
|
rc-web@69
|
932 for (var i = 0, l = transp.length; i < l; i++) {
|
rc-web@69
|
933 var transport = transp[i];
|
rc-web@69
|
934
|
rc-web@69
|
935 if (transport) {
|
rc-web@69
|
936 if (!transport.checkClient || transport.checkClient(data)) {
|
rc-web@69
|
937 ret.push(transport);
|
rc-web@69
|
938 }
|
rc-web@69
|
939 }
|
rc-web@69
|
940 }
|
rc-web@69
|
941
|
rc-web@69
|
942 return ret;
|
rc-web@69
|
943 };
|
rc-web@69
|
944
|
rc-web@69
|
945 /**
|
rc-web@69
|
946 * Checks whether a request is a socket.io one.
|
rc-web@69
|
947 *
|
rc-web@69
|
948 * @return {Object} a client request data object or `false`
|
rc-web@69
|
949 * @api private
|
rc-web@69
|
950 */
|
rc-web@69
|
951
|
rc-web@69
|
952 var regexp = /^\/([^\/]+)\/?([^\/]+)?\/?([^\/]+)?\/?$/
|
rc-web@69
|
953
|
rc-web@69
|
954 Manager.prototype.checkRequest = function (req) {
|
rc-web@69
|
955 var resource = this.get('resource');
|
rc-web@69
|
956
|
rc-web@69
|
957 var match;
|
rc-web@69
|
958 if (typeof resource === 'string') {
|
rc-web@69
|
959 match = req.url.substr(0, resource.length);
|
rc-web@69
|
960 if (match !== resource) match = null;
|
rc-web@69
|
961 } else {
|
rc-web@69
|
962 match = resource.exec(req.url);
|
rc-web@69
|
963 if (match) match = match[0];
|
rc-web@69
|
964 }
|
rc-web@69
|
965
|
rc-web@69
|
966 if (match) {
|
rc-web@69
|
967 var uri = url.parse(req.url.substr(match.length), true)
|
rc-web@69
|
968 , path = uri.pathname || ''
|
rc-web@69
|
969 , pieces = path.match(regexp);
|
rc-web@69
|
970
|
rc-web@69
|
971 // client request data
|
rc-web@69
|
972 var data = {
|
rc-web@69
|
973 query: uri.query || {}
|
rc-web@69
|
974 , headers: req.headers
|
rc-web@69
|
975 , request: req
|
rc-web@69
|
976 , path: path
|
rc-web@69
|
977 };
|
rc-web@69
|
978
|
rc-web@69
|
979 if (pieces) {
|
rc-web@69
|
980 data.protocol = Number(pieces[1]);
|
rc-web@69
|
981 data.transport = pieces[2];
|
rc-web@69
|
982 data.id = pieces[3];
|
rc-web@69
|
983 data.static = !!this.static.has(path);
|
rc-web@69
|
984 };
|
rc-web@69
|
985
|
rc-web@69
|
986 return data;
|
rc-web@69
|
987 }
|
rc-web@69
|
988
|
rc-web@69
|
989 return false;
|
rc-web@69
|
990 };
|
rc-web@69
|
991
|
rc-web@69
|
992 /**
|
rc-web@69
|
993 * Declares a socket namespace
|
rc-web@69
|
994 *
|
rc-web@69
|
995 * @api public
|
rc-web@69
|
996 */
|
rc-web@69
|
997
|
rc-web@69
|
998 Manager.prototype.of = function (nsp) {
|
rc-web@69
|
999 if (this.namespaces[nsp]) {
|
rc-web@69
|
1000 return this.namespaces[nsp];
|
rc-web@69
|
1001 }
|
rc-web@69
|
1002
|
rc-web@69
|
1003 return this.namespaces[nsp] = new SocketNamespace(this, nsp);
|
rc-web@69
|
1004 };
|
rc-web@69
|
1005
|
rc-web@69
|
1006 /**
|
rc-web@69
|
1007 * Perform garbage collection on long living objects and properties that cannot
|
rc-web@69
|
1008 * be removed automatically.
|
rc-web@69
|
1009 *
|
rc-web@69
|
1010 * @api private
|
rc-web@69
|
1011 */
|
rc-web@69
|
1012
|
rc-web@69
|
1013 Manager.prototype.garbageCollection = function () {
|
rc-web@69
|
1014 // clean up unused handshakes
|
rc-web@69
|
1015 var ids = Object.keys(this.handshaken)
|
rc-web@69
|
1016 , i = ids.length
|
rc-web@69
|
1017 , now = Date.now()
|
rc-web@69
|
1018 , handshake;
|
rc-web@69
|
1019
|
rc-web@69
|
1020 while (i--) {
|
rc-web@69
|
1021 handshake = this.handshaken[ids[i]];
|
rc-web@69
|
1022
|
rc-web@69
|
1023 if ('issued' in handshake && (now - handshake.issued) >= 3E4) {
|
rc-web@69
|
1024 this.onDisconnect(ids[i]);
|
rc-web@69
|
1025 }
|
rc-web@69
|
1026 }
|
rc-web@69
|
1027 };
|