Skip to content

Commit

Permalink
Introduce traceable stanza send/receive events in XMPPStream
Browse files Browse the repository at this point in the history
  • Loading branch information
pwetrifork committed Nov 10, 2017
1 parent ba08cf5 commit 141d3e3
Show file tree
Hide file tree
Showing 4 changed files with 381 additions and 136 deletions.
1 change: 1 addition & 0 deletions Core/XMPPInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ extern NSString *const XMPPStreamDidChangeMyJIDNotification;
* This is an advanced technique, but makes for some interesting possibilities.
**/
- (void)injectElement:(NSXMLElement *)element;
- (void)injectElement:(NSXMLElement *)element registeringEventWithID:(NSString *)eventID;

/**
* The XMPP standard only supports <iq>, <message> and <presence> stanzas (excluding session setup stuff).
Expand Down
115 changes: 113 additions & 2 deletions Core/XMPPStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ NS_ASSUME_NONNULL_BEGIN
@class XMPPModule;
@class XMPPElement;
@class XMPPElementReceipt;
@class XMPPElementEvent;
@protocol XMPPStreamDelegate;

#if TARGET_OS_IPHONE
Expand Down Expand Up @@ -615,8 +616,9 @@ extern const NSTimeInterval XMPPStreamTimeoutNone;
- (void)sendElement:(NSXMLElement *)element;

/**
* Just like the sendElement: method above,
* but allows you to receive a receipt that can later be used to verify the element has been sent.
* Just like the sendElement: method above, but allows you to:
* - Receive a receipt that can later be used to verify the element has been sent.
* - Provide an event ID that can later be used to trace the element in delegate callbacks.
*
* If you later want to check to see if the element has been sent:
*
Expand Down Expand Up @@ -644,6 +646,7 @@ extern const NSTimeInterval XMPPStreamTimeoutNone;
* Even if you close the xmpp stream after this point, the OS will still do everything it can to send the data.
**/
- (void)sendElement:(NSXMLElement *)element andGetReceipt:(XMPPElementReceipt * _Nullable * _Nullable)receiptPtr;
- (void)sendElement:(NSXMLElement *)element registeringEventWithID:(NSString *)eventID andGetReceipt:(XMPPElementReceipt * _Nullable * _Nullable)receiptPtr;

/**
* Fetches and resends the myPresence element (if available) in a single atomic operation.
Expand Down Expand Up @@ -733,6 +736,19 @@ extern const NSTimeInterval XMPPStreamTimeoutNone;
**/
- (void)enumerateModulesOfClass:(Class)aClass withBlock:(void (^)(XMPPModule *module, NSUInteger idx, BOOL *stop))block;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Element Event Context
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
* Returns the stream metadata corresponding to the currently processed XMPP stanza.
*
* Event information is only available in the context of @c didSendXXX/didFailToSendXXX/didReceiveXXX delegate callbacks.
* This method returns nil if called outside of those callbacks.
* For more details, please refer to @c XMPPElementEvent documentation.
*/
- (nullable XMPPElementEvent *)currentElementEvent;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Utilities
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -785,6 +801,79 @@ extern const NSTimeInterval XMPPStreamTimeoutNone;

@end

/**
* A handle that allows identifying elements sent or received in the stream across different delegates
* and tracking their processing progress.
*
* While the core XMPP specification does not require stanzas to be uniquely identifiable, you may still want to
* identify them internally across different modules or trace the sent ones to the respective send result delegate callbacks.
*
* An instance of this class is provided in the context of execution of any of the @c didSendXXX/didFailToSendXXX/didReceiveXXX
* stream delegate methods. It is retrieved by calling the @c currentElementEvent method on the calling stream.
* The delegates can then use it to:
* - identify the corresponding XMPP stanzas.
* - be notified of asynchronous processing completion for a given XMPP stanza.
*
* Using @c XMPPElementEvent handles is a more robust approach than relying on pointer equality of @c XMPPElement instances.
*/
@interface XMPPElementEvent : NSObject

/// The universally unique identifier of the event that provides the internal identity of the corresponding XMPP stanza.
@property (nonatomic, copy, readonly) NSString *uniqueID;

/// The value of the stream's @c myJID property at the time when the event occured.
@property (nonatomic, strong, readonly, nullable) XMPPJID *myJID;

/// The local device time when the event occured.
@property (nonatomic, strong, readonly) NSDate *timestamp;

/**
* A flag indicating whether all delegates are done processing the given event.
*
* Supports Key-Value Observing. Change notifications are emitted on the stream queue.
*
* @see beginDelayedProcessing
* @see endDelayedProcessingWithToken
*/
@property (nonatomic, assign, readonly, getter=isProcessingCompleted) BOOL processingCompleted;

