Handle attachment upload errors

Adds a new kind of replayable error that handles retry of pre-encryption
failures, e.g., attachment upload.

Fixes #485

// FREEBIE
This commit is contained in:
lilia 2016-02-05 16:42:53 -08:00
parent 910e37649a
commit 7e82d1295c
5 changed files with 64 additions and 17 deletions

View file

@ -10,6 +10,7 @@
ENCRYPT_MESSAGE: 1, ENCRYPT_MESSAGE: 1,
INIT_SESSION: 2, INIT_SESSION: 2,
TRANSMIT_MESSAGE: 3, TRANSMIT_MESSAGE: 3,
REBUILD_MESSAGE: 4,
}; };
window.textsecure = window.textsecure || {}; window.textsecure = window.textsecure || {};
window.textsecure.replay = { window.textsecure.replay = {
@ -88,11 +89,26 @@
SendMessageNetworkError.prototype = new ReplayableError(); SendMessageNetworkError.prototype = new ReplayableError();
SendMessageNetworkError.prototype.constructor = SendMessageNetworkError; SendMessageNetworkError.prototype.constructor = SendMessageNetworkError;
function MessageError(message, httpError) {
ReplayableError.call(this, {
functionCode : Type.REBUILD_MESSAGE,
args : [message]
});
this.name = 'MessageError';
this.code = httpError.code;
this.message = httpError.message;
this.stack = httpError.stack;
}
MessageError.prototype = new ReplayableError();
MessageError.prototype.constructor = MessageError;
window.textsecure.SendMessageNetworkError = SendMessageNetworkError; window.textsecure.SendMessageNetworkError = SendMessageNetworkError;
window.textsecure.IncomingIdentityKeyError = IncomingIdentityKeyError; window.textsecure.IncomingIdentityKeyError = IncomingIdentityKeyError;
window.textsecure.OutgoingIdentityKeyError = OutgoingIdentityKeyError; window.textsecure.OutgoingIdentityKeyError = OutgoingIdentityKeyError;
window.textsecure.ReplayableError = ReplayableError; window.textsecure.ReplayableError = ReplayableError;
window.textsecure.OutgoingMessageError = OutgoingMessageError; window.textsecure.OutgoingMessageError = OutgoingMessageError;
window.textsecure.MessageError = MessageError;
})(); })();
@ -37432,6 +37448,7 @@ function Message(options) {
this.timestamp = options.timestamp; this.timestamp = options.timestamp;
this.needsSync = options.needsSync; this.needsSync = options.needsSync;
} }
Message.prototype = { Message.prototype = {
constructor: Message, constructor: Message,
toProto: function() { toProto: function() {
@ -37502,7 +37519,8 @@ MessageSender.prototype = {
}.bind(this)); }.bind(this));
}, },
sendMessage: function(message) { sendMessage: function(attrs) {
var message = new Message(attrs);
return Promise.all( return Promise.all(
message.attachments.map(this.makeAttachmentPointer.bind(this)) message.attachments.map(this.makeAttachmentPointer.bind(this))
).then(function(attachmentPointers) { ).then(function(attachmentPointers) {
@ -37522,7 +37540,13 @@ MessageSender.prototype = {
} }
); );
}.bind(this)); }.bind(this));
}.bind(this)); }.bind(this)).catch(function(error) {
if (error instanceof Error && error.name === 'HTTPError') {
throw new textsecure.MessageError(attrs, error);
} else {
throw error;
}
});
}, },
sendMessageProto: function(timestamp, numbers, message, callback) { sendMessageProto: function(timestamp, numbers, message, callback) {
var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback); var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback);
@ -37616,15 +37640,13 @@ MessageSender.prototype = {
}, },
sendMessageToNumber: function(number, messageText, attachments, timestamp) { sendMessageToNumber: function(number, messageText, attachments, timestamp) {
var message = new Message({ return this.sendMessage({
recipients : [number], recipients : [number],
body : messageText, body : messageText,
timestamp : timestamp, timestamp : timestamp,
attachments : attachments, attachments : attachments,
needsSync : true needsSync : true
}); });
return this.sendMessage(message);
}, },
closeSession: function(number, timestamp) { closeSession: function(number, timestamp) {
@ -37655,7 +37677,7 @@ MessageSender.prototype = {
return Promise.reject(new Error('No other members in the group')); return Promise.reject(new Error('No other members in the group'));
} }
var message = new Message({ return this.sendMessage({
recipients : numbers, recipients : numbers,
body : messageText, body : messageText,
timestamp : timestamp, timestamp : timestamp,
@ -37666,7 +37688,6 @@ MessageSender.prototype = {
type: textsecure.protobuf.GroupContext.Type.DELIVER type: textsecure.protobuf.GroupContext.Type.DELIVER
} }
}); });
return this.sendMessage(message);
}.bind(this)); }.bind(this));
}, },
@ -37785,6 +37806,7 @@ textsecure.MessageSender = function(url, username, password, attachment_server_u
var sender = new MessageSender(url, username, password, attachment_server_url); var sender = new MessageSender(url, username, password, attachment_server_url);
textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE); textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE);
textsecure.replay.registerFunction(sender.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE); textsecure.replay.registerFunction(sender.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE);
textsecure.replay.registerFunction(sender.sendMessage.bind(sender), textsecure.replay.Type.REBUILD_MESSAGE);
this.sendRequestGroupSyncMessage = sender.sendRequestGroupSyncMessage .bind(sender); this.sendRequestGroupSyncMessage = sender.sendRequestGroupSyncMessage .bind(sender);
this.sendRequestContactSyncMessage = sender.sendRequestContactSyncMessage.bind(sender); this.sendRequestContactSyncMessage = sender.sendRequestContactSyncMessage.bind(sender);

