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 bincode;
25pub use bytes::Bytes;
26pub use http;
27pub use semver::Version;
28pub use serde_json;
29use serde_json::Value;
30pub use wasm_ctx::WasmHttpContext;
31/// 插件错误类型
32#[derive(Debug)]
33pub enum PluginError {
34    /// 执行插件业务逻辑时的错误
35    ExecuteError(String),
36    /// 插件不存在
37    NotFound(String),
38    /// 从磁盘或网络加载插件时错误
39    LoadError(String),
40    /// 序列化/反序列化错误
41    SerializeError(String),
42    /// HTTP 错误(发起HTTP调用错误)
43    HttpError(String),
44    /// 插件主动拒绝请求,携带 HTTP 状态码和消息
45    ///
46    /// 用于限流(429)、鉴权失败(403)、参数校验(400) 等场景,
47    /// 网关会透传 status 作为 HTTP 响应码返回给客户端。
48    Reject(u16, String),
49}
50
51impl std::fmt::Display for PluginError {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        match self {
54            PluginError::ExecuteError(msg) => write!(f, "{}", msg),
55            PluginError::NotFound(msg) => write!(f, "{}", msg),
56            PluginError::LoadError(msg) => write!(f, "{}", msg),
57            PluginError::SerializeError(msg) => write!(f, "{}", msg),
58            PluginError::HttpError(msg) => write!(f, "{}", msg),
59            PluginError::Reject(status, message) => write!(f, "[{}] {}", status, message),
60        }
61    }
62}
63
64/// 插件接口 trait
65///
66/// 插件开发者实现此 trait,宿主侧通过 `plugin-manager` 调用。
67/// 上下文参数使用 `&mut dyn PluginContext`,宿主侧和 WASM 侧分别有不同实现。
68#[async_trait]
69pub trait Plugin: Send + Sync {
70    /// 插件名称
71    fn name(&self) -> &str;
72    /// 插件信息
73    fn info(&self) -> PluginInfo;
74
75    /// 请求阶段,可改写头部
76    async fn on_request(
77        &self,
78        _config: &Value,
79        _head: &mut request::Parts,
80        _ctx: &mut dyn PluginContext,
81    ) -> Result<(), PluginError> {
82        Ok(())
83    }
84
85    /// 请求体阶段,可改写请求体
86    async fn on_request_body(
87        &self,
88        _config: &Value,
89        _body: &mut Option<Bytes>,
90        _ctx: &mut dyn PluginContext,
91    ) -> Result<(), PluginError> {
92        Ok(())
93    }
94
95    /// 响应阶段,可改写头部
96    async fn on_response(
97        &self,
98        _config: &Value,
99        _head: &mut response::Parts,
100        _ctx: &mut dyn PluginContext,
101    ) -> Result<(), PluginError> {
102        Ok(())
103    }
104
105    /// 响应体阶段,可改写响应体
106    async fn on_response_body(
107        &self,
108        _config: &Value,
109        _body: &mut Option<Bytes>,
110        _ctx: &mut dyn PluginContext,
111    ) -> Result<(), PluginError> {
112        Ok(())
113    }
114
115    /// 日志阶段
116    async fn on_logging(&self, _: &Value, _: &mut dyn PluginContext) {}
117}
118
119/// 插件信息
120#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
121pub struct PluginInfo {
122    /// 插件版本
123    pub version: Version,
124    /// 默认配置
125    pub default_config: Value,
126    /// 描述
127    pub description: String,
128    /// 插件使用手册
129    pub readme: Option<String>,
130}
131
132/// 简易 block_on,用于在同步上下文(WASM 内部)中执行 async 函数。
133///
134/// WASM 环境无真正异步 I/O,插件 future 必须立即返回 `Ready`。
135/// 若返回 `Pending` 说明插件误用了异步 I/O(如网络请求),直接 panic 露问题。
136pub fn block_on<F: Future>(f: F) -> F::Output {
137    use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
138
139    fn noop_clone(_: *const ()) -> RawWaker {
140        RawWaker::new(std::ptr::null(), &VTABLE)
141    }
142    fn noop(_: *const ()) {}
143    static VTABLE: RawWakerVTable = RawWakerVTable::new(noop_clone, noop, noop, noop);
144
145    let raw_waker = RawWaker::new(std::ptr::null(), &VTABLE);
146    let waker = unsafe { Waker::from_raw(raw_waker) };
147    let mut cx = Context::from_waker(&waker);
148
149    let mut f = std::pin::pin!(f);
150    match f.as_mut().poll(&mut cx) {
151        Poll::Ready(val) => val,
152        Poll::Pending => panic!(
153            "plugin future returned Pending in WASM context; \
154             WASM plugins must not perform real async I/O"
155        ),
156    }
157}