diff options
| author | Jon Petersson <jon.petersson@mullvad.net> | 2025-03-03 15:40:02 +0100 |
|---|---|---|
| committer | Jon Petersson <jon.petersson@mullvad.net> | 2025-03-03 15:40:02 +0100 |
| commit | b24faaa96394420935f4e4a282554e10bdc41e2d (patch) | |
| tree | 161e9cca2f1107034337e4248f66d5993e7b7eae /ios/MullvadREST/APIRequest/APIRequestProxy.swift | |
| parent | 8ceda7aeb54ed058543e1a665d24cd5c928a872e (diff) | |
| parent | 9a7866e96a1788494b955764fbe92961eb65a9f5 (diff) | |
| download | mullvadvpn-b24faaa96394420935f4e4a282554e10bdc41e2d.tar.xz mullvadvpn-b24faaa96394420935f4e4a282554e10bdc41e2d.zip | |
Merge branch 'transport-api-requests-across-apppacket-tunnel-boundary-ios-1041'
Diffstat (limited to 'ios/MullvadREST/APIRequest/APIRequestProxy.swift')
| -rw-r--r-- | ios/MullvadREST/APIRequest/APIRequestProxy.swift | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/ios/MullvadREST/APIRequest/APIRequestProxy.swift b/ios/MullvadREST/APIRequest/APIRequestProxy.swift new file mode 100644 index 0000000000..8e2ac4fad2 --- /dev/null +++ b/ios/MullvadREST/APIRequest/APIRequestProxy.swift @@ -0,0 +1,89 @@ +// +// APIRequestProxy.swift +// MullvadVPN +// +// Created by Jon Petersson on 2025-02-13. +// Copyright © 2025 Mullvad VPN AB. All rights reserved. +// + +import MullvadRustRuntime +import MullvadTypes + +public protocol APIRequestProxyProtocol { + func sendRequest(_ proxyRequest: ProxyAPIRequest, completion: @escaping @Sendable (ProxyAPIResponse) -> Void) + func sendRequest(_ proxyRequest: ProxyAPIRequest) async -> ProxyAPIResponse + func cancelRequest(identifier: UUID) +} + +/// Network request proxy capable of passing serializable requests and responses over the given transport provider. +public final class APIRequestProxy: APIRequestProxyProtocol, @unchecked Sendable { + /// Serial queue used for synchronizing access to class members. + private let dispatchQueue: DispatchQueue + + private let transportProvider: APITransportProviderProtocol + + /// List of all proxied network requests bypassing VPN. + private var proxiedRequests: [UUID: Cancellable] = [:] + + public init( + dispatchQueue: DispatchQueue, + transportProvider: APITransportProviderProtocol + ) { + self.dispatchQueue = dispatchQueue + self.transportProvider = transportProvider + } + + public func sendRequest( + _ proxyRequest: ProxyAPIRequest, + completion: @escaping @Sendable (ProxyAPIResponse) -> Void + ) { + dispatchQueue.async { + guard let transport = self.transportProvider.makeTransport() else { + // Cancel old task, if there's one scheduled. + self.cancelRequest(identifier: proxyRequest.id) + + completion(ProxyAPIResponse(data: nil, error: nil)) + return + } + + let cancellable = transport.sendRequest(proxyRequest.request) { [weak self] response in + guard let self else { return } + + // Use `dispatchQueue` to guarantee thread safe access to `proxiedRequests` + dispatchQueue.async { + _ = self.removeRequest(identifier: proxyRequest.id) + completion(response) + } + } + + // Cancel old task, if there's one scheduled. + let oldTask = self.addRequest(identifier: proxyRequest.id, task: cancellable) + oldTask?.cancel() + } + } + + public func sendRequest(_ proxyRequest: ProxyAPIRequest) async -> ProxyAPIResponse { + return await withCheckedContinuation { continuation in + sendRequest(proxyRequest) { proxyResponse in + continuation.resume(returning: proxyResponse) + } + } + } + + public func cancelRequest(identifier: UUID) { + dispatchQueue.async { + let task = self.removeRequest(identifier: identifier) + task?.cancel() + } + } + + private func addRequest(identifier: UUID, task: Cancellable) -> Cancellable? { + dispatchPrecondition(condition: .onQueue(dispatchQueue)) + return proxiedRequests.updateValue(task, forKey: identifier) + } + + private func removeRequest(identifier: UUID) -> Cancellable? { + dispatchPrecondition(condition: .onQueue(dispatchQueue)) + return proxiedRequests.removeValue(forKey: identifier) + } +} |
