/** * Process open event. * * @method processOpen * @memberof module:ari-client~Client~start * @private */ function processOpen () { processingError = false; // reset backoff handler when we successfully connect retry = backoff.create({ delay: 100 }); self.emit('WebSocketConnected'); // onced, will not be called when an automatic reconnect succeeds. resolve(); }
return new Promise(function(resolve, reject) { // Rewrite resolve/reject functions so they can only be called once and // each disables the other when called. resolve = _.once((function (oldResolve) { return function () { oldResolve(); reject = function () {}; }; })(resolve)); reject = _.once((function (oldReject) { return function () { oldReject(); resolve = function () {}; }; })(reject)); var applications = (_.isArray(apps)) ? apps.join(',') : apps; var wsUrl = util.format( 'ws://%s/ari/events?app=%s&api_key=%s:%s', self._connection.host, applications, self._connection.user, self._connection.pass ); if (subscribeAll) { wsUrl += '&subscribeAll=true'; } var retry = backoff.create({ delay: 100 }); connect(); /** * Connects to the application via WebSocket. * * @method connect * @memberof module:ari-client~Client~start * @private */ function connect () { self._ws = new WebSocket(wsUrl); self._ws.on('open', function () { processOpen(); }); self._ws.on('error', processError); self._ws.on('message', processMessage); self._ws.on('close', processClose); } /** * Process message received by web socket and emit event. * * @method processMessage * @memberof module:ari-client~Client~start * @private * @param {Object} msg - the web socket message * @param {Object} flags - web socket control flags */ function processMessage (msg, flags) { var event = {}; if (msg) { event = JSON.parse(msg); } var eventModels = self._swagger.apis.events.models; var eventModel = _.find(eventModels, function (item, key) { return key === event.type; }); var resources = {}; var instanceIds = []; // Pass in any property that is a known type as an object _.each(eventModel.properties, function (prop) { if (_.contains(_resources.knownTypes, prop.dataType) && event[prop.name] !== undefined && _resources[prop.dataType] !== undefined) { var instance = _resources[prop.dataType]( self, event[prop.name] ); resources[prop.name] = instance; // Keep track of which instance specific events we should // emit var listeners = self._instanceListeners[event.type]; var instanceId = instance._id().toString(); if (listeners) { var updatedListeners = []; _.each(listeners, function(listener) { if (listener.id === instanceId) { // make sure we do not duplicate events for a given instance if (!_.contains(instanceIds, instanceId)) { instanceIds.push(instanceId); } // remove listeners that should only be invoked once if (!listener.once) { updatedListeners.push(listener); } } else { updatedListeners.push(listener); } }); self._instanceListeners[event.type] = updatedListeners; } } }); var promoted = _.keys(resources).length; if (promoted === 1) { resources = resources[_.keys(resources)[0]]; } else if (promoted === 0) { resources = undefined; } self.emit(event.type, event, resources); // If appropriate, emit instance specific events if (instanceIds.length > 0) { _.each(instanceIds, function (instanceId) { self.emit( util.format('%s-%s', event.type, instanceId), event, resources ); }); } } /** * Process open event. * * @method processOpen * @memberof module:ari-client~Client~start * @private */ function processOpen () { processingError = false; // reset backoff handler when we successfully connect retry = backoff.create({ delay: 100 }); self.emit('WebSocketConnected'); // onced, will not be called when an automatic reconnect succeeds. resolve(); } /** * Process close event. Attempt to reconnect to web socket with a back off * to ensure we do not flood the server. * * @method processClose * @memberof module:ari-client~Client~start * @private * @param {Number} reason - reason code for disconnect * @param {String} description - reason text for disconnect */ function processClose (reason, description) { // was connection closed on purpose? if (self._wsClosed) { self._wsClosed = false; return; } if (!processingError) { reconnect(); } } /** * Process error event. * * @method processError * @memberof module:ari-client~Client~start * @private * @param {Error} err - error object */ function processError (err) { // was connection closed on purpose? if (self._wsClosed) { return; } processingError = true; reconnect(err); } /** * Attempts to reconnect to the WebSocket using a backoff function. * * @method reconnect * @memberof module:ari-client~Client~start * @private * @param {Error} [err] - error object */ function reconnect(err) { var scheduled = retry(connect); var msg = err ? err.message : 'unknown'; if (!scheduled) { // onced or disabled if initial connection succeeds. reject(new Error('Connection attempts exceeded WebSocketMaxRetries. ' + msg)); self.emit('WebSocketMaxRetries', err); } else { self.emit('WebSocketReconnecting', err); } } }).asCallback(callback);