Tighten up error handling a bit more

pull/318/head
Niels Andriesse 4 years ago
parent f0cbdb8cda
commit 7a8dbe1bf9

@ -88,10 +88,12 @@ 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 (promise, seal) = Promise<Void>.pending()
let storage = SNMessagingKitConfiguration.shared.storage let storage = SNMessagingKitConfiguration.shared.storage
let transaction = transaction as! YapDatabaseReadWriteTransaction
let userPublicKey = storage.getUserPublicKey()
// Set the timestamp, sender and recipient
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set
message.sentTimestamp = NSDate.millisecondTimestamp() message.sentTimestamp = NSDate.millisecondTimestamp()
} }
let userPublicKey = storage.getUserPublicKey()
message.sender = userPublicKey message.sender = userPublicKey
switch destination { switch destination {
case .contact(let publicKey): message.recipient = publicKey case .contact(let publicKey): message.recipient = publicKey
@ -99,17 +101,18 @@ public final class MessageSender : NSObject {
case .openGroup(_, _): preconditionFailure() case .openGroup(_, _): preconditionFailure()
} }
let isSelfSend = (message.recipient == userPublicKey) let isSelfSend = (message.recipient == userPublicKey)
// Set the failure handler (for precondition failure handling) // Set the failure handler (need it here already for precondition failure handling)
let _ = promise.catch(on: DispatchQueue.main) { error in func handleFailure(with error: Swift.Error, using transaction: YapDatabaseReadWriteTransaction) {
storage.withAsync({ transaction in MessageSender.handleFailedMessageSend(message, with: error, using: transaction)
MessageSender.handleFailedMessageSend(message, with: error, using: transaction)
}, completion: { })
if case .contact(_) = destination, message is VisibleMessage, !isSelfSend { if case .contact(_) = destination, message is VisibleMessage, !isSelfSend {
NotificationCenter.default.post(name: .messageSendingFailed, object: NSNumber(value: message.sentTimestamp!)) NotificationCenter.default.post(name: .messageSendingFailed, object: NSNumber(value: message.sentTimestamp!))
} }
transaction.addCompletionQueue(DispatchQueue.main) {
seal.reject(error)
}
} }
// Validate the message // Validate the message
guard message.isValid else { seal.reject(Error.invalidMessage); return promise } guard message.isValid else { handleFailure(with: Error.invalidMessage, using: transaction); return promise }
// Stop here if this is a self-send // Stop here if this is a self-send
guard !isSelfSend else { guard !isSelfSend else {
storage.withAsync({ transaction in storage.withAsync({ transaction in
@ -131,18 +134,18 @@ public final class MessageSender : NSObject {
// Convert it to protobuf // Convert it to protobuf
let protoOrNil: SNProtoContent? let protoOrNil: SNProtoContent?
if let message = message as? VisibleMessage { if let message = message as? VisibleMessage {
protoOrNil = message.toProto(using: transaction as! YapDatabaseReadWriteTransaction) // Needed because of how TSAttachmentStream works protoOrNil = message.toProto(using: transaction) // Needed because of how TSAttachmentStream works
} else { } else {
protoOrNil = message.toProto() protoOrNil = message.toProto()
} }
guard let proto = protoOrNil else { seal.reject(Error.protoConversionFailed); return promise } guard let proto = protoOrNil else { handleFailure(with: Error.protoConversionFailed, using: transaction); 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).")
seal.reject(error) handleFailure(with: error, using: transaction)
return promise return promise
} }
// Encrypt the serialized protobuf // Encrypt the serialized protobuf
@ -160,7 +163,7 @@ 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).")
seal.reject(error) handleFailure(with: error, using: transaction)
return promise return promise
} }
// Wrap the result // Wrap the result
@ -181,7 +184,7 @@ 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).")
seal.reject(error) handleFailure(with: error, using: transaction)
return promise return promise
} }
// Calculate proof of work // Calculate proof of work
@ -194,7 +197,7 @@ 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.")
seal.reject(Error.proofOfWorkCalculationFailed) handleFailure(with: Error.proofOfWorkCalculationFailed, using: transaction)
return promise return promise
} }
// Send the result // Send the result
@ -228,12 +231,16 @@ public final class MessageSender : NSObject {
$0.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in $0.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
errorCount += 1 errorCount += 1
guard errorCount == promiseCount else { return } // Only error out if all promises failed guard errorCount == promiseCount else { return } // Only error out if all promises failed
seal.reject(error) storage.withAsync({ transaction in
handleFailure(with: error, using: transaction as! YapDatabaseReadWriteTransaction)
}, completion: { })
} }
} }
}.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in }.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
SNLog("Couldn't send message due to error: \(error).") SNLog("Couldn't send message due to error: \(error).")
seal.reject(error) storage.withAsync({ transaction in
handleFailure(with: error, using: transaction as! YapDatabaseReadWriteTransaction)
}, completion: { })
} }
// Return // Return
return promise return promise
@ -243,6 +250,8 @@ public final class MessageSender : NSObject {
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 (promise, seal) = Promise<Void>.pending()
let storage = SNMessagingKitConfiguration.shared.storage let storage = SNMessagingKitConfiguration.shared.storage
let transaction = transaction as! YapDatabaseReadWriteTransaction
// Set the timestamp, sender and recipient
if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set if message.sentTimestamp == nil { // Visible messages will already have their sent timestamp set
message.sentTimestamp = NSDate.millisecondTimestamp() message.sentTimestamp = NSDate.millisecondTimestamp()
} }
@ -252,22 +261,23 @@ 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)"
} }
// Set the failure handler (for precondition failure handling) // Set the failure handler (need it here already for precondition failure handling)
let _ = promise.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in func handleFailure(with error: Swift.Error, using transaction: YapDatabaseReadWriteTransaction) {
storage.withAsync({ transaction in MessageSender.handleFailedMessageSend(message, with: error, using: transaction)
MessageSender.handleFailedMessageSend(message, with: error, using: transaction) transaction.addCompletionQueue(DispatchQueue.main) {
}, completion: { }) seal.reject(error)
}
} }
// Validate the message // Validate the message
guard let message = message as? VisibleMessage else { guard let message = message as? VisibleMessage else {
#if DEBUG #if DEBUG
preconditionFailure() preconditionFailure()
#else #else
seal.reject(Error.invalidMessage) handleFailure(with: Error.invalidMessage, using: transaction)
return promise return promise
#endif #endif
} }
guard message.isValid else { seal.reject(Error.invalidMessage); return promise } guard message.isValid else { handleFailure(with: Error.invalidMessage, using: transaction); return promise }
// Convert the message to an open group message // Convert the message to an open group message
let (channel, server) = { () -> (UInt64, String) in let (channel, server) = { () -> (UInt64, String) in
switch destination { switch destination {
@ -275,7 +285,7 @@ public final class MessageSender : NSObject {
default: preconditionFailure() default: preconditionFailure()
} }
}() }()
guard let openGroupMessage = OpenGroupMessage.from(message, for: server, using: transaction as! YapDatabaseReadWriteTransaction) else { seal.reject(Error.invalidMessage); return promise } guard let openGroupMessage = OpenGroupMessage.from(message, for: server, using: transaction) else { handleFailure(with: Error.invalidMessage, using: transaction); return promise }
// Send the result // Send the result
OpenGroupAPI.sendMessage(openGroupMessage, to: channel, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { openGroupMessage in OpenGroupAPI.sendMessage(openGroupMessage, to: channel, on: server).done(on: DispatchQueue.global(qos: .userInitiated)) { openGroupMessage in
message.openGroupServerMessageID = openGroupMessage.serverID message.openGroupServerMessageID = openGroupMessage.serverID
@ -285,7 +295,9 @@ public final class MessageSender : NSObject {
seal.fulfill(()) seal.fulfill(())
}) })
}.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in }.catch(on: DispatchQueue.global(qos: .userInitiated)) { error in
seal.reject(error) storage.withAsync({ transaction in
handleFailure(with: error, using: transaction as! YapDatabaseReadWriteTransaction)
}, completion: { })
} }
// Return // Return
return promise return promise

Loading…
Cancel
Save