diff options
| author | Jon Petersson <jon.petersson@mullvad.net> | 2025-03-13 13:17:16 +0100 |
|---|---|---|
| committer | Jon Petersson <jon.petersson@mullvad.net> | 2025-03-31 11:39:29 +0200 |
| commit | eb72686c74607872ee510b432a442ea10baa1b86 (patch) | |
| tree | 40b51bd8e94c88633ec4c7332241986465355bf6 /ios/MullvadREST/MullvadAPI/APIRequest/APIRequestProxy.swift | |
| parent | efbb2c3c0c95f7e7a195c03e9d2483ec731a578e (diff) | |
| download | mullvadvpn-eb72686c74607872ee510b432a442ea10baa1b86.tar.xz mullvadvpn-eb72686c74607872ee510b432a442ea10baa1b86.zip | |
Tie rust and Swift side together
Diffstat (limited to 'ios/MullvadREST/MullvadAPI/APIRequest/APIRequestProxy.swift')
| -rw-r--r-- | ios/MullvadREST/MullvadAPI/APIRequest/APIRequestProxy.swift | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/ios/MullvadREST/MullvadAPI/APIRequest/APIRequestProxy.swift b/ios/MullvadREST/MullvadAPI/APIRequest/APIRequestProxy.swift new file mode 100644 index 0000000000..8e2ac4fad2 --- /dev/null +++ b/ios/MullvadREST/MullvadAPI/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) + } +} |
