Skip to content

Commit

Permalink
fix(core): IPC fallback hanging when sending responses, closes #10327 (
Browse files Browse the repository at this point in the history
…#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.
  • Loading branch information
lucasfernog authored Aug 13, 2024
1 parent 794cf82 commit b1d9ffa
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .changes/fix-ipc-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"tauri": patch:bug
---

Fix IPC fallback (postMessage implementation when custom protocol fails) hanging when sending responses.
11 changes: 9 additions & 2 deletions core/tauri/scripts/ipc-protocol.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@
)
}
})
.catch(() => {
.catch((e) => {
console.warn(
'IPC custom protocol failed, Tauri will now use the postMessage interface instead',
e
)
// failed to use the custom protocol IPC (either the webview blocked a custom protocol or it was a CSP error)
// so we need to fallback to the postMessage interface
customProtocolIpcFailed = true
Expand All @@ -74,7 +78,10 @@
cmd,
callback,
error,
options,
options: {
...options,
customProtocolIpcBlocked: customProtocolIpcFailed
},
payload,
__TAURI_INVOKE_KEY__
})
Expand Down
25 changes: 19 additions & 6 deletions core/tauri/src/ipc/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager

use serde::{Deserialize, Deserializer};

#[derive(Default)]
pub(crate) struct HeaderMap(http::HeaderMap);

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

#[derive(Deserialize)]
#[derive(Deserialize, Default)]
#[serde(rename_all = "camelCase")]
struct RequestOptions {
#[serde(default)]
headers: HeaderMap,
#[serde(default)]
custom_protocol_ipc_blocked: bool,
}

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

match message {
Ok(message) => {
let options = message.options.unwrap_or_default();

let request = InvokeRequest {
cmd: message.cmd,
callback: message.callback,
error: message.error,
url: Url::parse(&request.uri().to_string()).expect("invalid IPC request URL"),
body: message.payload.into(),
headers: message.options.map(|o| o.headers.0).unwrap_or_default(),
headers: options.headers.0,
invoke_key: message.invoke_key,
};

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

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

let can_use_channel_for_response = cmd
!= crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND
&& !options.custom_protocol_ipc_blocked;

#[cfg(feature = "tracing")]
let _response_span = tracing::trace_span!(
"ipc::request::response",
Expand All @@ -327,6 +336,7 @@ fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager
InvokeResponse::Ok(InvokeBody::Json(v)) => {
if !(cfg!(target_os = "macos") || cfg!(target_os = "ios"))
&& matches!(v, JsonValue::Object(_) | JsonValue::Array(_))
&& can_use_channel_for_response
{
let _ = Channel::from_callback_fn(webview, callback).send(v);
} else {
Expand All @@ -338,7 +348,10 @@ fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager
}
}
InvokeResponse::Ok(InvokeBody::Raw(v)) => {
if cfg!(target_os = "macos") || cfg!(target_os = "ios") {
if cfg!(target_os = "macos")
|| cfg!(target_os = "ios")
|| !can_use_channel_for_response
{
responder_eval(
&webview,
format_callback_result(Result::<_, ()>::Ok(v), callback, error),
Expand Down

0 comments on commit b1d9ffa

Please sign in to comment.