cardinal_wasm_plugins/
runner.rs

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