// Instances are created by the stream only.
- (instancetype)init NS_UNAVAILABLE;

/**
* Marks the event as being asynchronously processed by a delegate and returns a completion token.
*
* Event processing is completed after every @c beginDelayedProcessing call has been followed
* by @c endDelayedProcessingWithToken: with a matching completion token.
*
* Unpaired invocations may lead to undefined behavior or stalled events.
*
* Events that are not marked for asynchronous processing by any of the delegates complete immediately
* after control returns from all callbacks.
*
* @see endDelayedProcessingWithToken:
* @see processingCompleted
*/
- (id)beginDelayedProcessing;

/**
* Marks an end of the previously initiated asynchronous delegate processing.
*
* Event processing is completed after every @c beginDelayedProcessing call has been followed
* by @c endDelayedProcessingWithToken: with a matching completion token.
*
* Unpaired invocations may lead to undefined behavior or stalled events.
*
* Events that are not marked for asynchronous processing by any of the delegates complete immediately
* after control returns from all callbacks.
*
* @see beginDelayedProcessing
* @see processingCompleted
*/
- (void)endDelayedProcessingWithToken:(id)delayedProcessingToken;

@end

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -987,6 +1076,9 @@ extern const NSTimeInterval XMPPStreamTimeoutNone;
* As documented in NSXML / KissXML, elements are read-access thread-safe, but write-access thread-unsafe.
* If you have need to modify an element for any reason,
* you should copy the element first, and then modify and use the copy.
*
* Delegates can obtain event metadata associated with the respective element by calling @c currentElementEvent on @c sender
* from within these callbacks. For more details, please refer to @c XMPPElementEvent documentation.
**/
- (BOOL)xmppStream:(XMPPStream *)sender didReceiveIQ:(XMPPIQ *)iq;
- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message;
Expand Down Expand Up @@ -1032,6 +1124,9 @@ extern const NSTimeInterval XMPPStreamTimeoutNone;
* These methods are called after their respective XML elements are sent over the stream.
* These methods may be used to listen for certain events (such as an unavailable presence having been sent),
* or for general logging purposes. (E.g. a central history logging mechanism).
*
* Delegates can obtain event metadata associated with the respective element by calling @c currentElementEvent on @c sender
* from within these callbacks. For more details, please refer to @c XMPPElementEvent documentation.
**/
- (void)xmppStream:(XMPPStream *)sender didSendIQ:(XMPPIQ *)iq;
- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message;
Expand All @@ -1040,11 +1135,24 @@ extern const NSTimeInterval XMPPStreamTimeoutNone;
/**
* These methods are called after failing to send the respective XML elements over the stream.
* This occurs when the stream gets disconnected before the element can get sent out.
*
* Delegates can obtain event metadata associated with the respective element by calling @c currentElementEvent on @c sender
* from within these callbacks.
* Note that if these methods are called, the event context is incomplete, e.g. the stream might have not been connected
* and the actual myJID value is not determined. For more details, please refer to @c XMPPElementEvent documentation.
**/
- (void)xmppStream:(XMPPStream *)sender didFailToSendIQ:(XMPPIQ *)iq error:(NSError *)error;
- (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error;
- (void)xmppStream:(XMPPStream *)sender didFailToSendPresence:(XMPPPresence *)presence error:(NSError *)error;

/**
* This method is called after all delegates are done processing the given event.
*
* For more details, please refer to @c XMPPElementEvent documentation.
*/
- (void)xmppStream:(XMPPStream *)sender didFinishProcessingElementEvent:(XMPPElementEvent *)event
NS_SWIFT_NAME(xmppStream(_:didFinishProcessing:));

/**
* This method is called if the XMPP Stream's jid changes.
**/
Expand Down Expand Up @@ -1136,6 +1244,9 @@ extern const NSTimeInterval XMPPStreamTimeoutNone;
* If you're using custom elements, you must register the custom element name(s).
* Otherwise the xmppStream will treat non-XMPP elements as errors (xmppStream:didReceiveError:).
*
* Delegates can obtain event metadata associated with the respective element by calling @c currentElementEvent on @c sender
* from within these callbacks. For more details, please refer to @c XMPPElementEvent documentation.
*
* @see registerCustomElementNames (in XMPPInternal.h)
**/
- (void)xmppStream:(XMPPStream *)sender didSendCustomElement:(NSXMLElement *)element;
Expand Down
Loading

0 comments on commit 141d3e3

Please sign in to comment.