Expand description
§waPC
This is the Rust implementation of the waPC host protocol. This crate defines the WapcHost
struct and the WebAssemblyEngineProvider
trait to provide dynamic execution of WebAssembly modules. For information on the implementations with WebAssembly engines, check out the provider crates below:
§wapc
The wapc
crate provides a high-level WebAssembly host runtime that conforms to an RPC mechanism called waPC (WebAssembly Procedure Calls). waPC is designed to be a fixed, lightweight standard allowing both sides of the guest/host boundary to make method calls containing arbitrary binary payloads. Neither side
of the contract is ever required to perform explicit allocation, ensuring maximum portability for wasm targets that might behave differently in the presence of garbage collectors and memory
relocation, compaction, etc.
To use wapc
, first you’ll need a waPC-compliant WebAssembly module (referred to as the guest) to load and execute. You can find a number of these samples available in the GitHub repository.
Next, you will need to chose a runtime engine. waPC describes the function call flow required for wasm-RPC, but it does not dictate how the low-level WebAssembly function calls are made. This allows you to select whatever engine best suits your needs, whether it’s a JIT-based engine or an interpreter-based one. Simply instantiate anything that implements the WebAssemblyEngineProvider trait and pass it to the WapcHost constructor and the WapcHost will facilitate all RPCs.
To make function calls, ensure that you provided a suitable host callback function (or closure) when you created your WapcHost. Then invoke the call
function to initiate the RPC flow.
§Example
The following is an example of synchronous, bi-directional procedure calls between a WebAssembly host runtime and the guest module.
use std::error::Error;
use wapc::WapcHost;
use wasmtime_provider::WasmtimeEngineProviderBuilder; // Or Wasm3EngineProvider
pub fn main() -> Result<(), Box<dyn Error>> {
// Sample host callback that prints the operation a WASM module requested.
let host_callback = |id: u64, bd: &str, ns: &str, op: &str, payload: &[u8]| {
println!("Guest {} invoked '{}->{}:{}' with a {} byte payload",
id, bd, ns, op, payload.len());
// Return success with zero-byte payload.
Ok(vec![])
};
let file = "../../wasm/crates/wasm-basic/build/wasm_basic.wasm";
let module_bytes = std::fs::read(file)?;
let engine = WasmtimeEngineProviderBuilder::new()
.module_bytes(&module_bytes)
.build()?;
let host = WapcHost::new(Box::new(engine), Some(Box::new(host_callback)))?;
let res = host.call("ping", b"payload bytes")?;
assert_eq!(res, b"payload bytes");
Ok(())
}
For running examples, take a look at the examples available in the individual engine provider repositories:
- wasmtime-provider - Utilizes the Bytecode Alliance runtime wasmtime for WebAssembly JIT compilation and execution.
- wasm3-provider - Uses the wasm3 C interpreter runtime (with a Rust wrapper)
§Notes
waPC is reactive. Hosts make requests and guests respond. During a request, guests can initiate calls back to the host and interact with the environment (via WASI). When a request is done the guest should be considered parked until the next request.
§async
Support
A waPC-compliant WebAssembly module can be used in an asynchronous context. This can
be done using a WapcHostAsync
and a provider that implements the WebAssemblyEngineProviderAsync
trait.
Currently only the wasmtime-provider
crate
provides an implementation of this trait.
Note: the async
support relies on the tokio runtime.
§Example
The following is an example of synchronous, bi-directional procedure calls between a WebAssembly host runtime and the guest module, all done inside an asynchronous context.
use std::error::Error;
use wapc::{HostCallbackAsync, WapcHostAsync};
use wasmtime_provider::WasmtimeEngineProviderBuilder;
async fn host_callback(
id: u64,
bd: String,
ns: String,
op: String,
payload: Vec<u8>,
) -> Result<Vec<u8>, Box<dyn std::error::Error + Send + Sync>> {
println!(
"Guest {} invoked '{}->{}:{}' on the host with a payload of '{}'",
id,
bd,
ns,
op,
::std::str::from_utf8(&payload).unwrap()
);
Ok(vec![])
}
#[tokio::main]
pub async fn main() -> Result<(), Box<dyn Error>> {
let file = "../../wasm/crates/wasm-basic/build/wasm_basic.wasm";
let module_bytes = std::fs::read(file)?;
let engine = WasmtimeEngineProviderBuilder::new()
.module_bytes(&module_bytes)
.build_async()?;
let callback: Box<HostCallbackAsync> = Box::new(move |id, bd, ns, op, payload| {
let fut = host_callback(id, bd, ns, op, payload);
Box::pin(fut)
});
let host = WapcHostAsync::new(Box::new(engine), Some(callback)).await?;
let res = host.call("ping", b"payload bytes").await?;
assert_eq!(res, b"payload bytes");
Ok(())
}
§RPC Exchange Flow
The following is a detailed outline of which functions are invoked and in which order to support
a waPC exchange flow, which is always triggered by a consumer invoking the call
function. Invoking
and handling these low-level functions is the responsibility of the engine provider, while
orchestrating the high-level control flow is the job of the WapcHost
.
- Host invokes
__guest_call
on the WebAssembly module (via the engine provider) - Guest calls the
__guest_request
function to instruct the host to write the request parameters to linear memory - Guest uses the
op_len
andmsg_len
parameters long with the pointer values it generated in step 2 to retrieve the operation (UTF-8 string) and payload (opaque byte array) - Guest performs work
- (Optional) Guest invokes
__host_call
on host with pointers and lengths indicating thebinding
,namespace
,operation
, and payload. - (Optional) Guest can use
__host_response
andhost_response_len
functions to obtain and interpret results - (Optional) Guest can use
__host_error_len
and__host_error
to obtain the host error if indicated (__host_call
returns 0)- Steps 5-7 can repeat with as many different host calls as the guest needs
- Guest will call
guest_error
to indicate if an error occurred during processing - Guest will call
guest_response
to store the opaque response payload - Guest will return 0 (error) or 1 (success) at the end of
__guest_call
§Required Host Exports
List of functions that must be exported by the host (imported by the guest)
Module | Function | Parameters | Description |
---|---|---|---|
wapc | __host_call | br_ptr: i32 bd_len: i32 ns_ptr: i32 ns_len: i32 op_ptr: i32 op_len: i32 ptr: i32 len: i32 -> i32 | Invoked to initiate a host call |
wapc | __console_log | ptr: i32, len: i32 | Allows guest to log to stdout |
wapc | __guest_request | op_ptr: i32 ptr: i32 | Writes the guest request payload and operation name to linear memory at the designated locations |
wapc | __host_response | ptr: i32 | Instructs host to write the host response payload to the given location in linear memory |
wapc | __host_response_len | -> i32 | Obtains the length of the current host response |
wapc | __guest_response | ptr: i32 len: i32 | Tells the host the size and location of the current guest response payload |
wapc | __guest_error | ptr: i32 len: i32 | Tells the host the size and location of the current guest error payload |
wapc | __host_error | ptr: i32 | Instructs the host to write the host error payload to the given location |
wapc | __host_error_len | -> i32 | Queries the host for the length of the current host error (0 if none) |
§Required Guest Exports
List of functions that must be exported by the guest (invoked by the host)
Function | Parameters | Description |
---|---|---|
__guest_call | op_len: i32 msg_len: i32 | Invoked by the host to start an RPC exchange with the guest module |
Modules§
- Library-specific error types and utility functions
- A list of the function names that are part of each waPC conversation
Structs§
- Represents a waPC invocation, which is a combination of an operation string and the corresponding binary payload
- Module state is essentially a ‘handle’ that is passed to a runtime engine to allow it to read and write relevant data as different low-level functions are executed during a waPC conversation
- Module
State Async async
Module state is essentially a ‘handle’ that is passed to a runtime engine to allow it to read and write relevant data as different low-level functions are executed during a waPC conversation - A WebAssembly host runtime for waPC-compliant modules
- Wapc
Host Async async
A WebAssembly host runtime for waPC-compliant modules that can be used in async contexts - Parameters defining the options for enabling WASI on a module (if applicable)
Constants§
- The host module name / namespace that guest modules must use for imports
Traits§
- The module host (waPC) must provide an implementation of this trait to the engine provider to enable waPC function calls.
- Module
Host Async async
The module host (waPC) must provide an implementation of this trait to the async engine provider to enable async waPC function calls. - An engine provider is any code that encapsulates low-level WebAssembly interactions such as reading from and writing to linear memory, executing functions, and mapping imports in a way that conforms to the waPC conversation protocol.
- An async engine provider is any code that encapsulates low-level WebAssembly interactions such as reading from and writing to linear memory, executing functions, and mapping imports in a way that conforms to the waPC conversation protocol.
Type Aliases§
- The signature of a Host Callback function.
- Host
Callback Async async
The signature of an async Host Callback function.