Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 13 additions & 29 deletions crates/shell/src/wasm_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
//! is left of the quota is what a call may add (a write past it fails inside
//! the component, as a full disk).

use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::collections::{BTreeMap, HashMap};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
Expand Down Expand Up @@ -99,14 +99,14 @@ static HOST_WAITING: Mutex<Option<Waiting>> = Mutex::new(None);
/// isolate's.
static NEXT_HOST_KEY: AtomicUsize = AtomicUsize::new(1 << 50);

/// An app's host services as its components reach them: the families its
/// admitted manifest grants (a system app's own namespace too), dispatched
/// on the UI thread as its script's `host.request` would be, but never with
/// a sheet or a prompt, so only the methods a background surface may call.
/// An app's host services as its components reach them: dispatched on the
/// UI thread as its script's `host.request` would be, and like it with no
/// check of the families the manifest declares (makepad#118, OctoSense
/// #450): a service that needs a grant checks it itself. Never with a sheet
/// or a prompt, so only the methods a background surface may call.
struct AppHostCalls {
app: String,
host_dir: PathBuf,
families: BTreeSet<String>,
}

impl HostCalls for AppHostCalls {
Expand All @@ -118,16 +118,6 @@ impl HostCalls for AppHostCalls {
"a component cannot call wasm.*: its app's functions are already running it".into(),
);
}
let own = self
.app
.strip_prefix(octosense_appstore::system::SYSTEM_ID_PREFIX)
== Some(family);
if !self.families.contains(family) && !own {
return Err(format!(
"{} was not granted the {family} service, which {service} needs",
self.app
));
}
let args: Value = serde_json::from_str(args)
.map_err(|e| format!("{service}: the arguments are not JSON: {e}"))?;
let (done, answer) = sync_channel(1);
Expand Down Expand Up @@ -758,12 +748,6 @@ impl Lab {
host_calls: Arc::new(AppHostCalls {
app: app.to_string(),
host_dir: admission.root.join(".host"),
families: admission.manifest["capabilities"]
.as_array()
.into_iter()
.flatten()
.filter_map(|c| c.as_str().map(str::to_string))
.collect(),
}),
admission: admission.clone(),
};
Expand Down Expand Up @@ -2078,7 +2062,7 @@ mod tests {
/// services, dispatched on the UI thread as its script's would be (a
/// thread here plays the UI's part); nothing else, and never `wasm.*`.
#[test]
fn a_components_host_calls_reach_only_its_apps_granted_services() {
fn a_components_host_calls_reach_the_host_as_its_apps_script_does() {
struct Echo;
impl HostService for Echo {
fn family(&self) -> &'static str {
Expand Down Expand Up @@ -2116,7 +2100,9 @@ mod tests {
let admission = Admission {
root: root.clone(),
bundle,
manifest: json!({"capabilities": ["wasm", "wasmhostecho"]}),
// No wasmhostecho capability: a host call, like the script's
// host.request, is not checked against the declared families.
manifest: json!({"capabilities": ["wasm"]}),
};
let mut lab = Lab::from_bundle(
"org.example.hostcalls",
Expand Down Expand Up @@ -2146,11 +2132,9 @@ mod tests {
answer,
json!({"app": "org.example.hostcalls", "method": "get", "args": {"id": 1}, "may_prompt": false})
);
let error = call("mail.list", "{}").unwrap_err();
assert!(
error.contains("was not granted the mail service"),
"{error}"
);
// Use a unique family: parallel shell tests may register real services.
let error = call("wasmhostmissing.get", "{}").unwrap_err();
assert!(error.contains(r#"no service answers "wasmhostmissing""#), "{error}");
let error = call("wasm.functions", "{}").unwrap_err();
assert!(error.contains("cannot call wasm.*"), "{error}");
let error = call("wasmhostecho.get", "not json").unwrap_err();
Expand Down
8 changes: 5 additions & 3 deletions docs/adr/0014-app-components-in-webassembly.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ component reaches only what its app may already reach.
end it, so its timeouts are clamped to the call's deadline, which is
10 s for a component that imports `wasi:http`.
- **`octosense:host`, phase 3:** `request(service, args)` reaches the host
services the app is granted, dispatched on the UI thread as the app's
script's `host.request` is, but with no sheet and no prompt (only
methods a background surface may call), and never `wasm.*`.
services as the app's script's `host.request` does, dispatched on the UI
thread and, since makepad#118 (OctoSense #450), not checked against the
families the manifest declares (a service that needs a grant checks it
itself), but with no sheet and no prompt (only methods a background
surface may call), and never `wasm.*`.
- **Never:** `wasi:sockets`, and any import outside these packages. A
component that asks for one is refused when it loads
(`LoadError::Import`) and by App Hub's gate.
Expand Down
6 changes: 3 additions & 3 deletions docs/adr/0014-app-components-in-webassembly.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ crates.io 上的大多数 crate 要么需要上述缺失能力中的某些(时
运行时闸门,边界是操作系统和宿主的 API 表面。把组件限制在应用的 `network.hosts` 内、后来又限制
为公共 HTTPS 主机的做法,都已按该裁定否决。请求在客体之外等待,epoch 检查无法结束它,因此
请求的超时被限制在调用的截止时间内;导入 `wasi:http` 的组件每次调用有 10 秒。
- **`octosense:host`(第 3 阶段):** `request(service, args)` 可以调用应用已获授权的宿主服务,
像应用脚本的 `host.request` 一样在 UI 线程上分派,但不打开面板、不询问用户(只能调用后台界面
可以调用的方法),并且绝不调用 `wasm.*`。
- **`octosense:host`(第 3 阶段):** `request(service, args)` 像应用脚本的 `host.request` 一样调用
宿主服务,在 UI 线程上分派;自 makepad#118(OctoSense #450)起不检查清单声明的服务族(需要授权的
服务自己检查),但不打开面板、不询问用户(只能调用后台界面可以调用的方法),并且绝不调用 `wasm.*`。
- **从不提供:** `wasi:sockets`,以及上述包以外的任何导入。请求这些导入的组件在加载时被拒绝
(`LoadError::Import`),App Hub 的审核闸门也会拒绝。
5. **无需编写 WIT。**
Expand Down
9 changes: 5 additions & 4 deletions docs/wasm.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,11 @@ two kinds apart by the file's preamble, and a bundle may carry both.
connect, first-byte and between-bytes timeouts end with the call.
- **Host services.** Through `octosense:host`
([`crates/wasm-host/wit/octosense-host.wit`](../crates/wasm-host/wit/octosense-host.wit)),
`request(service, args)` calls one of the host services its app is
granted, as the app's script does: the same families, dispatched on the UI
thread, but never with a sheet or a prompt, so only methods a background
surface may call. `wasm.*` is refused, since the app's worker is busy with
`request(service, args)` calls a host service as the app's script does:
dispatched on the UI thread, with no check of the families the manifest
declares (makepad#118; a service that needs a grant checks it itself), but
never with a sheet or a prompt, so only methods a background surface may
call. `wasm.*` is refused, since the app's worker is busy with
the call, and the call's deadline bounds the wait.

ADR 0014 records what this costs. On an M-series Mac, the 313 KiB test
Expand Down
5 changes: 3 additions & 2 deletions docs/wasm.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ Rust crate 构建。服务按文件头区分两者,一个应用包可以同时
每个请求的连接、首字节和字节间超时都随调用结束。
- **宿主服务。** 通过 `octosense:host`
([`crates/wasm-host/wit/octosense-host.wit`](../crates/wasm-host/wit/octosense-host.wit)),
组件可以用 `request(service, args)` 调用应用已获授权的宿主服务,与应用的脚本相同:同样的服务族,
在 UI 线程上分派,但绝不打开面板、不询问用户,因此只能调用后台界面可以调用的方法。`wasm.*`
组件可以用 `request(service, args)` 像应用的脚本一样调用宿主服务:在 UI 线程上分派,不检查清单
声明的服务族(makepad#118;需要授权的服务自己检查),但绝不打开面板、不询问用户,因此只能调用
后台界面可以调用的方法。`wasm.*`
被拒绝,因为应用的工作线程正忙于这次调用;调用的截止时间限制等待时长。

ADR 0014 记录了开销。在 M 系列 Mac 上,313 KiB 的测试组件约 40 毫秒完成编译,从缓存加载
Expand Down
Loading