Make sure sending errors bubble up to the user

pull/313/head
nielsandriesse 4 years ago
parent 921e2bced6
commit ce7a23c401

@ -38,6 +38,7 @@ public final class MessageSender : NSObject {
} }
internal static func sendToSnodeDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> { internal static func sendToSnodeDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> {
let (promise, seal) = Promise<Void>.pending()
let storage = Configuration.shared.storage let storage = Configuration.shared.storage
if message.sentTimestamp == nil { // Visible messages will already have the sent timestamp set if message.sentTimestamp == nil { // Visible messages will already have the sent timestamp set
message.sentTimestamp = NSDate.millisecondTimestamp() message.sentTimestamp = NSDate.millisecondTimestamp()
@ -48,17 +49,27 @@ public final class MessageSender : NSObject {
case .closedGroup(let groupPublicKey): message.recipient = groupPublicKey case .closedGroup(let groupPublicKey): message.recipient = groupPublicKey
case .openGroup(_, _): preconditionFailure() case .openGroup(_, _): preconditionFailure()
} }
// Set the failure handler
let _ = promise.catch(on: DispatchQueue.main) { error in
storage.withAsync({ transaction in
Configuration.shared.messageSenderDelegate.handleFailedMessageSend(message, with: error, using: transaction)
}, completion: { })
if case .contact(_) = destination {
NotificationCenter.default.post(name: .messageSendingFailed, object: NSNumber(value: message.sentTimestamp!))
}
}
// Validate the message // Validate the message
guard message.isValid else { return Promise(error: Error.invalidMessage) } guard message.isValid else { seal.reject(Error.invalidMessage); return promise }
// Convert it to protobuf // Convert it to protobuf
guard let proto = message.toProto() else { return Promise(error: Error.protoConversionFailed) } guard let proto = message.toProto() else { seal.reject(Error.protoConversionFailed); return promise }
// Serialize the protobuf // Serialize the protobuf
let plaintext: Data let plaintext: Data
do { do {
plaintext = try proto.serializedData() plaintext = try proto.serializedData()
} catch { } catch {
SNLog("Couldn't serialize proto due to error: \(error).") SNLog("Couldn't serialize proto due to error: \(error).")
return Promise(error: error) seal.reject(error)
return promise
} }
// Encrypt the serialized protobuf // Encrypt the serialized protobuf
if case .contact(_) = destination { if case .contact(_) = destination {
@ -75,7 +86,8 @@ public final class MessageSender : NSObject {
} }
} catch { } catch {
SNLog("Couldn't encrypt message for destination: \(destination) due to error: \(error).") SNLog("Couldn't encrypt message for destination: \(destination) due to error: \(error).")
return Promise(error: error) seal.reject(error)
return promise
} }
// Wrap the result // Wrap the result
let kind: SNProtoEnvelope.SNProtoEnvelopeType let kind: SNProtoEnvelope.SNProtoEnvelopeType
@ -95,7 +107,8 @@ public final class MessageSender : NSObject {
senderPublicKey: senderPublicKey, base64EncodedContent: ciphertext.base64EncodedString()) senderPublicKey: senderPublicKey, base64EncodedContent: ciphertext.base64EncodedString())
} catch { } catch {
SNLog("Couldn't wrap message due to error: \(error).") SNLog("Couldn't wrap message due to error: \(error).")
return Promise(error: error) seal.reject(error)
return promise
} }
// Calculate proof of work // Calculate proof of work
if case .contact(_) = destination { if case .contact(_) = destination {
@ -107,7 +120,8 @@ public final class MessageSender : NSObject {
let base64EncodedData = wrappedMessage.base64EncodedString() let base64EncodedData = wrappedMessage.base64EncodedString()
guard let (timestamp, nonce) = ProofOfWork.calculate(ttl: type(of: message).ttl, publicKey: recipient, data: base64EncodedData) else { guard let (timestamp, nonce) = ProofOfWork.calculate(ttl: type(of: message).ttl, publicKey: recipient, data: base64EncodedData) else {
SNLog("Proof of work calculation failed.") SNLog("Proof of work calculation failed.")
return Promise(error: Error.proofOfWorkCalculationFailed) seal.reject(Error.proofOfWorkCalculationFailed)
return promise
} }
// Send the result // Send the result
if case .contact(_) = destination { if case .contact(_) = destination {
@ -116,7 +130,6 @@ public final class MessageSender : NSObject {
} }
} }
let snodeMessage = SnodeMessage(recipient: recipient, data: base64EncodedData, ttl: type(of: message).ttl, timestamp: timestamp, nonce: nonce) let snodeMessage = SnodeMessage(recipient: recipient, data: base64EncodedData, ttl: type(of: message).ttl, timestamp: timestamp, nonce: nonce)
let (promise, seal) = Promise<Void>.pending()
SnodeAPI.sendMessage(snodeMessage).done(on: Threading.workQueue) { promises in SnodeAPI.sendMessage(snodeMessage).done(on: Threading.workQueue) { promises in
var isSuccess = false var isSuccess = false
let promiseCount = promises.count let promiseCount = promises.count
@ -149,18 +162,11 @@ public final class MessageSender : NSObject {
JobQueue.shared.add(notifyPNServerJob, using: transaction) JobQueue.shared.add(notifyPNServerJob, using: transaction)
}, completion: { }) }, completion: { })
} }
let _ = promise.catch(on: DispatchQueue.main) { error in
storage.withAsync({ transaction in
Configuration.shared.messageSenderDelegate.handleFailedMessageSend(message, with: error, using: transaction)
}, completion: { })
if case .contact(_) = destination {
NotificationCenter.default.post(name: .messageSendingFailed, object: NSNumber(value: message.sentTimestamp!))
}
}
return promise return promise
} }
internal static func sendToOpenGroupDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> { internal static func sendToOpenGroupDestination(_ destination: Message.Destination, message: Message, using transaction: Any) -> Promise<Void> {
let (promise, seal) = Promise<Void>.pending()
let storage = Configuration.shared.storage let storage = Configuration.shared.storage
message.sentTimestamp = NSDate.millisecondTimestamp() message.sentTimestamp = NSDate.millisecondTimestamp()
switch destination { switch destination {
@ -168,7 +174,12 @@ public final class MessageSender : NSObject {
case .closedGroup(_): preconditionFailure() case .closedGroup(_): preconditionFailure()
case .openGroup(let channel, let server): message.recipient = "\(server).\(channel)" case .openGroup(let channel, let server): message.recipient = "\(server).\(channel)"
} }
guard message.isValid else { return Promise(error: Error.invalidMessage) } let _ = promise.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
storage.withAsync({ transaction in
Configuration.shared.messageSenderDelegate.handleFailedMessageSend(message, with: error, using: transaction)
}, completion: { })
}
guard message.isValid else { seal.reject(Error.invalidMessage); return promise }
let (channel, server) = { () -> (UInt64, String) in let (channel, server) = { () -> (UInt64, String) in
switch destination { switch destination {
case .openGroup(let channel, let server): return (channel, server) case .openGroup(let channel, let server): return (channel, server)
@ -176,19 +187,18 @@ public final class MessageSender : NSObject {
} }
}() }()
guard let message = message as? VisibleMessage, guard let message = message as? VisibleMessage,
let openGroupMessage = OpenGroupMessage.from(message, for: server) else { return Promise(error: Error.invalidMessage) } let openGroupMessage = OpenGroupMessage.from(message, for: server) else { seal.reject(Error.invalidMessage); return promise }
let promise = OpenGroupAPI.sendMessage(openGroupMessage, to: channel, on: server) OpenGroupAPI.sendMessage(openGroupMessage, to: channel, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { openGroupMessage in
let _ = promise.done(on: DispatchQueue.global(qos: .userInitiated)) { openGroupMessage in
message.openGroupServerMessageID = openGroupMessage.serverID message.openGroupServerMessageID = openGroupMessage.serverID
storage.withAsync({ transaction in seal.fulfill(())
Configuration.shared.messageSenderDelegate.handleSuccessfulMessageSend(message, using: transaction) }.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
}, completion: { }) seal.reject(error)
} }
promise.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in let _ = promise.done(on: DispatchQueue.global(qos: .userInitiated)) {
storage.withAsync({ transaction in storage.withAsync({ transaction in
Configuration.shared.messageSenderDelegate.handleFailedMessageSend(message, with: error, using: transaction) Configuration.shared.messageSenderDelegate.handleSuccessfulMessageSend(message, using: transaction)
}, completion: { }) }, completion: { })
} }
return promise.map { _ in } return promise
} }
} }

