c14d699ee0
将桌面网络探测超时提升到共享 HostBridge 契约 让 Tauri 网络状态探测镜像共享超时边界 增加桌面壳配置门禁反查网络探测超时 补充宿主壳方案文档和共享决策记录
142 lines
4.4 KiB
Rust
142 lines
4.4 KiB
Rust
use crate::shell::events::host_bridge_event_script;
|
|
use crate::shell::url::WEB_APP_ORIGIN;
|
|
use serde_json::{json, Value};
|
|
use std::net::{TcpStream, ToSocketAddrs};
|
|
use std::time::Duration;
|
|
use tauri::{Url, WebviewWindow};
|
|
|
|
pub(crate) const DESKTOP_NETWORK_CHECK_TIMEOUT_MS: u64 = 1200;
|
|
|
|
fn desktop_network_probe_target() -> Option<(String, u16)> {
|
|
let url = Url::parse(WEB_APP_ORIGIN).ok()?;
|
|
if url.scheme() != "https" {
|
|
return None;
|
|
}
|
|
|
|
Some((
|
|
url.host_str()?.to_string(),
|
|
url.port_or_known_default()?,
|
|
))
|
|
}
|
|
|
|
pub(crate) fn desktop_network_status_payload(is_online: bool) -> Value {
|
|
json!({
|
|
"isConnected": is_online,
|
|
"isInternetReachable": is_online,
|
|
"connectionType": if is_online { "unknown" } else { "none" },
|
|
"nativeType": if is_online { "online" } else { "offline" },
|
|
})
|
|
}
|
|
|
|
pub(crate) fn resolve_desktop_network_status() -> Value {
|
|
let timeout = Duration::from_millis(DESKTOP_NETWORK_CHECK_TIMEOUT_MS);
|
|
let is_reachable = desktop_network_probe_target()
|
|
.and_then(|(host, port)| (host.as_str(), port).to_socket_addrs().ok())
|
|
.map(|addresses| {
|
|
addresses.into_iter().any(|address| {
|
|
TcpStream::connect_timeout(&address, timeout)
|
|
.map(|stream| {
|
|
drop(stream);
|
|
true
|
|
})
|
|
.unwrap_or(false)
|
|
})
|
|
})
|
|
.unwrap_or(false);
|
|
|
|
desktop_network_status_payload(is_reachable)
|
|
}
|
|
|
|
pub(crate) fn register_desktop_network_events(window: &WebviewWindow) -> tauri::Result<()> {
|
|
let online_script = host_bridge_event_script(
|
|
"network.statusChanged",
|
|
desktop_network_status_payload(true),
|
|
)
|
|
.map_err(tauri::Error::Json)?;
|
|
let offline_script = host_bridge_event_script(
|
|
"network.statusChanged",
|
|
desktop_network_status_payload(false),
|
|
)
|
|
.map_err(tauri::Error::Json)?;
|
|
let current_status_script = host_bridge_event_script(
|
|
"network.statusChanged",
|
|
json!({
|
|
"isConnected": "__GENARRATIVE_DESKTOP_ONLINE__",
|
|
"isInternetReachable": "__GENARRATIVE_DESKTOP_ONLINE__",
|
|
"connectionType": "__GENARRATIVE_DESKTOP_CONNECTION_TYPE__",
|
|
"nativeType": "__GENARRATIVE_DESKTOP_NATIVE_TYPE__",
|
|
}),
|
|
)
|
|
.map_err(tauri::Error::Json)?
|
|
.replace("\"__GENARRATIVE_DESKTOP_ONLINE__\"", "navigator.onLine")
|
|
.replace(
|
|
"\"__GENARRATIVE_DESKTOP_CONNECTION_TYPE__\"",
|
|
"(navigator.onLine ? 'unknown' : 'none')",
|
|
)
|
|
.replace(
|
|
"\"__GENARRATIVE_DESKTOP_NATIVE_TYPE__\"",
|
|
"(navigator.onLine ? 'online' : 'offline')",
|
|
);
|
|
|
|
let script = format!(
|
|
"(() => {{
|
|
if (window.__GENARRATIVE_DESKTOP_NETWORK_LISTENER_INSTALLED__) {{
|
|
return true;
|
|
}}
|
|
window.__GENARRATIVE_DESKTOP_NETWORK_LISTENER_INSTALLED__ = true;
|
|
const emitOnline = () => {{ {} }};
|
|
const emitOffline = () => {{ {} }};
|
|
window.addEventListener('online', emitOnline);
|
|
window.addEventListener('offline', emitOffline);
|
|
{}
|
|
return true;
|
|
}})();",
|
|
online_script, offline_script, current_status_script
|
|
);
|
|
|
|
window.eval(script)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use serde_json::json;
|
|
|
|
#[test]
|
|
fn desktop_network_probe_target_uses_public_web_origin() {
|
|
let origin = Url::parse(WEB_APP_ORIGIN).expect("desktop web origin");
|
|
|
|
assert_eq!(
|
|
desktop_network_probe_target(),
|
|
Some((
|
|
origin.host_str().expect("desktop web host").to_string(),
|
|
origin
|
|
.port_or_known_default()
|
|
.expect("desktop web default port")
|
|
))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn desktop_network_status_payload_reports_reachability() {
|
|
assert_eq!(
|
|
desktop_network_status_payload(true),
|
|
json!({
|
|
"isConnected": true,
|
|
"isInternetReachable": true,
|
|
"connectionType": "unknown",
|
|
"nativeType": "online",
|
|
})
|
|
);
|
|
assert_eq!(
|
|
desktop_network_status_payload(false),
|
|
json!({
|
|
"isConnected": false,
|
|
"isInternetReachable": false,
|
|
"connectionType": "none",
|
|
"nativeType": "offline",
|
|
})
|
|
);
|
|
}
|
|
}
|