@jayzyaj/centrifuge-js-cyy
Version:
Centrifuge and Centrifugo client for NodeJS and browser
1,654 lines (1,521 loc) • 90.4 kB
JavaScript
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define("Centrifuge", [], factory);
else if(typeof exports === 'object')
exports["Centrifuge"] = factory();
else
root["Centrifuge"] = factory();
})(typeof self !== 'undefined' ? self : 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] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = 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;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 31);
/******/ })
/************************************************************************/
/******/ ({
/***/ 10:
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/* WEBPACK VAR INJECTION */(function(global) {
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Centrifuge = undefined;
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; };
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _events = __webpack_require__(6);
var _events2 = _interopRequireDefault(_events);
var _subscription = __webpack_require__(11);
var _subscription2 = _interopRequireDefault(_subscription);
var _json = __webpack_require__(12);
var _utils = __webpack_require__(7);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _errorTimeout = 'timeout';
var _errorConnectionClosed = 'connection closed';
var Centrifuge = exports.Centrifuge = function (_EventEmitter) {
_inherits(Centrifuge, _EventEmitter);
function Centrifuge(url, options) {
_classCallCheck(this, Centrifuge);
var _this = _possibleConstructorReturn(this, (Centrifuge.__proto__ || Object.getPrototypeOf(Centrifuge)).call(this));
_this._url = url;
_this._websocket = null;
_this._sockjs = null;
_this._isSockjs = false;
_this._binary = false;
_this._methodType = null;
_this._pushType = null;
_this._encoder = null;
_this._decoder = null;
_this._status = 'disconnected';
_this._reconnect = true;
_this._reconnecting = false;
_this._transport = null;
_this._transportName = null;
_this._transportClosed = true;
_this._messageId = 0;
_this._clientID = null;
_this._refreshRequired = false;
_this._subs = {};
_this._serverSubs = {};
_this._lastSeq = {};
_this._lastGen = {};
_this._lastOffset = {};
_this._lastEpoch = {};
_this._messages = [];
_this._isBatching = false;
_this._isSubscribeBatching = false;
_this._privateChannels = {};
_this._numRefreshFailed = 0;
_this._refreshTimeout = null;
_this._pingTimeout = null;
_this._pongTimeout = null;
_this._subRefreshTimeouts = {};
_this._retries = 0;
_this._callbacks = {};
_this._latency = null;
_this._latencyStart = null;
_this._connectData = null;
_this._token = null;
_this._xhrID = 0;
_this._xhrs = {};
_this._dispatchPromise = Promise.resolve();
_this._config = {
debug: false,
websocket: null,
sockjs: null,
promise: null,
minRetry: 1000,
maxRetry: 20000,
timeout: 5000,
ping: true,
pingInterval: 25000,
pongWaitTimeout: 5000,
privateChannelPrefix: '$',
onTransportClose: null,
sockjsServer: null,
sockjsTransports: ['websocket', 'xdr-streaming', 'xhr-streaming', 'eventsource', 'iframe-eventsource', 'iframe-htmlfile', 'xdr-polling', 'xhr-polling', 'iframe-xhr-polling', 'jsonp-polling'],
refreshEndpoint: '/centrifuge/refresh',
refreshHeaders: {},
refreshParams: {},
refreshData: {},
refreshAttempts: null,
refreshInterval: 1000,
onRefreshFailed: null,
onRefresh: null,
subscribeEndpoint: '/centrifuge/subscribe',
subscribeHeaders: {},
subscribeParams: {},
subRefreshInterval: 1000,
onPrivateSubscribe: null
};
_this._configure(options);
return _this;
}
_createClass(Centrifuge, [{
key: 'setToken',
value: function setToken(token) {
this._token = token;
}
}, {
key: 'setConnectData',
value: function setConnectData(data) {
this._connectData = data;
}
}, {
key: 'setRefreshHeaders',
value: function setRefreshHeaders(headers) {
this._config.refreshHeaders = headers;
}
}, {
key: 'setRefreshParams',
value: function setRefreshParams(params) {
this._config.refreshParams = params;
}
}, {
key: 'setRefreshData',
value: function setRefreshData(data) {
this._config.refreshData = data;
}
}, {
key: 'setSubscribeHeaders',
value: function setSubscribeHeaders(headers) {
this._config.subscribeHeaders = headers;
}
}, {
key: 'setSubscribeParams',
value: function setSubscribeParams(params) {
this._config.subscribeParams = params;
}
}, {
key: '_ajax',
value: function _ajax(url, params, headers, data, callback) {
var _this2 = this;
var query = '';
this._debug('sending AJAX request to', url, 'with data', JSON.stringify(data));
var xhr = global.XMLHttpRequest ? new global.XMLHttpRequest() : new global.ActiveXObject('Microsoft.XMLHTTP');
for (var i in params) {
if (params.hasOwnProperty(i)) {
if (query.length > 0) {
query += '&';
}
query += encodeURIComponent(i) + '=' + encodeURIComponent(params[i]);
}
}
if (query.length > 0) {
query = '?' + query;
}
xhr.open('POST', url + query, true);
if ('withCredentials' in xhr) {
xhr.withCredentials = true;
}
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.setRequestHeader('Content-Type', 'application/json');
for (var headerName in headers) {
if (headers.hasOwnProperty(headerName)) {
xhr.setRequestHeader(headerName, headers[headerName]);
}
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var _data = void 0,
parsed = false;
try {
_data = JSON.parse(xhr.responseText);
parsed = true;
} catch (e) {
callback({
error: 'Invalid JSON. Data was: ' + xhr.responseText,
status: 200,
data: null
});
}
if (parsed) {
// prevents double execution.
callback({
data: _data,
status: 200
});
}
} else {
_this2._log('wrong status code in AJAX response', xhr.status);
callback({
status: xhr.status,
data: null
});
}
}
};
setTimeout(function () {
return xhr.send(JSON.stringify(data));
}, 20);
return xhr;
}
}, {
key: '_log',
value: function _log() {
(0, _utils.log)('info', arguments);
}
}, {
key: '_debug',
value: function _debug() {
if (this._config.debug === true) {
(0, _utils.log)('debug', arguments);
}
}
}, {
key: '_websocketSupported',
value: function _websocketSupported() {
if (this._config.websocket !== null) {
return true;
}
return !(typeof WebSocket !== 'function' && (typeof WebSocket === 'undefined' ? 'undefined' : _typeof(WebSocket)) !== 'object');
}
}, {
key: '_setFormat',
value: function _setFormat(format) {
if (this._formatOverride(format)) {
return;
}
if (format === 'protobuf') {
throw new Error('not implemented by JSON only Centrifuge client – use client with Protobuf');
}
this._binary = false;
this._methodType = _json.JsonMethodType;
this._pushType = _json.JsonPushType;
this._encoder = new _json.JsonEncoder();
this._decoder = new _json.JsonDecoder();
}
}, {
key: '_formatOverride',
value: function _formatOverride(format) {
return false;
}
}, {
key: '_configure',
value: function _configure(configuration) {
if (!('Promise' in global)) {
throw new Error('Promise polyfill required');
}
(0, _utils.extend)(this._config, configuration || {});
this._debug('centrifuge config', this._config);
if (!this._url) {
throw new Error('url required');
}
if ((0, _utils.startsWith)(this._url, 'ws') && this._url.indexOf('format=protobuf') > -1) {
this._setFormat('protobuf');
} else {
this._setFormat('json');
}
if ((0, _utils.startsWith)(this._url, 'http')) {
this._debug('client will try to connect to SockJS endpoint');
if (this._config.sockjs !== null) {
this._debug('SockJS explicitly provided in options');
this._sockjs = this._config.sockjs;
} else {
if (typeof global.SockJS === 'undefined') {
throw new Error('SockJS not found, use ws:// in url or include SockJS');
}
this._debug('use globally defined SockJS');
this._sockjs = global.SockJS;
}
} else {
this._debug('client will connect to websocket endpoint');
}
}
}, {
key: '_setStatus',
value: function _setStatus(newStatus) {
if (this._status !== newStatus) {
this._debug('Status', this._status, '->', newStatus);
this._status = newStatus;
}
}
}, {
key: '_isDisconnected',
value: function _isDisconnected() {
return this._status === 'disconnected';
}
}, {
key: '_isConnecting',
value: function _isConnecting() {
return this._status === 'connecting';
}
}, {
key: '_isConnected',
value: function _isConnected() {
return this._status === 'connected';
}
}, {
key: '_nextMessageId',
value: function _nextMessageId() {
return ++this._messageId;
}
}, {
key: '_resetRetry',
value: function _resetRetry() {
this._debug('reset retries count to 0');
this._retries = 0;
}
}, {
key: '_getRetryInterval',
value: function _getRetryInterval() {
var interval = (0, _utils.backoff)(this._retries, this._config.minRetry, this._config.maxRetry);
this._retries += 1;
return interval;
}
}, {
key: '_abortInflightXHRs',
value: function _abortInflightXHRs() {
for (var xhrID in this._xhrs) {
try {
this._xhrs[xhrID].abort();
} catch (e) {
this._debug('error aborting xhr', e);
}
delete this._xhrs[xhrID];
}
}
}, {
key: '_clearConnectedState',
value: function _clearConnectedState(reconnect) {
this._clientID = null;
this._stopPing();
// fire errbacks of registered outgoing calls.
for (var id in this._callbacks) {
if (this._callbacks.hasOwnProperty(id)) {
var callbacks = this._callbacks[id];
clearTimeout(callbacks.timeout);
var errback = callbacks.errback;
if (!errback) {
continue;
}
errback({ error: this._createErrorObject('disconnected') });
}
}
this._callbacks = {};
// fire unsubscribe events
for (var channel in this._subs) {
if (this._subs.hasOwnProperty(channel)) {
var sub = this._subs[channel];
if (reconnect) {
if (sub._isSuccess()) {
sub._triggerUnsubscribe();
sub._recover = true;
}
if (sub._shouldResubscribe()) {
sub._setSubscribing();
}
} else {
sub._setUnsubscribed();
}
}
}
this._abortInflightXHRs();
// clear refresh timer
if (this._refreshTimeout !== null) {
clearTimeout(this._refreshTimeout);
this._refreshTimeout = null;
}
// clear sub refresh timers
for (var _channel in this._subRefreshTimeouts) {
if (this._subRefreshTimeouts.hasOwnProperty(_channel) && this._subRefreshTimeouts[_channel]) {
this._clearSubRefreshTimeout(_channel);
}
}
this._subRefreshTimeouts = {};
if (!this._reconnect) {
// completely clear subscriptions
this._subs = {};
}
}
}, {
key: '_isTransportOpen',
value: function _isTransportOpen() {
if (this._isSockjs) {
return this._transport && this._transport.transport && this._transport.transport.readyState === this._transport.transport.OPEN;
}
return this._transport && this._transport.readyState === this._transport.OPEN;
}
}, {
key: '_transportSend',
value: function _transportSend(commands) {
if (!commands.length) {
return true;
}
if (!this._isTransportOpen()) {
// resolve pending commands with error if transport is not open
for (var command in commands) {
var id = command.id;
if (!(id in this._callbacks)) {
continue;
}
var callbacks = this._callbacks[id];
clearTimeout(this._callbacks[id].timeout);
delete this._callbacks[id];
var errback = callbacks.errback;
errback({ error: this._createErrorObject(_errorConnectionClosed, 0) });
}
return false;
}
this._transport.send(this._encoder.encodeCommands(commands));
return true;
}
}, {
key: '_setupTransport',
value: function _setupTransport() {
var _this3 = this;
this._isSockjs = false;
// detect transport to use - SockJS or Websocket
if (this._sockjs !== null) {
var sockjsOptions = {
transports: this._config.sockjsTransports
};
if (this._config.sockjsServer !== null) {
sockjsOptions.server = this._config.sockjsServer;
}
this._isSockjs = true;
this._transport = new this._sockjs(this._url, null, sockjsOptions);
} else {
if (!this._websocketSupported()) {
this._debug('No Websocket support and no SockJS configured, can not connect');
return;
}
if (this._config.websocket !== null) {
this._websocket = this._config.websocket;
} else {
this._websocket = WebSocket;
}
this._transport = new this._websocket(this._url);
if (this._binary === true) {
this._transport.binaryType = 'arraybuffer';
}
}
this._transport.onopen = function () {
_this3._transportClosed = false;
if (_this3._isSockjs) {
_this3._transportName = 'sockjs-' + _this3._transport.transport;
_this3._transport.onheartbeat = function () {
return _this3._restartPing();
};
} else {
_this3._transportName = 'websocket';
}
// Can omit method here due to zero value.
var msg = {
// method: this._methodType.CONNECT
};
if (_this3._token || _this3._connectData) {
msg.params = {};
}
if (_this3._token) {
msg.params.token = _this3._token;
}
if (_this3._connectData) {
msg.params.data = _this3._connectData;
}
var subs = {};
var hasSubs = false;
for (var channel in _this3._serverSubs) {
if (_this3._serverSubs.hasOwnProperty(channel) && _this3._serverSubs[channel].recoverable) {
hasSubs = true;
var sub = {
'recover': true
};
if (_this3._serverSubs[channel].seq || _this3._serverSubs[channel].gen) {
if (_this3._serverSubs[channel].seq) {
sub['seq'] = _this3._serverSubs[channel].seq;
}
if (_this3._serverSubs[channel].gen) {
sub['gen'] = _this3._serverSubs[channel].gen;
}
} else {
if (_this3._serverSubs[channel].offset) {
sub['offset'] = _this3._serverSubs[channel].offset;
}
}
if (_this3._serverSubs[channel].epoch) {
sub['epoch'] = _this3._serverSubs[channel].epoch;
}
subs[channel] = sub;
}
}
if (hasSubs) {
if (!msg.params) {
msg.params = {};
}
msg.params.subs = subs;
}
_this3._latencyStart = new Date();
_this3._call(msg).then(function (resolveCtx) {
_this3._connectResponse(_this3._decoder.decodeCommandResult(_this3._methodType.CONNECT, resolveCtx.result), hasSubs);
if (resolveCtx.next) {
resolveCtx.next();
}
}, function (rejectCtx) {
var err = rejectCtx.error;
if (err.code === 109) {
// token expired.
_this3._refreshRequired = true;
}
_this3._disconnect('connect error', true);
if (rejectCtx.next) {
rejectCtx.next();
}
});
};
this._transport.onerror = function (error) {
_this3._debug('transport level error', error);
};
this._transport.onclose = function (closeEvent) {
_this3._transportClosed = true;
var reason = _errorConnectionClosed;
var needReconnect = true;
if (closeEvent && 'reason' in closeEvent && closeEvent.reason) {
try {
var advice = JSON.parse(closeEvent.reason);
_this3._debug('reason is an advice object', advice);
reason = advice.reason;
needReconnect = advice.reconnect;
} catch (e) {
reason = closeEvent.reason;
_this3._debug('reason is a plain string', reason);
}
}
// onTransportClose callback should be executed every time transport was closed.
// This can be helpful to catch failed connection events (because our disconnect
// event only called once and every future attempts to connect do not fire disconnect
// event again).
if (_this3._config.onTransportClose !== null) {
_this3._config.onTransportClose({
event: closeEvent,
reason: reason,
reconnect: needReconnect
});
}
_this3._disconnect(reason, needReconnect);
if (_this3._reconnect === true) {
_this3._reconnecting = true;
var interval = _this3._getRetryInterval();
_this3._debug('reconnect after ' + interval + ' milliseconds');
setTimeout(function () {
if (_this3._reconnect === true) {
if (_this3._refreshRequired) {
_this3._refresh();
} else {
_this3._connect();
}
}
}, interval);
}
};
this._transport.onmessage = function (event) {
_this3._dataReceived(event.data);
};
}
}, {
key: 'rpc',
value: function rpc(data) {
return this._rpc('', data);
}
}, {
key: 'namedRPC',
value: function namedRPC(method, data) {
return this._rpc(method, data);
}
}, {
key: '_rpc',
value: function _rpc(method, data) {
var _this4 = this;
var params = {
data: data
};
if (method !== '') {
params.method = method;
};
var msg = {
method: this._methodType.RPC,
params: params
};
if (!this.isConnected()) {
return Promise.reject(this._createErrorObject(_errorConnectionClosed, 0));
}
return this._call(msg).then(function (resolveCtx) {
if (resolveCtx.next) {
resolveCtx.next();
}
return _this4._decoder.decodeCommandResult(_this4._methodType.RPC, resolveCtx.result);
}, function (rejectCtx) {
if (rejectCtx.next) {
rejectCtx.next();
}
return Promise.reject(rejectCtx.error);
});
}
}, {
key: 'send',
value: function send(data) {
var msg = {
method: this._methodType.SEND,
params: {
data: data
}
};
if (!this.isConnected()) {
return Promise.reject(this._createErrorObject(_errorConnectionClosed, 0));
}
var sent = this._transportSend([msg]); // can send async message to server without id set
if (!sent) {
return Promise.reject(this._createErrorObject(_errorConnectionClosed, 0));
};
return Promise.resolve({});
}
}, {
key: 'publish',
value: function publish(channel, data) {
var msg = {
method: this._methodType.PUBLISH,
params: {
channel: channel,
data: data
}
};
if (!this.isConnected()) {
return Promise.reject(this._createErrorObject(_errorConnectionClosed, 0));
}
return this._call(msg).then(function (result) {
if (result.next) {
result.next();
}
return {};
});
}
}, {
key: '_dataReceived',
value: function _dataReceived(data) {
var _this5 = this;
var replies = this._decoder.decodeReplies(data);
// we have to guarantee order of events in replies processing - i.e. start processing
// next reply only when we finished processing of current one. Without syncing things in
// this way we could get wrong publication events order as reply promises resolve
// on next loop tick so for loop continues before we finished emitting all reply events.
this._dispatchPromise = this._dispatchPromise.then(function () {
var finishDispatch = void 0;
_this5._dispatchPromise = new Promise(function (resolve) {
finishDispatch = resolve;
});
_this5._dispatchSynchronized(replies, finishDispatch);
});
this._restartPing();
}
}, {
key: '_dispatchSynchronized',
value: function _dispatchSynchronized(replies, finishDispatch) {
var _this6 = this;
var p = Promise.resolve();
var _loop = function _loop(i) {
if (replies.hasOwnProperty(i)) {
p = p.then(function () {
return _this6._dispatchReply(replies[i]);
});
}
};
for (var i in replies) {
_loop(i);
}
p = p.then(function () {
finishDispatch();
});
}
}, {
key: '_dispatchReply',
value: function _dispatchReply(reply) {
var next;
var p = new Promise(function (resolve) {
next = resolve;
});
if (reply === undefined || reply === null) {
this._debug('dispatch: got undefined or null reply');
next();
return p;
}
var id = reply.id;
if (id && id > 0) {
this._handleReply(reply, next);
} else {
this._handlePush(reply.result, next);
}
return p;
}
}, {
key: '_call',
value: function _call(msg) {
var _this7 = this;
return new Promise(function (resolve, reject) {
var id = _this7._addMessage(msg);
_this7._registerCall(id, resolve, reject);
});
}
}, {
key: '_connect',
value: function _connect() {
if (this.isConnected()) {
this._debug('connect called when already connected');
return;
}
if (this._status === 'connecting') {
return;
}
this._debug('start connecting');
this._setStatus('connecting');
this._clientID = null;
this._reconnect = true;
this._setupTransport();
}
}, {
key: '_disconnect',
value: function _disconnect(reason, shouldReconnect) {
var reconnect = shouldReconnect || false;
if (reconnect === false) {
this._reconnect = false;
}
if (this._isDisconnected()) {
if (!reconnect) {
this._clearConnectedState(reconnect);
}
return;
}
this._clearConnectedState(reconnect);
this._debug('disconnected:', reason, shouldReconnect);
this._setStatus('disconnected');
if (this._refreshTimeout) {
clearTimeout(this._refreshTimeout);
this._refreshTimeout = null;
}
if (this._reconnecting === false) {
// fire unsubscribe events for server side subs.
for (var channel in this._serverSubs) {
if (this._serverSubs.hasOwnProperty(channel)) {
this.emit('unsubscribe', { channel: channel });
}
}
this.emit('disconnect', {
reason: reason,
reconnect: reconnect
});
}
if (reconnect === false) {
this._subs = {};
this._serverSubs = {};
}
if (!this._transportClosed) {
this._transport.close();
}
}
}, {
key: '_refreshFailed',
value: function _refreshFailed() {
this._numRefreshFailed = 0;
if (!this._isDisconnected()) {
this._disconnect('refresh failed', false);
}
if (this._config.onRefreshFailed !== null) {
this._config.onRefreshFailed();
}
}
}, {
key: '_refresh',
value: function _refresh() {
var _this8 = this;
// ask application for new connection token.
this._debug('refresh token');
if (this._config.refreshAttempts === 0) {
this._debug('refresh attempts set to 0, do not send refresh request at all');
this._refreshFailed();
return;
}
if (this._refreshTimeout !== null) {
clearTimeout(this._refreshTimeout);
this._refreshTimeout = null;
}
var clientID = this._clientID;
var xhrID = this._newXHRID();
var cb = function cb(resp) {
if (xhrID in _this8._xhrs) {
delete _this8._xhrs[xhrID];
}
if (_this8._clientID !== clientID) {
return;
}
if (resp.error || resp.status !== 200) {
// We don't perform any connection status related actions here as we are
// relying on server that must close connection eventually.
if (resp.error) {
_this8._debug('error refreshing connection token', resp.error);
} else {
_this8._debug('error refreshing connection token: wrong status code', resp.status);
}
_this8._numRefreshFailed++;
if (_this8._refreshTimeout !== null) {
clearTimeout(_this8._refreshTimeout);
_this8._refreshTimeout = null;
}
if (_this8._config.refreshAttempts !== null && _this8._numRefreshFailed >= _this8._config.refreshAttempts) {
_this8._refreshFailed();
return;
}
var jitter = Math.round(Math.random() * 1000 * Math.max(_this8._numRefreshFailed, 20));
var interval = _this8._config.refreshInterval + jitter;
_this8._refreshTimeout = setTimeout(function () {
return _this8._refresh();
}, interval);
return;
}
_this8._numRefreshFailed = 0;
_this8._token = resp.data.token;
if (!_this8._token) {
_this8._refreshFailed();
return;
}
if (_this8._isDisconnected() && _this8._reconnect) {
_this8._debug('token refreshed, connect from scratch');
_this8._connect();
} else {
_this8._debug('send refreshed token');
var msg = {
method: _this8._methodType.REFRESH,
params: {
token: _this8._token
}
};
_this8._call(msg).then(function (resolveCtx) {
_this8._refreshResponse(_this8._decoder.decodeCommandResult(_this8._methodType.REFRESH, resolveCtx.result));
if (resolveCtx.next) {
resolveCtx.next();
}
}, function (rejectCtx) {
_this8._refreshError(rejectCtx.error);
if (rejectCtx.next) {
rejectCtx.next();
}
});
}
};
if (this._config.onRefresh !== null) {
var context = {};
this._config.onRefresh(context, cb);
} else {
var xhr = this._ajax(this._config.refreshEndpoint, this._config.refreshParams, this._config.refreshHeaders, this._config.refreshData, cb);
this._xhrs[xhrID] = xhr;
}
}
}, {
key: '_refreshError',
value: function _refreshError(err) {
var _this9 = this;
this._debug('refresh error', err);
if (this._refreshTimeout) {
clearTimeout(this._refreshTimeout);
this._refreshTimeout = null;
}
var interval = this._config.refreshInterval + Math.round(Math.random() * 1000);
this._refreshTimeout = setTimeout(function () {
return _this9._refresh();
}, interval);
}
}, {
key: '_refreshResponse',
value: function _refreshResponse(result) {
var _this10 = this;
if (this._refreshTimeout) {
clearTimeout(this._refreshTimeout);
this._refreshTimeout = null;
}
if (result.expires) {
this._clientID = result.client;
this._refreshTimeout = setTimeout(function () {
return _this10._refresh();
}, this._getTTLMilliseconds(result.ttl));
}
}
}, {
key: '_newXHRID',
value: function _newXHRID() {
this._xhrID++;
return this._xhrID;
}
}, {
key: '_subRefresh',
value: function _subRefresh(channel) {
var _this11 = this;
this._debug('refresh subscription token for channel', channel);
if (this._subRefreshTimeouts[channel] !== undefined) {
this._clearSubRefreshTimeout(channel);
} else {
return;
}
var clientID = this._clientID;
var xhrID = this._newXHRID();
var cb = function cb(resp) {
if (xhrID in _this11._xhrs) {
delete _this11._xhrs[xhrID];
}
if (resp.error || resp.status !== 200 || _this11._clientID !== clientID) {
return;
}
var channelsData = {};
if (resp.data.channels) {
for (var i in resp.data.channels) {
var channelData = resp.data.channels[i];
if (!channelData.channel) {
continue;
}
channelsData[channelData.channel] = channelData.token;
}
}
var token = channelsData[channel];
if (!token) {
return;
}
var msg = {
method: _this11._methodType.SUB_REFRESH,
params: {
channel: channel,
token: token
}
};
var sub = _this11._getSub(channel);
if (sub === null) {
return;
}
_this11._call(msg).then(function (resolveCtx) {
_this11._subRefreshResponse(channel, _this11._decoder.decodeCommandResult(_this11._methodType.SUB_REFRESH, resolveCtx.result));
if (resolveCtx.next) {
resolveCtx.next();
}
}, function (rejectCtx) {
_this11._subRefreshError(channel, rejectCtx.error);
if (rejectCtx.next) {
rejectCtx.next();
}
});
};
var data = {
client: this._clientID,
channels: [channel]
};
if (this._config.onPrivateSubscribe !== null) {
this._config.onPrivateSubscribe({
data: data
}, cb);
} else {
var xhr = this._ajax(this._config.subscribeEndpoint, this._config.subscribeParams, this._config.subscribeHeaders, data, cb);
this._xhrs[xhrID] = xhr;
}
}
}, {
key: '_clearSubRefreshTimeout',
value: function _clearSubRefreshTimeout(channel) {
if (this._subRefreshTimeouts[channel] !== undefined) {
clearTimeout(this._subRefreshTimeouts[channel]);
delete this._subRefreshTimeouts[channel];
}
}
}, {
key: '_subRefreshError',
value: function _subRefreshError(channel, err) {
var _this12 = this;
this._debug('subscription refresh error', channel, err);
this._clearSubRefreshTimeout(channel);
var sub = this._getSub(channel);
if (sub === null) {
return;
}
var jitter = Math.round(Math.random() * 1000);
var subRefreshTimeout = setTimeout(function () {
return _this12._subRefresh(channel);
}, this._config.subRefreshInterval + jitter);
this._subRefreshTimeouts[channel] = subRefreshTimeout;
return;
}
}, {
key: '_subRefreshResponse',
value: function _subRefreshResponse(channel, result) {
var _this13 = this;
this._debug('subscription refresh success', channel);
this._clearSubRefreshTimeout(channel);
var sub = this._getSub(channel);
if (sub === null) {
return;
}
if (result.expires === true) {
var subRefreshTimeout = setTimeout(function () {
return _this13._subRefresh(channel);
}, this._getTTLMilliseconds(result.ttl));
this._subRefreshTimeouts[channel] = subRefreshTimeout;
}
return;
}
}, {
key: '_subscribe',
value: function _subscribe(sub, isResubscribe) {
var _this14 = this;
this._debug('subscribing on', sub.channel);
var channel = sub.channel;
if (!(channel in this._subs)) {
this._subs[channel] = sub;
}
if (!this.isConnected()) {
// subscribe will be called later
sub._setNew();
return;
}
sub._setSubscribing(isResubscribe);
var msg = {
method: this._methodType.SUBSCRIBE,
params: {
channel: channel
}
};
// If channel name does not start with privateChannelPrefix - then we
// can just send subscription message to Centrifuge. If channel name
// starts with privateChannelPrefix - then this is a private channel
// and we should ask web application backend for permission first.
if ((0, _utils.startsWith)(channel, this._config.privateChannelPrefix)) {
// private channel.
if (this._isSubscribeBatching) {
this._privateChannels[channel] = true;
} else {
this.startSubscribeBatching();
this._subscribe(sub);
this.stopSubscribeBatching();
}
} else {
var recover = sub._needRecover();
if (recover === true) {
msg.params.recover = true;
var seq = this._getLastSeq(channel);
var gen = this._getLastGen(channel);
if (seq || gen) {
if (seq) {
msg.params.seq = seq;
}
if (gen) {
msg.params.gen = gen;
}
} else {
var offset = this._getLastOffset(channel);
if (offset) {
msg.params.offset = offset;
}
}
var epoch = this._getLastEpoch(channel);
if (epoch) {
msg.params.epoch = epoch;
}
}
this._call(msg).then(function (resolveCtx) {
_this14._subscribeResponse(channel, recover, _this14._decoder.decodeCommandResult(_this14._methodType.SUBSCRIBE, resolveCtx.result));
if (resolveCtx.next) {
resolveCtx.next();
}
}, function (rejectCtx) {
_this14._subscribeError(channel, rejectCtx.error);
if (rejectCtx.next) {
rejectCtx.next();
}
});
}
}
}, {
key: '_unsubscribe',
value: function _unsubscribe(sub) {
delete this._subs[sub.channel];
delete this._lastOffset[sub.channel];
delete this._lastSeq[sub.channel];
delete this._lastGen[sub.channel];
if (this.isConnected()) {
// No need to unsubscribe in disconnected state - i.e. client already unsubscribed.
this._addMessage({
method: this._methodType.UNSUBSCRIBE,
params: {
channel: sub.channel
}
});
}
}
}, {
key: '_getTTLMilliseconds',
value: function _getTTLMilliseconds(ttl) {
// https://stackoverflow.com/questions/12633405/what-is-the-maximum-delay-for-setinterval
return Math.min(ttl * 1000, 2147483647);
}
}, {
key: 'getSub',
value: function getSub(channel) {
return this._getSub(channel);
}
}, {
key: '_getSub',
value: function _getSub(channel) {
var sub = this._subs[channel];
if (!sub) {
return null;
}
return sub;
}
}, {
key: '_isServerSub',
value: function _isServerSub(channel) {
return this._serverSubs[channel] !== undefined;
}
}, {
key: '_connectResponse',
value: function _connectResponse(result, isRecover) {
var _this15 = this;
var wasReconnecting = this._reconnecting;
this._reconnecting = false;
this._resetRetry();
this._refreshRequired = false;
if (this.isConnected()) {
return;
}
if (this._latencyStart !== null) {
this._latency = new Date().getTime() - this._latencyStart.getTime();
this._latencyStart = null;
}
this._clientID = result.client;
this._setStatus('connected');
if (this._refreshTimeout) {
clearTimeout(this._refreshTimeout);
}
if (result.expires) {
this._refreshTimeout = setTimeout(function () {
return _this15._refresh();
}, this._getTTLMilliseconds(result.ttl));
}
this.startBatching();
this.startSubscribeBatching();
for (var channel in this._subs) {
if (this._subs.hasOwnProperty(channel)) {
var sub = this._subs[channel];
if (sub._shouldResubscribe()) {
this._subscribe(sub, wasReconnecting);
}
}
}
this.stopSubscribeBatching();
this.stopBatching();
this._startPing();
var ctx = {
client: result.client,
transport: this._transportName,
latency: this._latency
};
if (result.data) {
ctx.data = result.data;
}
this.emit('connect', ctx);
if (result.subs) {
this._processServerSubs(result.subs, isRecover);
}
}
}, {
key: '_processServerSubs',
value: function _processServerSubs(subs, isRecover) {
for (var channel in subs) {
if (subs.hasOwnProperty(channel)) {
var sub = subs[channel];
var recovered = sub.recovered === true;
var subCtx = { channel: channel, isResubscribe: isRecover, recovered: recovered };
this.emit('subscribe', subCtx);
}
}
for (var _channel2 in subs) {
if (subs.hasOwnProperty(_channel2)) {
var _sub = subs[_channel2];
if (_sub.recovered) {
var pubs = _sub.publications;
if (pubs && pubs.length > 0) {
// handle legacy order.
// TODO: remove as soon as Centrifuge v1 released.
if (pubs.length > 1 && (!pubs[0].offset || pubs[0].offset > pubs[1].offset)) {
pubs = pubs.reverse();
}
for (var i in pubs) {
if (pubs.hasOwnProperty(i)) {
this._handlePublication(_channel2, pubs[i]);
}
}
}
}
this._serverSubs[_channel2] = {
'seq': _sub.seq,
'gen': _sub.gen,
'offset': _sub.offset,
'epoch': _sub.epoch,
'recoverable': _sub.recoverable
};
}
}
}
}, {
key: '_stopPing',
value: function _stopPing() {
if (this._pongTimeout !== null) {
clearTimeout(this._pongTimeout);
this._pongTimeout = null;
}
if (this._pingTimeout !== null) {
clearTimeout(this._pingTimeout);
this._pingTimeout = null;
}
}
}, {
key: '_startPing',
value: function _startPing() {
var _this16 = this;
if (this._config.ping !== true || this._config.pingInterval <= 0) {
return;
}
if (!this.isConnected()) {
return;
}
this._pingTimeout = setTimeout(function () {
if (!_this16.isConnected()) {
_this16._stopPing();
return;
}
_this16.ping();
_this16._pongTimeout = setTimeout(function () {
_this16._disconnect('no ping', true);
}, _this16._config.pongWaitTimeout);
}, this._config.pingInterval);
}
}, {
key: '_restartPing',
value: function _restartPing() {
this._stopPing();
this._startPing();
}
}, {
key: '_subscribeError',
value: function _subscribeError(channel, error) {
var sub = this._getSub(channel);
if (!sub) {
return;
}
if (!sub._isSubscribing()) {
return;
}
if (error.code === 0 && error.message === _errorTimeout) {
// client side timeout.
this._disconnect('timeout', true);
return;
}
sub._setSubscribeError(error);
}
}, {
key: '_subscribeResponse',
value: function _subscribeResponse(channel, isRecover, result) {
var _this17 = this;
var sub = this._getSub(channel);
if (!sub) {
return;
}
if (!sub._isSubscribing()) {
return;
}
var recovered = false;
if ('recovered' in result) {
recovered = result.recovered;
}
sub._setSubscribeSuccess(recovered);
var pubs = result.publications;
if (pubs && pubs.length > 0) {
if (pubs.length >= 2 && !pubs[0].offset && !pubs[1].offset) {
// handle legacy order.
pubs = pubs.reverse();
}
for (var i in pubs) {
if (pubs.hasOwnProperty(i)) {
this._handlePublication(channel, pubs[i]);
}
}
}
if (result.recoverable && (!isRecover || !recovered)) {
this._lastSeq[channel] = result.seq || 0;
this._lastGen[channel] = result.gen || 0;
this._lastOffset[channel] = result.offset || 0;
}
this._lastEpoch[channel] = result.epoch || '';
if (result.recoverable) {
sub._recoverable = true;
}
if (result.expires === true) {
var subRefreshTimeout = setTimeout(function () {
return _this17._subRefresh(channel);
}, this._getTTLMilliseconds(result.ttl));
this._subRefreshTimeouts[channel] = subRefreshTimeout;
}
}
}, {
key: '_handleReply',
value: function _handleReply(reply, next) {
var id = reply.id;
var result = reply.result;
if (!(id in this._callbacks)) {
next();
return;
}
var callbacks = this._callbacks[id];
clearTimeout(this._callbacks[id].timeout);
delete this._callbacks[id];
if (!(0, _utils.errorExists)(reply)) {
var callback = callbacks.callback;
if (!callback) {
return;
}
callback({ result: result, next: next });
} else {
var errback = callbacks.errback;
if (!errback) {
next();
return;
}
var error = reply.error;
errback({ error: error, next: next });
}
}
}, {
key: '_handleJoin',
value: function _handleJoin(channel, join) {
var ctx = { 'info': join.info };
var sub = this._getSub(channel);
if (!sub) {
if (this._isServerSub(channel)) {
ctx.channel = channel;
this.emit('join', ctx);
}
return;
}
sub.emit('join', ctx);
}
}, {
key: '_handleLeave',
value: function _handleLeave(channel, leave) {
var ctx = { 'info': leave.info };
var sub = this._getSub(channel);
if (!sub) {
if (this._isServerSub(channel)) {
ctx.channel = channel;
this.emit('leave', ctx);
}
return;
}
sub.emit('leave', ctx);
}
}, {
key: '_handleUnsub',
value: function _handleUnsub(channel, unsub) {
var ctx = {};
var sub = this._getSub(channel);
if (!sub) {
if (this._isServerSub(channel)) {
delete this._serverSubs[channel];
ctx.channel = channel;
this.emit('unsubscribe', ctx);
}
return;
}
sub.unsubscribe();
if (unsub.resubscribe === true) {
sub.subscribe();
}
}
}, {
key: '_handleSub',
value: function _handleSub(channel, sub) {
this._serverSubs[channel] = {
'seq': sub.seq,
'gen': sub.gen,
'offset': sub.offset,
'epoch': sub.epoch,
'recoverable': sub.recoverable
};
var ctx = { 'channel': channel, isResubscribe: false, recovered: false };
this.emit('subscribe', ctx);
}
}, {
key: '_handlePublication',
value: function _handlePublication(channel, pub) {
var sub = this._getSub(channel);
var ctx = {
'data': pub.data,
'seq': pub.seq,
'gen': pub.gen,
'offset': pub.offset
};
if (pub.info) {
ctx.info = pub.info;
}
if (!sub) {
if (this._isServerSub(channel)) {
if (pub.seq !== undefined) {
this._serverSubs[channel].seq = pub.seq;
}
if (pub.gen !== undefined) {
this._serverSubs[channel].gen = pub.gen;
}
if (pub.offset !== undefined) {
this._serverSubs[channel].offset = pub.offset;
}
ctx.channel = channel;
this.emit('publish', ctx);
}
return;
}
if (pub.seq !== undefined) {
this._lastSeq[channel] = pub.seq;
}
if (pub.gen !== undefined) {
this._lastGen[channel] = pub.gen;
}
if (pub.offset !== undefined) {
this._lastOffset[channel] = pub.offset;
}
sub.emit('publish', ctx);
}
}, {
key: '_handleMessage',
value: function _handleMessage(message) {
this.emit('message', message.data);
}
}, {
key: '_handlePush',
value: function _handlePush(data, next) {
var push = this._