@ -17,6 +17,7 @@ public extension TSIncomingMessage {
wasReceivedByUD: true wasReceivedByUD: true
) )
result.openGroupServerMessageID = visibleMessage.openGroupServerMessageID ?? 0 result.openGroupServerMessageID = visibleMessage.openGroupServerMessageID ?? 0
result.isOpenGroupMessage = result.openGroupServerMessageID != 0
return result return result
} }
} }

@ -40,7 +40,7 @@ NSString *NSStringFromOWSInteractionType(OWSInteractionType value);
@property (nonatomic, readonly) uint64_t sortId; @property (nonatomic, readonly) uint64_t sortId;
@property (nonatomic, readonly) uint64_t receivedAtTimestamp; @property (nonatomic, readonly) uint64_t receivedAtTimestamp;
@property (nonatomic, readonly) BOOL shouldUseServerTime; @property (nonatomic, readonly) BOOL shouldUseServerTime;
@property (nonatomic, readonly) BOOL isOpenGroupMessage; @property (nonatomic) BOOL isOpenGroupMessage;
- (void)setServerTimestampToReceivedTimestamp:(uint64_t)receivedAtTimestamp; - (void)setServerTimestampToReceivedTimestamp:(uint64_t)receivedAtTimestamp;

@ -27,9 +27,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) BOOL isExpiringMessage; @property (nonatomic, readonly) BOOL isExpiringMessage;
@property (nonatomic, readonly, nullable) TSQuotedMessage *quotedMessage; @property (nonatomic, readonly, nullable) TSQuotedMessage *quotedMessage;
@property (nonatomic, nullable) OWSLinkPreview *linkPreview; @property (nonatomic, nullable) OWSLinkPreview *linkPreview;
// Open groups
@property (nonatomic) uint64_t openGroupServerMessageID; @property (nonatomic) uint64_t openGroupServerMessageID;
@property (nonatomic, readonly) BOOL isOpenGroupMessage;
- (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE; - (instancetype)initInteractionWithTimestamp:(uint64_t)timestamp inThread:(TSThread *)thread NS_UNAVAILABLE;

@ -432,12 +432,6 @@ static const NSUInteger OWSMessageSchemaVersion = 4;
}]; }];
} }
#pragma mark - Open Groups
- (BOOL)isOpenGroupMessage {
return self.openGroupServerMessageID > 0;
}
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

