More pinging logic

This commit is contained in:
Mikunj 2019-05-24 16:07:00 +10:00
parent 7f679ba5ed
commit 455c8c520b
9 changed files with 72 additions and 28 deletions

View File

@ -9,6 +9,9 @@ public extension LokiAPI {
let data: LosslessStringConvertible
/// The time to live for the message in milliseconds.
let ttl: UInt64
/// Wether this message is a ping.
/// This should always be false unless it is from p2p pinging logic.
let isPing: Bool
/// When the proof of work was calculated, if applicable.
///
/// - Note: Expressed as milliseconds since 00:00:00 UTC on 1 January 1970.
@ -25,9 +28,13 @@ public extension LokiAPI {
let wrappedMessage = try LokiMessageWrapper.wrap(message: signalMessage, timestamp: timestamp)
let data = wrappedMessage.base64EncodedString()
let destination = signalMessage["destination"] as! String
var ttl = LokiAPI.defaultMessageTTL
if let messageTTL = signalMessage["ttl"] as? UInt, messageTTL > 0 { ttl = UInt64(messageTTL) }
return Message(destination: destination, data: data, ttl: ttl, timestamp: nil, nonce: nil)
let isPing = signalMessage["isPing"] as! Bool
return Message(destination: destination, data: data, ttl: ttl, isPing: isPing)
} catch let error {
Logger.debug("[Loki] Failed to convert Signal message to Loki message: \(signalMessage)")
return nil
@ -40,17 +47,19 @@ public extension LokiAPI {
/// - destination: The destination
/// - data: The data
/// - ttl: The time to live
public init(destination: String, data: LosslessStringConvertible, ttl: UInt64) {
public init(destination: String, data: LosslessStringConvertible, ttl: UInt64, isPing: Bool = false) {
self.destination = destination
self.data = data
self.ttl = ttl
self.isPing = isPing
}
/// Private init for setting proof of work. Use `calculatePoW` to get a message with these fields
private init(destination: String, data: LosslessStringConvertible, ttl: UInt64, timestamp: UInt64?, nonce: String?) {
private init(destination: String, data: LosslessStringConvertible, ttl: UInt64, isPing: Bool, timestamp: UInt64?, nonce: String?) {
self.destination = destination
self.data = data
self.ttl = ttl
self.isPing = isPing
self.timestamp = timestamp
self.nonce = nonce
}
@ -64,7 +73,7 @@ public extension LokiAPI {
DispatchQueue.global(qos: .default).async {
let now = NSDate.ows_millisecondTimeStamp()
if let nonce = ProofOfWork.calculate(data: self.data as! String, pubKey: self.destination, timestamp: now, ttl: self.ttl) {
let result = Message(destination: self.destination, data: self.data, ttl: self.ttl, timestamp: now, nonce: nonce)
let result = Message(destination: self.destination, data: self.data, ttl: self.ttl, isPing: self.isPing, timestamp: now, nonce: nonce)
seal.fulfill(result)
} else {
seal.reject(Error.proofOfWorkCalculationFailed)

View File

@ -1,15 +1,17 @@
extension LokiAPI {
private static let messageSender: MessageSender = SSKEnvironment.shared.messageSender
/// The amount of time before pinging when a user is set to offline
private static let offlinePingTime = 2 * kMinuteInterval
/// A p2p state struct
internal struct P2PDetails {
var address: String
var port: UInt32
var isOnline: Bool
var timerDuration: Double
var pingTimer: WeakTimer? = nil
var pingTimer: Timer? = nil
var target: Target {
return Target(address: address, port: port)
@ -68,6 +70,23 @@ extension LokiAPI {
// TODO: Ping the contact
}
@objc public static func setOnline(_ isOnline: Bool, forContact pubKey: String) {
guard var details = contactP2PDetails[pubKey] else { return }
let interval = isOnline ? details.timerDuration : offlinePingTime
// Setup a new timer
details.pingTimer?.invalidate()
details.pingTimer = WeakTimer.scheduledTimer(timeInterval: interval, target: self, userInfo: nil, repeats: true) { _ in ping(contact: pubKey) }
details.isOnline = isOnline
contactP2PDetails[pubKey] = details
}
@objc public static func ping(contact pubKey: String) {
}
/// Set the Contact p2p details
///
/// - Parameters:
@ -109,7 +128,7 @@ extension LokiAPI {
return nil
}
return LokiAddressMessage(in: thread, address: ourAddress.address, port: ourAddress.port)
return LokiAddressMessage(in: thread, address: ourAddress.address, port: ourAddress.port, isPing: false)
}
/// Send a `Loki Address` message to the given thread

View File

@ -51,16 +51,6 @@ import PromiseKit
}.map { Set($0) }
}
public static func ping(_ hexEncodedPublicKey: String) -> Promise<Set<Promise<RawResponse>>> {
let isP2PMessagingPossible = false
if isP2PMessagingPossible {
// TODO: Send using P2P protocol
} else {
let parameters: [String:Any] = [ "pubKey" : hexEncodedPublicKey ] // TODO: Figure out correct parameters
return getTargetSnodes(for: hexEncodedPublicKey).mapValues { invoke(.sendMessage, on: $0, associatedWith: hexEncodedPublicKey, parameters: parameters) }.map { Set($0) }
}
}
// MARK: Public API (Obj-C)
@objc public static func objc_sendSignalMessage(_ signalMessage: SignalMessage, to destination: String, with timestamp: UInt64) -> AnyPromise {
let promise = sendSignalMessage(signalMessage, to: destination, timestamp: timestamp).mapValues { AnyPromise.from($0) }.map { Set($0) }
@ -82,13 +72,22 @@ import PromiseKit
}
}
// If we have the p2p details then send message to that
// If we have the p2p details and we have marked the user as online OR we are pinging the user, then use peer to peer
// If that failes then fallback to storage server
// TODO: probably only send to p2p if user is online or we are pinging them
// p2pDetails && (isPing || peerIsOnline)
if let p2pDetails = contactP2PDetails[destination] {
if let p2pDetails = contactP2PDetails[destination], message.isPing || p2pDetails.isOnline {
let targets = Promise.wrap([p2pDetails.target])
return sendMessage(message, targets: targets).recover { _ in return sendThroughStorageServer() }
return sendMessage(message, targets: targets).recover { error -> Promise<Set<Promise<RawResponse>>> in
// The user is not online
LokiAPI.setOnline(false, forContact: destination)
// If it was a ping then don't send to the storage server
if (message.isPing) {
Logger.warn("[Loki] Failed to ping \(destination) - Marking contact as offline.")
throw error
}
return sendThroughStorageServer()
}
}
return sendThroughStorageServer()

View File

@ -7,11 +7,13 @@ NS_SWIFT_NAME(LokiAddressMessage)
@interface LKAddressMessage : LKEphemeralMessage
- (instancetype)initInThread:(nullable TSThread *)thread
address:(NSString *)address
port:(uint)port;
address:(NSString *)address
port:(uint)port
isPing:(BOOL)isPing;
@property (nonatomic, readonly) NSString *address;
@property (nonatomic, readonly) uint port;
@property (nonatomic, readonly) BOOL isPing;
@end

View File

@ -7,6 +7,7 @@
@property (nonatomic) NSString *address;
@property (nonatomic) uint port;
@property (nonatomic) BOOL isPing;
@end
@ -15,6 +16,7 @@
- (instancetype)initInThread:(nullable TSThread *)thread
address:(NSString *)address
port:(uint)port
isPing:(bool)isPing
{
self = [super initInThread:thread];
if (!self) {
@ -23,6 +25,7 @@
_address = address;
_port = port;
_isPing = isPing;
return self;
}

View File

@ -437,7 +437,7 @@ NS_ASSUME_NONNULL_BEGIN
if (contentProto.lokiAddressMessage) {
NSString *address = contentProto.lokiAddressMessage.ptpAddress;
uint32_t port = contentProto.lokiAddressMessage.ptpPort;
[LokiAPI didReceiveLokiAddressMessageForContact:envelope.source address:address port:port receivedThroughP2P:envelope.isPtpMessage]
[LokiAPI didReceiveLokiAddressMessageForContact:envelope.source address:address port:port receivedThroughP2P:envelope.isPtpMessage];
}
if (contentProto.syncMessage) {

View File

@ -44,6 +44,7 @@
#import "TSSocketManager.h"
#import "TSThread.h"
#import "LKFriendRequestMessage.h"
#import "LKAddressMessage.h"
#import <AxolotlKit/AxolotlExceptions.h>
#import <AxolotlKit/CipherMessage.h>
#import <AxolotlKit/PreKeyBundle.h>
@ -1792,7 +1793,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
isSilent:false
isOnline:false
registrationId:0
ttl:message.ttl];
ttl:message.ttl
isPing:false];
NSError *error;
NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error];
@ -1879,6 +1881,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
BOOL isSilent = message.isSilent;
BOOL isOnline = message.isOnline;
LKAddressMessage *_Nullable addressMessage = [message as:[LKAddressMessage class]];
BOOL isPing = addressMessage != nil && addressMessage.isPing;
OWSMessageServiceParams *messageParams =
[[OWSMessageServiceParams alloc] initWithType:messageType
recipientId:recipientId
@ -1887,7 +1892,8 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
isSilent:isSilent
isOnline:isOnline
registrationId:[cipher throws_remoteRegistrationId:transaction]
ttl:message.ttl];
ttl:message.ttl
isPing:isPing];
NSError *error;
NSDictionary *jsonDict = [MTLJSONAdapter JSONDictionaryFromModel:messageParams error:&error];

View File

@ -27,6 +27,9 @@ NS_ASSUME_NONNULL_BEGIN
// Loki: Message ttl
@property (nonatomic, readonly) uint ttl;
// Loki: Wether this message is a p2p ping
@property (nonatomic, readonly) BOOL isPing;
- (instancetype)initWithType:(TSWhisperMessageType)type
recipientId:(NSString *)destination
device:(int)deviceId
@ -34,7 +37,8 @@ NS_ASSUME_NONNULL_BEGIN
isSilent:(BOOL)isSilent
isOnline:(BOOL)isOnline
registrationId:(int)registrationId
ttl:(uint)ttl;
ttl:(uint)ttl
isPing:(BOOL)isPing;
@end

View File

@ -23,6 +23,7 @@ NS_ASSUME_NONNULL_BEGIN
isOnline:(BOOL)isOnline
registrationId:(int)registrationId
ttl:(uint)ttl
isPing:(BOOL)isPing
{
self = [super init];
@ -38,6 +39,7 @@ NS_ASSUME_NONNULL_BEGIN
_silent = isSilent;
_online = isOnline;
_ttl = ttl;
_isPing = isPing;
return self;
}