123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- /**
- * Module dependencies.
- */
- var Socket = require('./socket')
- , EventEmitter = process.EventEmitter
- , parser = require('./parser')
- , util = require('./util');
- /**
- * Exports the constructor.
- */
- exports = module.exports = SocketNamespace;
- /**
- * Constructor.
- *
- * @api public.
- */
- function SocketNamespace (mgr, name) {
- this.manager = mgr;
- this.name = name || '';
- this.sockets = {};
- this.auth = false;
- this.setFlags();
- };
- /**
- * Inherits from EventEmitter.
- */
- SocketNamespace.prototype.__proto__ = EventEmitter.prototype;
- /**
- * Copies emit since we override it.
- *
- * @api private
- */
- SocketNamespace.prototype.$emit = EventEmitter.prototype.emit;
- /**
- * Retrieves all clients as Socket instances as an array.
- *
- * @api public
- */
- SocketNamespace.prototype.clients = function (room) {
- var room = this.name + (room !== undefined ?
- '/' + room : '');
- if (!this.manager.rooms[room]) {
- return [];
- }
- return this.manager.rooms[room].map(function (id) {
- return this.socket(id);
- }, this);
- };
- /**
- * Access logger interface.
- *
- * @api public
- */
- SocketNamespace.prototype.__defineGetter__('log', function () {
- return this.manager.log;
- });
- /**
- * Access store.
- *
- * @api public
- */
- SocketNamespace.prototype.__defineGetter__('store', function () {
- return this.manager.store;
- });
- /**
- * JSON message flag.
- *
- * @api public
- */
- SocketNamespace.prototype.__defineGetter__('json', function () {
- this.flags.json = true;
- return this;
- });
- /**
- * Volatile message flag.
- *
- * @api public
- */
- SocketNamespace.prototype.__defineGetter__('volatile', function () {
- this.flags.volatile = true;
- return this;
- });
- /**
- * Overrides the room to relay messages to (flag).
- *
- * @api public
- */
- SocketNamespace.prototype.in = SocketNamespace.prototype.to = function (room) {
- this.flags.endpoint = this.name + (room ? '/' + room : '');
- return this;
- };
- /**
- * Adds a session id we should prevent relaying messages to (flag).
- *
- * @api public
- */
- SocketNamespace.prototype.except = function (id) {
- this.flags.exceptions.push(id);
- return this;
- };
- /**
- * Sets the default flags.
- *
- * @api private
- */
- SocketNamespace.prototype.setFlags = function () {
- this.flags = {
- endpoint: this.name
- , exceptions: []
- };
- return this;
- };
- /**
- * Sends out a packet.
- *
- * @api private
- */
- SocketNamespace.prototype.packet = function (packet) {
- packet.endpoint = this.name;
- var store = this.store
- , log = this.log
- , volatile = this.flags.volatile
- , exceptions = this.flags.exceptions
- , packet = parser.encodePacket(packet);
- this.manager.onDispatch(this.flags.endpoint, packet, volatile, exceptions);
- this.store.publish('dispatch', this.flags.endpoint, packet, volatile, exceptions);
- this.setFlags();
- return this;
- };
- /**
- * Sends to everyone.
- *
- * @api public
- */
- SocketNamespace.prototype.send = function (data) {
- return this.packet({
- type: this.flags.json ? 'json' : 'message'
- , data: data
- });
- };
- /**
- * Emits to everyone (override).
- *
- * @api public
- */
- SocketNamespace.prototype.emit = function (name) {
- if (name == 'newListener') {
- return this.$emit.apply(this, arguments);
- }
- return this.packet({
- type: 'event'
- , name: name
- , args: util.toArray(arguments).slice(1)
- });
- };
- /**
- * Retrieves or creates a write-only socket for a client, unless specified.
- *
- * @param {Boolean} whether the socket will be readable when initialized
- * @api public
- */
- SocketNamespace.prototype.socket = function (sid, readable) {
- if (!this.sockets[sid]) {
- this.sockets[sid] = new Socket(this.manager, sid, this, readable);
- }
- return this.sockets[sid];
- };
- /**
- * Sets authorization for this namespace.
- *
- * @api public
- */
- SocketNamespace.prototype.authorization = function (fn) {
- this.auth = fn;
- return this;
- };
- /**
- * Called when a socket disconnects entirely.
- *
- * @api private
- */
- SocketNamespace.prototype.handleDisconnect = function (sid, reason, raiseOnDisconnect) {
- if (this.sockets[sid] && this.sockets[sid].readable) {
- if (raiseOnDisconnect) this.sockets[sid].onDisconnect(reason);
- delete this.sockets[sid];
- }
- };
- /**
- * Performs authentication.
- *
- * @param Object client request data
- * @api private
- */
- SocketNamespace.prototype.authorize = function (data, fn) {
- if (this.auth) {
- var self = this;
- this.auth.call(this, data, function (err, authorized) {
- self.log.debug('client ' +
- (authorized ? '' : 'un') + 'authorized for ' + self.name);
- fn(err, authorized);
- });
- } else {
- this.log.debug('client authorized for ' + this.name);
- fn(null, true);
- }
- return this;
- };
- /**
- * Handles a packet.
- *
- * @api private
- */
- SocketNamespace.prototype.handlePacket = function (sessid, packet) {
- var socket = this.socket(sessid)
- , dataAck = packet.ack == 'data'
- , manager = this.manager
- , self = this;
- function ack () {
- self.log.debug('sending data ack packet');
- socket.packet({
- type: 'ack'
- , args: util.toArray(arguments)
- , ackId: packet.id
- });
- };
- function error (err) {
- self.log.warn('handshake error ' + err + ' for ' + self.name);
- socket.packet({ type: 'error', reason: err });
- };
- function connect () {
- self.manager.onJoin(sessid, self.name);
- self.store.publish('join', sessid, self.name);
- // packet echo
- socket.packet({ type: 'connect' });
- // emit connection event
- self.$emit('connection', socket);
- };
- switch (packet.type) {
- case 'connect':
- if (packet.endpoint == '') {
- connect();
- } else {
- var handshakeData = manager.handshaken[sessid];
- this.authorize(handshakeData, function (err, authorized, newData) {
- if (err) return error(err);
- if (authorized) {
- manager.onHandshake(sessid, newData || handshakeData);
- self.store.publish('handshake', sessid, newData || handshakeData);
- connect();
- } else {
- error('unauthorized');
- }
- });
- }
- break;
- case 'ack':
- if (socket.acks[packet.ackId]) {
- socket.acks[packet.ackId].apply(socket, packet.args);
- } else {
- this.log.info('unknown ack packet');
- }
- break;
- case 'event':
- // check if the emitted event is not blacklisted
- if (-~manager.get('blacklist').indexOf(packet.name)) {
- this.log.debug('ignoring blacklisted event `' + packet.name + '`');
- } else {
- var params = [packet.name].concat(packet.args);
- if (dataAck) {
- params.push(ack);
- }
- socket.$emit.apply(socket, params);
- }
- break;
- case 'disconnect':
- this.manager.onLeave(sessid, this.name);
- this.store.publish('leave', sessid, this.name);
- socket.$emit('disconnect', packet.reason || 'packet');
- break;
- case 'json':
- case 'message':
- var params = ['message', packet.data];
- if (dataAck)
- params.push(ack);
- socket.$emit.apply(socket, params);
- };
- };
|