From 6cda49ef2aa2257ca481a008c6556fb34f0beffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toni=20C=C3=A1rdenas?= Date: Tue, 3 May 2016 12:55:13 +0200 Subject: [PATCH] Queue messages when CONNECTED but renewing token. --- Source/ARTRealtime+Private.h | 2 +- Source/ARTRealtime.m | 94 ++++++++++++++++++-------------- Source/ARTRealtimeChannel.m | 32 ++++++----- Source/ARTTypes.m | 4 ++ Spec/RealtimeClientChannel.swift | 2 +- 5 files changed, 77 insertions(+), 57 deletions(-) diff --git a/Source/ARTRealtime+Private.h b/Source/ARTRealtime+Private.h index f43c26637..a9b076ad7 100644 --- a/Source/ARTRealtime+Private.h +++ b/Source/ARTRealtime+Private.h @@ -24,7 +24,7 @@ ART_ASSUME_NONNULL_BEGIN @interface ARTRealtime () @property (readonly, strong, nonatomic) __GENERIC(ARTEventEmitter, NSNumber *, ARTConnectionStateChange *) *eventEmitter; -@property (readonly, strong, nonatomic) __GENERIC(ARTEventEmitter, NSNull *, NSNull *) *reconnectedEventEmitter; +@property (readonly, strong, nonatomic) __GENERIC(ARTEventEmitter, NSNull *, NSNull *) *connectedEventEmitter; + (NSString *)protocolStr:(ARTProtocolMessageAction)action; diff --git a/Source/ARTRealtime.m b/Source/ARTRealtime.m index a9e56d990..e9f45d456 100644 --- a/Source/ARTRealtime.m +++ b/Source/ARTRealtime.m @@ -31,6 +31,12 @@ #import "ARTRealtimeTransport.h" #import "ARTFallback.h" +@interface ARTConnectionStateChange () + +- (void)setRetryIn:(NSTimeInterval)retryIn; + +@end + #pragma mark - ARTRealtime implementation @implementation ARTRealtime { @@ -59,7 +65,7 @@ - (instancetype)initWithOptions:(ARTClientOptions *)options { _rest = [[ARTRest alloc] initWithOptions:options]; _eventEmitter = [[ARTEventEmitter alloc] init]; - _reconnectedEventEmitter = [[ARTEventEmitter alloc] init]; + _connectedEventEmitter = [[ARTEventEmitter alloc] init]; _pingEventEmitter = [[ARTEventEmitter alloc] init]; _channels = [[ARTRealtimeChannels alloc] initWithRealtime:self]; _transport = nil; @@ -163,13 +169,14 @@ - (void)ping:(void (^)(ARTErrorInfo *)) cb { cb([ARTErrorInfo createWithCode:0 status:ARTStateConnectionFailed message:[NSString stringWithFormat:@"Can't ping a %@ connection", ARTRealtimeStateToStr(self.connection.state)]]); return; case ARTRealtimeConnecting: - case ARTRealtimeDisconnected: { - [_connection once:^(ARTConnectionStateChange *change) { - [self ping:cb]; - }]; - return; - } + case ARTRealtimeDisconnected: case ARTRealtimeConnected: + if (![self shouldSendEvents]) { + [_connectedEventEmitter once:^(NSNull *n) { + [self ping:cb]; + }]; + return; + } [_pingEventEmitter timed:[_pingEventEmitter once:cb] deadline:[ARTDefault realtimeRequestTimeout] onTimeout:^{ cb([ARTErrorInfo createWithCode:0 status:ARTStateConnectionFailed message:@"timed out"]); }]; @@ -196,13 +203,21 @@ - (void)transition:(ARTRealtimeConnectionState)state { - (void)transition:(ARTRealtimeConnectionState)state withErrorInfo:(ARTErrorInfo *)errorInfo { [self.logger debug:__FILE__ line:__LINE__ message:@"%p transition to %@ requested", self, ARTRealtimeStateToStr(state)]; - ARTRealtimeConnectionState previousState = self.connection.state; + ARTConnectionStateChange *stateChange = [[ARTConnectionStateChange alloc] initWithCurrent:state previous:self.connection.state reason:errorInfo retryIn:0]; [self.connection setState:state]; + [self transitionSideEffects:stateChange]; + + if (errorInfo != nil) { + [self.connection setErrorReason:errorInfo]; + } + [self.connection emit:state with:stateChange]; +} + +- (void)transitionSideEffects:(ARTConnectionStateChange *)stateChange { ARTStatus *status = nil; - NSTimeInterval retryIn = 0; - switch (self.connection.state) { + switch (stateChange.current) { case ARTRealtimeConnecting: { [self unlessStateChangesBefore:[ARTDefault realtimeRequestTimeout] do:^{ [self transition:ARTRealtimeDisconnected withErrorInfo:[ARTErrorInfo createWithCode:0 status:ARTStateConnectionFailed message:@"timed out"]]; @@ -211,7 +226,7 @@ - (void)transition:(ARTRealtimeConnectionState)state withErrorInfo:(ARTErrorInfo if (!_transport) { NSString *resumeKey = nil; NSNumber *connectionSerial = nil; - if (previousState == ARTRealtimeFailed || previousState == ARTRealtimeDisconnected) { + if (stateChange.previous == ARTRealtimeFailed || stateChange.previous == ARTRealtimeDisconnected) { resumeKey = self.connection.key; connectionSerial = [NSNumber numberWithLongLong:self.connection.serial]; _resuming = true; @@ -221,20 +236,6 @@ - (void)transition:(ARTRealtimeConnectionState)state withErrorInfo:(ARTErrorInfo [_transport connect]; } - if (previousState == ARTRealtimeDisconnected) { - __GENERIC(NSArray, ARTQueuedMessage *) *pending = self.pendingMessages; - _pendingMessages = [[NSMutableArray alloc] init]; - for (ARTQueuedMessage *queued in pending) { - [self send:queued.msg callback:^(ARTStatus *__art_nonnull status) { - for (id cb in queued.cbs) { - ((void(^)(ARTStatus *__art_nonnull))cb)(status); - } - }]; - } - - [_reconnectedEventEmitter emit:[NSNull null] with:nil]; - } - break; } case ARTRealtimeClosing: { @@ -252,7 +253,7 @@ - (void)transition:(ARTRealtimeConnectionState)state withErrorInfo:(ARTErrorInfo _transport = nil; break; case ARTRealtimeFailed: - status = [ARTStatus state:ARTStateConnectionFailed info:errorInfo]; + status = [ARTStatus state:ARTStateConnectionFailed info:stateChange.reason]; [self.transport abort:status]; self.transport.delegate = nil; _transport = nil; @@ -267,16 +268,16 @@ - (void)transition:(ARTRealtimeConnectionState)state withErrorInfo:(ARTErrorInfo }]; } if ([[NSDate date] timeIntervalSinceDate:_startedReconnection] >= _connectionStateTtl) { - [self transition:ARTRealtimeSuspended withErrorInfo:errorInfo]; + [self transition:ARTRealtimeSuspended withErrorInfo:stateChange.reason]; return; } [self.transport close]; self.transport.delegate = nil; _transport = nil; - retryIn = self.options.disconnectedRetryTimeout; + [stateChange setRetryIn:self.options.disconnectedRetryTimeout]; - [self unlessStateChangesBefore:retryIn do:^{ + [self unlessStateChangesBefore:stateChange.retryIn do:^{ [self transition:ARTRealtimeConnecting]; }]; @@ -286,14 +287,25 @@ - (void)transition:(ARTRealtimeConnectionState)state withErrorInfo:(ARTErrorInfo [self.transport close]; self.transport.delegate = nil; _transport = nil; - retryIn = self.options.suspendedRetryTimeout; - [self unlessStateChangesBefore:retryIn do:^{ + [stateChange setRetryIn:self.options.suspendedRetryTimeout]; + [self unlessStateChangesBefore:stateChange.retryIn do:^{ [self transition:ARTRealtimeConnecting]; }]; break; } - case ARTRealtimeConnected: + case ARTRealtimeConnected: { + __GENERIC(NSArray, ARTQueuedMessage *) *pending = self.pendingMessages; + _pendingMessages = [[NSMutableArray alloc] init]; + for (ARTQueuedMessage *queued in pending) { + [self send:queued.msg callback:^(ARTStatus *__art_nonnull status) { + for (id cb in queued.cbs) { + ((void(^)(ARTStatus *__art_nonnull))cb)(status); + } + }]; + } + [_connectedEventEmitter emit:[NSNull null] with:nil]; break; + } case ARTRealtimeInitialized: break; } @@ -309,13 +321,13 @@ - (void)transition:(ARTRealtimeConnectionState)state withErrorInfo:(ARTErrorInfo // For every Channel for (ARTRealtimeChannel* channel in self.channels) { if (channel.state == ARTRealtimeChannelInitialized || channel.state == ARTRealtimeChannelAttaching || channel.state == ARTRealtimeChannelAttached || channel.state == ARTRealtimeChannelFailed) { - if(state == ARTRealtimeClosing) { + if(stateChange.current == ARTRealtimeClosing) { //do nothing. Closed state is coming. } - else if(state == ARTRealtimeClosed) { + else if(stateChange.current == ARTRealtimeClosed) { [channel detachChannel:[ARTStatus state:ARTStateOk]]; } - else if(state == ARTRealtimeSuspended) { + else if(stateChange.current == ARTRealtimeSuspended) { [channel detachChannel:channelStatus]; } else { @@ -327,11 +339,6 @@ - (void)transition:(ARTRealtimeConnectionState)state withErrorInfo:(ARTErrorInfo } } } - - if (errorInfo != nil) { - [self.connection setErrorReason:errorInfo]; - } - [self.connection emit:state with:[[ARTConnectionStateChange alloc] initWithCurrent:state previous:previousState reason:errorInfo retryIn:retryIn]]; } - (void)unlessStateChangesBefore:(NSTimeInterval)deadline do:(void(^)())callback { @@ -392,6 +399,9 @@ - (void)onConnected:(ARTProtocolMessage *)message { } [self transition:ARTRealtimeConnected withErrorInfo:message.error]; break; + case ARTRealtimeConnected: + // Renewing token. + [self transitionSideEffects:[[ARTConnectionStateChange alloc] initWithCurrent:ARTRealtimeConnected previous:ARTRealtimeConnected reason:nil]]; default: break; } @@ -488,7 +498,7 @@ - (void)onSuspended { - (BOOL)shouldSendEvents { switch (self.connection.state) { case ARTRealtimeConnected: - return true; + return !_renewingToken; default: return false; } @@ -503,6 +513,8 @@ - (BOOL)shouldQueueEvents { case ARTRealtimeConnecting: case ARTRealtimeDisconnected: return true; + case ARTRealtimeConnected: + return _renewingToken; default: return false; } diff --git a/Source/ARTRealtimeChannel.m b/Source/ARTRealtimeChannel.m index 31724c8e3..846b5e1e3 100644 --- a/Source/ARTRealtimeChannel.m +++ b/Source/ARTRealtimeChannel.m @@ -562,13 +562,15 @@ - (void)attachAfterChecks:(void (^)(ARTErrorInfo * _Nullable))callback { [_attachedEventEmitter emit:[NSNull null] with:errorInfo]; }]; - ARTEventListener *reconnectedListener = [self.realtime.reconnectedEventEmitter once:^(NSNull *n) { - // Disconnected and connected while attaching, re-attach. - [self attachAfterChecks:callback]; - }]; - [_attachedEventEmitter once:^(ARTErrorInfo *err) { - [self.realtime.reconnectedEventEmitter off:reconnectedListener]; - }]; + if (![self.realtime shouldQueueEvents]) { + ARTEventListener *reconnectedListener = [self.realtime.connectedEventEmitter once:^(NSNull *n) { + // Disconnected and connected while attaching, re-attach. + [self attachAfterChecks:callback]; + }]; + [_attachedEventEmitter once:^(ARTErrorInfo *err) { + [self.realtime.connectedEventEmitter off:reconnectedListener]; + }]; + } } - (void)detach:(void (^)(ARTErrorInfo * _Nullable))callback { @@ -624,13 +626,15 @@ - (void)detachAfterChecks:(void (^)(ARTErrorInfo * _Nullable))callback { [_detachedEventEmitter emit:[NSNull null] with:errorInfo]; }]; - ARTEventListener *reconnectedListener = [self.realtime.reconnectedEventEmitter once:^(NSNull *n) { - // Disconnected and connected while attaching, re-detach. - [self detachAfterChecks:callback]; - }]; - [_detachedEventEmitter once:^(ARTErrorInfo *err) { - [self.realtime.reconnectedEventEmitter off:reconnectedListener]; - }]; + if (![self.realtime shouldQueueEvents]) { + ARTEventListener *reconnectedListener = [self.realtime.connectedEventEmitter once:^(NSNull *n) { + // Disconnected and connected while attaching, re-detach. + [self detachAfterChecks:callback]; + }]; + [_detachedEventEmitter once:^(ARTErrorInfo *err) { + [self.realtime.connectedEventEmitter off:reconnectedListener]; + }]; + } } - (void)detach { diff --git a/Source/ARTTypes.m b/Source/ARTTypes.m index a789f7708..4fe993e2b 100644 --- a/Source/ARTTypes.m +++ b/Source/ARTTypes.m @@ -85,4 +85,8 @@ - (NSString *)description { } } +- (void)setRetryIn:(NSTimeInterval)retryIn { + _retryIn = retryIn; +} + @end diff --git a/Spec/RealtimeClientChannel.swift b/Spec/RealtimeClientChannel.swift index 52437f90b..a02514835 100644 --- a/Spec/RealtimeClientChannel.swift +++ b/Spec/RealtimeClientChannel.swift @@ -126,7 +126,7 @@ class RealtimeClientChannel: QuickSpec { expect(emitCounter).to(equal(5)) if states.count != 5 { - fail("Missing some states") + fail("Expecting 5 states; got \(states)") return }