1#[cfg(feature = "model")]
6use aiway_protocol::model::Provider;
7use serde::de::DeserializeOwned;
8use serde::{Deserialize, Serialize};
9use std::any::Any;
10use std::collections::HashMap;
11
12use crate::PluginError;
13use aiway_protocol::context::{
14 HeaderOp, HttpContext, REQUEST_HEADER_PATCH, REQUEST_URI_PATCH, RESPONSE_HEADER_PATCH,
15 parts::SerdeParts,
16};
17use bytes::Bytes;
18use http::Uri;
19use serde_json::Value;
20
21pub const LOG_ERROR: i32 = 1;
23pub const LOG_WARN: i32 = 2;
24pub const LOG_INFO: i32 = 3;
25pub const LOG_DEBUG: i32 = 4;
26pub const LOG_TRACE: i32 = 5;
27
28#[derive(Serialize, Deserialize)]
30pub struct HttpRequest {
31 pub method: String,
32 pub url: String,
33 pub headers: Vec<(String, String)>,
34 pub body: Option<Vec<u8>>,
35 pub form: Option<HashMap<String, String>>,
37 pub multipart: Option<Vec<FormPart>>,
39 pub timeout_ms: u64,
40}
41
42#[derive(Serialize, Deserialize)]
44pub struct FormPart {
45 pub key: String,
46 pub value: Vec<u8>,
48 pub file_name: Option<String>,
50 pub mime_type: Option<String>,
52}
53
54pub struct HttpRequestBuilder {
56 method: String,
57 url: String,
58 headers: Vec<(String, String)>,
59 body: Option<Vec<u8>>,
60 form: Option<HashMap<String, String>>,
61 multipart: Option<Vec<FormPart>>,
62 timeout_ms: u64,
63}
64
65impl HttpRequestBuilder {
66 pub fn new(method: impl Into<String>, url: impl Into<String>) -> Self {
68 Self {
69 method: method.into(),
70 url: url.into(),
71 headers: Vec::new(),
72 body: None,
73 form: None,
74 multipart: None,
75 timeout_ms: 10_000,
76 }
77 }
78
79 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
81 self.headers.push((key.into(), value.into()));
82 self
83 }
84
85 pub fn body(mut self, body: Vec<u8>) -> Self {
87 self.body = Some(body);
88 self
89 }
90
91 pub fn form(mut self, form: HashMap<String, String>) -> Self {
93 self.form = Some(form);
94 self
95 }
96
97 pub fn add_form_field(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
99 self.form
100 .get_or_insert_with(HashMap::new)
101 .insert(key.into(), value.into());
102 self
103 }
104
105 pub fn multipart(mut self, parts: Vec<FormPart>) -> Self {
107 self.multipart = Some(parts);
108 self
109 }
110
111 pub fn add_multipart_part(mut self, part: FormPart) -> Self {
113 self.multipart.get_or_insert_with(Vec::new).push(part);
114 self
115 }
116
117 pub fn timeout_ms(mut self, timeout_ms: u64) -> Self {
119 self.timeout_ms = timeout_ms;
120 self
121 }
122
123 pub fn build(self) -> HttpRequest {
125 HttpRequest {
126 method: self.method,
127 url: self.url,
128 headers: self.headers,
129 body: self.body,
130 form: self.form,
131 multipart: self.multipart,
132 timeout_ms: self.timeout_ms,
133 }
134 }
135}
136
137#[derive(Serialize, Deserialize)]
139pub struct HttpResponse {
140 pub status: u16,
141 pub headers: Vec<(String, String)>,
142 pub body: Vec<u8>,
143}
144
145impl HttpResponse {
146 pub fn text(&self) -> Result<String, PluginError> {
148 String::from_utf8(self.body.clone())
149 .map_err(|e| PluginError::HttpError(format!("invalid UTF-8 response: {e}")))
150 }
151
152 pub fn json<T: serde::de::DeserializeOwned>(&self) -> Result<T, PluginError> {
154 serde_json::from_slice(&self.body)
155 .map_err(|e| PluginError::SerdeError(format!("JSON deserialize failed: {e}")))
156 }
157}
158
159pub trait PluginContext: Send {
164 fn request_id(&self) -> String;
166
167 fn request_ts(&self) -> i64;
169
170 fn is_sse(&self) -> bool;
172
173 fn is_websocket(&self) -> bool;
175
176 fn get_request_header(&self, name: &str) -> Option<String>;
178
179 fn get_response_header(&self, name: &str) -> Option<String>;
181
182 fn method(&self) -> Option<String>;
184
185 fn uri(&self) -> Option<Uri>;
187
188 fn set_uri(&mut self, uri: Uri);
190
191 fn status(&self) -> Option<u16>;
193
194 fn get_route_name(&self) -> Option<String>;
196
197 fn get_routing_url(&self) -> Option<String>;
199
200 fn get_response_body_size(&self) -> Option<i64>;
202
203 fn set_response_body_size(&mut self, size: i64);
205
206 fn set_request_header(&mut self, name: &str, value: &str);
208
209 fn set_response_header(&mut self, name: &str, value: &str);
211
212 fn append_request_header(&mut self, name: &str, value: &str);
214
215 fn append_response_header(&mut self, name: &str, value: &str);
217
218 fn remove_request_header(&mut self, name: &str);
220
221 fn remove_response_header(&mut self, name: &str);
223
224 #[cfg(feature = "model")]
226 fn get_model_name(&self) -> Option<String>;
227
228 #[cfg(feature = "model")]
230 fn get_model_provider(&self) -> Option<Provider>;
231
232 fn log(&self, level: i32, msg: &str);
234
235 fn log_error(&self, msg: &str) {
237 self.log(LOG_ERROR, msg);
238 }
239
240 fn log_warn(&self, msg: &str) {
242 self.log(LOG_WARN, msg);
243 }
244
245 fn log_info(&self, msg: &str) {
247 self.log(LOG_INFO, msg);
248 }
249
250 fn log_debug(&self, msg: &str) {
252 self.log(LOG_DEBUG, msg);
253 }
254
255 fn log_trace(&self, msg: &str) {
257 self.log(LOG_TRACE, msg);
258 }
259
260 fn http_request(&self, _req: &HttpRequest) -> Result<HttpResponse, PluginError> {
262 Err(PluginError::HttpError(
263 "http_request not supported in this context".into(),
264 ))
265 }
266
267 fn config(&self) -> Option<String>;
269
270 fn request_body(&self) -> Option<Bytes>;
272
273 fn set_request_body(&mut self, _body: Vec<u8>);
275
276 fn response_body(&self) -> Option<Bytes>;
278
279 fn set_response_body(&mut self, _body: Vec<u8>);
281
282 fn as_any_mut(&mut self) -> &mut dyn Any;
284}
285
286impl PluginContext for HttpContext {
287 fn request_id(&self) -> String {
288 HttpContext::request_id(self)
289 }
290
291 fn request_ts(&self) -> i64 {
292 HttpContext::request_ts(self)
293 }
294
295 fn is_sse(&self) -> bool {
296 HttpContext::is_sse(self)
297 }
298
299 fn is_websocket(&self) -> bool {
300 HttpContext::is_websocket(self)
301 }
302
303 fn get_request_header(&self, name: &str) -> Option<String> {
304 self.get_any_state::<SerdeParts>(Self::REQUEST_RAW_PARTS)
305 .and_then(|parts| {
306 parts
307 .headers
308 .as_ref()?
309 .get(name)
310 .and_then(|v| v.to_str().ok())
311 .map(|s| s.to_string())
312 })
313 }
314
315 fn get_response_header(&self, name: &str) -> Option<String> {
316 self.get_any_state::<SerdeParts>(Self::RESPONSE_SERDE_PARTS)
317 .and_then(|parts| {
318 parts
319 .headers
320 .as_ref()?
321 .get(name)
322 .and_then(|v| v.to_str().ok())
323 .map(|s| s.to_string())
324 })
325 }
326
327 fn method(&self) -> Option<String> {
328 self.get_any_state::<SerdeParts>(Self::REQUEST_RAW_PARTS)
329 .and_then(|parts| parts.method.as_ref().map(|m| m.to_string()))
330 }
331
332 fn uri(&self) -> Option<Uri> {
333 self.get_any_state::<SerdeParts>(Self::REQUEST_RAW_PARTS)
334 .and_then(|parts| parts.uri.clone())
335 }
336
337 fn set_uri(&mut self, uri: Uri) {
338 self.insert_any_state(REQUEST_URI_PATCH, uri);
339 }
340
341 fn status(&self) -> Option<u16> {
342 self.get_any_state::<SerdeParts>(Self::RESPONSE_SERDE_PARTS)
343 .and_then(|parts| parts.status_code.map(|s| s.as_u16()))
344 }
345
346 fn get_route_name(&self) -> Option<String> {
347 self.get_route().map(|r| r.name.clone())
348 }
349
350 fn get_routing_url(&self) -> Option<String> {
351 HttpContext::get_routing_url(self).cloned()
352 }
353
354 fn get_response_body_size(&self) -> Option<i64> {
355 self.get_any_state::<i64>(Self::RESPONSE_BODY_SIZE)
356 .map(|v| *v)
357 }
358
359 fn set_response_body_size(&mut self, size: i64) {
360 self.insert_any_state(Self::RESPONSE_BODY_SIZE, size);
361 }
362
363 fn set_request_header(&mut self, name: &str, value: &str) {
364 let mut ops = self
365 .get_any_state::<Vec<HeaderOp>>(REQUEST_HEADER_PATCH)
366 .map(|arc| (*arc).clone())
367 .unwrap_or_default();
368 ops.push(HeaderOp::Set(name.to_string(), value.to_string()));
369 self.insert_any_state(REQUEST_HEADER_PATCH, ops);
370 }
371
372 fn set_response_header(&mut self, name: &str, value: &str) {
373 let mut ops = self
374 .get_any_state::<Vec<HeaderOp>>(RESPONSE_HEADER_PATCH)
375 .map(|arc| (*arc).clone())
376 .unwrap_or_default();
377 ops.push(HeaderOp::Set(name.to_string(), value.to_string()));
378 self.insert_any_state(RESPONSE_HEADER_PATCH, ops);
379 }
380
381 fn append_request_header(&mut self, name: &str, value: &str) {
382 let mut ops = self
383 .get_any_state::<Vec<HeaderOp>>(REQUEST_HEADER_PATCH)
384 .map(|arc| (*arc).clone())
385 .unwrap_or_default();
386 ops.push(HeaderOp::Append(name.to_string(), value.to_string()));
387 self.insert_any_state(REQUEST_HEADER_PATCH, ops);
388 }
389
390 fn append_response_header(&mut self, name: &str, value: &str) {
391 let mut ops = self
392 .get_any_state::<Vec<HeaderOp>>(RESPONSE_HEADER_PATCH)
393 .map(|arc| (*arc).clone())
394 .unwrap_or_default();
395 ops.push(HeaderOp::Append(name.to_string(), value.to_string()));
396 self.insert_any_state(RESPONSE_HEADER_PATCH, ops);
397 }
398
399 fn remove_request_header(&mut self, name: &str) {
400 let mut ops = self
401 .get_any_state::<Vec<HeaderOp>>(REQUEST_HEADER_PATCH)
402 .map(|arc| (*arc).clone())
403 .unwrap_or_default();
404 ops.push(HeaderOp::Remove(name.to_string()));
405 self.insert_any_state(REQUEST_HEADER_PATCH, ops);
406 }
407
408 fn remove_response_header(&mut self, name: &str) {
409 let mut ops = self
410 .get_any_state::<Vec<HeaderOp>>(RESPONSE_HEADER_PATCH)
411 .map(|arc| (*arc).clone())
412 .unwrap_or_default();
413 ops.push(HeaderOp::Remove(name.to_string()));
414 self.insert_any_state(RESPONSE_HEADER_PATCH, ops);
415 }
416
417 #[cfg(feature = "model")]
418 fn get_model_name(&self) -> Option<String> {
419 self.get_proxy_model_name()
420 }
421
422 #[cfg(feature = "model")]
423 fn get_model_provider(&self) -> Option<Provider> {
424 self.get_proxy_model_provider()
425 }
426
427 fn log(&self, level: i32, msg: &str) {
428 match level {
429 LOG_ERROR => log::error!("{}", msg),
430 LOG_WARN => log::warn!("{}", msg),
431 LOG_INFO => log::info!("{}", msg),
432 LOG_DEBUG => log::debug!("{}", msg),
433 LOG_TRACE => log::trace!("{}", msg),
434 _ => log::info!("{}", msg),
435 }
436 }
437
438 fn config(&self) -> Option<String> {
439 self.get_any_state::<String>(Self::PLUGIN_CONFIG)
440 .map(|v| (*v).clone())
441 }
442
443 fn request_body(&self) -> Option<Bytes> {
444 self.get_any_state::<Bytes>(Self::REQUEST_BODY)
445 .map(|b| (*b).clone())
446 }
447
448 fn set_request_body(&mut self, body: Vec<u8>) {
449 self.insert_any_state(Self::REQUEST_BODY, Bytes::from(body));
450 }
451
452 fn response_body(&self) -> Option<Bytes> {
453 self.get_any_state::<Bytes>(Self::RESPONSE_BODY)
454 .map(|b| (*b).clone())
455 }
456
457 fn set_response_body(&mut self, body: Vec<u8>) {
458 self.insert_any_state(Self::RESPONSE_BODY, Bytes::from(body));
459 }
460
461 fn as_any_mut(&mut self) -> &mut dyn Any {
462 self
463 }
464}
465
466pub trait PluginContextExt: PluginContext {
467 fn config_as_json(&self) -> Option<Value> {
469 self.config().and_then(|s| serde_json::from_str(&s).ok())
470 }
471 fn config_as<T>(&self) -> Result<T, PluginError>
473 where
474 T: DeserializeOwned,
475 {
476 let json = self
477 .config_as_json()
478 .ok_or_else(|| PluginError::SerdeError("plugin config not set in context".into()))?;
479 serde_json::from_value(json)
480 .map_err(|e| PluginError::SerdeError(format!("parse plugin config failed: {e}")))
481 }
482}
483
484impl<T: PluginContext + ?Sized> PluginContextExt for T {}