From ec4378ca492179fe810a72dfbb50a7845f18bd35 Mon Sep 17 00:00:00 2001 From: Mark Schisler Date: Thu, 19 Aug 2021 09:30:29 -0500 Subject: [PATCH] Handling reconnect scenarios properly when socket is hung --- .gitignore | 2 + Source/SocketIO/Client/SocketIOClient.swift | 3 +- Source/SocketIO/Manager/SocketManager.swift | 5 +-- Tests/TestSocketIO/SocketAckManagerTest.swift | 1 - Tests/TestSocketIO/SocketMangerTest.swift | 42 +++++++++++++++++++ Tests/TestSocketIO/SocketSideEffectTest.swift | 2 +- 6 files changed, 49 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index aec6c75b..4c165233 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ Socket.IO-Test-Server/node_modules/* .idea/ docs/docsets/ docs/undocumented.json + +.swiftpm diff --git a/Source/SocketIO/Client/SocketIOClient.swift b/Source/SocketIO/Client/SocketIOClient.swift index 4debd560..d97bae1e 100644 --- a/Source/SocketIO/Client/SocketIOClient.swift +++ b/Source/SocketIO/Client/SocketIOClient.swift @@ -150,7 +150,8 @@ open class SocketIOClient: NSObject, SocketIOClientSpec { manager.handleQueue.asyncAfter(deadline: DispatchTime.now() + timeoutAfter) {[weak self] in guard let this = self, this.status == .connecting || this.status == .notConnected else { return } - + DefaultSocketLogger.Logger.log("Timeout: Socket not connected, so setting to disconnected", type: this.logType) + this.status = .disconnected this.leaveNamespace() diff --git a/Source/SocketIO/Manager/SocketManager.swift b/Source/SocketIO/Manager/SocketManager.swift index c45c5f56..d69aa11f 100644 --- a/Source/SocketIO/Manager/SocketManager.swift +++ b/Source/SocketIO/Manager/SocketManager.swift @@ -132,7 +132,7 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat private(set) var reconnectAttempts = -1 private var _config: SocketIOClientConfiguration - private var currentReconnectAttempt = 0 + internal var currentReconnectAttempt = 0 private var reconnecting = false // MARK: Initializers @@ -186,9 +186,8 @@ open class SocketManager: NSObject, SocketManagerSpec, SocketParsable, SocketDat /// /// Override if you wish to attach a custom `SocketEngineSpec`. open func connect() { - guard !status.active else { + if status == .connected || (status == .connecting && currentReconnectAttempt == 0) { DefaultSocketLogger.Logger.log("Tried connecting an already active socket", type: SocketManager.logType) - return } diff --git a/Tests/TestSocketIO/SocketAckManagerTest.swift b/Tests/TestSocketIO/SocketAckManagerTest.swift index f5d9c2a5..4f7f5b0c 100644 --- a/Tests/TestSocketIO/SocketAckManagerTest.swift +++ b/Tests/TestSocketIO/SocketAckManagerTest.swift @@ -28,7 +28,6 @@ class SocketAckManagerTest : XCTestCase { func testManagerTimeoutAck() { let callbackExpection = expectation(description: "Manager should timeout ack with noAck status") - let itemsArray = ["Hi", "ho"] func callback(_ items: [Any]) { XCTAssertEqual(items.count, 1, "Timed out ack should have one value") diff --git a/Tests/TestSocketIO/SocketMangerTest.swift b/Tests/TestSocketIO/SocketMangerTest.swift index 1fa72a0a..b2dd5715 100644 --- a/Tests/TestSocketIO/SocketMangerTest.swift +++ b/Tests/TestSocketIO/SocketMangerTest.swift @@ -60,6 +60,44 @@ class SocketMangerTest : XCTestCase { waitForExpectations(timeout: 0.3) } + func testManagerDoesNotCallConnectWhenConnectingWithLessThanOneReconnect() { + setUpSockets() + + let expect = expectation(description: "The manager should not call connect on the engine") + expect.isInverted = true + + let engine = TestEngine(client: manager, url: manager.socketURL, options: nil) + + engine.onConnect = { + expect.fulfill() + } + manager.setTestStatus(.connecting) + manager.setCurrentReconnect(currentReconnect: 0) + manager.engine = engine + + manager.connect() + + waitForExpectations(timeout: 0.3) + } + + func testManagerCallConnectWhenConnectingAndMoreThanOneReconnect() { + setUpSockets() + + let expect = expectation(description: "The manager should call connect on the engine") + let engine = TestEngine(client: manager, url: manager.socketURL, options: nil) + + engine.onConnect = { + expect.fulfill() + } + manager.setTestStatus(.connecting) + manager.setCurrentReconnect(currentReconnect: 1) + manager.engine = engine + + manager.connect() + + waitForExpectations(timeout: 0.8) + } + func testManagerCallsDisconnect() { setUpSockets() @@ -154,6 +192,10 @@ public enum ManagerExpectation: String { } public class TestManager: SocketManager { + public func setCurrentReconnect(currentReconnect: Int) { + self.currentReconnectAttempt = currentReconnect + } + public override func disconnect() { setTestStatus(.disconnected) } diff --git a/Tests/TestSocketIO/SocketSideEffectTest.swift b/Tests/TestSocketIO/SocketSideEffectTest.swift index ecaaee03..749a819a 100644 --- a/Tests/TestSocketIO/SocketSideEffectTest.swift +++ b/Tests/TestSocketIO/SocketSideEffectTest.swift @@ -487,7 +487,7 @@ class TestEngine: SocketEngineSpec { private(set) var ws: WebSocket? = nil private(set) var version = SocketIOVersion.three - fileprivate var onConnect: (() -> ())? + internal var onConnect: (() -> ())? required init(client: SocketEngineClient, url: URL, options: [String: Any]?) { self.client = client