Skip to content

hiddenbit/duplex-rpc-rust

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

duplex-rpc (Rust / WASM)

A Rust companion library for duplex-rpc (see there for more details about the RPC protocol and its motivation), a TypeScript duplex RPC framework. It lets you write WASI-based WebAssembly binaries in Rust that expose functions (including streaming) to a TypeScript host process over stdin/stdout.

⚠️ This is an experimental prototype and not intended for production use. ⚠️

What it provides

  • RpcServer: a single-threaded, cooperative async event loop that reads incoming RPC messages from stdin and writes responses to stdout.
  • CallContext: per-call handle passed to every handler, giving access to streaming primitives (stream_reader, stream_writer) and the ability to call back into the host (call_host).
  • StreamReader / StreamWriter: async and sync helpers for consuming and producing streams over the RPC channel.

The underlying wire protocol (length-prefixed MessagePack frames) is defined and documented in the duplex-rpc TypeScript repository. This crate implements the same protocol so that Rust WASM binaries can participate as peers.

Example

The following example exposes a single add function that takes two numbers and returns their sum. When compiled to wasm32-wasip1, the resulting .wasm binary can be spawned by a TypeScript host using duplex-rpc.

Rust side

use duplex_rpc::{CallContext, RpcServer, rmpv};

fn main() {
    let mut server = RpcServer::new();
    server.register("add", handle_add);
    server
        .run(std::io::stdin(), std::io::stdout())
        .expect("RPC server error");
}

async fn handle_add(
    _ctx: CallContext,
    params: Option<rmpv::Value>,
) -> Result<Option<rmpv::Value>, String> {
    let args = params
        .and_then(|v| if v.is_array() { Some(v) } else { None })
        .ok_or("expected an array of arguments")?;

    let arr = args.as_array().unwrap();
    let a = arr.first().and_then(rmpv::Value::as_f64).unwrap_or(0.0);
    let b = arr.get(1).and_then(rmpv::Value::as_f64).unwrap_or(0.0);

    Ok(Some(rmpv::Value::F64(a + b)))
}

Compile with:

cargo build --target wasm32-wasip1 --release

TypeScript host

const child = spawn('wasmtime', ['run', 'path/to/duplex_rpc.wasm'], {
  stdio: ['pipe', 'pipe', 'inherit'],
});

const transport = duplexFromSpawnedProcess(child);
using api = createApiClientFromDuplex(transport, stringLengthApi);

const result = await api.add(2, 3);
console.log(result); // 5

child.kill();

About

RPC over duplex streams. Provides request/response calls, callbacks, timeouts, and pluggable transports such as subprocess stdin/stdout.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages