From 4e9e624379d1414cef6189dd9d9a14dcbeaf7f55 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Tue, 22 Nov 2016 12:40:48 +0000 Subject: [PATCH 1/4] RTN22 --- Spec/RealtimeClientConnection.swift | 58 ++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/Spec/RealtimeClientConnection.swift b/Spec/RealtimeClientConnection.swift index 0f4a5cd30..29a44a366 100644 --- a/Spec/RealtimeClientConnection.swift +++ b/Spec/RealtimeClientConnection.swift @@ -2359,7 +2359,6 @@ class RealtimeClientConnection: QuickSpec { } waitUntil(timeout: testTimeout) { done in - // Wait for token to expire client.connection.once(.Connected) { stateChange in expect(stateChange?.reason).to(beNil()) done() @@ -3401,6 +3400,63 @@ class RealtimeClientConnection: QuickSpec { } } + // RTN22 + it("Ably can request that a connected client re-authenticates by sending the client an AUTH ProtocolMessage") { + let options = AblyTests.commonAppSetup() + options.useTokenAuth = true + let client = ARTRealtime(options: options) + defer { client.dispose(); client.close() } + let channel = client.channels.get("foo") + + waitUntil(timeout: testTimeout) { done in + channel.attach { error in + expect(error).to(beNil()) + done() + } + } + + guard let initialConnectionId = client.connection.id else { + fail("ConnectionId is nil"); return + } + + guard let initialToken = client.auth.tokenDetails?.token else { + fail("Initial token is nil"); return + } + + waitUntil(timeout: testTimeout) { done in + client.connection.once(.Connected) { stateChange in + expect(stateChange?.reason).to(beNil()) + expect(initialToken).toNot(equal(client.auth.tokenDetails?.token)) + done() + } + + let authMessage = ARTProtocolMessage() + authMessage.action = .Auth + client.transport?.receive(authMessage) + } + + expect(client.connection.id).to(equal(initialConnectionId)) + + waitUntil(timeout: testTimeout) { done in + let partialDone = AblyTests.splitDone(2, done: done) + let expectedMessage = ARTMessage(name: "ios", data: "message1") + + channel.subscribe() { message in + expect(message.name).to(equal(expectedMessage.name)) + expect(message.data as? String).to(equal(expectedMessage.data as? String)) + partialDone() + } + + let rest = ARTRest(options: AblyTests.clientOptions(key: options.key!)) + rest.channels.get("foo").publish([expectedMessage]) { error in + expect(error).to(beNil()) + partialDone() + } + } + + channel.off() + } + } // https://github.com/ably/ably-ios/issues/454 From 8f9be836fe19a52c1a5f05bcd1bf1d628ab12ffc Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Tue, 22 Nov 2016 15:12:31 +0000 Subject: [PATCH 2/4] RTN22a --- Spec/RealtimeClientConnection.swift | 74 +++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/Spec/RealtimeClientConnection.swift b/Spec/RealtimeClientConnection.swift index 29a44a366..405e619e9 100644 --- a/Spec/RealtimeClientConnection.swift +++ b/Spec/RealtimeClientConnection.swift @@ -3457,6 +3457,80 @@ class RealtimeClientConnection: QuickSpec { channel.off() } + // RTN22a + it("re-authenticate and resume the connection when the client is forcibly disconnected following a DISCONNECTED message containing an error code in the range 40140 <= code < 40150") { + let options = AblyTests.commonAppSetup() + options.token = getTestToken(key: options.key!, ttl: 5.0) + let client = ARTRealtime(options: options) + defer { client.dispose(); client.close() } + let channel = client.channels.get("foo") + + waitUntil(timeout: testTimeout) { done in + channel.attach { error in + expect(error).to(beNil()) + done() + } + } + + guard let initialConnectionId = client.connection.id else { + fail("ConnectionId is nil"); return + } + + guard let initialToken = client.auth.tokenDetails?.token else { + fail("Initial token is nil"); return + } + + channel.once(.Detached) { _ in + fail("Should not detach channels") + } + + var authorizeMethodCallCount = 0 + let hook = client.auth.testSuite_injectIntoMethodAfter(#selector(client.auth.authorize)) { + authorizeMethodCallCount += 1 + } + defer { hook.remove() } + + waitUntil(timeout: testTimeout) { done in + client.connection.once(.Disconnected) { stateChange in + guard let error = stateChange?.reason else { + fail("Error is nil"); done(); return + } + expect(error.code) == 40142 + done() + } + } + + waitUntil(timeout: testTimeout) { done in + client.connection.once(.Connected) { stateChange in + expect(stateChange?.reason).to(beNil()) + expect(initialToken).toNot(equal(client.auth.tokenDetails?.token)) + done() + } + } + + expect(client.connection.id).to(equal(initialConnectionId)) + expect(authorizeMethodCallCount) == 1 + + waitUntil(timeout: testTimeout) { done in + let partialDone = AblyTests.splitDone(2, done: done) + let expectedMessage = ARTMessage(name: "ios", data: "message1") + + channel.subscribe() { message in + expect(message.name).to(equal(expectedMessage.name)) + expect(message.data as? String).to(equal(expectedMessage.data as? String)) + partialDone() + } + + let rest = ARTRest(options: AblyTests.clientOptions(key: options.key!)) + rest.channels.get("foo").publish([expectedMessage]) { error in + expect(error).to(beNil()) + partialDone() + } + } + + channel.off() + } + } // https://github.com/ably/ably-ios/issues/454 From 11d07512bcb6b93f7a432a4d09109823f4164232 Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Mon, 12 Dec 2016 13:16:38 +0000 Subject: [PATCH 3/4] Fix: realtime transport can be nil --- Source/ARTRealtime+Private.h | 2 +- Spec/Auth.swift | 8 ++++---- Spec/RealtimeClientChannel.swift | 3 +-- Spec/RealtimeClientConnection.swift | 5 ++--- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Source/ARTRealtime+Private.h b/Source/ARTRealtime+Private.h index f7e308e10..c5ee12235 100644 --- a/Source/ARTRealtime+Private.h +++ b/Source/ARTRealtime+Private.h @@ -45,7 +45,7 @@ ART_ASSUME_NONNULL_BEGIN @interface ARTRealtime () @property (readwrite, strong, nonatomic) ARTRest *rest; -@property (readonly, getter=getTransport) id transport; +@property (readonly, getter=getTransport, art_nullable) id transport; @property (readonly, strong, nonatomic, art_nonnull) id reachability; @property (readonly, getter=getLogger) ARTLog *logger; diff --git a/Spec/Auth.swift b/Spec/Auth.swift index 7ea03fb24..c76d932c0 100644 --- a/Spec/Auth.swift +++ b/Spec/Auth.swift @@ -409,7 +409,7 @@ class Auth : QuickSpec { // Inject AUTH let authMessage = ARTProtocolMessage() authMessage.action = ARTProtocolMessageAction.Auth - realtime.transport.receive(authMessage) + realtime.transport?.receive(authMessage) expect(realtime.connection.errorReason).toEventuallyNot(beNil(), timeout: testTimeout) guard let errorInfo = realtime.connection.errorReason else { @@ -482,7 +482,7 @@ class Auth : QuickSpec { // Inject AUTH let authMessage = ARTProtocolMessage() authMessage.action = ARTProtocolMessageAction.Auth - realtime.transport.receive(authMessage) + realtime.transport?.receive(authMessage) expect(realtime.connection.errorReason).toEventuallyNot(beNil(), timeout: testTimeout) guard let errorInfo = realtime.connection.errorReason else { @@ -573,7 +573,7 @@ class Auth : QuickSpec { // Inject AUTH let authMessage = ARTProtocolMessage() authMessage.action = ARTProtocolMessageAction.Auth - realtime.transport.receive(authMessage) + realtime.transport?.receive(authMessage) expect(realtime.connection.errorReason).toEventuallyNot(beNil(), timeout: testTimeout) guard let errorInfo = realtime.connection.errorReason else { @@ -656,7 +656,7 @@ class Auth : QuickSpec { // Inject AUTH let authMessage = ARTProtocolMessage() authMessage.action = ARTProtocolMessageAction.Auth - realtime.transport.receive(authMessage) + realtime.transport?.receive(authMessage) expect(realtime.connection.errorReason).toEventuallyNot(beNil(), timeout: testTimeout) guard let errorInfo = realtime.connection.errorReason else { diff --git a/Spec/RealtimeClientChannel.swift b/Spec/RealtimeClientChannel.swift index dd75060a6..9e49e0a58 100644 --- a/Spec/RealtimeClientChannel.swift +++ b/Spec/RealtimeClientChannel.swift @@ -2430,8 +2430,7 @@ class RealtimeClientChannel: QuickSpec { protoMsg.action = .Detach protoMsg.error = ARTErrorInfo.createWithCode(123, message: "test error") protoMsg.channel = "test" - - client.realtimeTransport(client.transport, didReceiveMessage: protoMsg) + client.transport?.receive(protoMsg) expect(channel.state).to(equal(ARTRealtimeChannelState.Detached)) expect(channel.errorReason).to(equal(protoMsg.error)) diff --git a/Spec/RealtimeClientConnection.swift b/Spec/RealtimeClientConnection.swift index 405e619e9..fb9fe517c 100644 --- a/Spec/RealtimeClientConnection.swift +++ b/Spec/RealtimeClientConnection.swift @@ -3547,8 +3547,7 @@ class RealtimeClientConnection: QuickSpec { let protoMsg = ARTProtocolMessage() protoMsg.action = .Disconnect protoMsg.error = ARTErrorInfo.createWithCode(123, message: "test error") - - client.realtimeTransport(client.transport, didReceiveMessage: protoMsg) + client.transport?.receive(protoMsg) expect(client.connection.state).to(equal(ARTRealtimeConnectionState.Disconnected)) expect(client.connection.errorReason).to(equal(protoMsg.error)) @@ -3781,7 +3780,7 @@ class RealtimeClientConnection: QuickSpec { let authMessage = ARTProtocolMessage() authMessage.action = .Auth - client.transport.receive(authMessage) + client.transport?.receive(authMessage) client.close() From 36a76b9202071f850d37ebfe680719b3d5d656ea Mon Sep 17 00:00:00 2001 From: Ricardo Pereira Date: Mon, 12 Dec 2016 18:52:55 +0000 Subject: [PATCH 4/4] Fix: realtime should renew token by transitioning to CONNECTING --- Source/ARTRealtime.m | 12 +++++------- Spec/Auth.swift | 4 +--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Source/ARTRealtime.m b/Source/ARTRealtime.m index b96c46523..46549bf3e 100644 --- a/Source/ARTRealtime.m +++ b/Source/ARTRealtime.m @@ -306,7 +306,7 @@ - (void)transitionSideEffects:(ARTConnectionStateChange *)stateChange { } _transport = [[_transportClass alloc] initWithRest:self.rest options:self.options resumeKey:resumeKey connectionSerial:connectionSerial]; _transport.delegate = self; - [self transportConnect]; + [self transportConnectForcingNewToken:_renewingToken]; } if (self.connection.state != ARTRealtimeFailed && self.connection.state != ARTRealtimeClosed && self.connection.state != ARTRealtimeDisconnected) { @@ -530,9 +530,10 @@ - (void)onDisconnected:(ARTProtocolMessage *)message { [self.logger info:@"R:%p ARTRealtime disconnected", self]; ARTErrorInfo *error = message.error; if ([self shouldRenewToken:&error]) { - [self transportReconnectWithRenewedToken]; [self transition:ARTRealtimeDisconnected withErrorInfo:error]; [self.connection setErrorReason:nil]; + _renewingToken = true; + [self transition:ARTRealtimeConnecting withErrorInfo:nil]; return; } [self transition:ARTRealtimeDisconnected withErrorInfo:error]; @@ -558,6 +559,7 @@ - (void)onAuth { switch (self.connection.state) { case ARTRealtimeConnecting: case ARTRealtimeConnected: + _resuming = true; [self transportReconnectWithRenewedToken]; break; default: @@ -612,7 +614,7 @@ - (BOOL)shouldRenewToken:(ARTErrorInfo **)errorPtr { - (void)transportReconnectWithHost:(NSString *)host { [self.transport setHost:host]; - [self transportConnect]; + [self transportConnectForcingNewToken:false]; } - (void)transportReconnectWithRenewedToken { @@ -620,10 +622,6 @@ - (void)transportReconnectWithRenewedToken { [self transportConnectForcingNewToken:true]; } -- (void)transportConnect { - [self transportConnectForcingNewToken:false]; -} - - (void)transportConnectForcingNewToken:(BOOL)forceNewToken { ARTClientOptions *options = [self.options copy]; if ([options isBasicAuth]) { diff --git a/Spec/Auth.swift b/Spec/Auth.swift index c76d932c0..f67c06496 100644 --- a/Spec/Auth.swift +++ b/Spec/Auth.swift @@ -714,9 +714,7 @@ class Auth : QuickSpec { options.autoConnect = false let client = ARTRealtime(options: options) - defer { - client.close() - } + defer { client.dispose(); client.close() } client.setTransportClass(TestProxyTransport.self) client.connect()