Skip to main content

aiway_plugin/
lib.rs

1//! # aiway-plugin
2//!
3//! 网关插件 SDK,定义插件接口和数据交换类型。
4//!
5//! ## 架构
6//! - **插件侧**:插件开发者依赖此 crate,实现 `Plugin` trait,使用 `export_wasm!` 宏导出 WASM
7//! - **宿主侧**:`plugin-manager` crate 提供 wasmtime 运行时,加载并执行 WASM 插件
8//!
9//! ## 数据交换
10//! Host 与 WASM 之间通过 bincode 序列化传递数据,定义在 [`wasm_types`] 模块中。
11
12pub mod wasm_types;
13
14mod macros;
15mod plugin_ctx;
16mod wasm_ctx;
17
18pub use crate::plugin_ctx::{
19    FormPart, HttpRequest, HttpRequestBuilder, HttpResponse, LOG_DEBUG, LOG_ERROR, LOG_INFO,
20    LOG_TRACE, LOG_WARN, PluginContext,
21};
22use aiway_protocol::context::http::{request, response};
23pub use async_trait::async_trait;
24pub use bytes::Bytes;
25pub use http;
26pub use semver::Version;
27pub use serde_json;
28use serde_json::Value;
29pub use wasm_ctx::WasmHttpContext;
30/// 插件错误类型
31#[derive(Debug)]
32pub enum PluginError {
33    /// 执行插件业务逻辑时的错误
34    ExecuteError(String),
35    /// 插件不存在
36    NotFound(String),
37    /// 从磁盘或网络加载插件时错误
38    LoadError(String),
39    /// 序列化/反序列化错误
40    SerializeError(String),
41    /// HTTP 错误
42    HttpError(String),
43}
44
45impl std::fmt::Display for PluginError {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47        match self {
48            PluginError::ExecuteError(msg) => write!(f, "{}", msg),
49            PluginError::NotFound(msg) => write!(f, "{}", msg),
50            PluginError::LoadError(msg) => write!(f, "{}", msg),
51            PluginError::SerializeError(msg) => write!(f, "{}", msg),
52            PluginError::HttpError(msg) => write!(f, "{}", msg),
53        }
54    }
55}
56
57/// 插件接口 trait
58///
59/// 插件开发者实现此 trait,宿主侧通过 `plugin-manager` 调用。
60/// 上下文参数使用 `&mut dyn PluginContext`,宿主侧和 WASM 侧分别有不同实现。
61#[async_trait]
62pub trait Plugin: Send + Sync {
63    /// 插件名称
64    fn name(&self) -> &str;
65    /// 插件信息
66    fn info(&self) -> PluginInfo;
67
68    /// 请求阶段,可改写头部
69    async fn on_request(
70        &self,
71        _config: &Value,
72        _head: &mut request::Parts,
73        _ctx: &mut dyn PluginContext,
74    ) -> Result<(), PluginError> {
75        Ok(())
76    }
77
78    /// 请求体阶段,可改写请求体
79    async fn on_request_body(
80        &self,
81        _config: &Value,
82        _body: &mut Option<Bytes>,
83        _ctx: &mut dyn PluginContext,
84    ) -> Result<(), PluginError> {
85        Ok(())
86    }
87
88    /// 响应阶段,可改写头部
89    async fn on_response(
90        &self,
91        _config: &Value,
92        _head: &mut response::Parts,
93        _ctx: &mut dyn PluginContext,
94    ) -> Result<(), PluginError> {
95        Ok(())
96    }
97
98    /// 响应体阶段,可改写响应体(同步)
99    fn on_response_body(
100        &self,
101        _config: &Value,
102        _body: &mut Option<Bytes>,
103        _ctx: &mut dyn PluginContext,
104    ) -> Result<(), PluginError> {
105        Ok(())
106    }
107
108    /// 日志阶段
109    async fn on_logging(&self, _: &Value, _: &mut dyn PluginContext) {}
110}
111
112/// 插件信息
113#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
114pub struct PluginInfo {
115    /// 插件版本
116    pub version: Version,
117    /// 默认配置
118    pub default_config: Value,
119    /// 描述
120    pub description: String,
121    /// 插件使用手册
122    pub readme: Option<String>,
123}
124
125/// 简易 block_on,用于在同步上下文(WASM 内部)中执行 async 函数。
126///
127/// WASM 环境无真正异步 I/O,插件 future 必须立即返回 `Ready`。
128/// 若返回 `Pending` 说明插件误用了异步 I/O(如网络请求),直接 panic 露问题。
129pub fn block_on<F: Future>(f: F) -> F::Output {
130    use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
131
132    fn noop_clone(_: *const ()) -> RawWaker {
133        RawWaker::new(std::ptr::null(), &VTABLE)
134    }
135    fn noop(_: *const ()) {}
136    static VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
137
138    let raw_waker = RawWaker::new(std::ptr::null(), &VTABLE);
139    let waker = unsafe { Waker::from_raw(raw_waker) };
140    let mut cx = Context::from_waker(&waker);
141
142    let mut f = std::pin::pin!(f);
143    match f.as_mut().poll(&mut cx) {
144        Poll::Ready(val) => val,
145        Poll::Pending => panic!(
146            "plugin future returned Pending in WASM context; \
147             WASM plugins must not perform real async I/O"
148        ),
149    }
150}