diff options
| -rw-r--r-- | ios/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | ios/MullvadREST/Duration.swift | 62 | ||||
| -rw-r--r-- | ios/MullvadREST/ExponentialBackoff.swift | 54 | ||||
| -rw-r--r-- | ios/MullvadREST/RESTAPIProxy.swift | 1 | ||||
| -rw-r--r-- | ios/MullvadREST/RESTNetworkOperation.swift | 31 | ||||
| -rw-r--r-- | ios/MullvadREST/RESTRetryStrategy.swift | 80 | ||||
| -rw-r--r-- | ios/MullvadREST/ServerRelaysResponse.swift | 2 | ||||
| -rw-r--r-- | ios/MullvadREST/URLSessionTransport.swift | 32 | ||||
| -rw-r--r-- | ios/MullvadRESTTests/DurationTests.swift | 35 | ||||
| -rw-r--r-- | ios/MullvadRESTTests/ExponentialBackoffTests.swift | 59 | ||||
| -rw-r--r-- | ios/MullvadTypes/FixedWidthInteger+Arithmetics.swift (renamed from ios/PacketTunnel/FixedWidthInteger+Arithmetics.swift) | 8 | ||||
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/project.pbxproj | 136 | ||||
| -rw-r--r-- | ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme | 10 | ||||
| -rw-r--r-- | ios/MullvadVPN/TransportMonitor/TransportMonitor.swift | 4 | ||||
| -rw-r--r-- | ios/PacketTunnel/TunnelMonitor/TunnelMonitor.swift | 1 |
15 files changed, 474 insertions, 42 deletions
diff --git a/ios/CHANGELOG.md b/ios/CHANGELOG.md index 73eca63dda..3a60a96ab9 100644 --- a/ios/CHANGELOG.md +++ b/ios/CHANGELOG.md @@ -39,6 +39,7 @@ Line wrap the file at 100 chars. Th ### Changed - When logged into an account with no time left, a new view is shown instead of account settings, with the option to buy more time. +- Use exponential backoff with jitter for delay interval when retrying REST API requests. ### Fixed - Improve random port distribution. Should be less biased towards port 53. diff --git a/ios/MullvadREST/Duration.swift b/ios/MullvadREST/Duration.swift new file mode 100644 index 0000000000..d773d944c1 --- /dev/null +++ b/ios/MullvadREST/Duration.swift @@ -0,0 +1,62 @@ +// +// Duration.swift +// MullvadREST +// +// Created by pronebird on 04/11/2022. +// Copyright © 2022 Mullvad VPN AB. All rights reserved. +// + +import Foundation + +extension REST { + public struct Duration: Comparable { + public let milliseconds: UInt64 + + public var seconds: UInt64 { + return milliseconds / 1000 + } + + public var timeInterval: TimeInterval { + return TimeInterval(milliseconds) / 1000 + } + + private init(milliseconds: UInt64) { + self.milliseconds = milliseconds + } + + public func format() -> String { + guard milliseconds >= 1000 else { + return "\(milliseconds)ms" + } + + let trailingZeroesSuffix = ".00" + var string = String(format: "%.2f", timeInterval) + + if string.hasSuffix(trailingZeroesSuffix) { + string.removeLast(trailingZeroesSuffix.count) + } + + return "\(string)s" + } + + public static func seconds(_ seconds: UInt64) -> Duration { + return Duration(milliseconds: seconds.saturatingMultiplication(1000)) + } + + public static func milliseconds(_ milliseconds: UInt64) -> Duration { + return Duration(milliseconds: milliseconds) + } + + public static func < (lhs: Duration, rhs: Duration) -> Bool { + return lhs.milliseconds < rhs.milliseconds + } + + public static func == (lhs: Duration, rhs: Duration) -> Bool { + return lhs.milliseconds == rhs.milliseconds + } + + public static func * (lhs: Duration, factor: UInt64) -> Duration { + return Duration(milliseconds: lhs.milliseconds.saturatingMultiplication(factor)) + } + } +} diff --git a/ios/MullvadREST/ExponentialBackoff.swift b/ios/MullvadREST/ExponentialBackoff.swift new file mode 100644 index 0000000000..a765d53011 --- /dev/null +++ b/ios/MullvadREST/ExponentialBackoff.swift @@ -0,0 +1,54 @@ +// +// ExponentialBackoff.swift +// MullvadREST +// +// Created by pronebird on 03/11/2022. +// Copyright © 2022 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import MullvadTypes + +struct ExponentialBackoff: IteratorProtocol { + private var _next: REST.Duration + private let multiplier: UInt64 + private let maxDelay: REST.Duration? + + init(initial: REST.Duration, multiplier: UInt64, maxDelay: REST.Duration? = nil) { + _next = initial + self.multiplier = multiplier + self.maxDelay = maxDelay + } + + mutating func next() -> REST.Duration? { + let next = _next + + if let maxDelay = maxDelay, next > maxDelay { + return maxDelay + } + + _next = next * multiplier + + return next + } +} + +struct Jittered<InnerIterator: IteratorProtocol>: IteratorProtocol + where InnerIterator.Element == REST.Duration +{ + private var inner: InnerIterator + + init(_ inner: InnerIterator) { + self.inner = inner + } + + mutating func next() -> REST.Duration? { + guard let interval = inner.next() else { return nil } + + let jitter = Double.random(in: 0.0 ... 1.0) + let millis = interval.milliseconds + let millisWithJitter = millis.saturatingAddition(UInt64(Double(millis) * jitter)) + + return .milliseconds(millisWithJitter) + } +} diff --git a/ios/MullvadREST/RESTAPIProxy.swift b/ios/MullvadREST/RESTAPIProxy.swift index 2cf3ee4c86..5defa28a24 100644 --- a/ios/MullvadREST/RESTAPIProxy.swift +++ b/ios/MullvadREST/RESTAPIProxy.swift @@ -8,7 +8,6 @@ import Foundation import MullvadTypes -import Network import struct WireGuardKitTypes.IPAddressRange import class WireGuardKitTypes.PublicKey diff --git a/ios/MullvadREST/RESTNetworkOperation.swift b/ios/MullvadREST/RESTNetworkOperation.swift index 0db21bfd05..3e188094c9 100644 --- a/ios/MullvadREST/RESTNetworkOperation.swift +++ b/ios/MullvadREST/RESTNetworkOperation.swift @@ -27,6 +27,7 @@ extension REST { private var retryInvalidAccessTokenError = true private let retryStrategy: RetryStrategy + private var retryDelayIterator: AnyIterator<Duration> private var retryTimer: DispatchSourceTimer? private var retryCount = 0 @@ -42,6 +43,7 @@ extension REST { addressCacheStore = configuration.addressCacheStore transportRegistry = configuration.transportRegistry self.retryStrategy = retryStrategy + retryDelayIterator = retryStrategy.makeDelayIterator() self.requestHandler = requestHandler self.responseHandler = responseHandler @@ -270,23 +272,28 @@ extension REST { if retryStrategy.maxRetryCount > 0 { logger.debug("Ran out of retry attempts (\(retryStrategy.maxRetryCount))") } - - let restError: REST.Error = (error as? URLError).map { .network($0) } - ?? .transport(error) - - finish(completion: .failure(restError)) + finish(completion: .failure(wrapRequestError(error))) return } // Increment retry count. retryCount += 1 - // Retry immediatly if retry delay is set to never. - guard retryStrategy.retryDelay != .never else { + // Retry immediately if retry delay is set to never. + guard retryStrategy.delay != .never else { startRequest() return } + guard let waitDelay = retryDelayIterator.next() else { + logger.debug("Retry delay iterator failed to produce next value.") + + finish(completion: .failure(wrapRequestError(error))) + return + } + + logger.debug("Retry in \(waitDelay.format()).") + // Create timer to delay retry. let timer = DispatchSource.makeTimerSource(queue: dispatchQueue) @@ -298,10 +305,18 @@ extension REST { self?.finish(completion: .cancelled) } - timer.schedule(wallDeadline: .now() + retryStrategy.retryDelay) + timer.schedule(wallDeadline: .now() + waitDelay.timeInterval) timer.activate() retryTimer = timer } + + private func wrapRequestError(_ error: Swift.Error) -> REST.Error { + if let error = error as? URLError { + return .network(error) + } else { + return .transport(error) + } + } } } diff --git a/ios/MullvadREST/RESTRetryStrategy.swift b/ios/MullvadREST/RESTRetryStrategy.swift index e88ebf072d..a3b4873de1 100644 --- a/ios/MullvadREST/RESTRetryStrategy.swift +++ b/ios/MullvadREST/RESTRetryStrategy.swift @@ -11,15 +11,83 @@ import Foundation extension REST { public struct RetryStrategy { public var maxRetryCount: Int - public var retryDelay: DispatchTimeInterval + public var delay: RetryDelay + public var applyJitter: Bool + + public init(maxRetryCount: Int, delay: RetryDelay, applyJitter: Bool) { + self.maxRetryCount = maxRetryCount + self.delay = delay + self.applyJitter = applyJitter + } + + public func makeDelayIterator() -> AnyIterator<Duration> { + let inner = delay.makeIterator() + + if applyJitter { + return AnyIterator(Jittered(inner)) + } else { + return AnyIterator(inner) + } + } /// Strategy configured to never retry. - public static var noRetry = RetryStrategy(maxRetryCount: 0, retryDelay: .never) + public static var noRetry = RetryStrategy( + maxRetryCount: 0, + delay: .never, + applyJitter: false + ) + + /// Startegy configured with 3 retry attempts and exponential backoff. + public static var `default` = RetryStrategy( + maxRetryCount: 3, + delay: defaultRetryDelay, + applyJitter: true + ) + + /// Strategy configured with 10 retry attempts and exponential backoff. + public static var aggressive = RetryStrategy( + maxRetryCount: 10, + delay: defaultRetryDelay, + applyJitter: true + ) + + /// Default retry delay. + public static var defaultRetryDelay: RetryDelay = .exponentialBackoff( + initial: .seconds(2), + multiplier: 2, + maxDelay: .seconds(8) + ) + } + + public enum RetryDelay: Equatable { + /// Never wait to retry. + case never + + /// Constant delay. + case constant(Duration) + + /// Exponential backoff. + case exponentialBackoff(initial: Duration, multiplier: UInt64, maxDelay: Duration?) + + func makeIterator() -> AnyIterator<Duration> { + switch self { + case .never: + return AnyIterator { + return nil + } - /// Startegy configured with 3 retry attempts with 2 seconds delay between. - public static var `default` = RetryStrategy(maxRetryCount: 3, retryDelay: .seconds(2)) + case let .constant(duration): + return AnyIterator { + return duration + } - /// Strategy configured with 10 retry attempts with 2 seconds delay between. - public static var aggressive = RetryStrategy(maxRetryCount: 10, retryDelay: .seconds(2)) + case let .exponentialBackoff(initial, multiplier, maxDelay): + return AnyIterator(ExponentialBackoff( + initial: initial, + multiplier: multiplier, + maxDelay: maxDelay + )) + } + } } } diff --git a/ios/MullvadREST/ServerRelaysResponse.swift b/ios/MullvadREST/ServerRelaysResponse.swift index be446cfbf0..1327b596af 100644 --- a/ios/MullvadREST/ServerRelaysResponse.swift +++ b/ios/MullvadREST/ServerRelaysResponse.swift @@ -1,6 +1,6 @@ // // ServerRelaysResponse.swift -// ServerRelaysResponse +// MullvadREST // // Created by pronebird on 27/07/2021. // Copyright © 2021 Mullvad VPN AB. All rights reserved. diff --git a/ios/MullvadREST/URLSessionTransport.swift b/ios/MullvadREST/URLSessionTransport.swift index f8bd4cca24..4acc155d58 100644 --- a/ios/MullvadREST/URLSessionTransport.swift +++ b/ios/MullvadREST/URLSessionTransport.swift @@ -11,23 +11,25 @@ import MullvadTypes extension URLSessionTask: Cancellable {} -public final class URLSessionTransport: RESTTransport { - public var name: String { - return "url-session" - } +extension REST { + public final class URLSessionTransport: RESTTransport { + public var name: String { + return "url-session" + } - public let urlSession: URLSession + public let urlSession: URLSession - public init(urlSession: URLSession) { - self.urlSession = urlSession - } + public init(urlSession: URLSession) { + self.urlSession = urlSession + } - public func sendRequest( - _ request: URLRequest, - completion: @escaping (Data?, URLResponse?, Error?) -> Void - ) throws -> Cancellable { - let dataTask = urlSession.dataTask(with: request, completionHandler: completion) - dataTask.resume() - return dataTask + public func sendRequest( + _ request: URLRequest, + completion: @escaping (Data?, URLResponse?, Swift.Error?) -> Void + ) throws -> Cancellable { + let dataTask = urlSession.dataTask(with: request, completionHandler: completion) + dataTask.resume() + return dataTask + } } } diff --git a/ios/MullvadRESTTests/DurationTests.swift b/ios/MullvadRESTTests/DurationTests.swift new file mode 100644 index 0000000000..c5b5906c02 --- /dev/null +++ b/ios/MullvadRESTTests/DurationTests.swift @@ -0,0 +1,35 @@ +// +// DurationTests.swift +// MullvadRESTTests +// +// Created by pronebird on 05/11/2022. +// Copyright © 2022 Mullvad VPN AB. All rights reserved. +// + +@testable import MullvadREST +import XCTest + +final class DurationTests: XCTestCase { + func testComparable() throws { + XCTAssertEqual(REST.Duration.milliseconds(1000), .seconds(1)) + XCTAssertEqual(REST.Duration.milliseconds(.max), .seconds(.max)) + + XCTAssertGreaterThan(REST.Duration.milliseconds(1001), .seconds(1)) + XCTAssertGreaterThanOrEqual(REST.Duration.seconds(1), .milliseconds(1000)) + + XCTAssertLessThan(REST.Duration.milliseconds(999), .seconds(1)) + XCTAssertLessThanOrEqual(REST.Duration.seconds(1), .milliseconds(1000)) + } + + func testMultiplication() throws { + XCTAssertEqual(REST.Duration.seconds(4) * 4, .seconds(16)) + XCTAssertEqual(REST.Duration.seconds(4) * 4, .seconds(16)) + XCTAssertEqual(REST.Duration.milliseconds(.max - 1) * 2, .milliseconds(.max)) + } + + func testFormat() throws { + XCTAssertEqual(REST.Duration.milliseconds(999).format(), "999ms") + XCTAssertEqual(REST.Duration.milliseconds(1000).format(), "1s") + XCTAssertEqual(REST.Duration.milliseconds(1200).format(), "1.20s") + } +} diff --git a/ios/MullvadRESTTests/ExponentialBackoffTests.swift b/ios/MullvadRESTTests/ExponentialBackoffTests.swift new file mode 100644 index 0000000000..85529e260a --- /dev/null +++ b/ios/MullvadRESTTests/ExponentialBackoffTests.swift @@ -0,0 +1,59 @@ +// +// ExponentialBackoffTests.swift +// ExponentialBackoffTests +// +// Created by pronebird on 05/11/2022. +// Copyright © 2022 Mullvad VPN AB. All rights reserved. +// + +@testable import MullvadREST +import XCTest + +final class ExponentialBackoffTests: XCTestCase { + func testExponentialBackoff() { + var backoff = ExponentialBackoff(initial: .seconds(2), multiplier: 3) + + XCTAssertEqual(backoff.next(), .seconds(2)) + XCTAssertEqual(backoff.next(), .seconds(6)) + XCTAssertEqual(backoff.next(), .seconds(18)) + } + + func testAtMaximumValue() { + var backoff = ExponentialBackoff(initial: .milliseconds(.max - 1), multiplier: 2) + + XCTAssertEqual(backoff.next(), .milliseconds(.max - 1)) + XCTAssertEqual(backoff.next(), .seconds(.max)) + XCTAssertEqual(backoff.next(), .seconds(.max)) + } + + func testMaximumBound() { + var backoff = ExponentialBackoff( + initial: .milliseconds(2), + multiplier: 3, + maxDelay: .milliseconds(7) + ) + + XCTAssertEqual(backoff.next(), .milliseconds(2)) + XCTAssertEqual(backoff.next(), .milliseconds(6)) + XCTAssertEqual(backoff.next(), .milliseconds(7)) + } + + func testMinimumValue() { + var backoff = ExponentialBackoff(initial: .milliseconds(0), multiplier: 10) + + XCTAssertEqual(backoff.next(), .milliseconds(0)) + XCTAssertEqual(backoff.next(), .milliseconds(0)) + + backoff = ExponentialBackoff(initial: .milliseconds(1), multiplier: 0) + + XCTAssertEqual(backoff.next(), .milliseconds(1)) + XCTAssertEqual(backoff.next(), .milliseconds(0)) + } + + func testJitter() { + let initial = REST.Duration.milliseconds(500) + var iterator = Jittered(ExponentialBackoff(initial: initial, multiplier: 3)) + + XCTAssertGreaterThanOrEqual(iterator.next()!, initial) + } +} diff --git a/ios/PacketTunnel/FixedWidthInteger+Arithmetics.swift b/ios/MullvadTypes/FixedWidthInteger+Arithmetics.swift index 71d1ace4d2..31913775b1 100644 --- a/ios/PacketTunnel/FixedWidthInteger+Arithmetics.swift +++ b/ios/MullvadTypes/FixedWidthInteger+Arithmetics.swift @@ -11,7 +11,7 @@ import Foundation extension FixedWidthInteger { /// Saturating integer multiplication. Computes `self * rhs`, saturating at the numeric bounds /// instead of overflowing. - func saturatingMultiplication(_ rhs: Self) -> Self { + public func saturatingMultiplication(_ rhs: Self) -> Self { let (partialValue, isOverflow) = multipliedReportingOverflow(by: rhs) if isOverflow { @@ -23,7 +23,7 @@ extension FixedWidthInteger { /// Saturating integer addition. Computes `self + rhs`, saturating at the numeric bounds /// instead of overflowing. - func saturatingAddition(_ rhs: Self) -> Self { + public func saturatingAddition(_ rhs: Self) -> Self { let (partialValue, isOverflow) = addingReportingOverflow(rhs) if isOverflow { @@ -35,7 +35,7 @@ extension FixedWidthInteger { /// Saturating integer subtraction. Computes `self - rhs`, saturating at the numeric bounds /// instead of overflowing. - func saturatingSubtraction(_ rhs: Self) -> Self { + public func saturatingSubtraction(_ rhs: Self) -> Self { let (partialValue, isOverflow) = subtractingReportingOverflow(rhs) if isOverflow { @@ -47,7 +47,7 @@ extension FixedWidthInteger { /// Saturating integer exponentiation. Computes `self ** exp`, saturating at the numeric /// bounds instead of overflowing. - func saturatingPow(_ exp: UInt32) -> Self { + public func saturatingPow(_ exp: UInt32) -> Self { let result = pow(Double(self), Double(exp)) if result.isFinite { diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index b8d6b404c8..a1cea3ff0a 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -101,7 +101,6 @@ 58293FB3251241B4005D0BB5 /* CustomTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58293FB2251241B3005D0BB5 /* CustomTextView.swift */; }; 58293FB725138B88005D0BB5 /* CustomNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58293FB625138B88005D0BB5 /* CustomNavigationController.swift */; }; 582A8A3A28BCE19B00D0F9FB /* FixedWidthIntegerArithmeticsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582A8A3928BCE19B00D0F9FB /* FixedWidthIntegerArithmeticsTests.swift */; }; - 582A8A3B28BCE1AB00D0F9FB /* FixedWidthInteger+Arithmetics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58900D0228BBDCC70094E4F0 /* FixedWidthInteger+Arithmetics.swift */; }; 582AE3102440A6CA00E6733A /* AccountTokenInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582AE30F2440A6CA00E6733A /* AccountTokenInput.swift */; }; 582AE3122440CA0D00E6733A /* AccountTokenInputTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582AE3112440CA0D00E6733A /* AccountTokenInputTests.swift */; }; 582AE3132440CA2700E6733A /* AccountTokenInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 582AE30F2440A6CA00E6733A /* AccountTokenInput.swift */; }; @@ -193,7 +192,6 @@ 5888AD83227B11080051EB06 /* SelectLocationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5888AD82227B11080051EB06 /* SelectLocationCell.swift */; }; 5888AD87227B17950051EB06 /* SelectLocationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5888AD86227B17950051EB06 /* SelectLocationViewController.swift */; }; 588E4EAE28FEEDD8008046E3 /* MullvadREST.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06799ABC28F98E1D00ACD94E /* MullvadREST.framework */; }; - 58900D0328BBDCC70094E4F0 /* FixedWidthInteger+Arithmetics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58900D0228BBDCC70094E4F0 /* FixedWidthInteger+Arithmetics.swift */; }; 58906DE02445C7A5002F0673 /* NEProviderStopReason+Debug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */; }; 58907D9524D17B4E00CFC3F5 /* DisconnectSplitButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58907D9424D17B4E00CFC3F5 /* DisconnectSplitButton.swift */; }; 5891BF1C25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5891BF1B25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift */; }; @@ -205,6 +203,8 @@ 5896AE86246D6AD8005B36CB /* CustomDateComponentsFormattingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5896AE85246D6AD8005B36CB /* CustomDateComponentsFormattingTests.swift */; }; 5896AE88246D7FAF005B36CB /* CustomDateComponentsFormatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5896AE83246D5889005B36CB /* CustomDateComponentsFormatting.swift */; }; 5896CEF226972DEB00B0FAE8 /* AccountContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5896CEF126972DEB00B0FAE8 /* AccountContentView.swift */; }; + 5897F1742913EAF800AF5695 /* ExponentialBackoff.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5897F1732913EAF800AF5695 /* ExponentialBackoff.swift */; }; + 5897F1762914E62E00AF5695 /* Duration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5897F1752914E62E00AF5695 /* Duration.swift */; }; 5898D29029017BEE00EB5EBA /* PacketTunnelOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 587C575226D2615F005EF767 /* PacketTunnelOptions.swift */; }; 5898D29129017C3100EB5EBA /* TunnelProviderMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585DA89226B0323E00B8C587 /* TunnelProviderMessage.swift */; }; 5898D29229017CA000EB5EBA /* ProxyURLRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 063687AF28EB083800BE7161 /* ProxyURLRequest.swift */; }; @@ -231,6 +231,7 @@ 589A455F28E094BF00565204 /* OperationConditionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 580CBFB72848D503007878F0 /* OperationConditionTests.swift */; }; 58A1AA8C23F5584C009F7EA6 /* ConnectionPanelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */; }; 58A3BDB028A1821A00C8C2C6 /* WgStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A3BDAF28A1821A00C8C2C6 /* WgStats.swift */; }; + 58A8B0842913C6F7004B59B1 /* FixedWidthInteger+Arithmetics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58900D0228BBDCC70094E4F0 /* FixedWidthInteger+Arithmetics.swift */; }; 58A99ED3240014A0006599E9 /* TermsOfServiceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58A99ED2240014A0006599E9 /* TermsOfServiceViewController.swift */; }; 58AC829428F803A200181C40 /* libMullvadLogging.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 581943D628F800C900B0CB5E /* libMullvadLogging.a */; }; 58AC829528F803A200181C40 /* libMullvadTypes.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 581943F128F8014500B0CB5E /* libMullvadTypes.a */; }; @@ -303,6 +304,9 @@ 58F8AC0E25D3F8CE002BE0ED /* ProblemReportReviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58F8AC0D25D3F8CE002BE0ED /* ProblemReportReviewViewController.swift */; }; 58FB865526E8BF3100F188BC /* StorePaymentManagerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865426E8BF3100F188BC /* StorePaymentManagerError.swift */; }; 58FB865A26EA214400F188BC /* RelayCacheTrackerObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FB865926EA214400F188BC /* RelayCacheTrackerObserver.swift */; }; + 58FBFBE9291622580020E046 /* ExponentialBackoffTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FBFBE8291622580020E046 /* ExponentialBackoffTests.swift */; }; + 58FBFBEA291622580020E046 /* MullvadREST.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06799ABC28F98E1D00ACD94E /* MullvadREST.framework */; }; + 58FBFBF1291630700020E046 /* DurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FBFBF0291630700020E046 /* DurationTests.swift */; }; 58FC040A27B3EE03001C21F0 /* TunnelMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FC040927B3EE03001C21F0 /* TunnelMonitor.swift */; }; 58FD5BE724192A2C00112C88 /* StoreReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD5BE624192A2B00112C88 /* StoreReceipt.swift */; }; 58FD5BF024238EB300112C88 /* SKProduct+Formatting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */; }; @@ -433,6 +437,13 @@ remoteGlobalIDString = 58FBDA9722A519BC00EB69A3; remoteInfo = WireGuardGoBridge; }; + 58FBFBEB291622580020E046 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 06799ABB28F98E1D00ACD94E; + remoteInfo = MullvadREST; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -691,6 +702,8 @@ 5896AE83246D5889005B36CB /* CustomDateComponentsFormatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDateComponentsFormatting.swift; sourceTree = "<group>"; }; 5896AE85246D6AD8005B36CB /* CustomDateComponentsFormattingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomDateComponentsFormattingTests.swift; sourceTree = "<group>"; }; 5896CEF126972DEB00B0FAE8 /* AccountContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountContentView.swift; sourceTree = "<group>"; }; + 5897F1732913EAF800AF5695 /* ExponentialBackoff.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExponentialBackoff.swift; sourceTree = "<group>"; }; + 5897F1752914E62E00AF5695 /* Duration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Duration.swift; sourceTree = "<group>"; }; 5898D28929017BD400EB5EBA /* libTunnelProviderMessaging.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTunnelProviderMessaging.a; sourceTree = BUILT_PRODUCTS_DIR; }; 5898D29829017DAC00EB5EBA /* libRelaySelector.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRelaySelector.a; sourceTree = BUILT_PRODUCTS_DIR; }; 5898D2A7290182B000EB5EBA /* TunnelProviderReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelProviderReply.swift; sourceTree = "<group>"; }; @@ -784,6 +797,9 @@ 58F8AC0D25D3F8CE002BE0ED /* ProblemReportReviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportReviewViewController.swift; sourceTree = "<group>"; }; 58FB865426E8BF3100F188BC /* StorePaymentManagerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorePaymentManagerError.swift; sourceTree = "<group>"; }; 58FB865926EA214400F188BC /* RelayCacheTrackerObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayCacheTrackerObserver.swift; sourceTree = "<group>"; }; + 58FBFBE6291622580020E046 /* MullvadRESTTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MullvadRESTTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 58FBFBE8291622580020E046 /* ExponentialBackoffTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExponentialBackoffTests.swift; sourceTree = "<group>"; }; + 58FBFBF0291630700020E046 /* DurationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DurationTests.swift; sourceTree = "<group>"; }; 58FC040927B3EE03001C21F0 /* TunnelMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelMonitor.swift; sourceTree = "<group>"; }; 58FD5BE624192A2B00112C88 /* StoreReceipt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreReceipt.swift; sourceTree = "<group>"; }; 58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SKProduct+Formatting.swift"; sourceTree = "<group>"; }; @@ -916,6 +932,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 58FBFBE3291622580020E046 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 58FBFBEA291622580020E046 /* MullvadREST.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -972,6 +996,8 @@ 06FAE67428F83CA40033DD93 /* RESTRequestHandler.swift */, 06FAE66628F83CA30033DD93 /* RESTResponseHandler.swift */, 06FAE67628F83CA40033DD93 /* RESTRetryStrategy.swift */, + 5897F1752914E62E00AF5695 /* Duration.swift */, + 5897F1732913EAF800AF5695 /* ExponentialBackoff.swift */, 06FAE67528F83CA40033DD93 /* RESTTaskIdentifier.swift */, 06FAE67D28F83CA50033DD93 /* RESTTransport.swift */, 06FAE66D28F83CA40033DD93 /* RESTTransportRegistry.swift */, @@ -1029,6 +1055,7 @@ 5898D2AF2902A67C00EB5EBA /* RelayLocation.swift */, 585DA89826B0329200B8C587 /* PacketTunnelStatus.swift */, 5898D2B62902A9EA00EB5EBA /* PacketTunnelRelay.swift */, + 58900D0228BBDCC70094E4F0 /* FixedWidthInteger+Arithmetics.swift */, ); path = MullvadTypes; sourceTree = "<group>"; @@ -1194,6 +1221,7 @@ 581943D728F800C900B0CB5E /* MullvadLogging */, 581943F228F8014500B0CB5E /* MullvadTypes */, 06799ABD28F98E1D00ACD94E /* MullvadREST */, + 58FBFBE7291622580020E046 /* MullvadRESTTests */, 063F02742902B63F001FA09F /* RelayCache */, 5898D29929017DAC00EB5EBA /* RelaySelector */, 5898D28A29017BD400EB5EBA /* TunnelProviderMessaging */, @@ -1220,6 +1248,7 @@ 063F02732902B63F001FA09F /* RelayCache.framework */, 5898D28929017BD400EB5EBA /* libTunnelProviderMessaging.a */, 5898D29829017DAC00EB5EBA /* libRelaySelector.a */, + 58FBFBE6291622580020E046 /* MullvadRESTTests.xctest */, ); name = Products; sourceTree = "<group>"; @@ -1366,7 +1395,6 @@ 58E0729C28814AAE008902F8 /* PacketTunnelConfiguration.swift */, 58CE5E7B224146470008646E /* PacketTunnelProvider.swift */, 58E072A028814B0E008902F8 /* MullvadEndpoint+WgEndpoint.swift */, - 58900D0228BBDCC70094E4F0 /* FixedWidthInteger+Arithmetics.swift */, 58E072A228814B96008902F8 /* TunnelMonitor */, 58E07298288031D5008902F8 /* WireGuardAdapterError+Localization.swift */, 58E0729E28814ACC008902F8 /* WireGuardLogLevel+Logging.swift */, @@ -1442,6 +1470,15 @@ path = Assets; sourceTree = "<group>"; }; + 58FBFBE7291622580020E046 /* MullvadRESTTests */ = { + isa = PBXGroup; + children = ( + 58FBFBF0291630700020E046 /* DurationTests.swift */, + 58FBFBE8291622580020E046 /* ExponentialBackoffTests.swift */, + ); + path = MullvadRESTTests; + sourceTree = "<group>"; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -1728,6 +1765,24 @@ productReference = 58E5126528DDF04200B0BCDE /* libOperations.a */; productType = "com.apple.product-type.library.static"; }; + 58FBFBE5291622580020E046 /* MullvadRESTTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 58FBFBEF291622580020E046 /* Build configuration list for PBXNativeTarget "MullvadRESTTests" */; + buildPhases = ( + 58FBFBE2291622580020E046 /* Sources */, + 58FBFBE3291622580020E046 /* Frameworks */, + 58FBFBE4291622580020E046 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 58FBFBEC291622580020E046 /* PBXTargetDependency */, + ); + name = MullvadRESTTests; + productName = MullvadRESTTests; + productReference = 58FBFBE6291622580020E046 /* MullvadRESTTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1794,6 +1849,9 @@ 58FBDA9722A519BC00EB69A3 = { CreatedOnToolsVersion = 10.2.1; }; + 58FBFBE5291622580020E046 = { + CreatedOnToolsVersion = 14.1; + }; }; }; buildConfigurationList = 58CE5E5B224146200008646E /* Build configuration list for PBXProject "MullvadVPN" */; @@ -1823,6 +1881,7 @@ 581943D528F800C900B0CB5E /* MullvadLogging */, 581943F028F8014500B0CB5E /* MullvadTypes */, 06799ABB28F98E1D00ACD94E /* MullvadREST */, + 58FBFBE5291622580020E046 /* MullvadRESTTests */, 063F02722902B63F001FA09F /* RelayCache */, 5898D29729017DAC00EB5EBA /* RelaySelector */, 5898D28829017BD300EB5EBA /* TunnelProviderMessaging */, @@ -1887,6 +1946,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 58FBFBE4291622580020E046 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -1965,11 +2031,13 @@ 58505FFA290A7F0F00118C23 /* ApplicationConfiguration.swift in Sources */, 06799AE028F98E4800ACD94E /* RESTCoding.swift in Sources */, 06799AFC28F98EE300ACD94E /* AddressCache.swift in Sources */, + 5897F1762914E62E00AF5695 /* Duration.swift in Sources */, 06799AF028F98E4800ACD94E /* REST.swift in Sources */, 06799ADF28F98E4800ACD94E /* RESTDevicesProxy.swift in Sources */, 06799ADA28F98E4800ACD94E /* RESTResponseHandler.swift in Sources */, 062B45BC28FD8C3B00746E77 /* RESTDefaults.swift in Sources */, 06799AE428F98E4800ACD94E /* RESTAccountsProxy.swift in Sources */, + 5897F1742913EAF800AF5695 /* ExponentialBackoff.swift in Sources */, 06799AE328F98E4800ACD94E /* RESTNetworkOperation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1997,6 +2065,7 @@ 5898D2B82902ABC400EB5EBA /* KeychainError.swift in Sources */, 581943FC28F8020500B0CB5E /* Error+Chain.swift in Sources */, 5898D29329017CFD00EB5EBA /* Location.swift in Sources */, + 58A8B0842913C6F7004B59B1 /* FixedWidthInteger+Arithmetics.swift in Sources */, 581943FB28F801D500B0CB5E /* CustomErrorDescriptionProtocol.swift in Sources */, 5898D2B52902A8F000EB5EBA /* RelayConstraint.swift in Sources */, 5898D2B42902A8F000EB5EBA /* RelayConstraints.swift in Sources */, @@ -2057,7 +2126,6 @@ 5807E2C3243203E700F5FF30 /* String+Split.swift in Sources */, 58B0A2A8238EE68200BC001D /* RelaySelectorTests.swift in Sources */, 5819C2152726CC9400D6EC38 /* DataSourceSnapshot.swift in Sources */, - 582A8A3B28BCE1AB00D0F9FB /* FixedWidthInteger+Arithmetics.swift in Sources */, 5896AE88246D7FAF005B36CB /* CustomDateComponentsFormatting.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2241,7 +2309,6 @@ 580F8B872819795C002E0998 /* DNSSettings.swift in Sources */, 58E072A128814B0E008902F8 /* MullvadEndpoint+WgEndpoint.swift in Sources */, 06AC116228F94C450037AF9A /* ApplicationConfiguration.swift in Sources */, - 58900D0328BBDCC70094E4F0 /* FixedWidthInteger+Arithmetics.swift in Sources */, 58A3BDB028A1821A00C8C2C6 /* WgStats.swift in Sources */, 5877D70F282137E8002FCFC7 /* SettingsManager.swift in Sources */, 58CE38C728992C8700A6D6E5 /* WireGuardAdapterError+Localization.swift in Sources */, @@ -2284,6 +2351,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 58FBFBE2291622580020E046 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 58FBFBE9291622580020E046 /* ExponentialBackoffTests.swift in Sources */, + 58FBFBF1291630700020E046 /* DurationTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -2367,6 +2443,11 @@ target = 58FBDA9722A519BC00EB69A3 /* WireGuardGoBridge */; targetProxy = 58FBDAA122A52A6800EB69A3 /* PBXContainerItemProxy */; }; + 58FBFBEC291622580020E046 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 06799ABB28F98E1D00ACD94E /* MullvadREST */; + targetProxy = 58FBFBEB291622580020E046 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ @@ -3008,6 +3089,42 @@ }; name = Release; }; + 58FBFBED291622580020E046 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = CKG9MXH72F; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 58FBFBEE291622580020E046 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = CKG9MXH72F; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = net.mullvad.MullvadVPN.MullvadRESTTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -3137,6 +3254,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 58FBFBEF291622580020E046 /* Build configuration list for PBXNativeTarget "MullvadRESTTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58FBFBED291622580020E046 /* Debug */, + 58FBFBEE291622580020E046 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme index 5f4b4b5a25..dc70ad4ec7 100644 --- a/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme +++ b/ios/MullvadVPN.xcodeproj/xcshareddata/xcschemes/MullvadVPN.xcscheme @@ -72,6 +72,16 @@ ReferencedContainer = "container:MullvadVPN.xcodeproj"> </BuildableReference> </TestableReference> + <TestableReference + skipped = "NO"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "58FBFBE5291622580020E046" + BuildableName = "MullvadRESTTests.xctest" + BlueprintName = "MullvadRESTTests" + ReferencedContainer = "container:MullvadVPN.xcodeproj"> + </BuildableReference> + </TestableReference> </Testables> </TestAction> <LaunchAction diff --git a/ios/MullvadVPN/TransportMonitor/TransportMonitor.swift b/ios/MullvadVPN/TransportMonitor/TransportMonitor.swift index 140a9b3027..7b0e49f70b 100644 --- a/ios/MullvadVPN/TransportMonitor/TransportMonitor.swift +++ b/ios/MullvadVPN/TransportMonitor/TransportMonitor.swift @@ -12,13 +12,13 @@ import MullvadREST class TransportMonitor: TunnelObserver { private let tunnelManager: TunnelManager private let packetTunnelTransport: PacketTunnelTransport - private let urlSessionTransport: URLSessionTransport + private let urlSessionTransport: REST.URLSessionTransport init(tunnelManager: TunnelManager = .shared) { self.tunnelManager = tunnelManager packetTunnelTransport = PacketTunnelTransport(tunnelManager: tunnelManager) - urlSessionTransport = URLSessionTransport(urlSession: REST.makeURLSession()) + urlSessionTransport = REST.URLSessionTransport(urlSession: REST.makeURLSession()) tunnelManager.addObserver(self) diff --git a/ios/PacketTunnel/TunnelMonitor/TunnelMonitor.swift b/ios/PacketTunnel/TunnelMonitor/TunnelMonitor.swift index 00a43108d9..4614c8e733 100644 --- a/ios/PacketTunnel/TunnelMonitor/TunnelMonitor.swift +++ b/ios/PacketTunnel/TunnelMonitor/TunnelMonitor.swift @@ -8,6 +8,7 @@ import Foundation import MullvadLogging +import MullvadTypes import NetworkExtension import WireGuardKit |
