diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2022-07-26 14:09:44 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2022-07-28 11:28:37 +0200 |
| commit | 695a95c5f55b17657f4f08d65da2e6368181a3d9 (patch) | |
| tree | 1727dab4445ca79baa850990013666be0c8561c6 | |
| parent | 12a019fab64167417f5d1f40bd1e106c80d213c7 (diff) | |
| download | mullvadvpn-695a95c5f55b17657f4f08d65da2e6368181a3d9.tar.xz mullvadvpn-695a95c5f55b17657f4f08d65da2e6368181a3d9.zip | |
AddressCache: drop error type
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 4 | ||||
| -rw-r--r-- | ios/MullvadVPN/AddressCache/AddressCacheStore.swift | 186 | ||||
| -rw-r--r-- | ios/MullvadVPN/AddressCache/AddressCacheStoreError.swift | 53 | ||||
| -rw-r--r-- | ios/MullvadVPN/AddressCache/AddressCacheTracker.swift | 41 |
4 files changed, 116 insertions, 168 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 75009f9de2..4922b11220 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ 5807E2C02432038B00F5FF30 /* String+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5807E2BF2432038B00F5FF30 /* String+Split.swift */; }; 5807E2C2243203D000F5FF30 /* StringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5807E2C1243203D000F5FF30 /* StringTests.swift */; }; 5807E2C3243203E700F5FF30 /* String+Split.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5807E2BF2432038B00F5FF30 /* String+Split.swift */; }; - 58095C4B2760B4F200890776 /* AddressCacheStoreError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58095C4A2760B4F200890776 /* AddressCacheStoreError.swift */; }; 58095C4F2760BA9100890776 /* AddressCacheStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58095C4E2760BA9100890776 /* AddressCacheStore.swift */; }; 58095C512760BBB500890776 /* AddressCacheTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58095C502760BBB400890776 /* AddressCacheTracker.swift */; }; 58095C532760EEC700890776 /* RESTNetworkOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58095C522760EEC700890776 /* RESTNetworkOperation.swift */; }; @@ -362,7 +361,6 @@ 5808273928487E3E006B77A4 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Base.xcconfig; sourceTree = "<group>"; }; 5808273B284888BC006B77A4 /* App.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = App.xcconfig; sourceTree = "<group>"; }; 5808273C284888E5006B77A4 /* PacketTunnel.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = PacketTunnel.xcconfig; sourceTree = "<group>"; }; - 58095C4A2760B4F200890776 /* AddressCacheStoreError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressCacheStoreError.swift; sourceTree = "<group>"; }; 58095C4E2760BA9100890776 /* AddressCacheStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressCacheStore.swift; sourceTree = "<group>"; }; 58095C502760BBB400890776 /* AddressCacheTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressCacheTracker.swift; sourceTree = "<group>"; }; 58095C522760EEC700890776 /* RESTNetworkOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTNetworkOperation.swift; sourceTree = "<group>"; }; @@ -638,7 +636,6 @@ children = ( 58FEAFB82750DA2F003C1625 /* AddressCache.swift */, 58095C4E2760BA9100890776 /* AddressCacheStore.swift */, - 58095C4A2760B4F200890776 /* AddressCacheStoreError.swift */, 58095C502760BBB400890776 /* AddressCacheTracker.swift */, ); path = AddressCache; @@ -1383,7 +1380,6 @@ 58F7CA882692E34000FC59FD /* WireguardKeysContentView.swift in Sources */, 58554F7B280B125F00013055 /* RESTAccountsProxy.swift in Sources */, 5801C9A527A14B2A0031566A /* TunnelManagerState.swift in Sources */, - 58095C4B2760B4F200890776 /* AddressCacheStoreError.swift in Sources */, 5868585524054096000B8131 /* AppButton.swift in Sources */, 58781CC922AE7CA8009B9D8E /* RelayConstraints.swift in Sources */, 584E96BC240FD4DA00D3334F /* Location.swift in Sources */, diff --git a/ios/MullvadVPN/AddressCache/AddressCacheStore.swift b/ios/MullvadVPN/AddressCache/AddressCacheStore.swift index 8ae790789e..2addeb2754 100644 --- a/ios/MullvadVPN/AddressCache/AddressCacheStore.swift +++ b/ios/MullvadVPN/AddressCache/AddressCacheStore.swift @@ -41,6 +41,14 @@ extension AddressCache { var source: CacheSource } + struct EmptyCacheError: LocalizedError { + let source: CacheSource + + var errorDescription: String? { + return "Address cache file from \(source) does not contain any API addresses." + } + } + class Store { static let shared: Store = { @@ -83,9 +91,46 @@ extension AddressCache { init(cacheFileURL: URL, prebundledCacheFileURL: URL) { self.cacheFileURL = cacheFileURL self.prebundledCacheFileURL = prebundledCacheFileURL - cachedAddresses = Self.defaultCachedAddresses - initializeStore() + do { + let readResult = try Self.readFromCacheLocationWithFallback( + cacheFileURL: cacheFileURL, + prebundledCacheFileURL: prebundledCacheFileURL, + logger: logger + ) + + switch readResult.source { + case .disk: + cachedAddresses = readResult.cachedAddresses + + case .bundle: + var addresses = readResult.cachedAddresses + addresses.endpoints.shuffle() + cachedAddresses = addresses + + logger.debug("Persist address list read from bundle.") + + do { + try writeToDisk() + } catch { + logger.error( + chainedError: AnyChainedError(error), + message: "Failed to persist address cache after reading it from bundle." + ) + } + } + + logger.debug( + """ + Initialized cache from \(readResult.source) with \ + \(cachedAddresses.endpoints.count) endpoint(s). + """ + ) + } catch { + logger.debug("Initialized cache with default API endpoint.") + + cachedAddresses = Self.defaultCachedAddresses + } } func getCurrentEndpoint() -> AnyIPEndpoint { @@ -123,12 +168,12 @@ extension AddressCache { return currentEndpoint } - func setEndpoints(_ endpoints: [AnyIPEndpoint]) throws { + func setEndpoints(_ endpoints: [AnyIPEndpoint]) { nslock.lock() defer { nslock.unlock() } guard !endpoints.isEmpty else { - throw StoreError.emptyAddressList + return } if Set(cachedAddresses.endpoints) == Set(endpoints) { @@ -150,7 +195,14 @@ extension AddressCache { ) } - try writeToDisk() + do { + try writeToDisk() + } catch { + logger.error( + chainedError: AnyChainedError(error), + message: "Failed to write address cache after setting new endpoints." + ) + } } func getLastUpdateDate() -> Date { @@ -160,85 +212,67 @@ extension AddressCache { return cachedAddresses.updatedAt } - private func initializeStore() { - let readResult: ReadResult + private static func readFromCacheLocationWithFallback( + cacheFileURL: URL, + prebundledCacheFileURL: URL, + logger: Logger + ) throws -> ReadResult + { do { - readResult = try readFromCacheLocationWithFallback() + let readResult = ReadResult( + cachedAddresses: try readFromCacheLocation(cacheFileURL), + source: .disk + ) + + try checkReadResultContainsEndpoints(readResult) + + return readResult } catch { logger.error( chainedError: AnyChainedError(error), - message: "Failed to read address cache. Fallback to default API endpoint." + message: "Failed to read address cache from disk. Fallback to pre-bundled cache." ) - cachedAddresses = Self.defaultCachedAddresses - - logger.debug("Initialized cache with default API endpoint.") - return - } - - guard !readResult.cachedAddresses.endpoints.isEmpty else { - logger.debug("Read empty cache from \(readResult.source). Fallback to default API endpoint.") - - cachedAddresses = Self.defaultCachedAddresses - - logger.debug("Initialized cache with default API endpoint.") - - return - } - - switch readResult.source { - case .disk: - cachedAddresses = readResult.cachedAddresses - - case .bundle: - var addresses = readResult.cachedAddresses - addresses.endpoints.shuffle() - cachedAddresses = addresses + do { + let readResult = ReadResult( + cachedAddresses: try readFromBundle(prebundledCacheFileURL), + source: .bundle + ) - logger.debug("Persist address list read from bundle.") + try checkReadResultContainsEndpoints(readResult) - do { - try writeToDisk() + return readResult } catch { logger.error( chainedError: AnyChainedError(error), - message: "Failed to persist address cache after reading it from bundle." + message: "Failed to read address cache from bundle." ) + + throw error } } - - logger.debug("Initialized cache from \(readResult.source) with \(cachedAddresses.endpoints.count) endpoint(s).") } - private func readFromCacheLocationWithFallback() throws -> ReadResult { - do { - return ReadResult( - cachedAddresses: try readFromCacheLocation(), - source: .disk - ) - } catch { - logger.error( - chainedError: AnyChainedError(error), - message: "Failed to read address cache from disk. Fallback to pre-bundled cache." - ) + private static func checkReadResultContainsEndpoints(_ readResult: ReadResult) throws { + if readResult.cachedAddresses.endpoints.isEmpty { + throw EmptyCacheError(source: readResult.source) } + } - return ReadResult( - cachedAddresses: try readFromBundle(), - source: .bundle - ) + private static func readFromCacheLocation(_ cacheFileURL: URL) throws -> CachedAddresses { + let data = try Data(contentsOf: cacheFileURL) + + return try JSONDecoder().decode(CachedAddresses.self, from: data) } - private func readFromCacheLocation() throws -> CachedAddresses { - do { - let data = try Data(contentsOf: cacheFileURL) + private static func readFromBundle(_ prebundledCacheFileURL: URL) throws -> CachedAddresses { + let data = try Data(contentsOf: prebundledCacheFileURL) + let endpoints = try JSONDecoder().decode([AnyIPEndpoint].self, from: data) - return try JSONDecoder().decode(CachedAddresses.self, from: data) - } catch let error as DecodingError { - throw StoreError.decodeCache(error) - } catch { - throw StoreError.readCache(error) - } + return CachedAddresses( + updatedAt: Date(timeIntervalSince1970: 0), + endpoints: endpoints + ) } private func writeToDisk() throws { @@ -250,30 +284,8 @@ extension AddressCache { attributes: nil ) - do { - let data = try JSONEncoder().encode(cachedAddresses) - try data.write(to: cacheFileURL, options: .atomic) - } catch let error as EncodingError { - throw StoreError.encodeCache(error) - } catch { - throw StoreError.writeCache(error) - } - } - - private func readFromBundle() throws -> CachedAddresses { - do { - let data = try Data(contentsOf: prebundledCacheFileURL) - let endpoints = try JSONDecoder().decode([AnyIPEndpoint].self, from: data) - - return CachedAddresses( - updatedAt: Date(timeIntervalSince1970: 0), - endpoints: endpoints - ) - } catch let error as DecodingError { - throw StoreError.decodeCacheFromBundle(error) - } catch { - throw StoreError.decodeCacheFromBundle(error) - } + let data = try JSONEncoder().encode(cachedAddresses) + try data.write(to: cacheFileURL, options: .atomic) } } diff --git a/ios/MullvadVPN/AddressCache/AddressCacheStoreError.swift b/ios/MullvadVPN/AddressCache/AddressCacheStoreError.swift deleted file mode 100644 index 98fc8179e0..0000000000 --- a/ios/MullvadVPN/AddressCache/AddressCacheStoreError.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// AddressCacheStoreError.swift -// MullvadVPN -// -// Created by pronebird on 08/12/2021. -// Copyright © 2021 Mullvad VPN AB. All rights reserved. -// - -import Foundation - -extension AddressCache { - enum StoreError: ChainedError { - /// Failure to read address cache. - case readCache(Swift.Error) - - /// Failure to read address cache from application bundle. - case readCacheFromBundle(Swift.Error) - - /// Failure to write address cache. - case writeCache(Swift.Error) - - /// Failure to decode address cache. - case decodeCache(Swift.Error) - - /// Failure to encode address cache. - case encodeCache(Swift.Error) - - /// Failure to decode address cache from application bundle. - case decodeCacheFromBundle(Swift.Error) - - /// Failure to update endpoints with empty address list. - case emptyAddressList - - var errorDescription: String? { - switch self { - case .readCache(_): - return "Cannot read address cache" - case .readCacheFromBundle(_): - return "Cannot read address cache from application bundle" - case .writeCache(_): - return "Cannot write address cache" - case .decodeCache(_): - return "Cannot decode address cache" - case .encodeCache(_): - return "Cannot encode address cache" - case .decodeCacheFromBundle(_): - return "Cannot decode address cache from application bundle" - case .emptyAddressList: - return "Cannot update endpoints with empty address list" - } - } - } -} diff --git a/ios/MullvadVPN/AddressCache/AddressCacheTracker.swift b/ios/MullvadVPN/AddressCache/AddressCacheTracker.swift index 70397032ca..430212c24d 100644 --- a/ios/MullvadVPN/AddressCache/AddressCacheTracker.swift +++ b/ios/MullvadVPN/AddressCache/AddressCacheTracker.swift @@ -104,9 +104,12 @@ extension AddressCache { } let task = self.apiProxy.getAddressList(retryStrategy: .default) { completion in - operation.finish( - completion: self.handleResponse(completion: completion) - ) + self.setEndpoints(from: completion) + + let mappedCompletion = completion.map { _ in true } + .eraseFailureType() + + operation.finish(completion: mappedCompletion) } operation.addCancellationBlock { @@ -133,36 +136,26 @@ extension AddressCache { return _nextScheduleDate() } - private func handleResponse( - completion: OperationCompletion<[AnyIPEndpoint], REST.Error> - ) -> OperationCompletion<Bool, Error> + private func setEndpoints(from completion: OperationCompletion<[AnyIPEndpoint], REST.Error>) { - let mappedCompletion = completion - .flatMapError { error -> OperationCompletion<[AnyIPEndpoint], REST.Error> in - if case URLError.cancelled = error { - return .cancelled - } else { - return .failure(error) - } - } - .tryMap { endpoints -> Bool in - try store.setEndpoints(endpoints) - - return true - } - nslock.lock() - lastFailureAttemptDate = mappedCompletion.isSuccess ? nil : Date() - nslock.unlock() + defer { nslock.unlock() } - if let error = mappedCompletion.error { + switch completion { + case .success(let endpoints): + store.setEndpoints(endpoints) + + case .failure(let error): logger.error( chainedError: AnyChainedError(error), message: "Failed to update address cache." ) + + case .cancelled: + break } - return mappedCompletion + lastFailureAttemptDate = completion.isSuccess ? nil : Date() } private func scheduleEndpointsUpdate(startTime: DispatchWallTime) { |
