cardinal_wasm_plugins/
runner.rs1use crate::instance::WasmInstance;
2use crate::plugin::WasmPlugin;
3use crate::ExecutionContext;
4use cardinal_errors::internal::CardinalInternalError;
5use cardinal_errors::CardinalError;
6use std::collections::HashMap;
7use std::sync::Arc;
8use wasmer::TypedFunction;
9use wasmer::{Function, FunctionEnv, Store};
10
11#[derive(Debug, Copy, Clone)]
12pub enum ExecutionType {
13 Inbound,
14 Outbound,
15}
16
17pub struct ExecutionResult {
18 pub should_continue: bool,
19 pub execution_context: ExecutionContext,
20}
21
22pub type HostFunctionBuilder =
23 Arc<dyn Fn(&mut Store, &FunctionEnv<ExecutionContext>) -> Function + Send + Sync>;
24pub type HostFunctionMap = HashMap<String, Vec<(String, HostFunctionBuilder)>>;
25
26pub struct WasmRunner<'a> {
27 pub plugin: &'a WasmPlugin,
28 pub execution_type: ExecutionType,
29 host_imports: Option<&'a HostFunctionMap>,
30}
31
32impl<'a> WasmRunner<'a> {
33 pub fn new(
34 plugin: &'a WasmPlugin,
35 execution_type: ExecutionType,
36 host_imports: Option<&'a HostFunctionMap>,
37 ) -> Self {
38 Self {
39 plugin,
40 execution_type,
41 host_imports,
42 }
43 }
44
45 pub fn run(&self, exec_ctx: ExecutionContext) -> Result<ExecutionResult, CardinalError> {
46 let mut instance =
48 WasmInstance::from_plugin(self.plugin, self.execution_type, self.host_imports)?;
49
50 {
51 let ctx = instance.env.as_mut(&mut instance.store);
52 match ctx {
53 ExecutionContext::Inbound(inbound) => {
54 let inbound_ctx = exec_ctx.as_inbound().unwrap().to_owned();
55 inbound.req_headers = inbound_ctx.req_headers;
56 inbound.query = inbound_ctx.query;
57 inbound.body = inbound_ctx.body;
58 }
59 ExecutionContext::Outbound(outbound) => {
60 let inbound_ctx = exec_ctx.as_outbound().unwrap().to_owned();
61 outbound.req_headers = inbound_ctx.req_headers;
62 outbound.query = inbound_ctx.query;
63 outbound.body = inbound_ctx.body;
64 outbound.resp_headers = inbound_ctx.resp_headers;
65 outbound.status = inbound_ctx.status;
66 }
67 }
68 }
69
70 let handle: TypedFunction<(i32, i32), i32> = instance
72 .instance
73 .exports
74 .get_typed_function(&instance.store, "handle")
75 .map_err(|e| {
76 CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
77 "missing `handle` export {e}"
78 )))
79 })?;
80
81 let alloc: TypedFunction<(i32, i32), i32> = instance
82 .instance
83 .exports
84 .get_typed_function(&instance.store, "__new")
85 .map_err(|e| {
86 CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
87 "missing `alloc` export {e}"
88 )))
89 })?;
90
91 let body_opt = {
92 let ctx_ref = instance.env.as_ref(&instance.store);
93 ctx_ref.body().clone()
94 };
95
96 let (ptr, len) = if let Some(body) = body_opt.filter(|b| !b.is_empty()) {
97 let len = body.len() as i32;
98
99 let p = alloc.call(&mut instance.store, len, 0).map_err(|e| {
100 CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
101 "Alloc failed {e}"
102 )))
103 })?;
104
105 {
106 let view = instance.memory.view(&instance.store);
107 view.write(p as u64, &body).map_err(|e| {
108 CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
109 "Writing Body failed {e}"
110 )))
111 })?;
112 }
113
114 (p, len)
115 } else {
116 (0, 0)
117 };
118
119 let decision = handle.call(&mut instance.store, ptr, len).map_err(|e| {
120 CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
121 "WASM Handle call failed {e}"
122 )))
123 })?;
124
125 Ok(ExecutionResult {
126 should_continue: decision == 1,
127 execution_context: instance.env.as_ref(&instance.store).clone(),
128 })
129 }
130}