Fix processing of cached envelopes

This commit is contained in:
Fedor Indutny 2021-05-24 14:30:56 -07:00 committed by GitHub
parent 25f4154cde
commit 227f532ec2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 87 additions and 48 deletions

View file

@ -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();
} }

View file

@ -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));
} }
})(); })();
} }

View file

@ -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.