Fix processing of cached envelopes
This commit is contained in:
parent
25f4154cde
commit
227f532ec2
3 changed files with 87 additions and 48 deletions
|
@ -3120,6 +3120,7 @@ export async function startApp(): Promise<void> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handleGroupCallUpdateMessage(data.message, messageDescriptor)) {
|
if (handleGroupCallUpdateMessage(data.message, messageDescriptor)) {
|
||||||
|
event.confirm();
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1790,6 +1790,50 @@ function updateToSchemaVersion30(currentVersion: number, db: Database) {
|
||||||
console.log('updateToSchemaVersion30: success!');
|
console.log('updateToSchemaVersion30: success!');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateToSchemaVersion31(currentVersion: number, db: Database): void {
|
||||||
|
if (currentVersion >= 31) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log('updateToSchemaVersion10: starting...');
|
||||||
|
db.transaction(() => {
|
||||||
|
db.exec(`
|
||||||
|
DROP INDEX unprocessed_id;
|
||||||
|
DROP INDEX unprocessed_timestamp;
|
||||||
|
ALTER TABLE unprocessed RENAME TO unprocessed_old;
|
||||||
|
|
||||||
|
CREATE TABLE unprocessed(
|
||||||
|
id STRING PRIMARY KEY ASC,
|
||||||
|
timestamp INTEGER,
|
||||||
|
version INTEGER,
|
||||||
|
attempts INTEGER,
|
||||||
|
envelope TEXT,
|
||||||
|
decrypted TEXT,
|
||||||
|
source TEXT,
|
||||||
|
sourceDevice TEXT,
|
||||||
|
serverTimestamp INTEGER,
|
||||||
|
sourceUuid STRING
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX unprocessed_timestamp ON unprocessed (
|
||||||
|
timestamp
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT OR REPLACE INTO unprocessed
|
||||||
|
(id, timestamp, version, attempts, envelope, decrypted, source,
|
||||||
|
sourceDevice, serverTimestamp, sourceUuid)
|
||||||
|
SELECT
|
||||||
|
id, timestamp, version, attempts, envelope, decrypted, source,
|
||||||
|
sourceDevice, serverTimestamp, sourceUuid
|
||||||
|
FROM unprocessed_old;
|
||||||
|
|
||||||
|
DROP TABLE unprocessed_old;
|
||||||
|
`);
|
||||||
|
|
||||||
|
db.pragma('user_version = 31');
|
||||||
|
})();
|
||||||
|
console.log('updateToSchemaVersion31: success!');
|
||||||
|
}
|
||||||
|
|
||||||
const SCHEMA_VERSIONS = [
|
const SCHEMA_VERSIONS = [
|
||||||
updateToSchemaVersion1,
|
updateToSchemaVersion1,
|
||||||
updateToSchemaVersion2,
|
updateToSchemaVersion2,
|
||||||
|
@ -1821,6 +1865,7 @@ const SCHEMA_VERSIONS = [
|
||||||
updateToSchemaVersion28,
|
updateToSchemaVersion28,
|
||||||
updateToSchemaVersion29,
|
updateToSchemaVersion29,
|
||||||
updateToSchemaVersion30,
|
updateToSchemaVersion30,
|
||||||
|
updateToSchemaVersion31,
|
||||||
];
|
];
|
||||||
|
|
||||||
function updateSchema(db: Database): void {
|
function updateSchema(db: Database): void {
|
||||||
|
@ -2229,11 +2274,11 @@ async function commitSessionsAndUnprocessed({
|
||||||
|
|
||||||
db.transaction(() => {
|
db.transaction(() => {
|
||||||
for (const item of sessions) {
|
for (const item of sessions) {
|
||||||
createOrUpdateSession(item);
|
assertSync(createOrUpdateSessionSync(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const item of unprocessed) {
|
for (const item of unprocessed) {
|
||||||
saveUnprocessedSync(item);
|
assertSync(saveUnprocessedSync(item));
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,6 @@ import WebSocketResource, {
|
||||||
import Crypto from './Crypto';
|
import Crypto from './Crypto';
|
||||||
import { deriveMasterKeyFromGroupV1, typedArrayToArrayBuffer } from '../Crypto';
|
import { deriveMasterKeyFromGroupV1, typedArrayToArrayBuffer } from '../Crypto';
|
||||||
import { ContactBuffer, GroupBuffer } from './ContactsParser';
|
import { ContactBuffer, GroupBuffer } from './ContactsParser';
|
||||||
import { assert } from '../util/assert';
|
|
||||||
import { isByteBufferEmpty } from '../util/isByteBufferEmpty';
|
import { isByteBufferEmpty } from '../util/isByteBufferEmpty';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -120,7 +119,7 @@ declare global {
|
||||||
type CacheAddItemType = {
|
type CacheAddItemType = {
|
||||||
envelope: EnvelopeClass;
|
envelope: EnvelopeClass;
|
||||||
data: UnprocessedType;
|
data: UnprocessedType;
|
||||||
request: IncomingWebSocketRequest;
|
request: Pick<IncomingWebSocketRequest, 'respond'>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type DecryptedEnvelope = {
|
type DecryptedEnvelope = {
|
||||||
|
@ -145,7 +144,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
|
|
||||||
appQueue: PQueue;
|
appQueue: PQueue;
|
||||||
|
|
||||||
cacheAddBatcher: BatcherType<CacheAddItemType>;
|
decryptAndCacheBatcher: BatcherType<CacheAddItemType>;
|
||||||
|
|
||||||
cacheRemoveBatcher: BatcherType<string>;
|
cacheRemoveBatcher: BatcherType<string>;
|
||||||
|
|
||||||
|
@ -246,14 +245,14 @@ class MessageReceiverInner extends EventTarget {
|
||||||
timeout: 1000 * 60 * 2,
|
timeout: 1000 * 60 * 2,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.cacheAddBatcher = createBatcher<CacheAddItemType>({
|
this.decryptAndCacheBatcher = createBatcher<CacheAddItemType>({
|
||||||
name: 'MessageReceiver.cacheAddBatcher',
|
name: 'MessageReceiver.decryptAndCacheBatcher',
|
||||||
wait: 75,
|
wait: 75,
|
||||||
maxSize: 30,
|
maxSize: 30,
|
||||||
processBatch: (items: Array<CacheAddItemType>) => {
|
processBatch: (items: Array<CacheAddItemType>) => {
|
||||||
// Not returning the promise here because we don't want to stall
|
// Not returning the promise here because we don't want to stall
|
||||||
// the batch.
|
// the batch.
|
||||||
this.cacheAndQueueBatch(items);
|
this.decryptAndCacheBatch(items);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.cacheRemoveBatcher = createBatcher<string>({
|
this.cacheRemoveBatcher = createBatcher<string>({
|
||||||
|
@ -330,7 +329,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
|
|
||||||
unregisterBatchers() {
|
unregisterBatchers() {
|
||||||
window.log.info('MessageReceiver: unregister batchers');
|
window.log.info('MessageReceiver: unregister batchers');
|
||||||
this.cacheAddBatcher.unregister();
|
this.decryptAndCacheBatcher.unregister();
|
||||||
this.cacheRemoveBatcher.unregister();
|
this.cacheRemoveBatcher.unregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,7 +483,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
envelope.serverTimestamp
|
envelope.serverTimestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
this.cacheAndQueue(envelope, plaintext, request);
|
this.decryptAndCache(envelope, plaintext, request);
|
||||||
this.processedCount += 1;
|
this.processedCount += 1;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
request.respond(500, 'Bad encrypted websocket message');
|
request.respond(500, 'Bad encrypted websocket message');
|
||||||
|
@ -554,7 +553,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
onEmpty() {
|
onEmpty() {
|
||||||
const emitEmpty = async () => {
|
const emitEmpty = async () => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this.cacheAddBatcher.flushAndWait(),
|
this.decryptAndCacheBatcher.flushAndWait(),
|
||||||
this.cacheRemoveBatcher.flushAndWait(),
|
this.cacheRemoveBatcher.flushAndWait(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -588,7 +587,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
};
|
};
|
||||||
|
|
||||||
const waitForCacheAddBatcher = async () => {
|
const waitForCacheAddBatcher = async () => {
|
||||||
await this.cacheAddBatcher.onIdle();
|
await this.decryptAndCacheBatcher.onIdle();
|
||||||
this.incomingQueue.add(waitForIncomingQueue);
|
this.incomingQueue.add(waitForIncomingQueue);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -635,11 +634,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
envelopePlaintext = MessageReceiverInner.stringToArrayBufferBase64(
|
envelopePlaintext = MessageReceiverInner.stringToArrayBufferBase64(
|
||||||
item.envelope
|
item.envelope
|
||||||
);
|
);
|
||||||
} else if (typeof item.envelope === 'string') {
|
} else if (item.envelope && typeof item.envelope === 'string') {
|
||||||
assert(
|
|
||||||
item.envelope || item.decrypted,
|
|
||||||
'MessageReceiver.queueCached: empty envelope without decrypted data'
|
|
||||||
);
|
|
||||||
envelopePlaintext = MessageReceiverInner.stringToArrayBuffer(
|
envelopePlaintext = MessageReceiverInner.stringToArrayBuffer(
|
||||||
item.envelope
|
item.envelope
|
||||||
);
|
);
|
||||||
|
@ -686,7 +681,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
this.queueDecryptedEnvelope(envelope, payloadPlaintext);
|
this.queueDecryptedEnvelope(envelope, payloadPlaintext);
|
||||||
}, TaskType.Encrypted);
|
}, TaskType.Encrypted);
|
||||||
} else {
|
} else {
|
||||||
this.queueCachedEnvelope(envelope);
|
this.queueCachedEnvelope(item, envelope);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.log.error(
|
window.log.error(
|
||||||
|
@ -784,14 +779,14 @@ class MessageReceiverInner extends EventTarget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async cacheAndQueueBatch(items: Array<CacheAddItemType>) {
|
async decryptAndCacheBatch(items: Array<CacheAddItemType>) {
|
||||||
window.log.info('MessageReceiver.cacheAndQueueBatch', items.length);
|
window.log.info('MessageReceiver.decryptAndCacheBatch', items.length);
|
||||||
|
|
||||||
const decrypted: Array<DecryptedEnvelope> = [];
|
const decrypted: Array<DecryptedEnvelope> = [];
|
||||||
const storageProtocol = window.textsecure.storage.protocol;
|
const storageProtocol = window.textsecure.storage.protocol;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const zone = new Zone('cacheAndQueueBatch', {
|
const zone = new Zone('decryptAndCacheBatch', {
|
||||||
pendingSessions: true,
|
pendingSessions: true,
|
||||||
pendingUnprocessed: true,
|
pendingUnprocessed: true,
|
||||||
});
|
});
|
||||||
|
@ -822,7 +817,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
failed.push(data);
|
failed.push(data);
|
||||||
window.log.error(
|
window.log.error(
|
||||||
'cacheAndQueue error when processing the envelope',
|
'decryptAndCache error when processing the envelope',
|
||||||
error && error.stack ? error.stack : error
|
error && error.stack ? error.stack : error
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -830,7 +825,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
);
|
);
|
||||||
|
|
||||||
window.log.info(
|
window.log.info(
|
||||||
'MessageReceiver.cacheAndQueueBatch storing ' +
|
'MessageReceiver.decryptAndCacheBatch storing ' +
|
||||||
`${decrypted.length} decrypted envelopes`
|
`${decrypted.length} decrypted envelopes`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -840,10 +835,6 @@ class MessageReceiverInner extends EventTarget {
|
||||||
return {
|
return {
|
||||||
...data,
|
...data,
|
||||||
|
|
||||||
// We have sucessfully decrypted the message so don't bother with
|
|
||||||
// storing the envelope.
|
|
||||||
envelope: '',
|
|
||||||
|
|
||||||
source: envelope.source,
|
source: envelope.source,
|
||||||
sourceUuid: envelope.sourceUuid,
|
sourceUuid: envelope.sourceUuid,
|
||||||
sourceDevice: envelope.sourceDevice,
|
sourceDevice: envelope.sourceDevice,
|
||||||
|
@ -862,7 +853,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
});
|
});
|
||||||
|
|
||||||
window.log.info(
|
window.log.info(
|
||||||
'MessageReceiver.cacheAndQueueBatch acknowledging receipt'
|
'MessageReceiver.decryptAndCacheBatch acknowledging receipt'
|
||||||
);
|
);
|
||||||
|
|
||||||
// Acknowledge all envelopes
|
// Acknowledge all envelopes
|
||||||
|
@ -871,13 +862,13 @@ class MessageReceiverInner extends EventTarget {
|
||||||
request.respond(200, 'OK');
|
request.respond(200, 'OK');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.log.error(
|
window.log.error(
|
||||||
'cacheAndQueueBatch: Failed to send 200 to server; still queuing envelope'
|
'decryptAndCacheBatch: Failed to send 200 to server; still queuing envelope'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.log.error(
|
window.log.error(
|
||||||
'cacheAndQueue error trying to add messages to cache:',
|
'decryptAndCache error trying to add messages to cache:',
|
||||||
error && error.stack ? error.stack : error
|
error && error.stack ? error.stack : error
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -893,19 +884,19 @@ class MessageReceiverInner extends EventTarget {
|
||||||
await this.queueDecryptedEnvelope(envelope, plaintext);
|
await this.queueDecryptedEnvelope(envelope, plaintext);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
window.log.error(
|
window.log.error(
|
||||||
'cacheAndQueue error when processing decrypted envelope',
|
'decryptAndCache error when processing decrypted envelope',
|
||||||
error && error.stack ? error.stack : error
|
error && error.stack ? error.stack : error
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
window.log.info('MessageReceiver.cacheAndQueueBatch fully processed');
|
window.log.info('MessageReceiver.decryptAndCacheBatch fully processed');
|
||||||
|
|
||||||
this.maybeScheduleRetryTimeout();
|
this.maybeScheduleRetryTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheAndQueue(
|
decryptAndCache(
|
||||||
envelope: EnvelopeClass,
|
envelope: EnvelopeClass,
|
||||||
plaintext: ArrayBuffer,
|
plaintext: ArrayBuffer,
|
||||||
request: IncomingWebSocketRequest
|
request: IncomingWebSocketRequest
|
||||||
|
@ -918,7 +909,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
timestamp: envelope.receivedAtCounter,
|
timestamp: envelope.receivedAtCounter,
|
||||||
attempts: 1,
|
attempts: 1,
|
||||||
};
|
};
|
||||||
this.cacheAddBatcher.add({
|
this.decryptAndCacheBatcher.add({
|
||||||
request,
|
request,
|
||||||
envelope,
|
envelope,
|
||||||
data,
|
data,
|
||||||
|
@ -944,7 +935,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
const task = this.handleDecryptedEnvelope.bind(this, envelope, plaintext);
|
const task = this.handleDecryptedEnvelope.bind(this, envelope, plaintext);
|
||||||
const taskWithTimeout = window.textsecure.createTaskWithTimeout(
|
const taskWithTimeout = window.textsecure.createTaskWithTimeout(
|
||||||
task,
|
task,
|
||||||
`queueEncryptedEnvelope ${id}`
|
`queueDecryptedEnvelope ${id}`
|
||||||
);
|
);
|
||||||
const promise = this.addToQueue(taskWithTimeout, TaskType.Decrypted);
|
const promise = this.addToQueue(taskWithTimeout, TaskType.Decrypted);
|
||||||
|
|
||||||
|
@ -989,20 +980,22 @@ class MessageReceiverInner extends EventTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async queueCachedEnvelope(envelope: EnvelopeClass): Promise<void> {
|
async queueCachedEnvelope(
|
||||||
const plaintext = await this.queueEncryptedEnvelope(
|
data: UnprocessedType,
|
||||||
{
|
envelope: EnvelopeClass
|
||||||
sessionStore: new Sessions(),
|
): Promise<void> {
|
||||||
identityKeyStore: new IdentityKeys(),
|
this.decryptAndCacheBatcher.add({
|
||||||
|
request: {
|
||||||
|
respond(code, status) {
|
||||||
|
window.log.info(
|
||||||
|
'queueCachedEnvelope: fake response ' +
|
||||||
|
`with code ${code} and status ${status}`
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
envelope
|
envelope,
|
||||||
);
|
data,
|
||||||
|
});
|
||||||
if (!plaintext) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.queueDecryptedEnvelope(envelope, plaintext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called after `decryptEnvelope` decrypted the message.
|
// Called after `decryptEnvelope` decrypted the message.
|
||||||
|
|
Loading…
Reference in a new issue