diff --git a/src/matrix/Sync.js b/src/matrix/Sync.js index 02d4f8b2..cee8347d 100644 --- a/src/matrix/Sync.js +++ b/src/matrix/Sync.js @@ -119,8 +119,13 @@ export class Sync { this._status.set(SyncStatus.Syncing); } } catch (err) { + // retry same request on timeout + if (err.name === "ConnectionError" && err.isTimeout) { + // don't run afterSyncCompleted + continue; + } this._status.set(SyncStatus.Stopped); - if (!(err instanceof AbortError)) { + if (err.name !== AbortError) { console.warn("stopping sync because of error"); console.error(err); this._error = err; diff --git a/src/matrix/e2ee/RoomEncryption.js b/src/matrix/e2ee/RoomEncryption.js index 2c23933d..94af64d1 100644 --- a/src/matrix/e2ee/RoomEncryption.js +++ b/src/matrix/e2ee/RoomEncryption.js @@ -54,6 +54,7 @@ export class RoomEncryption { this._notifyMissingMegolmSession = notifyMissingMegolmSession; this._clock = clock; this._disposed = false; + this._isFlushingRoomKeyShares = false; } async enableSessionBackup(sessionBackup) { @@ -321,25 +322,34 @@ export class RoomEncryption { } async flushPendingRoomKeyShares(hsApi, operations = null) { - if (!operations) { - const txn = await this._storage.readTxn([this._storage.storeNames.operations]); - operations = await txn.operations.getAllByTypeAndScope("share_room_key", this._room.id); + // this has to be reentrant as it can be called from Room.start while still running + if (this._isFlushingRoomKeyShares) { + return; } - for (const operation of operations) { - // just to be sure - if (operation.type !== "share_room_key") { - continue; + this._isFlushingRoomKeyShares = true; + try { + if (!operations) { + const txn = await this._storage.readTxn([this._storage.storeNames.operations]); + operations = await txn.operations.getAllByTypeAndScope("share_room_key", this._room.id); } - const devices = await this._deviceTracker.devicesForRoomMembers(this._room.id, operation.userIds, hsApi); - await this._sendRoomKey(operation.roomKeyMessage, devices, hsApi); - const removeTxn = await this._storage.readWriteTxn([this._storage.storeNames.operations]); - try { - removeTxn.operations.remove(operation.id); - } catch (err) { - removeTxn.abort(); - throw err; + for (const operation of operations) { + // just to be sure + if (operation.type !== "share_room_key") { + continue; + } + const devices = await this._deviceTracker.devicesForRoomMembers(this._room.id, operation.userIds, hsApi); + await this._sendRoomKey(operation.roomKeyMessage, devices, hsApi); + const removeTxn = await this._storage.readWriteTxn([this._storage.storeNames.operations]); + try { + removeTxn.operations.remove(operation.id); + } catch (err) { + removeTxn.abort(); + throw err; + } + await removeTxn.complete(); } - await removeTxn.complete(); + } finally { + this._isFlushingRoomKeyShares = false; } } diff --git a/src/matrix/net/HomeServerApi.js b/src/matrix/net/HomeServerApi.js index 63d5c830..3c5eda8a 100644 --- a/src/matrix/net/HomeServerApi.js +++ b/src/matrix/net/HomeServerApi.js @@ -75,13 +75,17 @@ export class HomeServerApi { method, headers, body: bodyString, - timeout: options && options.timeout + timeout: options?.timeout }); const wrapper = new RequestWrapper(method, url, requestResult); if (this._reconnector) { wrapper.response().catch(err => { + // Some endpoints such as /sync legitimately time-out + // (which is also reported as a ConnectionError) and will re-attempt, + // but spinning up the reconnector in this case is ok, + // as all code ran on session and sync start should be reentrant if (err.name === "ConnectionError") { this._reconnector.onRequestFailed(this); }