618 lines
27 KiB
Objective-C
618 lines
27 KiB
Objective-C
#import <Foundation/Foundation.h>
|
||
#import "XMPP.h"
|
||
|
||
#define _XMPP_STREAM_MANAGEMENT_H
|
||
|
||
@protocol XMPPStreamManagementStorage;
|
||
|
||
|
||
@interface XMPPStreamManagement : XMPPModule <XMPPCustomBinding>
|
||
|
||
/**
|
||
* The XMPPStreamManagement extension implements XEP-0198:
|
||
* http://xmpp.org/extensions/xep-0198.html
|
||
*
|
||
* @param storage
|
||
* You must configure the extension with a storage module.
|
||
* A persistent storage layer is recommended for distribution.
|
||
* For testing, or if you're not planning on using stream resumption, then the memory storage solution will work.
|
||
*
|
||
* @param queue
|
||
* The standard dispatch_queue option, with which to run the extension on.
|
||
**/
|
||
- (id)initWithStorage:(id <XMPPStreamManagementStorage>)storage;
|
||
- (id)initWithStorage:(id <XMPPStreamManagementStorage>)storage dispatchQueue:(dispatch_queue_t)queue;
|
||
|
||
@property (nonatomic, strong, readonly) id <XMPPStreamManagementStorage> storage;
|
||
|
||
#pragma mark Enable
|
||
|
||
/**
|
||
* This method sends the <enable> stanza to the server to request enabling stream management.
|
||
*
|
||
* XEP-0198 specifies that the <enable> stanza should only be sent by clients after authentication,
|
||
* and after binding has occurred.
|
||
*
|
||
* The servers response is reported via the delegate methods:
|
||
* @see xmppStreamManagement:wasEnabled:
|
||
* @see xmppStreamManagement:wasNotEnabled:
|
||
*
|
||
* @param supportsResumption
|
||
* Whether the client should request resumptions support.
|
||
* If YES, the resume attribute will be included. E.g. <enable resume='true'/>
|
||
*
|
||
* @param maxTimeout
|
||
* Allows you to specify the client's preferred maximum resumption time.
|
||
* This is optional, and will only be sent if you provide a positive value (maxTimeout > 0.0).
|
||
* Note that XEP-0198 only supports sending this value in seconds.
|
||
* So it the provided maxTimeout includes millisecond precision, this will be ignored via truncation
|
||
* (rounding down to nearest whole seconds value).
|
||
*
|
||
* @see supportsStreamManagement
|
||
**/
|
||
- (void)enableStreamManagementWithResumption:(BOOL)supportsResumption maxTimeout:(NSTimeInterval)maxTimeout;
|
||
|
||
#pragma mark Resume
|
||
|
||
/**
|
||
* If set to YES, then the extension will automatically attempt to resume any sessions that appear resumable.
|
||
*
|
||
* That is, if the canResumeStream method would return YES, then the module will automatically plug into the xmppStream,
|
||
* and attempts to resume the session. If the attempt fails, the xmppStream will automatically fall back to
|
||
* the standard binding process.
|
||
*
|
||
* Remember: If the extension does not believe that resumption is possible, then it won't attempt to resume.
|
||
* That is, if it doesn't have data in storage that matches the current connection, or the data is expired,
|
||
* then it allows the xmppStream to perform standard binding immediately, without attempting to resume.
|
||
*
|
||
* If you wish to handle stream resumption manually, then you can simply implement xmppStreamWillBind:,
|
||
* and return this extension instance according to your own conditions.
|
||
*
|
||
* In order to determine if a stream was resumed, you should invoke didResumeWithAckedStanzaIds:serverResponse:
|
||
* from within the xmppStreamDidAuthenticate: callback.
|
||
*
|
||
* The default value is NO.
|
||
**/
|
||
@property (atomic, readwrite) BOOL autoResume;
|
||
|
||
/**
|
||
* This method is meant to be called by other extensions when they receive an xmppStreamDidAuthenticate callback.
|
||
*
|
||
* Returns YES if the stream was resumed during the authentication process.
|
||
* Returns NO otherwise (if resume wasn't available, or it failed).
|
||
*
|
||
* Other extensions may wish to skip certain setup processes that aren't
|
||
* needed if the stream was resumed (since the previous session state has been restored server-side).
|
||
**/
|
||
@property (atomic, readonly) BOOL didResume;
|
||
|
||
/**
|
||
* This method is meant to be called when you receive an xmppStreamDidAuthenticate callback.
|
||
*
|
||
* It is used instead of a standard delegate method in order to provide a cleaner API.
|
||
* By using this method, one can put all the logic for handling authentication in a single place.
|
||
* But more importantly, it solves several subtle timing and threading issues.
|
||
*
|
||
* > A delegate method could have hit either before or after xmppStreamDidAuthenticate, depending on thread scheduling.
|
||
* > We could have queued it up, and forced it to hit after.
|
||
* > But your code would likely still have needed to add a check within xmppStreamDidAuthenticate...
|
||
*
|
||
* @param stanzaIdsPtr (optional)
|
||
* Just like the stanzaIdsPtr provided in xmppStreamManagement:didReceiveAckForStanzaIds:.
|
||
* This comes from the h value provided within the <resumed h='X'/> stanza sent by the server.
|
||
*
|
||
* @param responsePtr (optional)
|
||
* Returns the response we got from the server. Either <resumed/> or <failed/>.
|
||
* This will be nil if resume wasn't tried.
|
||
*
|
||
* @return
|
||
* YES if the stream was resumed.
|
||
* NO otherwise.
|
||
**/
|
||
- (BOOL)didResumeWithAckedStanzaIds:(NSArray **)stanzaIdsPtr
|
||
serverResponse:(NSXMLElement **)responsePtr;
|
||
|
||
/**
|
||
* Returns YES if the stream can be resumed.
|
||
*
|
||
* This would be the case if there's an available resumptionId for the authenticated xmppStream,
|
||
* and the timeout from the last stream has not been exceeded.
|
||
**/
|
||
- (BOOL)canResumeStream;
|
||
|
||
|
||
#pragma mark Requesting Acks
|
||
|
||
/**
|
||
* Sends a request <r/> element, requesting the server reply with an ack <a h='lastHandled'/>.
|
||
*
|
||
* You can also configure the extension to automatically sends requests.
|
||
* @see automaticallyRequestAcksAfterStanzaCount:orTimeout:
|
||
*
|
||
* When the server replies with an ack, the delegate method will be invoked.
|
||
* @see xmppStreamManagement:didReceiveAckForStanzaIds:
|
||
**/
|
||
- (void)requestAck;
|
||
|
||
/**
|
||
* The module can be configured to automatically request acks (send <r/>) based on your criteria.
|
||
* The algorithm to do this takes into account:
|
||
*
|
||
* - The number of stanzas that have been sent since the last request was sent.
|
||
* - The amount of time that has elapsed since the first stanza (after the last request) was sent.
|
||
*
|
||
* So, for example, if you set the stanzaCount to 5, and the timeout to 2.0 seconds then:
|
||
* - Sending 5 stanzas back-to-back will automatically trigger an outgoing request
|
||
* - Sending 1 stanza will automatically trigger an outgoing request to be sent 2.0 seconds later,
|
||
* which will get preempted if 4 more stanzas are sent before the 2.0 second timer expires.
|
||
*
|
||
* In other words, whichever event takes place FIRST will trigger the request to be sent.
|
||
*
|
||
* You can disable either trigger by setting its value to zero.
|
||
* So, for example, if you only want to use a timeout of 5 seconds,
|
||
* then you could set the stanzaCount to zero and the timeout to 5 seconds.
|
||
*
|
||
* @param stanzaCount
|
||
* The stanzaCount to use for the auto request algorithm.
|
||
* If stanzaCount is zero, then the number of stanzas will be ignored in the algorithm.
|
||
*
|
||
* @param timeout
|
||
* The timeout to use for the auto request algorithm.
|
||
* If the timeout is zero (or negative), then the timer will be ignored in the algorithm.
|
||
*
|
||
* The default stanzaCount is 0 (disabled).
|
||
* The default timeout is 0.0 seconds (disabled).
|
||
**/
|
||
- (void)automaticallyRequestAcksAfterStanzaCount:(NSUInteger)stanzaCount orTimeout:(NSTimeInterval)timeout;
|
||
|
||
/**
|
||
* Returns the current auto-request configuration.
|
||
*
|
||
* @see automaticallyRequestAcksAfterStanzaCount:orTimeout:
|
||
**/
|
||
- (void)getAutomaticallyRequestAcksAfterStanzaCount:(NSUInteger *)stanzaCountPtr orTimeout:(NSTimeInterval *)timeoutPtr;
|
||
|
||
|
||
#pragma mark Sending Acks
|
||
|
||
/**
|
||
* Sends an unrequested ack <a h='lastHandled'/> element, acking the server's recently received (and handled) elements.
|
||
*
|
||
* You can also configure the extension to automatically sends acks.
|
||
* @see automaticallySendAcksAfterStanzaCount:orTimeout:
|
||
*
|
||
* Keep in mind that the extension will automatically send an ack if it receives an explicit request.
|
||
**/
|
||
- (void)sendAck;
|
||
|
||
/**
|
||
* The module can be configured to automatically send unrequested acks.
|
||
* That is, rather than waiting to receive explicit requests <r/> from the server,
|
||
* the client automatically sends them based on configurable criteria.
|
||
*
|
||
* The algorithm to do this takes into account:
|
||
*
|
||
* - The number of stanzas that have been received since the last ack was sent.
|
||
* - The amount of time that has elapsed since the first stanza (after the last ack) was received.
|
||
*
|
||
* In other words, whichever event takes place FIRST will trigger the request to be sent.
|
||
* You can disable either trigger by setting its value to zero.
|
||
*
|
||
* As would be expected, if you manually send an unrequested ack (via the sendAck method),
|
||
* or if an ack is sent out in response to a received request </r> from the server,
|
||
* then the stanzaCount & timeout are reset.
|
||
*
|
||
* @param stanzaCount
|
||
* The stanzaCount to use for the auto ack algorithm.
|
||
* If stanzaCount is zero, then the number of stanzas will be ignored in the algorithm.
|
||
*
|
||
* @param timeout
|
||
* The timeout to sue fo the auto ack algorithm.
|
||
* If the timeout is zero (or negative), then the timer will be ignored in the algorithm.
|
||
*
|
||
* The default stanzaCount is 0 (disabled).
|
||
* The default timeout is 0.0 seconds (disabled).
|
||
**/
|
||
- (void)automaticallySendAcksAfterStanzaCount:(NSUInteger)stanzaCount orTimeout:(NSTimeInterval)timeout;
|
||
|
||
/**
|
||
* Returns the current "auto-send unrequested acks" configuration.
|
||
*
|
||
* @see automaticallySendAcksAfterStanzaCount:orTimeout:
|
||
**/
|
||
- (void)getAutomaticallySendAcksAfterStanzaCount:(NSUInteger *)stanzaCountPtr orTimeout:(NSTimeInterval *)timeoutPtr;
|
||
|
||
/**
|
||
* If an explicit request <r/> is received from the server, should we delay sending the ack <a/> ?
|
||
* From XEP-0198 :
|
||
*
|
||
* > When an <r/> element ("request") is received, the recipient MUST acknowledge it by sending an <a/> element
|
||
* > to the sender containing a value of 'h' that is equal to the number of stanzas handled by the recipient of
|
||
* > the <r/> element. The response SHOULD be sent as soon as possible after receiving the <r/> element,
|
||
* > and MUST NOT be withheld for any condition other than a timeout. For example, a client with a slow connection
|
||
* > might want to collect many stanzas over a period of time before acking, and a server might want to throttle
|
||
* > incoming stanzas.
|
||
*
|
||
* Thus the XEP recommends that you do not use a delay.
|
||
* However, it acknowledges that there may be certain situations in which a delay could prove helpful.
|
||
*
|
||
* The default value is 0.0 (as recommended by XEP-0198)
|
||
**/
|
||
@property (atomic, assign, readwrite) NSTimeInterval ackResponseDelay;
|
||
|
||
/**
|
||
* It's critically important to understand what an ACK means.
|
||
*
|
||
* Every ACK contains an 'h' attribute, which stands for "handled".
|
||
* To paraphrase XEP-0198 (in client-side terminology):
|
||
*
|
||
* Acknowledging a previously received element indicates that the stanza has been "handled" by the client.
|
||
* By "handled" we mean that the client has successfully processed the stanza
|
||
* (including possibly saving the item to the database if needed);
|
||
* Until a stanza has been affirmed as handled by the client, that stanza is the responsibility of the server
|
||
* (e.g., to resend it or generate an error if it is never affirmed as handled by the client).
|
||
*
|
||
* This means that if your processing of certain elements includes saving them to a database,
|
||
* then you should not mark those elements as handled until after your database has confirmed the data is on disk.
|
||
*
|
||
* You should note that this is a critical component of any networking app that claims to have "reliable messaging".
|
||
*
|
||
* By default, all elements will be marked as handled as soon as they arrive.
|
||
* You'll want to override the default behavior for important elements that require proper handling by your app.
|
||
* For example, messages that need to be saved to the database.
|
||
* Here's how to do so:
|
||
*
|
||
* - Implement the delegate method xmppStreamManagement:getIsHandled:stanzaId:forReceivedElement:
|
||
*
|
||
* This method is invoked for all received elements.
|
||
* You can inspect the element, and if it is important and requires special handling by the app,
|
||
* then flag the element as NOT handled (overriding the default).
|
||
* Also assign the element a "stanzaId". This can be anything you want, such as the elementID,
|
||
* or maybe something more app-specific (e.g. something you already use that's associated with the message).
|
||
*
|
||
* - Handle the important element however you need to
|
||
*
|
||
* If you're saving something to the database,
|
||
* then wait until after the database commit has completed successfully.
|
||
*
|
||
* - Notify the module that the element has been handled via the method markHandledStanzaId:
|
||
*
|
||
* You must pass the stanzaId that you returned from the delegate method.
|
||
*
|
||
*
|
||
* @see xmppStreamManagement:getIsHandled:stanzaId:forReceivedElement:
|
||
**/
|
||
- (void)markHandledStanzaId:(id)stanzaId;
|
||
|
||
@end
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
#pragma mark -
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
@protocol XMPPStreamManagementDelegate
|
||
@optional
|
||
|
||
/**
|
||
* Notifies delegates of the server's response from sending the <enable> stanza.
|
||
**/
|
||
- (void)xmppStreamManagement:(XMPPStreamManagement *)sender wasEnabled:(NSXMLElement *)enabled;
|
||
- (void)xmppStreamManagement:(XMPPStreamManagement *)sender wasNotEnabled:(NSXMLElement *)failed;
|
||
|
||
/**
|
||
* Notifies delegates that a request <r/> for an ack from the server was sent.
|
||
**/
|
||
- (void)xmppStreamManagementDidRequestAck:(XMPPStreamManagement *)sender;
|
||
|
||
/**
|
||
* Invoked when an ack is received from the server, and new stanzas have been acked.
|
||
*
|
||
* @param stanzaIds
|
||
* Includes all "stanzaIds" of sent elements that were just acked.
|
||
*
|
||
* What is a "stanzaId" ?
|
||
*
|
||
* A stanzaId is a unique identifier that ** YOU can provide ** in order to track an element.
|
||
* It could simply be the elementId of the sent element. Or,
|
||
* it could be something custom that you provide in order to properly lookup a message in your data store.
|
||
*
|
||
* For more information, see the delegate method xmppStreamManagement:stanzaIdForSentElement:
|
||
**/
|
||
- (void)xmppStreamManagement:(XMPPStreamManagement *)sender didReceiveAckForStanzaIds:(NSArray *)stanzaIds;
|
||
|
||
/**
|
||
* XEP-0198 reports the following regarding duplicate stanzas:
|
||
*
|
||
* Because unacknowledged stanzas might have been received by the other party,
|
||
* resending them might result in duplicates; there is no way to prevent such a
|
||
* result in this protocol, although use of the XMPP 'id' attribute on all stanzas
|
||
* can at least assist the intended recipients in weeding out duplicate stanzas.
|
||
*
|
||
* In other words, there are edge cases in which you might receive duplicates.
|
||
* And the proper way to fix this is to use some kind of identifier in order to detect duplicates.
|
||
*
|
||
* What kind of identifier to use is up to you. (It's app specific.)
|
||
* The XEP notes that you might use the 'id' attribute for this purpose. And this is certainly the most common case.
|
||
* However, you may have an alternative scheme that works better for your purposes.
|
||
* In which case you can use this delegate method to opt-in.
|
||
*
|
||
* For example:
|
||
* You store all your messages in YapDatabase, which is a collection/key/value storage system.
|
||
* Perhaps the collection is the conversationId, and the key is a messageId.
|
||
* Therefore, to efficiently lookup a message in your datastore you'd prefer a collection/key tuple.
|
||
*
|
||
* To achieve this, you would implement this method, and return a YapCollectionKey object for message elements.
|
||
* This way, when the xmppStreamManagement:didReceiveAckForStanzaIds: method is invoked,
|
||
* you'll get a list that contains your collection/key tuple objects. And then you can quickly and efficiently
|
||
* fetch and update your message objects.
|
||
*
|
||
* If there are no delegates that implement this method,
|
||
* or all delegates return nil, then the stanza's elementId is used as the stanzaId.
|
||
*
|
||
* If the stanza isn't assigned a stanzaId (via a delegate method),
|
||
* and it doesn't have an elementId, then it isn't reported in the acked stanzaIds array.
|
||
**/
|
||
- (id)xmppStreamManagement:(XMPPStreamManagement *)sender stanzaIdForSentElement:(XMPPElement *)element;
|
||
|
||
/**
|
||
* It's critically important to understand what an ACK means.
|
||
*
|
||
* Every ACK contains an 'h' attribute, which stands for "handled".
|
||
* To paraphrase XEP-0198 (in client-side terminology):
|
||
*
|
||
* Acknowledging a previously received element indicates that the stanza has been "handled" by the client.
|
||
* By "handled" we mean that the client has successfully processed the stanza
|
||
* (including possibly saving the item to the database if needed);
|
||
* Until a stanza has been affirmed as handled by the client, that stanza is the responsibility of the server
|
||
* (e.g., to resend it or generate an error if it is never affirmed as handled by the client).
|
||
*
|
||
* This means that if your processing of certain elements includes saving them to a database,
|
||
* then you should not mark those elements as handled until after your database has confirmed the data is on disk.
|
||
*
|
||
* You should note that this is a critical component of any networking app that claims to have "reliable messaging".
|
||
*
|
||
* By default, all elements will be marked as handled as soon as they arrive.
|
||
* You'll want to override the default behavior for important elements that require proper handling by your app.
|
||
* For example, messages that need to be saved to the database.
|
||
* Here's how to do so:
|
||
*
|
||
* - Implement the delegate method xmppStreamManagement:getIsHandled:stanzaId:forReceivedElement:
|
||
*
|
||
* This method is invoked for all received elements.
|
||
* You can inspect the element, and if it is important and requires special handling by the app,
|
||
* then flag the element as NOT handled (overriding the default).
|
||
* Also assign the element a "stanzaId". This can be anything you want, such as the elementID,
|
||
* or maybe something more app-specific (e.g. something you already use that's associated with the message).
|
||
*
|
||
* - Handle the important element however you need to
|
||
*
|
||
* If you're saving something to the database,
|
||
* then wait until after the database commit has completed successfully.
|
||
*
|
||
* - Notify the module that the element has been handled via the method markHandledStanzaId:
|
||
*
|
||
* You must pass the stanzaId that you returned from this delegate method.
|
||
*
|
||
*
|
||
* @see markHandledStanzaId:
|
||
**/
|
||
- (void)xmppStreamManagement:(XMPPStreamManagement *)sender
|
||
getIsHandled:(BOOL *)isHandledPtr
|
||
stanzaId:(id *)stanzaIdPtr
|
||
forReceivedElement:(XMPPElement *)element;
|
||
|
||
@end
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
#pragma mark -
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
@protocol XMPPStreamManagementStorage <NSObject>
|
||
@required
|
||
|
||
//
|
||
//
|
||
// -- PRIVATE METHODS --
|
||
//
|
||
// These methods are designed to be used ONLY by the XMPPStreamManagement class.
|
||
//
|
||
//
|
||
|
||
/**
|
||
* Configures the storage class, passing it's parent and the parent's dispatch queue.
|
||
*
|
||
* This method is called by the init methods of the XMPPStreamManagement class.
|
||
* This method is designed to inform the storage class of it's parent
|
||
* and of the dispatch queue the parent will be operating on.
|
||
*
|
||
* A storage class may choose to operate on the same queue as it's parent,
|
||
* as the majority of the time it will be getting called by the parent.
|
||
* If both are operating on the same queue, the combination may run faster.
|
||
*
|
||
* Some storage classes support multiple xmppStreams,
|
||
* and may choose to operate on their own internal queue.
|
||
*
|
||
* This method should return YES if it was configured properly.
|
||
* It should return NO only if configuration failed.
|
||
* For example, a storage class designed to be used only with a single xmppStream is being added to a second stream.
|
||
**/
|
||
- (BOOL)configureWithParent:(XMPPStreamManagement *)parent queue:(dispatch_queue_t)queue;
|
||
|
||
/**
|
||
* Invoked after we receive <enabled/> from the server.
|
||
*
|
||
* @param resumptionId
|
||
* The ID required to resume the session, given to us by the server.
|
||
*
|
||
* @param timeout
|
||
* The timeout in seconds.
|
||
* After a disconnect, the server will maintain our state for this long.
|
||
* If we attempt to resume the session after this timeout it likely won't work.
|
||
*
|
||
* @param lastDisconnect
|
||
* Used to reset the lastDisconnect value.
|
||
* This value is often updated during the session, to ensure it closely resemble the date the server will use.
|
||
* That is, if the client application is killed (or crashes) we want a relatively accurate lastDisconnect date.
|
||
*
|
||
* @param stream
|
||
* The associated xmppStream (standard parameter for storage classes)
|
||
*
|
||
* This method should also nil out the following values (if needed) associated with the account:
|
||
* - lastHandledByClient
|
||
* - lastHandledByServer
|
||
* - pendingOutgoingStanzas
|
||
**/
|
||
- (void)setResumptionId:(NSString *)resumptionId
|
||
timeout:(uint32_t)timeout
|
||
lastDisconnect:(NSDate *)date
|
||
forStream:(XMPPStream *)stream;
|
||
|
||
/**
|
||
* This method is invoked ** often ** during stream operation.
|
||
* It is not invoked when the xmppStream is disconnected.
|
||
*
|
||
* Important: See the note below: "Optimizing storage demands during active stream usage"
|
||
*
|
||
* @param date
|
||
* Updates the previous lastDisconnect value.
|
||
*
|
||
* @param lastHandledByClient
|
||
* The most recent 'h' value we can safely send to the server.
|
||
*
|
||
* @param stream
|
||
* The associated xmppStream (standard parameter for storage classes)
|
||
**/
|
||
- (void)setLastDisconnect:(NSDate *)date
|
||
lastHandledByClient:(uint32_t)lastHandledByClient
|
||
forStream:(XMPPStream *)stream;
|
||
|
||
/**
|
||
* This method is invoked ** often ** during stream operation.
|
||
* It is not invoked when the xmppStream is disconnected.
|
||
*
|
||
* Important: See the note below: "Optimizing storage demands during active stream usage"
|
||
*
|
||
* @param date
|
||
* Updates the previous lastDisconnect value.
|
||
*
|
||
* @param lastHandledByServer
|
||
* The most recent 'h' value we've received from the server.
|
||
*
|
||
* @param pendingOutgoingStanzas
|
||
* An array of XMPPStreamManagementOutgoingStanza objects.
|
||
* The storage layer is in charge of properly persisting this array, including:
|
||
* - the array count
|
||
* - the stanzaId of each element, including those that are nil
|
||
*
|
||
* @param stream
|
||
* The associated xmppStream (standard parameter for storage classes)
|
||
**/
|
||
- (void)setLastDisconnect:(NSDate *)date
|
||
lastHandledByServer:(uint32_t)lastHandledByServer
|
||
pendingOutgoingStanzas:(NSArray *)pendingOutgoingStanzas
|
||
forStream:(XMPPStream *)stream;
|
||
|
||
|
||
/// ***** Optimizing storage demands during active stream usage *****
|
||
///
|
||
/// There are 2 methods that are invoked frequently during stream activity:
|
||
///
|
||
/// - setLastDisconnect:lastHandledByClient:forStream:
|
||
/// - setLastDisconnect:lastHandledByServer:pendingOutgoingStanzas:forStream:
|
||
///
|
||
/// They are invoked any time the 'h' values change, or whenver the pendingStanzaIds change.
|
||
/// In other words, they are invoked continually as stanzas get sent and received.
|
||
/// And it is the job of the storage layer to decide how to handle the traffic.
|
||
/// There are a few things to consider here:
|
||
///
|
||
/// - How much chatter does the xmppStream do?
|
||
/// - How fast is the storage layer?
|
||
/// - How does the overhead on the storage layer affect the rest of the app?
|
||
///
|
||
/// If your xmppStream isn't very chatty, and you've got a fast concurrent database,
|
||
/// then you may be able to simply pipe all these method calls to the database without thinking.
|
||
/// However, if your xmppStream is always constantly sending/receiving presence stanzas, and pinging the server,
|
||
/// then you might consider a bit of optimzation here. Below is a simple recommendation for how to accomplish this.
|
||
///
|
||
/// You could choose to queue the changes from these method calls, and dump them to the database after a timeout.
|
||
/// Thus you'll be able to consolidate a large traffic surge into a small handful of database operations.
|
||
///
|
||
/// Also, you could expose a 'flush' operation on the storage layer.
|
||
/// And invoke the flush operation when the app is backgrounded, or about to quit.
|
||
|
||
|
||
/**
|
||
* This method is invoked immediately after an accidental disconnect.
|
||
* And may be invoked post-disconnect if the state changes, such as for the following edge cases:
|
||
*
|
||
* - due to continued processing of stanzas received pre-disconnect,
|
||
* that are just now being marked as handled by the delegate(s)
|
||
* - due to a delayed response from the delegate(s),
|
||
* such that we didn't receive the stanzaId for an outgoing stanza until after the disconnect occurred.
|
||
*
|
||
* This method is not invoked if stream management is started on a connected xmppStream.
|
||
*
|
||
* @param date
|
||
* This value will be the actual disconnect date.
|
||
*
|
||
* @param lastHandledByClient
|
||
* The most recent 'h' value we can safely send to the server.
|
||
*
|
||
* @param lastHandledByServer
|
||
* The most recent 'h' value we've received from the server.
|
||
*
|
||
* @param pendingOutgoingStanzas
|
||
* An array of XMPPStreamManagementOutgoingStanza objects.
|
||
* The storage layer is in charge of properly persisting this array, including:
|
||
* - the array count
|
||
* - the stanzaId of each element, including those that are nil
|
||
*
|
||
* @param stream
|
||
* The associated xmppStream (standard parameter for storage classes)
|
||
**/
|
||
- (void)setLastDisconnect:(NSDate *)date
|
||
lastHandledByClient:(uint32_t)lastHandledByClient
|
||
lastHandledByServer:(uint32_t)lastHandledByServer
|
||
pendingOutgoingStanzas:(NSArray *)pendingOutgoingStanzas
|
||
forStream:(XMPPStream *)stream;
|
||
|
||
/**
|
||
* Invoked when the extension needs values from a previous session.
|
||
* This method is used to get values needed in order to determine if it can resume a previous stream.
|
||
**/
|
||
- (void)getResumptionId:(NSString **)resumptionIdPtr
|
||
timeout:(uint32_t *)timeoutPtr
|
||
lastDisconnect:(NSDate **)lastDisconnectPtr
|
||
forStream:(XMPPStream *)stream;
|
||
|
||
/**
|
||
* Invoked when the extension needs values from a previous session.
|
||
* This method is used to get values needed in order to resume a previous stream.
|
||
**/
|
||
- (void)getLastHandledByClient:(uint32_t *)lastHandledByClientPtr
|
||
lastHandledByServer:(uint32_t *)lastHandledByServerPtr
|
||
pendingOutgoingStanzas:(NSArray **)pendingOutgoingStanzasPtr
|
||
forStream:(XMPPStream *)stream;
|
||
|
||
/**
|
||
* Instructs the storage layer to remove all values stored for the given stream.
|
||
* This occurs after the extension detects a "cleanly closed stream",
|
||
* in which case the stream cannot be resumed next time.
|
||
**/
|
||
- (void)removeAllForStream:(XMPPStream *)stream;
|
||
|
||
@end
|
||
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
#pragma mark -
|
||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||
|
||
@interface XMPPStream (XMPPStreamManagement)
|
||
|
||
/**
|
||
* Returns whether or not the server's <stream:features> includes <sm xmlns='urn:xmpp:sm:3'/>.
|
||
**/
|
||
- (BOOL)supportsStreamManagement;
|
||
|
||
@end
|