Advertisement
Guest User

Untitled

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