Skip to main content

aiway_plugin/
wasm_ctx.rs

1//! WASM 侧 HttpContext 实现
2//!
3//! 通过宿主函数访问网关的真实请求数据,而非在 WASM 内部维护独立状态。
4//! 所有数据读取均委托给宿主侧的 `aiway::host_xxx` 函数。
5
6use crate::PluginError;
7use crate::plugin_ctx::{HttpRequest, HttpResponse, PluginContext};
8#[cfg(feature = "model")]
9use aiway_protocol::model::Provider;
10use std::any::Any;
11// ---------------------------------------------------------------------------
12// 宿主函数 FFI 声明
13// ---------------------------------------------------------------------------
14
15#[link(wasm_import_module = "aiway")]
16unsafe extern "C" {
17    fn host_request_id(buf_ptr: *mut u8, buf_len: i32) -> i32;
18    fn host_request_ts() -> i64;
19    fn host_is_sse() -> i32;
20    fn host_is_websocket() -> i32;
21    fn host_get_route_name(buf_ptr: *mut u8, buf_len: i32) -> i32;
22    fn host_get_routing_url(buf_ptr: *mut u8, buf_len: i32) -> i32;
23    fn host_get_response_body_size() -> i64;
24    fn host_set_response_body_size(size: i64);
25    fn host_log(level: i32, msg_ptr: *const u8, msg_len: i32);
26    #[cfg(feature = "model")]
27    fn host_get_model_name(buf_ptr: *mut u8, buf_len: i32) -> i32;
28    #[cfg(feature = "model")]
29    fn host_get_model_provider(buf_ptr: *mut u8, buf_len: i32) -> i32;
30    fn host_http_request(
31        req_ptr: *const u8,
32        req_len: i32,
33        resp_buf_ptr: *mut u8,
34        resp_buf_len: i32,
35    ) -> i32;
36}
37
38// ---------------------------------------------------------------------------
39// WasmHttpContext
40// ---------------------------------------------------------------------------
41
42/// WASM 侧的插件上下文实现。
43///
44/// 不持有任何状态,所有数据通过宿主函数按需获取。
45pub struct WasmHttpContext;
46
47/// 通过宿主函数读取字符串。
48///
49/// `f` 为宿主函数,遵循 snprintf 语义:返回数据实际长度(可能大于 `buf_len`)。
50/// `initial_len` 为初始缓冲区大小,若数据超出则自动扩容重试。
51fn read_host_string(
52    f: unsafe extern "C" fn(*mut u8, i32) -> i32,
53    initial_len: i32,
54) -> Option<String> {
55    let mut buf = vec![0u8; initial_len as usize];
56    let needed = unsafe { f(buf.as_mut_ptr(), initial_len) };
57    if needed <= 0 {
58        return None;
59    }
60    let needed = needed as usize;
61    if needed > buf.len() {
62        buf.resize(needed, 0);
63        let len = unsafe { f(buf.as_mut_ptr(), needed as i32) };
64        if len <= 0 {
65            return None;
66        }
67        return Some(String::from_utf8_lossy(&buf[..len as usize]).to_string());
68    }
69    Some(String::from_utf8_lossy(&buf[..needed]).to_string())
70}
71
72/// 通过宿主函数读取 bincode 序列化数据并反序列化。
73///
74/// 语义同 [`read_host_string`]:初始缓冲区不足时自动扩容重试。
75#[cfg(feature = "model")]
76fn read_host_bincode<T: serde::de::DeserializeOwned>(
77    f: unsafe extern "C" fn(*mut u8, i32) -> i32,
78    initial_len: i32,
79) -> Option<T> {
80    let mut buf = vec![0u8; initial_len as usize];
81    let needed = unsafe { f(buf.as_mut_ptr(), initial_len) };
82    if needed <= 0 {
83        return None;
84    }
85    let needed = needed as usize;
86    if needed > buf.len() {
87        buf.resize(needed, 0);
88        let len = unsafe { f(buf.as_mut_ptr(), needed as i32) };
89        if len <= 0 {
90            return None;
91        }
92        return bincode::deserialize(&buf[..len as usize]).ok();
93    }
94    bincode::deserialize(&buf[..needed]).ok()
95}
96
97impl PluginContext for WasmHttpContext {
98    fn request_id(&self) -> String {
99        read_host_string(host_request_id, 64).unwrap_or_default()
100    }
101
102    fn request_ts(&self) -> i64 {
103        unsafe { host_request_ts() }
104    }
105
106    fn is_sse(&self) -> bool {
107        unsafe { host_is_sse() != 0 }
108    }
109
110    fn is_websocket(&self) -> bool {
111        unsafe { host_is_websocket() != 0 }
112    }
113
114    fn get_route_name(&self) -> Option<String> {
115        read_host_string(host_get_route_name, 256)
116    }
117
118    fn get_routing_url(&self) -> Option<String> {
119        read_host_string(host_get_routing_url, 512)
120    }
121
122    fn get_response_body_size(&self) -> Option<i64> {
123        let v = unsafe { host_get_response_body_size() };
124        if v < 0 { None } else { Some(v) }
125    }
126
127    fn set_response_body_size(&mut self, size: i64) {
128        unsafe { host_set_response_body_size(size) }
129    }
130
131    #[cfg(feature = "model")]
132    fn get_model_name(&self) -> Option<String> {
133        read_host_string(host_get_model_name, 256)
134    }
135
136    #[cfg(feature = "model")]
137    fn get_model_provider(&self) -> Option<Provider> {
138        read_host_bincode(host_get_model_provider, 512)
139    }
140
141    fn log(&self, level: i32, msg: &str) {
142        let bytes = msg.as_bytes();
143        unsafe { host_log(level, bytes.as_ptr(), bytes.len() as i32) }
144    }
145
146    fn http_request(&self, req: &HttpRequest) -> Result<HttpResponse, PluginError> {
147        let req_bytes = bincode::serialize(req)
148            .map_err(|e| PluginError::HttpError(format!("serialize request failed: {e}")))?;
149
150        // 初始缓冲区 4KB,不足时扩容重试
151        let mut buf = vec![0u8; 4096];
152        loop {
153            let needed = unsafe {
154                host_http_request(
155                    req_bytes.as_ptr(),
156                    req_bytes.len() as i32,
157                    buf.as_mut_ptr(),
158                    buf.len() as i32,
159                )
160            };
161            if needed < 0 {
162                return Err(PluginError::HttpError(format!(
163                    "http_request failed with code {needed}"
164                )));
165            }
166            if needed == 0 {
167                return Err(PluginError::HttpError(
168                    "http_request returned empty response".into(),
169                ));
170            }
171            let needed = needed as usize;
172            if needed > buf.len() {
173                buf.resize(needed, 0);
174                continue;
175            }
176            return bincode::deserialize(&buf[..needed])
177                .map_err(|e| PluginError::HttpError(format!("deserialize response failed: {e}")));
178        }
179    }
180
181    fn as_any_mut(&mut self) -> &mut dyn Any {
182        self
183    }
184}