Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stream delegate context tracing #996

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Core/XMPP.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#import "XMPPMessage.h"
#import "XMPPPresence.h"
#import "XMPPModule.h"
#import "XMPPElementEvent.h"

//
// Authentication
Expand Down
80 changes: 80 additions & 0 deletions Core/XMPPElementEvent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@class XMPPJID;

/**
* 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 from the @c currentElementEvent property 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) BOOL isProcessingCompleted;

// 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 isProcessingCompleted
*/
- (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 isProcessingCompleted
*/
- (void)endDelayedProcessingWithToken:(id)delayedProcessingToken;

@end

NS_ASSUME_NONNULL_END
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
42 changes: 40 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 andGetReceipt:(XMPPElementReceipt * _Nullable * _Nullable)receiptPtr registeringEventWithID:(NSString *)eventID;

/**
* 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
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
* 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 property is nil if accessed outside of those callbacks.
* For more details, please refer to @c XMPPElementEvent documentation.
*/
@property (nonatomic, readonly, nullable) XMPPElementEvent *currentElementEvent;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark Utilities
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -987,6 +1003,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 accessing @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 +1051,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 accessing @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 +1062,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 accessing @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 +1171,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 accessing @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