Skip to content

Commit c2cf3a3

Browse files
author
mojganii
committed
Fix getting stuck in blocked state after reconnecting
1 parent 11adc05 commit c2cf3a3

23 files changed

+220
-163
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// AccessMethodRepositoryStub.swift
2+
// AccessMethodRepository+Stub.swift
33
// MullvadRESTTests
44
//
55
// Created by Mojgan on 2024-01-02.
@@ -9,27 +9,27 @@
99
import Combine
1010
import MullvadSettings
1111

12-
struct AccessMethodRepositoryStub: AccessMethodRepositoryDataSource {
13-
var directAccess: PersistentAccessMethod
12+
public struct AccessMethodRepositoryStub: AccessMethodRepositoryDataSource {
13+
public var directAccess: PersistentAccessMethod
1414

15-
var accessMethodsPublisher: AnyPublisher<[PersistentAccessMethod], Never> {
15+
public var accessMethodsPublisher: AnyPublisher<[PersistentAccessMethod], Never> {
1616
passthroughSubject.eraseToAnyPublisher()
1717
}
1818

1919
let passthroughSubject: CurrentValueSubject<[PersistentAccessMethod], Never> = CurrentValueSubject([])
2020

21-
init(accessMethods: [PersistentAccessMethod]) {
21+
public init(accessMethods: [PersistentAccessMethod]) {
2222
directAccess = accessMethods.first(where: { $0.kind == .direct })!
2323
passthroughSubject.send(accessMethods)
2424
}
2525

26-
func fetchAll() -> [PersistentAccessMethod] {
26+
public func fetchAll() -> [PersistentAccessMethod] {
2727
passthroughSubject.value
2828
}
2929

30-
func saveLastReachable(_ method: PersistentAccessMethod) {}
30+
public func saveLastReachable(_ method: PersistentAccessMethod) {}
3131

32-
func fetchLastReachable() -> PersistentAccessMethod {
32+
public func fetchLastReachable() -> PersistentAccessMethod {
3333
directAccess
3434
}
3535
}

ios/MullvadMockData/MullvadREST/RelaySelectorStub.swift

+14-3
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@ import MullvadTypes
1111
import WireGuardKitTypes
1212

1313
/// Relay selector stub that accepts a block that can be used to provide custom implementation.
14-
public struct RelaySelectorStub: RelaySelectorProtocol {
15-
let block: (RelayConstraints, UInt) throws -> SelectedRelays
14+
public final class RelaySelectorStub: RelaySelectorProtocol {
15+
var selectedRelaysResult: (RelayConstraints, UInt) throws -> SelectedRelays
16+
17+
init(selectedRelaysResult: @escaping (RelayConstraints, UInt) throws -> SelectedRelays) {
18+
self.selectedRelaysResult = selectedRelaysResult
19+
}
1620

1721
public func selectRelays(
1822
with constraints: RelayConstraints,
1923
connectionAttemptCount: UInt
2024
) throws -> SelectedRelays {
21-
return try block(constraints, connectionAttemptCount)
25+
return try selectedRelaysResult(constraints, connectionAttemptCount)
2226
}
2327
}
2428

@@ -53,4 +57,11 @@ extension RelaySelectorStub {
5357
)
5458
}
5559
}
60+
61+
/// Returns a relay selector that cannot satisfy constraints .
62+
public static func unsatisfied() -> RelaySelectorStub {
63+
return RelaySelectorStub { _, _ in
64+
throw NoRelaysSatisfyingConstraintsError()
65+
}
66+
}
5667
}

ios/MullvadRESTTests/TransportStrategyTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
// Copyright © 2023 Mullvad VPN AB. All rights reserved.
77
//
88

9+
@testable import MullvadMockData
910
@testable import MullvadREST
1011
@testable import MullvadSettings
1112
@testable import MullvadTypes

ios/MullvadVPN.xcodeproj/project.pbxproj

+4-8
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,6 @@
836836
A9DF789D2B7D1E8B0094E4AD /* LoggedInWithTimeUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 859089692B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift */; };
837837
A9E031782ACB09930095D843 /* UIApplication+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E031762ACB08950095D843 /* UIApplication+Extensions.swift */; };
838838
A9E0317A2ACB0AE70095D843 /* UIApplication+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E031792ACB0AE70095D843 /* UIApplication+Stubs.swift */; };
839-
A9E0317C2ACBFC7E0095D843 /* TunnelStore+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E0317B2ACBFC7E0095D843 /* TunnelStore+Stubs.swift */; };
840839
A9E0317F2ACC331C0095D843 /* TunnelStatusBlockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E0317D2ACC32920095D843 /* TunnelStatusBlockObserver.swift */; };
841840
A9E034642ABB302000E59A5A /* UIEdgeInsets+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E034632ABB302000E59A5A /* UIEdgeInsets+Extensions.swift */; };
842841
E1187ABC289BBB850024E748 /* OutOfTimeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */; };
@@ -846,7 +845,6 @@
846845
F006CCFC2B99CC8400C6C2AC /* EditLocationsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F006CCFB2B99CC8400C6C2AC /* EditLocationsCoordinator.swift */; };
847846
F0077EEE2C52844800DAB2AA /* KeyExchangingResultStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0FBD98E2C4A60CC00EE5323 /* KeyExchangingResultStub.swift */; };
848847
F01528BB2BFF3FEE00B01D00 /* ShadowsocksRelaySelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = F01528BA2BFF3FEE00B01D00 /* ShadowsocksRelaySelector.swift */; };
849-
F0164EBA2B4456D30020268D /* AccessMethodRepositoryStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0164EB92B4456D30020268D /* AccessMethodRepositoryStub.swift */; };
850848
F0164EBC2B482E430020268D /* AppStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0164EBB2B482E430020268D /* AppStorage.swift */; };
851849
F0164EBE2B4BFF940020268D /* ShadowsocksLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0164EBD2B4BFF940020268D /* ShadowsocksLoader.swift */; };
852850
F0164EC32B4C49D30020268D /* ShadowsocksLoaderStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0164EC22B4C49D30020268D /* ShadowsocksLoaderStub.swift */; };
@@ -895,6 +893,7 @@
895893
F07BF2622A26279100042943 /* RedeemVoucherOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F07BF2612A26279100042943 /* RedeemVoucherOperation.swift */; };
896894
F07C9D952B220C77006F1C5E /* libmullvad_ios.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 01F1FF1D29F0627D007083C3 /* libmullvad_ios.a */; };
897895
F07CFF2029F2720E008C0343 /* RegisteredDeviceInAppNotificationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F07CFF1F29F2720E008C0343 /* RegisteredDeviceInAppNotificationProvider.swift */; };
896+
F07F63CE2C63E5790027A351 /* AccessMethodRepository+Stub.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0164EB92B4456D30020268D /* AccessMethodRepository+Stub.swift */; };
898897
F08827872B318C840020A383 /* ShadowsocksCipherOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7D92B02862E00F864E0 /* ShadowsocksCipherOptions.swift */; };
899898
F08827882B318F960020A383 /* PersistentAccessMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D962B04E0AC00E7CDD7 /* PersistentAccessMethod.swift */; };
900899
F08827892B3192110020A383 /* AccessMethodRepositoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF875A2B16385400C098B2 /* AccessMethodRepositoryProtocol.swift */; };
@@ -2041,7 +2040,6 @@
20412040
A9D9A4D32C36E1EA004088DD /* mullvad_rust_runtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mullvad_rust_runtime.h; path = include/mullvad_rust_runtime.h; sourceTree = "<group>"; };
20422041
A9E031762ACB08950095D843 /* UIApplication+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Extensions.swift"; sourceTree = "<group>"; };
20432042
A9E031792ACB0AE70095D843 /* UIApplication+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Stubs.swift"; sourceTree = "<group>"; };
2044-
A9E0317B2ACBFC7E0095D843 /* TunnelStore+Stubs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TunnelStore+Stubs.swift"; sourceTree = "<group>"; };
20452043
A9E0317D2ACC32920095D843 /* TunnelStatusBlockObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStatusBlockObserver.swift; sourceTree = "<group>"; };
20462044
A9E034632ABB302000E59A5A /* UIEdgeInsets+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIEdgeInsets+Extensions.swift"; sourceTree = "<group>"; };
20472045
A9EB4F9C2B7FAB21002A2D7A /* PostQuantumKeyNegotiator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostQuantumKeyNegotiator.swift; sourceTree = "<group>"; };
@@ -2053,7 +2051,7 @@
20532051
E1FD0DF428AA7CE400299DB4 /* StatusActivityView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusActivityView.swift; sourceTree = "<group>"; };
20542052
F006CCFB2B99CC8400C6C2AC /* EditLocationsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditLocationsCoordinator.swift; sourceTree = "<group>"; };
20552053
F01528BA2BFF3FEE00B01D00 /* ShadowsocksRelaySelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksRelaySelector.swift; sourceTree = "<group>"; };
2056-
F0164EB92B4456D30020268D /* AccessMethodRepositoryStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessMethodRepositoryStub.swift; sourceTree = "<group>"; };
2054+
F0164EB92B4456D30020268D /* AccessMethodRepository+Stub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccessMethodRepository+Stub.swift"; sourceTree = "<group>"; };
20572055
F0164EBB2B482E430020268D /* AppStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStorage.swift; sourceTree = "<group>"; };
20582056
F0164EBD2B4BFF940020268D /* ShadowsocksLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksLoader.swift; sourceTree = "<group>"; };
20592057
F0164EC22B4C49D30020268D /* ShadowsocksLoaderStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksLoaderStub.swift; sourceTree = "<group>"; };
@@ -2513,7 +2511,6 @@
25132511
A9A5F9A12ACB003D0083449F /* TunnelManagerTests.swift */,
25142512
F0A0868F2C22D6A700BF83E7 /* TunnelSettingsStrategyTests.swift */,
25152513
44BB5F992BE529FE002520EB /* TunnelStateTests.swift */,
2516-
A9E0317B2ACBFC7E0095D843 /* TunnelStore+Stubs.swift */,
25172514
A9E031792ACB0AE70095D843 /* UIApplication+Stubs.swift */,
25182515
58165EBD2A262CBB00688EAD /* WgKeyRotationTests.swift */,
25192516
);
@@ -3710,7 +3707,6 @@
37103707
58FBFBE7291622580020E046 /* MullvadRESTTests */ = {
37113708
isa = PBXGroup;
37123709
children = (
3713-
F0164EB92B4456D30020268D /* AccessMethodRepositoryStub.swift */,
37143710
58FBFBE8291622580020E046 /* ExponentialBackoffTests.swift */,
37153711
A932D9F22B5EB61100999395 /* HeadRequestTests.swift */,
37163712
58BDEB9E2A98F6B400F578F2 /* Mocks */,
@@ -4091,6 +4087,7 @@
40914087
F0ACE3172BE4E487006D5333 /* MullvadREST */ = {
40924088
isa = PBXGroup;
40934089
children = (
4090+
F0164EB92B4456D30020268D /* AccessMethodRepository+Stub.swift */,
40944091
A900E9BF2ACC661900C95F67 /* AccessTokenManager+Stubs.swift */,
40954092
A900E9B72ACC5C2B00C95F67 /* AccountsProxy+Stubs.swift */,
40964093
A900E9BD2ACC654100C95F67 /* APIProxy+Stubs.swift */,
@@ -5271,7 +5268,6 @@
52715268
A9A5FA072ACB05160083449F /* SimulatorVPNConnection.swift in Sources */,
52725269
7A6F2FA52AFA3CB2006D0856 /* AccountExpiryTests.swift in Sources */,
52735270
A9A5FA082ACB05160083449F /* StorePaymentBlockObserver.swift in Sources */,
5274-
A9E0317C2ACBFC7E0095D843 /* TunnelStore+Stubs.swift in Sources */,
52755271
7A516C3C2B712F0B00BBD33D /* IPOverrideWrapperTests.swift in Sources */,
52765272
A9A5FA092ACB05160083449F /* SendStoreReceiptOperation.swift in Sources */,
52775273
A9A5FA0A2ACB05160083449F /* StorePaymentEvent.swift in Sources */,
@@ -5972,7 +5968,6 @@
59725968
buildActionMask = 2147483647;
59735969
files = (
59745970
58B465702A98C53300467203 /* RequestExecutorTests.swift in Sources */,
5975-
F0164EBA2B4456D30020268D /* AccessMethodRepositoryStub.swift in Sources */,
59765971
A917352129FAAA5200D5DCFD /* TransportStrategyTests.swift in Sources */,
59775972
58FBFBE9291622580020E046 /* ExponentialBackoffTests.swift in Sources */,
59785973
F0164EC32B4C49D30020268D /* ShadowsocksLoaderStub.swift in Sources */,
@@ -6096,6 +6091,7 @@
60966091
buildActionMask = 2147483647;
60976092
files = (
60986093
F0ACE31D2BE4E4F2006D5333 /* DevicesProxy+Stubs.swift in Sources */,
6094+
F07F63CE2C63E5790027A351 /* AccessMethodRepository+Stub.swift in Sources */,
60996095
F0ACE31E2BE4E4F2006D5333 /* AccountsProxy+Stubs.swift in Sources */,
61006096
F0ACE3202BE4E4F2006D5333 /* AccessTokenManager+Stubs.swift in Sources */,
61016097
F0ACE32C2BE4E77E006D5333 /* DeviceMock.swift in Sources */,

ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift

+3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ final class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate {
6262
do {
6363
setInternalStateConnected(with: try selectedRelays ?? pickRelays())
6464
completionHandler(nil)
65+
} catch let error where error is NoRelaysSatisfyingConstraintsError {
66+
observedState = .error(ObservedBlockedState(reason: .noRelaysSatisfyingConstraints))
67+
completionHandler(error)
6568
} catch {
6669
providerLogger.error(
6770
error: error,

ios/MullvadVPN/SimulatorTunnelProvider/SimulatorVPNConnection.swift

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#if targetEnvironment(simulator)
1010

1111
import Foundation
12+
import MullvadREST
1213
import NetworkExtension
1314

1415
class SimulatorVPNConnection: NSObject, VPNConnectionProtocol {
@@ -94,6 +95,9 @@ class SimulatorVPNConnection: NSObject, VPNConnectionProtocol {
9495
if error == nil {
9596
self.status = .connected
9697
self.connectedDate = Date()
98+
} else if error is NoRelaysSatisfyingConstraintsError {
99+
self.reasserting = true
100+
self.connectedDate = nil
97101
} else {
98102
self.status = .disconnected
99103
self.connectedDate = nil

ios/MullvadVPN/TunnelManager/MapConnectionStatusOperation.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class MapConnectionStatusOperation: AsyncOperation {
2222

2323
private let logger = Logger(label: "TunnelManager.MapConnectionStatusOperation")
2424

25-
init(
25+
required init(
2626
queue: DispatchQueue,
2727
interactor: TunnelInteractor,
2828
connectionStatus: NEVPNStatus,

ios/MullvadVPN/TunnelManager/SendTunnelProviderMessageOperation.swift

+9-9
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ private let defaultTimeout: Duration = .seconds(5)
2323
final class SendTunnelProviderMessageOperation<Output>: ResultOperation<Output> {
2424
typealias DecoderHandler = (Data?) throws -> Output
2525

26-
private let application: UIApplication
26+
private let backgroundTaskProvider: BackgroundTaskProvider
2727
private let tunnel: any TunnelProtocol
2828
private let message: TunnelProviderMessage
2929
private let timeout: Duration
@@ -38,14 +38,14 @@ final class SendTunnelProviderMessageOperation<Output>: ResultOperation<Output>
3838

3939
init(
4040
dispatchQueue: DispatchQueue,
41-
application: UIApplication,
41+
backgroundTaskProvider: BackgroundTaskProvider,
4242
tunnel: any TunnelProtocol,
4343
message: TunnelProviderMessage,
4444
timeout: Duration? = nil,
4545
decoderHandler: @escaping DecoderHandler,
4646
completionHandler: CompletionHandler?
4747
) {
48-
self.application = application
48+
self.backgroundTaskProvider = backgroundTaskProvider
4949
self.tunnel = tunnel
5050
self.message = message
5151
self.timeout = timeout ?? defaultTimeout
@@ -60,7 +60,7 @@ final class SendTunnelProviderMessageOperation<Output>: ResultOperation<Output>
6060

6161
addObserver(
6262
BackgroundObserver(
63-
application: application,
63+
application: backgroundTaskProvider,
6464
name: "Send tunnel provider message: \(message)",
6565
cancelUponExpiration: true
6666
)
@@ -193,7 +193,7 @@ final class SendTunnelProviderMessageOperation<Output>: ResultOperation<Output>
193193
return
194194
}
195195

196-
guard application.backgroundTimeRemaining > timeout else {
196+
guard backgroundTaskProvider.backgroundTimeRemaining > timeout else {
197197
finish(result: .failure(SendTunnelProviderMessageError.notEnoughBackgroundTime))
198198
return
199199
}
@@ -218,15 +218,15 @@ final class SendTunnelProviderMessageOperation<Output>: ResultOperation<Output>
218218
extension SendTunnelProviderMessageOperation where Output: Codable {
219219
convenience init(
220220
dispatchQueue: DispatchQueue,
221-
application: UIApplication,
221+
backgroundTaskProvider: BackgroundTaskProvider,
222222
tunnel: any TunnelProtocol,
223223
message: TunnelProviderMessage,
224224
timeout: Duration? = nil,
225225
completionHandler: @escaping CompletionHandler
226226
) {
227227
self.init(
228228
dispatchQueue: dispatchQueue,
229-
application: application,
229+
backgroundTaskProvider: backgroundTaskProvider,
230230
tunnel: tunnel,
231231
message: message,
232232
timeout: timeout,
@@ -245,15 +245,15 @@ extension SendTunnelProviderMessageOperation where Output: Codable {
245245
extension SendTunnelProviderMessageOperation where Output == Void {
246246
convenience init(
247247
dispatchQueue: DispatchQueue,
248-
application: UIApplication,
248+
backgroundTaskProvider: BackgroundTaskProvider,
249249
tunnel: any TunnelProtocol,
250250
message: TunnelProviderMessage,
251251
timeout: Duration? = nil,
252252
completionHandler: CompletionHandler?
253253
) {
254254
self.init(
255255
dispatchQueue: dispatchQueue,
256-
application: application,
256+
backgroundTaskProvider: backgroundTaskProvider,
257257
tunnel: tunnel,
258258
message: message,
259259
timeout: timeout,

ios/MullvadVPN/TunnelManager/Tunnel+Messaging.swift

+5-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ extension TunnelProtocol {
2929
) -> Cancellable {
3030
let operation = SendTunnelProviderMessageOperation(
3131
dispatchQueue: dispatchQueue,
32-
application: .shared,
32+
backgroundTaskProvider: backgroundTaskProvider,
3333
tunnel: self,
3434
message: .reconnectTunnel(nextRelays),
3535
completionHandler: completionHandler
@@ -46,7 +46,7 @@ extension TunnelProtocol {
4646
) -> Cancellable {
4747
let operation = SendTunnelProviderMessageOperation(
4848
dispatchQueue: dispatchQueue,
49-
application: .shared,
49+
backgroundTaskProvider: backgroundTaskProvider,
5050
tunnel: self,
5151
message: .getTunnelStatus,
5252
completionHandler: completionHandler
@@ -64,7 +64,7 @@ extension TunnelProtocol {
6464
) -> Cancellable {
6565
let operation = SendTunnelProviderMessageOperation(
6666
dispatchQueue: dispatchQueue,
67-
application: .shared,
67+
backgroundTaskProvider: backgroundTaskProvider,
6868
tunnel: self,
6969
message: .sendURLRequest(proxyRequest),
7070
timeout: proxyRequestTimeout,
@@ -76,7 +76,7 @@ extension TunnelProtocol {
7676

7777
let cancelOperation = SendTunnelProviderMessageOperation(
7878
dispatchQueue: dispatchQueue,
79-
application: .shared,
79+
backgroundTaskProvider: backgroundTaskProvider,
8080
tunnel: self,
8181
message: .cancelURLRequest(proxyRequest.id),
8282
completionHandler: nil
@@ -96,7 +96,7 @@ extension TunnelProtocol {
9696
) -> Cancellable {
9797
let operation = SendTunnelProviderMessageOperation(
9898
dispatchQueue: dispatchQueue,
99-
application: .shared,
99+
backgroundTaskProvider: backgroundTaskProvider,
100100
tunnel: self,
101101
message: .privateKeyRotation,
102102
completionHandler: completionHandler

0 commit comments

Comments
 (0)