Skip to content

Commit 3b950ec

Browse files
committed
[release] v1.0.0:发布前全面审查与修复
- CMake/Server 头版本号统一为 1.0.0,通过 configure_file 注入避免硬编码 - HttpServer 多线程安全文档补充(ioThreads > 1 时 handler 并发警告) - WebSocket 新增 onDisconnect 回调,正常/异常断开均触发 - clang-format 18 → 22.1.2,Standard 设为 Latest 支持 C++26 - 替换 5 个弃用选项为 clang-format 22 新语法 - CI 添加 pip install clang-format==22.1.1 保证 Ubuntu 上版本一致
1 parent 3abf53e commit 3b950ec

20 files changed

Lines changed: 188 additions & 51 deletions

.clang-format

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
# 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto, CSharp
44
# Language: Cpp
5-
# 标准: c++03, c++11, c++14, c++17, c++20, c++26, Latest, Auto
6-
Standard: Auto
5+
# 标准: c++03, c++11, c++14, c++17, c++20, Latest, Auto
6+
# 使用 Latest 以支持 C++26 语法(clang-format 22+)
7+
Standard: Latest
78
# 基于什么风格:LLVM、Google、GNU、Chromium、Mozilla、WebKit、Microsoft
89
# BasedOnStyle: LLVM
910
# 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always
@@ -38,8 +39,8 @@ AlignEscapedNewlines: Left
3839
AlignOperands: true
3940
# 对齐连续的尾随的注释
4041
AlignTrailingComments: true
41-
# 允许所有构造函数初始化列表放在下一行
42-
AllowAllConstructorInitializersOnNextLine: false
42+
# 构造函数初始化列表打包方式: Never, BinPack, CurrentLine, NextLine, NextLineOnly
43+
PackConstructorInitializers: NextLine
4344
# 允许所有参数放在下一行
4445
AllowAllArgumentsOnNextLine: false
4546
# 允许函数声明的所有参数在放在下一行
@@ -56,15 +57,13 @@ AllowShortIfStatementsOnASingleLine: Never
5657
AllowShortLoopsOnASingleLine : false
5758
# 允许短的Lambdas在同一行
5859
AllowShortLambdasOnASingleLine: None
59-
# 总是在定义返回类型后换行(deprecated)
60-
AlwaysBreakAfterDefinitionReturnType: None
61-
# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
62-
# AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
63-
AlwaysBreakAfterReturnType: None
60+
# 在返回类型后换行: None, Automatic, ExceptShortType, All, TopLevel,
61+
# AllDefinitions, TopLevelDefinitions
62+
BreakAfterReturnType: None
6463
# 总是在多行string字面量前换行
6564
AlwaysBreakBeforeMultilineStrings: false
66-
# 总是在template声明后换行
67-
AlwaysBreakTemplateDeclarations: Yes
65+
# 在template声明后换行: Leave, No, MultiLine, Yes
66+
BreakTemplateDeclarations: Yes
6867
# false表示函数实参要么都在同一行,要么都各自一行
6968
BinPackArguments: false
7069
# false表示所有形参要么都在同一行,要么都各自一行
@@ -129,8 +128,7 @@ ColumnLimit: 120
129128
PenaltyReturnTypeOnItsOwnLine: 999
130129
# 描述具有特殊意义的注释的正则表达式,它不应该被分割为多行或以其它方式改变
131130
CommentPragmas: '^ IWYU pragma:'
132-
# 构造函数的初始化列表要么都在同一行,要么都各自一行
133-
ConstructorInitializerAllOnOneLineOrOnePerLine: true
131+
# (已由 PackConstructorInitializers 替代)
134132
# 构造函数的初始化列表的缩进宽度
135133
# ConstructorInitializerIndentWidth: 2
136134
# 延续的行的缩进宽度

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ jobs:
3838
run: |
3939
sudo apt-get update
4040
sudo apt-get install -y libboost-all-dev libssl-dev libgtest-dev cmake
41+
pip install clang-format==22.1.1
4142
4243
# ── Linux 构建与测试 ──
4344
- name: Configure (Linux)

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
## [1.0.0] - 2026-04-11
10+
## [1.0.0] - 2026-04-12
1111

