- Update
This commit is contained in:
1 parent
d281d765ea
commit
5b074a5176
1261 files changed
+199158
-7303
No files matched your search
@@ -0,0 +1,102 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPPModule.h"
|
||||
#import "XMPPPing.h"
|
||||
|
||||
#define _XMPP_AUTO_PING_H
|
||||
|
||||
@class XMPPJID;
|
||||
|
||||
|
||||
/**
|
||||
* The XMPPAutoPing module sends pings on a designated interval to the target.
|
||||
* The target may simply be the server, or a specific resource.
|
||||
*
|
||||
* The module only sends pings as needed.
|
||||
* If the xmpp stream is receiving data from the target, there's no need to send a ping.
|
||||
* Only when no data has been received from the target is a ping sent.
|
||||
**/
|
||||
@interface XMPPAutoPing : XMPPModule {
|
||||
@private
|
||||
NSTimeInterval pingInterval;
|
||||
NSTimeInterval pingTimeout;
|
||||
XMPPJID *targetJID;
|
||||
NSString *targetJIDStr;
|
||||
|
||||
NSTimeInterval lastReceiveTime;
|
||||
dispatch_source_t pingIntervalTimer;
|
||||
|
||||
BOOL awaitingPingResponse;
|
||||
XMPPPing *xmppPing;
|
||||
}
|
||||
|
||||
/**
|
||||
* How often to send a ping.
|
||||
*
|
||||
* The internal 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 interval.
|
||||
*
|
||||
* To temporarily disable auto-ping, set the interval to zero.
|
||||
*
|
||||
* The default pingInterval is 60 seconds.
|
||||
**/
|
||||
@property (readwrite) NSTimeInterval pingInterval;
|
||||
|
||||
/**
|
||||
* How long to wait after sending a ping before timing out.
|
||||
*
|
||||
* The timeout is decoupled from the pingInterval to allow for longer pingIntervals,
|
||||
* which avoids flooding the network, and to allow more precise control overall.
|
||||
*
|
||||
* After a ping is sent, if a reply is not received by this timeout,
|
||||
* the delegate method is invoked.
|
||||
*
|
||||
* The default pingTimeout is 10 seconds.
|
||||
**/
|
||||
@property (readwrite) NSTimeInterval pingTimeout;
|
||||
|
||||
/**
|
||||
* The target to send pings to.
|
||||
*
|
||||
* If the targetJID is nil, this implies the target is the xmpp server we're connected to.
|
||||
* In this case, receiving any data means we've received data from the target.
|
||||
*
|
||||
* If the targetJID is non-nil, it must be a full JID (user@domain.tld/rsrc).
|
||||
* In this case, the module will monitor the stream for data from the given JID.
|
||||
*
|
||||
* The default targetJID is nil.
|
||||
**/
|
||||
@property (readwrite, strong) XMPPJID *targetJID;
|
||||
|
||||
/**
|
||||
* Corresponds to the last time data was received from the target.
|
||||
* The NSTimeInterval value comes from [NSDate timeIntervalSinceReferenceDate]
|
||||
**/
|
||||
@property (readonly) NSTimeInterval lastReceiveTime;
|
||||
|
||||
/**
|
||||
* XMPPAutoPing is used to automatically send pings on a regular interval.
|
||||
* Sometimes the target is also sending pings to us as well.
|
||||
* If so, you may optionally set respondsToQueries to YES to allow the module to respond to incoming pings.
|
||||
*
|
||||
* If you create multiple instances of XMPPAutoPing or XMPPPing,
|
||||
* then only one instance should respond to queries.
|
||||
*
|
||||
* The default value is NO.
|
||||
**/
|
||||
@property (readwrite) BOOL respondsToQueries;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@protocol XMPPAutoPingDelegate
|
||||
@optional
|
||||
|
||||
- (void)xmppAutoPingDidSendPing:(XMPPAutoPing *)sender;
|
||||
- (void)xmppAutoPingDidReceivePong:(XMPPAutoPing *)sender;
|
||||
|
||||
- (void)xmppAutoPingDidTimeout:(XMPPAutoPing *)sender;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,416 @@
|
||||
#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
|
||||
@@ -0,0 +1,51 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "XMPP.h"
|
||||
|
||||
#define _XMPP_PING_H
|
||||
|
||||
@class XMPPIDTracker;
|
||||
|
||||
|
||||
@interface XMPPPing : XMPPModule
|
||||
{
|
||||
BOOL respondsToQueries;
|
||||
XMPPIDTracker *pingTracker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the module should respond to incoming ping queries.
|
||||
* It you create multiple instances of this module, only one instance should respond to queries.
|
||||
*
|
||||
* It is recommended you set this (if needed) before you activate the module.
|
||||
* The default value is YES.
|
||||
**/
|
||||
@property (readwrite) BOOL respondsToQueries;
|
||||
|
||||
/**
|
||||
* Send pings to the server or a specific JID.
|
||||
* The disco module may be used to detect if the target supports ping.
|
||||
*
|
||||
* The returned string is the pingID (the elementID of the query that was sent).
|
||||
* In other words:
|
||||
*
|
||||
* SEND: <iq id="<returned_string>" type="get" .../>
|
||||
* RECV: <iq id="<returned_string>" type="result" .../>
|
||||
*
|
||||
* This may be helpful if you are sending multiple simultaneous pings to the same target.
|
||||
**/
|
||||
- (NSString *)sendPingToServer;
|
||||
- (NSString *)sendPingToServerWithTimeout:(NSTimeInterval)timeout;
|
||||
- (NSString *)sendPingToJID:(XMPPJID *)jid;
|
||||
- (NSString *)sendPingToJID:(XMPPJID *)jid withTimeout:(NSTimeInterval)timeout;
|
||||
|
||||
@end
|
||||
|
||||
@protocol XMPPPingDelegate
|
||||
@optional
|
||||
|
||||
- (void)xmppPing:(XMPPPing *)sender didReceivePong:(XMPPIQ *)pong withRTT:(NSTimeInterval)rtt;
|
||||
- (void)xmppPing:(XMPPPing *)sender didNotReceivePong:(NSString *)pingID dueToTimeout:(NSTimeInterval)timeout;
|
||||
|
||||
// Note: If the xmpp stream is disconnected, no delegate methods will be called, and outstanding pings are forgotten.
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,314 @@
|
||||
#import "XMPPPing.h"
|
||||
#import "XMPPIDTracker.h"
|
||||
#import "XMPPFramework.h"
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
||||
#endif
|
||||
|
||||
#define DEFAULT_TIMEOUT 30.0 // seconds
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface XMPPPingInfo : XMPPBasicTrackingInfo
|
||||
{
|
||||
NSDate *timeSent;
|
||||
}
|
||||
|
||||
@property (nonatomic, readonly) NSDate *timeSent;
|
||||
|
||||
- (NSTimeInterval)rtt;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPPing
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithDispatchQueue:NULL];
|
||||
}
|
||||
|
||||
- (id)initWithDispatchQueue:(dispatch_queue_t)queue
|
||||
{
|
||||
if ((self = [super initWithDispatchQueue:queue]))
|
||||
{
|
||||
respondsToQueries = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)activate:(XMPPStream *)aXmppStream
|
||||
{
|
||||
if ([super activate:aXmppStream])
|
||||
{
|
||||
#ifdef _XMPP_CAPABILITIES_H
|
||||
[xmppStream autoAddDelegate:self delegateQueue:moduleQueue toModulesOfClass:[XMPPCapabilities class]];
|
||||
#endif
|
||||
|
||||
pingTracker = [[XMPPIDTracker alloc] initWithDispatchQueue:moduleQueue];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)deactivate
|
||||
{
|
||||
#ifdef _XMPP_CAPABILITIES_H
|
||||
[xmppStream removeAutoDelegate:self delegateQueue:moduleQueue fromModulesOfClass:[XMPPCapabilities class]];
|
||||
#endif
|
||||
|
||||
dispatch_block_t block = ^{ @autoreleasepool {
|
||||
|
||||
[pingTracker removeAllIDs];
|
||||
pingTracker = nil;
|
||||
|
||||
}};
|
||||
|
||||
if (dispatch_get_specific(moduleQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_sync(moduleQueue, block);
|
||||
|
||||
[super deactivate];
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)respondsToQueries
|
||||
{
|
||||
if (dispatch_get_specific(moduleQueueTag))
|
||||
{
|
||||
return respondsToQueries;
|
||||
}
|
||||
else
|
||||
{
|
||||
__block BOOL result;
|
||||
|
||||
dispatch_sync(moduleQueue, ^{
|
||||
result = respondsToQueries;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setRespondsToQueries:(BOOL)flag
|
||||
{
|
||||
dispatch_block_t block = ^{
|
||||
|
||||
if (respondsToQueries != flag)
|
||||
{
|
||||
respondsToQueries = flag;
|
||||
|
||||
#ifdef _XMPP_CAPABILITIES_H
|
||||
@autoreleasepool {
|
||||
// Capabilities may have changed, need to notify others.
|
||||
[xmppStream resendMyPresence];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
if (dispatch_get_specific(moduleQueueTag))
|
||||
block();
|
||||
else
|
||||
dispatch_async(moduleQueue, block);
|
||||
}
|
||||
|
||||
- (NSString *)generatePingIDWithTimeout:(NSTimeInterval)timeout
|
||||
{
|
||||
// This method may be invoked on any thread/queue.
|
||||
|
||||
// Generate unique ID for Ping packet
|
||||
// It's important the ID be unique as the ID is the only thing that distinguishes a pong packet
|
||||
|
||||
NSString *pingID = [xmppStream generateUUID];
|
||||
|
||||
dispatch_async(moduleQueue, ^{ @autoreleasepool {
|
||||
|
||||
XMPPPingInfo *pingInfo = [[XMPPPingInfo alloc] initWithTarget:self
|
||||
selector:@selector(handlePong:withInfo:)
|
||||
timeout:timeout];
|
||||
|
||||
[pingTracker addID:pingID trackingInfo:pingInfo];
|
||||
|
||||
}});
|
||||
|
||||
return pingID;
|
||||
}
|
||||
|
||||
- (NSString *)sendPingToServer
|
||||
{
|
||||
// This is a public method.
|
||||
// It may be invoked on any thread/queue.
|
||||
|
||||
return [self sendPingToServerWithTimeout:DEFAULT_TIMEOUT];
|
||||
}
|
||||
|
||||
- (NSString *)sendPingToServerWithTimeout:(NSTimeInterval)timeout
|
||||
{
|
||||
// This is a public method.
|
||||
// It may be invoked on any thread/queue.
|
||||
|
||||
NSString *pingID = [self generatePingIDWithTimeout:timeout];
|
||||
|
||||
// Send ping packet
|
||||
//
|
||||
// <iq type="get" id="pingID">
|
||||
// <ping xmlns="urn:xmpp:ping"/>
|
||||
// </iq>
|
||||
|
||||
NSXMLElement *ping = [NSXMLElement elementWithName:@"ping" xmlns:@"urn:xmpp:ping"];
|
||||
|
||||
XMPPIQ *iq = [XMPPIQ iqWithType:@"get" to:nil elementID:pingID child:ping];
|
||||
|
||||
[xmppStream sendElement:iq];
|
||||
|
||||
return pingID;
|
||||
}
|
||||
|
||||
- (NSString *)sendPingToJID:(XMPPJID *)jid
|
||||
{
|
||||
// This is a public method.
|
||||
// It may be invoked on any thread/queue.
|
||||
|
||||
return [self sendPingToJID:jid withTimeout:DEFAULT_TIMEOUT];
|
||||
}
|
||||
|
||||
- (NSString *)sendPingToJID:(XMPPJID *)jid withTimeout:(NSTimeInterval)timeout
|
||||
{
|
||||
// This is a public method.
|
||||
// It may be invoked on any thread/queue.
|
||||
|
||||
NSString *pingID = [self generatePingIDWithTimeout:timeout];
|
||||
|
||||
// Send ping element
|
||||
//
|
||||
// <iq to="fullJID" type="get" id="pingID">
|
||||
// <ping xmlns="urn:xmpp:ping"/>
|
||||
// </iq>
|
||||
|
||||
NSXMLElement *ping = [NSXMLElement elementWithName:@"ping" xmlns:@"urn:xmpp:ping"];
|
||||
|
||||
XMPPIQ *iq = [XMPPIQ iqWithType:@"get" to:jid elementID:pingID child:ping];
|
||||
|
||||
[xmppStream sendElement:iq];
|
||||
|
||||
return pingID;
|
||||
}
|
||||
|
||||
- (void)handlePong:(XMPPIQ *)pongIQ withInfo:(XMPPPingInfo *)pingInfo
|
||||
{
|
||||
if (pongIQ)
|
||||
{
|
||||
[multicastDelegate xmppPing:self didReceivePong:pongIQ withRTT:[pingInfo rtt]];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Timeout
|
||||
|
||||
[multicastDelegate xmppPing:self didNotReceivePong:[pingInfo elementID] dueToTimeout:[pingInfo timeout]];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq
|
||||
{
|
||||
// This method is invoked on the moduleQueue.
|
||||
|
||||
NSString *type = [iq type];
|
||||
|
||||
if ([type isEqualToString:@"result"] || [type isEqualToString:@"error"])
|
||||
{
|
||||
// Example:
|
||||
//
|
||||
// <iq from="deusty.com" to="robbiehanson@deusty.com/work" id="abc123" type="result"/>
|
||||
|
||||
// If this is a response to a ping that we've sent,
|
||||
// then the pingTracker will invoke our handlePong:withInfo: method and return YES.
|
||||
|
||||
return [pingTracker invokeForID:[iq elementID] withObject:iq];
|
||||
}
|
||||
else if (respondsToQueries && [type isEqualToString:@"get"])
|
||||
{
|
||||
// Example:
|
||||
//
|
||||
// <iq from="deusty.com" to="robbiehanson@deusty.com/work" id="zhq325" type="get">
|
||||
// <ping xmlns="urn:xmpp:ping"/>
|
||||
// </iq>
|
||||
|
||||
NSXMLElement *ping = [iq elementForName:@"ping" xmlns:@"urn:xmpp:ping"];
|
||||
if (ping)
|
||||
{
|
||||
XMPPIQ *pong = [XMPPIQ iqWithType:@"result" to:[iq from] elementID:[iq elementID]];
|
||||
|
||||
[sender sendElement:pong];
|
||||
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
|
||||
{
|
||||
[pingTracker removeAllIDs];
|
||||
}
|
||||
|
||||
#ifdef _XMPP_CAPABILITIES_H
|
||||
/**
|
||||
* If an XMPPCapabilites instance is used we want to advertise our support for ping.
|
||||
**/
|
||||
- (void)xmppCapabilities:(XMPPCapabilities *)sender collectingMyCapabilities:(NSXMLElement *)query
|
||||
{
|
||||
// This method is invoked on the moduleQueue.
|
||||
|
||||
if (respondsToQueries)
|
||||
{
|
||||
// <query xmlns="http://jabber.org/protocol/disco#info">
|
||||
// ...
|
||||
// <feature var="urn:xmpp:ping"/>
|
||||
// ...
|
||||
// </query>
|
||||
|
||||
NSXMLElement *feature = [NSXMLElement elementWithName:@"feature"];
|
||||
[feature addAttributeWithName:@"var" stringValue:@"urn:xmpp:ping"];
|
||||
|
||||
[query addChild:feature];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@implementation XMPPPingInfo
|
||||
|
||||
@synthesize timeSent;
|
||||
|
||||
- (id)initWithTarget:(id)aTarget selector:(SEL)aSelector timeout:(NSTimeInterval)aTimeout
|
||||
{
|
||||
if ((self = [super initWithTarget:aTarget selector:aSelector timeout:aTimeout]))
|
||||
{
|
||||
timeSent = [[NSDate alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSTimeInterval)rtt
|
||||
{
|
||||
return [timeSent timeIntervalSinceNow] * -1.0;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
Reference in new issue
Block a user