```
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}")]
|
||||
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运行环境、中间件系统和配置加载功能
|
||||
//! 使用rquickjs库集成QuickJS引擎
|
||||
|
||||
pub mod bindings;
|
||||
pub mod error;
|
||||
pub mod middleware;
|
||||
pub mod runtime;
|
||||
pub mod types;
|
||||
|
||||
pub use bindings::{JsRequest, JsResponse, register_bindings};
|
||||
pub use error::JsEngineError;
|
||||
pub use middleware::{MiddlewareExecutor, extract_middleware_from_config};
|
||||
pub use runtime::JsRuntime;
|
||||
pub use types::{MiddlewareContainer, MiddlewareFunction, MiddlewareHook};
|
||||
|
||||
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;
|
||||
|
||||
/// JavaScript中间件钩子类型
|
||||
#[derive(Debug, Clone)]
|
||||
/// JavaScript中间件钩子类型(简化版本)
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum MiddlewareHook {
|
||||
/// 请求前钩子
|
||||
OnRequest(MiddlewareFunction),
|
||||
OnRequest,
|
||||
/// 响应前钩子
|
||||
OnResponse(MiddlewareFunction),
|
||||
OnResponse,
|
||||
/// 响应后钩子
|
||||
OnResponseSent(MiddlewareFunction),
|
||||
OnResponseSent,
|
||||
}
|
||||
|
||||
/// 中间件函数类型
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MiddlewareFunction {
|
||||
/// 函数代码
|
||||
pub code: String,
|
||||
pub struct MiddlewareFunction(pub String);
|
||||
|
||||
impl std::fmt::Display for MiddlewareFunction {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl MiddlewareFunction {
|
||||
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)]
|
||||
pub struct MiddlewareContainer {
|
||||
/// 全局中间件
|
||||
global: Vec<MiddlewareHook>,
|
||||
/// 全局中间件(hook -> functions)
|
||||
pub global: HashMap<MiddlewareHook, Vec<MiddlewareFunction>>,
|
||||
/// 站点级别中间件(hostname -> hooks)
|
||||
site: HashMap<String, Vec<MiddlewareHook>>,
|
||||
pub sites: HashMap<String, HashMap<MiddlewareHook, Vec<MiddlewareFunction>>>,
|
||||
/// 路由级别中间件(route_key -> hooks)
|
||||
route: HashMap<String, Vec<MiddlewareHook>>,
|
||||
pub routes: HashMap<String, HashMap<MiddlewareHook, Vec<MiddlewareFunction>>>,
|
||||
}
|
||||
|
||||
impl MiddlewareContainer {
|
||||
|
|
@ -40,37 +49,43 @@ impl MiddlewareContainer {
|
|||
Self::default()
|
||||
}
|
||||
|
||||
pub fn add_global(&mut self, hook: MiddlewareHook) {
|
||||
self.global.push(hook);
|
||||
pub fn add_global(&mut self, hook: MiddlewareHook, func: MiddlewareFunction) {
|
||||
self.global.entry(hook).or_default().push(func);
|
||||
}
|
||||
|
||||
pub fn add_site(&mut self, hostname: String, hook: MiddlewareHook) {
|
||||
self.site
|
||||
pub fn add_site(&mut self, hostname: String, hook: MiddlewareHook, func: MiddlewareFunction) {
|
||||
self.sites
|
||||
.entry(hostname)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(hook);
|
||||
.or_default()
|
||||
.entry(hook)
|
||||
.or_default()
|
||||
.push(func);
|
||||
}
|
||||
|
||||
pub fn add_route(&mut self, route_key: String, hook: MiddlewareHook) {
|
||||
self.route
|
||||
pub fn add_route(&mut self, route_key: String, hook: MiddlewareHook, func: MiddlewareFunction) {
|
||||
self.routes
|
||||
.entry(route_key)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(hook);
|
||||
.or_default()
|
||||
.entry(hook)
|
||||
.or_default()
|
||||
.push(func);
|
||||
}
|
||||
|
||||
pub fn get_site_hooks(&self, hostname: &str) -> Option<&[MiddlewareHook]> {
|
||||
self.site.get(hostname).map(|v| v.as_slice())
|
||||
pub fn get_site_hooks(
|
||||
&self,
|
||||
hostname: &str,
|
||||
) -> Option<&HashMap<MiddlewareHook, Vec<MiddlewareFunction>>> {
|
||||
self.sites.get(hostname)
|
||||
}
|
||||
|
||||
pub fn get_route_hooks(&self, route_key: &str) -> Option<&[MiddlewareHook]> {
|
||||
self.route.get(route_key).map(|v| v.as_slice())
|
||||
}
|
||||
|
||||
pub fn global_hooks(&self) -> &[MiddlewareHook] {
|
||||
&self.global
|
||||
pub fn get_route_hooks(
|
||||
&self,
|
||||
route_key: &str,
|
||||
) -> Option<&HashMap<MiddlewareHook, Vec<MiddlewareFunction>>> {
|
||||
self.routes.get(route_key)
|
||||
}
|
||||
|
||||
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