Skip to content

Commit b1d9ffa

Browse files
authored
fix(core): IPC fallback hanging when sending responses, closes #10327 (#10582)
The IPC fallback system kicks in when the custom protocol implementation cannot be used (e.g. CORS issues). The fallback uses the postMessage mechanism, which by default uses channels to send large responses. If the custom protocol implementation cannot be used, we should not use channels, but eval the response directly.
1 parent 794cf82 commit b1d9ffa

File tree

3 files changed

+33
-8
lines changed

3 files changed

+33
-8
lines changed

.changes/fix-ipc-fallback.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch:bug
3+
---
4+
5+
Fix IPC fallback (postMessage implementation when custom protocol fails) hanging when sending responses.

core/tauri/scripts/ipc-protocol.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,11 @@
6262
)
6363
}
6464
})
65-
.catch(() => {
65+
.catch((e) => {
66+
console.warn(
67+
'IPC custom protocol failed, Tauri will now use the postMessage interface instead',
68+
e
69+
)
6670
// failed to use the custom protocol IPC (either the webview blocked a custom protocol or it was a CSP error)
6771
// so we need to fallback to the postMessage interface
6872
customProtocolIpcFailed = true
@@ -74,7 +78,10 @@
7478
cmd,
7579
callback,
7680
error,
77-
options,
81+
options: {
82+
...options,
83+
customProtocolIpcBlocked: customProtocolIpcFailed
84+
},
7885
payload,
7986
__TAURI_INVOKE_KEY__
8087
})

core/tauri/src/ipc/protocol.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager
174174

175175
use serde::{Deserialize, Deserializer};
176176

177+
#[derive(Default)]
177178
pub(crate) struct HeaderMap(http::HeaderMap);
178179

179180
impl<'de> Deserialize<'de> for HeaderMap {
@@ -199,9 +200,13 @@ fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager
199200
}
200201
}
201202

202-
#[derive(Deserialize)]
203+
#[derive(Deserialize, Default)]
204+
#[serde(rename_all = "camelCase")]
203205
struct RequestOptions {
206+
#[serde(default)]
204207
headers: HeaderMap,
208+
#[serde(default)]
209+
custom_protocol_ipc_blocked: bool,
205210
}
206211

207212
#[derive(Deserialize)]
@@ -260,13 +265,15 @@ fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager
260265

261266
match message {
262267
Ok(message) => {
268+
let options = message.options.unwrap_or_default();
269+
263270
let request = InvokeRequest {
264271
cmd: message.cmd,
265272
callback: message.callback,
266273
error: message.error,
267274
url: Url::parse(&request.uri().to_string()).expect("invalid IPC request URL"),
268275
body: message.payload.into(),
269-
headers: message.options.map(|o| o.headers.0).unwrap_or_default(),
276+
headers: options.headers.0,
270277
invoke_key: message.invoke_key,
271278
};
272279

@@ -293,9 +300,7 @@ fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager
293300
.entered();
294301

295302
// the channel data command is the only command that uses a custom protocol on Linux
296-
if webview.manager().webview.invoke_responder.is_none()
297-
&& cmd != crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND
298-
{
303+
if webview.manager().webview.invoke_responder.is_none() {
299304
fn responder_eval<R: Runtime>(
300305
webview: &crate::Webview<R>,
301306
js: crate::Result<String>,
@@ -310,6 +315,10 @@ fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager
310315
let _ = webview.eval(&eval_js);
311316
}
312317

318+
let can_use_channel_for_response = cmd
319+
!= crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND
320+
&& !options.custom_protocol_ipc_blocked;
321+
313322
#[cfg(feature = "tracing")]
314323
let _response_span = tracing::trace_span!(
315324
"ipc::request::response",
@@ -327,6 +336,7 @@ fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager
327336
InvokeResponse::Ok(InvokeBody::Json(v)) => {
328337
if !(cfg!(target_os = "macos") || cfg!(target_os = "ios"))
329338
&& matches!(v, JsonValue::Object(_) | JsonValue::Array(_))
339+
&& can_use_channel_for_response
330340
{
331341
let _ = Channel::from_callback_fn(webview, callback).send(v);
332342
} else {
@@ -338,7 +348,10 @@ fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager
338348
}
339349
}
340350
InvokeResponse::Ok(InvokeBody::Raw(v)) => {
341-
if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
351+
if cfg!(target_os = "macos")
352+
|| cfg!(target_os = "ios")
353+
|| !can_use_channel_for_response
354+
{
342355
responder_eval(
343356
&webview,
344357
format_callback_result(Result::<_, ()>::Ok(v), callback, error),

0 commit comments

Comments
 (0)