1212
首次公开发布。
1313

CLAUDE.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ find src -name '*.cpp' | xargs clang-tidy -p build
6767
- `Middleware.h` — Onion-model middleware pipeline with `MiddlewareNext` chaining
6868
- `Coroutine.h``Awaitable<T>` alias for `boost::asio::awaitable<T>`, plus `sleep()` / `coSpawn()` helpers
6969
- `Reflection.h` / `MetaJson.h` / `MetaRoutes.h` — C++26 reflection layer (see below)
70+
- `StaticFiles.h` — Static file serving with ETag/304, MIME detection, path traversal protection
71+
- `Multipart.h/cpp` — RFC 7578 multipart/form-data parser (256 part DoS limit)
72+
- `Session.h/cpp` — In-memory session manager with lazy GC, 128-bit secure random IDs, `makeSessionMiddleware` factory
73+
- `Version.h.in` — CMake-configured version header (single source of truth from `project(VERSION)`)
7074

7175
**`src/asio/`** — Boost.Asio concrete implementations:
7276
- `AsioEventLoop` — Wraps `boost::asio::io_context`, implements `EventLoop`

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cmake_minimum_required(VERSION 3.20)
2-
project(hical VERSION 0.2.0 LANGUAGES CXX)
2+
project(hical VERSION 1.0.0 LANGUAGES CXX)
33

44
# C++26 反射可选支持
55
option(HICAL_ENABLE_REFLECTION "Enable C++26 reflection (requires compatible compiler)" OFF)

