diff options
7 files changed, 142 insertions, 200 deletions
diff --git a/ios/MullvadREST/RESTAccessTokenManager.swift b/ios/MullvadREST/RESTAccessTokenManager.swift index d8d7ef5521..a0fe79ba75 100644 --- a/ios/MullvadREST/RESTAccessTokenManager.swift +++ b/ios/MullvadREST/RESTAccessTokenManager.swift @@ -27,41 +27,35 @@ extension REST { accountNumber: String, completionHandler: @escaping (Result<REST.AccessTokenData, Swift.Error>) -> Void ) -> Cancellable { - let operation = ResultBlockOperation<REST.AccessTokenData>(dispatchQueue: dispatchQueue) + let operation = ResultBlockOperation<REST.AccessTokenData>( + dispatchQueue: dispatchQueue, + cancellableTask: { operation -> Cancellable in + if let tokenData = self.tokens[accountNumber], tokenData.expiry > Date() { + operation.finish(result: .success(tokenData)) + return AnyCancellable {} + } - operation.setExecutionBlock { operation in - if let tokenData = self.tokens[accountNumber], tokenData.expiry > Date() { - operation.finish(result: .success(tokenData)) - return - } + return self.proxy.getAccessToken(accountNumber: accountNumber, retryStrategy: .noRetry) { result in + self.dispatchQueue.async { + switch result { + case let .success(tokenData): + self.tokens[accountNumber] = tokenData - let task = self.proxy.getAccessToken( - accountNumber: accountNumber, - retryStrategy: .noRetry - ) { result in - self.dispatchQueue.async { - switch result { - case let .success(tokenData): - self.tokens[accountNumber] = tokenData + case let .failure(error) where !error.isOperationCancellationError: + self.logger.error( + error: error, + message: "Failed to fetch access token." + ) - case let .failure(error) where !error.isOperationCancellationError: - self.logger.error( - error: error, - message: "Failed to fetch access token." - ) + default: + break + } - default: - break + operation.finish(result: result) } - - operation.finish(result: result) } } - - operation.addCancellationBlock { - task.cancel() - } - } + ) operation.completionQueue = .main operation.completionHandler = completionHandler diff --git a/ios/MullvadVPN/AddressCacheTracker/AddressCacheTracker.swift b/ios/MullvadVPN/AddressCacheTracker/AddressCacheTracker.swift index 8c3100f92b..99c666e193 100644 --- a/ios/MullvadVPN/AddressCacheTracker/AddressCacheTracker.swift +++ b/ios/MullvadVPN/AddressCacheTracker/AddressCacheTracker.swift @@ -88,18 +88,14 @@ final class AddressCacheTracker { let operation = ResultBlockOperation<Bool> { operation in guard self.nextScheduleDate() <= Date() else { operation.finish(result: .success(false)) - return + return AnyCancellable {} } - let task = self.apiProxy.getAddressList(retryStrategy: .default) { result in + return self.apiProxy.getAddressList(retryStrategy: .default) { result in self.setEndpoints(from: result) operation.finish(result: result.map { _ in true }) } - - operation.addCancellationBlock { - task.cancel() - } } operation.completionQueue = .main diff --git a/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift b/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift index 75b73b2119..afc761ebf5 100644 --- a/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift +++ b/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift @@ -97,25 +97,18 @@ final class RelayCacheTracker { func updateRelays(completionHandler: ((Result<RelaysFetchResult, Error>) -> Void)? = nil) -> Cancellable { - let operation = ResultBlockOperation<RelaysFetchResult>(dispatchQueue: nil) { operation in + let operation = ResultBlockOperation<RelaysFetchResult>(cancellableTask: { operation in let cachedRelays = try? self.getCachedRelays() if self.getNextUpdateDate() > Date() { operation.finish(result: .success(.throttled)) - return + return AnyCancellable {} } - let task = self.apiProxy.getRelays( - etag: cachedRelays?.etag, - retryStrategy: .noRetry - ) { result in + return self.apiProxy.getRelays(etag: cachedRelays?.etag, retryStrategy: .noRetry) { result in operation.finish(result: self.handleResponse(result: result)) } - - operation.addCancellationBlock { - task.cancel() - } - } + }) operation.addObserver( BackgroundObserver( diff --git a/ios/MullvadVPN/StorePaymentManager/StorePaymentManager.swift b/ios/MullvadVPN/StorePaymentManager/StorePaymentManager.swift index 40e1b6b7e3..7e8545bea5 100644 --- a/ios/MullvadVPN/StorePaymentManager/StorePaymentManager.swift +++ b/ios/MullvadVPN/StorePaymentManager/StorePaymentManager.swift @@ -182,16 +182,9 @@ final class StorePaymentManager: NSObject, SKPaymentTransactionObserver { completionHandler: @escaping (StorePaymentManagerError?) -> Void ) { let accountOperation = ResultBlockOperation<REST.AccountData>(dispatchQueue: .main) { op in - let task = self.accountsProxy.getAccountData( - accountNumber: accountNumber, - retryStrategy: .default - ) { result in + return self.accountsProxy.getAccountData(accountNumber: accountNumber, retryStrategy: .default) { result in op.finish(result: result) } - - op.addCancellationBlock { - task.cancel() - } } accountOperation.addObserver(BackgroundObserver( diff --git a/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift b/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift index 4f99c86bdc..aa08234af3 100644 --- a/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift +++ b/ios/MullvadVPN/TunnelManager/SetAccountOperation.swift @@ -9,6 +9,7 @@ import Foundation import MullvadLogging import MullvadREST +import MullvadTypes import Operations import class WireGuardKitTypes.PrivateKey import class WireGuardKitTypes.PublicKey @@ -183,12 +184,10 @@ class SetAccountOperation: ResultOperation<StoredAccountData?> { } private func getCreateAccountOperation() -> ResultBlockOperation<StoredAccountData> { - let operation = ResultBlockOperation<StoredAccountData>(dispatchQueue: dispatchQueue) - - operation.setExecutionBlock { operation in + let operation = ResultBlockOperation<StoredAccountData>(dispatchQueue: dispatchQueue) { operation in self.logger.debug("Create new account...") - let task = self.accountsProxy.createAccount(retryStrategy: .default) { result in + return self.accountsProxy.createAccount(retryStrategy: .default) { result in let result = result.inspectError { error in guard !error.isOperationCancellationError else { return } @@ -208,53 +207,40 @@ class SetAccountOperation: ResultOperation<StoredAccountData?> { operation.finish(result: result) } - - operation.addCancellationBlock { - task.cancel() - } } return operation } - private func getExistingAccountOperation(accountNumber: String) - -> ResultOperation<StoredAccountData> - { - let operation = ResultBlockOperation<StoredAccountData>(dispatchQueue: dispatchQueue) - - operation.setExecutionBlock { operation in - self.logger.debug("Request account data...") - - let task = self.accountsProxy.getAccountData( - accountNumber: accountNumber, - retryStrategy: .default - ) { result in - let result = result.inspectError { error in - guard !error.isOperationCancellationError else { return } + private func getExistingAccountOperation(accountNumber: String) -> ResultOperation<StoredAccountData> { + return ResultBlockOperation<StoredAccountData>( + dispatchQueue: dispatchQueue, + cancellableTask: { operation -> Cancellable in + self.logger.debug("Request account data...") - self.logger.error( - error: error, - message: "Failed to receive account data." - ) - }.map { accountData -> StoredAccountData in - self.logger.debug("Received account data.") + return self.accountsProxy + .getAccountData(accountNumber: accountNumber, retryStrategy: .default) { result in + let result = result.inspectError { error in + guard !error.isOperationCancellationError else { return } - return StoredAccountData( - identifier: accountData.id, - number: accountNumber, - expiry: accountData.expiry - ) - } + self.logger.error( + error: error, + message: "Failed to receive account data." + ) + }.map { accountData -> StoredAccountData in + self.logger.debug("Received account data.") - operation.finish(result: result) - } + return StoredAccountData( + identifier: accountData.id, + number: accountNumber, + expiry: accountData.expiry + ) + } - operation.addCancellationBlock { - task.cancel() + operation.finish(result: result) + } } - } - - return operation + ) } private func getDeleteDeviceOperation() -> AsyncBlockOperation? { @@ -262,12 +248,10 @@ class SetAccountOperation: ResultOperation<StoredAccountData?> { return nil } - let operation = AsyncBlockOperation(dispatchQueue: dispatchQueue) - - operation.setExecutionBlock { operation in + let operation = AsyncBlockOperation(dispatchQueue: dispatchQueue, cancellableTask: { operation -> Cancellable in self.logger.debug("Delete current device...") - let task = self.devicesProxy.deleteDevice( + return self.devicesProxy.deleteDevice( accountNumber: accountData.number, identifier: deviceData.identifier, retryStrategy: .default @@ -288,11 +272,7 @@ class SetAccountOperation: ResultOperation<StoredAccountData?> { operation.finish(error: result.error) } - - operation.addCancellationBlock { - task.cancel() - } - } + }) return operation } @@ -334,68 +314,53 @@ class SetAccountOperation: ResultOperation<StoredAccountData?> { } } - private func getCreateDeviceOperation() - -> TransformOperation<StoredAccountData, (PrivateKey, REST.Device)> - { - let createDeviceOperation = TransformOperation< - StoredAccountData, - (PrivateKey, REST.Device) - >(dispatchQueue: dispatchQueue) - - createDeviceOperation.setExecutionBlock { storedAccountData, operation in - self.logger.debug("Store last used account.") - - do { - try SettingsManager.setLastUsedAccount(storedAccountData.number) - } catch { - self.logger.error( - error: error, - message: "Failed to store last used account number." - ) - } + private func getCreateDeviceOperation() -> TransformOperation<StoredAccountData, (PrivateKey, REST.Device)> { + let createDeviceOperation = TransformOperation<StoredAccountData, (PrivateKey, REST.Device)>( + dispatchQueue: dispatchQueue, + cancellableTask: { storedAccountData, operation -> Cancellable in + self.logger.debug("Store last used account.") - self.logger.debug("Create device...") + do { + try SettingsManager.setLastUsedAccount(storedAccountData.number) + } catch { + self.logger.error( + error: error, + message: "Failed to store last used account number." + ) + } - let privateKey = PrivateKey() + self.logger.debug("Create device...") - let request = REST.CreateDeviceRequest( - publicKey: privateKey.publicKey, - hijackDNS: false - ) + let privateKey = PrivateKey() - let task = self.devicesProxy.createDevice( - accountNumber: storedAccountData.number, - request: request, - retryStrategy: .default - ) { result in - let result = result - .map { device in - return (privateKey, device) - } - .inspectError { error in - self.logger.error(error: error, message: "Failed to create device.") - } + let request = REST.CreateDeviceRequest( + publicKey: privateKey.publicKey, + hijackDNS: false + ) - operation.finish(result: result) - } + return self.devicesProxy.createDevice( + accountNumber: storedAccountData.number, + request: request, + retryStrategy: .default + ) { result in + let result = result + .map { device in + return (privateKey, device) + } + .inspectError { error in + self.logger.error(error: error, message: "Failed to create device.") + } - operation.addCancellationBlock { - task.cancel() + operation.finish(result: result) + } } - } + ) return createDeviceOperation } - private func getSaveSettingsOperation() - -> TransformOperation<SetAccountResult, StoredAccountData> - { - let saveSettingsOperation = TransformOperation< - SetAccountResult, - StoredAccountData - >(dispatchQueue: dispatchQueue) - - saveSettingsOperation.setExecutionBlock { input in + private func getSaveSettingsOperation() -> TransformOperation<SetAccountResult, StoredAccountData> { + return TransformOperation<SetAccountResult, StoredAccountData>(dispatchQueue: dispatchQueue) { input in self.logger.debug("Saving settings...") let device = input.device @@ -421,7 +386,5 @@ class SetAccountOperation: ResultOperation<StoredAccountData?> { return input.accountData } - - return saveSettingsOperation } } diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift index 9c9190200b..47f7196ea4 100644 --- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift +++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift @@ -268,26 +268,24 @@ final class TunnelManager: StorePaymentObserver { } func reconnectTunnel(selectNewRelay: Bool, completionHandler: ((Error?) -> Void)? = nil) { - let operation = AsyncBlockOperation(dispatchQueue: internalQueue) { operation in + let operation = AsyncBlockOperation(dispatchQueue: internalQueue, cancellableTask: { operation -> Cancellable in guard let tunnel = self.tunnel else { operation.finish(error: UnsetTunnelError()) - return + return AnyCancellable {} } do { let selectorResult = selectNewRelay ? try self.selectRelay() : nil - let task = tunnel.reconnectTunnel(relaySelectorResult: selectorResult) { result in + return tunnel.reconnectTunnel(relaySelectorResult: selectorResult) { result in operation.finish(error: result.error) } - - operation.addCancellationBlock { - task.cancel() - } } catch { operation.finish(error: error) + + return AnyCancellable {} } - } + }) operation.completionBlock = { DispatchQueue.main.async { @@ -740,9 +738,10 @@ final class TunnelManager: StorePaymentObserver { @objc private func applicationDidBecomeActive(_ notification: Notification) { #if DEBUG - logger.debug("Refresh tunnel status due to application becoming active.") + logger.debug("Refresh device state and tunnel status due to application becoming active.") #endif refreshTunnelStatus() + refreshDeviceState() } fileprivate func selectRelay() throws -> RelaySelectorResult { @@ -819,6 +818,32 @@ final class TunnelManager: StorePaymentObserver { } } + private func refreshDeviceState() { + let operation = AsyncBlockOperation(dispatchQueue: internalQueue) { + do { + let newDeviceState = try SettingsManager.readDeviceState() + + self.setDeviceState(newDeviceState, persist: false) + } catch { + if let error = error as? KeychainError, error == .itemNotFound { + return + } + + self.logger.error(error: error, message: "Failed to refresh device state") + } + } + + operation.addCondition(MutuallyExclusive(category: OperationCategory.deviceStateUpdate.category)) + operation + .addObserver(BackgroundObserver( + application: application, + name: "Refresh device state", + cancelUponExpiration: true + )) + + operationQueue.addOperation(operation) + } + /// Update `TunnelStatus` from `NEVPNStatus`. /// Collects the `PacketTunnelStatus` from the tunnel via IPC if needed before assigning /// the `tunnelStatus`. diff --git a/ios/PacketTunnel/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider.swift index 380293695b..614626b2e8 100644 --- a/ios/PacketTunnel/PacketTunnelProvider.swift +++ b/ios/PacketTunnel/PacketTunnelProvider.swift @@ -585,7 +585,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider, TunnelMonitorDelegate { // Ignore all requests to reconnect once tunnel is preparing to stop. guard !isStopping else { return } - let blockOperation = AsyncBlockOperation(dispatchQueue: dispatchQueue) { operation in + let blockOperation = AsyncBlockOperation(dispatchQueue: dispatchQueue, block: { operation in if shouldStopTunnelMonitor { self.tunnelMonitor.stop() } @@ -594,7 +594,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider, TunnelMonitorDelegate { completionHandler?(error) operation.finish() } - } + }) if let reconnectTunnelTask = reconnectTunnelTask { blockOperation.addDependency(reconnectTunnelTask) @@ -779,49 +779,27 @@ class PacketTunnelProvider: NEPacketTunnelProvider, TunnelMonitorDelegate { operationQueue.addOperation(groupOperation) } - private func createGetAccountDataOperation(accountNumber: String) - -> ResultOperation<REST.AccountData> - { - let operation = ResultBlockOperation<REST.AccountData>( - dispatchQueue: dispatchQueue - ) - - operation.setExecutionBlock { operation in - let task = self.accountsProxy.getAccountData( - accountNumber: accountNumber, - retryStrategy: .noRetry - ) { result in + private func createGetAccountDataOperation(accountNumber: String) -> ResultOperation<REST.AccountData> { + return ResultBlockOperation<REST.AccountData>(dispatchQueue: dispatchQueue, cancellableTask: { operation in + return self.accountsProxy.getAccountData(accountNumber: accountNumber, retryStrategy: .noRetry) { result in operation.finish(result: result) } - - operation.addCancellationBlock { - task.cancel() - } - } - - return operation + }) } - private func createGetDeviceDataOperation(accountNumber: String, identifier: String) - -> ResultOperation<REST.Device> - { - let operation = ResultBlockOperation<REST.Device>(dispatchQueue: dispatchQueue) - - operation.setExecutionBlock { operation in - let task = self.devicesProxy.getDevice( + private func createGetDeviceDataOperation( + accountNumber: String, + identifier: String + ) -> ResultOperation<REST.Device> { + return ResultBlockOperation<REST.Device>(dispatchQueue: dispatchQueue, cancellableTask: { operation in + return self.devicesProxy.getDevice( accountNumber: accountNumber, identifier: identifier, retryStrategy: .noRetry ) { result in operation.finish(result: result) } - - operation.addCancellationBlock { - task.cancel() - } - } - - return operation + }) } } |
