From 805f7d1a3be57dc2ef353933b4b3efdb8c8714b9 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Wed, 10 Apr 2024 13:57:05 +0200 Subject: [PATCH 01/11] savepoint --- Sources/WalletConnectRelay/RelayClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectRelay/RelayClient.swift b/Sources/WalletConnectRelay/RelayClient.swift index c3146adc3..e1a1b9197 100644 --- a/Sources/WalletConnectRelay/RelayClient.swift +++ b/Sources/WalletConnectRelay/RelayClient.swift @@ -102,7 +102,7 @@ public final class RelayClient { logger.debug("[Publish] Sending payload on topic: \(topic)") - Task {try await dispatcher.protectedSend(message)} + try await dispatcher.protectedSend(message) return try await withUnsafeThrowingContinuation { continuation in var cancellable: AnyCancellable? From 2fcde83908ded2c7543f6a2a004285acc1f1c60e Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 11 Apr 2024 14:52:01 +0200 Subject: [PATCH 02/11] refactor dispatcher --- Sources/WalletConnectRelay/Dispatching.swift | 46 ++++++------------- .../RelayClientFactory.swift | 26 +++++++++-- .../WalletConnectRelay/RelayURLFactory.swift | 13 ++++-- .../AutomaticSocketConnectionHandler.swift | 4 ++ .../ManualSocketConnectionHandler.swift | 7 +++ .../SocketConnectionHandler.swift | 1 + .../SocketUrlFallbackHandler.swift | 32 +++++++++++++ 7 files changed, 91 insertions(+), 38 deletions(-) create mode 100644 Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index 7d13bc39f..10ab43fd5 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -16,16 +16,14 @@ final class Dispatcher: NSObject, Dispatching { var onMessage: ((String) -> Void)? var socket: WebSocketConnecting var socketConnectionHandler: SocketConnectionHandler - + var socketUrlFallbackHandler: SocketUrlFallbackHandler + private let relayUrlFactory: RelayUrlFactory private let networkMonitor: NetworkMonitoring private let logger: ConsoleLogging private let defaultTimeout: Int = 5 - /// The property is used to determine whether relay.walletconnect.org will be used - /// in case relay.walletconnect.com doesn't respond for some reason (most likely due to being blocked in the user's location). - private var fallback = false - + private let socketConnectionStatusPublisherSubject = CurrentValueSubject(.disconnected) var socketConnectionStatusPublisher: AnyPublisher { @@ -42,25 +40,19 @@ final class Dispatcher: NSObject, Dispatching { socketFactory: WebSocketFactory, relayUrlFactory: RelayUrlFactory, networkMonitor: NetworkMonitoring, - socketConnectionType: SocketConnectionType, - logger: ConsoleLogging + socket: WebSocketConnecting, + logger: ConsoleLogging, + socketUrlFallbackHandler: SocketUrlFallbackHandler, + socketConnectionHandler:SocketConnectionHandler ) { + self.socketConnectionHandler = socketConnectionHandler self.relayUrlFactory = relayUrlFactory self.networkMonitor = networkMonitor self.logger = logger - - let socket = socketFactory.create(with: relayUrlFactory.create(fallback: fallback)) - socket.request.addValue(EnvironmentInfo.userAgent, forHTTPHeaderField: "User-Agent") - if let bundleId = Bundle.main.bundleIdentifier { - socket.request.addValue(bundleId, forHTTPHeaderField: "Origin") - } + self.socket = socket - - switch socketConnectionType { - case .automatic: socketConnectionHandler = AutomaticSocketConnectionHandler(socket: socket) - case .manual: socketConnectionHandler = ManualSocketConnectionHandler(socket: socket) - } - + self.socketUrlFallbackHandler = socketUrlFallbackHandler + super.init() setUpWebSocketSession() setUpSocketConnectionObserving() @@ -91,7 +83,7 @@ final class Dispatcher: NSObject, Dispatching { case .failure(let error): cancellable?.cancel() if !socket.isConnected { - handleFallbackIfNeeded(error: error) + socketUrlFallbackHandler.handleFallbackIfNeeded(error: error) } completion(error) case .finished: break @@ -101,6 +93,7 @@ final class Dispatcher: NSObject, Dispatching { send(string, completion: completion) }) } + func protectedSend(_ string: String) async throws { return try await withCheckedThrowingContinuation { continuation in @@ -138,19 +131,8 @@ extension Dispatcher { socket.onDisconnect = { [unowned self] error in self.socketConnectionStatusPublisherSubject.send(.disconnected) if error != nil { - self.socket.request.url = relayUrlFactory.create(fallback: fallback) - } - Task(priority: .high) { - await self.socketConnectionHandler.handleDisconnection() + self.socket.request.url = relayUrlFactory.create() } - } - } - - private func handleFallbackIfNeeded(error: NetworkError) { - if error == .connectionFailed && socket.request.url?.host == NetworkConstants.defaultUrl { - logger.debug("[WebSocket] - Fallback to \(NetworkConstants.fallbackUrl)") - fallback = true - socket.request.url = relayUrlFactory.create(fallback: fallback) Task(priority: .high) { await self.socketConnectionHandler.handleDisconnection() } diff --git a/Sources/WalletConnectRelay/RelayClientFactory.swift b/Sources/WalletConnectRelay/RelayClientFactory.swift index b59a50d29..f6eda742b 100644 --- a/Sources/WalletConnectRelay/RelayClientFactory.swift +++ b/Sources/WalletConnectRelay/RelayClientFactory.swift @@ -56,13 +56,33 @@ public struct RelayClientFactory { projectId: projectId, socketAuthenticator: socketAuthenticator ) + let socket = socketFactory.create(with: relayUrlFactory.create()) + socket.request.addValue(EnvironmentInfo.userAgent, forHTTPHeaderField: "User-Agent") + if let bundleId = Bundle.main.bundleIdentifier { + socket.request.addValue(bundleId, forHTTPHeaderField: "Origin") + } + var socketConnectionHandler: SocketConnectionHandler! + switch socketConnectionType { + case .automatic: socketConnectionHandler = AutomaticSocketConnectionHandler(socket: socket) + case .manual: socketConnectionHandler = ManualSocketConnectionHandler(socket: socket) + } + + let socketFallbackHandler = SocketUrlFallbackHandler( + relayUrlFactory: relayUrlFactory, + logger: logger, + socketConnectionHandler: socketConnectionHandler, + socket: socket, + networkMonitor: networkMonitor + ) let dispatcher = Dispatcher( socketFactory: socketFactory, - relayUrlFactory: relayUrlFactory, + relayUrlFactory: relayUrlFactory, networkMonitor: networkMonitor, - socketConnectionType: socketConnectionType, - logger: logger + socket: socket, + logger: logger, + socketUrlFallbackHandler: socketFallbackHandler, + socketConnectionHandler: socketConnectionHandler ) let rpcHistory = RPCHistoryFactory.createForRelay(keyValueStorage: keyValueStorage) diff --git a/Sources/WalletConnectRelay/RelayURLFactory.swift b/Sources/WalletConnectRelay/RelayURLFactory.swift index ff99759c0..20290fd24 100644 --- a/Sources/WalletConnectRelay/RelayURLFactory.swift +++ b/Sources/WalletConnectRelay/RelayURLFactory.swift @@ -1,10 +1,13 @@ import Foundation -struct RelayUrlFactory { +class RelayUrlFactory { private let relayHost: String private let projectId: String private let socketAuthenticator: ClientIdAuthenticating - + /// The property is used to determine whether relay.walletconnect.org will be used + /// in case relay.walletconnect.com doesn't respond for some reason (most likely due to being blocked in the user's location). + private var fallback: Bool = false + init( relayHost: String, projectId: String, @@ -15,7 +18,11 @@ struct RelayUrlFactory { self.socketAuthenticator = socketAuthenticator } - func create(fallback: Bool) -> URL { + func setFallback() { + self.fallback = true + } + + func create() -> URL { var components = URLComponents() components.scheme = "wss" components.host = fallback ? NetworkConstants.fallbackUrl : relayHost diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index 99c61c61c..adb0d3929 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -73,6 +73,10 @@ class AutomaticSocketConnectionHandler { // MARK: - SocketConnectionHandler extension AutomaticSocketConnectionHandler: SocketConnectionHandler { + func tryReconect() async { + guard await appStateObserver.currentState == .foreground else { return } + reconnectIfNeeded() + } func handleConnect() throws { throw Errors.manualSocketConnectionForbidden diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift index 7f145d1c5..2198c9e12 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift @@ -1,6 +1,7 @@ import Foundation class ManualSocketConnectionHandler: SocketConnectionHandler { + var socket: WebSocketConnecting init(socket: WebSocketConnecting) { @@ -19,4 +20,10 @@ class ManualSocketConnectionHandler: SocketConnectionHandler { // No operation // ManualSocketConnectionHandler does not support reconnection logic } + + func tryReconect() async { + if !socket.isConnected { + socket.connect() + } + } } diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/SocketConnectionHandler.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/SocketConnectionHandler.swift index 4ac3046dd..91284893b 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/SocketConnectionHandler.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/SocketConnectionHandler.swift @@ -4,4 +4,5 @@ protocol SocketConnectionHandler { func handleConnect() throws func handleDisconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws func handleDisconnection() async + func tryReconect() async } diff --git a/Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift b/Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift new file mode 100644 index 000000000..0db60b2f4 --- /dev/null +++ b/Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift @@ -0,0 +1,32 @@ +import Foundation + +class SocketUrlFallbackHandler { + private let relayUrlFactory: RelayUrlFactory + private var logger: ConsoleLogging + private var socketConnectionHandler: SocketConnectionHandler + private var socket: WebSocketConnecting + private let networkMonitor: NetworkMonitoring + + init(relayUrlFactory: RelayUrlFactory, + logger: ConsoleLogging, + socketConnectionHandler: SocketConnectionHandler, + socket: WebSocketConnecting, + networkMonitor: NetworkMonitoring) { + self.relayUrlFactory = relayUrlFactory + self.logger = logger + self.socketConnectionHandler = socketConnectionHandler + self.socket = socket + self.networkMonitor = networkMonitor + } + + func handleFallbackIfNeeded(error: NetworkError) { + if error == .connectionFailed && socket.request.url?.host == NetworkConstants.defaultUrl { + logger.debug("[WebSocket] - Fallback to \(NetworkConstants.fallbackUrl)") + relayUrlFactory.setFallback() + socket.request.url = relayUrlFactory.create() + Task(priority: .high) { + await self.socketConnectionHandler.tryReconect() + } + } + } +} From 2942f0132d1ec5243a96b669b54afadfc1cfffec Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 12 Apr 2024 08:34:43 +0200 Subject: [PATCH 03/11] savepoint --- Sources/WalletConnectRelay/Dispatching.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index 10ab43fd5..2d18d1fdd 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -109,6 +109,7 @@ final class Dispatcher: NSObject, Dispatching { func connect() throws { try socketConnectionHandler.handleConnect() + start counting for fallback on first connect attempt } func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { From cc211b098cbaed6b3c85c443df6f9ff022bc23a7 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Mon, 29 Apr 2024 16:43:48 +0100 Subject: [PATCH 04/11] savepoint --- Sources/WalletConnectRelay/Dispatching.swift | 15 +++++++++++- .../SocketUrlFallbackHandler.swift | 23 ++++++++++--------- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index 2d18d1fdd..070e0fd64 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -108,10 +108,23 @@ final class Dispatcher: NSObject, Dispatching { } func connect() throws { + // Attempt to handle connection try socketConnectionHandler.handleConnect() - start counting for fallback on first connect attempt + + // Start a timer for the fallback mechanism + let timer = DispatchSource.makeTimerSource(queue: concurrentQueue) + timer.schedule(deadline: .now() + .seconds(defaultTimeout)) + timer.setEventHandler { [unowned self] in + if !self.socket.isConnected { + self.logger.debug("Connection timed out, initiating fallback...") + self.socketUrlFallbackHandler.handleFallbackIfNeeded(error: .connectionFailed) + } + timer.cancel() + } + timer.resume() } + func disconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { try socketConnectionHandler.handleDisconnect(closeCode: closeCode) } diff --git a/Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift b/Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift index 0db60b2f4..46d1ed5a0 100644 --- a/Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift +++ b/Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift @@ -7,17 +7,18 @@ class SocketUrlFallbackHandler { private var socket: WebSocketConnecting private let networkMonitor: NetworkMonitoring - init(relayUrlFactory: RelayUrlFactory, - logger: ConsoleLogging, - socketConnectionHandler: SocketConnectionHandler, - socket: WebSocketConnecting, - networkMonitor: NetworkMonitoring) { - self.relayUrlFactory = relayUrlFactory - self.logger = logger - self.socketConnectionHandler = socketConnectionHandler - self.socket = socket - self.networkMonitor = networkMonitor - } + init( + relayUrlFactory: RelayUrlFactory, + logger: ConsoleLogging, + socketConnectionHandler: SocketConnectionHandler, + socket: WebSocketConnecting, + networkMonitor: NetworkMonitoring) { + self.relayUrlFactory = relayUrlFactory + self.logger = logger + self.socketConnectionHandler = socketConnectionHandler + self.socket = socket + self.networkMonitor = networkMonitor + } func handleFallbackIfNeeded(error: NetworkError) { if error == .connectionFailed && socket.request.url?.host == NetworkConstants.defaultUrl { From ed0ca50323ef1a85cfb7524f4d84bebe1353ce50 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 2 May 2024 11:15:17 +0100 Subject: [PATCH 05/11] savepoint --- Sources/WalletConnectRelay/Dispatching.swift | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index 070e0fd64..f52192cb5 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -76,6 +76,7 @@ final class Dispatcher: NSObject, Dispatching { var cancellable: AnyCancellable? cancellable = Publishers.CombineLatest(socketConnectionStatusPublisher, networkConnectionStatusPublisher) .filter { $0.0 == .connected && $0.1 == .connected } + .first() .setFailureType(to: NetworkError.self) .timeout(.seconds(defaultTimeout), scheduler: concurrentQueue, customError: { .connectionFailed }) .sink(receiveCompletion: { [unowned self] result in @@ -96,12 +97,16 @@ final class Dispatcher: NSObject, Dispatching { func protectedSend(_ string: String) async throws { + var isResumed = false return try await withCheckedThrowingContinuation { continuation in protectedSend(string) { error in - if let error = error { - continuation.resume(throwing: error) - } else { - continuation.resume(returning: ()) + if !isResumed { + if let error = error { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: ()) + } + isResumed = true } } } From d705ce2604c0b0127de2c5a1b204016b76331e8d Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 2 May 2024 13:32:54 +0100 Subject: [PATCH 06/11] savepoint --- Sources/WalletConnectRelay/Dispatching.swift | 23 ++----------- .../RelayClientFactory.swift | 13 +++---- .../AutomaticSocketConnectionHandler.swift | 34 ++++++++++++++++++- .../ManualSocketConnectionHandler.swift | 29 +++++++++++++--- .../SocketUrlFallbackHandler.swift | 8 ++--- 5 files changed, 67 insertions(+), 40 deletions(-) diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index f52192cb5..32fac0135 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -16,14 +16,12 @@ final class Dispatcher: NSObject, Dispatching { var onMessage: ((String) -> Void)? var socket: WebSocketConnecting var socketConnectionHandler: SocketConnectionHandler - var socketUrlFallbackHandler: SocketUrlFallbackHandler + private let defaultTimeout: Int = 5 private let relayUrlFactory: RelayUrlFactory private let networkMonitor: NetworkMonitoring private let logger: ConsoleLogging - private let defaultTimeout: Int = 5 - private let socketConnectionStatusPublisherSubject = CurrentValueSubject(.disconnected) var socketConnectionStatusPublisher: AnyPublisher { @@ -42,8 +40,7 @@ final class Dispatcher: NSObject, Dispatching { networkMonitor: NetworkMonitoring, socket: WebSocketConnecting, logger: ConsoleLogging, - socketUrlFallbackHandler: SocketUrlFallbackHandler, - socketConnectionHandler:SocketConnectionHandler + socketConnectionHandler: SocketConnectionHandler ) { self.socketConnectionHandler = socketConnectionHandler self.relayUrlFactory = relayUrlFactory @@ -51,7 +48,6 @@ final class Dispatcher: NSObject, Dispatching { self.logger = logger self.socket = socket - self.socketUrlFallbackHandler = socketUrlFallbackHandler super.init() setUpWebSocketSession() @@ -83,9 +79,6 @@ final class Dispatcher: NSObject, Dispatching { switch result { case .failure(let error): cancellable?.cancel() - if !socket.isConnected { - socketUrlFallbackHandler.handleFallbackIfNeeded(error: error) - } completion(error) case .finished: break } @@ -115,18 +108,6 @@ final class Dispatcher: NSObject, Dispatching { func connect() throws { // Attempt to handle connection try socketConnectionHandler.handleConnect() - - // Start a timer for the fallback mechanism - let timer = DispatchSource.makeTimerSource(queue: concurrentQueue) - timer.schedule(deadline: .now() + .seconds(defaultTimeout)) - timer.setEventHandler { [unowned self] in - if !self.socket.isConnected { - self.logger.debug("Connection timed out, initiating fallback...") - self.socketUrlFallbackHandler.handleFallbackIfNeeded(error: .connectionFailed) - } - timer.cancel() - } - timer.resume() } diff --git a/Sources/WalletConnectRelay/RelayClientFactory.swift b/Sources/WalletConnectRelay/RelayClientFactory.swift index f6eda742b..3a4097336 100644 --- a/Sources/WalletConnectRelay/RelayClientFactory.swift +++ b/Sources/WalletConnectRelay/RelayClientFactory.swift @@ -61,19 +61,17 @@ public struct RelayClientFactory { if let bundleId = Bundle.main.bundleIdentifier { socket.request.addValue(bundleId, forHTTPHeaderField: "Origin") } - var socketConnectionHandler: SocketConnectionHandler! - switch socketConnectionType { - case .automatic: socketConnectionHandler = AutomaticSocketConnectionHandler(socket: socket) - case .manual: socketConnectionHandler = ManualSocketConnectionHandler(socket: socket) - } - let socketFallbackHandler = SocketUrlFallbackHandler( relayUrlFactory: relayUrlFactory, logger: logger, - socketConnectionHandler: socketConnectionHandler, socket: socket, networkMonitor: networkMonitor ) + var socketConnectionHandler: SocketConnectionHandler! + switch socketConnectionType { + case .automatic: socketConnectionHandler = AutomaticSocketConnectionHandler(socket: socket, logger: logger, socketUrlFallbackHandler: socketFallbackHandler) + case .manual: socketConnectionHandler = ManualSocketConnectionHandler(socket: socket, logger: logger, socketUrlFallbackHandler: socketFallbackHandler) + } let dispatcher = Dispatcher( socketFactory: socketFactory, @@ -81,7 +79,6 @@ public struct RelayClientFactory { networkMonitor: networkMonitor, socket: socket, logger: logger, - socketUrlFallbackHandler: socketFallbackHandler, socketConnectionHandler: socketConnectionHandler ) diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index adb0d3929..f7fe3ad99 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -14,24 +14,56 @@ class AutomaticSocketConnectionHandler { private let appStateObserver: AppStateObserving private let networkMonitor: NetworkMonitoring private let backgroundTaskRegistrar: BackgroundTaskRegistering + private let defaultTimeout: Int = 5 + private let logger: ConsoleLogging + private var socketUrlFallbackHandler: SocketUrlFallbackHandler private var publishers = Set() + private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.automatic_socket_connection", attributes: .concurrent) init( socket: WebSocketConnecting, networkMonitor: NetworkMonitoring = NetworkMonitor(), appStateObserver: AppStateObserving = AppStateObserver(), - backgroundTaskRegistrar: BackgroundTaskRegistering = BackgroundTaskRegistrar() + backgroundTaskRegistrar: BackgroundTaskRegistering = BackgroundTaskRegistrar(), + logger: ConsoleLogging, + socketUrlFallbackHandler: SocketUrlFallbackHandler ) { self.appStateObserver = appStateObserver self.socket = socket self.networkMonitor = networkMonitor self.backgroundTaskRegistrar = backgroundTaskRegistrar + self.logger = logger + self.socketUrlFallbackHandler = socketUrlFallbackHandler setUpStateObserving() setUpNetworkMonitoring() + socketUrlFallbackHandler.onTryReconnect = { [unowned self] in + Task(priority: .high) { + await tryReconect() + } + } + + connect() + + } + + func connect() { + // Attempt to handle connection socket.connect() + + // Start a timer for the fallback mechanism + let timer = DispatchSource.makeTimerSource(queue: concurrentQueue) + timer.schedule(deadline: .now() + .seconds(defaultTimeout)) + timer.setEventHandler { [unowned self] in + if !self.socket.isConnected { + self.logger.debug("Connection timed out, initiating fallback...") + self.socketUrlFallbackHandler.handleFallbackIfNeeded(error: .connectionFailed) + } + timer.cancel() + } + timer.resume() } private func setUpStateObserving() { diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift index 2198c9e12..3a1b70aec 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift @@ -2,14 +2,35 @@ import Foundation class ManualSocketConnectionHandler: SocketConnectionHandler { - var socket: WebSocketConnecting + private let socket: WebSocketConnecting + private let logger: ConsoleLogging + private let defaultTimeout: Int = 5 + private var socketUrlFallbackHandler: SocketUrlFallbackHandler + private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.manual_socket_connection", attributes: .concurrent) - init(socket: WebSocketConnecting) { - self.socket = socket - } + + init( + socket: WebSocketConnecting, + logger: ConsoleLogging, + socketUrlFallbackHandler: SocketUrlFallbackHandler) { + self.socket = socket + self.logger = logger + self.socketUrlFallbackHandler = socketUrlFallbackHandler + } func handleConnect() throws { socket.connect() + // Start a timer for the fallback mechanism + let timer = DispatchSource.makeTimerSource(queue: concurrentQueue) + timer.schedule(deadline: .now() + .seconds(defaultTimeout)) + timer.setEventHandler { [unowned self] in + if !self.socket.isConnected { + self.logger.debug("Connection timed out, initiating fallback...") + self.socketUrlFallbackHandler.handleFallbackIfNeeded(error: .connectionFailed) + } + timer.cancel() + } + timer.resume() } func handleDisconnect(closeCode: URLSessionWebSocketTask.CloseCode) throws { diff --git a/Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift b/Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift index 46d1ed5a0..dea30eecd 100644 --- a/Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift +++ b/Sources/WalletConnectRelay/SocketUrlFallbackHandler.swift @@ -3,19 +3,17 @@ import Foundation class SocketUrlFallbackHandler { private let relayUrlFactory: RelayUrlFactory private var logger: ConsoleLogging - private var socketConnectionHandler: SocketConnectionHandler private var socket: WebSocketConnecting private let networkMonitor: NetworkMonitoring + var onTryReconnect: (()->())? init( relayUrlFactory: RelayUrlFactory, logger: ConsoleLogging, - socketConnectionHandler: SocketConnectionHandler, socket: WebSocketConnecting, networkMonitor: NetworkMonitoring) { self.relayUrlFactory = relayUrlFactory self.logger = logger - self.socketConnectionHandler = socketConnectionHandler self.socket = socket self.networkMonitor = networkMonitor } @@ -25,9 +23,7 @@ class SocketUrlFallbackHandler { logger.debug("[WebSocket] - Fallback to \(NetworkConstants.fallbackUrl)") relayUrlFactory.setFallback() socket.request.url = relayUrlFactory.create() - Task(priority: .high) { - await self.socketConnectionHandler.tryReconect() - } + onTryReconnect?() } } } From 1d127e4ed813b97f4cb02197576170b2ec3b9900 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 2 May 2024 13:39:28 +0100 Subject: [PATCH 07/11] savepoint --- Example/DApp/SceneDelegate.swift | 1 + .../ManualSocketConnectionHandler.swift | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Example/DApp/SceneDelegate.swift b/Example/DApp/SceneDelegate.swift index c8abceae9..56cb86c11 100644 --- a/Example/DApp/SceneDelegate.swift +++ b/Example/DApp/SceneDelegate.swift @@ -50,6 +50,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { ) Sign.instance.logger.setLogging(level: .debug) + Networking.instance.setLogging(level: .debug) Sign.instance.logsPublisher.sink { log in switch log { diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift index 3a1b70aec..c81082ebc 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift @@ -8,7 +8,6 @@ class ManualSocketConnectionHandler: SocketConnectionHandler { private var socketUrlFallbackHandler: SocketUrlFallbackHandler private let concurrentQueue = DispatchQueue(label: "com.walletconnect.sdk.manual_socket_connection", attributes: .concurrent) - init( socket: WebSocketConnecting, logger: ConsoleLogging, @@ -16,6 +15,12 @@ class ManualSocketConnectionHandler: SocketConnectionHandler { self.socket = socket self.logger = logger self.socketUrlFallbackHandler = socketUrlFallbackHandler + + socketUrlFallbackHandler.onTryReconnect = { [unowned self] in + Task(priority: .high) { + await tryReconect() + } + } } func handleConnect() throws { From 369917d899de3a0fd16483a93108e2a78312d36f Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 2 May 2024 16:48:12 +0100 Subject: [PATCH 08/11] fix unit tests --- ...utomaticSocketConnectionHandlerTests.swift | 20 ++++++++++++++++++- Tests/RelayerTests/DispatcherTests.swift | 9 ++++++--- .../ManualSocketConnectionHandlerTests.swift | 18 ++++++++++++++++- Tests/RelayerTests/RelayClientTests.swift | 11 ---------- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift b/Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift index 12f7c1d94..b29a830ba 100644 --- a/Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift +++ b/Tests/RelayerTests/AutomaticSocketConnectionHandlerTests.swift @@ -13,12 +13,30 @@ final class AutomaticSocketConnectionHandlerTests: XCTestCase { webSocketSession = WebSocketMock() networkMonitor = NetworkMonitoringMock() appStateObserver = AppStateObserverMock() + let webSocket = WebSocketMock() + + let defaults = RuntimeKeyValueStorage() + let logger = ConsoleLoggerMock() + let keychainStorageMock = DispatcherKeychainStorageMock() + let clientIdStorage = ClientIdStorage(defaults: defaults, keychain: keychainStorageMock, logger: logger) + + + let socketAuthenticator = ClientIdAuthenticator(clientIdStorage: clientIdStorage) + let relayUrlFactory = RelayUrlFactory( + relayHost: "relay.walletconnect.com", + projectId: "1012db890cf3cfb0c1cdc929add657ba", + socketAuthenticator: socketAuthenticator + ) backgroundTaskRegistrar = BackgroundTaskRegistrarMock() + let socketUrlFallbackHandler = SocketUrlFallbackHandler(relayUrlFactory: relayUrlFactory, logger: ConsoleLoggerMock(), socket: webSocket, networkMonitor: networkMonitor) sut = AutomaticSocketConnectionHandler( socket: webSocketSession, networkMonitor: networkMonitor, appStateObserver: appStateObserver, - backgroundTaskRegistrar: backgroundTaskRegistrar) + backgroundTaskRegistrar: backgroundTaskRegistrar, + logger: ConsoleLoggerMock(), + socketUrlFallbackHandler: socketUrlFallbackHandler + ) } func testConnectsOnConnectionSatisfied() { diff --git a/Tests/RelayerTests/DispatcherTests.swift b/Tests/RelayerTests/DispatcherTests.swift index 331bd640d..4a58cfd97 100644 --- a/Tests/RelayerTests/DispatcherTests.swift +++ b/Tests/RelayerTests/DispatcherTests.swift @@ -5,7 +5,7 @@ import Combine import TestingUtils import Combine -private class DispatcherKeychainStorageMock: KeychainStorageProtocol { +class DispatcherKeychainStorageMock: KeychainStorageProtocol { func add(_ item: T, forKey key: String) throws where T : WalletConnectKMS.GenericPasswordConvertible {} func read(key: String) throws -> T where T : WalletConnectKMS.GenericPasswordConvertible { return try T(rawRepresentation: Data()) @@ -71,12 +71,15 @@ final class DispatcherTests: XCTestCase { projectId: "1012db890cf3cfb0c1cdc929add657ba", socketAuthenticator: socketAuthenticator ) + let socketUrlFallbackHandler = SocketUrlFallbackHandler(relayUrlFactory: relayUrlFactory, logger: logger, socket: webSocket, networkMonitor: networkMonitor) + let socketConnectionHandler = ManualSocketConnectionHandler(socket: webSocket, logger: logger, socketUrlFallbackHandler: socketUrlFallbackHandler) sut = Dispatcher( socketFactory: webSocketFactory, relayUrlFactory: relayUrlFactory, networkMonitor: networkMonitor, - socketConnectionType: .manual, - logger: ConsoleLoggerMock() + socket: webSocket, + logger: ConsoleLoggerMock(), + socketConnectionHandler: socketConnectionHandler ) } diff --git a/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift b/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift index 0b809b918..6f8a939cb 100644 --- a/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift +++ b/Tests/RelayerTests/ManualSocketConnectionHandlerTests.swift @@ -8,7 +8,23 @@ final class ManualSocketConnectionHandlerTests: XCTestCase { var networkMonitor: NetworkMonitoringMock! override func setUp() { socket = WebSocketMock() - sut = ManualSocketConnectionHandler(socket: socket) + + let defaults = RuntimeKeyValueStorage() + let logger = ConsoleLoggerMock() + let networkMonitor = NetworkMonitoringMock() + let keychainStorageMock = DispatcherKeychainStorageMock() + let clientIdStorage = ClientIdStorage(defaults: defaults, keychain: keychainStorageMock, logger: logger) + + + let socketAuthenticator = ClientIdAuthenticator(clientIdStorage: clientIdStorage) + let relayUrlFactory = RelayUrlFactory( + relayHost: "relay.walletconnect.com", + projectId: "1012db890cf3cfb0c1cdc929add657ba", + socketAuthenticator: socketAuthenticator + ) + let socketUrlFallbackHandler = SocketUrlFallbackHandler(relayUrlFactory: relayUrlFactory, logger: ConsoleLoggerMock(), socket: socket, networkMonitor: networkMonitor) + + sut = ManualSocketConnectionHandler(socket: socket, logger: ConsoleLoggerMock(), socketUrlFallbackHandler: socketUrlFallbackHandler) } func testHandleDisconnect() { diff --git a/Tests/RelayerTests/RelayClientTests.swift b/Tests/RelayerTests/RelayClientTests.swift index 5905e6e70..6442757cb 100644 --- a/Tests/RelayerTests/RelayClientTests.swift +++ b/Tests/RelayerTests/RelayClientTests.swift @@ -48,12 +48,6 @@ final class RelayClientTests: XCTestCase { XCTAssertNotNil(request) } - func testPublishRequest() async { - try? await sut.publish(topic: "", payload: "{}", tag: 0, prompt: false, ttl: 60) - let request = dispatcher.getLastRequestSent() - XCTAssertNotNil(request) - } - func testUnsubscribeRequest() { let topic = String.randomTopic() sut.subscriptions[topic] = "" @@ -77,11 +71,6 @@ final class RelayClientTests: XCTestCase { waitForExpectations(timeout: 0.1, handler: nil) } - func testSendOnPublish() async { - try? await sut.publish(topic: "", payload: "", tag: 0, prompt: false, ttl: 60) - XCTAssertTrue(dispatcher.sent) - } - func testSendOnSubscribe() async { try? await sut.subscribe(topic: "") XCTAssertTrue(dispatcher.sent) From fd02b0a3810285c48bc1fcbcfb303ba37b9aef37 Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Thu, 2 May 2024 16:56:42 +0100 Subject: [PATCH 09/11] fix relay tests --- .../RelayClientEndToEndTests.swift | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Example/RelayIntegrationTests/RelayClientEndToEndTests.swift b/Example/RelayIntegrationTests/RelayClientEndToEndTests.swift index 3ac8d75b7..eef6758de 100644 --- a/Example/RelayIntegrationTests/RelayClientEndToEndTests.swift +++ b/Example/RelayIntegrationTests/RelayClientEndToEndTests.swift @@ -42,15 +42,26 @@ final class RelayClientEndToEndTests: XCTestCase { projectId: InputConfig.projectId, socketAuthenticator: socketAuthenticator ) - let socket = WebSocket(url: urlFactory.create(fallback: false)) + let socket = WebSocket(url: urlFactory.create()) let webSocketFactory = WebSocketFactoryMock(webSocket: socket) let networkMonitor = NetworkMonitor() + + let relayUrlFactory = RelayUrlFactory( + relayHost: "relay.walletconnect.com", + projectId: "1012db890cf3cfb0c1cdc929add657ba", + socketAuthenticator: socketAuthenticator + ) + + let socketUrlFallbackHandler = SocketUrlFallbackHandler(relayUrlFactory: relayUrlFactory, logger: logger, socket: socket, networkMonitor: networkMonitor) + + let socketConnectionHandler = AutomaticSocketConnectionHandler(socket: socket, logger: logger, socketUrlFallbackHandler: socketUrlFallbackHandler) let dispatcher = Dispatcher( socketFactory: webSocketFactory, relayUrlFactory: urlFactory, networkMonitor: networkMonitor, - socketConnectionType: .manual, - logger: logger + socket: socket, + logger: logger, + socketConnectionHandler: socketConnectionHandler ) let keychain = KeychainStorageMock() let relayClient = RelayClientFactory.create( From 590394ca8778f5f569fef5188b25bdce7f2ec7ed Mon Sep 17 00:00:00 2001 From: Bartosz Rozwarski Date: Fri, 3 May 2024 10:24:12 +0100 Subject: [PATCH 10/11] fix continuation being missused --- Sources/WalletConnectRelay/Dispatching.swift | 1 - .../AutomaticSocketConnectionHandler.swift | 6 +++++- .../ManualSocketConnectionHandler.swift | 6 +++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Sources/WalletConnectRelay/Dispatching.swift b/Sources/WalletConnectRelay/Dispatching.swift index 32fac0135..a5c47ea17 100644 --- a/Sources/WalletConnectRelay/Dispatching.swift +++ b/Sources/WalletConnectRelay/Dispatching.swift @@ -72,7 +72,6 @@ final class Dispatcher: NSObject, Dispatching { var cancellable: AnyCancellable? cancellable = Publishers.CombineLatest(socketConnectionStatusPublisher, networkConnectionStatusPublisher) .filter { $0.0 == .connected && $0.1 == .connected } - .first() .setFailureType(to: NetworkError.self) .timeout(.seconds(defaultTimeout), scheduler: concurrentQueue, customError: { .connectionFailed }) .sink(receiveCompletion: { [unowned self] result in diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift index f7fe3ad99..eb070ec0f 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/AutomaticSocketConnectionHandler.swift @@ -56,7 +56,11 @@ class AutomaticSocketConnectionHandler { // Start a timer for the fallback mechanism let timer = DispatchSource.makeTimerSource(queue: concurrentQueue) timer.schedule(deadline: .now() + .seconds(defaultTimeout)) - timer.setEventHandler { [unowned self] in + timer.setEventHandler { [weak self] in + guard let self = self else { + timer.cancel() + return + } if !self.socket.isConnected { self.logger.debug("Connection timed out, initiating fallback...") self.socketUrlFallbackHandler.handleFallbackIfNeeded(error: .connectionFailed) diff --git a/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift b/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift index c81082ebc..d0589ca9e 100644 --- a/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift +++ b/Sources/WalletConnectRelay/SocketConnectionHandler/ManualSocketConnectionHandler.swift @@ -28,7 +28,11 @@ class ManualSocketConnectionHandler: SocketConnectionHandler { // Start a timer for the fallback mechanism let timer = DispatchSource.makeTimerSource(queue: concurrentQueue) timer.schedule(deadline: .now() + .seconds(defaultTimeout)) - timer.setEventHandler { [unowned self] in + timer.setEventHandler { [weak self] in + guard let self = self else { + timer.cancel() + return + } if !self.socket.isConnected { self.logger.debug("Connection timed out, initiating fallback...") self.socketUrlFallbackHandler.handleFallbackIfNeeded(error: .connectionFailed) From 4169e4b34a05dd2c411788eb18ea50e74aae7420 Mon Sep 17 00:00:00 2001 From: llbartekll Date: Fri, 3 May 2024 09:39:18 +0000 Subject: [PATCH 11/11] Set User Agent --- Sources/WalletConnectRelay/PackageConfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/WalletConnectRelay/PackageConfig.json b/Sources/WalletConnectRelay/PackageConfig.json index c4ccda17c..42dfeb72c 100644 --- a/Sources/WalletConnectRelay/PackageConfig.json +++ b/Sources/WalletConnectRelay/PackageConfig.json @@ -1 +1 @@ -{"version": "1.18.6"} +{"version": "1.18.7"}