UNPKG

socket.io-client-v2

Version:

[![Build Status](https://github.com/socketio/socket.io-client/workflows/CI/badge.svg)](https://github.com/socketio/socket.io-client/actions) [![Dependency Status](https://david-dm.org/socketio/socket.io-client.svg)](https://david-dm.org/socketio/socket.io

2,141 lines (1,745 loc) 133 kB
/*! * Socket.IO v2.5.0 * (c) 2014-2021 Guillermo Rauch * Released under the MIT License. */ (function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["io"] = factory(); else root["io"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** * Module dependencies. */ var url = __webpack_require__(1); var parser = __webpack_require__(4); var Manager = __webpack_require__(9); var debug = __webpack_require__(3)('socket.io-client'); /** * Module exports. */ module.exports = exports = lookup; /** * Managers cache. */ var cache = exports.managers = {}; /** * Looks up an existing `Manager` for multiplexing. * If the user summons: * * `io('http://localhost/a');` * `io('http://localhost/b');` * * We reuse the existing instance based on same scheme/port/host, * and we initialize sockets for each namespace. * * @api public */ function lookup(uri, opts) { if ((typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) === 'object') { opts = uri; uri = undefined; } opts = opts || {}; var parsed = url(uri); var source = parsed.source; var id = parsed.id; var path = parsed.path; var sameNamespace = cache[id] && path in cache[id].nsps; var newConnection = opts.forceNew || opts['force new connection'] || false === opts.multiplex || sameNamespace; var io; if (newConnection) { io = Manager(source, opts); } else { if (!cache[id]) { cache[id] = Manager(source, opts); } io = cache[id]; } if (parsed.query && !opts.query) { opts.query = parsed.query; } return io.socket(parsed.path, opts); } /** * Protocol version. * * @api public */ exports.protocol = parser.protocol; /** * `connect`. * * @param {String} uri * @api public */ exports.connect = lookup; /** * Expose constructors for standalone build. * * @api public */ exports.Manager = __webpack_require__(9); exports.Socket = __webpack_require__(34); /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { 'use strict'; /** * Module dependencies. */ var parseuri = __webpack_require__(2); var debug = __webpack_require__(3)('socket.io-client:url'); /** * Module exports. */ module.exports = url; /** * URL parser. * * @param {String} url * @param {Object} An object meant to mimic window.location. * Defaults to window.location. * @api public */ function url(uri, loc) { var obj = uri; // default to window.location loc = loc || typeof location !== 'undefined' && location; if (null == uri) uri = loc.protocol + '//' + loc.host; // relative path support if ('string' === typeof uri) { if ('/' === uri.charAt(0)) { if ('/' === uri.charAt(1)) { uri = loc.protocol + uri; } else { uri = loc.host + uri; } } if (!/^(https?|wss?):\/\//.test(uri)) { if ('undefined' !== typeof loc) { uri = loc.protocol + '//' + uri; } else { uri = 'https://' + uri; } } // parse obj = parseuri(uri); } // make sure we treat `localhost:80` and `localhost` equally if (!obj.port) { if (/^(http|ws)$/.test(obj.protocol)) { obj.port = '80'; } else if (/^(http|ws)s$/.test(obj.protocol)) { obj.port = '443'; } } obj.path = obj.path || '/'; var ipv6 = obj.host.indexOf(':') !== -1; var host = ipv6 ? '[' + obj.host + ']' : obj.host; // define unique id obj.id = obj.protocol + '://' + host + ':' + obj.port; // define href obj.href = obj.protocol + '://' + host + (loc && loc.port === obj.port ? '' : ':' + obj.port); return obj; } /***/ }), /* 2 */ /***/ (function(module, exports) { /** * Parses an URI * * @author Steven Levithan <stevenlevithan.com> (MIT license) * @api private */ var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; var parts = [ 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor' ]; module.exports = function parseuri(str) { var src = str, b = str.indexOf('['), e = str.indexOf(']'); if (b != -1 && e != -1) { str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length); } var m = re.exec(str || ''), uri = {}, i = 14; while (i--) { uri[parts[i]] = m[i] || ''; } if (b != -1 && e != -1) { uri.source = src; uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':'); uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':'); uri.ipv6uri = true; } uri.pathNames = pathNames(uri, uri['path']); uri.queryKey = queryKey(uri, uri['query']); return uri; }; function pathNames(obj, path) { var regx = /\/{2,9}/g, names = path.replace(regx, "/").split("/"); if (path.substr(0, 1) == '/' || path.length === 0) { names.splice(0, 1); } if (path.substr(path.length - 1, 1) == '/') { names.splice(names.length - 1, 1); } return names; } function queryKey(uri, query) { var data = {}; query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, function ($0, $1, $2) { if ($1) { data[$1] = $2; } }); return data; } /***/ }), /* 3 */ /***/ (function(module, exports) { "use strict"; module.exports = function () { return function () {}; }; /***/ }), /* 4 */ /***/ (function(module, exports, __webpack_require__) { /** * Module dependencies. */ var debug = __webpack_require__(3)('socket.io-parser'); var Emitter = __webpack_require__(5); var binary = __webpack_require__(6); var isArray = __webpack_require__(7); var isBuf = __webpack_require__(8); /** * Protocol version. * * @api public */ exports.protocol = 4; /** * Packet types. * * @api public */ exports.types = [ 'CONNECT', 'DISCONNECT', 'EVENT', 'ACK', 'ERROR', 'BINARY_EVENT', 'BINARY_ACK' ]; /** * Packet type `connect`. * * @api public */ exports.CONNECT = 0; /** * Packet type `disconnect`. * * @api public */ exports.DISCONNECT = 1; /** * Packet type `event`. * * @api public */ exports.EVENT = 2; /** * Packet type `ack`. * * @api public */ exports.ACK = 3; /** * Packet type `error`. * * @api public */ exports.ERROR = 4; /** * Packet type 'binary event' * * @api public */ exports.BINARY_EVENT = 5; /** * Packet type `binary ack`. For acks with binary arguments. * * @api public */ exports.BINARY_ACK = 6; /** * Encoder constructor. * * @api public */ exports.Encoder = Encoder; /** * Decoder constructor. * * @api public */ exports.Decoder = Decoder; /** * A socket.io Encoder instance * * @api public */ function Encoder() {} var ERROR_PACKET = exports.ERROR + '"encode error"'; /** * Encode a packet as a single string if non-binary, or as a * buffer sequence, depending on packet type. * * @param {Object} obj - packet object * @param {Function} callback - function to handle encodings (likely engine.write) * @return Calls callback with Array of encodings * @api public */ Encoder.prototype.encode = function(obj, callback){ if (exports.BINARY_EVENT === obj.type || exports.BINARY_ACK === obj.type) { encodeAsBinary(obj, callback); } else { var encoding = encodeAsString(obj); callback([encoding]); } }; /** * Encode packet as string. * * @param {Object} packet * @return {String} encoded * @api private */ function encodeAsString(obj) { // first is type var str = '' + obj.type; // attachments if we have them if (exports.BINARY_EVENT === obj.type || exports.BINARY_ACK === obj.type) { str += obj.attachments + '-'; } // if we have a namespace other than `/` // we append it followed by a comma `,` if (obj.nsp && '/' !== obj.nsp) { str += obj.nsp + ','; } // immediately followed by the id if (null != obj.id) { str += obj.id; } // json data if (null != obj.data) { var payload = tryStringify(obj.data); if (payload !== false) { str += payload; } else { return ERROR_PACKET; } } return str; } function tryStringify(str) { try { return JSON.stringify(str); } catch(e){ return false; } } /** * Encode packet as 'buffer sequence' by removing blobs, and * deconstructing packet into object with placeholders and * a list of buffers. * * @param {Object} packet * @return {Buffer} encoded * @api private */ function encodeAsBinary(obj, callback) { function writeEncoding(bloblessData) { var deconstruction = binary.deconstructPacket(bloblessData); var pack = encodeAsString(deconstruction.packet); var buffers = deconstruction.buffers; buffers.unshift(pack); // add packet info to beginning of data list callback(buffers); // write all the buffers } binary.removeBlobs(obj, writeEncoding); } /** * A socket.io Decoder instance * * @return {Object} decoder * @api public */ function Decoder() { this.reconstructor = null; } /** * Mix in `Emitter` with Decoder. */ Emitter(Decoder.prototype); /** * Decodes an encoded packet string into packet JSON. * * @param {String} obj - encoded packet * @return {Object} packet * @api public */ Decoder.prototype.add = function(obj) { var packet; if (typeof obj === 'string') { packet = decodeString(obj); if (exports.BINARY_EVENT === packet.type || exports.BINARY_ACK === packet.type) { // binary packet's json this.reconstructor = new BinaryReconstructor(packet); // no attachments, labeled binary but no binary data to follow if (this.reconstructor.reconPack.attachments === 0) { this.emit('decoded', packet); } } else { // non-binary full packet this.emit('decoded', packet); } } else if (isBuf(obj) || obj.base64) { // raw binary data if (!this.reconstructor) { throw new Error('got binary data when not reconstructing a packet'); } else { packet = this.reconstructor.takeBinaryData(obj); if (packet) { // received final buffer this.reconstructor = null; this.emit('decoded', packet); } } } else { throw new Error('Unknown type: ' + obj); } }; /** * Decode a packet String (JSON data) * * @param {String} str * @return {Object} packet * @api private */ function decodeString(str) { var i = 0; // look up type var p = { type: Number(str.charAt(0)) }; if (null == exports.types[p.type]) { return error('unknown packet type ' + p.type); } // look up attachments if type binary if (exports.BINARY_EVENT === p.type || exports.BINARY_ACK === p.type) { var buf = ''; while (str.charAt(++i) !== '-') { buf += str.charAt(i); if (i == str.length) break; } if (buf != Number(buf) || str.charAt(i) !== '-') { throw new Error('Illegal attachments'); } p.attachments = Number(buf); } // look up namespace (if any) if ('/' === str.charAt(i + 1)) { p.nsp = ''; while (++i) { var c = str.charAt(i); if (',' === c) break; p.nsp += c; if (i === str.length) break; } } else { p.nsp = '/'; } // look up id var next = str.charAt(i + 1); if ('' !== next && Number(next) == next) { p.id = ''; while (++i) { var c = str.charAt(i); if (null == c || Number(c) != c) { --i; break; } p.id += str.charAt(i); if (i === str.length) break; } p.id = Number(p.id); } // look up json data if (str.charAt(++i)) { var payload = tryParse(str.substr(i)); var isPayloadValid = payload !== false && (p.type === exports.ERROR || isArray(payload)); if (isPayloadValid) { p.data = payload; } else { return error('invalid payload'); } } return p; } function tryParse(str) { try { return JSON.parse(str); } catch(e){ return false; } } /** * Deallocates a parser's resources * * @api public */ Decoder.prototype.destroy = function() { if (this.reconstructor) { this.reconstructor.finishedReconstruction(); } }; /** * A manager of a binary event's 'buffer sequence'. Should * be constructed whenever a packet of type BINARY_EVENT is * decoded. * * @param {Object} packet * @return {BinaryReconstructor} initialized reconstructor * @api private */ function BinaryReconstructor(packet) { this.reconPack = packet; this.buffers = []; } /** * Method to be called when binary data received from connection * after a BINARY_EVENT packet. * * @param {Buffer | ArrayBuffer} binData - the raw binary data received * @return {null | Object} returns null if more binary data is expected or * a reconstructed packet object if all buffers have been received. * @api private */ BinaryReconstructor.prototype.takeBinaryData = function(binData) { this.buffers.push(binData); if (this.buffers.length === this.reconPack.attachments) { // done with buffer list var packet = binary.reconstructPacket(this.reconPack, this.buffers); this.finishedReconstruction(); return packet; } return null; }; /** * Cleans up binary packet reconstruction variables. * * @api private */ BinaryReconstructor.prototype.finishedReconstruction = function() { this.reconPack = null; this.buffers = []; }; function error(msg) { return { type: exports.ERROR, data: 'parser error: ' + msg }; } /***/ }), /* 5 */ /***/ (function(module, exports, __webpack_require__) { /** * Expose `Emitter`. */ if (true) { module.exports = Emitter; } /** * Initialize a new `Emitter`. * * @api public */ function Emitter(obj) { if (obj) return mixin(obj); }; /** * Mixin the emitter properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin(obj) { for (var key in Emitter.prototype) { obj[key] = Emitter.prototype[key]; } return obj; } /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.on = Emitter.prototype.addEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; (this._callbacks['$' + event] = this._callbacks['$' + event] || []) .push(fn); return this; }; /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.once = function(event, fn){ function on() { this.off(event, on); fn.apply(this, arguments); } on.fn = fn; this.on(event, on); return this; }; /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function(event, fn){ this._callbacks = this._callbacks || {}; // all if (0 == arguments.length) { this._callbacks = {}; return this; } // specific event var callbacks = this._callbacks['$' + event]; if (!callbacks) return this; // remove all handlers if (1 == arguments.length) { delete this._callbacks['$' + event]; return this; } // remove specific handler var cb; for (var i = 0; i < callbacks.length; i++) { cb = callbacks[i]; if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1); break; } } // Remove event specific arrays for event types that no // one is subscribed for to avoid memory leak. if (callbacks.length === 0) { delete this._callbacks['$' + event]; } return this; }; /** * Emit `event` with the given args. * * @param {String} event * @param {Mixed} ... * @return {Emitter} */ Emitter.prototype.emit = function(event){ this._callbacks = this._callbacks || {}; var args = new Array(arguments.length - 1) , callbacks = this._callbacks['$' + event]; for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } if (callbacks) { callbacks = callbacks.slice(0); for (var i = 0, len = callbacks.length; i < len; ++i) { callbacks[i].apply(this, args); } } return this; }; /** * Return array of callbacks for `event`. * * @param {String} event * @return {Array} * @api public */ Emitter.prototype.listeners = function(event){ this._callbacks = this._callbacks || {}; return this._callbacks['$' + event] || []; }; /** * Check if this emitter has `event` handlers. * * @param {String} event * @return {Boolean} * @api public */ Emitter.prototype.hasListeners = function(event){ return !! this.listeners(event).length; }; /***/ }), /* 6 */ /***/ (function(module, exports, __webpack_require__) { /*global Blob,File*/ /** * Module requirements */ var isArray = __webpack_require__(7); var isBuf = __webpack_require__(8); var toString = Object.prototype.toString; var withNativeBlob = typeof Blob === 'function' || (typeof Blob !== 'undefined' && toString.call(Blob) === '[object BlobConstructor]'); var withNativeFile = typeof File === 'function' || (typeof File !== 'undefined' && toString.call(File) === '[object FileConstructor]'); /** * Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder. * Anything with blobs or files should be fed through removeBlobs before coming * here. * * @param {Object} packet - socket.io event packet * @return {Object} with deconstructed packet and list of buffers * @api public */ exports.deconstructPacket = function(packet) { var buffers = []; var packetData = packet.data; var pack = packet; pack.data = _deconstructPacket(packetData, buffers); pack.attachments = buffers.length; // number of binary 'attachments' return {packet: pack, buffers: buffers}; }; function _deconstructPacket(data, buffers) { if (!data) return data; if (isBuf(data)) { var placeholder = { _placeholder: true, num: buffers.length }; buffers.push(data); return placeholder; } else if (isArray(data)) { var newData = new Array(data.length); for (var i = 0; i < data.length; i++) { newData[i] = _deconstructPacket(data[i], buffers); } return newData; } else if (typeof data === 'object' && !(data instanceof Date)) { var newData = {}; for (var key in data) { newData[key] = _deconstructPacket(data[key], buffers); } return newData; } return data; } /** * Reconstructs a binary packet from its placeholder packet and buffers * * @param {Object} packet - event packet with placeholders * @param {Array} buffers - binary buffers to put in placeholder positions * @return {Object} reconstructed packet * @api public */ exports.reconstructPacket = function(packet, buffers) { packet.data = _reconstructPacket(packet.data, buffers); packet.attachments = undefined; // no longer useful return packet; }; function _reconstructPacket(data, buffers) { if (!data) return data; if (data && data._placeholder) { return buffers[data.num]; // appropriate buffer (should be natural order anyway) } else if (isArray(data)) { for (var i = 0; i < data.length; i++) { data[i] = _reconstructPacket(data[i], buffers); } } else if (typeof data === 'object') { for (var key in data) { data[key] = _reconstructPacket(data[key], buffers); } } return data; } /** * Asynchronously removes Blobs or Files from data via * FileReader's readAsArrayBuffer method. Used before encoding * data as msgpack. Calls callback with the blobless data. * * @param {Object} data * @param {Function} callback * @api private */ exports.removeBlobs = function(data, callback) { function _removeBlobs(obj, curKey, containingObject) { if (!obj) return obj; // convert any blob if ((withNativeBlob && obj instanceof Blob) || (withNativeFile && obj instanceof File)) { pendingBlobs++; // async filereader var fileReader = new FileReader(); fileReader.onload = function() { // this.result == arraybuffer if (containingObject) { containingObject[curKey] = this.result; } else { bloblessData = this.result; } // if nothing pending its callback time if(! --pendingBlobs) { callback(bloblessData); } }; fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer } else if (isArray(obj)) { // handle array for (var i = 0; i < obj.length; i++) { _removeBlobs(obj[i], i, obj); } } else if (typeof obj === 'object' && !isBuf(obj)) { // and object for (var key in obj) { _removeBlobs(obj[key], key, obj); } } } var pendingBlobs = 0; var bloblessData = data; _removeBlobs(bloblessData); if (!pendingBlobs) { callback(bloblessData); } }; /***/ }), /* 7 */ /***/ (function(module, exports) { var toString = {}.toString; module.exports = Array.isArray || function (arr) { return toString.call(arr) == '[object Array]'; }; /***/ }), /* 8 */ /***/ (function(module, exports) { module.exports = isBuf; var withNativeBuffer = typeof Buffer === 'function' && typeof Buffer.isBuffer === 'function'; var withNativeArrayBuffer = typeof ArrayBuffer === 'function'; var isView = function (obj) { return typeof ArrayBuffer.isView === 'function' ? ArrayBuffer.isView(obj) : (obj.buffer instanceof ArrayBuffer); }; /** * Returns true if obj is a buffer or an arraybuffer. * * @api private */ function isBuf(obj) { return (withNativeBuffer && Buffer.isBuffer(obj)) || (withNativeArrayBuffer && (obj instanceof ArrayBuffer || isView(obj))); } /***/ }), /* 9 */ /***/ (function(module, exports, __webpack_require__) { 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /** * Module dependencies. */ var eio = __webpack_require__(10); var Socket = __webpack_require__(34); var Emitter = __webpack_require__(5); var parser = __webpack_require__(4); var on = __webpack_require__(36); var bind = __webpack_require__(37); var debug = __webpack_require__(3)('socket.io-client:manager'); var indexOf = __webpack_require__(33); var Backoff = __webpack_require__(38); /** * IE6+ hasOwnProperty */ var has = Object.prototype.hasOwnProperty; /** * Module exports */ module.exports = Manager; /** * `Manager` constructor. * * @param {String} engine instance or engine uri/opts * @param {Object} options * @api public */ function Manager(uri, opts) { if (!(this instanceof Manager)) return new Manager(uri, opts); if (uri && 'object' === (typeof uri === 'undefined' ? 'undefined' : _typeof(uri))) { opts = uri; uri = undefined; } opts = opts || {}; opts.path = opts.path || '/socket.io'; this.nsps = {}; this.subs = []; this.opts = opts; this.reconnection(opts.reconnection !== false); this.reconnectionAttempts(opts.reconnectionAttempts || Infinity); this.reconnectionDelay(opts.reconnectionDelay || 1000); this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000); this.randomizationFactor(opts.randomizationFactor || 0.5); this.backoff = new Backoff({ min: this.reconnectionDelay(), max: this.reconnectionDelayMax(), jitter: this.randomizationFactor() }); this.timeout(null == opts.timeout ? 20000 : opts.timeout); this.readyState = 'closed'; this.uri = uri; this.connecting = []; this.lastPing = null; this.encoding = false; this.packetBuffer = []; var _parser = opts.parser || parser; this.encoder = new _parser.Encoder(); this.decoder = new _parser.Decoder(); this.autoConnect = opts.autoConnect !== false; if (this.autoConnect) this.open(); } /** * Propagate given event to sockets and emit on `this` * * @api private */ Manager.prototype.emitAll = function () { this.emit.apply(this, arguments); for (var nsp in this.nsps) { if (has.call(this.nsps, nsp)) { this.nsps[nsp].emit.apply(this.nsps[nsp], arguments); } } }; /** * Update `socket.id` of all sockets * * @api private */ Manager.prototype.updateSocketIds = function () { for (var nsp in this.nsps) { if (has.call(this.nsps, nsp)) { this.nsps[nsp].id = this.generateId(nsp); } } }; /** * generate `socket.id` for the given `nsp` * * @param {String} nsp * @return {String} * @api private */ Manager.prototype.generateId = function (nsp) { return (nsp === '/' ? '' : nsp + '#') + this.engine.id; }; /** * Mix in `Emitter`. */ Emitter(Manager.prototype); /** * Sets the `reconnection` config. * * @param {Boolean} true/false if it should automatically reconnect * @return {Manager} self or value * @api public */ Manager.prototype.reconnection = function (v) { if (!arguments.length) return this._reconnection; this._reconnection = !!v; return this; }; /** * Sets the reconnection attempts config. * * @param {Number} max reconnection attempts before giving up * @return {Manager} self or value * @api public */ Manager.prototype.reconnectionAttempts = function (v) { if (!arguments.length) return this._reconnectionAttempts; this._reconnectionAttempts = v; return this; }; /** * Sets the delay between reconnections. * * @param {Number} delay * @return {Manager} self or value * @api public */ Manager.prototype.reconnectionDelay = function (v) { if (!arguments.length) return this._reconnectionDelay; this._reconnectionDelay = v; this.backoff && this.backoff.setMin(v); return this; }; Manager.prototype.randomizationFactor = function (v) { if (!arguments.length) return this._randomizationFactor; this._randomizationFactor = v; this.backoff && this.backoff.setJitter(v); return this; }; /** * Sets the maximum delay between reconnections. * * @param {Number} delay * @return {Manager} self or value * @api public */ Manager.prototype.reconnectionDelayMax = function (v) { if (!arguments.length) return this._reconnectionDelayMax; this._reconnectionDelayMax = v; this.backoff && this.backoff.setMax(v); return this; }; /** * Sets the connection timeout. `false` to disable * * @return {Manager} self or value * @api public */ Manager.prototype.timeout = function (v) { if (!arguments.length) return this._timeout; this._timeout = v; return this; }; /** * Starts trying to reconnect if reconnection is enabled and we have not * started reconnecting yet * * @api private */ Manager.prototype.maybeReconnectOnOpen = function () { // Only try to reconnect if it's the first time we're connecting if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) { // keeps reconnection from firing twice for the same reconnection loop this.reconnect(); } }; /** * Sets the current transport `socket`. * * @param {Function} optional, callback * @return {Manager} self * @api public */ Manager.prototype.open = Manager.prototype.connect = function (fn, opts) { if (~this.readyState.indexOf('open')) return this; this.engine = eio(this.uri, this.opts); var socket = this.engine; var self = this; this.readyState = 'opening'; this.skipReconnect = false; // emit `open` var openSub = on(socket, 'open', function () { self.onopen(); fn && fn(); }); // emit `connect_error` var errorSub = on(socket, 'error', function (data) { self.cleanup(); self.readyState = 'closed'; self.emitAll('connect_error', data); if (fn) { var err = new Error('Connection error'); err.data = data; fn(err); } else { // Only do this if there is no fn to handle the error self.maybeReconnectOnOpen(); } }); // emit `connect_timeout` if (false !== this._timeout) { var timeout = this._timeout; if (timeout === 0) { openSub.destroy(); // prevents a race condition with the 'open' event } // set timer var timer = setTimeout(function () { openSub.destroy(); socket.close(); socket.emit('error', 'timeout'); self.emitAll('connect_timeout', timeout); }, timeout); this.subs.push({ destroy: function destroy() { clearTimeout(timer); } }); } this.subs.push(openSub); this.subs.push(errorSub); return this; }; /** * Called upon transport open. * * @api private */ Manager.prototype.onopen = function () { // clear old subs this.cleanup(); // mark as open this.readyState = 'open'; this.emit('open'); // add new subs var socket = this.engine; this.subs.push(on(socket, 'data', bind(this, 'ondata'))); this.subs.push(on(socket, 'ping', bind(this, 'onping'))); this.subs.push(on(socket, 'pong', bind(this, 'onpong'))); this.subs.push(on(socket, 'error', bind(this, 'onerror'))); this.subs.push(on(socket, 'close', bind(this, 'onclose'))); this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded'))); }; /** * Called upon a ping. * * @api private */ Manager.prototype.onping = function () { this.lastPing = new Date(); this.emitAll('ping'); }; /** * Called upon a packet. * * @api private */ Manager.prototype.onpong = function () { this.emitAll('pong', new Date() - this.lastPing); }; /** * Called with data. * * @api private */ Manager.prototype.ondata = function (data) { this.decoder.add(data); }; /** * Called when parser fully decodes a packet. * * @api private */ Manager.prototype.ondecoded = function (packet) { this.emit('packet', packet); }; /** * Called upon socket error. * * @api private */ Manager.prototype.onerror = function (err) { this.emitAll('error', err); }; /** * Creates a new socket for the given `nsp`. * * @return {Socket} * @api public */ Manager.prototype.socket = function (nsp, opts) { var socket = this.nsps[nsp]; if (!socket) { socket = new Socket(this, nsp, opts); this.nsps[nsp] = socket; var self = this; socket.on('connecting', onConnecting); socket.on('connect', function () { socket.id = self.generateId(nsp); }); if (this.autoConnect) { // manually call here since connecting event is fired before listening onConnecting(); } } function onConnecting() { if (!~indexOf(self.connecting, socket)) { self.connecting.push(socket); } } return socket; }; /** * Called upon a socket close. * * @param {Socket} socket */ Manager.prototype.destroy = function (socket) { var index = indexOf(this.connecting, socket); if (~index) this.connecting.splice(index, 1); if (this.connecting.length) return; this.close(); }; /** * Writes a packet. * * @param {Object} packet * @api private */ Manager.prototype.packet = function (packet) { var self = this; if (packet.query && packet.type === 0) packet.nsp += '?' + packet.query; if (!self.encoding) { // encode, then write to engine with result self.encoding = true; this.encoder.encode(packet, function (encodedPackets) { for (var i = 0; i < encodedPackets.length; i++) { self.engine.write(encodedPackets[i], packet.options); } self.encoding = false; self.processPacketQueue(); }); } else { // add packet to the queue self.packetBuffer.push(packet); } }; /** * If packet buffer is non-empty, begins encoding the * next packet in line. * * @api private */ Manager.prototype.processPacketQueue = function () { if (this.packetBuffer.length > 0 && !this.encoding) { var pack = this.packetBuffer.shift(); this.packet(pack); } }; /** * Clean up transport subscriptions and packet buffer. * * @api private */ Manager.prototype.cleanup = function () { var subsLength = this.subs.length; for (var i = 0; i < subsLength; i++) { var sub = this.subs.shift(); sub.destroy(); } this.packetBuffer = []; this.encoding = false; this.lastPing = null; this.decoder.destroy(); }; /** * Close the current socket. * * @api private */ Manager.prototype.close = Manager.prototype.disconnect = function () { this.skipReconnect = true; this.reconnecting = false; if ('opening' === this.readyState) { // `onclose` will not fire because // an open event never happened this.cleanup(); } this.backoff.reset(); this.readyState = 'closed'; if (this.engine) this.engine.close(); }; /** * Called upon engine close. * * @api private */ Manager.prototype.onclose = function (reason) { this.cleanup(); this.backoff.reset(); this.readyState = 'closed'; this.emit('close', reason); if (this._reconnection && !this.skipReconnect) { this.reconnect(); } }; /** * Attempt a reconnection. * * @api private */ Manager.prototype.reconnect = function () { if (this.reconnecting || this.skipReconnect) return this; var self = this; if (this.backoff.attempts >= this._reconnectionAttempts) { this.backoff.reset(); this.emitAll('reconnect_failed'); this.reconnecting = false; } else { var delay = this.backoff.duration(); this.reconnecting = true; var timer = setTimeout(function () { if (self.skipReconnect) return; self.emitAll('reconnect_attempt', self.backoff.attempts); self.emitAll('reconnecting', self.backoff.attempts); // check again for the case socket closed in above events if (self.skipReconnect) return; self.open(function (err) { if (err) { self.reconnecting = false; self.reconnect(); self.emitAll('reconnect_error', err.data); } else { self.onreconnect(); } }); }, delay); this.subs.push({ destroy: function destroy() { clearTimeout(timer); } }); } }; /** * Called upon successful reconnect. * * @api private */ Manager.prototype.onreconnect = function () { var attempt = this.backoff.attempts; this.reconnecting = false; this.backoff.reset(); this.updateSocketIds(); this.emitAll('reconnect', attempt); }; /***/ }), /* 10 */ /***/ (function(module, exports, __webpack_require__) { module.exports = __webpack_require__(11); /** * Exports parser * * @api public * */ module.exports.parser = __webpack_require__(19); /***/ }), /* 11 */ /***/ (function(module, exports, __webpack_require__) { /** * Module dependencies. */ var transports = __webpack_require__(12); var Emitter = __webpack_require__(5); var debug = __webpack_require__(3)('engine.io-client:socket'); var index = __webpack_require__(33); var parser = __webpack_require__(19); var parseuri = __webpack_require__(2); var parseqs = __webpack_require__(27); /** * Module exports. */ module.exports = Socket; /** * Socket constructor. * * @param {String|Object} uri or options * @param {Object} options * @api public */ function Socket (uri, opts) { if (!(this instanceof Socket)) return new Socket(uri, opts); opts = opts || {}; if (uri && 'object' === typeof uri) { opts = uri; uri = null; } if (uri) { uri = parseuri(uri); opts.hostname = uri.host; opts.secure = uri.protocol === 'https' || uri.protocol === 'wss'; opts.port = uri.port; if (uri.query) opts.query = uri.query; } else if (opts.host) { opts.hostname = parseuri(opts.host).host; } this.secure = null != opts.secure ? opts.secure : (typeof location !== 'undefined' && 'https:' === location.protocol); if (opts.hostname && !opts.port) { // if no port is specified manually, use the protocol default opts.port = this.secure ? '443' : '80'; } this.agent = opts.agent || false; this.hostname = opts.hostname || (typeof location !== 'undefined' ? location.hostname : 'localhost'); this.port = opts.port || (typeof location !== 'undefined' && location.port ? location.port : (this.secure ? 443 : 80)); this.query = opts.query || {}; if ('string' === typeof this.query) this.query = parseqs.decode(this.query); this.upgrade = false !== opts.upgrade; this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/'; this.forceJSONP = !!opts.forceJSONP; this.jsonp = false !== opts.jsonp; this.forceBase64 = !!opts.forceBase64; this.enablesXDR = !!opts.enablesXDR; this.withCredentials = false !== opts.withCredentials; this.timestampParam = opts.timestampParam || 't'; this.timestampRequests = opts.timestampRequests; this.transports = opts.transports || ['polling', 'websocket']; this.transportOptions = opts.transportOptions || {}; this.readyState = ''; this.writeBuffer = []; this.prevBufferLen = 0; this.policyPort = opts.policyPort || 843; this.rememberUpgrade = opts.rememberUpgrade || false; this.binaryType = null; this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades; this.perMessageDeflate = false !== opts.perMessageDeflate ? (opts.perMessageDeflate || {}) : false; if (true === this.perMessageDeflate) this.perMessageDeflate = {}; if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) { this.perMessageDeflate.threshold = 1024; } // SSL options for Node.js client this.pfx = opts.pfx || null; this.key = opts.key || null; this.passphrase = opts.passphrase || null; this.cert = opts.cert || null; this.ca = opts.ca || null; this.ciphers = opts.ciphers || null; this.rejectUnauthorized = opts.rejectUnauthorized === undefined ? true : opts.rejectUnauthorized; this.forceNode = !!opts.forceNode; // detect ReactNative environment this.isReactNative = (typeof navigator !== 'undefined' && typeof navigator.product === 'string' && navigator.product.toLowerCase() === 'reactnative'); // other options for Node.js or ReactNative client if (typeof self === 'undefined' || this.isReactNative) { if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) { this.extraHeaders = opts.extraHeaders; } if (opts.localAddress) { this.localAddress = opts.localAddress; } } // set on handshake this.id = null; this.upgrades = null; this.pingInterval = null; this.pingTimeout = null; // set on heartbeat this.pingIntervalTimer = null; this.pingTimeoutTimer = null; this.open(); } Socket.priorWebsocketSuccess = false; /** * Mix in `Emitter`. */ Emitter(Socket.prototype); /** * Protocol version. * * @api public */ Socket.protocol = parser.protocol; // this is an int /** * Expose deps for legacy compatibility * and standalone browser access. */ Socket.Socket = Socket; Socket.Transport = __webpack_require__(18); Socket.transports = __webpack_require__(12); Socket.parser = __webpack_require__(19); /** * Creates transport of the given type. * * @param {String} transport name * @return {Transport} * @api private */ Socket.prototype.createTransport = function (name) { var query = clone(this.query); // append engine.io protocol identifier query.EIO = parser.protocol; // transport name query.transport = name; // per-transport options var options = this.transportOptions[name] || {}; // session id if we already have one if (this.id) query.sid = this.id; var transport = new transports[name]({ query: query, socket: this, agent: options.agent || this.agent, hostname: options.hostname || this.hostname, port: options.port || this.port, secure: options.secure || this.secure, path: options.path || this.path, forceJSONP: options.forceJSONP || this.forceJSONP, jsonp: options.jsonp || this.jsonp, forceBase64: options.forceBase64 || this.forceBase64, enablesXDR: options.enablesXDR || this.enablesXDR, withCredentials: options.withCredentials || this.withCredentials, timestampRequests: options.timestampRequests || this.timestampRequests, timestampParam: options.timestampParam || this.timestampParam, policyPort: options.policyPort || this.policyPort, pfx: options.pfx || this.pfx, key: options.key || this.key, passphrase: options.passphrase || this.passphrase, cert: options.cert || this.cert, ca: options.ca || this.ca, ciphers: options.ciphers || this.ciphers, rejectUnauthorized: options.rejectUnauthorized || this.rejectUnauthorized, perMessageDeflate: options.perMessageDeflate || this.perMessageDeflate, extraHeaders: options.extraHeaders || this.extraHeaders, forceNode: options.forceNode || this.forceNode, localAddress: options.localAddress || this.localAddress, requestTimeout: options.requestTimeout || this.requestTimeout, protocols: options.protocols || void (0), isReactNative: this.isReactNative }); return transport; }; function clone (obj) { var o = {}; for (var i in obj) { if (obj.hasOwnProperty(i)) { o[i] = obj[i]; } } return o; } /** * Initializes transport to use and starts probe. * * @api private */ Socket.prototype.open = function () { var transport; if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') !== -1) { transport = 'websocket'; } else if (0 === this.transports.length) { // Emit error on next tick so it can be listened to var self = this; setTimeout(function () { self.emit('error', 'No transports available'); }, 0); return; } else { transport = this.transports[0]; } this.readyState = 'opening'; // Retry with the next transport if the transport is disabled (jsonp: false) try { transport = this.createTransport(transport); } catch (e) { this.transports.shift(); this.open(); return; } transport.open(); this.setTransport(transport); }; /** * Sets the current transport. Disables the existing one (if any). * * @api private */ Socket.prototype.setTransport = function (transport) { var self = this; if (this.transport) { this.transport.removeAllListeners(); } // set up transport this.transport = transport; // set up transport listeners transport .on('drain', function () { self.onDrain(); }) .on('packet', function (packet) { self.onPacket(packet); }) .on('error', function (e) { self.onError(e); }) .on('close', function () { self.onClose('transport close'); }); }; /** * Probes a transport. * * @param {String} transport name * @api private */ Socket.prototype.probe = function (name) { var transport = this.createTransport(name, { probe: 1 }); var failed = false; var self = this; Socket.priorWebsocketSuccess = false; function onTransportOpen () { if (self.onlyBinaryUpgrades) { var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary; failed = failed || upgradeLosesBinary; } if (failed) return; transport.send([{ type: 'ping', data: 'probe' }]); transport.once('packet', function (msg) { if (failed) return; if ('pong' === msg.type && 'probe' === msg.data) { self.upgrading = true; self.emit('upgrading', transport); if (!transport) return; Socket.priorWebsocketSuccess = 'websocket' === transport.name; self.transport.pause(function () { if (failed) return; if ('closed' === self.readyState) return; cleanup(); self.setTransport(transport); transport.send([{ type: 'upgrade' }]); self.emit('upgrade', transport); transport = null; self.upgrading = false; self.flush(); }); } else { var err = new Error('probe error'); err.transport = transport.name; self.emit('upgradeError', err); } }); } function freezeTransport () { if (failed) return; // Any callback called by transport should be ignored since now failed = true; cleanup(); transport.close(); transport = null; } // Handle any error that happens while probing function onerror (err) { var error = new Error('probe error: ' + err); error.transport = transport.name; freezeTransport(); self.emit('upgradeError', error); } function onTransportClose () { onerror('transport closed'); } // When the socket is closed while we're probing function onclose () { onerror('socket closed'); } // When the socket is upgraded while we're probing function onupgrade (to) { if (transport && to.name !== transport.name) { freezeTransport(); } } // Remove all listeners on the transport and on self function cleanup () { transport.removeListener('open', onTransportOpen); transport.removeListener('error', onerror); transport.removeListener('close', onTransportClose); self.removeListener('close', onclose); self.removeListener('upgrading', onupgrade); } transport.once('open', onTransportOpen); transport.once('error', onerror); transport.once('close', onTransportClose); this.once('close', onclose); this.once('upgrading', onupgrade); transport.open(); }; /** * Called when connection is deemed open. * * @api public */ Socket.prototype.onOpen = function () { this.readyState = 'open'; Socket.priorWebsocketSuccess = 'websocket' === this.transport.name; this.emit('open'); this.flush(); // we check for `readyState` in case an `open` // listener already closed the socket if ('open' === this.readyState && this.upgrade && this.transport.pause) { for (var i = 0, l = this.upgrades.length; i < l; i++) { this.probe(this.upgrades[i]); } } }; /** * Handles a packet. * * @api private */ Socket.prototype.onPacket = function (packet) { if ('openi