summaryrefslogtreecommitdiffhomepage
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
parentefbb2c3c0c95f7e7a195c03e9d2483ec731a578e (diff)
downloadmullvadvpn-eb72686c74607872ee510b432a442ea10baa1b86.tar.xz
mullvadvpn-eb72686c74607872ee510b432a442ea10baa1b86.zip
Tie rust and Swift side together
-rw-r--r--ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift8
-rw-r--r--ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift58
-rw-r--r--ios/MullvadREST/ApiHandlers/RESTResponseHandler.swift28
-rw-r--r--ios/MullvadREST/MullvadAPI/APIRequest/APIError.swift (renamed from ios/MullvadREST/APIRequest/APIError.swift)0
-rw-r--r--ios/MullvadREST/MullvadAPI/APIRequest/APIRequest.swift (renamed from ios/MullvadREST/APIRequest/APIRequest.swift)18
-rw-r--r--ios/MullvadREST/MullvadAPI/APIRequest/APIRequestProxy.swift (renamed from ios/MullvadREST/APIRequest/APIRequestProxy.swift)0
-rw-r--r--ios/MullvadREST/MullvadAPI/MullvadApiNetworkOperation.swift (renamed from ios/MullvadREST/ApiHandlers/MullvadApiNetworkOperation.swift)2
-rw-r--r--ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift (renamed from ios/MullvadREST/ApiHandlers/MullvadApiRequestFactory.swift)14
-rw-r--r--ios/MullvadREST/Transport/APITransport.swift3
-rw-r--r--ios/MullvadRustRuntime/MullvadApiResponse.swift8
-rw-r--r--ios/MullvadRustRuntime/include/mullvad_rust_runtime.h4
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj14
-rw-r--r--ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift9
-rw-r--r--mullvad-api/src/relay_list.rs3
-rw-r--r--mullvad-ios/src/api_client/api.rs20
15 files changed, 152 insertions, 37 deletions
diff --git a/ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift b/ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift
index 3c8350b8fb..6f0e60a4ad 100644
--- a/ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift
+++ b/ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift
@@ -19,6 +19,14 @@ struct APIProxyStub: APIQuerying {
AnyCancellable()
}
+ func mullvadApiGetRelayList(
+ retryStrategy: REST.RetryStrategy,
+ etag: String?,
+ completionHandler: @escaping ProxyCompletionHandler<REST.ServerRelaysCacheResponse>)
+ -> Cancellable {
+ AnyCancellable()
+ }
+
func getAddressList(
retryStrategy: REST.RetryStrategy,
completionHandler: @escaping ProxyCompletionHandler<[AnyIPEndpoint]>
diff --git a/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift b/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift
index 908e0bddd4..38486d3115 100644
--- a/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift
+++ b/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift
@@ -18,6 +18,12 @@ public protocol APIQuerying: Sendable {
completionHandler: @escaping @Sendable ProxyCompletionHandler<[AnyIPEndpoint]>
) -> Cancellable
+ func mullvadApiGetRelayList(
+ retryStrategy: REST.RetryStrategy,
+ etag: String?,
+ completionHandler: @escaping @Sendable ProxyCompletionHandler<REST.ServerRelaysCacheResponse>
+ ) -> Cancellable
+
func getAddressList(
retryStrategy: REST.RetryStrategy,
completionHandler: @escaping @Sendable ProxyCompletionHandler<[AnyIPEndpoint]>
@@ -71,10 +77,56 @@ extension REST {
with: responseDecoder
)
+ return createNetworkOperation(
+ request: .getAddressList(retryStrategy),
+ responseHandler: responseHandler,
+ completionHandler: completionHandler
+ )
+ }
+
+ public func mullvadApiGetRelayList(
+ retryStrategy: REST.RetryStrategy,
+ etag: String?,
+ completionHandler: @escaping @Sendable ProxyCompletionHandler<REST.ServerRelaysCacheResponse>
+ ) -> Cancellable {
+ if var etag {
+ // Enforce weak validator to account for some backend caching quirks.
+ if etag.starts(with: "\"") {
+ etag.insert(contentsOf: "W/", at: etag.startIndex)
+ }
+ }
+
+ let responseHandler = rustCustomResponseHandler { [weak self] (data, responseEtag) in
+ // Discarding result since we're only interested in knowing that it's parseable.
+ let canDecodeResponse = (try? self?.responseDecoder.decode(REST.ServerRelaysResponse.self, from: data)) != nil
+
+ return if canDecodeResponse {
+ if let responseEtag, responseEtag == etag {
+ REST.ServerRelaysCacheResponse.notModified
+ } else {
+ REST.ServerRelaysCacheResponse.newContent(responseEtag, data)
+ }
+ } else {
+ nil
+ }
+ }
+
+ return createNetworkOperation(
+ request: .getRelayList(retryStrategy, etag: etag),
+ responseHandler: responseHandler,
+ completionHandler: completionHandler
+ )
+ }
+
+ private func createNetworkOperation<Success: Decodable>(
+ request: APIRequest,
+ responseHandler: RustResponseHandler<Success>,
+ completionHandler: @escaping @Sendable ProxyCompletionHandler<Success>
+ ) -> MullvadApiNetworkOperation<Success> {
let networkOperation = MullvadApiNetworkOperation(
- name: "get-api-addrs",
+ name: request.name,
dispatchQueue: dispatchQueue,
- request: .getAddressList(retryStrategy),
+ request: request,
transportProvider: configuration.apiTransportProvider,
responseDecoder: responseDecoder,
responseHandler: responseHandler,
@@ -314,7 +366,7 @@ extension REST {
// MARK: - Response types
- public enum ServerRelaysCacheResponse: Sendable {
+ public enum ServerRelaysCacheResponse: Sendable, Decodable {
case notModified
case newContent(_ etag: String?, _ rawData: Data)
}
diff --git a/ios/MullvadREST/ApiHandlers/RESTResponseHandler.swift b/ios/MullvadREST/ApiHandlers/RESTResponseHandler.swift
index c6197e983e..e4acc1482c 100644
--- a/ios/MullvadREST/ApiHandlers/RESTResponseHandler.swift
+++ b/ios/MullvadREST/ApiHandlers/RESTResponseHandler.swift
@@ -19,7 +19,7 @@ protocol RESTResponseHandler<Success> {
protocol RESTRustResponseHandler<Success> {
associatedtype Success
- func handleResponse(_ body: Data?) -> REST.ResponseHandlerResult<Success>
+ func handleResponse(_ resonse: ProxyAPIResponse) -> REST.ResponseHandlerResult<Success>
}
extension REST {
@@ -76,7 +76,7 @@ extension REST {
}
final class RustResponseHandler<Success>: RESTRustResponseHandler {
- typealias HandlerBlock = (Data?) -> REST.ResponseHandlerResult<Success>
+ typealias HandlerBlock = (ProxyAPIResponse) -> REST.ResponseHandlerResult<Success>
private let handlerBlock: HandlerBlock
@@ -84,8 +84,8 @@ extension REST {
handlerBlock = block
}
- func handleResponse(_ body: Data?) -> REST.ResponseHandlerResult<Success> {
- handlerBlock(body)
+ func handleResponse(_ response: ProxyAPIResponse) -> REST.ResponseHandlerResult<Success> {
+ handlerBlock(response)
}
}
@@ -96,8 +96,8 @@ extension REST {
decoding type: T.Type,
with decoder: JSONDecoder
) -> RustResponseHandler<T> {
- RustResponseHandler { data in
- guard let data else {
+ RustResponseHandler { (response: ProxyAPIResponse) in
+ guard let data = response.data else {
return .unhandledResponse(nil)
}
@@ -109,6 +109,22 @@ extension REST {
}
}
+ static func rustCustomResponseHandler<T: Decodable>(
+ conversion: @escaping (_ data: Data, _ etag: String?) -> T?
+ ) -> RustResponseHandler<T> {
+ RustResponseHandler { (response: ProxyAPIResponse) in
+ guard let data = response.data else {
+ return .unhandledResponse(nil)
+ }
+
+ return if let convertedResponse = conversion(data, response.etag) {
+ .decoding { convertedResponse }
+ } else {
+ .unhandledResponse(nil)
+ }
+ }
+ }
+
/// Response handler for reponses where the body is empty.
static func rustEmptyResponseHandler() -> RustResponseHandler<Void> {
RustResponseHandler { _ in
diff --git a/ios/MullvadREST/APIRequest/APIError.swift b/ios/MullvadREST/MullvadAPI/APIRequest/APIError.swift
index f62fde619a..f62fde619a 100644
--- a/ios/MullvadREST/APIRequest/APIError.swift
+++ b/ios/MullvadREST/MullvadAPI/APIRequest/APIError.swift
diff --git a/ios/MullvadREST/APIRequest/APIRequest.swift b/ios/MullvadREST/MullvadAPI/APIRequest/APIRequest.swift
index 4fff7bd32b..2462a998a7 100644
--- a/ios/MullvadREST/APIRequest/APIRequest.swift
+++ b/ios/MullvadREST/MullvadAPI/APIRequest/APIRequest.swift
@@ -8,11 +8,21 @@
public enum APIRequest: Codable, Sendable {
case getAddressList(_ retryStrategy: REST.RetryStrategy)
+ case getRelayList(_ retryStrategy: REST.RetryStrategy, etag: String?)
+
+ var name: String {
+ switch self {
+ case .getAddressList:
+ "get-address-list"
+ case .getRelayList:
+ "get-relay-lisy"
+ }
+ }
var retryStrategy: REST.RetryStrategy {
switch self {
- case let .getAddressList(strategy):
- return strategy
+ case .getAddressList(let strategy), .getRelayList(let strategy, _):
+ strategy
}
}
}
@@ -30,9 +40,11 @@ public struct ProxyAPIRequest: Codable, Sendable {
public struct ProxyAPIResponse: Codable, Sendable {
public let data: Data?
public let error: APIError?
+ public let etag: String?
- public init(data: Data?, error: APIError?) {
+ public init(data: Data?, error: APIError?, etag: String? = nil) {
self.data = data
self.error = error
+ self.etag = etag
}
}
diff --git a/ios/MullvadREST/APIRequest/APIRequestProxy.swift b/ios/MullvadREST/MullvadAPI/APIRequest/APIRequestProxy.swift
index 8e2ac4fad2..8e2ac4fad2 100644
--- a/ios/MullvadREST/APIRequest/APIRequestProxy.swift
+++ b/ios/MullvadREST/MullvadAPI/APIRequest/APIRequestProxy.swift
diff --git a/ios/MullvadREST/ApiHandlers/MullvadApiNetworkOperation.swift b/ios/MullvadREST/MullvadAPI/MullvadApiNetworkOperation.swift
index 68d4ecb0c7..12d9c0346d 100644
--- a/ios/MullvadREST/ApiHandlers/MullvadApiNetworkOperation.swift
+++ b/ios/MullvadREST/MullvadAPI/MullvadApiNetworkOperation.swift
@@ -78,7 +78,7 @@ extension REST {
return
}
- let decodedResponse = responseHandler.handleResponse(response.data)
+ let decodedResponse = responseHandler.handleResponse(response)
switch decodedResponse {
case let .success(value):
diff --git a/ios/MullvadREST/ApiHandlers/MullvadApiRequestFactory.swift b/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift
index d361beef1b..fd64408e0e 100644
--- a/ios/MullvadREST/ApiHandlers/MullvadApiRequestFactory.swift
+++ b/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift
@@ -18,19 +18,27 @@ public struct MullvadApiRequestFactory: Sendable {
public func makeRequest(_ request: APIRequest) -> REST.MullvadApiRequestHandler {
{ completion in
- let pointerClass = MullvadApiCompletion { apiResponse in
+ let completionPointer = MullvadApiCompletion { apiResponse in
try? completion?(apiResponse)
}
- let rawPointer = Unmanaged.passRetained(pointerClass).toOpaque()
+ let rawCompletionPointer = Unmanaged.passRetained(completionPointer).toOpaque()
return switch request {
case let .getAddressList(retryStrategy):
MullvadApiCancellable(handle: mullvad_api_get_addresses(
apiContext.context,
- rawPointer,
+ rawCompletionPointer,
retryStrategy.toRustStrategy()
))
+
+ case let .getRelayList(retryStrategy, etag: etag):
+ MullvadApiCancellable(handle: mullvad_api_get_relays(
+ apiContext.context,
+ rawCompletionPointer,
+ retryStrategy.toRustStrategy(),
+ etag
+ ))
}
}
}
diff --git a/ios/MullvadREST/Transport/APITransport.swift b/ios/MullvadREST/Transport/APITransport.swift
index 811e775a19..84b76a05af 100644
--- a/ios/MullvadREST/Transport/APITransport.swift
+++ b/ios/MullvadREST/Transport/APITransport.swift
@@ -44,7 +44,8 @@ public final class APITransport: APITransportProtocol {
completion(ProxyAPIResponse(
data: response.body,
- error: error
+ error: error,
+ etag: response.etag
))
}
}
diff --git a/ios/MullvadRustRuntime/MullvadApiResponse.swift b/ios/MullvadRustRuntime/MullvadApiResponse.swift
index 7836d43971..ddead026b3 100644
--- a/ios/MullvadRustRuntime/MullvadApiResponse.swift
+++ b/ios/MullvadRustRuntime/MullvadApiResponse.swift
@@ -25,6 +25,14 @@ public class MullvadApiResponse {
return Data(UnsafeBufferPointer(start: body, count: Int(response.body_size)))
}
+ public var etag: String? {
+ return if response.etag == nil {
+ nil
+ } else {
+ String(cString: response.etag)
+ }
+ }
+
public var errorDescription: String? {
return if response.error_description == nil {
nil
diff --git a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
index abdc8ea809..8ebdea8863 100644
--- a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
+++ b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
@@ -24,8 +24,6 @@ typedef struct EncryptedDnsProxyState EncryptedDnsProxyState;
typedef struct ExchangeCancelToken ExchangeCancelToken;
-typedef struct Option______u8 Option______u8;
-
typedef struct RequestCancelHandle RequestCancelHandle;
typedef struct RetryStrategy RetryStrategy;
@@ -119,7 +117,7 @@ struct SwiftCancelHandle mullvad_api_get_addresses(struct SwiftApiContext api_co
struct SwiftCancelHandle mullvad_api_get_relays(struct SwiftApiContext api_context,
void *completion_cookie,
struct SwiftRetryStrategy retry_strategy,
- struct Option______u8 etag);
+ const uint8_t *etag);
/**
* Called by the Swift side to signal that a Mullvad API call should be cancelled.
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 1e623f8bc0..25dbbd2c96 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -2681,8 +2681,8 @@
06799ABD28F98E1D00ACD94E /* MullvadREST */ = {
isa = PBXGroup;
children = (
+ 7A2C0E872D82E450003D8048 /* MullvadAPI */,
F06045F02B2324DA00B2D37A /* ApiHandlers */,
- 7A2E7B6B2D6C9E45009EF2C3 /* APIRequest */,
062B45A228FD4C0F00746E77 /* Assets */,
7AD63A422CDA661B00445268 /* Extensions */,
582FFA82290A84E700895745 /* Info.plist */,
@@ -4161,6 +4161,16 @@
path = Alert;
sourceTree = "<group>";
};
+ 7A2C0E872D82E450003D8048 /* MullvadAPI */ = {
+ isa = PBXGroup;
+ children = (
+ 7A2E7B6B2D6C9E45009EF2C3 /* APIRequest */,
+ 7AB9312D2D4A5D0A005FCEBA /* MullvadApiNetworkOperation.swift */,
+ 7A99D36E2D5606F900891FF7 /* MullvadApiRequestFactory.swift */,
+ );
+ path = MullvadAPI;
+ sourceTree = "<group>";
+ };
7A2E7B6B2D6C9E45009EF2C3 /* APIRequest */ = {
isa = PBXGroup;
children = (
@@ -4540,7 +4550,6 @@
06AC114128F8413A0037AF9A /* AddressCache.swift */,
A935594B2B4C2DA900D5D524 /* APIAvailabilityTestRequest.swift */,
06FAE67128F83CA40033DD93 /* HTTP.swift */,
- 7AB9312D2D4A5D0A005FCEBA /* MullvadApiNetworkOperation.swift */,
06FAE67228F83CA40033DD93 /* RESTAccessTokenManager.swift */,
06FAE66828F83CA30033DD93 /* RESTAccountsProxy.swift */,
06FAE67328F83CA40033DD93 /* RESTAPIProxy.swift */,
@@ -4559,7 +4568,6 @@
06FAE66628F83CA30033DD93 /* RESTResponseHandler.swift */,
06FAE67528F83CA40033DD93 /* RESTTaskIdentifier.swift */,
06FAE66528F83CA30033DD93 /* RESTURLSession.swift */,
- 7A99D36E2D5606F900891FF7 /* MullvadApiRequestFactory.swift */,
06FAE67728F83CA40033DD93 /* ServerRelaysResponse.swift */,
06FAE66B28F83CA30033DD93 /* SSLPinningURLSessionDelegate.swift */,
);
diff --git a/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift b/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift
index e1fd209f95..b3db3ab240 100644
--- a/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift
+++ b/ios/MullvadVPN/RelayCacheTracker/RelayCacheTracker.swift
@@ -26,7 +26,7 @@ protocol RelayCacheTrackerProtocol: Sendable {
final class RelayCacheTracker: RelayCacheTrackerProtocol, @unchecked Sendable {
/// Relay update interval.
- static let relayUpdateInterval: Duration = .hours(1)
+ static let relayUpdateInterval: Duration = .seconds(30)
/// Tracker log.
nonisolated(unsafe) private let logger = Logger(label: "RelayCacheTracker")
@@ -174,9 +174,14 @@ final class RelayCacheTracker: RelayCacheTrackerProtocol, @unchecked Sendable {
return AnyCancellable()
}
- return self.apiProxy.getRelays(etag: cachedRelays?.etag, retryStrategy: .noRetry) { result in
+ return self.apiProxy.getRelays(etag: "hello", retryStrategy: .noRetry) { result in
+ print(result)
finish(self.handleResponse(result: result))
}
+
+// return self.apiProxy.mullvadApiGetRelayList(retryStrategy: .noRetry, etag: cachedRelays.etag) { result in
+// finish(self.handleResponse(result: result))
+// }
}
operation.addObserver(
diff --git a/mullvad-api/src/relay_list.rs b/mullvad-api/src/relay_list.rs
index 2b34996c23..d3cc30108d 100644
--- a/mullvad-api/src/relay_list.rs
+++ b/mullvad-api/src/relay_list.rs
@@ -2,13 +2,12 @@
use crate::rest;
-use hyper::{body::Incoming, header, Error, StatusCode};
+use hyper::{body::Incoming, header, StatusCode};
use mullvad_types::{location, relay_list};
use talpid_types::net::wireguard;
use std::{
collections::BTreeMap,
- future::Future,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
ops::RangeInclusive,
time::Duration,
diff --git a/mullvad-ios/src/api_client/api.rs b/mullvad-ios/src/api_client/api.rs
index d3c521159f..ef7b18af9b 100644
--- a/mullvad-ios/src/api_client/api.rs
+++ b/mullvad-ios/src/api_client/api.rs
@@ -1,4 +1,4 @@
-use std::{ffi::CStr, ptr};
+use std::{ffi::CStr, ptr::null};
use mullvad_api::{
rest::{self, MullvadRestHandle},
@@ -77,7 +77,7 @@ pub unsafe extern "C" fn mullvad_api_get_relays(
api_context: SwiftApiContext,
completion_cookie: *mut libc::c_void,
retry_strategy: SwiftRetryStrategy,
- etag: Option<*const u8>,
+ etag: *const u8,
) -> SwiftCancelHandle {
let completion_handler = SwiftCompletionHandler::new(CompletionCookie(completion_cookie));
@@ -89,17 +89,15 @@ pub unsafe extern "C" fn mullvad_api_get_relays(
let api_context = api_context.into_rust_context();
let retry_strategy = unsafe { retry_strategy.into_rust() };
- let etag = match etag {
- Some(etag) => {
- let unwrapped_tag = unsafe { CStr::from_ptr(etag.cast()) }.to_str().unwrap();
- Some(String::from(unwrapped_tag))
- },
- None => None,
- };
+ let mut maybe_etag: Option<String> = None;
+ if etag != null() {
+ let unwrapped_tag = unsafe { CStr::from_ptr(etag.cast()) }.to_str().unwrap();
+ maybe_etag = Some(String::from(unwrapped_tag));
+ }
let completion = completion_handler.clone();
let task = tokio_handle.clone().spawn(async move {
- match mullvad_api_get_relays_inner(api_context.rest_handle(), retry_strategy, etag).await {
+ match mullvad_api_get_relays_inner(api_context.rest_handle(), retry_strategy, maybe_etag).await {
Ok(response) => completion.finish(response),
Err(err) => {
log::error!("{err:?}");
@@ -125,6 +123,8 @@ async fn mullvad_api_get_relays_inner(
Ok(_) => false,
};
+
+
let response = retry_future(future_factory, should_retry, retry_strategy.delays()).await?;
SwiftMullvadApiResponse::with_body(response).await