From 4fddf92c108ed9e9f1103e119e7dc84e55bd03da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Tue, 15 Aug 2023 01:24:43 +0200 Subject: [PATCH 1/2] Extract method to process "token_expired" errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel Calviño Sánchez --- src/utils/signaling.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/utils/signaling.js b/src/utils/signaling.js index ed09af42911..07413a929d7 100644 --- a/src/utils/signaling.js +++ b/src/utils/signaling.js @@ -764,8 +764,7 @@ Signaling.Standalone.prototype.connect = function() { console.error('An error occurred processing the signaling message, please ask your server administrator to check the log file') break case 'token_expired': - console.info('The signaling token is expired, need to update settings') - this._trigger('updateSettings') + this.processErrorTokenExpired() break default: console.error('Ignore unknown error', data) @@ -1351,6 +1350,12 @@ Signaling.Standalone.prototype.processRoomParticipantsEvent = function(data) { } } +Signaling.Standalone.prototype.processErrorTokenExpired = function() { + console.info('The signaling token is expired, need to update settings') + + this._trigger('updateSettings') +} + Signaling.Standalone.prototype.requestOffer = function(sessionid, roomType, sid = undefined) { if (!this.hasFeature('mcu')) { console.warn("Can't request an offer without a MCU.") From 1c1c408aee82a949fa012dd15e7b0dd991233ed5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Fri, 2 Dec 2022 21:13:55 +0100 Subject: [PATCH 2/2] Fix using signaling settings while being refetched MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the external signaling server returns the error "token_expired" the signaling settings are fetched again. However, if there are further requests and "token_expired" is returned again the previous fetch is canceled and a new one started instead, which caused the settings to be temporary set to "null". The signaling settings are expected to always be an object, so setting them to "null" could cause an error if they were used during that time (for example, during a reconnection, which caused the signaling object to "hang" and not do any further connection attempt). Preventing the settings to be nullified is only half of the story, though; "token_expired" can be thrown when trying to connect, and errors thrown when trying to connect cause a reconnection attempt. This could end in a reconnection loop, as each "token_expired" error would cause another fetch of the settings, cancelling the previous fetch and thus preventing the settings to be updated, and as the previous settings would be still used any connection attempt would end again in another "token_expired" error. To solve all that now any connection attempt done after receiving a "token_expired" error is deferred until the signaling settings were updated. Note that the previous signaling settings are kept to ensure that a signaling object is always available, even if outdated. However, any usage of the outdated signaling settings is expected to cause a "token_expired" error to be thrown; right now that only happens during connections, so only that code needs to wait for the settings to be fetched again. Signed-off-by: Daniel Calviño Sánchez --- src/utils/signaling.js | 38 ++++++++++++++++++++++++++++++++++++++ src/utils/webrtc/index.js | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/utils/signaling.js b/src/utils/signaling.js index 07413a929d7..5ab0f90dbeb 100644 --- a/src/utils/signaling.js +++ b/src/utils/signaling.js @@ -140,6 +140,20 @@ Signaling.Base.prototype._trigger = function(ev, args) { EventBus.$emit('signaling-' + kebabCase(ev), args) } +Signaling.Base.prototype.setSettings = function(settings) { + if (!settings) { + // Signaling object is expected to always have a settings object + return + } + + this.settings = settings + + if (this._pendingUpdateSettingsPromise) { + this._pendingUpdateSettingsPromise.resolve() + delete this._pendingUpdateSettingsPromise + } +} + Signaling.Base.prototype.isNoMcuWarningEnabled = function() { return !this.settings.hideWarning } @@ -646,6 +660,19 @@ Signaling.Standalone.prototype.connect = function() { }, 2000) } + if (this._pendingUpdateSettingsPromise) { + console.info('Deferring establishing signaling connection until signaling settings are updated') + + this._pendingUpdateSettingsPromise.then(() => { + // "reconnect()" is called instead of "connect()", even if that + // slightly delays the connection, as "reconnect()" prevents + // duplicated connection requests. + this.reconnect() + }) + + return + } + console.debug('Connecting to ' + this.url + ' for ' + this.settings.token) this.callbacks = {} this.id = 1 @@ -1353,6 +1380,17 @@ Signaling.Standalone.prototype.processRoomParticipantsEvent = function(data) { Signaling.Standalone.prototype.processErrorTokenExpired = function() { console.info('The signaling token is expired, need to update settings') + if (!this._pendingUpdateSettingsPromise) { + let pendingUpdateSettingsPromiseResolve + this._pendingUpdateSettingsPromise = new Promise((resolve, reject) => { + // The Promise executor is run even before the Promise constructor has + // finished, so "this._pendingUpdateSettingsPromise" is not available + // yet. + pendingUpdateSettingsPromiseResolve = resolve + }) + this._pendingUpdateSettingsPromise.resolve = pendingUpdateSettingsPromiseResolve + } + this._trigger('updateSettings') } diff --git a/src/utils/webrtc/index.js b/src/utils/webrtc/index.js index 8be650f7610..0a32cde2494 100644 --- a/src/utils/webrtc/index.js +++ b/src/utils/webrtc/index.js @@ -104,7 +104,7 @@ async function connectSignaling(token) { signaling.on('updateSettings', async function() { const settings = await getSignalingSettings(token) console.debug('Received updated settings', settings) - signaling.settings = settings + signaling.setSettings(settings) }) }