/lib/socket.js
JavaScript | 355 lines | 172 code | 61 blank | 122 comment | 21 complexity | f29f8a5b81c816d1c40c85bd1ce87ce3 MD5 | raw file
- /**
- * Module dependencies.
- */
- var parser = require('socket.io-parser');
- var Emitter = require('emitter');
- var toArray = require('to-array');
- var on = require('./on');
- var bind = require('bind');
- var debug = require('debug')('socket.io-client:socket');
- var hasBin = require('has-binary-data');
- var indexOf = require('indexof');
- /**
- * Module exports.
- */
- module.exports = exports = Socket;
- /**
- * Internal events (blacklisted).
- * These events can't be emitted by the user.
- *
- * @api private
- */
- var events = {
- connect: 1,
- disconnect: 1,
- error: 1
- };
- /**
- * Shortcut to `Emitter#emit`.
- */
- var emit = Emitter.prototype.emit;
- /**
- * `Socket` constructor.
- *
- * @api public
- */
- function Socket(io, nsp){
- this.io = io;
- this.nsp = nsp;
- this.json = this; // compat
- this.ids = 0;
- this.acks = {};
- this.open();
- this.buffer = [];
- this.connected = false;
- this.disconnected = true;
- }
- /**
- * Mix in `Emitter`.
- */
- Emitter(Socket.prototype);
- /**
- * Called upon engine `open`.
- *
- * @api private
- */
- Socket.prototype.open =
- Socket.prototype.connect = function(){
- if (this.connected) return this;
- var io = this.io;
- io.open(); // ensure open
- this.subs = [
- on(io, 'open', bind(this, 'onopen')),
- on(io, 'error', bind(this, 'onerror')),
- on(io, 'packet', bind(this, 'onpacket')),
- on(io, 'close', bind(this, 'onclose'))
- ];
- if ('open' == this.io.readyState) this.onopen();
- return this;
- };
- /**
- * Sends a `message` event.
- *
- * @return {Socket} self
- * @api public
- */
- Socket.prototype.send = function(){
- var args = toArray(arguments);
- args.unshift('message');
- this.emit.apply(this, args);
- return this;
- };
- /**
- * Override `emit`.
- * If the event is in `events`, it's emitted normally.
- *
- * @param {String} event name
- * @return {Socket} self
- * @api public
- */
- Socket.prototype.emit = function(ev){
- if (events.hasOwnProperty(ev)) {
- emit.apply(this, arguments);
- return this;
- }
- var args = toArray(arguments);
- var parserType = parser.EVENT; // default
- if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary
- var packet = { type: parserType, data: args };
- // event ack callback
- if ('function' == typeof args[args.length - 1]) {
- debug('emitting packet with ack id %d', this.ids);
- this.acks[this.ids] = args.pop();
- packet.id = this.ids++;
- }
- this.packet(packet);
- return this;
- };
- /**
- * Sends a packet.
- *
- * @param {Object} packet
- * @api private
- */
- Socket.prototype.packet = function(packet){
- packet.nsp = this.nsp;
- this.io.packet(packet);
- };
- /**
- * Called upon `error`.
- *
- * @param {Object} data
- * @api private
- */
- Socket.prototype.onerror = function(data){
- this.emit('error', data);
- };
- /**
- * "Opens" the socket.
- *
- * @api private
- */
- Socket.prototype.onopen = function(){
- debug('transport is open - connecting');
- // write connect packet if necessary
- if ('/' != this.nsp) {
- this.packet({ type: parser.CONNECT });
- }
- };
- /**
- * Called upon engine `close`.
- *
- * @param {String} reason
- * @api private
- */
- Socket.prototype.onclose = function(reason){
- debug('close (%s)', reason);
- this.connected = false;
- this.disconnected = true;
- this.emit('disconnect', reason);
- };
- /**
- * Called with socket packet.
- *
- * @param {Object} packet
- * @api private
- */
- Socket.prototype.onpacket = function(packet){
- if (packet.nsp != this.nsp) return;
- switch (packet.type) {
- case parser.CONNECT:
- this.onconnect();
- break;
- case parser.EVENT:
- this.onevent(packet);
- break;
- case parser.BINARY_EVENT:
- this.onevent(packet);
- break;
- case parser.ACK:
- this.onack(packet);
- break;
- case parser.DISCONNECT:
- this.ondisconnect();
- break;
- case parser.ERROR:
- this.emit('error', packet.data);
- break;
- }
- };
- /**
- * Called upon a server event.
- *
- * @param {Object} packet
- * @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));
- }
- if (this.connected) {
- emit.apply(this, args);
- } else {
- this.buffer.push(args);
- }
- };
- /**
- * Produces an ack callback to emit with an event.
- *
- * @api private
- */
- Socket.prototype.ack = function(id){
- var self = this;
- var sent = false;
- return function(){
- // prevent double callbacks
- if (sent) return;
- sent = true;
- var args = toArray(arguments);
- debug('sending ack %j', args);
- self.packet({
- type: parser.ACK,
- id: id,
- data: args
- });
- };
- };
- /**
- * Called upon a server acknowlegement.
- *
- * @param {Object} packet
- * @api private
- */
- Socket.prototype.onack = function(packet){
- debug('calling ack %s with %j', packet.id, packet.data);
- var fn = this.acks[packet.id];
- fn.apply(this, packet.data);
- delete this.acks[packet.id];
- };
- /**
- * Called upon server connect.
- *
- * @api private
- */
- Socket.prototype.onconnect = function(){
- this.connected = true;
- this.disconnected = false;
- this.emit('connect');
- this.emitBuffered();
- };
- /**
- * Emit buffered events.
- *
- * @api private
- */
- Socket.prototype.emitBuffered = function(){
- for (var i = 0; i < this.buffer.length; i++) {
- emit.apply(this, this.buffer[i]);
- }
- this.buffer = [];
- };
- /**
- * Called upon server disconnect.
- *
- * @api private
- */
- Socket.prototype.ondisconnect = function(){
- debug('server disconnect (%s)', this.nsp);
- this.destroy();
- this.onclose('io server disconnect');
- };
- /**
- * Called upon forced client/server side disconnections,
- * this method ensures the manager stops tracking us and
- * that reconnections don't get triggered for this.
- *
- * @api private.
- */
- Socket.prototype.destroy = function(){
- // clean subscriptions to avoid reconnections
- for (var i = 0; i < this.subs.length; i++) {
- this.subs[i].destroy();
- }
- this.io.destroy(this);
- };
- /**
- * Disconnects the socket manually.
- *
- * @return {Socket} self
- * @api public
- */
- Socket.prototype.close =
- Socket.prototype.disconnect = function(){
- if (!this.connected) return this;
- debug('performing disconnect (%s)', this.nsp);
- this.packet({ type: parser.DISCONNECT });
- // remove socket from pool
- this.destroy();
- // fire events
- this.onclose('io client disconnect');
- return this;
- };