417 lines
8.6 KiB
Objective-C
417 lines
8.6 KiB
Objective-C
#import "XMPPAutoPing.h"
|
|
#import "XMPPPing.h"
|
|
#import "XMPP.h"
|
|
#import "XMPPLogging.h"
|
|
|
|
#if ! __has_feature(objc_arc)
|
|
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
|
#endif
|
|
|
|
// Log levels: off, error, warn, info, verbose
|
|
// Log flags: trace
|
|
#if DEBUG
|
|
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
|
#else
|
|
static const int xmppLogLevel = XMPP_LOG_LEVEL_WARN;
|
|
#endif
|
|
|
|
@interface XMPPAutoPing ()
|
|
- (void)updatePingIntervalTimer;
|
|
- (void)startPingIntervalTimer;
|
|
- (void)stopPingIntervalTimer;
|
|
@end
|
|
|
|
#pragma mark -
|
|
|
|
@implementation XMPPAutoPing
|
|
|
|
- (id)init
|
|
{
|
|
return [self initWithDispatchQueue:NULL];
|
|
}
|
|
|
|
- (id)initWithDispatchQueue:(dispatch_queue_t)queue
|
|
{
|
|
if ((self = [super initWithDispatchQueue:queue]))
|
|
{
|
|
pingInterval = 60;
|
|
pingTimeout = 10;
|
|
|
|
lastReceiveTime = 0;
|
|
|
|
xmppPing = [[XMPPPing alloc] initWithDispatchQueue:queue];
|
|
xmppPing.respondsToQueries = NO;
|
|
|
|
[xmppPing addDelegate:self delegateQueue:moduleQueue];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)activate:(XMPPStream *)aXmppStream
|
|
{
|
|
if ([super activate:aXmppStream])
|
|
{
|
|
[xmppPing activate:aXmppStream];
|
|
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)deactivate
|
|
{
|
|
dispatch_block_t block = ^{ @autoreleasepool {
|
|
|
|
[self stopPingIntervalTimer];
|
|
|
|
lastReceiveTime = 0;
|
|
awaitingPingResponse = NO;
|
|
|
|
[xmppPing deactivate];
|
|
[super deactivate];
|
|
|
|
}};
|
|
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
block();
|
|
else
|
|
dispatch_sync(moduleQueue, block);
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
|
|
[self stopPingIntervalTimer];
|
|
|
|
[xmppPing removeDelegate:self];
|
|
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark Properties
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
- (NSTimeInterval)pingInterval
|
|
{
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
{
|
|
return pingInterval;
|
|
}
|
|
else
|
|
{
|
|
__block NSTimeInterval result;
|
|
|
|
dispatch_sync(moduleQueue, ^{
|
|
result = pingInterval;
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
|
|
- (void)setPingInterval:(NSTimeInterval)interval
|
|
{
|
|
dispatch_block_t block = ^{
|
|
|
|
if (pingInterval != interval)
|
|
{
|
|
pingInterval = interval;
|
|
|
|
// Update the pingTimer.
|
|
//
|
|
// Depending on new value and current state of the pingTimer,
|
|
// this may mean starting, stoping, or simply updating the timer.
|
|
|
|
if (pingInterval > 0)
|
|
{
|
|
// Remember: Only start the pinger after the xmpp stream is up and authenticated
|
|
if ([xmppStream isAuthenticated])
|
|
[self startPingIntervalTimer];
|
|
}
|
|
else
|
|
{
|
|
[self stopPingIntervalTimer];
|
|
}
|
|
}
|
|
};
|
|
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
block();
|
|
else
|
|
dispatch_async(moduleQueue, block);
|
|
}
|
|
|
|
- (NSTimeInterval)pingTimeout
|
|
{
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
{
|
|
return pingTimeout;
|
|
}
|
|
else
|
|
{
|
|
__block NSTimeInterval result;
|
|
|
|
dispatch_sync(moduleQueue, ^{
|
|
result = pingTimeout;
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
|
|
- (void)setPingTimeout:(NSTimeInterval)timeout
|
|
{
|
|
dispatch_block_t block = ^{
|
|
|
|
if (pingTimeout != timeout)
|
|
{
|
|
pingTimeout = timeout;
|
|
}
|
|
};
|
|
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
block();
|
|
else
|
|
dispatch_async(moduleQueue, block);
|
|
}
|
|
|
|
- (XMPPJID *)targetJID
|
|
{
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
{
|
|
return targetJID;
|
|
}
|
|
else
|
|
{
|
|
__block XMPPJID *result;
|
|
|
|
dispatch_sync(moduleQueue, ^{
|
|
result = targetJID;
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
|
|
- (void)setTargetJID:(XMPPJID *)jid
|
|
{
|
|
dispatch_block_t block = ^{
|
|
|
|
if (![targetJID isEqualToJID:jid])
|
|
{
|
|
targetJID = jid;
|
|
|
|
targetJIDStr = [targetJID full];
|
|
}
|
|
};
|
|
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
block();
|
|
else
|
|
dispatch_async(moduleQueue, block);
|
|
}
|
|
|
|
- (NSTimeInterval)lastReceiveTime
|
|
{
|
|
if (dispatch_get_specific(moduleQueueTag))
|
|
{
|
|
return lastReceiveTime;
|
|
}
|
|
else
|
|
{
|
|
__block NSTimeInterval result;
|
|
|
|
dispatch_sync(moduleQueue, ^{
|
|
result = lastReceiveTime;
|
|
});
|
|
return result;
|
|
}
|
|
}
|
|
|
|
- (BOOL)respondsToQueries
|
|
{
|
|
return xmppPing.respondsToQueries;
|
|
}
|
|
|
|
- (void)setRespondsToQueries:(BOOL)respondsToQueries
|
|
{
|
|
xmppPing.respondsToQueries = respondsToQueries;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark Ping Interval
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
- (void)handlePingIntervalTimerFire
|
|
{
|
|
if (awaitingPingResponse) return;
|
|
|
|
BOOL sendPing = NO;
|
|
|
|
if (lastReceiveTime == 0)
|
|
{
|
|
sendPing = YES;
|
|
}
|
|
else
|
|
{
|
|
NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
|
|
NSTimeInterval elapsed = (now - lastReceiveTime);
|
|
|
|
XMPPLogTrace2(@"%@: %@ - elapsed(%f)", [self class], THIS_METHOD, elapsed);
|
|
|
|
sendPing = ((elapsed < 0) || (elapsed >= pingInterval));
|
|
}
|
|
|
|
if (sendPing)
|
|
{
|
|
awaitingPingResponse = YES;
|
|
|
|
if (targetJID)
|
|
[xmppPing sendPingToJID:targetJID withTimeout:pingTimeout];
|
|
else
|
|
[xmppPing sendPingToServerWithTimeout:pingTimeout];
|
|
|
|
[multicastDelegate xmppAutoPingDidSendPing:self];
|
|
}
|
|
}
|
|
|
|
- (void)updatePingIntervalTimer
|
|
{
|
|
XMPPLogTrace();
|
|
|
|
NSAssert(pingIntervalTimer != NULL, @"Broken logic (1)");
|
|
NSAssert(pingInterval > 0, @"Broken logic (2)");
|
|
|
|
// The timer fires every (pingInterval / 4) seconds.
|
|
// Upon firing it checks when data was last received from the target,
|
|
// and sends a ping if the elapsed time has exceeded the pingInterval.
|
|
// Thus the effective resolution of the timer is based on the configured pingInterval.
|
|
|
|
uint64_t interval = ((pingInterval / 4.0) * NSEC_PER_SEC);
|
|
|
|
// The timer's first fire should occur 'interval' after lastReceiveTime.
|
|
// If there is no lastReceiveTime, then the timer's first fire should occur 'interval' after now.
|
|
|
|
NSTimeInterval diff;
|
|
if (lastReceiveTime == 0)
|
|
diff = 0.0;
|
|
else
|
|
diff = lastReceiveTime - [NSDate timeIntervalSinceReferenceDate];;
|
|
|
|
dispatch_time_t bt = dispatch_time(DISPATCH_TIME_NOW, (diff * NSEC_PER_SEC));
|
|
dispatch_time_t tt = dispatch_time(bt, interval);
|
|
|
|
dispatch_source_set_timer(pingIntervalTimer, tt, interval, 0);
|
|
}
|
|
|
|
- (void)startPingIntervalTimer
|
|
{
|
|
XMPPLogTrace();
|
|
|
|
if (pingInterval <= 0)
|
|
{
|
|
// Pinger is disabled
|
|
return;
|
|
}
|
|
|
|
BOOL newTimer = NO;
|
|
|
|
if (pingIntervalTimer == NULL)
|
|
{
|
|
newTimer = YES;
|
|
pingIntervalTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, moduleQueue);
|
|
|
|
dispatch_source_set_event_handler(pingIntervalTimer, ^{ @autoreleasepool {
|
|
|
|
[self handlePingIntervalTimerFire];
|
|
|
|
}});
|
|
}
|
|
|
|
[self updatePingIntervalTimer];
|
|
|
|
if (newTimer)
|
|
{
|
|
dispatch_resume(pingIntervalTimer);
|
|
}
|
|
}
|
|
|
|
- (void)stopPingIntervalTimer
|
|
{
|
|
XMPPLogTrace();
|
|
|
|
if (pingIntervalTimer)
|
|
{
|
|
#if !OS_OBJECT_USE_OBJC
|
|
dispatch_release(pingIntervalTimer);
|
|
#endif
|
|
pingIntervalTimer = NULL;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark XMPPPing Delegate
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
- (void)xmppPing:(XMPPPing *)sender didReceivePong:(XMPPIQ *)pong withRTT:(NSTimeInterval)rtt
|
|
{
|
|
XMPPLogTrace();
|
|
|
|
awaitingPingResponse = NO;
|
|
[multicastDelegate xmppAutoPingDidReceivePong:self];
|
|
}
|
|
|
|
- (void)xmppPing:(XMPPPing *)sender didNotReceivePong:(NSString *)pingID dueToTimeout:(NSTimeInterval)timeout
|
|
{
|
|
XMPPLogTrace();
|
|
|
|
awaitingPingResponse = NO;
|
|
[multicastDelegate xmppAutoPingDidTimeout:self];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark XMPPStream Delegate
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
|
|
{
|
|
lastReceiveTime = [NSDate timeIntervalSinceReferenceDate];
|
|
awaitingPingResponse = NO;
|
|
|
|
[self startPingIntervalTimer];
|
|
}
|
|
|
|
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
|
|
{
|
|
if (targetJID == nil || [targetJIDStr isEqualToString:[iq fromStr]])
|
|
{
|
|
lastReceiveTime = [NSDate timeIntervalSinceReferenceDate];
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
|
|
{
|
|
if (targetJID == nil || [targetJIDStr isEqualToString:[message fromStr]])
|
|
{
|
|
lastReceiveTime = [NSDate timeIntervalSinceReferenceDate];
|
|
}
|
|
}
|
|
|
|
- (void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
|
|
{
|
|
if (targetJID == nil || [targetJIDStr isEqualToString:[presence fromStr]])
|
|
{
|
|
lastReceiveTime = [NSDate timeIntervalSinceReferenceDate];
|
|
}
|
|
}
|
|
|
|
- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
|
|
{
|
|
[self stopPingIntervalTimer];
|
|
|
|
lastReceiveTime = 0;
|
|
awaitingPingResponse = NO;
|
|
}
|
|
|
|
@end
|