View file

@ -212,7 +212,8 @@
removeOutgoingErrors: function(number) { removeOutgoingErrors: function(number) {
var errors = _.partition(this.get('errors'), function(e) { var errors = _.partition(this.get('errors'), function(e) {
return e.number === number && return e.number === number &&
(e.name === 'OutgoingMessageError' || (e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
e.name === 'SendMessageNetworkError'); e.name === 'SendMessageNetworkError');
}); });
this.set({errors: errors[1]}); this.set({errors: errors[1]});

View file

@ -86,7 +86,8 @@
}, },
retryMessage: function() { retryMessage: function() {
var retrys = _.filter(this.model.get('errors'), function(e) { var retrys = _.filter(this.model.get('errors'), function(e) {
return (e.name === 'OutgoingMessageError' || return (e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
e.name === 'SendMessageNetworkError'); e.name === 'SendMessageNetworkError');
}); });
_.map(retrys, 'number').forEach(function(number) { _.map(retrys, 'number').forEach(function(number) {
@ -105,7 +106,8 @@
render: function() { render: function() {
this.errors = _.groupBy(this.model.get('errors'), 'number'); this.errors = _.groupBy(this.model.get('errors'), 'number');
var hasRetry = _.find(this.model.get('errors'), function(e) { var hasRetry = _.find(this.model.get('errors'), function(e) {
return (e.name === 'OutgoingMessageError' || return (e.name === 'MessageError' ||
e.name === 'OutgoingMessageError' ||
e.name === 'SendMessageNetworkError'); e.name === 'SendMessageNetworkError');
}); });
this.$el.html(Mustache.render(_.result(this, 'template', ''), { this.$el.html(Mustache.render(_.result(this, 'template', ''), {

View file

@ -9,6 +9,7 @@
ENCRYPT_MESSAGE: 1, ENCRYPT_MESSAGE: 1,
INIT_SESSION: 2, INIT_SESSION: 2,
TRANSMIT_MESSAGE: 3, TRANSMIT_MESSAGE: 3,
REBUILD_MESSAGE: 4,
}; };
window.textsecure = window.textsecure || {}; window.textsecure = window.textsecure || {};
window.textsecure.replay = { window.textsecure.replay = {
@ -87,10 +88,25 @@
SendMessageNetworkError.prototype = new ReplayableError(); SendMessageNetworkError.prototype = new ReplayableError();
SendMessageNetworkError.prototype.constructor = SendMessageNetworkError; SendMessageNetworkError.prototype.constructor = SendMessageNetworkError;
function MessageError(message, httpError) {
ReplayableError.call(this, {
functionCode : Type.REBUILD_MESSAGE,
args : [message]
});
this.name = 'MessageError';
this.code = httpError.code;
this.message = httpError.message;
this.stack = httpError.stack;
}
MessageError.prototype = new ReplayableError();
MessageError.prototype.constructor = MessageError;
window.textsecure.SendMessageNetworkError = SendMessageNetworkError; window.textsecure.SendMessageNetworkError = SendMessageNetworkError;
window.textsecure.IncomingIdentityKeyError = IncomingIdentityKeyError; window.textsecure.IncomingIdentityKeyError = IncomingIdentityKeyError;
window.textsecure.OutgoingIdentityKeyError = OutgoingIdentityKeyError; window.textsecure.OutgoingIdentityKeyError = OutgoingIdentityKeyError;
window.textsecure.ReplayableError = ReplayableError; window.textsecure.ReplayableError = ReplayableError;
window.textsecure.OutgoingMessageError = OutgoingMessageError; window.textsecure.OutgoingMessageError = OutgoingMessageError;
window.textsecure.MessageError = MessageError;
})(); })();

View file

@ -11,6 +11,7 @@ function Message(options) {
this.timestamp = options.timestamp; this.timestamp = options.timestamp;
this.needsSync = options.needsSync; this.needsSync = options.needsSync;
} }
Message.prototype = { Message.prototype = {
constructor: Message, constructor: Message,
toProto: function() { toProto: function() {
@ -81,7 +82,8 @@ MessageSender.prototype = {
}.bind(this)); }.bind(this));
}, },
sendMessage: function(message) { sendMessage: function(attrs) {
var message = new Message(attrs);
return Promise.all( return Promise.all(
message.attachments.map(this.makeAttachmentPointer.bind(this)) message.attachments.map(this.makeAttachmentPointer.bind(this))
).then(function(attachmentPointers) { ).then(function(attachmentPointers) {
@ -101,7 +103,13 @@ MessageSender.prototype = {
} }
); );
}.bind(this)); }.bind(this));
}.bind(this)); }.bind(this)).catch(function(error) {
if (error instanceof Error && error.name === 'HTTPError') {
throw new textsecure.MessageError(attrs, error);
} else {
throw error;
}
});
}, },
sendMessageProto: function(timestamp, numbers, message, callback) { sendMessageProto: function(timestamp, numbers, message, callback) {
var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback); var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback);
@ -195,15 +203,13 @@ MessageSender.prototype = {
}, },
sendMessageToNumber: function(number, messageText, attachments, timestamp) { sendMessageToNumber: function(number, messageText, attachments, timestamp) {
var message = new Message({ return this.sendMessage({
recipients : [number], recipients : [number],
body : messageText, body : messageText,
timestamp : timestamp, timestamp : timestamp,
attachments : attachments, attachments : attachments,
needsSync : true needsSync : true
}); });
return this.sendMessage(message);
}, },
closeSession: function(number, timestamp) { closeSession: function(number, timestamp) {
@ -234,7 +240,7 @@ MessageSender.prototype = {
return Promise.reject(new Error('No other members in the group')); return Promise.reject(new Error('No other members in the group'));
} }
var message = new Message({ return this.sendMessage({
recipients : numbers, recipients : numbers,
body : messageText, body : messageText,
timestamp : timestamp, timestamp : timestamp,
@ -245,7 +251,6 @@ MessageSender.prototype = {
type: textsecure.protobuf.GroupContext.Type.DELIVER type: textsecure.protobuf.GroupContext.Type.DELIVER
} }
}); });
return this.sendMessage(message);
}.bind(this)); }.bind(this));
}, },
@ -364,6 +369,7 @@ textsecure.MessageSender = function(url, username, password, attachment_server_u
var sender = new MessageSender(url, username, password, attachment_server_url); var sender = new MessageSender(url, username, password, attachment_server_url);
textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE); textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE);
textsecure.replay.registerFunction(sender.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE); textsecure.replay.registerFunction(sender.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE);
textsecure.replay.registerFunction(sender.sendMessage.bind(sender), textsecure.replay.Type.REBUILD_MESSAGE);
this.sendRequestGroupSyncMessage = sender.sendRequestGroupSyncMessage .bind(sender); this.sendRequestGroupSyncMessage = sender.sendRequestGroupSyncMessage .bind(sender);
this.sendRequestContactSyncMessage = sender.sendRequestContactSyncMessage.bind(sender); this.sendRequestContactSyncMessage = sender.sendRequestContactSyncMessage.bind(sender);