1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
//
// RESTProxy.swift
// MullvadREST
//
// Created by pronebird on 20/04/2022.
// Copyright © 2025 Mullvad VPN AB. All rights reserved.
//
import Foundation
import MullvadRustRuntime
import MullvadTypes
import Operations
public typealias ProxyCompletionHandler<Success: Sendable> = @Sendable (Result<Success, Swift.Error>) -> Void
extension REST {
public class Proxy<ConfigurationType: ProxyConfiguration>: @unchecked Sendable {
/// Synchronization queue used by network operations.
let dispatchQueue: DispatchQueue
/// Operation queue used for running network operations.
let operationQueue = AsyncOperationQueue()
/// Proxy configuration.
let configuration: ConfigurationType
/// URL request factory.
let requestFactory: REST.RequestFactory
/// URL response decoder.
let responseDecoder: JSONDecoder
init(
name: String,
configuration: ConfigurationType,
requestFactory: REST.RequestFactory,
responseDecoder: JSONDecoder
) {
dispatchQueue = DispatchQueue(label: "REST.\(name).dispatchQueue")
operationQueue.name = "REST.\(name).operationQueue"
self.configuration = configuration
self.requestFactory = requestFactory
self.responseDecoder = responseDecoder
}
func makeRequestExecutor<Success: Sendable>(
name: String,
requestHandler: RESTRequestHandler,
responseHandler: some RESTResponseHandler<Success>
) -> any RESTRequestExecutor<Success> {
let operationFactory = NetworkOperationFactory(
dispatchQueue: dispatchQueue,
configuration: configuration,
name: name,
requestHandler: requestHandler,
responseHandler: responseHandler
)
return RequestExecutor(operationFactory: operationFactory, operationQueue: operationQueue)
}
}
/// Factory object producing instances of `NetworkOperation`.
private struct NetworkOperationFactory<Success: Sendable, ConfigurationType: ProxyConfiguration> {
let dispatchQueue: DispatchQueue
let configuration: ConfigurationType
let name: String
let requestHandler: RESTRequestHandler
let responseHandler: any RESTResponseHandler<Success>
/// Creates new network operation but does not schedule it for execution.
func makeOperation(
retryStrategy: REST.RetryStrategy,
completionHandler: ProxyCompletionHandler<Success>? = nil
) -> NetworkOperation<Success> {
return NetworkOperation(
name: getTaskIdentifier(name: name),
dispatchQueue: dispatchQueue,
configuration: configuration,
retryStrategy: retryStrategy,
requestHandler: requestHandler,
responseHandler: responseHandler,
completionHandler: completionHandler
)
}
}
/// Network request executor that supports block-based and async execution flows.
private struct RequestExecutor<Success: Sendable, ConfigurationType: ProxyConfiguration>: RESTRequestExecutor {
let operationFactory: NetworkOperationFactory<Success, ConfigurationType>
let operationQueue: AsyncOperationQueue
func execute(
retryStrategy: REST.RetryStrategy,
completionHandler: @escaping ProxyCompletionHandler<Success>
) -> Cancellable {
let operation = operationFactory.makeOperation(
retryStrategy: retryStrategy,
completionHandler: completionHandler
)
operationQueue.addOperation(operation)
return operation
}
func execute(retryStrategy: REST.RetryStrategy) async throws -> Success {
let operation = operationFactory.makeOperation(retryStrategy: retryStrategy)
return try await withTaskCancellationHandler {
return try await withCheckedThrowingContinuation { continuation in
operation.completionHandler = { result in
continuation.resume(with: result)
}
operationQueue.addOperation(operation)
}
} onCancel: {
operation.cancel()
}
}
func execute(completionHandler: @escaping @Sendable ProxyCompletionHandler<Success>) -> Cancellable {
return execute(retryStrategy: .noRetry, completionHandler: completionHandler)
}
func execute() async throws -> Success {
return try await execute(retryStrategy: .noRetry)
}
}
public class ProxyConfiguration: @unchecked Sendable {
public let transportProvider: RESTTransportProvider
public let apiTransportProvider: APITransportProviderProtocol
public let addressCacheStore: AddressCache
public init(
transportProvider: RESTTransportProvider,
apiTransportProvider: APITransportProviderProtocol,
addressCacheStore: AddressCache
) {
self.transportProvider = transportProvider
self.apiTransportProvider = apiTransportProvider
self.addressCacheStore = addressCacheStore
}
}
public class AuthProxyConfiguration: ProxyConfiguration, @unchecked Sendable {
public let accessTokenManager: RESTAccessTokenManagement
public init(
proxyConfiguration: ProxyConfiguration,
accessTokenManager: RESTAccessTokenManagement
) {
self.accessTokenManager = accessTokenManager
super.init(
transportProvider: proxyConfiguration.transportProvider,
apiTransportProvider: proxyConfiguration.apiTransportProvider,
addressCacheStore: proxyConfiguration.addressCacheStore
)
}
}
}
|