You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-12-08 19:08:34 +03:00
Make Event.attemptDecryption return useful promises
Even if a decryption attempt is in progress, return a promise which blocks until the attempt is complete.
This commit is contained in:
@@ -43,6 +43,8 @@ describe("MatrixEvent", () => {
|
|||||||
it('should retry decryption if a retry is queued', () => {
|
it('should retry decryption if a retry is queued', () => {
|
||||||
let callCount = 0;
|
let callCount = 0;
|
||||||
|
|
||||||
|
let prom2;
|
||||||
|
|
||||||
const crypto = {
|
const crypto = {
|
||||||
decryptEvent: function() {
|
decryptEvent: function() {
|
||||||
++callCount;
|
++callCount;
|
||||||
@@ -50,12 +52,15 @@ describe("MatrixEvent", () => {
|
|||||||
if (callCount == 1) {
|
if (callCount == 1) {
|
||||||
// schedule a second decryption attempt while
|
// schedule a second decryption attempt while
|
||||||
// the first one is still running.
|
// the first one is still running.
|
||||||
encryptedEvent.attemptDecryption(crypto);
|
prom2 = encryptedEvent.attemptDecryption(crypto);
|
||||||
|
|
||||||
const error = new Error("nope");
|
const error = new Error("nope");
|
||||||
error.name = 'DecryptionError';
|
error.name = 'DecryptionError';
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
} else {
|
} else {
|
||||||
|
expect(prom2.isFulfilled()).toBe(
|
||||||
|
false, 'second attemptDecryption resolved too soon');
|
||||||
|
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
clearEvent: {
|
clearEvent: {
|
||||||
type: 'm.room.message',
|
type: 'm.room.message',
|
||||||
@@ -68,6 +73,9 @@ describe("MatrixEvent", () => {
|
|||||||
return encryptedEvent.attemptDecryption(crypto).then(() => {
|
return encryptedEvent.attemptDecryption(crypto).then(() => {
|
||||||
expect(callCount).toEqual(2);
|
expect(callCount).toEqual(2);
|
||||||
expect(encryptedEvent.getType()).toEqual('m.room.message');
|
expect(encryptedEvent.getType()).toEqual('m.room.message');
|
||||||
|
|
||||||
|
// make sure the second attemptDecryption resolves
|
||||||
|
return prom2;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ limitations under the License.
|
|||||||
* @module models/event
|
* @module models/event
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const EventEmitter = require("events").EventEmitter;
|
import Promise from 'bluebird';
|
||||||
|
import {EventEmitter} from 'events';
|
||||||
const utils = require('../utils.js');
|
import utils from '../utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum for event statuses.
|
* Enum for event statuses.
|
||||||
@@ -129,8 +129,10 @@ module.exports.MatrixEvent = function MatrixEvent(
|
|||||||
*/
|
*/
|
||||||
this._forwardingCurve25519KeyChain = [];
|
this._forwardingCurve25519KeyChain = [];
|
||||||
|
|
||||||
/* flag to indicate if we have a process decrypting this event */
|
/* if we have a process decrypting this event, a Promise which resolves
|
||||||
this._decrypting = false;
|
* when it is finished. Normally null.
|
||||||
|
*/
|
||||||
|
this._decryptionPromise = null;
|
||||||
|
|
||||||
/* flag to indicate if we should retry decrypting this event after the
|
/* flag to indicate if we should retry decrypting this event after the
|
||||||
* first attempt (eg, we have received new data which means that a second
|
* first attempt (eg, we have received new data which means that a second
|
||||||
@@ -313,7 +315,7 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
* @return {boolean} True if this event is currently being decrypted, else false.
|
* @return {boolean} True if this event is currently being decrypted, else false.
|
||||||
*/
|
*/
|
||||||
isBeingDecrypted: function() {
|
isBeingDecrypted: function() {
|
||||||
return this._decrypting;
|
return this._decryptionPromise != null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -325,7 +327,7 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
*
|
*
|
||||||
* @param {module:crypto} crypto crypto module
|
* @param {module:crypto} crypto crypto module
|
||||||
*
|
*
|
||||||
* returns a promise which resolves (to undefined) when the decryption
|
* @returns {Promise} promise which resolves (to undefined) when the decryption
|
||||||
* attempt is completed.
|
* attempt is completed.
|
||||||
*/
|
*/
|
||||||
attemptDecryption: async function(crypto) {
|
attemptDecryption: async function(crypto) {
|
||||||
@@ -350,15 +352,25 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
// attempts going at the same time, so just set a flag that says we have
|
// attempts going at the same time, so just set a flag that says we have
|
||||||
// new info.
|
// new info.
|
||||||
//
|
//
|
||||||
if (this._decrypting) {
|
if (this._decryptionPromise) {
|
||||||
console.log(
|
console.log(
|
||||||
`Event ${this.getId()} already being decrypted; queueing a retry`,
|
`Event ${this.getId()} already being decrypted; queueing a retry`,
|
||||||
);
|
);
|
||||||
this._retryDecryption = true;
|
this._retryDecryption = true;
|
||||||
return;
|
return this._decryptionPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._decrypting = true;
|
this._decryptionPromise = this._decryptionLoop(crypto);
|
||||||
|
return this._decryptionPromise;
|
||||||
|
},
|
||||||
|
|
||||||
|
_decryptionLoop: async function(crypto) {
|
||||||
|
// make sure that this method never runs completely synchronously.
|
||||||
|
// (doing so would mean that we would clear _decryptionPromise *before*
|
||||||
|
// it is set in attemptDecryption - and hence end up with a stuck
|
||||||
|
// `_decryptionPromise`).
|
||||||
|
await Promise.resolve();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
this._retryDecryption = false;
|
this._retryDecryption = false;
|
||||||
|
|
||||||
@@ -376,7 +388,7 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
console.error(
|
console.error(
|
||||||
`Error decrypting event (id=${this.getId()}): ${e.stack || e}`,
|
`Error decrypting event (id=${this.getId()}): ${e.stack || e}`,
|
||||||
);
|
);
|
||||||
this._decrypting = false;
|
this._decryptionPromise = null;
|
||||||
this._retryDecryption = false;
|
this._retryDecryption = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -384,15 +396,16 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
// see if we have a retry queued.
|
// see if we have a retry queued.
|
||||||
//
|
//
|
||||||
// NB: make sure to keep this check in the same tick of the
|
// NB: make sure to keep this check in the same tick of the
|
||||||
// event loop as `_decrypting = false` below - otherwise we
|
// event loop as `_decryptionPromise = null` below - otherwise we
|
||||||
// risk a race:
|
// risk a race:
|
||||||
//
|
//
|
||||||
// * A: we check _retryDecryption here and see that it is
|
// * A: we check _retryDecryption here and see that it is
|
||||||
// false
|
// false
|
||||||
// * B: we get a second call to attemptDecryption, which sees
|
// * B: we get a second call to attemptDecryption, which sees
|
||||||
// that _decrypting is true so sets _retryDecryption
|
// that _decryptionPromise is set so sets
|
||||||
// * A: we continue below, clear _decrypting, and never do
|
// _retryDecryption
|
||||||
// the retry.
|
// * A: we continue below, clear _decryptionPromise, and
|
||||||
|
// never do the retry.
|
||||||
//
|
//
|
||||||
if (this._retryDecryption) {
|
if (this._retryDecryption) {
|
||||||
// decryption error, but we have a retry queued.
|
// decryption error, but we have a retry queued.
|
||||||
@@ -416,13 +429,13 @@ utils.extend(module.exports.MatrixEvent.prototype, {
|
|||||||
// (and set res to a 'badEncryptedMessage'). Either way, we can now set the
|
// (and set res to a 'badEncryptedMessage'). Either way, we can now set the
|
||||||
// cleartext of the event and raise Event.decrypted.
|
// cleartext of the event and raise Event.decrypted.
|
||||||
//
|
//
|
||||||
// make sure we clear '_decrypting' before sending the 'Event.decrypted' event,
|
// make sure we clear '_decryptionPromise' before sending the 'Event.decrypted' event,
|
||||||
// otherwise the app will be confused to see `isBeingDecrypted` still set when
|
// otherwise the app will be confused to see `isBeingDecrypted` still set when
|
||||||
// there isn't an `Event.decrypted` on the way.
|
// there isn't an `Event.decrypted` on the way.
|
||||||
//
|
//
|
||||||
// see also notes on _retryDecryption above.
|
// see also notes on _retryDecryption above.
|
||||||
//
|
//
|
||||||
this._decrypting = false;
|
this._decryptionPromise = null;
|
||||||
this._retryDecryption = false;
|
this._retryDecryption = false;
|
||||||
this._setClearData(res);
|
this._setClearData(res);
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user