cardinal_wasm_plugins/host/
mod.rs1use crate::context::ExecutionContext;
2use crate::runner::ExecutionPhase;
3use crate::utils::{read_bytes, with_mem_view, write_bytes};
4use crate::SharedExecutionContext;
5use std::collections::HashMap;
6use std::sync::Arc;
7use wasmer::{Exports, Function, FunctionEnv, FunctionEnvMut, Imports, Store};
8
9mod abort;
10pub mod get_header;
11mod get_query_param;
12mod get_req_var;
13mod set_header;
14mod set_req_var;
15mod set_status;
16
17use self::abort::ABORT_IMPORT;
18use self::get_header::GET_HEADER_IMPORT;
19use self::get_query_param::GET_QUERY_PARAM_IMPORT;
20use self::get_req_var::GET_REQ_VAR_IMPORT;
21use self::set_header::SET_HEADER_IMPORT;
22use self::set_req_var::SET_REQ_VAR_IMPORT;
23use self::set_status::SET_STATUS_IMPORT;
24
25pub type HostFunctionBuilder =
26 Arc<dyn Fn(&mut Store, &FunctionEnv<SharedExecutionContext>) -> Function + Send + Sync>;
27
28pub trait HostImport: Send + Sync {
29 fn namespace(&self) -> &str;
30 fn name(&self) -> &str;
31 fn build(&self, store: &mut Store, env: &FunctionEnv<SharedExecutionContext>) -> Function;
32}
33
34pub type HostImportHandle = Arc<dyn HostImport>;
35
36#[derive(Clone)]
37pub struct DynamicHostImport {
38 namespace: String,
39 name: String,
40 builder: HostFunctionBuilder,
41}
42
43impl DynamicHostImport {
44 pub fn new<N, S>(namespace: N, name: S, builder: HostFunctionBuilder) -> Self
45 where
46 N: Into<String>,
47 S: Into<String>,
48 {
49 Self {
50 namespace: namespace.into(),
51 name: name.into(),
52 builder,
53 }
54 }
55}
56
57impl HostImport for DynamicHostImport {
58 fn namespace(&self) -> &str {
59 &self.namespace
60 }
61
62 fn name(&self) -> &str {
63 &self.name
64 }
65
66 fn build(&self, store: &mut Store, env: &FunctionEnv<SharedExecutionContext>) -> Function {
67 (self.builder)(store, env)
68 }
69}
70
71pub fn make_imports(
72 store: &mut Store,
73 env: &FunctionEnv<SharedExecutionContext>,
74 phase: ExecutionPhase,
75 dynamic_imports: &[HostImportHandle],
76) -> Imports {
77 let mut namespaces: HashMap<String, Exports> = HashMap::new();
78
79 for import in builtin_imports(phase) {
80 register_import(&mut namespaces, store, env, *import);
81 }
82
83 for import in dynamic_imports {
84 register_import(&mut namespaces, store, env, import.as_ref());
85 }
86
87 let mut imports = Imports::new();
88 for (namespace, exports) in namespaces {
89 imports.register_namespace(&namespace, exports);
90 }
91 imports
92}
93
94fn register_import(
95 namespaces: &mut HashMap<String, Exports>,
96 store: &mut Store,
97 env: &FunctionEnv<SharedExecutionContext>,
98 import: &dyn HostImport,
99) {
100 let namespace = import.namespace().to_string();
101 let exports = namespaces.entry(namespace.clone()).or_default();
102 let function = import.build(store, env);
103 exports.insert(import.name(), function);
104}
105
106fn builtin_imports(phase: ExecutionPhase) -> &'static [&'static dyn HostImport] {
107 match phase {
108 ExecutionPhase::Inbound => INBOUND_IMPORTS,
109 ExecutionPhase::Outbound => OUTBOUND_IMPORTS,
110 }
111}
112
113static INBOUND_IMPORTS: &[&dyn HostImport] = &[
114 &ABORT_IMPORT,
115 &GET_HEADER_IMPORT,
116 &GET_QUERY_PARAM_IMPORT,
117 &SET_HEADER_IMPORT,
118 &SET_STATUS_IMPORT,
119 &SET_REQ_VAR_IMPORT,
120 &GET_REQ_VAR_IMPORT,
121];
122
123static OUTBOUND_IMPORTS: &[&dyn HostImport] = &[
124 &ABORT_IMPORT,
125 &GET_HEADER_IMPORT,
126 &GET_QUERY_PARAM_IMPORT,
127 &SET_HEADER_IMPORT,
128 &SET_STATUS_IMPORT,
129 &SET_REQ_VAR_IMPORT,
130 &GET_REQ_VAR_IMPORT,
131];
132
133pub fn read_key_lookup_and_write(
136 ctx: &FunctionEnvMut<SharedExecutionContext>,
137 key_ptr: i32,
138 key_len: i32,
139 out_ptr: i32,
140 out_cap: i32,
141 normalize_key: bool,
142 lookup: impl Fn(&ExecutionContext, &str) -> Option<Vec<u8>>,
143) -> i32 {
144 let view = match with_mem_view(ctx) {
145 Ok(v) => v,
146 Err(_) => return -1,
147 };
148
149 let raw_key = match String::from_utf8(read_bytes(&view, key_ptr, key_len).unwrap_or_default()) {
150 Ok(key) => key,
151 Err(_) => return -1,
152 };
153
154 let key = if normalize_key {
155 raw_key.to_ascii_lowercase()
156 } else {
157 raw_key
158 };
159
160 let guard = ctx.data().read();
161 let bytes = match lookup(&guard, &key) {
162 Some(data) => data,
163 None => return -1,
164 };
165
166 let write_len = bytes.len().min(out_cap as usize);
167 if write_len > 0 && write_bytes(&view, out_ptr, &bytes[..write_len]).is_err() {
168 return -1;
169 }
170
171 write_len as i32
172}