examples/benchmark.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@ class BenchmarkClient
1616
int num_requests,
1717
std::atomic<int>& completed,
1818
std::atomic<int>& errors)
19-
: socket_(io)
20-
, resolver_(io)
21-
, num_requests_(num_requests)
22-
, completed_(completed)
23-
, errors_(errors)
19+
: socket_(io), resolver_(io), num_requests_(num_requests), completed_(completed), errors_(errors)
2420
{
2521
auto endpoints = resolver_.resolve(host, port);
2622
boost::asio::async_connect(socket_,

src/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Hical 核心库源码
22

3+
# 从 CMake 版本号生成 Version.h(唯一版本号来源)
4+
configure_file(
5+
${CMAKE_CURRENT_SOURCE_DIR}/core/Version.h.in
6+
${CMAKE_CURRENT_BINARY_DIR}/core/Version.h
7+
@ONLY
8+
)
9+
310
# 核心库实现
411
add_library(hical_core
512
core/InetAddress.cpp
@@ -24,6 +31,7 @@ add_library(hical_core
2431
target_include_directories(hical_core
2532
PUBLIC
2633
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
34+
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
2735
$<INSTALL_INTERFACE:include>
2836
)
2937

src/asio/AsioTimer.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@ namespace hical
55
{
66

77
AsioTimer::AsioTimer(AsioEventLoop* loop, double delay, Callback cb)
8-
: loop_(loop)
9-
, timer_(loop->getIoContext())
10-
, callback_(std::move(cb))
11-
, interval_(delay)
12-
, repeating_(false)
8+
: loop_(loop), timer_(loop->getIoContext()), callback_(std::move(cb)), interval_(delay), repeating_(false)
139
{
1410
}
1511

src/core/HttpResponse.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "HttpResponse.h"
2+
#include <iomanip>
23
#include <sstream>
34

45
namespace hical
@@ -71,8 +72,30 @@ namespace hical
7172
return;
7273
}
7374

75+
// RFC 6265 cookie-value 合法字符百分号编码
76+
// 合法: %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
77+
auto encodeCookieValue = [](const std::string& raw) -> std::string
78+
{
79+
std::ostringstream encoded;
80+
encoded << std::hex << std::uppercase;
81+
for (unsigned char c : raw)
82+
{
83+
bool safe = (c == 0x21) || (c >= 0x23 && c <= 0x2B) || (c >= 0x2D && c <= 0x3A)
84+
|| (c >= 0x3C && c <= 0x5B) || (c >= 0x5D && c <= 0x7E);
85+
if (safe)
86+
{
87+
encoded << static_cast<char>(c);
88+
}
89+
else
90+
{
91+
encoded << '%' << std::setw(2) << std::setfill('0') << static_cast<int>(c);
92+
}
93+
}
94+
return encoded.str();
95+
};
96+
7497
std::ostringstream oss;
75-
oss << name << "=" << value;
98+
oss << name << "=" << encodeCookieValue(value);
7699

77100
if (!options.path.empty())
78101
{

src/core/HttpServer.cpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "HttpServer.h"
22
#include "MemoryPool.h"
3+
#include "core/Version.h"
34
#include "WebSocket.h"
45
#include <boost/beast/websocket.hpp>
56
#include <iostream>
@@ -314,7 +315,7 @@ namespace hical
314315
// 设置通用头部
315316
auto& nativeRes = res.native();
316317
nativeRes.version(11);
317-
nativeRes.set(http::field::server, "hical/0.2.0");
318+
nativeRes.set(http::field::server, HICAL_VERSION_STRING);
318319
nativeRes.keep_alive(req.native().keep_alive());
319320
nativeRes.prepare_payload();
320321

@@ -333,7 +334,7 @@ namespace hical
333334
{
334335
// 请求体过大:返回 413 Payload Too Large
335336
http::response<http::string_body> res {http::status::payload_too_large, 11};
336-
res.set(http::field::server, "hical/0.2.0");
337+
res.set(http::field::server, HICAL_VERSION_STRING);
337338
res.set(http::field::connection, "close");
338339
res.body() = "Request body too large";
339340
res.prepare_payload();
@@ -352,33 +353,35 @@ namespace hical
352353
http::request<http::string_body> req,
353354
const Router::WsRoute& wsRoute)
354355
{
356+
std::unique_ptr<WebSocketSession> session;
357+
355358
try
356359
{
357360
ws::stream<tcp::socket> wsStream(std::move(socket));
358361

359362
// 接受 WebSocket 升级
360363
co_await wsStream.async_accept(req, boost::asio::use_awaitable);
361364

362-
WebSocketSession session(std::move(wsStream));
365+
session = std::make_unique<WebSocketSession>(std::move(wsStream));
363366

364367
// 调用连接回调
365368
if (wsRoute.onConnect)
366369
{
367-
co_await wsRoute.onConnect(session);
370+
co_await wsRoute.onConnect(*session);
368371
}
369372

370373
// 消息循环
371-
while (session.isOpen())
374+
while (session->isOpen())
372375
{
373-
auto msg = co_await session.receive();
376+
auto msg = co_await session->receive();
374377
if (!msg.has_value())
375378
{
376379
break;
377380
}
378381

379382
if (wsRoute.onMessage)
380383
{
381-
co_await wsRoute.onMessage(*msg, session);
384+
co_await wsRoute.onMessage(*msg, *session);
382385
}
383386
}
384387
}
@@ -389,6 +392,19 @@ namespace hical
389392
// 忽略正常关闭
390393
}
391394
}
395+
396+
// 连接断开回调(正常退出和异常退出都会触发)
397+
if (session && wsRoute.onDisconnect)
398+
{
399+
try
400+
{
401+
co_await wsRoute.onDisconnect(*session);
402+
}
403+
catch (...)
404+
{
405+
// 忽略断开回调中的异常
406+
}
407+
}
392408
}
393409

394410
} // namespace hical

0 commit comments

Comments
 (0)