@ -602,8 +602,7 @@ NSString *NSStringForOutgoingMessageRecipientState(OWSOutgoingMessageRecipientSt
[self applyChangeToSelfAndLatestCopy:transaction [self applyChangeToSelfAndLatestCopy:transaction
changeBlock:^(TSOutgoingMessage *message) { changeBlock:^(TSOutgoingMessage *message) {
// Mark any "sending" recipients as "failed." // Mark any "sending" recipients as "failed."
for (TSOutgoingMessageRecipientState *recipientState in message.recipientStateMap for (TSOutgoingMessageRecipientState *recipientState in message.recipientStateMap.allValues) {
.allValues) {
if (recipientState.state == OWSOutgoingMessageRecipientStateSending) { if (recipientState.state == OWSOutgoingMessageRecipientStateSending) {
recipientState.state = OWSOutgoingMessageRecipientStateFailed; recipientState.state = OWSOutgoingMessageRecipientStateFailed;
} }

@ -26,9 +26,8 @@ public final class MessageSenderDelegate : NSObject, SessionMessagingKit.Message
// MARK: Sending // MARK: Sending
public func handleSuccessfulMessageSend(_ message: Message, using transaction: Any) { public func handleSuccessfulMessageSend(_ message: Message, using transaction: Any) {
guard let tsMessage = TSOutgoingMessage.find(withTimestamp: message.sentTimestamp!) else { return } guard let tsMessage = TSOutgoingMessage.find(withTimestamp: message.sentTimestamp!) else { return }
if let openGroupServerMessageID = message.openGroupServerMessageID { tsMessage.openGroupServerMessageID = message.openGroupServerMessageID ?? 0
tsMessage.openGroupServerMessageID = openGroupServerMessageID tsMessage.isOpenGroupMessage = tsMessage.openGroupServerMessageID != 0
}
tsMessage.update(withSentRecipient: message.recipient!, wasSentByUD: true, transaction: transaction as! YapDatabaseReadWriteTransaction) tsMessage.update(withSentRecipient: message.recipient!, wasSentByUD: true, transaction: transaction as! YapDatabaseReadWriteTransaction)
} }

Loading…
Cancel
Save