# grweb

[![Rust](https://img.shields.io/badge/rust-2024%20edition-orange.svg)](https://www.rust-lang.org/)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

基于 [gorust](https://github.com/WLmutou/gorust) 协程运行时的高性能 Rust Web 框架，结合 Go 风格的并发模型与 Rust 的零成本抽象。

## 特性

- **Go 风格并发** — 基于 gorust 的 goroutine-per-connection 模型，每个连接一个轻量级协程
- **树形路由** — 支持路径参数 `/hello/:name`，精确匹配优先于参数匹配
- **中间件链** — 零堆分配的洋葱模型中间件，内置 Logger / Recovery / CORS
- **TOML 配置** — 所有参数通过 `config.toml` 统一管理，均有合理默认值
- **低 CPU 空闲** — 阻塞 I/O + 指数退避调度，空闲 CPU < 1%
- **高性能** — release 模式 ~89,000 QPS（与 gorust 原生示例差距 < 3%）

## 快速开始

### 1. 添加依赖

```toml
[dependencies]
grweb = { path = "http://github.com/WLmutou/grweb.git" }
serde_json = "1.0"
env_logger = "0.10"
```

### 2. 编写应用

```rust
use grweb::{Server, Router, Context, Response, ServerConfig,
    middleware::{LoggerMiddleware, RecoveryMiddleware, CORSMiddleware}};

fn main() {
    env_logger::init();

    let mut router = Router::new();

    // 全局中间件
    router.use_middleware(LoggerMiddleware);
    router.use_middleware(RecoveryMiddleware);
    router.use_middleware(CORSMiddleware::new(
        vec!["*".to_string()],
        vec!["GET".to_string(), "POST".to_string()],
        vec!["Content-Type".to_string()],
    ));

    // 路由
    router.get("/", |_ctx: Context| {
        Response::html("<h1>Hello grweb!</h1>")
    });

    router.get("/hello/:name", |ctx: Context| {
        let name = ctx.param("name").unwrap_or(&"World".to_string());
        Response::html(format!("<h1>Hello, {}!</h1>", name))
    });

    // 启动
    let config = ServerConfig::default();
    Server::new(config, router).run().unwrap();
}
```

### 3. 运行

```bash
RUST_LOG=info cargo run --release
```

访问 `http://127.0.0.1:9030/hello/grweb` 看到 `Hello, grweb!`

---

## 配置文件

创建 `config.toml`（所有字段均可选）：

```toml
[server]
host = "127.0.0.1"          # 监听地址（默认 127.0.0.1）
port = 9030                 # 监听端口（默认 9030）
worker_pool_size = 4        # 工作线程数（默认 CPU 核心数）
read_buffer_size = 8192     # 读缓冲区字节数（默认 8192）
tcp_nodelay = true          # TCP_NODELAY（默认 true）
keep_alive_timeout = 5      # Keep-Alive 超时秒数（默认 5）
static_dir = "public"       # 静态文件目录（默认 public）

[logging]
level = "error"             # trace | debug | info | warn | error（默认 info）

[cors]
allowed_origins = ["*"]
allowed_methods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
allowed_headers = ["Content-Type"]
```

加载配置：

```rust
use grweb::AppConfig;

let config = AppConfig::load("config.toml").expect("Failed to load config");

// 日志级别自动设置
unsafe { std::env::set_var("RUST_LOG", &config.logging.level); }
env_logger::init();

// CORS 从配置读取
router.use_middleware(CORSMiddleware::new(
    config.cors.allowed_origins,
    config.cors.allowed_methods,
    config.cors.allowed_headers,
));

// 服务器从配置构建
let server = Server::new(config.server, router);
```

---

## 路由

### 基本路由

```rust
router.get("/", handler);
router.post("/api/user", handler);
router.put("/api/user/:id", handler);
router.delete("/api/user/:id", handler);
```

### 路径参数

以 `:` 前缀定义参数，通过 `ctx.param()` 获取：

```rust
router.get("/user/:id", |ctx: Context| {
    let user_id = ctx.param("id").unwrap();
    Response::html(format!("User: {}", user_id))
});

router.get("/post/:year/:month", |ctx: Context| {
    let year = ctx.param("year").unwrap();
    let month = ctx.param("month").unwrap();
    // ...
});
```

### 路由优先级

精确匹配优先于参数匹配。例如同时注册 `/user/me` 和 `/user/:id`：

```rust
router.get("/user/me", |_| Response::html("It's me!"));
router.get("/user/:id", |ctx| {
    // /user/me 走上面，/user/123 走这里
});
```

---

## 中间件

### 内置中间件

| 中间件 | 说明 |
|--------|------|
| `LoggerMiddleware` | 记录请求方法、路径、状态码、耗时 |
| `RecoveryMiddleware` | 捕获 handler panic，返回 500 |
| `CORSMiddleware` | 添加跨域响应头 |

### 自定义中间件

实现 `Middleware` trait：

```rust
use grweb::{Middleware, Context, Response};

struct AuthMiddleware;

impl Middleware for AuthMiddleware {
    fn call(&self, ctx: Context, next: &dyn Fn(Context) -> Response) -> Response {
        // 前置逻辑
        if ctx.path.starts_with("/admin") {
            return Response::new(403, "Forbidden");
        }

        // 调用下一个中间件 / handler
        let mut response = next(ctx);

        // 后置逻辑
        response.headers.push(("X-Powered-By".to_string(), "grweb".to_string()));

        response
    }
}

// 注册
router.use_middleware(AuthMiddleware);
```

### 中间件链执行顺序

注册顺序即执行顺序（洋葱模型）：

```
请求 → Logger → Recovery → CORS → Handler → CORS → Recovery → Logger → 响应
```

---

## Context API

```rust
pub struct Context {
    pub method: Method,                    // HTTP 方法
    pub path: String,                      // 请求路径
    pub params: HashMap<String, String>,   // 路径参数
    pub headers: HashMap<String, String>,  // 请求头
    pub body: Vec<u8>,                     // 请求体
}

impl Context {
    // 获取路径参数
    pub fn param(&self, key: &str) -> Option<&String>;

    // 获取请求头
    pub fn header(&self, key: &str) -> Option<&String>;

    // 请求体转字符串
    pub fn body_string(&self) -> String;

    // 请求体转 JSON（需 serde::Deserialize）
    pub fn body_json<T: DeserializeOwned>(&self) -> Result<T, serde_json::Error>;

    // 获取单个表单字段（自动识别 urlencoded / multipart）
    pub fn form_value(&self, key: &str) -> Option<String>;

    // 获取所有表单字段
    pub fn form_values(&self) -> HashMap<String, String>;
}
```

---

## Response API

```rust
// 基础响应
Response::new(200, "OK")
Response::new(404, "Not Found")

// 快捷方法
Response::html("<h1>Title</h1>")          // Content-Type: text/html
Response::json(r#"{"key":"val"}"#)        // Content-Type: application/json
Response::not_found()                      // 404
Response::internal_error()                 // 500

// 自动类型转换
"hello"           → Response::html("hello")
"hello".to_string() → Response::html("hello")
vec![1,2,3]       → Response::new(200, vec![1,2,3])

// 自定义响应头
let mut resp = Response::html("<h1>OK</h1>");
resp.headers.push(("X-Custom".to_string(), "value".to_string()));
```

---

## 静态文件服务

通过 `serve_static` 将 URL 前缀映射到本地目录：

```rust
// 将 /static/* 映射到 ./public 目录
router.serve_static("/static", "public");
```

访问 `http://127.0.0.1:9030/static/css/style.css` → `./public/css/style.css`

**安全特性**：自动防止路径穿越攻击（`../` 等），不存在的文件返回 404。

**MIME 类型**：自动根据文件扩展名设置 Content-Type，支持 HTML/CSS/JS/JSON/图片/字体/视频/音频等 20+ 种类型。

---

## 表单数据解析

自动识别 `application/x-www-form-urlencoded` 和 `multipart/form-data`：

```rust
router.post("/login", |ctx: Context| {
    let username = ctx.form_value("username").unwrap_or_default();
    let password = ctx.form_value("password").unwrap_or_default();

    // 或一次性获取所有字段
    let all_fields = ctx.form_values();

    Response::html(format!("Welcome, {}!", username))
});
```

**URL 解码**：自动处理 `%XX` 和 `+`（空格）解码。

**multipart 支持**：自动提取 boundary、解析 `Content-Disposition` 头中的 `name` 字段。

---

## WebSocket

通过 `router.websocket()` 注册 WebSocket 路由，handler 接收 `WebSocket` 对象进行双向通信：

```rust
use grweb::{WebSocket, Message};

router.websocket("/ws", |mut ws: WebSocket| {
    ws.send_text(r#"{"type":"welcome"}"#);

    loop {
        match ws.read_message() {
            Some(Message::Text(text)) => {
                ws.send_text(&format!("Echo: {}", text));
            }
            Some(Message::Binary(data)) => {
                ws.send_binary(&data);
            }
            Some(Message::Ping(data)) => {
                ws.send_pong(&data);
            }
            Some(Message::Close(_)) => break,
            _ => break,
        }
    }
});
```

**WebSocket API**：

```rust
impl WebSocket {
    pub fn read_message(&mut self) -> Option<Message>;
    pub fn send_text(&mut self, text: &str) -> bool;
    pub fn send_binary(&mut self, data: &[u8]) -> bool;
    pub fn send_ping(&mut self, data: &[u8]) -> bool;
    pub fn send_pong(&mut self, data: &[u8]) -> bool;
    pub fn send_close(&mut self, code: u16, reason: &str) -> bool;
}

pub enum Message {
    Text(String),
    Binary(Vec<u8>),
    Close(Option<(u16, String)>),
    Ping(Vec<u8>),
    Pong(Vec<u8>),
}
```

**协议支持**：RFC 6455，支持分片消息重组、ping/pong 心跳、close 帧（含状态码）。

---

## 性能

wrk 压测（4 线程 / 100 连接 / 5 秒，AMD Ryzen）：

| 服务 | QPS | 延迟 | 备注 |
|------|-----|------|------|
| gorust web_server_yield | 89,300 | 626μs | 无路由/无解析/预构建响应 |
| gorust web_server_router | 72,900 | 816μs | HashMap 路由 |
| **grweb** | **88,000** | 660μs | 树形路由 + 3 层中间件 + 完整解析 |

空闲 CPU：**< 1%**（阻塞 I/O + 指数退避调度）

---

## features
- [x] Keep-Alive 连接复用
- [x] 请求头解析（Cookie / Authorization）
- [x] 静态文件服务（MIME 自动检测 / 路径穿越防护）
- [x] WebSocket 支持（RFC 6455 / 分片重组 / ping-pong）
- [x] 表单数据解析（urlencoded / multipart）
- HTTPS 支持
[x] 连接池管理
- 优雅降级和限流
- 测试用例（未见 tests/ 目录）

## License

MIT © 2026 WLmutou