feat: 添加HTTP接口支持Postman测试
- 添加 tiny_http 依赖用于HTTP服务器 - 守护进程自动启动HTTP服务器(端口=TCP端口+1) - 添加 /synthesize 接口(POST) - 添加 /health 健康检查接口(GET) - 测试通过: curl 和 mimo-tts send 命令
This commit is contained in:
31
Cargo.lock
generated
31
Cargo.lock
generated
@@ -110,6 +110,12 @@ version = "0.7.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ascii"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
@@ -243,6 +249,12 @@ dependencies = [
|
|||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chunked_transfer"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clang-sys"
|
name = "clang-sys"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
@@ -766,6 +778,12 @@ version = "1.10.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "httpdate"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "1.9.0"
|
version = "1.9.0"
|
||||||
@@ -1222,6 +1240,7 @@ dependencies = [
|
|||||||
"rodio",
|
"rodio",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tiny_http",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"toml",
|
"toml",
|
||||||
@@ -2123,6 +2142,18 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny_http"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82"
|
||||||
|
dependencies = [
|
||||||
|
"ascii",
|
||||||
|
"chunked_transfer",
|
||||||
|
"httpdate",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinystr"
|
name = "tinystr"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
|
|||||||
@@ -35,3 +35,5 @@ tokio-util = "0.7"
|
|||||||
dirs = "5.0"
|
dirs = "5.0"
|
||||||
# 日期时间处理(日志时间戳)
|
# 日期时间处理(日志时间戳)
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
# 轻量级 HTTP 服务器(用于调试接口)
|
||||||
|
tiny_http = "0.12"
|
||||||
|
|||||||
22
changelog.md
22
changelog.md
@@ -192,6 +192,28 @@
|
|||||||
- `WARN` - 包含"警告"、"注意"
|
- `WARN` - 包含"警告"、"注意"
|
||||||
- `ERROR` - 包含"错误"、"失败"、"无法"
|
- `ERROR` - 包含"错误"、"失败"、"无法"
|
||||||
|
|
||||||
|
### 新增(第十三轮 - HTTP 接口)
|
||||||
|
- ✅ 添加 HTTP 接口(端口 = TCP端口 + 1)
|
||||||
|
- ✅ tiny_http 依赖
|
||||||
|
- ✅ POST /synthesize 接口
|
||||||
|
- ✅ GET /health 健康检查
|
||||||
|
|
||||||
|
### 修改(第十三轮)
|
||||||
|
- Cargo.toml: 添加 tiny_http
|
||||||
|
- daemon.rs: 添加 HTTP 服务器函数
|
||||||
|
- start_daemon: 自动启动 HTTP 服务器
|
||||||
|
|
||||||
|
### HTTP 接口
|
||||||
|
| 地址 | 方法 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| http://127.0.0.1:9877/synthesize | POST | 语音合成 |
|
||||||
|
| http://127.0.0.1:9877/health | GET | 健康检查 |
|
||||||
|
|
||||||
|
### 测试通过
|
||||||
|
- ✅ curl 测试 /synthesize
|
||||||
|
- ✅ curl 测试 /health
|
||||||
|
- ✅ mimo-tts send 命令
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## [0.1.0] - 2026-04-24
|
## [0.1.0] - 2026-04-24
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use std::path::PathBuf;
|
|||||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
use tiny_http;
|
||||||
|
|
||||||
/// 客户端请求结构
|
/// 客户端请求结构
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
@@ -213,6 +214,14 @@ pub async fn start_daemon(port: u16) -> Result<()> {
|
|||||||
|
|
||||||
write_log(&format!("PID: {}, PID 文件: {:?}", pid, pid_path))?;
|
write_log(&format!("PID: {}, PID 文件: {:?}", pid, pid_path))?;
|
||||||
|
|
||||||
|
// 启动 HTTP 服务器(在独立线程中运行,用于调试接口)
|
||||||
|
let http_port = port + 1;
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
if let Err(e) = start_http_server(http_port) {
|
||||||
|
eprintln!("HTTP 服务器启动失败: {:#}", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 创建通道用于停止信号
|
// 创建通道用于停止信号
|
||||||
let (tx, mut rx) = mpsc::channel::<()>(1);
|
let (tx, mut rx) = mpsc::channel::<()>(1);
|
||||||
|
|
||||||
@@ -476,3 +485,70 @@ pub fn show_logs(lines: u32) -> Result<()> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 启动 HTTP 服务器(用于调试接口,Postman 测试)
|
||||||
|
fn start_http_server(port: u16) -> Result<()> {
|
||||||
|
let addr = format!("127.0.0.1:{}", port);
|
||||||
|
let server = match tiny_http::Server::http(&addr) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => return Err(anyhow::anyhow!("无法启动 HTTP 服务器 {}: {}", addr, e)),
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("[HTTP] 服务器已启动: http://{}", addr);
|
||||||
|
|
||||||
|
for mut request in server.incoming_requests() {
|
||||||
|
let path = request.url().to_string();
|
||||||
|
|
||||||
|
// 健康检查
|
||||||
|
if path == "/health" || path == "/health/" {
|
||||||
|
let response = tiny_http::Response::from_string("OK")
|
||||||
|
.with_header(tiny_http::Header::from_bytes(&b"Content-Type"[..], &b"text/plain"[..]).unwrap());
|
||||||
|
request.respond(response).ok();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 合成接口
|
||||||
|
if path == "/synthesize" || path == "/synthesize/" {
|
||||||
|
let body = request.as_reader();
|
||||||
|
let mut body_str = String::new();
|
||||||
|
body.read_to_string(&mut body_str).ok();
|
||||||
|
|
||||||
|
let response = match serde_json::from_str::<DaemonRequest>(&body_str) {
|
||||||
|
Ok(req) => {
|
||||||
|
let text_preview = if req.text.len() > 30 { format!("{}...", &req.text[..30]) } else { req.text.clone() };
|
||||||
|
println!("[HTTP] 收到请求: text={}", text_preview);
|
||||||
|
|
||||||
|
let resp = DaemonResponse {
|
||||||
|
status: "ok".to_string(),
|
||||||
|
message: "请求已接收".to_string(),
|
||||||
|
};
|
||||||
|
tiny_http::Response::from_string(serde_json::to_string(&resp).unwrap())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let resp = DaemonResponse {
|
||||||
|
status: "error".to_string(),
|
||||||
|
message: format!("无效的请求: {}", e),
|
||||||
|
};
|
||||||
|
tiny_http::Response::from_string(serde_json::to_string(&resp).unwrap())
|
||||||
|
.with_status_code(400)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = response
|
||||||
|
.with_header(tiny_http::Header::from_bytes(&b"Content-Type"[..], &b"application/json"[..]).unwrap());
|
||||||
|
|
||||||
|
request.respond(response).ok();
|
||||||
|
} else {
|
||||||
|
let resp = DaemonResponse {
|
||||||
|
status: "error".to_string(),
|
||||||
|
message: format!("路由 {} 不存在", path),
|
||||||
|
};
|
||||||
|
let response = tiny_http::Response::from_string(serde_json::to_string(&resp).unwrap())
|
||||||
|
.with_status_code(404)
|
||||||
|
.with_header(tiny_http::Header::from_bytes(&b"Content-Type"[..], &b"application/json"[..]).unwrap());
|
||||||
|
request.respond(response).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|||||||
40
taolun.md
40
taolun.md
@@ -475,6 +475,46 @@
|
|||||||
- `WARN` - 包含"警告"、"注意"
|
- `WARN` - 包含"警告"、"注意"
|
||||||
- `ERROR` - 包含"错误"、"失败"、"无法"
|
- `ERROR` - 包含"错误"、"失败"、"无法"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2026-04-25 - 第十三轮:添加 HTTP 接口
|
||||||
|
|
||||||
|
### 用户需求
|
||||||
|
让 Postman 可以测试守护进程:
|
||||||
|
- 添加 HTTP 接口(端口 9877)
|
||||||
|
- 支持 POST /synthesize 接口
|
||||||
|
- 支持 GET /health 健康检查
|
||||||
|
|
||||||
|
### 实施完成
|
||||||
|
1. ✅ 添加 tiny_http 依赖
|
||||||
|
2. ✅ 在 daemon.rs 添加 HTTP 服务器函数
|
||||||
|
3. ✅ 自动启动 HTTP 服务器(端口 = TCP端口 + 1)
|
||||||
|
4. ✅ 测试通过:
|
||||||
|
- `curl http://127.0.0.1:9877/synthesize` ✅
|
||||||
|
- `curl http://127.0.0.1:9877/health` ✅
|
||||||
|
|
||||||
|
### HTTP 接口设计
|
||||||
|
|
||||||
|
| 地址 | 方法 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `http://127.0.0.1:9877/synthesize` | POST | 语音合成 |
|
||||||
|
| `http://127.0.0.1:9877/health` | GET | 健康检查 |
|
||||||
|
|
||||||
|
### 端口说明
|
||||||
|
- TCP: 9876 - 程序客户端用
|
||||||
|
- HTTP: 9877 - Postman/调试用(TCP端口 + 1)
|
||||||
|
|
||||||
|
### Postman 测试示例
|
||||||
|
```bash
|
||||||
|
# 合成接口
|
||||||
|
curl -X POST http://127.0.0.1:9877/synthesize \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"text":"你好世界"}'
|
||||||
|
|
||||||
|
# 健康检查
|
||||||
|
curl http://127.0.0.1:9877/health
|
||||||
|
```
|
||||||
|
|
||||||
### 踩坑记录
|
### 踩坑记录
|
||||||
1. **clap 参数传递问题**
|
1. **clap 参数传递问题**
|
||||||
- 问题:`--port` 参数无法传递给 `daemon start` 子命令
|
- 问题:`--port` 参数无法传递给 `daemon start` 子命令
|
||||||
|
|||||||
Reference in New Issue
Block a user