Skip to content

Commit a77be97

Browse files
authored
fix(ipc): fallback to postMessage if protocol fails, closes #8476 (#9038)
1 parent c68218b commit a77be97

File tree

7 files changed

+87
-86
lines changed

7 files changed

+87
-86
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": patch:enhance
3+
---
4+
5+
Fallback to the postMessage IPC interface if we cannot reach the IPC custom protocol.

core/tauri/build.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -217,11 +217,6 @@ fn main() {
217217
alias("desktop", !mobile);
218218
alias("mobile", mobile);
219219

220-
alias(
221-
"ipc_custom_protocol",
222-
target_os != "android" && (target_os != "linux" || has_feature("linux-ipc-protocol")),
223-
);
224-
225220
let out_dir = PathBuf::from(var("OUT_DIR").unwrap());
226221

227222
let checked_features_out_path = out_dir.join("checked_features");

core/tauri/scripts/ipc-protocol.js

Lines changed: 62 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,72 +6,73 @@
66
const processIpcMessage = __RAW_process_ipc_message_fn__
77
const osName = __TEMPLATE_os_name__
88
const fetchChannelDataCommand = __TEMPLATE_fetch_channel_data_command__
9-
const useCustomProtocol = __TEMPLATE_use_custom_protocol__
9+
const linuxIpcProtocolEnabled = __TEMPLATE_linux_ipc_protocol_enabled__
10+
let customProtocolIpcFailed = false
1011

11-
Object.defineProperty(window.__TAURI_INTERNALS__, 'postMessage', {
12-
value: (message) => {
13-
const { cmd, callback, error, payload, options } = message
12+
// on Linux we only use the custom-protocol-based IPC if the the linux-ipc-protocol Cargo feature is enabled
13+
// on Android we never use it because Android does not have support to reading the request body
14+
const canUseCustomProtocol =
15+
osName === 'linux' ? linuxIpcProtocolEnabled : osName !== 'android'
16+
17+
function sendIpcMessage(message) {
18+
const { cmd, callback, error, payload, options } = message
1419

15-
// use custom protocol for IPC if:
16-
// - the flag is set to true or
17-
// - the command is the fetch data command or
18-
// - when not on Linux/Android
19-
// AND
20-
// - when not on macOS with an https URL
21-
if (
22-
(useCustomProtocol ||
23-
cmd === fetchChannelDataCommand ||
24-
!(osName === 'linux' || osName === 'android')) &&
25-
!(
26-
(osName === 'macos' || osName === 'ios') &&
27-
location.protocol === 'https:'
28-
)
29-
) {
30-
const { contentType, data } = processIpcMessage(payload)
31-
fetch(window.__TAURI_INTERNALS__.convertFileSrc(cmd, 'ipc'), {
32-
method: 'POST',
33-
body: data,
34-
headers: {
35-
'Content-Type': contentType,
36-
'Tauri-Callback': callback,
37-
'Tauri-Error': error,
38-
...options?.headers
20+
if (
21+
!customProtocolIpcFailed &&
22+
(canUseCustomProtocol || cmd === fetchChannelDataCommand)
23+
) {
24+
const { contentType, data } = processIpcMessage(payload)
25+
fetch(window.__TAURI_INTERNALS__.convertFileSrc(cmd, 'ipc'), {
26+
method: 'POST',
27+
body: data,
28+
headers: {
29+
'Content-Type': contentType,
30+
'Tauri-Callback': callback,
31+
'Tauri-Error': error,
32+
...options?.headers
33+
}
34+
})
35+
.then((response) => {
36+
const cb = response.ok ? callback : error
37+
// we need to split here because on Android the content-type gets duplicated
38+
switch ((response.headers.get('content-type') || '').split(',')[0]) {
39+
case 'application/json':
40+
return response.json().then((r) => [cb, r])
41+
case 'text/plain':
42+
return response.text().then((r) => [cb, r])
43+
default:
44+
return response.arrayBuffer().then((r) => [cb, r])
45+
}
46+
})
47+
.then(([cb, data]) => {
48+
if (window[`_${cb}`]) {
49+
window[`_${cb}`](data)
50+
} else {
51+
console.warn(
52+
`[TAURI] Couldn't find callback id {cb} in window. This might happen when the app is reloaded while Rust is running an asynchronous operation.`
53+
)
3954
}
4055
})
41-
.then((response) => {
42-
const cb = response.ok ? callback : error
43-
// we need to split here because on Android the content-type gets duplicated
44-
switch (
45-
(response.headers.get('content-type') || '').split(',')[0]
46-
) {
47-
case 'application/json':
48-
return response.json().then((r) => [cb, r])
49-
case 'text/plain':
50-
return response.text().then((r) => [cb, r])
51-
default:
52-
return response.arrayBuffer().then((r) => [cb, r])
53-
}
54-
})
55-
.then(([cb, data]) => {
56-
if (window[`_${cb}`]) {
57-
window[`_${cb}`](data)
58-
} else {
59-
console.warn(
60-
`[TAURI] Couldn't find callback id {cb} in window. This might happen when the app is reloaded while Rust is running an asynchronous operation.`
61-
)
62-
}
63-
})
64-
} else {
65-
// otherwise use the postMessage interface
66-
const { data } = processIpcMessage({
67-
cmd,
68-
callback,
69-
error,
70-
options,
71-
payload
56+
.catch(() => {
57+
// failed to use the custom protocol IPC (either the webview blocked a custom protocol or it was a CSP error)
58+
// so we need to fallback to the postMessage interface
59+
customProtocolIpcFailed = true
60+
sendIpcMessage(message)
7261
})
73-
window.ipc.postMessage(data)
74-
}
62+
} else {
63+
// otherwise use the postMessage interface
64+
const { data } = processIpcMessage({
65+
cmd,
66+
callback,
67+
error,
68+
options,
69+
payload
70+
})
71+
window.ipc.postMessage(data)
7572
}
73+
}
74+
75+
Object.defineProperty(window.__TAURI_INTERNALS__, 'postMessage', {
76+
value: sendIpcMessage
7677
})
7778
})()

core/tauri/src/app.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,7 @@ struct InvokeInitializationScript<'a> {
10841084
process_ipc_message_fn: &'a str,
10851085
os_name: &'a str,
10861086
fetch_channel_data_command: &'a str,
1087-
use_custom_protocol: bool,
1087+
linux_ipc_protocol_enabled: bool,
10881088
}
10891089

10901090
/// Make `Wry` the default `Runtime` for `Builder`
@@ -1117,7 +1117,7 @@ impl<R: Runtime> Builder<R> {
11171117
process_ipc_message_fn: crate::manager::webview::PROCESS_IPC_MESSAGE_FN,
11181118
os_name: std::env::consts::OS,
11191119
fetch_channel_data_command: crate::ipc::channel::FETCH_CHANNEL_DATA_COMMAND,
1120-
use_custom_protocol: cfg!(ipc_custom_protocol),
1120+
linux_ipc_protocol_enabled: cfg!(feature = "linux-ipc-protocol"),
11211121
}
11221122
.render_default(&Default::default())
11231123
.unwrap()

core/tauri/src/ipc/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ use crate::{webview::Webview, Runtime, StateManager};
2121
mod authority;
2222
pub(crate) mod channel;
2323
mod command;
24-
#[cfg(any(target_os = "macos", target_os = "ios", not(ipc_custom_protocol)))]
2524
pub(crate) mod format_callback;
2625
pub(crate) mod protocol;
2726

core/tauri/src/ipc/protocol.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use super::{CallbackFn, InvokeBody, InvokeResponse};
1919
const TAURI_CALLBACK_HEADER_NAME: &str = "Tauri-Callback";
2020
const TAURI_ERROR_HEADER_NAME: &str = "Tauri-Error";
2121

22-
#[cfg(any(target_os = "macos", target_os = "ios", not(ipc_custom_protocol)))]
2322
pub fn message_handler<R: Runtime>(
2423
manager: Arc<AppManager<R>>,
2524
) -> crate::runtime::webview::WebviewIpcHandler<crate::EventLoopMessage, R> {
@@ -162,7 +161,6 @@ pub fn get<R: Runtime>(manager: Arc<AppManager<R>>, label: String) -> UriSchemeP
162161
})
163162
}
164163

165-
#[cfg(any(target_os = "macos", target_os = "ios", not(ipc_custom_protocol)))]
166164
fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, label: &str) {
167165
if let Some(webview) = manager.get_webview(label) {
168166
#[cfg(feature = "tracing")]
@@ -374,15 +372,21 @@ fn parse_invoke_request<R: Runtime>(
374372
.decode_utf8_lossy()
375373
.to_string();
376374

377-
// the body is not set if ipc_custom_protocol is not enabled so we'll just ignore it
378-
#[cfg(all(feature = "isolation", ipc_custom_protocol))]
375+
// on Android and on Linux (without the linux-ipc-protocol Cargo feature) we cannot read the request body
376+
// so we must ignore it because some commands use the IPC for faster response
377+
let has_payload = !body.is_empty();
378+
379+
#[cfg(feature = "isolation")]
379380
if let crate::Pattern::Isolation { crypto_keys, .. } = &*manager.pattern {
380-
#[cfg(feature = "tracing")]
381-
let _span = tracing::trace_span!("ipc::request::decrypt_isolation_payload").entered();
381+
// if the platform does not support request body, we ignore it
382+
if has_payload {
383+
#[cfg(feature = "tracing")]
384+
let _span = tracing::trace_span!("ipc::request::decrypt_isolation_payload").entered();
382385

383-
body = crate::utils::pattern::isolation::RawIsolationPayload::try_from(&body)
384-
.and_then(|raw| crypto_keys.decrypt(raw))
385-
.map_err(|e| e.to_string())?;
386+
body = crate::utils::pattern::isolation::RawIsolationPayload::try_from(&body)
387+
.and_then(|raw| crypto_keys.decrypt(raw))
388+
.map_err(|e| e.to_string())?;
389+
}
386390
}
387391

388392
let callback = CallbackFn(
@@ -420,12 +424,12 @@ fn parse_invoke_request<R: Runtime>(
420424
let body = if content_type == mime::APPLICATION_OCTET_STREAM {
421425
body.into()
422426
} else if content_type == mime::APPLICATION_JSON {
423-
if cfg!(ipc_custom_protocol) {
427+
// if the platform does not support request body, we ignore it
428+
if has_payload {
424429
serde_json::from_slice::<serde_json::Value>(&body)
425430
.map_err(|e| e.to_string())?
426431
.into()
427432
} else {
428-
// the body is not set if ipc_custom_protocol is not enabled so we'll just ignore it
429433
serde_json::Value::Object(Default::default()).into()
430434
}
431435
} else {

core/tauri/src/manager/webview.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -496,12 +496,9 @@ impl<R: Runtime> WebviewManager<R> {
496496
manager,
497497
)?;
498498

499-
#[cfg(any(target_os = "macos", target_os = "ios", not(ipc_custom_protocol)))]
500-
{
501-
pending.ipc_handler = Some(crate::ipc::protocol::message_handler(
502-
manager.manager_owned(),
503-
));
504-
}
499+
pending.ipc_handler = Some(crate::ipc::protocol::message_handler(
500+
manager.manager_owned(),
501+
));
505502

506503
// in `windows`, we need to force a data_directory
507504
// but we do respect user-specification

0 commit comments

Comments
 (0)