Skip to content

Commit 6ff41eb

Browse files
committed
Implement mullvad_api_send_problem_report in Rust
1 parent 7df3893 commit 6ff41eb

File tree

5 files changed

+156
-2
lines changed

5 files changed

+156
-2
lines changed

ios/MullvadRustRuntime/include/mullvad_rust_runtime.h

+14
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,15 @@ typedef struct CompletionCookie {
5353
void *_0;
5454
} CompletionCookie;
5555

56+
typedef struct SwiftProblemReportRequest {
57+
const uint8_t *address;
58+
uintptr_t address_len;
59+
const uint8_t *message;
60+
uintptr_t message_len;
61+
const uint8_t *log;
62+
uintptr_t log_len;
63+
} SwiftProblemReportRequest;
64+
5665
typedef struct ProxyHandle {
5766
void *context;
5867
uint16_t port;
@@ -184,6 +193,11 @@ struct SwiftRetryStrategy mullvad_api_retry_strategy_exponential(uintptr_t max_r
184193
uint32_t factor,
185194
uint64_t max_delay_sec);
186195

196+
struct SwiftCancelHandle mullvad_api_send_problem_report(struct SwiftApiContext api_context,
197+
void *completion_cookie,
198+
struct SwiftRetryStrategy retry_strategy,
199+
const struct SwiftProblemReportRequest *request);
200+
187201
/**
188202
* Initializes a valid pointer to an instance of `EncryptedDnsProxyState`.
189203
*

mullvad-api/src/lib.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,21 @@ impl ProblemReportProxy {
669669
log: &str,
670670
metadata: &BTreeMap<String, String>,
671671
) -> impl Future<Output = Result<(), rest::Error>> {
672+
let future = self.porblem_report_response(email, message, log, metadata);
673+
674+
async move {
675+
future.await?;
676+
Ok(())
677+
}
678+
}
679+
680+
pub fn porblem_report_response(
681+
&self,
682+
email: &str,
683+
message: &str,
684+
log: &str,
685+
metadata: &BTreeMap<String, String>,
686+
) -> impl Future<Output = Result<rest::Response<Incoming>, rest::Error>> {
672687
#[derive(serde::Serialize)]
673688
struct ProblemReport {
674689
address: String,
@@ -691,8 +706,7 @@ impl ProblemReportProxy {
691706
let request = factory
692707
.post_json(&format!("{APP_URL_PREFIX}/problem-report"), &report)?
693708
.expected_status(&[StatusCode::NO_CONTENT]);
694-
service.request(request).await?;
695-
Ok(())
709+
service.request(request).await
696710
}
697711
}
698712
}

mullvad-ios/src/api_client/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod cancellation;
1111
mod completion;
1212
mod response;
1313
mod retry_strategy;
14+
mod send_problem_report;
1415

1516
#[repr(C)]
1617
pub struct SwiftApiContext(*const ApiContext);

mullvad-ios/src/api_client/response.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::{ffi::CString, ptr::null_mut};
22

33
use mullvad_api::rest::{self, Response};
44

5+
56
#[repr(C)]
67
pub struct SwiftMullvadApiResponse {
78
body: *mut u8,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use mullvad_api::{
2+
rest::{self, MullvadRestHandle},
3+
ProblemReportProxy,
4+
};
5+
use talpid_future::retry::retry_future;
6+
7+
use super::{
8+
cancellation::{RequestCancelHandle, SwiftCancelHandle},
9+
completion::{CompletionCookie, SwiftCompletionHandler},
10+
response::SwiftMullvadApiResponse,
11+
retry_strategy::{RetryStrategy, SwiftRetryStrategy},
12+
SwiftApiContext,
13+
};
14+
15+
use mullvad_api::rest::Error;
16+
use std::{collections::BTreeMap};
17+
use std::slice;
18+
use tokio::task::JoinHandle;
19+
20+
#[no_mangle]
21+
pub unsafe extern "C" fn mullvad_api_send_problem_report(
22+
api_context: SwiftApiContext,
23+
completion_cookie: *mut libc::c_void,
24+
retry_strategy: SwiftRetryStrategy,
25+
request: *const SwiftProblemReportRequest,
26+
) -> SwiftCancelHandle {
27+
let completion_handler = SwiftCompletionHandler::new(CompletionCookie(completion_cookie));
28+
let completion = completion_handler.clone();
29+
30+
let Ok(tokio_handle) = crate::mullvad_ios_runtime() else {
31+
completion_handler.finish(SwiftMullvadApiResponse::no_tokio_runtime());
32+
return SwiftCancelHandle::empty();
33+
};
34+
35+
let api_context = api_context.into_rust_context();
36+
let retry_strategy = unsafe { retry_strategy.into_rust() };
37+
38+
let problem_report_request = match unsafe { ProblemReportRequest::from_swift_parameters(request) } {
39+
Some(req) => req,
40+
None => {
41+
let err = Error::ApiError(rest::StatusCode::BAD_REQUEST, "Failed to send problem report: invalid address, message, or log data.".to_string());
42+
log::error!("{err:?}");
43+
completion.finish(SwiftMullvadApiResponse::rest_error(err));
44+
return SwiftCancelHandle::empty();
45+
}
46+
};
47+
48+
let task: JoinHandle<()> = tokio_handle.spawn(async move {
49+
match mullvad_api_send_problem_report_inner(
50+
api_context.rest_handle(),
51+
retry_strategy,
52+
problem_report_request,
53+
)
54+
.await
55+
{
56+
Ok(response) => completion.finish(response),
57+
Err(err) => {
58+
log::error!("{err:?}");
59+
completion.finish(SwiftMullvadApiResponse::rest_error(err));
60+
}
61+
}
62+
});
63+
64+
RequestCancelHandle::new(task, completion_handler.clone()).into_swift()
65+
}
66+
67+
async fn mullvad_api_send_problem_report_inner(
68+
rest_client: MullvadRestHandle,
69+
retry_strategy: RetryStrategy,
70+
problem_report_request: ProblemReportRequest,
71+
) -> Result<SwiftMullvadApiResponse, rest::Error> {
72+
let api = ProblemReportProxy::new(rest_client);
73+
let empty_metadata: BTreeMap<String, String> = BTreeMap::new();
74+
75+
let future_factory = || api.porblem_report_response(&problem_report_request.address, &problem_report_request.message, &(String::from_utf8_lossy(&problem_report_request.log)), &empty_metadata);
76+
77+
let should_retry = |result: &Result<_, rest::Error>| match result {
78+
Err(err) => err.is_network_error(),
79+
Ok(_) => false,
80+
};
81+
82+
let response = retry_future(future_factory, should_retry, retry_strategy.delays()).await?;
83+
SwiftMullvadApiResponse::with_body(response).await
84+
85+
}
86+
87+
#[repr(C)]
88+
pub struct SwiftProblemReportRequest {
89+
address: *const u8,
90+
address_len: usize,
91+
message: *const u8,
92+
message_len: usize,
93+
log: *const u8,
94+
log_len: usize,
95+
}
96+
97+
struct ProblemReportRequest {
98+
address: String,
99+
message: String,
100+
log: Vec<u8>,
101+
}
102+
103+
104+
unsafe impl Send for SwiftProblemReportRequest {}
105+
106+
impl ProblemReportRequest {
107+
unsafe fn from_swift_parameters(request: *const SwiftProblemReportRequest) -> Option<Self> {
108+
if request.is_null() {
109+
return None;
110+
}
111+
112+
let request = &*request; // Dereference the pointer
113+
114+
let address_slice = slice::from_raw_parts(request.address, request.address_len);
115+
let message_slice = slice::from_raw_parts(request.message, request.message_len);
116+
let log_slice = slice::from_raw_parts(request.log, request.log_len);
117+
118+
let address = String::from_utf8(address_slice.to_vec()).ok()?;
119+
let message = String::from_utf8(message_slice.to_vec()).ok()?;
120+
let log = log_slice.to_vec();
121+
122+
Some(Self { address, message, log })
123+
}
124+
}

0 commit comments

Comments
 (0)