aboutsummaryrefslogtreecommitdiffhomepage
path: root/node_modules/socket.io/lib/socket.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/socket.io/lib/socket.js')
-rw-r--r--node_modules/socket.io/lib/socket.js557
1 files changed, 557 insertions, 0 deletions
diff --git a/node_modules/socket.io/lib/socket.js b/node_modules/socket.io/lib/socket.js
new file mode 100644
index 0000000..6c6bcde
--- /dev/null
+++ b/node_modules/socket.io/lib/socket.js
@@ -0,0 +1,557 @@
+
+/**
+ * Module dependencies.
+ */
+
+var Emitter = require('events').EventEmitter;
+var parser = require('socket.io-parser');
+var url = require('url');
+var debug = require('debug')('socket.io:socket');
+
+/**
+ * Module exports.
+ */
+
+module.exports = exports = Socket;
+
+/**
+ * Blacklisted events.
+ *
+ * @api public
+ */
+
+exports.events = [
+ 'error',
+ 'connect',
+ 'disconnect',
+ 'disconnecting',
+ 'newListener',
+ 'removeListener'
+];
+
+/**
+ * Flags.
+ *
+ * @api private
+ */
+
+var flags = [
+ 'json',
+ 'volatile',
+ 'broadcast'
+];
+
+/**
+ * `EventEmitter#emit` reference.
+ */
+
+var emit = Emitter.prototype.emit;
+
+/**
+ * Interface to a `Client` for a given `Namespace`.
+ *
+ * @param {Namespace} nsp
+ * @param {Client} client
+ * @api public
+ */
+
+function Socket(nsp, client, query){
+ this.nsp = nsp;
+ this.server = nsp.server;
+ this.adapter = this.nsp.adapter;
+ this.id = nsp.name !== '/' ? nsp.name + '#' + client.id : client.id;
+ this.client = client;
+ this.conn = client.conn;
+ this.rooms = {};
+ this.acks = {};
+ this.connected = true;
+ this.disconnected = false;
+ this.handshake = this.buildHandshake(query);
+ this.fns = [];
+ this.flags = {};
+ this._rooms = [];
+}
+
+/**
+ * Inherits from `EventEmitter`.
+ */
+
+Socket.prototype.__proto__ = Emitter.prototype;
+
+/**
+ * Apply flags from `Socket`.
+ */
+
+flags.forEach(function(flag){
+ Object.defineProperty(Socket.prototype, flag, {
+ get: function() {
+ this.flags[flag] = true;
+ return this;
+ }
+ });
+});
+
+/**
+ * `request` engine.io shortcut.
+ *
+ * @api public
+ */
+
+Object.defineProperty(Socket.prototype, 'request', {
+ get: function() {
+ return this.conn.request;
+ }
+});
+
+/**
+ * Builds the `handshake` BC object
+ *
+ * @api private
+ */
+
+Socket.prototype.buildHandshake = function(query){
+ var self = this;
+ function buildQuery(){
+ var requestQuery = url.parse(self.request.url, true).query;
+ //if socket-specific query exist, replace query strings in requestQuery
+ return Object.assign({}, query, requestQuery);
+ }
+ return {
+ headers: this.request.headers,
+ time: (new Date) + '',
+ address: this.conn.remoteAddress,
+ xdomain: !!this.request.headers.origin,
+ secure: !!this.request.connection.encrypted,
+ issued: +(new Date),
+ url: this.request.url,
+ query: buildQuery()
+ };
+};
+
+/**
+ * Emits to this client.
+ *
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.emit = function(ev){
+ if (~exports.events.indexOf(ev)) {
+ emit.apply(this, arguments);
+ return this;
+ }
+
+ var args = Array.prototype.slice.call(arguments);
+ var packet = {
+ type: parser.EVENT,
+ data: args
+ };
+
+ // access last argument to see if it's an ACK callback
+ if (typeof args[args.length - 1] === 'function') {
+ if (this._rooms.length || this.flags.broadcast) {
+ throw new Error('Callbacks are not supported when broadcasting');
+ }
+
+ debug('emitting packet with ack id %d', this.nsp.ids);
+ this.acks[this.nsp.ids] = args.pop();
+ packet.id = this.nsp.ids++;
+ }
+
+ var rooms = this._rooms.slice(0);
+ var flags = Object.assign({}, this.flags);
+
+ // reset flags
+ this._rooms = [];
+ this.flags = {};
+
+ if (rooms.length || flags.broadcast) {
+ this.adapter.broadcast(packet, {
+ except: [this.id],
+ rooms: rooms,
+ flags: flags
+ });
+ } else {
+ // dispatch packet
+ this.packet(packet, flags);
+ }
+ return this;
+};
+
+/**
+ * Targets a room when broadcasting.
+ *
+ * @param {String} name
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.to =
+Socket.prototype.in = function(name){
+ if (!~this._rooms.indexOf(name)) this._rooms.push(name);
+ return this;
+};
+
+/**
+ * Sends a `message` event.
+ *
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.send =
+Socket.prototype.write = function(){
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift('message');
+ this.emit.apply(this, args);
+ return this;
+};
+
+/**
+ * Writes a packet.
+ *
+ * @param {Object} packet object
+ * @param {Object} opts options
+ * @api private
+ */
+
+Socket.prototype.packet = function(packet, opts){
+ packet.nsp = this.nsp.name;
+ opts = opts || {};
+ opts.compress = false !== opts.compress;
+ this.client.packet(packet, opts);
+};
+
+/**
+ * Joins a room.
+ *
+ * @param {String|Array} room or array of rooms
+ * @param {Function} fn optional, callback
+ * @return {Socket} self
+ * @api private
+ */
+
+Socket.prototype.join = function(rooms, fn){
+ debug('joining room %s', rooms);
+ var self = this;
+ if (!Array.isArray(rooms)) {
+ rooms = [rooms];
+ }
+ rooms = rooms.filter(function (room) {
+ return !self.rooms.hasOwnProperty(room);
+ });
+ if (!rooms.length) {
+ fn && fn(null);
+ return this;
+ }
+ this.adapter.addAll(this.id, rooms, function(err){
+ if (err) return fn && fn(err);
+ debug('joined room %s', rooms);
+ rooms.forEach(function (room) {
+ self.rooms[room] = room;
+ });
+ fn && fn(null);
+ });
+ return this;
+};
+
+/**
+ * Leaves a room.
+ *
+ * @param {String} room
+ * @param {Function} fn optional, callback
+ * @return {Socket} self
+ * @api private
+ */
+
+Socket.prototype.leave = function(room, fn){
+ debug('leave room %s', room);
+ var self = this;
+ this.adapter.del(this.id, room, function(err){
+ if (err) return fn && fn(err);
+ debug('left room %s', room);
+ delete self.rooms[room];
+ fn && fn(null);
+ });
+ return this;
+};
+
+/**
+ * Leave all rooms.
+ *
+ * @api private
+ */
+
+Socket.prototype.leaveAll = function(){
+ this.adapter.delAll(this.id);
+ this.rooms = {};
+};
+
+/**
+ * Called by `Namespace` upon successful
+ * middleware execution (ie: authorization).
+ * Socket is added to namespace array before
+ * call to join, so adapters can access it.
+ *
+ * @api private
+ */
+
+Socket.prototype.onconnect = function(){
+ debug('socket connected - writing packet');
+ this.nsp.connected[this.id] = this;
+ this.join(this.id);
+ var skip = this.nsp.name === '/' && this.nsp.fns.length === 0;
+ if (skip) {
+ debug('packet already sent in initial handshake');
+ } else {
+ this.packet({ type: parser.CONNECT });
+ }
+};
+
+/**
+ * Called with each packet. Called by `Client`.
+ *
+ * @param {Object} packet
+ * @api private
+ */
+
+Socket.prototype.onpacket = function(packet){
+ debug('got packet %j', packet);
+ switch (packet.type) {
+ case parser.EVENT:
+ this.onevent(packet);
+ break;
+
+ case parser.BINARY_EVENT:
+ this.onevent(packet);
+ break;
+
+ case parser.ACK:
+ this.onack(packet);
+ break;
+
+ case parser.BINARY_ACK:
+ this.onack(packet);
+ break;
+
+ case parser.DISCONNECT:
+ this.ondisconnect();
+ break;
+
+ case parser.ERROR:
+ this.onerror(new Error(packet.data));
+ }
+};
+
+/**
+ * Called upon event packet.
+ *
+ * @param {Object} packet object
+ * @api private
+ */
+
+Socket.prototype.onevent = function(packet){
+ var args = packet.data || [];
+ debug('emitting event %j', args);
+
+ if (null != packet.id) {
+ debug('attaching ack callback to event');
+ args.push(this.ack(packet.id));
+ }
+
+ this.dispatch(args);
+};
+
+/**
+ * Produces an ack callback to emit with an event.
+ *
+ * @param {Number} id packet id
+ * @api private
+ */
+
+Socket.prototype.ack = function(id){
+ var self = this;
+ var sent = false;
+ return function(){
+ // prevent double callbacks
+ if (sent) return;
+ var args = Array.prototype.slice.call(arguments);
+ debug('sending ack %j', args);
+
+ self.packet({
+ id: id,
+ type: parser.ACK,
+ data: args
+ });
+
+ sent = true;
+ };
+};
+
+/**
+ * Called upon ack packet.
+ *
+ * @api private
+ */
+
+Socket.prototype.onack = function(packet){
+ var ack = this.acks[packet.id];
+ if ('function' == typeof ack) {
+ debug('calling ack %s with %j', packet.id, packet.data);
+ ack.apply(this, packet.data);
+ delete this.acks[packet.id];
+ } else {
+ debug('bad ack %s', packet.id);
+ }
+};
+
+/**
+ * Called upon client disconnect packet.
+ *
+ * @api private
+ */
+
+Socket.prototype.ondisconnect = function(){
+ debug('got disconnect packet');
+ this.onclose('client namespace disconnect');
+};
+
+/**
+ * Handles a client error.
+ *
+ * @api private
+ */
+
+Socket.prototype.onerror = function(err){
+ if (this.listeners('error').length) {
+ this.emit('error', err);
+ } else {
+ console.error('Missing error handler on `socket`.');
+ console.error(err.stack);
+ }
+};
+
+/**
+ * Called upon closing. Called by `Client`.
+ *
+ * @param {String} reason
+ * @throw {Error} optional error object
+ * @api private
+ */
+
+Socket.prototype.onclose = function(reason){
+ if (!this.connected) return this;
+ debug('closing socket - reason %s', reason);
+ this.emit('disconnecting', reason);
+ this.leaveAll();
+ this.nsp.remove(this);
+ this.client.remove(this);
+ this.connected = false;
+ this.disconnected = true;
+ delete this.nsp.connected[this.id];
+ this.emit('disconnect', reason);
+};
+
+/**
+ * Produces an `error` packet.
+ *
+ * @param {Object} err error object
+ * @api private
+ */
+
+Socket.prototype.error = function(err){
+ this.packet({ type: parser.ERROR, data: err });
+};
+
+/**
+ * Disconnects this client.
+ *
+ * @param {Boolean} close if `true`, closes the underlying connection
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.disconnect = function(close){
+ if (!this.connected) return this;
+ if (close) {
+ this.client.disconnect();
+ } else {
+ this.packet({ type: parser.DISCONNECT });
+ this.onclose('server namespace disconnect');
+ }
+ return this;
+};
+
+/**
+ * Sets the compress flag.
+ *
+ * @param {Boolean} compress if `true`, compresses the sending data
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.compress = function(compress){
+ this.flags.compress = compress;
+ return this;
+};
+
+/**
+ * Dispatch incoming event to socket listeners.
+ *
+ * @param {Array} event that will get emitted
+ * @api private
+ */
+
+Socket.prototype.dispatch = function(event){
+ debug('dispatching an event %j', event);
+ var self = this;
+ function dispatchSocket(err) {
+ process.nextTick(function(){
+ if (err) {
+ return self.error(err.data || err.message);
+ }
+ emit.apply(self, event);
+ });
+ }
+ this.run(event, dispatchSocket);
+};
+
+/**
+ * Sets up socket middleware.
+ *
+ * @param {Function} middleware function (event, next)
+ * @return {Socket} self
+ * @api public
+ */
+
+Socket.prototype.use = function(fn){
+ this.fns.push(fn);
+ return this;
+};
+
+/**
+ * Executes the middleware for an incoming event.
+ *
+ * @param {Array} event that will get emitted
+ * @param {Function} last fn call in the middleware
+ * @api private
+ */
+Socket.prototype.run = function(event, fn){
+ var fns = this.fns.slice(0);
+ if (!fns.length) return fn(null);
+
+ function run(i){
+ fns[i](event, function(err){
+ // upon error, short-circuit
+ if (err) return fn(err);
+
+ // if no middleware left, summon callback
+ if (!fns[i + 1]) return fn(null);
+
+ // go on to next
+ run(i + 1);
+ });
+ }
+
+ run(0);
+};