Skip to content

Commit 29dc0ff

Browse files
committed
Initial support for XEP-0198 (stream management)
1 parent 6d3c44a commit 29dc0ff

File tree

8 files changed

+2887
-0
lines changed

8 files changed

+2887
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#import <Foundation/Foundation.h>
2+
#import "XMPPStreamManagement.h"
3+
4+
/**
5+
* This class provides an in-memory only storage system for XMPPStreamManagement.
6+
* As such, it will only support stream resumption so long as the application doesn't terminate.
7+
*
8+
* This class should be considered primarily for testing.
9+
* An application making use of stream management should likely transition
10+
* to a persistent storage layer before distribution.
11+
**/
12+
@interface XMPPStreamManagementMemoryStorage : NSObject <XMPPStreamManagementStorage>
13+
14+
@end
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#import "XMPPStreamManagementMemoryStorage.h"
2+
#import <libkern/OSAtomic.h>
3+
4+
5+
@interface XMPPStreamManagementMemoryStorage ()
6+
7+
@property (atomic, weak, readwrite) XMPPStreamManagement *parent;
8+
@end
9+
10+
#pragma mark -
11+
12+
@implementation XMPPStreamManagementMemoryStorage
13+
{
14+
int32_t isConfigured;
15+
16+
NSString *resumptionId;
17+
uint32_t timeout;
18+
19+
NSDate *lastDisconnect;
20+
uint32_t lastHandledByClient;
21+
uint32_t lastHandledByServer;
22+
NSArray *pendingOutgoingStanzas;
23+
24+
}
25+
26+
- (BOOL)configureWithParent:(XMPPStreamManagement *)parent queue:(dispatch_queue_t)queue
27+
{
28+
// This implementation only supports a single xmppStream.
29+
// You must create multiple instances for multiple xmppStreams.
30+
31+
return OSAtomicCompareAndSwap32(0, 1, &isConfigured);
32+
}
33+
34+
/**
35+
* Invoked after we receive <enabled/> from the server.
36+
*
37+
* @param resumptionId
38+
* The ID required to resume the session, given to us by the server.
39+
*
40+
* @param timeout
41+
* The timeout in seconds.
42+
* After a disconnect, the server will maintain our state for this long.
43+
* If we attempt to resume the session after this timeout it likely won't work.
44+
*
45+
* @param lastDisconnect
46+
* Used to reset the lastDisconnect value.
47+
* This value is often updated during the session, to ensure it closely resemble the date the server will use.
48+
* That is, if the client application is killed (or crashes) we want a relatively accurate lastDisconnect date.
49+
*
50+
* @param stream
51+
* The associated xmppStream (standard parameter for storage classes)
52+
**/
53+
- (void)setResumptionId:(NSString *)inResumptionId
54+
timeout:(uint32_t)inTimeout
55+
lastDisconnect:(NSDate *)inLastDisconnect
56+
forStream:(XMPPStream *)stream
57+
{
58+
resumptionId = inResumptionId;
59+
timeout = inTimeout;
60+
lastDisconnect = inLastDisconnect;
61+
}
62+
63+
/**
64+
* This method is invoked ** often ** during stream operation.
65+
* It is not invoked when the xmppStream is disconnected.
66+
*
67+
* Important: See the note [in XMPPStreamManagement.h]: "Optimizing storage demands during active stream usage"
68+
*
69+
* @param date
70+
* Updates the previous lastDisconnect value.
71+
*
72+
* @param lastHandledByClient
73+
* The most recent 'h' value we can safely send to the server.
74+
*
75+
* @param stream
76+
* The associated xmppStream (standard parameter for storage classes)
77+
**/
78+
- (void)setLastDisconnect:(NSDate *)inLastDisconnect
79+
lastHandledByClient:(uint32_t)inLastHandledByClient
80+
forStream:(XMPPStream *)stream
81+
{
82+
lastDisconnect = inLastDisconnect;
83+
lastHandledByClient = inLastHandledByClient;
84+
}
85+
86+
/**
87+
* This method is invoked ** often ** during stream operation.
88+
* It is not invoked when the xmppStream is disconnected.
89+
*
90+
* Important: See the note [in XMPPStreamManagement.h]: "Optimizing storage demands during active stream usage"
91+
*
92+
* @param date
93+
* Updates the previous lastDisconnect value.
94+
*
95+
* @param lastHandledByServer
96+
* The most recent 'h' value we've received from the server.
97+
*
98+
* @param pendingOutgoingStanzas
99+
* An array of XMPPStreamManagementOutgoingStanza objects.
100+
* The storage layer is in charge of properly persisting this array, including:
101+
* - the array count
102+
* - the stanzaId of each element, including those that are nil
103+
*
104+
* @param stream
105+
* The associated xmppStream (standard parameter for storage classes)
106+
**/
107+
- (void)setLastDisconnect:(NSDate *)inLastDisconnect
108+
lastHandledByServer:(uint32_t)inLastHandledByServer
109+
pendingOutgoingStanzas:(NSArray *)inPendingOutgoingStanzas
110+
forStream:(XMPPStream *)stream
111+
{
112+
lastDisconnect = inLastDisconnect;
113+
lastHandledByServer = inLastHandledByServer;
114+
pendingOutgoingStanzas = inPendingOutgoingStanzas;
115+
}
116+
117+
/**
118+
* This method is invoked immediately after an accidental disconnect.
119+
* And may be invoked post-disconnect if the state changes, such as for the following edge cases:
120+
*
121+
* - due to continued processing of stanzas received pre-disconnect,
122+
* that are just now being marked as handled by the delegate(s)
123+
* - due to a delayed response from the delegate(s),
124+
* such that we didn't receive the stanzaId for an outgoing stanza until after the disconnect occurred.
125+
*
126+
* This method is not invoked if stream management is started on a connected xmppStream.
127+
*
128+
* @param date
129+
* This value will be the actual disconnect date.
130+
*
131+
* @param lastHandledByClient
132+
* The most recent 'h' value we can safely send to the server.
133+
*
134+
* @param lastHandledByServer
135+
* The most recent 'h' value we've received from the server.
136+
*
137+
* @param pendingOutgoingStanzas
138+
* An array of XMPPStreamManagementOutgoingStanza objects.
139+
* The storage layer is in charge of properly persisting this array, including:
140+
* - the array count
141+
* - the stanzaId of each element, including those that are nil
142+
*
143+
* @param stream
144+
* The associated xmppStream (standard parameter for storage classes)
145+
**/
146+
- (void)setLastDisconnect:(NSDate *)inLastDisconnect
147+
lastHandledByClient:(uint32_t)inLastHandledByClient
148+
lastHandledByServer:(uint32_t)inLastHandledByServer
149+
pendingOutgoingStanzas:(NSArray *)inPendingOutgoingStanzas
150+
forStream:(XMPPStream *)stream
151+
{
152+
lastDisconnect = inLastDisconnect;
153+
lastHandledByClient = inLastHandledByClient;
154+
lastHandledByServer = inLastHandledByServer;
155+
pendingOutgoingStanzas = inPendingOutgoingStanzas;
156+
}
157+
158+
/**
159+
* Invoked when the extension needs values from a previous session.
160+
* This method is used to get values needed in order to determine if it can resume a previous stream.
161+
**/
162+
- (void)getResumptionId:(NSString **)resumptionIdPtr
163+
timeout:(uint32_t *)timeoutPtr
164+
lastDisconnect:(NSDate **)lastDisconnectPtr
165+
forStream:(XMPPStream *)stream
166+
{
167+
if (resumptionIdPtr) *resumptionIdPtr = resumptionId;
168+
if (timeoutPtr) *timeoutPtr = timeout;
169+
if (lastDisconnectPtr) *lastDisconnectPtr = lastDisconnect;
170+
}
171+
172+
/**
173+
* Invoked when the extension needs values from a previous session.
174+
* This method is used to get values needed in order to resume a previous stream.
175+
**/
176+
- (void)getLastHandledByClient:(uint32_t *)lastHandledByClientPtr
177+
lastHandledByServer:(uint32_t *)lastHandledByServerPtr
178+
pendingOutgoingStanzas:(NSArray **)pendingOutgoingStanzasPtr
179+
forStream:(XMPPStream *)stream;
180+
{
181+
if (lastHandledByClientPtr) *lastHandledByClientPtr = lastHandledByClient;
182+
if (lastHandledByServerPtr) *lastHandledByServerPtr = lastHandledByServer;
183+
if (pendingOutgoingStanzasPtr) *pendingOutgoingStanzasPtr = pendingOutgoingStanzas;
184+
}
185+
186+
/**
187+
* Instructs the storage layer to remove all values stored for the given stream.
188+
* This occurs after the extension detects a "cleanly closed stream",
189+
* in which case the stream cannot be resumed next time.
190+
**/
191+
- (void)removeAllForStream:(XMPPStream *)stream
192+
{
193+
resumptionId = nil;
194+
timeout = 0;
195+
196+
lastDisconnect = nil;
197+
lastHandledByClient = 0;
198+
lastHandledByServer = 0;
199+
pendingOutgoingStanzas = nil;
200+
}
201+
202+
@end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#import <Foundation/Foundation.h>
2+
#import "XMPPElement.h"
3+
4+
/**
5+
* An outgoing stanza.
6+
*
7+
* The translation from element to stanzaId may be an asynchronous process,
8+
* so this structure is used to assist in the process.
9+
**/
10+
@interface XMPPStreamManagementOutgoingStanza : NSObject <NSCopying, NSCoding>
11+
12+
- (instancetype)initAwaitingStanzaId;
13+
- (instancetype)initWithStanzaId:(id)stanzaId;
14+
15+
@property (nonatomic, strong, readwrite) id stanzaId;
16+
@property (nonatomic, assign, readwrite) BOOL awaitingStanzaId;
17+
18+
@end
19+
20+
#pragma mark -
21+
22+
/**
23+
* An incoming stanza.
24+
*
25+
* The translation from element to stanzaId may be an asynchronous process,
26+
* so this structure is used to assist in the process.
27+
**/
28+
@interface XMPPStreamManagementIncomingStanza : NSObject
29+
30+
- (instancetype)initWithStanzaId:(id)stanzaId isHandled:(BOOL)isHandled;
31+
32+
@property (nonatomic, strong, readwrite) id stanzaId;
33+
@property (nonatomic, assign, readwrite) BOOL isHandled;
34+
35+
@end
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#import "XMPPStreamManagementStanzas.h"
2+
3+
4+
@implementation XMPPStreamManagementOutgoingStanza
5+
6+
@synthesize awaitingStanzaId = awaitingStanzaId;
7+
@synthesize stanzaId = stanzaId;
8+
9+
/**
10+
* Use when the stanzaId is unknown, and we are awaiting a stanzaId from the delegate(s).
11+
**/
12+
- (instancetype)initAwaitingStanzaId
13+
{
14+
if ((self = [super init]))
15+
{
16+
awaitingStanzaId = YES;
17+
}
18+
return self;
19+
}
20+
21+
/**
22+
* Use when the stanzaId is known, meaning we are NOT awaiting a stanzaId from the delegate(s).
23+
* The stanzaId may be nil.
24+
**/
25+
- (instancetype)initWithStanzaId:(id)inStanzaId
26+
{
27+
if ((self = [super init]))
28+
{
29+
stanzaId = inStanzaId;
30+
awaitingStanzaId = NO;
31+
}
32+
return self;
33+
}
34+
35+
/* NSCopying */
36+
37+
- (id)copyWithZone:(NSZone *)zone
38+
{
39+
XMPPStreamManagementOutgoingStanza *copy = [[XMPPStreamManagementOutgoingStanza alloc] init];
40+
copy->awaitingStanzaId = awaitingStanzaId;
41+
copy->stanzaId = stanzaId;
42+
43+
return copy;
44+
}
45+
46+
/* NSCoding */
47+
48+
- (id)initWithCoder:(NSCoder *)decoder
49+
{
50+
if ((self = [super init]))
51+
{
52+
awaitingStanzaId = [decoder decodeBoolForKey:@"awaitingStanzaId"];
53+
stanzaId = [decoder decodeObjectForKey:@"stanzaId"];
54+
}
55+
return self;
56+
}
57+
58+
- (void)encodeWithCoder:(NSCoder *)coder
59+
{
60+
[coder encodeBool:awaitingStanzaId forKey:@"awaitingStanzaId"];
61+
[coder encodeObject:stanzaId forKey:@"stanzaId"];
62+
}
63+
64+
@end
65+
66+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
67+
#pragma mark -
68+
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
69+
70+
@implementation XMPPStreamManagementIncomingStanza
71+
72+
@synthesize stanzaId = stanzaId;
73+
@synthesize isHandled = isHandled;
74+
75+
- (instancetype)initWithStanzaId:(id)inStanzaId isHandled:(BOOL)inIsHandled
76+
{
77+
if ((self = [super init]))
78+
{
79+
stanzaId = inStanzaId;
80+
isHandled = inIsHandled;
81+
}
82+
return self;
83+
}
84+
85+
@end

0 commit comments

Comments
 (0)