1use crate::PluginError;
7use crate::plugin_ctx::{HttpRequest, HttpResponse, PluginContext};
8#[cfg(feature = "model")]
9use aiway_protocol::model::Provider;
10use std::any::Any;
11#[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
38pub struct WasmHttpContext;
46
47fn 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#[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 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}