diff --git a/spec/integ/matrix-client-crypto.spec.js b/spec/integ/matrix-client-crypto.spec.js index 966385394..cf6f33c52 100644 --- a/spec/integ/matrix-client-crypto.spec.js +++ b/spec/integ/matrix-client-crypto.spec.js @@ -130,7 +130,12 @@ function expectAliClaimKeys() { return {one_time_keys: result}; }); - return aliTestClient.httpBackend.flush("/keys/claim", 1); + // it can take a while to process the key query, so give it some extra + // time, and make sure the claim actually happens tather than ploughing on + // confusingly. + return aliTestClient.httpBackend.flush("/keys/claim", 1, 20).then((r) => { + expect(r).toEqual(1); + }); } @@ -263,16 +268,16 @@ function sendMessage(client) { function expectSendMessageRequest(httpBackend) { const path = "/send/m.room.encrypted/"; - let sent; + const deferred = q.defer(); httpBackend.when("PUT", path).respond(200, function(path, content) { - sent = content; + deferred.resolve(content); return { event_id: "asdfgh", }; }); - return httpBackend.flush(path, 1).then(function() { - return sent; - }); + + // it can take a while to process the key query, so give it 20ms + return httpBackend.flush(path, 1, 20).then(() => deferred.promise); } function aliRecvMessage() { diff --git a/spec/mock-request.js b/spec/mock-request.js index bf1a1b04e..e4a929a61 100644 --- a/spec/mock-request.js +++ b/spec/mock-request.js @@ -36,15 +36,23 @@ HttpBackend.prototype = { * Respond to all of the requests (flush the queue). * @param {string} path The path to flush (optional) default: all. * @param {integer} numToFlush The number of things to flush (optional), default: all. - * @return {Promise} resolved when there is nothing left to flush. + * @param {integer=} waitTime The time (in ms) to wait for a request to happen. + * default: 5 + * + * @return {Promise} resolves when there is nothing left to flush, with the + * number of requests flushed */ - flush: function(path, numToFlush) { + flush: function(path, numToFlush, waitTime) { const defer = q.defer(); const self = this; let flushed = 0; let triedWaiting = false; + if (waitTime === undefined) { + waitTime = 5; + } console.log( - "HTTP backend flushing... (path=%s numToFlush=%s)", path, numToFlush, + "HTTP backend flushing... (path=%s numToFlush=%s waitTime=%s)", + path, numToFlush, waitTime, ); const tryFlush = function() { // if there's more real requests and more expected requests, flush 'em. @@ -57,7 +65,7 @@ HttpBackend.prototype = { flushed += 1; if (numToFlush && flushed === numToFlush) { console.log(" Flushed assigned amount: %s", numToFlush); - defer.resolve(); + defer.resolve(flushed); } else { console.log(" flushed. Trying for more."); setTimeout(tryFlush, 0); @@ -65,11 +73,11 @@ HttpBackend.prototype = { } else if (flushed === 0 && !triedWaiting) { // we may not have made the request yet, wait a generous amount of // time before giving up. - setTimeout(tryFlush, 5); + setTimeout(tryFlush, waitTime); triedWaiting = true; } else { console.log(" no more flushes."); - defer.resolve(); + defer.resolve(flushed); } }; diff --git a/src/crypto/DeviceList.js b/src/crypto/DeviceList.js index 49b4a4179..6cb7735e6 100644 --- a/src/crypto/DeviceList.js +++ b/src/crypto/DeviceList.js @@ -306,41 +306,57 @@ export default class DeviceList { ).then((res) => { const dk = res.device_keys || {}; + // do each user in a separate promise, to avoid wedging the CPU + // (https://github.com/vector-im/riot-web/issues/3158) + // + // of course we ought to do this in a web worker or similar, but + // this serves as an easy solution for now. + let prom = q(); for (const userId of downloadUsers) { - console.log('got keys for ' + userId + ':', dk[userId]); - - // map from deviceid -> deviceinfo for this user - const userStore = {}; - const devs = this._sessionStore.getEndToEndDevicesForUser(userId); - if (devs) { - Object.keys(devs).forEach((deviceId) => { - const d = DeviceInfo.fromStorage(devs[deviceId], deviceId); - userStore[deviceId] = d; - }); - } - - _updateStoredDeviceKeysForUser( - this._olmDevice, userId, userStore, dk[userId] || {}, - ); - - // update the session store - const storage = {}; - Object.keys(userStore).forEach((deviceId) => { - storage[deviceId] = userStore[deviceId].toStorage(); + prom = prom.delay(5).then(() => { + this._processQueryResponseForUser(userId, dk[userId]); }); - - this._sessionStore.storeEndToEndDevicesForUser( - userId, storage, - ); - - if (token) { - this._sessionStore.storeEndToEndDeviceSyncToken(token); - } } + + return prom; + }).then(() => { + if (token) { + this._sessionStore.storeEndToEndDeviceSyncToken(token); + } + console.log('Completed key download for ' + downloadUsers); }); } + + _processQueryResponseForUser(userId, response) { + console.log('got keys for ' + userId + ':', response); + + // map from deviceid -> deviceinfo for this user + const userStore = {}; + const devs = this._sessionStore.getEndToEndDevicesForUser(userId); + if (devs) { + Object.keys(devs).forEach((deviceId) => { + const d = DeviceInfo.fromStorage(devs[deviceId], deviceId); + userStore[deviceId] = d; + }); + } + + _updateStoredDeviceKeysForUser( + this._olmDevice, userId, userStore, response || {}, + ); + + // update the session store + const storage = {}; + Object.keys(userStore).forEach((deviceId) => { + storage[deviceId] = userStore[deviceId].toStorage(); + }); + + this._sessionStore.storeEndToEndDevicesForUser( + userId, storage, + ); + } } + function _updateStoredDeviceKeysForUser(_olmDevice, userId, userStore, userResult) { let updated = false;