diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2023-06-20 16:23:41 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2023-06-20 16:23:41 +0200 |
| commit | 7cbccbc9ff029bf77d301e9ee871a101889017b2 (patch) | |
| tree | e3f2a78b9e512c1d6ea5559a08a3914cb0ef3ca3 | |
| parent | 152dfd869ff3db5c8b81cc3ed5a82355f396e70f (diff) | |
| parent | 69b45ee945ec3dc2e9611c39af6cfc49398f0da3 (diff) | |
| download | mullvadvpn-7cbccbc9ff029bf77d301e9ee871a101889017b2.tar.xz mullvadvpn-7cbccbc9ff029bf77d301e9ee871a101889017b2.zip | |
Merge branch 'cache-successful-shadowsocks-relay-and-api-endpoints-for-ios-165'
17 files changed, 152 insertions, 137 deletions
diff --git a/ios/MullvadREST/AddressCache.swift b/ios/MullvadREST/AddressCache.swift index 8279b1c263..da27798c9d 100644 --- a/ios/MullvadREST/AddressCache.swift +++ b/ios/MullvadREST/AddressCache.swift @@ -73,11 +73,6 @@ extension REST { return currentEndpoint } - public func selectNextEndpoint(_ failedEndpoint: AnyIPEndpoint) -> AnyIPEndpoint { - // This function currently acts as a convoluted no-op. It will be soon deleted. - return getCurrentEndpoint() - } - /// Updates the available endpoints to use /// /// Only the first available endpoint is kept, the rest are discarded. diff --git a/ios/MullvadREST/RESTNetworkOperation.swift b/ios/MullvadREST/RESTNetworkOperation.swift index 41072de713..372991ed8d 100644 --- a/ios/MullvadREST/RESTNetworkOperation.swift +++ b/ios/MullvadREST/RESTNetworkOperation.swift @@ -17,7 +17,7 @@ extension REST { private let responseHandler: AnyResponseHandler<Success> private let logger: Logger - private let transportProvider: () -> RESTTransportProvider? + private let transportProvider: () -> RESTTransport? private let addressCacheStore: AddressCache private var networkTask: Cancellable? @@ -30,7 +30,6 @@ extension REST { private var retryDelayIterator: AnyIterator<Duration> private var retryTimer: DispatchSourceTimer? private var retryCount = 0 - private var transportStrategy = TransportStrategy() init( name: String, @@ -141,12 +140,7 @@ extension REST { private func didReceiveURLRequest(_ restRequest: REST.Request, endpoint: AnyIPEndpoint) { dispatchPrecondition(condition: .onQueue(dispatchQueue)) - let suggestedTransport = transportStrategy.connectionTransport() - let transportProvider = transportProvider() - let transport = suggestedTransport == .useShadowsocks - ? transportProvider?.shadowsocksTransport() - : transportProvider?.transport() - + let transport = transportProvider() guard let transport else { logger.error("Failed to obtain transport.") finish(result: .failure(REST.Error.transport(NoTransportError()))) @@ -175,7 +169,6 @@ extension REST { self.didReceiveURLResponse( httpResponse, - transport: transport, data: data, endpoint: endpoint ) @@ -202,34 +195,19 @@ extension REST { ) { dispatchPrecondition(condition: .onQueue(dispatchQueue)) - if let urlError = error as? URLError { - switch urlError.code { - case .cancelled: - finish(result: .failure(OperationError.cancelled)) - return - - case .notConnectedToInternet, .internationalRoamingOff, .callIsActive: - break - - default: - if !REST.isStagingEnvironment { - _ = addressCacheStore.selectNextEndpoint(endpoint) - transportStrategy.didFail() - } - } + if case URLError.cancelled = error { + finish(result: .failure(OperationError.cancelled)) + } else { + logger.error( + error: error, + message: "Failed to perform request to \(endpoint) using \(transport.name)." + ) + retryRequest(with: error) } - - logger.error( - error: error, - message: "Failed to perform request to \(endpoint) using \(transport.name)." - ) - - retryRequest(with: error) } private func didReceiveURLResponse( _ response: HTTPURLResponse, - transport: RESTTransport, data: Data, endpoint: AnyIPEndpoint ) { diff --git a/ios/MullvadREST/RESTProxy.swift b/ios/MullvadREST/RESTProxy.swift index b252d2e33f..5c8a14df81 100644 --- a/ios/MullvadREST/RESTProxy.swift +++ b/ios/MullvadREST/RESTProxy.swift @@ -67,11 +67,11 @@ extension REST { } public class ProxyConfiguration { - public let transportProvider: () -> RESTTransportProvider? + public let transportProvider: () -> RESTTransport? public let addressCacheStore: AddressCache public init( - transportProvider: @escaping () -> RESTTransportProvider?, + transportProvider: @escaping () -> RESTTransport?, addressCacheStore: AddressCache ) { self.transportProvider = transportProvider diff --git a/ios/MullvadREST/RESTProxyFactory.swift b/ios/MullvadREST/RESTProxyFactory.swift index 8d2bba41d3..6bc793a722 100644 --- a/ios/MullvadREST/RESTProxyFactory.swift +++ b/ios/MullvadREST/RESTProxyFactory.swift @@ -13,7 +13,7 @@ extension REST { public let configuration: AuthProxyConfiguration public class func makeProxyFactory( - transportProvider: @escaping () -> RESTTransportProvider?, + transportProvider: @escaping () -> RESTTransport?, addressCache: AddressCache ) -> ProxyFactory { let basicConfiguration = REST.ProxyConfiguration( diff --git a/ios/MullvadREST/RESTTransport.swift b/ios/MullvadREST/RESTTransport.swift index 8b6854c486..54b5e561d6 100644 --- a/ios/MullvadREST/RESTTransport.swift +++ b/ios/MullvadREST/RESTTransport.swift @@ -16,13 +16,3 @@ public protocol RESTTransport { completion: @escaping (Data?, URLResponse?, Error?) -> Void ) -> Cancellable } - -public protocol RESTTransportProvider { - /// Requests a new transport - /// - Returns: A transport layer - func transport() -> RESTTransport? - - /// Requests a Shadowsocks transport - /// - Returns: A transport layer that proxies the requests to a local Shadowsocks proxy instance - func shadowsocksTransport() -> RESTTransport? -} diff --git a/ios/MullvadREST/RESTTransportStrategy.swift b/ios/MullvadREST/RESTTransportStrategy.swift index c04384a552..114fe12f64 100644 --- a/ios/MullvadREST/RESTTransportStrategy.swift +++ b/ios/MullvadREST/RESTTransportStrategy.swift @@ -8,7 +8,7 @@ import Foundation -public struct TransportStrategy: Codable { +public struct TransportStrategy: Codable, Equatable { /// The different transports suggested by the strategy public enum Transport { /// Suggests using a direct connection @@ -21,7 +21,9 @@ public struct TransportStrategy: Codable { /// /// A value of `0` means a direct transport suggestion, a value of `1` or `2` means a Shadowsocks transport /// suggestion. - private var connectionAttempts: UInt + /// + /// `internal` instead of `private` for testing purposes. + internal var connectionAttempts: UInt public init() { connectionAttempts = 0 @@ -31,9 +33,9 @@ public struct TransportStrategy: Codable { /// /// Every third failure results in a direct transport suggestion. public mutating func didFail() { - connectionAttempts += 1 - // Avoid overflowing by resetting back to 0 every 3rd failure - connectionAttempts = connectionAttempts.isMultiple(of: 3) ? 0 : connectionAttempts + let (partial, isOverflow) = connectionAttempts.addingReportingOverflow(1) + // UInt.max is a multiple of 3, go directly to 1 when overflowing + connectionAttempts = isOverflow ? 1 : partial } /// The suggested connection transport diff --git a/ios/MullvadRESTTests/TransportStrategyTests.swift b/ios/MullvadRESTTests/TransportStrategyTests.swift index 31eb449d2f..7767762a3f 100644 --- a/ios/MullvadRESTTests/TransportStrategyTests.swift +++ b/ios/MullvadRESTTests/TransportStrategyTests.swift @@ -6,19 +6,19 @@ // Copyright © 2023 Mullvad VPN AB. All rights reserved. // -import MullvadREST +@testable import MullvadREST import XCTest final class TransportStrategyTests: XCTestCase { func testEveryThirdConnectionAttemptsIsDirect() { + loopStrategyTest(with: TransportStrategy()) + } + + func testOverflowingConnectionAttempts() { var strategy = TransportStrategy() + strategy.connectionAttempts = UInt.max - for index in 0 ... 12 { - let expectedResult: TransportStrategy.Transport - expectedResult = index.isMultiple(of: 3) ? .useURLSession : .useShadowsocks - XCTAssertEqual(strategy.connectionTransport(), expectedResult) - strategy.didFail() - } + loopStrategyTest(with: strategy) } func testLoadingFromCacheDoesNotImpactStrategy() throws { @@ -37,4 +37,15 @@ final class TransportStrategyTests: XCTestCase { reloadedStrategy.didFail() XCTAssertEqual(reloadedStrategy.connectionTransport(), .useURLSession) } + + private func loopStrategyTest(with strategy: TransportStrategy) { + var strategy = strategy + + for index in 0 ... 12 { + let expectedResult: TransportStrategy.Transport + expectedResult = index.isMultiple(of: 3) ? .useURLSession : .useShadowsocks + XCTAssertEqual(strategy.connectionTransport(), expectedResult) + strategy.didFail() + } + } } diff --git a/ios/MullvadTransport/TransportProvider.swift b/ios/MullvadTransport/TransportProvider.swift index 786f34a2b2..cb24300153 100644 --- a/ios/MullvadTransport/TransportProvider.swift +++ b/ios/MullvadTransport/TransportProvider.swift @@ -13,30 +13,36 @@ import MullvadTypes import RelayCache import RelaySelector -public final class TransportProvider: RESTTransportProvider { +public final class TransportProvider: RESTTransport { private let urlSessionTransport: URLSessionTransport private let relayCache: RelayCache private let logger = Logger(label: "TransportProvider") private let addressCache: REST.AddressCache private let shadowsocksCache: ShadowsocksConfigurationCache + private var transportStrategy: TransportStrategy + + private var currentTransport: RESTTransport? + private let parallelRequestsMutex = NSLock() public init( urlSessionTransport: URLSessionTransport, relayCache: RelayCache, addressCache: REST.AddressCache, - shadowsocksCache: ShadowsocksConfigurationCache + shadowsocksCache: ShadowsocksConfigurationCache, + transportStrategy: TransportStrategy = .init() ) { self.urlSessionTransport = urlSessionTransport self.relayCache = relayCache self.addressCache = addressCache self.shadowsocksCache = shadowsocksCache + self.transportStrategy = transportStrategy } - public func transport() -> RESTTransport? { - urlSessionTransport - } + // MARK: - + + // MARK: RESTTransport implementation - public func shadowsocksTransport() -> RESTTransport? { + private func shadowsocks() -> RESTTransport? { do { let shadowsocksConfiguration = try shadowsocksConfiguration() @@ -57,8 +63,6 @@ public final class TransportProvider: RESTTransportProvider { /// Returns the last used shadowsocks configuration, otherwise a new randomized configuration. private func shadowsocksConfiguration() throws -> ShadowsocksConfiguration { // If a previous shadowsocks configuration was in cache, return it directly. - // There is no previous configuration either if this is the first time this code ran - // Or because the previous shadowsocks configuration was invalid, therefore generate a new one. do { return try shadowsocksCache.read() } catch { @@ -91,4 +95,84 @@ public final class TransportProvider: RESTTransportProvider { return newConfiguration } + + // MARK: - + + // MARK: RESTTransport implementation + + public var name: String { currentTransport?.name ?? "TransportProvider" } + + public func sendRequest( + _ request: URLRequest, + completion: @escaping (Data?, URLResponse?, Error?) -> Void + ) -> Cancellable { + parallelRequestsMutex.lock() + defer { + parallelRequestsMutex.unlock() + } + + let currentStrategy = transportStrategy + guard let transport = makeTransport() else { return AnyCancellable() } + + let failureCompletionHandler: (Data?, URLResponse?, Error?) + -> Void = { [weak self] data, response, error in + guard let self else { return } + + if let error = error as? URLError, error.shouldResetNetworkTransport { + resetTransportMatching(currentStrategy) + } + completion(data, response, error) + } + + return transport.sendRequest(request, completion: failureCompletionHandler) + } + + /// When several requests fail at the same time, prevents the `transportStrategy` from switching multiple times. + /// + /// The `strategy` is checked against the `transportStrategy`. When several requests are made and fail in parallel, + /// only the first failure will pass the equality check. + /// Subsequent failures will not cause the strategy to change several times in a quick fashion. + /// - Parameter strategy: The strategy object used when sending a request + private func resetTransportMatching(_ strategy: TransportStrategy) { + parallelRequestsMutex.lock() + defer { parallelRequestsMutex.unlock() } + + if strategy == transportStrategy { + transportStrategy.didFail() + currentTransport = nil + } + } + + /// Sets and returns the `currentTransport` according to the suggestion from `transportStrategy` + /// + /// > Warning: Do not lock the `parallelRequestsMutex` in this method + /// + /// - Returns: A `RESTTransport` object to make a connection + private func makeTransport() -> RESTTransport? { + if currentTransport == nil { + switch transportStrategy.connectionTransport() { + case .useShadowsocks: + currentTransport = shadowsocks() + case .useURLSession: + currentTransport = urlSessionTransport + } + } + return currentTransport + } +} + +private extension URLError { + /// Whether the transport selection should be reset. + /// + /// `true` if the network request + /// * Was not cancelled + /// * Was not done during a phone call + /// * Was made when internet connection was available + /// * Was made in a context with data roaming, but international roaming was turned off + var shouldResetNetworkTransport: Bool { + code != .cancelled && + code != .notConnectedToInternet && + code != .internationalRoamingOff && + code != .callIsActive + } } diff --git a/ios/MullvadTransport/shadowsocks-proxy/build.sh b/ios/MullvadTransport/shadowsocks-proxy/build.sh index 7617bc91c8..2a39185a14 100644 --- a/ios/MullvadTransport/shadowsocks-proxy/build.sh +++ b/ios/MullvadTransport/shadowsocks-proxy/build.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash +set -euvx + if [ "$#" -ne 1 ] then echo "Usage (note: only call inside xcode!):" @@ -11,12 +13,10 @@ fi FFI_TARGET=$1 RELFLAG= -if [[ "$CONFIGURATION" -eq "Release" ]]; then +if [[ "$CONFIGURATION" == "Release" ]]; then RELFLAG=--release fi -set -euvx - if [[ -n "${DEVELOPER_SDK_DIR:-}" ]]; then # Assume we're in Xcode, which means we're probably cross-compiling. # In this case, we need to add an extra library search path for build scripts and proc-macros, diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index 1b9482ee7b..4a248cc5d1 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -398,7 +398,6 @@ A97F1F442A1F4E1A00ECEFDE /* MullvadTransport.h in Headers */ = {isa = PBXBuildFile; fileRef = A97F1F432A1F4E1A00ECEFDE /* MullvadTransport.h */; settings = {ATTRIBUTES = (Public, ); }; }; A97F1F472A1F4E1A00ECEFDE /* MullvadTransport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A97F1F412A1F4E1A00ECEFDE /* MullvadTransport.framework */; }; A97F1F482A1F4E1A00ECEFDE /* MullvadTransport.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A97F1F412A1F4E1A00ECEFDE /* MullvadTransport.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - A97FF54B2A0B7AD000900996 /* SimulatorTunnelTransportProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97FF54A2A0B7AD000900996 /* SimulatorTunnelTransportProvider.swift */; }; A97FF5502A0D2FFC00900996 /* NSFileCoordinator+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97FF54F2A0D2FFC00900996 /* NSFileCoordinator+Extensions.swift */; }; A9A8A8EB2A262AB30086D569 /* FileCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9A8A8EA2A262AB30086D569 /* FileCache.swift */; }; A9B2CF722A1F64CD0013CC6C /* MullvadREST.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06799ABC28F98E1D00ACD94E /* MullvadREST.framework */; }; @@ -1130,7 +1129,6 @@ A9467E8A2A2E0317000DC21F /* ShadowsocksConfigurationCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksConfigurationCache.swift; sourceTree = "<group>"; }; A97F1F412A1F4E1A00ECEFDE /* MullvadTransport.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MullvadTransport.framework; sourceTree = BUILT_PRODUCTS_DIR; }; A97F1F432A1F4E1A00ECEFDE /* MullvadTransport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MullvadTransport.h; sourceTree = "<group>"; }; - A97FF54A2A0B7AD000900996 /* SimulatorTunnelTransportProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatorTunnelTransportProvider.swift; sourceTree = "<group>"; }; A97FF54F2A0D2FFC00900996 /* NSFileCoordinator+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSFileCoordinator+Extensions.swift"; sourceTree = "<group>"; }; A9A8A8EA2A262AB30086D569 /* FileCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileCache.swift; sourceTree = "<group>"; }; A9CF11FC2A0518E7001D9565 /* AddressCacheTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressCacheTests.swift; sourceTree = "<group>"; }; @@ -1672,7 +1670,6 @@ children = ( 58BA693023EADA6A009DC256 /* SimulatorTunnelProvider.swift */, 587A01FB23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift */, - A97FF54A2A0B7AD000900996 /* SimulatorTunnelTransportProvider.swift */, ); path = SimulatorTunnelProvider; sourceTree = "<group>"; @@ -2982,7 +2979,6 @@ 587C93002986E2B600FB9664 /* TermsOfServiceCoordinator.swift in Sources */, 5864AF0929C78850005B0CD9 /* PreferencesCellFactory.swift in Sources */, 587B7536266528A200DEF7E9 /* NotificationManager.swift in Sources */, - A97FF54B2A0B7AD000900996 /* SimulatorTunnelTransportProvider.swift in Sources */, 5820EDA9288FE064006BF4E4 /* DeviceManagementInteractor.swift in Sources */, 58FB865A26EA214400F188BC /* RelayCacheTrackerObserver.swift in Sources */, 58ACF64D26567A5000ACE4B7 /* CustomSwitch.swift in Sources */, diff --git a/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift b/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift index bc2772ee83..b347d080d1 100644 --- a/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift +++ b/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelProviderHost.swift @@ -22,7 +22,6 @@ final class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate { private var selectorResult: RelaySelectorResult? private let urlRequestProxy: URLRequestProxy private let relayCacheTracker: RelayCacheTracker - private let simulatorTransportProvider: SimulatorTunnelTransportProvider private let providerLogger = Logger(label: "SimulatorTunnelProviderHost") private let dispatchQueue = DispatchQueue(label: "SimulatorTunnelProviderHostQueue") @@ -32,12 +31,10 @@ final class SimulatorTunnelProviderHost: SimulatorTunnelProviderDelegate { let urlSession = REST.makeURLSession() let urlSessionTransport = URLSessionTransport(urlSession: urlSession) - let simulatorTransportProvider = SimulatorTunnelTransportProvider(urlSessionTransport: urlSessionTransport) - self.simulatorTransportProvider = simulatorTransportProvider self.urlRequestProxy = URLRequestProxy( dispatchQueue: dispatchQueue, - transportProvider: { simulatorTransportProvider } + transportProvider: { urlSessionTransport } ) } diff --git a/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelTransportProvider.swift b/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelTransportProvider.swift deleted file mode 100644 index 87f5cf62e8..0000000000 --- a/ios/MullvadVPN/SimulatorTunnelProvider/SimulatorTunnelTransportProvider.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// SimulatorTunnelTransportProvider.swift -// MullvadVPN -// -// Created by Marco Nikic on 2023-05-10. -// Copyright © 2023 Mullvad VPN AB. All rights reserved. -// - -import Foundation -import MullvadREST -import MullvadTransport - -final class SimulatorTunnelTransportProvider: RESTTransportProvider { - private let urlSessionTransport: URLSessionTransport - - init(urlSessionTransport: URLSessionTransport) { - self.urlSessionTransport = urlSessionTransport - } - - func transport() -> RESTTransport? { - urlSessionTransport - } - - func shadowsocksTransport() -> RESTTransport? { - urlSessionTransport - } -} diff --git a/ios/MullvadVPN/TransportMonitor/PacketTunnelTransport.swift b/ios/MullvadVPN/TransportMonitor/PacketTunnelTransport.swift index ebb0acefb0..b1c3fea1e7 100644 --- a/ios/MullvadVPN/TransportMonitor/PacketTunnelTransport.swift +++ b/ios/MullvadVPN/TransportMonitor/PacketTunnelTransport.swift @@ -19,11 +19,9 @@ struct PacketTunnelTransport: RESTTransport { } let tunnel: Tunnel - let useShadowsocksTransport: Bool - init(tunnel: Tunnel, useShadowsocksTransport: Bool) { + init(tunnel: Tunnel) { self.tunnel = tunnel - self.useShadowsocksTransport = useShadowsocksTransport } func sendRequest( @@ -32,8 +30,7 @@ struct PacketTunnelTransport: RESTTransport { ) -> Cancellable { let proxyRequest = ProxyURLRequest( id: UUID(), - urlRequest: request, - useShadowsocksTransport: useShadowsocksTransport + urlRequest: request ) // If the URL provided to the proxy request was invalid, indicate failure via `.badURL` and return a no-op. diff --git a/ios/MullvadVPN/TransportMonitor/TransportMonitor.swift b/ios/MullvadVPN/TransportMonitor/TransportMonitor.swift index ebc24913b2..395b4e1eaf 100644 --- a/ios/MullvadVPN/TransportMonitor/TransportMonitor.swift +++ b/ios/MullvadVPN/TransportMonitor/TransportMonitor.swift @@ -10,10 +10,11 @@ import Foundation import MullvadLogging import MullvadREST import MullvadTransport +import MullvadTypes import RelayCache import RelaySelector -final class TransportMonitor: RESTTransportProvider { +final class TransportMonitor: RESTTransport { private let tunnelManager: TunnelManager private let tunnelStore: TunnelStore private let transportProvider: TransportProvider @@ -28,12 +29,11 @@ final class TransportMonitor: RESTTransportProvider { self.transportProvider = transportProvider } - public func transport() -> RESTTransport? { - return selectTransport(transportProvider.transport(), useShadowsocksTransport: false) - } + var name: String { selectTransport(transportProvider).name } - public func shadowsocksTransport() -> RESTTransport? { - return selectTransport(transportProvider.shadowsocksTransport(), useShadowsocksTransport: true) + func sendRequest(_ request: URLRequest, completion: @escaping (Data?, URLResponse?, Error?) -> Void) -> Cancellable + { + selectTransport(transportProvider).sendRequest(request, completion: completion) } // MARK: - @@ -48,10 +48,8 @@ final class TransportMonitor: RESTTransportProvider { /// /// - Parameters: /// - transport: The transport to use if there is no tunnel, or if it shouldn't be bypassed - /// - useShadowsocksTransport: A hint for enforcing a Shadowsocks transport when proxying a request via an - /// available `Tunnel` /// - Returns: A transport to use for sending an `URLRequest` - private func selectTransport(_ transport: RESTTransport?, useShadowsocksTransport: Bool) -> RESTTransport? { + private func selectTransport(_ transport: RESTTransport) -> RESTTransport { let tunnel = tunnelStore.getPersistentTunnels().first { tunnel in return tunnel.status == .connecting || tunnel.status == .reasserting || @@ -60,8 +58,7 @@ final class TransportMonitor: RESTTransportProvider { if let tunnel, shouldByPassVPN(tunnel: tunnel) { return PacketTunnelTransport( - tunnel: tunnel, - useShadowsocksTransport: useShadowsocksTransport + tunnel: tunnel ) } return transport diff --git a/ios/RelaySelector/RelaySelector.swift b/ios/RelaySelector/RelaySelector.swift index 956461751b..8c9c7a6915 100644 --- a/ios/RelaySelector/RelaySelector.swift +++ b/ios/RelaySelector/RelaySelector.swift @@ -21,10 +21,12 @@ public enum RelaySelector { } /// Return a random Shadowsocks bridge relay, or `nil` if no relay were found. + /// + /// Non `active` relays are filtered out. /// - Parameter relays: The list of relays to randomly select from /// - Returns: A Shadowsocks relay or `nil` if no relay were found. public static func getShadowsocksRelay(relays: REST.ServerRelaysResponse) -> REST.BridgeRelay? { - relays.bridge.relays.randomElement() + relays.bridge.relays.filter { $0.active }.randomElement() } /** diff --git a/ios/TunnelProviderMessaging/ProxyURLRequest.swift b/ios/TunnelProviderMessaging/ProxyURLRequest.swift index cf392eee12..4ff7bd72eb 100644 --- a/ios/TunnelProviderMessaging/ProxyURLRequest.swift +++ b/ios/TunnelProviderMessaging/ProxyURLRequest.swift @@ -15,7 +15,6 @@ public struct ProxyURLRequest: Codable { public let method: String? public let httpBody: Data? public let httpHeaders: [String: String]? - public let useShadowsocksTransport: Bool public var urlRequest: URLRequest { var urlRequest = URLRequest(url: url) @@ -25,7 +24,7 @@ public struct ProxyURLRequest: Codable { return urlRequest } - public init?(id: UUID, urlRequest: URLRequest, useShadowsocksTransport: Bool = false) { + public init?(id: UUID, urlRequest: URLRequest) { guard let urlRequestUrl = urlRequest.url else { return nil } self.id = id @@ -33,6 +32,5 @@ public struct ProxyURLRequest: Codable { method = urlRequest.httpMethod httpBody = urlRequest.httpBody httpHeaders = urlRequest.allHTTPHeaderFields - self.useShadowsocksTransport = useShadowsocksTransport } } diff --git a/ios/TunnelProviderMessaging/URLRequestProxy.swift b/ios/TunnelProviderMessaging/URLRequestProxy.swift index defa7822e4..7fe2e060cf 100644 --- a/ios/TunnelProviderMessaging/URLRequestProxy.swift +++ b/ios/TunnelProviderMessaging/URLRequestProxy.swift @@ -15,14 +15,14 @@ public final class URLRequestProxy { /// Serial queue used for synchronizing access to class members. private let dispatchQueue: DispatchQueue - private let transportProvider: () -> RESTTransportProvider? + private let transportProvider: () -> RESTTransport? /// List of all proxied network requests bypassing VPN. private var proxiedRequests: [UUID: Cancellable] = [:] public init( dispatchQueue: DispatchQueue, - transportProvider: @escaping () -> RESTTransportProvider? + transportProvider: @escaping () -> RESTTransport? ) { self.dispatchQueue = dispatchQueue self.transportProvider = transportProvider @@ -33,14 +33,9 @@ public final class URLRequestProxy { completionHandler: @escaping (ProxyURLResponse) -> Void ) { dispatchQueue.async { - // Instruct the Packet Tunnel to try to reach the API via the local shadow socks proxy instance if needed - let transportProvider = self.transportProvider() - let transport = proxyRequest.useShadowsocksTransport - ? transportProvider?.shadowsocksTransport() - : transportProvider?.transport() - guard let transport else { return } + guard let transportProvider = self.transportProvider() else { return } // The task sent by `transport.sendRequest` comes in an already resumed state - let task = transport.sendRequest(proxyRequest.urlRequest) { [weak self] data, response, error in + let task = transportProvider.sendRequest(proxyRequest.urlRequest) { [weak self] data, response, error in guard let self else { return } // However there is no guarantee about which queue the execution resumes on // Use `dispatchQueue` to guarantee thread safe access to `proxiedRequests` |
