```
feat(js-engine): 实现JavaScript引擎核心功能和中间件系统 添加完整的JavaScript运行时支持,包括: - 创建bindings模块处理JavaScript与Rust类型转换 - 实现JsRequest和JsResponse包装器用于HTTP对象转换 - 添加register_bindings函数注册全局JavaScript绑定 - 实现MiddlewareExecutor执行JavaScript中间件链 - 创建JsRuntime包装器管理QuickJS运行时和上下文 - 支持多种中间件钩子(onRequest, onResponse, onResponseSent) - 实现多层级中间件系统(全局/站点/路由级别) - 添加JavaScript配置解析和类型转换功能 - 提供错误处理和日志记录支持 ```
This commit is contained in:
parent
045ca3b1a9
commit
cf3b6dd95a
|
|
@ -0,0 +1,159 @@
|
||||||
|
use crate::js_engine::error::JsEngineError;
|
||||||
|
use rquickjs::{AsyncContext, Ctx, FromJs, IntoJs, Object, Value};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// JavaScript请求对象包装器
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct JsRequest {
|
||||||
|
pub method: String,
|
||||||
|
pub uri: String,
|
||||||
|
pub headers: HashMap<String, String>,
|
||||||
|
pub body: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'js> FromJs<'js> for JsRequest {
|
||||||
|
fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self, rquickjs::Error> {
|
||||||
|
let obj = value
|
||||||
|
.as_object()
|
||||||
|
.ok_or_else(|| rquickjs::Error::new_from_js("value", "object"))?;
|
||||||
|
|
||||||
|
let method = obj.get::<_, String>("method")?;
|
||||||
|
let uri = obj.get::<_, String>("uri")?;
|
||||||
|
|
||||||
|
// 获取headers
|
||||||
|
let headers_obj: Object = match obj.get("headers") {
|
||||||
|
Ok(obj) => obj,
|
||||||
|
Err(_) => Object::new(ctx.clone())?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut headers = HashMap::new();
|
||||||
|
for key in headers_obj.keys::<String>() {
|
||||||
|
if let Ok(key_str) = key {
|
||||||
|
if let Ok(value) = headers_obj.get::<_, String>(&key_str) {
|
||||||
|
headers.insert(key_str, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选的body
|
||||||
|
let body = obj.get::<_, Option<String>>("body").unwrap_or(None);
|
||||||
|
|
||||||
|
Ok(JsRequest {
|
||||||
|
method,
|
||||||
|
uri,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'js> IntoJs<'js> for JsRequest {
|
||||||
|
fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>, rquickjs::Error> {
|
||||||
|
let obj = Object::new(ctx.clone())?;
|
||||||
|
|
||||||
|
obj.set("method", self.method)?;
|
||||||
|
obj.set("uri", self.uri)?;
|
||||||
|
|
||||||
|
// 设置headers
|
||||||
|
let headers_obj = Object::new(ctx.clone())?;
|
||||||
|
for (key, value) in self.headers {
|
||||||
|
headers_obj.set(key, value)?;
|
||||||
|
}
|
||||||
|
obj.set("headers", headers_obj)?;
|
||||||
|
|
||||||
|
// 设置body
|
||||||
|
obj.set("body", self.body)?;
|
||||||
|
|
||||||
|
Ok(obj.into_value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JavaScript响应对象包装器
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct JsResponse {
|
||||||
|
pub status: u16,
|
||||||
|
pub headers: HashMap<String, String>,
|
||||||
|
pub body: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'js> FromJs<'js> for JsResponse {
|
||||||
|
fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self, rquickjs::Error> {
|
||||||
|
let obj = value
|
||||||
|
.as_object()
|
||||||
|
.ok_or_else(|| rquickjs::Error::new_from_js("value", "object"))?;
|
||||||
|
|
||||||
|
let status = obj.get::<_, u16>("status")?;
|
||||||
|
|
||||||
|
// 获取headers
|
||||||
|
let headers_obj: Object = match obj.get("headers") {
|
||||||
|
Ok(obj) => obj,
|
||||||
|
Err(_) => Object::new(ctx.clone())?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut headers = HashMap::new();
|
||||||
|
for key in headers_obj.keys::<String>() {
|
||||||
|
if let Ok(key_str) = key {
|
||||||
|
if let Ok(value) = headers_obj.get::<_, String>(&key_str) {
|
||||||
|
headers.insert(key_str, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 可选的body
|
||||||
|
let body = obj.get::<_, Option<String>>("body").unwrap_or(None);
|
||||||
|
|
||||||
|
Ok(JsResponse {
|
||||||
|
status,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'js> IntoJs<'js> for JsResponse {
|
||||||
|
fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>, rquickjs::Error> {
|
||||||
|
let obj = Object::new(ctx.clone())?;
|
||||||
|
|
||||||
|
obj.set("status", self.status)?;
|
||||||
|
|
||||||
|
// 设置headers
|
||||||
|
let headers_obj = Object::new(ctx.clone())?;
|
||||||
|
for (key, value) in self.headers {
|
||||||
|
headers_obj.set(key, value)?;
|
||||||
|
}
|
||||||
|
obj.set("headers", headers_obj)?;
|
||||||
|
|
||||||
|
// 设置body
|
||||||
|
obj.set("body", self.body)?;
|
||||||
|
|
||||||
|
Ok(obj.into_value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: 由于类型复杂性,实际的HTTP转换函数将在middleware模块中实现
|
||||||
|
// 这样可以更容易地处理axum/hyper的类型转换
|
||||||
|
|
||||||
|
/// 为JavaScript上下文注册全局绑定
|
||||||
|
pub async fn register_bindings(context: &AsyncContext) -> Result<(), JsEngineError> {
|
||||||
|
context
|
||||||
|
.clone()
|
||||||
|
.with(|ctx| {
|
||||||
|
let globals = ctx.globals();
|
||||||
|
|
||||||
|
// 创建console对象
|
||||||
|
let console = Object::new(ctx.clone())?;
|
||||||
|
|
||||||
|
// 添加log方法 - 使用简单的函数
|
||||||
|
let log_func = rquickjs::Function::new(ctx.clone(), |args: Vec<String>| {
|
||||||
|
println!("[JS] {}", args.join(" "));
|
||||||
|
Ok::<(), rquickjs::Error>(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
console.set("log", log_func)?;
|
||||||
|
globals.set("console", console)?;
|
||||||
|
|
||||||
|
Ok::<_, rquickjs::Error>(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e| JsEngineError::Runtime(format!("Failed to register bindings: {}", e)))
|
||||||
|
}
|
||||||
|
|
@ -17,3 +17,9 @@ pub enum JsEngineError {
|
||||||
#[error("Generic error: {0}")]
|
#[error("Generic error: {0}")]
|
||||||
Generic(String),
|
Generic(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<rquickjs::Error> for JsEngineError {
|
||||||
|
fn from(err: rquickjs::Error) -> Self {
|
||||||
|
JsEngineError::Execution(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,280 @@
|
||||||
|
use crate::js_engine::error::JsEngineError;
|
||||||
|
use crate::js_engine::types::{MiddlewareContainer, MiddlewareFunction, MiddlewareHook};
|
||||||
|
use crate::js_engine::{JsRequest, JsResponse};
|
||||||
|
use axum::{body::Body, http::StatusCode};
|
||||||
|
use rquickjs::{AsyncContext, FromJs, Value};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tracing::{info, warn};
|
||||||
|
|
||||||
|
/// 中间件执行器
|
||||||
|
/// 负责执行JavaScript中间件链
|
||||||
|
pub struct MiddlewareExecutor {
|
||||||
|
context: Arc<AsyncContext>,
|
||||||
|
middleware_container: Arc<MiddlewareContainer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MiddlewareExecutor {
|
||||||
|
/// 创建新的中间件执行器
|
||||||
|
pub fn new(context: Arc<AsyncContext>, middleware_container: Arc<MiddlewareContainer>) -> Self {
|
||||||
|
Self {
|
||||||
|
context,
|
||||||
|
middleware_container,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 执行完整的中间件链
|
||||||
|
pub async fn execute(
|
||||||
|
&self,
|
||||||
|
hook: MiddlewareHook,
|
||||||
|
request: axum::http::Request<Body>,
|
||||||
|
site_name: Option<&str>,
|
||||||
|
route_path: Option<&str>,
|
||||||
|
) -> Result<Option<axum::http::Response<Body>>, JsEngineError> {
|
||||||
|
info!(
|
||||||
|
"Executing middleware hook: {:?} for site: {:?}, route: {:?}",
|
||||||
|
hook, site_name, route_path
|
||||||
|
);
|
||||||
|
|
||||||
|
// 获取对应Hook的中间件链
|
||||||
|
let middleware_functions = self.get_middleware_chain(hook.clone(), site_name, route_path);
|
||||||
|
|
||||||
|
if middleware_functions.is_empty() {
|
||||||
|
info!("No middleware found for hook: {:?}", hook);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将Rust请求转换为JsRequest
|
||||||
|
let js_request = convert_request(&request).await?;
|
||||||
|
|
||||||
|
// 执行中间件链
|
||||||
|
let js_response = self
|
||||||
|
.execute_middleware_chain(middleware_functions, js_request)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// 如果中间件返回了响应,转换为Rust响应
|
||||||
|
if let Some(js_response) = js_response {
|
||||||
|
let response = convert_response(js_response).await?;
|
||||||
|
Ok(Some(response))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取指定Hook的中间件链
|
||||||
|
fn get_middleware_chain(
|
||||||
|
&self,
|
||||||
|
hook: MiddlewareHook,
|
||||||
|
site_name: Option<&str>,
|
||||||
|
route_path: Option<&str>,
|
||||||
|
) -> Vec<MiddlewareFunction> {
|
||||||
|
let mut chain = Vec::new();
|
||||||
|
|
||||||
|
// 添加全局中间件
|
||||||
|
if let Some(global_middleware) = self.middleware_container.global.get(&hook) {
|
||||||
|
chain.extend(global_middleware.iter().cloned());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加站点级别中间件
|
||||||
|
if let Some(site_name) = site_name {
|
||||||
|
if let Some(site_middleware) = self.middleware_container.sites.get(site_name) {
|
||||||
|
if let Some(middleware) = site_middleware.get(&hook) {
|
||||||
|
chain.extend(middleware.iter().cloned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加路由级别中间件
|
||||||
|
if let Some(route_path) = route_path {
|
||||||
|
if let Some(route_middleware) = self.middleware_container.routes.get(route_path) {
|
||||||
|
if let Some(middleware) = route_middleware.get(&hook) {
|
||||||
|
chain.extend(middleware.iter().cloned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chain
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 执行JavaScript中间件链
|
||||||
|
async fn execute_middleware_chain(
|
||||||
|
&self,
|
||||||
|
middleware_functions: Vec<MiddlewareFunction>,
|
||||||
|
_request: JsRequest,
|
||||||
|
) -> Result<Option<JsResponse>, JsEngineError> {
|
||||||
|
let context = self.context.clone();
|
||||||
|
|
||||||
|
context
|
||||||
|
.with(move |ctx| {
|
||||||
|
// 简化版本:直接按顺序执行中间件
|
||||||
|
// 如果某个中间件返回了响应,就停止执行
|
||||||
|
|
||||||
|
for middleware_code in middleware_functions {
|
||||||
|
// 将代码包装为async函数
|
||||||
|
let wrapped_code = format!("(async function() {{ {} }})", middleware_code.0);
|
||||||
|
|
||||||
|
let middleware_func: rquickjs::Function = ctx.eval(wrapped_code.as_bytes())?;
|
||||||
|
|
||||||
|
// 调用中间件函数
|
||||||
|
let result = middleware_func.call::<(), rquickjs::Value>(())?;
|
||||||
|
|
||||||
|
// 检查是否返回了响应
|
||||||
|
if !result.is_undefined() && !result.is_null() {
|
||||||
|
let js_response = JsResponse::from_js(&ctx, result)?;
|
||||||
|
return Ok(Some(js_response));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有中间件都执行完毕,没有返回响应
|
||||||
|
Ok(None)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(|e: rquickjs::Error| {
|
||||||
|
JsEngineError::Execution(format!("Failed to execute middleware chain: {}", e))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将Rust HTTP请求转换为JsRequest
|
||||||
|
async fn convert_request(request: &axum::http::Request<Body>) -> Result<JsRequest, JsEngineError> {
|
||||||
|
let method = request.method().to_string();
|
||||||
|
let uri = request.uri().to_string();
|
||||||
|
|
||||||
|
// 提取headers
|
||||||
|
let mut headers = std::collections::HashMap::new();
|
||||||
|
for (name, value) in request.headers() {
|
||||||
|
if let Ok(value_str) = value.to_str() {
|
||||||
|
headers.insert(name.as_str().to_string(), value_str.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暂时不读取请求体,因为 Body 不支持克隆
|
||||||
|
// TODO: 需要重构来支持请求体读取
|
||||||
|
let body = None;
|
||||||
|
|
||||||
|
Ok(JsRequest {
|
||||||
|
method,
|
||||||
|
uri,
|
||||||
|
headers,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 将JsResponse转换为Rust HTTP响应
|
||||||
|
async fn convert_response(
|
||||||
|
js_response: JsResponse,
|
||||||
|
) -> Result<axum::http::Response<Body>, JsEngineError> {
|
||||||
|
let status = StatusCode::from_u16(js_response.status)
|
||||||
|
.map_err(|e| JsEngineError::Execution(format!("Invalid status code: {}", e)))?;
|
||||||
|
|
||||||
|
let mut response_builder = axum::http::Response::builder().status(status);
|
||||||
|
|
||||||
|
// 设置headers
|
||||||
|
for (name, value) in js_response.headers {
|
||||||
|
response_builder = response_builder.header(&name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建响应
|
||||||
|
let body = if let Some(body_str) = js_response.body {
|
||||||
|
Body::from(body_str)
|
||||||
|
} else {
|
||||||
|
Body::empty()
|
||||||
|
};
|
||||||
|
|
||||||
|
response_builder
|
||||||
|
.body(body)
|
||||||
|
.map_err(|e| JsEngineError::Execution(format!("Failed to build response: {}", e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 辅助函数:从JavaScript配置文件提取中间件函数
|
||||||
|
pub fn extract_middleware_from_config(
|
||||||
|
config: &serde_json::Value,
|
||||||
|
) -> Result<MiddlewareContainer, JsEngineError> {
|
||||||
|
let mut container = MiddlewareContainer::new();
|
||||||
|
|
||||||
|
// 解析全局中间件
|
||||||
|
if let Some(global_middleware) = config.get("middleware") {
|
||||||
|
parse_middleware_object(global_middleware, &mut container.global)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析站点中间件
|
||||||
|
if let Some(sites) = config.get("sites") {
|
||||||
|
if let serde_json::Value::Object(sites_obj) = sites {
|
||||||
|
for (site_name, site_config) in sites_obj {
|
||||||
|
if let serde_json::Value::Object(site_config_obj) = site_config {
|
||||||
|
let site_middleware = container.sites.entry(site_name.clone()).or_default();
|
||||||
|
|
||||||
|
if let Some(middleware) = site_config_obj.get("middleware") {
|
||||||
|
parse_middleware_object(middleware, site_middleware)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析站点内的路由
|
||||||
|
if let Some(routes) = site_config_obj.get("routes") {
|
||||||
|
if let serde_json::Value::Array(routes_arr) = routes {
|
||||||
|
for route in routes_arr {
|
||||||
|
if let serde_json::Value::Object(route_obj) = route {
|
||||||
|
if let (Some(path), Some(middleware)) = (
|
||||||
|
route_obj.get("path").and_then(|p| p.as_str()),
|
||||||
|
route_obj.get("middleware"),
|
||||||
|
) {
|
||||||
|
let route_middleware =
|
||||||
|
container.routes.entry(path.to_string()).or_default();
|
||||||
|
parse_middleware_object(middleware, route_middleware)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(container)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 解析中间件对象
|
||||||
|
fn parse_middleware_object(
|
||||||
|
middleware_value: &serde_json::Value,
|
||||||
|
middleware_map: &mut std::collections::HashMap<MiddlewareHook, Vec<MiddlewareFunction>>,
|
||||||
|
) -> Result<(), JsEngineError> {
|
||||||
|
if let serde_json::Value::Object(middleware_obj) = middleware_value {
|
||||||
|
for (hook_name, hook_value) in middleware_obj {
|
||||||
|
let hook = match hook_name.as_str() {
|
||||||
|
"onRequest" => MiddlewareHook::OnRequest,
|
||||||
|
"onResponse" => MiddlewareHook::OnResponse,
|
||||||
|
"onResponseSent" => MiddlewareHook::OnResponseSent,
|
||||||
|
_ => {
|
||||||
|
warn!("Unknown middleware hook: {}", hook_name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match hook_value {
|
||||||
|
serde_json::Value::String(code) => {
|
||||||
|
middleware_map
|
||||||
|
.entry(hook.clone())
|
||||||
|
.or_default()
|
||||||
|
.push(MiddlewareFunction::new(code.clone()));
|
||||||
|
}
|
||||||
|
serde_json::Value::Array(codes) => {
|
||||||
|
for code in codes {
|
||||||
|
if let serde_json::Value::String(code_str) = code {
|
||||||
|
middleware_map
|
||||||
|
.entry(hook.clone())
|
||||||
|
.or_default()
|
||||||
|
.push(MiddlewareFunction::new(code_str.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
warn!(
|
||||||
|
"Invalid middleware value for hook {}: {:?}",
|
||||||
|
hook_name, hook_value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,16 @@
|
||||||
//! 提供JavaScript运行环境、中间件系统和配置加载功能
|
//! 提供JavaScript运行环境、中间件系统和配置加载功能
|
||||||
//! 使用rquickjs库集成QuickJS引擎
|
//! 使用rquickjs库集成QuickJS引擎
|
||||||
|
|
||||||
|
pub mod bindings;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
pub mod middleware;
|
||||||
|
pub mod runtime;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
|
pub use bindings::{JsRequest, JsResponse, register_bindings};
|
||||||
pub use error::JsEngineError;
|
pub use error::JsEngineError;
|
||||||
|
pub use middleware::{MiddlewareExecutor, extract_middleware_from_config};
|
||||||
|
pub use runtime::JsRuntime;
|
||||||
pub use types::{MiddlewareContainer, MiddlewareFunction, MiddlewareHook};
|
pub use types::{MiddlewareContainer, MiddlewareFunction, MiddlewareHook};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,172 @@
|
||||||
|
use crate::js_engine::error::JsEngineError;
|
||||||
|
use rquickjs::{AsyncContext, AsyncRuntime, Function, Module, Value};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
/// JavaScript Runtime包装器
|
||||||
|
/// 管理QuickJS运行时和上下文
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct JsRuntime {
|
||||||
|
runtime: Arc<AsyncRuntime>,
|
||||||
|
context: Option<Arc<AsyncContext>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JsRuntime {
|
||||||
|
/// 创建新的JavaScript运行时
|
||||||
|
pub async fn new() -> Result<Self, JsEngineError> {
|
||||||
|
// 创建运行时
|
||||||
|
let runtime = AsyncRuntime::new()
|
||||||
|
.map_err(|e| JsEngineError::Runtime(format!("Failed to create runtime: {}", e)))?;
|
||||||
|
|
||||||
|
info!("JavaScript runtime created successfully");
|
||||||
|
|
||||||
|
// 创建完整上下文
|
||||||
|
let context = AsyncContext::full(&runtime)
|
||||||
|
.await
|
||||||
|
.map_err(|e| JsEngineError::Runtime(format!("Failed to create context: {}", e)))?;
|
||||||
|
|
||||||
|
let runtime = Arc::new(runtime);
|
||||||
|
let context = Arc::new(context);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
runtime,
|
||||||
|
context: Some(context),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取异步上下文
|
||||||
|
pub fn context(&self) -> Option<&AsyncContext> {
|
||||||
|
self.context.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 执行JavaScript代码片段并返回结果
|
||||||
|
pub async fn evaluate<R>(&self, code: &str) -> Result<R, JsEngineError>
|
||||||
|
where
|
||||||
|
R: for<'js> rquickjs::FromJs<'js> + Send + 'static,
|
||||||
|
{
|
||||||
|
let context = match &self.context {
|
||||||
|
Some(ctx) => ctx.clone(),
|
||||||
|
None => {
|
||||||
|
return Err(JsEngineError::Runtime(
|
||||||
|
"Context not initialized".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
context
|
||||||
|
.with(|ctx| {
|
||||||
|
let result = ctx.eval(code.as_bytes()).map_err(|e| {
|
||||||
|
JsEngineError::Execution(format!("Failed to evaluate JavaScript: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
rquickjs::FromJs::from_js(&ctx, result)
|
||||||
|
.map_err(|e| JsEngineError::Execution(format!("Type conversion failed: {}", e)))
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 执行JavaScript模块(带有export default)
|
||||||
|
pub async fn evaluate_module<R>(
|
||||||
|
&self,
|
||||||
|
module_name: &str,
|
||||||
|
code: &str,
|
||||||
|
) -> Result<R, JsEngineError>
|
||||||
|
where
|
||||||
|
R: for<'js> rquickjs::FromJs<'js> + Send + 'static,
|
||||||
|
{
|
||||||
|
let context = match &self.context {
|
||||||
|
Some(ctx) => ctx.clone(),
|
||||||
|
None => {
|
||||||
|
return Err(JsEngineError::Runtime(
|
||||||
|
"Context not initialized".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
context
|
||||||
|
.with(|ctx| {
|
||||||
|
let module =
|
||||||
|
Module::evaluate(ctx.clone(), module_name, code.as_bytes()).map_err(|e| {
|
||||||
|
JsEngineError::Execution(format!("Failed to evaluate module: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let default_export = module.get::<_, Value>("default").map_err(|e| {
|
||||||
|
JsEngineError::Execution(format!("Failed to get default export: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
rquickjs::FromJs::from_js(&ctx, default_export).map_err(|e| {
|
||||||
|
JsEngineError::Execution(format!("Module export type conversion failed: {}", e))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 创建一个JavaScript函数并立即调用(无参数)
|
||||||
|
pub async fn call_function<R>(&self, code: &str) -> Result<R, JsEngineError>
|
||||||
|
where
|
||||||
|
R: for<'js> rquickjs::FromJs<'js> + Send + 'static,
|
||||||
|
{
|
||||||
|
let context = match &self.context {
|
||||||
|
Some(ctx) => ctx.clone(),
|
||||||
|
None => {
|
||||||
|
return Err(JsEngineError::Runtime(
|
||||||
|
"Context not initialized".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 创建并调用函数
|
||||||
|
context
|
||||||
|
.with(|ctx| {
|
||||||
|
// 将代码包装为匿名函数并调用
|
||||||
|
let func_code = format!("(function() {{ {} }})", code);
|
||||||
|
let func: Function = ctx.eval(func_code.as_bytes()).map_err(|e| {
|
||||||
|
JsEngineError::Execution(format!("Failed to create function: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// 调用函数(无参数)
|
||||||
|
let result = func.call(()).map_err(|e| {
|
||||||
|
JsEngineError::Execution(format!("Failed to call function: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
rquickjs::FromJs::from_js(&ctx, result).map_err(|e| {
|
||||||
|
JsEngineError::Execution(format!("Return value conversion failed: {}", e))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查是否有待处理的作业
|
||||||
|
pub async fn has_pending_jobs(&self) -> bool {
|
||||||
|
self.runtime.is_job_pending().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 执行第一个待处理作业
|
||||||
|
pub async fn execute_pending_job(&self) -> Result<bool, JsEngineError> {
|
||||||
|
self.runtime
|
||||||
|
.execute_pending_job()
|
||||||
|
.await
|
||||||
|
.map_err(|e| JsEngineError::Runtime(format!("Failed to execute pending job: {}", e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 运行垃圾回收
|
||||||
|
pub async fn run_gc(&self) {
|
||||||
|
self.runtime.run_gc().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 释放上下文和运行时
|
||||||
|
pub async fn shutdown(&mut self) -> Result<(), JsEngineError> {
|
||||||
|
if let Some(context) = self.context.take() {
|
||||||
|
// 调用 GC 清理内存
|
||||||
|
self.run_gc().await;
|
||||||
|
|
||||||
|
// 释放上下文
|
||||||
|
drop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放运行时
|
||||||
|
// 注意:Arc 会处理引用计数
|
||||||
|
info!("JavaScript runtime shutdown complete");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,38 +1,47 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// JavaScript中间件钩子类型
|
/// JavaScript中间件钩子类型(简化版本)
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum MiddlewareHook {
|
pub enum MiddlewareHook {
|
||||||
/// 请求前钩子
|
/// 请求前钩子
|
||||||
OnRequest(MiddlewareFunction),
|
OnRequest,
|
||||||
/// 响应前钩子
|
/// 响应前钩子
|
||||||
OnResponse(MiddlewareFunction),
|
OnResponse,
|
||||||
/// 响应后钩子
|
/// 响应后钩子
|
||||||
OnResponseSent(MiddlewareFunction),
|
OnResponseSent,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 中间件函数类型
|
/// 中间件函数类型
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MiddlewareFunction {
|
pub struct MiddlewareFunction(pub String);
|
||||||
/// 函数代码
|
|
||||||
pub code: String,
|
impl std::fmt::Display for MiddlewareFunction {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MiddlewareFunction {
|
impl MiddlewareFunction {
|
||||||
pub fn new(code: String) -> Self {
|
pub fn new(code: String) -> Self {
|
||||||
Self { code }
|
Self(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for MiddlewareFunction {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 中间件容器
|
/// 中间件容器
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct MiddlewareContainer {
|
pub struct MiddlewareContainer {
|
||||||
/// 全局中间件
|
/// 全局中间件(hook -> functions)
|
||||||
global: Vec<MiddlewareHook>,
|
pub global: HashMap<MiddlewareHook, Vec<MiddlewareFunction>>,
|
||||||
/// 站点级别中间件(hostname -> hooks)
|
/// 站点级别中间件(hostname -> hooks)
|
||||||
site: HashMap<String, Vec<MiddlewareHook>>,
|
pub sites: HashMap<String, HashMap<MiddlewareHook, Vec<MiddlewareFunction>>>,
|
||||||
/// 路由级别中间件(route_key -> hooks)
|
/// 路由级别中间件(route_key -> hooks)
|
||||||
route: HashMap<String, Vec<MiddlewareHook>>,
|
pub routes: HashMap<String, HashMap<MiddlewareHook, Vec<MiddlewareFunction>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MiddlewareContainer {
|
impl MiddlewareContainer {
|
||||||
|
|
@ -40,37 +49,43 @@ impl MiddlewareContainer {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_global(&mut self, hook: MiddlewareHook) {
|
pub fn add_global(&mut self, hook: MiddlewareHook, func: MiddlewareFunction) {
|
||||||
self.global.push(hook);
|
self.global.entry(hook).or_default().push(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_site(&mut self, hostname: String, hook: MiddlewareHook) {
|
pub fn add_site(&mut self, hostname: String, hook: MiddlewareHook, func: MiddlewareFunction) {
|
||||||
self.site
|
self.sites
|
||||||
.entry(hostname)
|
.entry(hostname)
|
||||||
.or_insert_with(Vec::new)
|
.or_default()
|
||||||
.push(hook);
|
.entry(hook)
|
||||||
|
.or_default()
|
||||||
|
.push(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_route(&mut self, route_key: String, hook: MiddlewareHook) {
|
pub fn add_route(&mut self, route_key: String, hook: MiddlewareHook, func: MiddlewareFunction) {
|
||||||
self.route
|
self.routes
|
||||||
.entry(route_key)
|
.entry(route_key)
|
||||||
.or_insert_with(Vec::new)
|
.or_default()
|
||||||
.push(hook);
|
.entry(hook)
|
||||||
|
.or_default()
|
||||||
|
.push(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_site_hooks(&self, hostname: &str) -> Option<&[MiddlewareHook]> {
|
pub fn get_site_hooks(
|
||||||
self.site.get(hostname).map(|v| v.as_slice())
|
&self,
|
||||||
|
hostname: &str,
|
||||||
|
) -> Option<&HashMap<MiddlewareHook, Vec<MiddlewareFunction>>> {
|
||||||
|
self.sites.get(hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_route_hooks(&self, route_key: &str) -> Option<&[MiddlewareHook]> {
|
pub fn get_route_hooks(
|
||||||
self.route.get(route_key).map(|v| v.as_slice())
|
&self,
|
||||||
}
|
route_key: &str,
|
||||||
|
) -> Option<&HashMap<MiddlewareHook, Vec<MiddlewareFunction>>> {
|
||||||
pub fn global_hooks(&self) -> &[MiddlewareHook] {
|
self.routes.get(route_key)
|
||||||
&self.global
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.global.is_empty() && self.site.is_empty() && self.route.is_empty()
|
self.global.is_empty() && self.sites.is_empty() && self.routes.is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
use rquickjs::{AsyncContext, AsyncRuntime, Error};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let rt = AsyncRuntime::new()?;
|
||||||
|
let ctx = AsyncContext::full(&rt).await?;
|
||||||
|
|
||||||
|
ctx.with(|ctx| {
|
||||||
|
let value: i32 = ctx.eval("1 + 1")?;
|
||||||
|
println!("1 + 1 = {}", value);
|
||||||
|
Ok::<(), Error>(())
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
use rhttpd::js_engine::{JsEngineError, JsRuntime};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), JsEngineError> {
|
||||||
|
println!("Testing JavaScript runtime...");
|
||||||
|
|
||||||
|
// 创建运行时
|
||||||
|
let runtime = JsRuntime::new().await?;
|
||||||
|
println!("Runtime created successfully");
|
||||||
|
|
||||||
|
// 测试简单表达式
|
||||||
|
let result: i32 = runtime.evaluate("1 + 2").await?;
|
||||||
|
println!("1 + 2 = {}", result);
|
||||||
|
|
||||||
|
// 测试字符串
|
||||||
|
let message: String = runtime.evaluate("'Hello, World!'").await?;
|
||||||
|
println!("Message: {}", message);
|
||||||
|
|
||||||
|
// 测试布尔值
|
||||||
|
let is_true: bool = runtime.evaluate("5 > 3").await?;
|
||||||
|
println!("5 > 3 is {}", is_true);
|
||||||
|
|
||||||
|
// 测试函数调用
|
||||||
|
let func_result: i32 = runtime.call_function("return 42;").await?;
|
||||||
|
println!("Function returned: {}", func_result);
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
println!("Runtime test completed successfully");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue