metamaps--metamaps/realtime/node_modules/socket.io/lib/namespace.js

355 lines
6.9 KiB
JavaScript

/**
* 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);
};
};