summaryrefslogtreecommitdiffhomepage
path: root/ios/MullvadREST/MullvadAPI/APIRequest/APIRequestProxy.swift
diff options
context:
space:
mode:
authorJon Petersson <jon.petersson@mullvad.net>2025-03-13 13:17:16 +0100
committerJon Petersson <jon.petersson@mullvad.net>2025-03-31 11:39:29 +0200
commiteb72686c74607872ee510b432a442ea10baa1b86 (patch)
tree40b51bd8e94c88633ec4c7332241986465355bf6 /ios/MullvadREST/MullvadAPI/APIRequest/APIRequestProxy.swift
parentefbb2c3c0c95f7e7a195c03e9d2483ec731a578e (diff)
downloadmullvadvpn-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.swift89
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)
+ }
+}