16 KiB
libhttp — C++20 HTTP / WebSocket 网络库
1. 模块概览
libhttp 是 micro_servers 项目的基础网络通信层,封装了 HTTP/HTTPS 服务器、HTTP 客户端(基于 libcurl)、WebSocket 服务器和 WebSocket 客户端四大功能。
以 C++20 标准编写,编译为动态库 libus_http.so 和静态库 libus_http_static.a,命名空间 pmc::net。
| 属性 | 值 |
|---|---|
| 版本 | 0.0.1 |
| 语言标准 | C++20 |
| 许可证 | MIT |
| 框架名 | us |
| 库名 | libus_http |
| 命名空间 | pmc::net |
项目角色:libhttp 被 gateway 模块和 core 模块直接依赖,承担所有入站请求的接收、路由、SSL 卸载和 WebSocket 长连接管理。
2. 文件清单
libhttp/
├── .gitignore # 构建产物忽略规则
├── CMakeLists.txt # CMake 构建 (v3.12+)
├── LICENSE # MIT License
├── Makefile # GNU Make 构建
├── _set.sh # 一键编译安装脚本
├── inc/us/
│ ├── HttpClient.hpp # HTTP 客户端 (header-only)
│ ├── HttpServer.hpp # HTTP/HTTPS 服务器声明
│ └── detail/
│ └── HttpServerImpl.hpp # Session/Listener 内部共享定义
└── src/
├── HttpServer.cpp # 核心生命周期/SSL 初始化
├── HttpServer_Listener.cpp # TCP 连接接受器
├── HttpServer_Routes.cpp # 路由注册/匹配/通配符
├── HttpServer_Request.cpp # 请求分发/静态文件/CORS/HTTP 读写
├── HttpServer_Ws.cpp # WebSocket 路由注册/客户端管理
└── HttpServer_WsSession.cpp# WebSocket 会话/代理桥接
3. 外部依赖
| 依赖 | 用途 | 备注 |
|---|---|---|
| Boost 1.91+ | Asio (I/O)、Beast (HTTP/WS)、Algorithms | 默认 BOOST_ROOT=C:/local/boost_1_91_0,可通过环境变量覆盖 |
| OpenSSL | TLS/SSL 加密传输 | 通过 Boost.Asio.SSL 调用 |
| libcurl | HTTP 客户端请求 | 仅 HttpClient.hpp 依赖 |
| pthread (Unix) | 多线程 | CMake 自动链接 |
| ws2_32 (Windows) | Winsock | CMake 自动链接 |
4. 构建
4.1 GNU Make
# 编译 (仅 HttpServer.o → libus_http.so)
make
# 清理
make clear
# 安装到 /lib 和 /usr/include/us/
make install
# 卸载
make uninstall
注意:HttpServer 拆分为 6 个 TU,统一编译进
libus_http.so;WebSocket 能力已内置于 HttpServer(单端口同时服务 HTTP + WebSocket),客户端通过HttpClient提供,无独立 WebSocketServer/WebSocketClient 类。
4.2 CMake
mkdir build && cd build
cmake .. -DBOOST_ROOT=/path/to/boost
cmake --build .
cmake --install . --prefix /usr/local
CMake 会同时产出 libus_http.so (服务端) 和 libus_httpclient.so (客户端)。
4.3 一键脚本
bash _set.sh # make clear → make → make install
5. API 参考
5.1 HttpServer — HTTP/HTTPS 服务器
头文件: <us/HttpServer.hpp>
实现: src/HttpServer.cpp
5.1.1 类型别名
using HttpRequestHandler = std::function<http::response<http::string_body>(
const http::request<http::string_body>& req,
const std::unordered_map<std::string, std::string>& params)>;
using MiddlewareHandler = std::function<bool(
const http::request<http::string_body>& req,
http::response<http::string_body>& res,
const std::unordered_map<std::string, std::string>& params)>;
HttpRequestHandler: 路由处理函数,接收 Boost.Beast 请求对象和已解析的查询参数,返回 HTTP 响应。MiddlewareHandler: 中间件函数,返回false表示拦截(短路后续处理),true表示放行。
5.1.2 构造函数
| 签名 | 说明 |
|---|---|
HttpServer(address, port, threads = 1) |
纯 HTTP 模式 |
HttpServer(address, port, cert_file, key_file, threads = 1) |
HTTPS 模式(自动启用 SSL) |
address: 监听地址,如"0.0.0.0"port: 监听端口threads: 工作线程数(即io_context线程池大小)cert_file/key_file: PEM 格式证书和私钥路径
5.1.3 路由注册
void get(const std::string& path, HttpRequestHandler handler);
void post(const std::string& path, HttpRequestHandler handler);
void put(const std::string& path, HttpRequestHandler handler);
void del(const std::string& path, HttpRequestHandler handler);
void options(const std::string& path, HttpRequestHandler handler);
void use(MiddlewareHandler middleware);
- 路径支持通配符:
*(任意字符) 和?(单个字符),自动编译为std::regex(大小写不敏感)。 - 匹配优先级:精确匹配 > 通配符匹配。
- 请求方法字符串对应:
"GET","POST","PUT","DELETE","OPTIONS"。
5.1.4 中间件
void use(MiddlewareHandler middleware);
中间件在路由匹配之前执行,按注册顺序依次调用。任意中间件返回 false 时,请求处理结束,响应直接返回客户端。
5.1.5 静态文件服务
void setStaticDirectory(const std::string& path);
设置静态资源根目录。当请求路径未匹配任何路由时,自动尝试从该目录读取文件。支持 MIME 类型自动识别:.html, .css, .js, .json, .png, .jpg/.jpeg。
5.1.6 OPTIONS 预检请求
当收到 OPTIONS 请求时:
- 优先查找自定义
OPTIONS路由处理器 - 若未注册自定义处理器且请求包含
Origin头,自动生成 CORS 响应- 自动检测该路径下已注册的方法(GET/POST/PUT/DELETE/OPTIONS)
- 返回
Access-Control-Allow-Origin,Access-Control-Allow-Methods,Access-Control-Allow-Headers,Access-Control-Max-Age
5.1.7 SSL 配置
void setPrivateKeyPassword(const std::string& password);
- SSL 上下文使用 TLS 协议,禁用 SSLv2/SSLv3
- 仅允许 ECDHE/DHE 密钥交换的强密码套件:
ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS - 支持 PEM 加密私钥密码
5.1.8 生命周期
void start(); // 启动服务器(非阻塞,线程池跑 io_context)
void stop(); // 停止服务器(关闭所有会话,join 线程)
void run(); // 启动并在当前线程运行 io_context(阻塞)
bool isRunning() const;
5.1.9 调试
void showStatus() const; // 打印服务器状态和已注册路由
内嵌类 HttpServer::debug 提供 print_all_header_fields() 静态方法,打印请求的全部头字段。
5.1.10 内部架构
HttpServer
├── Listener (内部类)
│ └── acceptor_ (tcp::acceptor)
│ └── async_accept → Session 实例
└── Session (内部类,enable_shared_from_this)
├── HTTP: socket_ (tcp::socket)
├── HTTPS: ssl_stream_ (asio::ssl::stream<tcp::socket>)
├── strand_ 保证线程安全
└── 读取 → handleRequest() → 写入 → [keep-alive 则继续读取]
5.2 HttpClient — HTTP 客户端
头文件: <us/HttpClient.hpp> (header-only,无独立 .cpp)
依赖: libcurl
5.2.1 枚举 HttpMethod
enum class HttpMethod { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS };
5.2.2 公共方法
HttpClient(); // 初始化 CURL 句柄,失败抛 runtime_error
~HttpClient(); // 清理 CURL 和 header 链表
void set_header(const std::string& header);
void clear_headers();
const std::map<std::string, std::string>& get_response_headers() const;
long get_response_code() const;
流式请求(通过回调函数接收响应体,保留原始字节):
void request_stream_sync(HttpMethod method, const std::string& url,
const std::string& data,
std::function<void(const std::string&)> callback);
// 便捷方法
void get_stream_sync(const std::string& url, std::function<void(const std::string&)> callback);
void post_stream_sync(const std::string& url, const std::string& data,
std::function<void(const std::string&)> callback);
void put_stream_sync(const std::string& url, const std::string& data,
std::function<void(const std::string&)> callback);
void delete_stream_sync(const std::string& url, std::function<void(const std::string&)> callback);
特点:
- 同步阻塞调用
- 响应体通过
CURLOPT_WRITEFUNCTION直接追加到std::string,保留所有换行符和特殊字符 - 超时设置:连接超时 10 秒,总超时 30 秒
- 每次请求调用
curl_easy_reset重置状态
5.3 WebSocket 支持
WebSocket 能力已内置于 HttpServer(单端口同时服务 HTTP + WebSocket),通过 ws()/ws_proxy() 注册路由;客户端通过 HttpClient 的 ws_* 方法提供(using WebSocketClient = HttpClient)。原独立 WebSocketServer/WebSocketClient 类已删除(未构建的历史遗留,见安全审计 P3-1)。
6. 使用示例
6.1 基本 HTTP 服务器
#include <us/HttpServer.hpp>
#include <iostream>
int main() {
pmc::net::HttpServer server("0.0.0.0", 8080, 4);
server.get("/hello", [](const auto& req, const auto& params) {
http::response<http::string_body> res{http::status::ok, req.version()};
res.set(http::field::content_type, "text/plain");
res.body() = "Hello, world!";
res.prepare_payload();
return res;
});
server.post("/echo", [](const auto& req, const auto& params) {
http::response<http::string_body> res{http::status::ok, req.version()};
res.set(http::field::content_type, "application/json");
res.body() = req.body();
res.prepare_payload();
return res;
});
server.start(); // 非阻塞启动
std::cout << "Server running on port " << server.getPort() << std::endl;
server.isRunning(); // 阻塞直到手动 stop()
}
6.2 HTTPS 服务器
pmc::net::HttpServer server("0.0.0.0", 443,
"/etc/ssl/certs/server.crt",
"/etc/ssl/private/server.key",
4);
server.setPrivateKeyPassword("mysecret");
server.start();
6.3 中间件 + 静态文件
pmc::net::HttpServer server("0.0.0.0", 8080);
// 日志中间件
server.use([](const auto& req, auto&, const auto&) {
std::cout << "[" << req.method_string() << "] " << req.target() << std::endl;
return true; // 继续处理
});
// 认证中间件
server.use([](const auto& req, auto& res, const auto&) {
auto auth = req.find(http::field::authorization);
if (auth == req.end()) {
res.result(http::status::unauthorized);
res.body() = "Unauthorized";
res.prepare_payload();
return false; // 拦截
}
return true;
});
server.setStaticDirectory("/var/www/html");
server.start();
6.4 HTTP 客户端
pmc::net::HttpClient client;
client.set_header("Authorization: Bearer token123");
client.set_header("Content-Type: application/json");
client.post_stream_sync("https://api.example.com/data",
R"({"key":"value"})",
[](const std::string& body) {
std::cout << "Response: " << body << std::endl;
});
long status = client.get_response_code();
auto& headers = client.get_response_headers();
6.5 WebSocket 服务器
pmc::net::HttpServer server("0.0.0.0", 9000, 4);
server.onWsConnect([](const std::string& clientId, const std::string& addr) {
std::cout << "Client connected: " << clientId << " from " << addr << std::endl;
});
server.ws("/chat", [](const std::string& clientId,
const std::string& message,
auto sendCallback) {
std::cout << "[" << clientId << "]: " << message << std::endl;
sendCallback("echo: " + message);
});
server.onWsDisconnect([](const std::string& clientId) {
std::cout << "Client disconnected: " << clientId << std::endl;
});
server.start();
6.6 WebSocket 客户端
pmc::net::HttpClient ws_client;
if (ws_client.ws_connect("ws://localhost:9000/chat")) {
ws_client.ws_send("hello");
std::string reply = ws_client.ws_recv();
std::cout << "Received: " << reply << std::endl;
ws_client.ws_close();
}
7. 请求处理流程
客户端 → TCP 连接
├─ HTTP: Session::doRead()
└─ HTTPS: Session::doSSLHandshake() → Session::doRead()
↓
HttpServer::handleRequest(req)
├─ 解析 target_path + query params
├─ 执行中间件链 (MiddlewareHandler[])
│ └─ 任意中间件返回 false → 短路返回
├─ OPTIONS 请求?
│ ├─ 有自定义处理器 → 执行
│ └─ 有 Origin → 自动生成 CORS 响应
├─ 路由匹配 (精确匹配 → 通配符匹配)
│ └─ 命中 → 执行 HttpRequestHandler
├─ 路由未命中 + static_directory_ 非空
│ └─ 尝试读取静态文件 → 自动设 Content-Type
└─ 全部未命中 → 404
8. 设计要点
8.1 线程模型
- HttpServer: 每个工作线程独立运行
io_context::run(),所有异步操作通过strand序列化,同一 Session 的读写不会并发冲突 - HttpClient: 同步阻塞,无额外线程(HTTP 经 libcurl;WebSocket 经 Beast)
8.2 会话管理
HttpServer通过weak_ptr<Session>跟踪所有活跃会话,stop()时遍历清理
8.3 发送队列
HttpServer::Session 的 ws_send_queue_ 与代理队列 (proxy_to_backend_queue_/proxy_to_client_queue_) 采用 std::deque 消息队列,确保消息按序发送且不会因并发写入冲突。
9. 安全特性
| 特性 | 实现位置 |
|---|---|
| SSL/TLS 加密 | HttpServer SSL 构造函数 |
| 禁止 SSLv2/SSLv3 | initSSLContext() |
| 仅强密码套件 (ECDHE/DHE) | initSSLContext() 密码列表 |
| 禁用 NULL/MD5/DSS | initSSLContext() 密码列表 |
| 加密私钥密码支持 | setPrivateKeyPassword() |
| CORS 自动处理 | handleRequest() OPTIONS 分支 |
10. 与其他模块的关系
micro_servers/
├── gateway/ ──依赖──→ libhttp (HttpServer 接收外部请求)
├── core/ ──依赖──→ libhttp (HTTP 服务基础能力)
└── libhttp/ ──依赖──→ Boost, OpenSSL, libcurl
- gateway 使用
pmc::net::HttpServer作为 API 网关入口,处理 JWT 认证、令牌刷新、请求转发 - core 模块使用 libhttp 提供的 HTTP 服务能力
11. 已知限制
- 无 HTTP/2 支持:仅 HTTP/1.1
- 静态文件 MIME 有限:仅支持 6 种常见类型
- 无请求体大小限制:依赖 Boost.Beast 默认配置(见安全审计 P1-1)
- HttpClient 仅同步:无异步 HTTP 客户端实现