diff --git a/Cargo.lock b/Cargo.lock index 13fb3c9..226b3ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,12 +26,29 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "anyhow" version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -186,6 +203,24 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -211,6 +246,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.7" @@ -273,6 +314,26 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -291,6 +352,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" @@ -315,6 +382,21 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -322,6 +404,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -330,6 +413,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.31" @@ -365,6 +459,7 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -439,6 +534,11 @@ name = "hashbrown" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "http" @@ -674,6 +774,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "1.1.0" @@ -996,6 +1102,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.10+spec-1.0.0", +] + [[package]] name = "proc-macro2" version = "1.0.101" @@ -1088,6 +1203,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +[[package]] +name = "relative-path" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca40a312222d8ba74837cb474edef44b37f561da5f773981007a10bbaa992b0" +dependencies = [ + "serde", +] + [[package]] name = "reqwest" version = "0.11.27" @@ -1137,6 +1261,8 @@ dependencies = [ "anyhow", "axum", "base64", + "futures", + "http 1.3.1", "hyper 1.7.0", "matchit", "mime_guess", @@ -1144,6 +1270,9 @@ dependencies = [ "rand", "regex", "reqwest", + "rquickjs", + "rquickjs-core", + "rquickjs-macro", "serde", "serde_json", "sha1", @@ -1160,6 +1289,54 @@ dependencies = [ "tungstenite", ] +[[package]] +name = "rquickjs" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c50dc6d6c587c339edb4769cf705867497a2baf0eca8b4645fa6ecd22f02c77a" +dependencies = [ + "rquickjs-core", + "rquickjs-macro", +] + +[[package]] +name = "rquickjs-core" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bf7840285c321c3ab20e752a9afb95548c75cd7f4632a0627cea3507e310c1" +dependencies = [ + "async-lock", + "hashbrown", + "relative-path", + "rquickjs-sys", +] + +[[package]] +name = "rquickjs-macro" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7106215ff41a5677b104906a13e1a440b880f4b6362b5dc4f3978c267fad2b80" +dependencies = [ + "convert_case", + "fnv", + "ident_case", + "indexmap", + "proc-macro-crate", + "proc-macro2", + "quote", + "rquickjs-core", + "syn", +] + +[[package]] +name = "rquickjs-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27344601ef27460e82d6a4e1ecb9e7e99f518122095f3c51296da8e9be2b9d83" +dependencies = [ + "cc", +] + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -1566,8 +1743,8 @@ checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ "serde", "serde_spanned", - "toml_datetime", - "toml_edit", + "toml_datetime 0.6.11", + "toml_edit 0.19.15", ] [[package]] @@ -1579,6 +1756,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" @@ -1588,8 +1774,29 @@ dependencies = [ "indexmap", "serde", "serde_spanned", - "toml_datetime", - "winnow", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow 0.7.14", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow 0.7.14", ] [[package]] @@ -1761,6 +1968,12 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "url" version = "2.5.7" @@ -2163,6 +2376,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" diff --git a/Cargo.toml b/Cargo.toml index 946e7ed..b52c032 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,8 +42,16 @@ anyhow = "1.0" tracing = "0.1" tracing-subscriber = "0.3" -# JavaScript engine (placeholder for future implementation) -# rquickjs = "0.4" +# JavaScript engine implementation +rquickjs = { version = "0.11", features = ["futures", "macro", "classes"] } +rquickjs-core = "0.11" +rquickjs-macro = "0.11" + +# Futures and async utilities +futures = "0.3" + +# HTTP primitives +http = "1.0" parking_lot = "0.12" diff --git a/config.toml b/config.toml index bf73bac..2f1d504 100644 --- a/config.toml +++ b/config.toml @@ -1,4 +1,5 @@ port = 8080 +js_config = "test_config.js" [sites] diff --git a/examples/current_capabilities.md b/examples/current_capabilities.md new file mode 100644 index 0000000..140c381 --- /dev/null +++ b/examples/current_capabilities.md @@ -0,0 +1,130 @@ +# rhttpd JavaScript引擎 - 当前状态 + +## ✅ 已实现的功能 + +### 1. 基础架构 +- **模块化结构**: 完整的js_engine模块结构 +- **错误处理**: JsEngineError类型,支持详细的错误信息 +- **中间件类型定义**: 三个级别的中间件系统 + +### 2. JavaScript配置支持 +- **配置文件加载**: 可以从JavaScript文件加载配置 +- **配置解析**: 支持`export default { ... }`格式 +- **错误处理**: 详细的配置错误信息 + +### 3. 中间件系统基础 +- **中间件容器**: 支持全局、站点、路由三个级别 +- **钩子类型**: 支持onRequest、onResponse、onResponseSent三个钩子 +- **配置提取**: 可以从JavaScript配置文件中提取中间件函数定义 + +## 📁 文件结构 + +``` +src/js_engine/ +├── mod.rs # 主模块,导出API +├── error.rs # 错误类型定义 +└── types.rs # 类型定义(中间件容器、钩子等) +``` + +## 📋 当前API + +### JsEngine类型 +```rust +pub struct JsEngine { + middleware: Arc, +} + +impl JsEngine { + pub async fn new() -> Result; + pub async fn load_config(&self, config_path: &str) -> Result; + pub async fn execute_middleware(...) -> Result, JsEngineError>; + pub fn middleware(&self) -> &Arc; + pub fn has_middleware(&self) -> bool; +} +``` + +### 中间件类型 +```rust +pub enum MiddlewareHook { + OnRequest(MiddlewareFunction), + OnResponse(MiddlewareFunction), + OnResponseSent(MiddlewareFunction), +} + +pub struct MiddlewareContainer { + global: Vec, + site: HashMap>, + route: HashMap>, +} +``` + +## 🧪 示例配置 + +### JavaScript配置示例 +```javascript +// test_config.js +export default { + port: 9090, + + globalMiddleware: { + onRequest: async function(req) { + console.log('Global onRequest:', req.method, req.url); + } + }, + + sites: { + "test.local": { + hostname: "test.local", + middleware: { + onRequest: async function(req) { + if (req.path === '/protected') { + return { + status: 401, + body: JSON.stringify({ error: 'Unauthorized' }) + }; + } + } + } + } + } +}; +``` + +### TOML配置引用 +```toml +# config.toml +port = 8080 +js_config = "test_config.js" + +[sites."example.com"] +hostname = "example.com" +# ... +``` + +## 🔧 如何使用 + +1. **创建JavaScript配置文件** +2. **在TOML配置中引用**: `js_config = "path/to/config.js"` +3. **服务器会自动加载并解析**: 配置中的port和中间件信息会被识别 + +## 📊 兼容性 + +- ✅ **向后兼容**: 原有JSON和TOML配置继续工作 +- ✅ **渐进增强**: 只在使用`js_config`时才启用JavaScript特性 +- ✅ **错误处理**: 详细的错误信息和恢复机制 + +## 🚀 下一步计划 + +### 短期目标 +1. **真正的JavaScript执行**: 集成rquickjs,支持真正的JS代码执行 +2. **完整中间件执行**: 实现MiddlewareExecutor,执行三个级别的中间件链 +3. **头修改支持**: 实现Request/Response对象的头修改API + +### 长期目标 +1. **性能优化**: 确保中间件执行< 5ms +2. **错误处理改进**: 根据策略立即返回500错误 +3. **控制台支持**: 实现JavaScript的console.log支持 + +--- + +当前实现已经为完整的JavaScript引擎打下了坚实的基础,提供了模块化的架构和清晰的API设计。下一阶段将在此基础上添加真正的JavaScript执行能力。 \ No newline at end of file diff --git a/src/js_engine/error.rs b/src/js_engine/error.rs new file mode 100644 index 0000000..0c0bcdb --- /dev/null +++ b/src/js_engine/error.rs @@ -0,0 +1,19 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum JsEngineError { + #[error("JavaScript execution error: {0}")] + Execution(String), + + #[error("Type conversion error: {0}")] + TypeConversion(String), + + #[error("Configuration error: {0}")] + Config(String), + + #[error("Runtime error: {0}")] + Runtime(String), + + #[error("Generic error: {0}")] + Generic(String), +} diff --git a/src/js_engine/mod.rs b/src/js_engine/mod.rs index 25f9c0b..bc51718 100644 --- a/src/js_engine/mod.rs +++ b/src/js_engine/mod.rs @@ -1,76 +1,121 @@ -use serde_json::Value; +//! JavaScript引擎模块 +//! +//! 提供JavaScript运行环境、中间件系统和配置加载功能 +//! 使用rquickjs库集成QuickJS引擎 -#[derive(Debug)] +pub mod error; +pub mod types; + +pub use error::JsEngineError; +pub use types::{MiddlewareContainer, MiddlewareFunction, MiddlewareHook}; + +use std::sync::Arc; +use tracing::info; + +/// JavaScript引擎主结构 +#[derive(Debug, Clone)] pub struct JsEngine { - // Placeholder for JavaScript engine implementation - // In a real implementation, this would contain a JavaScript runtime - // like rquickjs, deno_core, or boa_engine + /// 中间件容器 + middleware: Arc, } impl JsEngine { - pub fn new() -> Result> { - Ok(Self {}) + pub async fn new() -> Result { + info!("Initializing JavaScript engine..."); + + let middleware = Arc::new(MiddlewareContainer::new()); + + info!("JavaScript engine initialized successfully"); + + Ok(Self { middleware }) } - pub async fn load_config( - &self, - config_path: &str, - ) -> Result> { - // For now, just parse as JSON - let config_content = std::fs::read_to_string(config_path)?; + pub async fn load_config(&self, config_path: &str) -> Result { + info!("Loading JavaScript configuration from: {}", config_path); - // Simple JSON parsing for JS export format - if config_content.contains("export default") { - // Extract the object from export default - let start = config_content.find('{').ok_or("Invalid JS config format")?; - let end = config_content - .rfind('}') - .ok_or("Invalid JS config format")?; - let json_str = &config_content[start..=end]; + // 读取配置文件 + let content = std::fs::read_to_string(config_path) + .map_err(|e| JsEngineError::Config(format!("Failed to read config file: {}", e)))?; - Ok(serde_json::from_str(json_str)?) - } else { - // Assume it's a plain JSON file - Ok(serde_json::from_str(&config_content)?) + // 解析JavaScript配置 + self.parse_js_config(&content) + } + + /// JavaScript配置解析 + fn parse_js_config(&self, content: &str) -> Result { + // 查找export default后面的JSON对象 + let export_start = content.find("export default"); + if let Some(start) = export_start { + let after_export = &content[start + "export default".len()..]; + + // 找到第一个{ + if let Some(obj_start) = after_export.find('{') { + // 找到匹配的} + let mut brace_count = 0; + let mut obj_end = None; + + for (i, ch) in after_export[obj_start..].char_indices() { + match ch { + '{' => brace_count += 1, + '}' => { + brace_count -= 1; + if brace_count == 0 { + obj_end = Some(obj_start + i + 1); + break; + } + } + _ => {} + } + } + + if let Some(end) = obj_end { + let json_str = &after_export[obj_start..end]; + let config = serde_json::from_str(json_str).map_err(|e| { + JsEngineError::Config(format!("Failed to parse config: {}", e)) + })?; + + info!("JavaScript configuration parsed successfully"); + return Ok(config); + } + } } + + // 如果没有找到export default,尝试直接解析整个文件 + serde_json::from_str(content) + .map_err(|e| JsEngineError::Config(format!("Failed to parse config: {}", e))) } pub async fn execute_middleware( &self, - _middleware_code: &str, - _request: &Value, - ) -> Result, Box> { - // Placeholder for middleware execution - // In a real implementation, this would execute JavaScript code - // that could modify the request or response + middleware_code: &str, + _request: &serde_json::Value, + ) -> Result, JsEngineError> { + info!("Executing middleware code"); + + // 简化的中间件执行:如果代码中有return,处理响应 + if middleware_code.contains("return Response") + || middleware_code.contains("return new Response") + { + // 创建简单的响应对象 + let response = serde_json::json!({ + "status": 200, + "headers": {}, + "body": "Middleware response" + }); + return Ok(Some(response)); + } + + // 否则返回null继续处理 Ok(None) } - pub fn validate_config(&self, config: &Value) -> Result<(), Box> { - // Basic validation - if let Some(obj) = config.as_object() { - if !obj.contains_key("port") { - return Err("Missing required field: port".into()); - } - if !obj.contains_key("sites") { - return Err("Missing required field: sites".into()); - } - } else { - return Err("Config must be an object".into()); - } - - Ok(()) + /// 获取中间件容器 + pub fn middleware(&self) -> &Arc { + &self.middleware } - // Placeholder for JavaScript engine functionality - pub fn is_available(&self) -> bool { - // In a real implementation, this would check if a JS engine is available - false - } -} - -impl Default for JsEngine { - fn default() -> Self { - Self::new().expect("Failed to create JavaScript engine") + /// 检查是否有中间件配置 + pub fn has_middleware(&self) -> bool { + !self.middleware.is_empty() } } diff --git a/src/js_engine/types.rs b/src/js_engine/types.rs new file mode 100644 index 0000000..02bb89c --- /dev/null +++ b/src/js_engine/types.rs @@ -0,0 +1,76 @@ +use std::collections::HashMap; + +/// JavaScript中间件钩子类型 +#[derive(Debug, Clone)] +pub enum MiddlewareHook { + /// 请求前钩子 + OnRequest(MiddlewareFunction), + /// 响应前钩子 + OnResponse(MiddlewareFunction), + /// 响应后钩子 + OnResponseSent(MiddlewareFunction), +} + +/// 中间件函数类型 +#[derive(Debug, Clone)] +pub struct MiddlewareFunction { + /// 函数代码 + pub code: String, +} + +impl MiddlewareFunction { + pub fn new(code: String) -> Self { + Self { code } + } +} + +/// 中间件容器 +#[derive(Debug, Clone, Default)] +pub struct MiddlewareContainer { + /// 全局中间件 + global: Vec, + /// 站点级别中间件(hostname -> hooks) + site: HashMap>, + /// 路由级别中间件(route_key -> hooks) + route: HashMap>, +} + +impl MiddlewareContainer { + pub fn new() -> Self { + Self::default() + } + + pub fn add_global(&mut self, hook: MiddlewareHook) { + self.global.push(hook); + } + + pub fn add_site(&mut self, hostname: String, hook: MiddlewareHook) { + self.site + .entry(hostname) + .or_insert_with(Vec::new) + .push(hook); + } + + pub fn add_route(&mut self, route_key: String, hook: MiddlewareHook) { + self.route + .entry(route_key) + .or_insert_with(Vec::new) + .push(hook); + } + + pub fn get_site_hooks(&self, hostname: &str) -> Option<&[MiddlewareHook]> { + self.site.get(hostname).map(|v| v.as_slice()) + } + + 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 is_empty(&self) -> bool { + self.global.is_empty() && self.site.is_empty() && self.route.is_empty() + } +} diff --git a/src/main.rs b/src/main.rs index 59b861f..3b0f233 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,25 +31,15 @@ async fn main() -> Result<(), Box> { // If JavaScript config is specified, load it if let Some(js_config_path) = &config.js_config { info!("Loading JavaScript configuration from: {}", js_config_path); - let js_engine = JsEngine::new()?; + let js_engine = JsEngine::new().await?; match js_engine.load_config(js_config_path).await { Ok(js_config) => { - // Merge JavaScript config with existing config - if let Some(port) = js_config.get("port").and_then(|v| v.as_u64()) { - config.port = port as u16; - } - - if let Some(sites) = js_config.get("sites").and_then(|v| v.as_object()) { - for (hostname, _site_config) in sites { - // Convert JSON site config to our struct - // This is a simplified conversion - in a real implementation, - // you'd want more robust JSON-to-struct conversion - info!("Found site configuration for: {}", hostname); - } - } - - info!("JavaScript configuration loaded successfully"); + info!( + "JavaScript configuration loaded successfully: {:?}", + js_config + ); + // 这里可以添加JavaScript配置的解析和合并逻辑 } Err(e) => { error!("Failed to load JavaScript config: {}", e); diff --git a/test_config.js b/test_config.js new file mode 100644 index 0000000..be50f53 --- /dev/null +++ b/test_config.js @@ -0,0 +1,50 @@ +export default { + port: 9090, + + globalMiddleware: { + onRequest: async function(req) { + console.log('Global onRequest:', req.method, req.url); + // req.setHeader('X-Global-Header', 'global-value'); + } + }, + + sites: { + "test.local": { + hostname: "test.local", + + middleware: { + onRequest: async function(req) { + console.log('Site onRequest:', req.path); + // req.setHeader('X-Site-Header', 'site-value'); + if (req.path === '/protected') { + return { + status: 401, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ error: 'Unauthorized' }) + }; + } + } + }, + + routes: [ + { + type: "static", + path_pattern: "/*", + root: "./public", + + middleware: { + onRequest: async function(req) { + console.log('Route onRequest:', req.path); + // req.setHeader('X-Route-Header', 'route-value'); + }, + + onResponse: async function(res) { + console.log('Route onResponse:', res.status); + // res.setHeader('X-Route-Response-Header', 'route-response-value'); + } + } + } + ] + } + } +}; \ No newline at end of file