rc-web@69: rc-web@69: /*! rc-web@69: * socket.io-node rc-web@69: * Copyright(c) 2011 LearnBoost rc-web@69: * MIT Licensed rc-web@69: */ rc-web@69: rc-web@69: /** rc-web@69: * Module dependencies. rc-web@69: */ rc-web@69: rc-web@69: var parser = require('./parser') rc-web@69: , util = require('./util') rc-web@69: , EventEmitter = process.EventEmitter rc-web@69: rc-web@69: /** rc-web@69: * Export the constructor. rc-web@69: */ rc-web@69: rc-web@69: exports = module.exports = Socket; rc-web@69: rc-web@69: /** rc-web@69: * Default error event listener to prevent uncaught exceptions. rc-web@69: */ rc-web@69: rc-web@69: var defaultError = function () {}; rc-web@69: rc-web@69: /** rc-web@69: * Socket constructor. rc-web@69: * rc-web@69: * @param {Manager} manager instance rc-web@69: * @param {String} session id rc-web@69: * @param {Namespace} namespace the socket belongs to rc-web@69: * @param {Boolean} whether the rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: function Socket (manager, id, nsp, readable) { rc-web@69: this.id = id; rc-web@69: this.namespace = nsp; rc-web@69: this.manager = manager; rc-web@69: this.disconnected = false; rc-web@69: this.ackPackets = 0; rc-web@69: this.acks = {}; rc-web@69: this.setFlags(); rc-web@69: this.readable = readable; rc-web@69: this.store = this.manager.store.client(this.id); rc-web@69: this.on('error', defaultError); rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Inherits from EventEmitter. rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.__proto__ = EventEmitter.prototype; rc-web@69: rc-web@69: /** rc-web@69: * Accessor shortcut for the handshake data rc-web@69: * rc-web@69: * @api private rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.__defineGetter__('handshake', function () { rc-web@69: return this.manager.handshaken[this.id]; rc-web@69: }); rc-web@69: rc-web@69: /** rc-web@69: * Accessor shortcut for the transport type rc-web@69: * rc-web@69: * @api private rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.__defineGetter__('transport', function () { rc-web@69: return this.manager.transports[this.id].name; rc-web@69: }); rc-web@69: rc-web@69: /** rc-web@69: * Accessor shortcut for the logger. rc-web@69: * rc-web@69: * @api private rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.__defineGetter__('log', function () { rc-web@69: return this.manager.log; rc-web@69: }); rc-web@69: rc-web@69: /** rc-web@69: * JSON message flag. rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.__defineGetter__('json', function () { rc-web@69: this.flags.json = true; rc-web@69: return this; rc-web@69: }); rc-web@69: rc-web@69: /** rc-web@69: * Volatile message flag. rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.__defineGetter__('volatile', function () { rc-web@69: this.flags.volatile = true; rc-web@69: return this; rc-web@69: }); rc-web@69: rc-web@69: /** rc-web@69: * Broadcast message flag. rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.__defineGetter__('broadcast', function () { rc-web@69: this.flags.broadcast = true; rc-web@69: return this; rc-web@69: }); rc-web@69: rc-web@69: /** rc-web@69: * Overrides the room to broadcast messages to (flag) rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.to = Socket.prototype.in = function (room) { rc-web@69: this.flags.room = room; rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Resets flags rc-web@69: * rc-web@69: * @api private rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.setFlags = function () { rc-web@69: this.flags = { rc-web@69: endpoint: this.namespace.name rc-web@69: , room: '' rc-web@69: }; rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Triggered on disconnect rc-web@69: * rc-web@69: * @api private rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.onDisconnect = function (reason) { rc-web@69: if (!this.disconnected) { rc-web@69: this.$emit('disconnect', reason); rc-web@69: this.disconnected = true; rc-web@69: } rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Joins a user to a room. rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.join = function (name, fn) { rc-web@69: var nsp = this.namespace.name rc-web@69: , name = (nsp + '/') + name; rc-web@69: rc-web@69: this.manager.onJoin(this.id, name); rc-web@69: this.manager.store.publish('join', this.id, name); rc-web@69: rc-web@69: if (fn) { rc-web@69: this.log.warn('Client#join callback is deprecated'); rc-web@69: fn(); rc-web@69: } rc-web@69: rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Un-joins a user from a room. rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.leave = function (name, fn) { rc-web@69: var nsp = this.namespace.name rc-web@69: , name = (nsp + '/') + name; rc-web@69: rc-web@69: this.manager.onLeave(this.id, name); rc-web@69: this.manager.store.publish('leave', this.id, name); rc-web@69: rc-web@69: if (fn) { rc-web@69: this.log.warn('Client#leave callback is deprecated'); rc-web@69: fn(); rc-web@69: } rc-web@69: rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Transmits a packet. rc-web@69: * rc-web@69: * @api private rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.packet = function (packet) { rc-web@69: if (this.flags.broadcast) { rc-web@69: this.log.debug('broadcasting packet'); rc-web@69: this.namespace.in(this.flags.room).except(this.id).packet(packet); rc-web@69: } else { rc-web@69: packet.endpoint = this.flags.endpoint; rc-web@69: packet = parser.encodePacket(packet); rc-web@69: rc-web@69: this.dispatch(packet, this.flags.volatile); rc-web@69: } rc-web@69: rc-web@69: this.setFlags(); rc-web@69: rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Dispatches a packet rc-web@69: * rc-web@69: * @api private rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.dispatch = function (packet, volatile) { rc-web@69: if (this.manager.transports[this.id] && this.manager.transports[this.id].open) { rc-web@69: this.manager.transports[this.id].onDispatch(packet, volatile); rc-web@69: } else { rc-web@69: if (!volatile) { rc-web@69: this.manager.onClientDispatch(this.id, packet, volatile); rc-web@69: } rc-web@69: rc-web@69: this.manager.store.publish('dispatch:' + this.id, packet, volatile); rc-web@69: } rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Stores data for the client. rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.set = function (key, value, fn) { rc-web@69: this.store.set(key, value, fn); rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Retrieves data for the client rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.get = function (key, fn) { rc-web@69: this.store.get(key, fn); rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Checks data for the client rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.has = function (key, fn) { rc-web@69: this.store.has(key, fn); rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Deletes data for the client rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.del = function (key, fn) { rc-web@69: this.store.del(key, fn); rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Kicks client rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.disconnect = function () { rc-web@69: if (!this.disconnected) { rc-web@69: this.log.info('booting client'); rc-web@69: rc-web@69: if ('' === this.namespace.name) { rc-web@69: if (this.manager.transports[this.id] && this.manager.transports[this.id].open) { rc-web@69: this.manager.transports[this.id].onForcedDisconnect(); rc-web@69: } else { rc-web@69: this.manager.onClientDisconnect(this.id); rc-web@69: this.manager.store.publish('disconnect:' + this.id); rc-web@69: } rc-web@69: } else { rc-web@69: this.packet({type: 'disconnect'}); rc-web@69: this.manager.onLeave(this.id, this.namespace.name); rc-web@69: this.$emit('disconnect', 'booted'); rc-web@69: } rc-web@69: rc-web@69: } rc-web@69: rc-web@69: return this; rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Send a message. rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.send = function (data, fn) { rc-web@69: var packet = { rc-web@69: type: this.flags.json ? 'json' : 'message' rc-web@69: , data: data rc-web@69: }; rc-web@69: rc-web@69: if (fn) { rc-web@69: packet.id = ++this.ackPackets; rc-web@69: packet.ack = true; rc-web@69: this.acks[packet.id] = fn; rc-web@69: } rc-web@69: rc-web@69: return this.packet(packet); rc-web@69: }; rc-web@69: rc-web@69: /** rc-web@69: * Original emit function. rc-web@69: * rc-web@69: * @api private rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.$emit = EventEmitter.prototype.emit; rc-web@69: rc-web@69: /** rc-web@69: * Emit override for custom events. rc-web@69: * rc-web@69: * @api public rc-web@69: */ rc-web@69: rc-web@69: Socket.prototype.emit = function (ev) { rc-web@69: if (ev == 'newListener') { rc-web@69: return this.$emit.apply(this, arguments); rc-web@69: } rc-web@69: rc-web@69: var args = util.toArray(arguments).slice(1) rc-web@69: , lastArg = args[args.length - 1] rc-web@69: , packet = { rc-web@69: type: 'event' rc-web@69: , name: ev rc-web@69: }; rc-web@69: rc-web@69: if ('function' == typeof lastArg) { rc-web@69: packet.id = ++this.ackPackets; rc-web@69: packet.ack = lastArg.length ? 'data' : true; rc-web@69: this.acks[packet.id] = lastArg; rc-web@69: args = args.slice(0, args.length - 1); rc-web@69: } rc-web@69: rc-web@69: packet.args = args; rc-web@69: rc-web@69: return this.packet(packet); rc-web@69: };