Download attachments in separate queue from message processing

This commit is contained in:
Scott Nonnenberg
2019-01-30 12:15:07 -08:00
parent a43a78731a
commit 1d2c3ae23c
34 changed files with 2062 additions and 214 deletions

View File

@@ -1118,8 +1118,11 @@ MessageReceiver.prototype.extend({
},
handleContacts(envelope, contacts) {
window.log.info('contact sync');
const attachmentPointer = contacts.blob;
return this.handleAttachment(attachmentPointer).then(() => {
const { blob } = contacts;
// Note: we do not return here because we don't want to block the next message on
// this attachment download and a lot of processing of that attachment.
this.handleAttachment(blob).then(attachmentPointer => {
const results = [];
const contactBuffer = new ContactBuffer(attachmentPointer.data);
let contactDetails = contactBuffer.next();
@@ -1142,8 +1145,11 @@ MessageReceiver.prototype.extend({
},
handleGroups(envelope, groups) {
window.log.info('group sync');
const attachmentPointer = groups.blob;
return this.handleAttachment(attachmentPointer).then(() => {
const { blob } = groups;
// Note: we do not return here because we don't want to block the next message on
// this attachment download and a lot of processing of that attachment.
this.handleAttachment(blob).then(attachmentPointer => {
const groupBuffer = new GroupBuffer(attachmentPointer.data);
let groupDetails = groupBuffer.next();
const promises = [];
@@ -1211,32 +1217,32 @@ MessageReceiver.prototype.extend({
isGroupBlocked(groupId) {
return textsecure.storage.get('blocked-groups', []).indexOf(groupId) >= 0;
},
cleanAttachment(attachment) {
return {
..._.omit(attachment, 'thumbnail'),
id: attachment.id.toString(),
key: attachment.key ? attachment.key.toString('base64') : null,
digest: attachment.digest ? attachment.digest.toString('base64') : null,
};
},
async downloadAttachment(attachment) {
const encrypted = await this.server.getAttachment(attachment.id);
const { key, digest } = attachment;
const data = await textsecure.crypto.decryptAttachment(
encrypted,
window.Signal.Crypto.base64ToArrayBuffer(key),
window.Signal.Crypto.base64ToArrayBuffer(digest)
);
return {
..._.omit(attachment, 'digest', 'key'),
data,
};
},
handleAttachment(attachment) {
// eslint-disable-next-line no-param-reassign
attachment.id = attachment.id.toString();
// eslint-disable-next-line no-param-reassign
attachment.key = attachment.key.toArrayBuffer();
if (attachment.digest) {
// eslint-disable-next-line no-param-reassign
attachment.digest = attachment.digest.toArrayBuffer();
}
function decryptAttachment(encrypted) {
return textsecure.crypto.decryptAttachment(
encrypted,
attachment.key,
attachment.digest
);
}
function updateAttachment(data) {
// eslint-disable-next-line no-param-reassign
attachment.data = data;
}
return this.server
.getAttachment(attachment.id)
.then(decryptAttachment)
.then(updateAttachment);
const cleaned = this.cleanAttachment(attachment);
return this.downloadAttachment(cleaned);
},
async handleEndSession(number) {
window.log.info('got end session');
@@ -1291,14 +1297,6 @@ MessageReceiver.prototype.extend({
if (decrypted.group !== null) {
decrypted.group.id = decrypted.group.id.toBinary();
if (
decrypted.group.type === textsecure.protobuf.GroupContext.Type.UPDATE
) {
if (decrypted.group.avatar !== null) {
promises.push(this.handleAttachment(decrypted.group.avatar));
}
}
const storageGroups = textsecure.storage.groups;
promises.push(
@@ -1366,65 +1364,67 @@ MessageReceiver.prototype.extend({
);
}
for (let i = 0; i < attachmentCount; i += 1) {
const attachment = decrypted.attachments[i];
promises.push(this.handleAttachment(attachment));
}
// Here we go from binary to string/base64 in all AttachmentPointer digest/key fields
const previewCount = (decrypted.preview || []).length;
for (let i = 0; i < previewCount; i += 1) {
const preview = decrypted.preview[i];
if (preview.image) {
promises.push(this.handleAttachment(preview.image));
if (
decrypted.group &&
decrypted.group.type === textsecure.protobuf.GroupContext.Type.UPDATE
) {
if (decrypted.group.avatar !== null) {
decrypted.group.avatar = this.cleanAttachment(decrypted.group.avatar);
}
}
if (decrypted.contact && decrypted.contact.length) {
const contacts = decrypted.contact;
decrypted.attachments = (decrypted.attachments || []).map(
this.cleanAttachment.bind(this)
);
decrypted.preview = (decrypted.preview || []).map(item => {
const { image } = item;
for (let i = 0, max = contacts.length; i < max; i += 1) {
const contact = contacts[i];
const { avatar } = contact;
if (avatar && avatar.avatar) {
// We don't want the failure of a thumbnail download to fail the handling of
// this message entirely, like we do for full attachments.
promises.push(
this.handleAttachment(avatar.avatar).catch(error => {
window.log.error(
'Problem loading avatar for contact',
error && error.stack ? error.stack : error
);
})
);
}
if (!image) {
return item;
}
}
return {
...item,
image: this.cleanAttachment(image),
};
});
decrypted.contact = (decrypted.contact || []).map(item => {
const { avatar } = item;
if (!avatar || !avatar.avatar) {
return item;
}
return {
...item,
avatar: {
...item.avatar,
avatar: this.cleanAttachment(item.avatar.avatar),
},
};
});
if (decrypted.quote && decrypted.quote.id) {
decrypted.quote.id = decrypted.quote.id.toNumber();
}
if (decrypted.quote && decrypted.quote.attachments) {
const { attachments } = decrypted.quote;
if (decrypted.quote) {
decrypted.quote.attachments = (decrypted.quote.attachments || []).map(
item => {
const { thumbnail } = item;
for (let i = 0, max = attachments.length; i < max; i += 1) {
const attachment = attachments[i];
const { thumbnail } = attachment;
if (!thumbnail) {
return item;
}
if (thumbnail) {
// We don't want the failure of a thumbnail download to fail the handling of
// this message entirely, like we do for full attachments.
promises.push(
this.handleAttachment(thumbnail).catch(error => {
window.log.error(
'Problem loading thumbnail for quote',
error && error.stack ? error.stack : error
);
})
);
return {
...item,
thumbnail: this.cleanAttachment(item.thumbnail),
};
}
}
);
}
return Promise.all(promises).then(() => decrypted);
@@ -1454,6 +1454,11 @@ textsecure.MessageReceiver = function MessageReceiverWrapper(
);
this.getStatus = messageReceiver.getStatus.bind(messageReceiver);
this.close = messageReceiver.close.bind(messageReceiver);
this.downloadAttachment = messageReceiver.downloadAttachment.bind(
messageReceiver
);
messageReceiver.connect();
};