From daa6a7f22e87e7257fa29ebf299f30e6ae09735a Mon Sep 17 00:00:00 2001 From: Cesare Rocchi Date: Thu, 23 Aug 2018 11:19:58 +0200 Subject: [PATCH 1/8] Add suspendImmediateReconnection This is needed for some tests, to suspend the behavior defined by RTN15a (reconnect immediately after disconnection). --- Source/ARTRealtime+Private.h | 3 +++ Source/ARTRealtime.m | 1 + 2 files changed, 4 insertions(+) diff --git a/Source/ARTRealtime+Private.h b/Source/ARTRealtime+Private.h index b9fc67250..cc37f2225 100644 --- a/Source/ARTRealtime+Private.h +++ b/Source/ARTRealtime+Private.h @@ -68,6 +68,9 @@ NS_ASSUME_NONNULL_BEGIN @property (readonly, getter=getClientOptions) ARTClientOptions *options; +/// Suspend the behavior defined in RTN15a, that is trying to immediately reconnect after a disconnection +@property (readwrite, assign, nonatomic) BOOL suspendImmediateReconnection; + @end @interface ARTRealtime (Private) diff --git a/Source/ARTRealtime.m b/Source/ARTRealtime.m index eaa943757..98c2299bc 100644 --- a/Source/ARTRealtime.m +++ b/Source/ARTRealtime.m @@ -52,6 +52,7 @@ - (void)setRetryIn:(NSTimeInterval)retryIn; @implementation ARTRealtime { BOOL _resuming; BOOL _renewingToken; + BOOL _suspendImmediateReconnection; ARTEventEmitter *_pingEventEmitter; NSDate *_startedReconnection; NSDate *_lastActivity; From eef66ae849ff557a8274e72e4f39f1d2c5909908 Mon Sep 17 00:00:00 2001 From: Cesare Rocchi Date: Thu, 23 Aug 2018 11:21:14 +0200 Subject: [PATCH 2/8] Add implementation of RTN15a --- Source/ARTRealtime.m | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/ARTRealtime.m b/Source/ARTRealtime.m index 98c2299bc..198c1642b 100644 --- a/Source/ARTRealtime.m +++ b/Source/ARTRealtime.m @@ -506,7 +506,12 @@ - (ARTEventListener *)transitionSideEffects:(ARTConnectionStateChange *)stateCha [self.transport close]; _transport = nil; - [stateChange setRetryIn:self.options.disconnectedRetryTimeout]; + NSTimeInterval retryInterval = self.options.disconnectedRetryTimeout; + // RTN15a - retry immediately if client was connected + if (stateChange.previous == ARTRealtimeConnected && !_suspendImmediateReconnection) { + retryInterval = 0.1; + } + [stateChange setRetryIn:retryInterval]; stateChangeEventListener = [self unlessStateChangesBefore:stateChange.retryIn do:^{ [weakSelf transition:ARTRealtimeConnecting]; _connectionRetryFromDisconnectedListener = nil; From bace71d8d29aa067efd1490950b79036fccd81fe Mon Sep 17 00:00:00 2001 From: Cesare Rocchi Date: Thu, 23 Aug 2018 11:21:49 +0200 Subject: [PATCH 3/8] Fix tests that were depending on a low value for disconnectedRetryTimeout --- Spec/RealtimeClientConnection.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Spec/RealtimeClientConnection.swift b/Spec/RealtimeClientConnection.swift index 4c1edebf5..5d9174c52 100644 --- a/Spec/RealtimeClientConnection.swift +++ b/Spec/RealtimeClientConnection.swift @@ -2235,7 +2235,6 @@ class RealtimeClientConnection: QuickSpec { it("should not receive published messages until the connection reconnects successfully") { let options = AblyTests.commonAppSetup() options.autoConnect = false - options.disconnectedRetryTimeout = 1.0 let client1 = ARTRealtime(options: options) defer { client1.close() } @@ -2636,7 +2635,6 @@ class RealtimeClientConnection: QuickSpec { // RTN15f it("ACK and NACK responses for published messages can only ever be received on the transport connection on which those messages were sent") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.5 let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } let channel = client.channels.get("test") @@ -2684,6 +2682,7 @@ class RealtimeClientConnection: QuickSpec { it("uses a new connection") { client = AblyTests.newRealtime(options) + client.suspendImmediateReconnection = true client.connect() defer { client.close() } @@ -2713,6 +2712,7 @@ class RealtimeClientConnection: QuickSpec { // RTN15g3 it("reattaches to the same channels after a new connection has been established") { client = AblyTests.newRealtime(options) + client.suspendImmediateReconnection = true defer { client.close() } let channelName = "test-reattach-after-ttl" let channel = client.channels.get(channelName) From bf04b9915d98d445465bd75dc3beb20186e91a4e Mon Sep 17 00:00:00 2001 From: Cesare Rocchi Date: Thu, 23 Aug 2018 13:13:20 +0200 Subject: [PATCH 4/8] Remove unneeded setup of disconnectedRetryTimeout values for tests --- Spec/RealtimeClient.swift | 1 - Spec/RealtimeClientChannel.swift | 5 ----- Spec/RealtimeClientConnection.swift | 15 --------------- Spec/RealtimeClientPresence.swift | 4 ---- 4 files changed, 25 deletions(-) diff --git a/Spec/RealtimeClient.swift b/Spec/RealtimeClient.swift index 2c5e90cc4..6b9d510d7 100644 --- a/Spec/RealtimeClient.swift +++ b/Spec/RealtimeClient.swift @@ -1439,7 +1439,6 @@ class RealtimeClient: QuickSpec { it("moves to DISCONNECTED on an unexpected normal WebSocket close") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 0.3 let client = ARTRealtime(options: options) defer { client.dispose(); client.close() } diff --git a/Spec/RealtimeClientChannel.swift b/Spec/RealtimeClientChannel.swift index 6c25e9bce..5226a2279 100644 --- a/Spec/RealtimeClientChannel.swift +++ b/Spec/RealtimeClientChannel.swift @@ -319,7 +319,6 @@ class RealtimeClientChannel: QuickSpec { // RTL2f it("ChannelStateChange will contain a resumed boolean attribute with value @true@ if the bit flag RESUMED was included") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 options.tokenDetails = getTestTokenDetails(ttl: 5.0) let client = ARTRealtime(options: options) defer { client.dispose(); client.close() } @@ -833,7 +832,6 @@ class RealtimeClientChannel: QuickSpec { it("DISCONNECTED") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 0.1 let client = ARTRealtime(options: options) defer { client.dispose(); client.close() } @@ -1491,7 +1489,6 @@ class RealtimeClientChannel: QuickSpec { it("DISCONNECTED") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 0.1 let client = ARTRealtime(options: options) defer { client.dispose(); client.close() } @@ -1750,7 +1747,6 @@ class RealtimeClientChannel: QuickSpec { context("the message should be queued and delivered as soon as the connection state returns to CONNECTED if the connection is") { let options = AblyTests.commonAppSetup() options.useTokenAuth = true - options.disconnectedRetryTimeout = 0.3 options.autoConnect = false var client: ARTRealtime! var channel: ARTRealtimeChannel! @@ -1897,7 +1893,6 @@ class RealtimeClientChannel: QuickSpec { // RTL6c4 context("will result in an error if the") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 0.1 options.suspendedRetryTimeout = 0.3 options.autoConnect = false var client: ARTRealtime! diff --git a/Spec/RealtimeClientConnection.swift b/Spec/RealtimeClientConnection.swift index 5d9174c52..b9d5ef5e9 100644 --- a/Spec/RealtimeClientConnection.swift +++ b/Spec/RealtimeClientConnection.swift @@ -264,7 +264,6 @@ class RealtimeClientConnection: QuickSpec { it("should emit events for state changes") { let options = AblyTests.commonAppSetup() options.autoConnect = false - options.disconnectedRetryTimeout = 0.0 let client = ARTRealtime(options: options) defer { client.dispose(); client.close() } @@ -975,7 +974,6 @@ class RealtimeClientConnection: QuickSpec { it("should reset msgSerial serially if the connection does not resume") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 options.clientId = "tester" let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } @@ -1136,7 +1134,6 @@ class RealtimeClientConnection: QuickSpec { it("lost connection state") { let options = AblyTests.commonAppSetup() options.autoConnect = false - options.disconnectedRetryTimeout = 0.1 let client = ARTRealtime(options: options) client.setTransport(TestProxyTransport.self) client.connect() @@ -1918,7 +1915,6 @@ class RealtimeClientConnection: QuickSpec { it("should not emit error with a renewable token") { let options = AblyTests.commonAppSetup() options.autoConnect = false - options.disconnectedRetryTimeout = 0.1 options.authCallback = { tokenParams, callback in getTestTokenDetails(key: options.key, capability: tokenParams.capability, ttl: tokenParams.ttl as! TimeInterval?, completion: callback) } @@ -2289,7 +2285,6 @@ class RealtimeClientConnection: QuickSpec { // RTN15b1, RTN15b2 it("resume is the private connection key and connection_serial is the most recent ProtocolMessage#connectionSerial received") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 0.1 let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } @@ -2317,7 +2312,6 @@ class RealtimeClientConnection: QuickSpec { // RTN15c1 it("CONNECTED ProtocolMessage with the same connectionId as the current client, and no error") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } let channel = client.channels.get("test") @@ -2345,7 +2339,6 @@ class RealtimeClientConnection: QuickSpec { // RTN15c2 it("CONNECTED ProtocolMessage with the same connectionId as the current client and an non-fatal error") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } let channel = client.channels.get("test") @@ -2405,7 +2398,6 @@ class RealtimeClientConnection: QuickSpec { // RTN15c3 it("CONNECTED ProtocolMessage with a new connectionId and an error") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } let channel = client.channels.get("test") @@ -2444,7 +2436,6 @@ class RealtimeClientConnection: QuickSpec { // RTN15c4 it("ERROR ProtocolMessage indicating a fatal error in the connection") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } let channel = client.channels.get("test") @@ -2476,7 +2467,6 @@ class RealtimeClientConnection: QuickSpec { it("should resume the connection after an auth renewal") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 options.tokenDetails = getTestTokenDetails(ttl: 5.0) let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } @@ -2599,7 +2589,6 @@ class RealtimeClientConnection: QuickSpec { it("the connection#key may change and will be provided in the first CONNECTED ProtocolMessage#connectionDetails") { let options = AblyTests.commonAppSetup() options.autoConnect = false - options.disconnectedRetryTimeout = 1.0 let client = ARTRealtime(options: options) client.setTransport(TestProxyTransport.self) @@ -2838,7 +2827,6 @@ class RealtimeClientConnection: QuickSpec { // RTN15h2 it("should transition to disconnected when the token renewal fails and the error should be emitted") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 options.autoConnect = false let tokenTtl = 5.0 let tokenDetails = getTestTokenDetails(key: options.key, capability: nil, ttl: tokenTtl)! @@ -3678,7 +3666,6 @@ class RealtimeClientConnection: QuickSpec { // RTN19a it("should resend any ProtocolMessage that is awaiting a ACK/NACK") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 0.1 let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } let channel = client.channels.get("test") @@ -3709,7 +3696,6 @@ class RealtimeClientConnection: QuickSpec { // RTN19b it("should resent the ATTACH message if there are any pending channels") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 0.1 let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } let channel = client.channels.get("test") @@ -3742,7 +3728,6 @@ class RealtimeClientConnection: QuickSpec { // RTN19b it("should resent the DETACH message if there are any pending channels") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } let channel = client.channels.get("test") diff --git a/Spec/RealtimeClientPresence.swift b/Spec/RealtimeClientPresence.swift index 5c59eb094..36d6c1fd5 100644 --- a/Spec/RealtimeClientPresence.swift +++ b/Spec/RealtimeClientPresence.swift @@ -93,7 +93,6 @@ class RealtimeClientPresence: QuickSpec { let membersCount = 110 let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 var clientSecondary: ARTRealtime! defer { clientSecondary.dispose(); clientSecondary.close() } @@ -690,7 +689,6 @@ class RealtimeClientPresence: QuickSpec { } defer { clientMembers?.dispose(); clientMembers?.close() } - options.disconnectedRetryTimeout = 1.0 options.tokenDetails = getTestTokenDetails(key: options.key!, ttl: 5.0) let client = AblyTests.newRealtime(options) defer { client.dispose(); client.close() } @@ -3171,7 +3169,6 @@ class RealtimeClientPresence: QuickSpec { // RTP16b it("all presence messages will be queued and delivered as soon as the connection state returns to CONNECTED") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 let client = ARTRealtime(options: options) defer { client.dispose(); client.close() } let channel = client.channels.get("test") @@ -3199,7 +3196,6 @@ class RealtimeClientPresence: QuickSpec { // RTP16b it("all presence messages will be lost if queueMessages has been explicitly set to false") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 options.queueMessages = false let client = ARTRealtime(options: options) defer { client.dispose(); client.close() } From 2af14146803d85849a88255f69219e5400a725f5 Mon Sep 17 00:00:00 2001 From: Cesare Rocchi Date: Wed, 5 Sep 2018 12:07:50 +0200 Subject: [PATCH 5/8] Clarify RTN14e test --- Spec/RealtimeClientConnection.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Spec/RealtimeClientConnection.swift b/Spec/RealtimeClientConnection.swift index b9d5ef5e9..97620a687 100644 --- a/Spec/RealtimeClientConnection.swift +++ b/Spec/RealtimeClientConnection.swift @@ -2138,6 +2138,7 @@ class RealtimeClientConnection: QuickSpec { // RTN14e it("connection state has been in the DISCONNECTED state for more than the default connectionStateTtl should change the state to SUSPENDED") { let options = AblyTests.commonAppSetup() + // to not wait the defaul 15s before reconnecting options.disconnectedRetryTimeout = 0.1 options.suspendedRetryTimeout = 0.5 options.autoConnect = false @@ -2156,6 +2157,7 @@ class RealtimeClientConnection: QuickSpec { ARTDefault.setRealtimeRequestTimeout(0.1) let client = ARTRealtime(options: options) + client.suspendImmediateReconnection = true defer { client.dispose(); client.close() } waitUntil(timeout: testTimeout) { done in From df6be9eca190a512380d0077636e5350aa0ef61b Mon Sep 17 00:00:00 2001 From: Cesare Rocchi Date: Wed, 5 Sep 2018 12:16:20 +0200 Subject: [PATCH 6/8] Remove leftover disconnectedRetryTimeout test settings --- Spec/RealtimeClientConnection.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Spec/RealtimeClientConnection.swift b/Spec/RealtimeClientConnection.swift index 97620a687..a69bc1aca 100644 --- a/Spec/RealtimeClientConnection.swift +++ b/Spec/RealtimeClientConnection.swift @@ -2100,6 +2100,7 @@ class RealtimeClientConnection: QuickSpec { ARTDefault.setRealtimeRequestTimeout(0.1) let client = ARTRealtime(options: options) + client.suspendImmediateReconnection = true defer { client.connection.off() client.close() @@ -2177,6 +2178,7 @@ class RealtimeClientConnection: QuickSpec { it("on CLOSE the connection should stop connection retries") { let options = AblyTests.commonAppSetup() + // to avoid waiting for the default 15s before trying a reconnection options.disconnectedRetryTimeout = 0.1 options.suspendedRetryTimeout = 0.5 options.autoConnect = false @@ -2195,6 +2197,7 @@ class RealtimeClientConnection: QuickSpec { ARTDefault.setRealtimeRequestTimeout(0.1) let client = ARTRealtime(options: options) + client.suspendImmediateReconnection = true defer { client.dispose(); client.close() } waitUntil(timeout: testTimeout) { done in @@ -2546,7 +2549,6 @@ class RealtimeClientConnection: QuickSpec { // RTN15d it("should recover from disconnection and messages should be delivered once the connection is resumed") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 let client1 = ARTRealtime(options: options) defer { client1.close() } @@ -2740,7 +2742,6 @@ class RealtimeClientConnection: QuickSpec { // RTN15g2 context("when connection (ttl + idle interval) period has NOT passed since last activity") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 5.0 var client: ARTRealtime! var connectionId = "" @@ -2775,7 +2776,6 @@ class RealtimeClientConnection: QuickSpec { it("if the token is renewable then error should not be emitted") { let options = AblyTests.commonAppSetup() - options.disconnectedRetryTimeout = 1.0 options.autoConnect = false options.authCallback = { tokenParams, callback in getTestTokenDetails(key: options.key, capability: tokenParams.capability, ttl: TimeInterval(60 * 60), completion: callback) From 8bab329f3f96471273b830643c20cf0528261f7b Mon Sep 17 00:00:00 2001 From: Cesare Rocchi Date: Thu, 6 Sep 2018 17:30:28 +0200 Subject: [PATCH 7/8] Add explicit test for RTN15a --- Spec/RealtimeClientConnection.swift | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Spec/RealtimeClientConnection.swift b/Spec/RealtimeClientConnection.swift index a69bc1aca..b9c528672 100644 --- a/Spec/RealtimeClientConnection.swift +++ b/Spec/RealtimeClientConnection.swift @@ -2283,6 +2283,27 @@ class RealtimeClientConnection: QuickSpec { expect(states).toEventually(equal([.connecting, .connected, .disconnected, .connecting, .connected]), timeout: testTimeout) } + + // RTN15a + it ("if a Connection transport is disconnected unexpectedly or if a token expires, then the Connection manager will immediately attempt to reconnect") { + let options = AblyTests.commonAppSetup() + options.autoConnect = false + options.tokenDetails = getTestTokenDetails(ttl: 3.0) + let client = ARTRealtime(options: options) + defer { client.dispose(); client.close() } + + waitUntil(timeout: testTimeout) { done in + client.connection.on(.disconnected) { _ in + let disconnectedTime = Date() + client.connection.on(.connected) { _ in + let reconnectedTime = Date() + expect(reconnectedTime.timeIntervalSince(disconnectedTime)).to(beCloseTo(1, within: 0.9)) + done() + } + } + client.connect() + } + } // RTN15b context("reconnects to the websocket endpoint with additional querystring params") { From ca194267ef42b1b6c3f5a753d4e861b42e829699 Mon Sep 17 00:00:00 2001 From: Cesare Rocchi Date: Thu, 6 Sep 2018 18:25:14 +0200 Subject: [PATCH 8/8] Tweak values for RTN15a test --- Spec/RealtimeClientConnection.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Spec/RealtimeClientConnection.swift b/Spec/RealtimeClientConnection.swift index b9c528672..86c08a9c9 100644 --- a/Spec/RealtimeClientConnection.swift +++ b/Spec/RealtimeClientConnection.swift @@ -2297,7 +2297,9 @@ class RealtimeClientConnection: QuickSpec { let disconnectedTime = Date() client.connection.on(.connected) { _ in let reconnectedTime = Date() - expect(reconnectedTime.timeIntervalSince(disconnectedTime)).to(beCloseTo(1, within: 0.9)) + // test that reconnection happens within 10 seconds, + // so that we are sure it doesn't wait for the default 15s + expect(reconnectedTime.timeIntervalSince(disconnectedTime)).to(beCloseTo(0, within: 10)) done() } }