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.
This commit is contained in:
Lucas Fernandes Nogueira
2024-08-13 07:38:49 -03:00
committed by GitHub
parent 794cf8234f
commit b1d9ffa1ab
3 changed files with 33 additions and 8 deletions

View File

@@ -0,0 +1,5 @@
---
"tauri": patch:bug
---
Fix IPC fallback (postMessage implementation when custom protocol fails) hanging when sending responses.

View File

@@ -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
@@ -74,7 +78,10 @@
cmd,
callback,
error,
options,
options: {
...options,
customProtocolIpcBlocked: customProtocolIpcFailed
},
payload,
__TAURI_INVOKE_KEY__
})

View File

@@ -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 {
@@ -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)]
@@ -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,
};
@@ -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>,
@@ -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",
@@ -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 {
@@ -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),