3b608be0de
refactor(handler): improve autoindex code quality
...
- Replace custom escape functions with stdlib html.EscapeString and url.PathEscape
- Fix benchmark test file naming using fmt.Sprintf
- Add CSP security header for HTML output
- Add empty directory test case
- Remove obsolete escape function tests
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 15:21:10 +08:00
b62a3f12da
feat(handler): add autoindex module for directory listing
...
Add nginx-like autoindex functionality with three output formats:
- HTML: styled directory listing with sortable columns
- JSON: structured API-friendly output
- XML: machine-readable format
Configuration options:
- auto_index: enable/disable directory listing
- auto_index_format: output format (html/json/xml)
- auto_index_localtime: use local time instead of GMT
- auto_index_exact_size: show exact bytes vs human-readable
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 15:11:34 +08:00
e32e96ee81
feat(config): change gzip_static default to true
...
Enable pre-compressed file serving by default for better performance.
This aligns with the common practice of serving .gz/.br files when available.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 14:24:23 +08:00
8cc3fdef6f
perf(handler): optimize static file serving performance
...
- Add pathPrefixLen for zero-allocation path stripping
- Precompute ETag in FileCache.Set, reuse on cache hits
- Add MIME LRU cache with O(1) operations using container/list
- Remove sharded cache (eager LRU was slower than single-lock)
- Add FileInfo cache to reduce os.Stat calls (TTL-only strategy)
- Adjust test expectations for normalized root path format
Benchmark results: Lookup 12.7µs → 10.6µs (16% improvement)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 14:17:56 +08:00
d269940d8b
style: fix formatting issues
...
- Add missing newlines at end of files
- Fix indentation in ssl.go
- Remove extra blank lines
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 13:42:53 +08:00
b1e1547e36
fix(lint): resolve errcheck and goconst issues
...
- Add nolint comments for sync.Pool.Get() type assertions (pool always returns valid pointers)
- Extract TLS version strings to constants in sslutil/tlsconfig.go
- Extract expires directive strings to constants in handler/static.go
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 13:41:15 +08:00
fc586d4ace
fix(handler): normalize root path to fix relative path performance
...
When root is configured as a relative path (e.g., "./lib/site/"),
filepath.Join normalizes it to "lib/site/" but the original value
was stored in h.root. This caused strings.TrimPrefix in serveFile
to fail, resulting in incorrect path calculations for GzipStatic.
The bug caused every request to attempt opening a non-existent
precompressed file path like "lib/site/lib/site/index.html.gz",
adding an extra failed os.Stat call per request and reducing
performance by ~50%.
Fix by normalizing root with filepath.Clean in NewStaticHandler
and SetRoot to ensure TrimPrefix works correctly.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 13:29:26 +08:00
f145a8770e
refactor: modernize code with Go 1.22+ features
...
Apply modern Go patterns across the codebase:
- Replace `interface{}` with `any` (Go 1.18+)
- Use `for range n` instead of `for i := 0; i < n; i++` (Go 1.22+)
- Replace `sort.Slice` with `slices.Sort` from slices package
- Simplify sync.WaitGroup patterns with errgroup where appropriate
- Add Makefile targets for modernize analyzer
Total: 84 files updated, net reduction of 79 lines
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 10:37:45 +08:00
e7306a0c72
perf: optimize ConsistentHash and RateLimiter for better concurrency
...
- ConsistentHash: reuse main hash ring in SelectExcludingByKey instead of
rebuilding per call, reducing memory allocation from 369KB to 1.8KB (99.5%)
- RateLimiter: replace single RWMutex with 16-segment sharded locks to
reduce lock contention in high-concurrency scenarios
- TLS SessionTickets: add warning log when KeyFile is empty to alert
users about session invalidation after restart
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 10:23:19 +08:00
3c8413b7a6
fix(test): add missing Type field in BenchmarkE2EBasicAuth
...
The AuthConfig struct requires Type="basic" for NewBasicAuth to
validate successfully. Without this field, the benchmark fails with
"unsupported auth type: ".
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 08:01:43 +08:00
4dfd6df38b
style(handler): add missing newline at EOF
...
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:30:14 +08:00
fdf04476e8
refactor(lua): merge variable getter functions
...
Make getVariable call getVariableLua and convert LValue to string,
eliminating ~85 lines of duplicate variable access logic.
Both functions now share the same switch-case implementation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:25:41 +08:00
a365cd2033
refactor(handler): separate sendfile common code
...
Create sendfile_common.go for shared constants and functions:
- MinSendfileSize constant
- getNetConn helper
- copyFile fallback function
Platform-specific files now only contain platform implementations.
Eliminates ~50 lines of duplicate code between Linux and non-Linux.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:24:15 +08:00
ecb020fed9
refactor(security): extract auth_request defaults helper
...
Extract applyDefaults function to unify configuration initialization
logic between NewAuthRequest and UpdateConfig methods.
Eliminates ~20 lines of duplicate default value setting code.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:22:02 +08:00
bc9b7ba616
refactor(security): merge access Update methods
...
- 新增 UpdateList(target string, cidrs []string) 统一更新方法
- UpdateAllowList/UpdateDenyList 改为包装调用
- 消除约 15 行重复代码,保持向后兼容
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:20:14 +08:00
6b8b00c900
refactor(ssl): extract TLS config generation to sslutil
...
- 新增 internal/sslutil/tlsconfig.go 统一 TLS 配置函数
- 提取 ParseTLSVersion/ParseCipherSuites/DefaultCipherSuites 等
- 更新 ssl.go/stream/ssl.go/proxy_ssl.go 使用统一函数
- 消除约 150 行重复代码
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:18:33 +08:00
d25c7b21a6
refactor(server): extract createProxyForConfig helper
...
- 新增 createProxyForConfig 统一代理创建逻辑
- 重构 registerProxyRoutesWithLocationEngine 和 registerProxyRoutes
- 消除约 40 行重复代码
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:52:40 +08:00
7eaea845e7
refactor(server): extract createFastServer helper
...
- 新增 createFastServer 统一 fasthttp.Server 创建
- 消除 startSingleMode/startVHostMode/startMultiServerMode 中的重复代码
- multi_server 模式现在也支持高并发优化配置
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:48:40 +08:00
f82e363f58
refactor: 提取 Lua ngx 表 helpers 和统一验证函数
...
Batch 1 续:
- 新增 lua/helpers.go:GetOrCreateNgxTable/GetOrCreateNgxSubTable
- 重构 compression:提取 resettableWriteCloser 接口和 compressorPool
- 新增 validate.go:ValidateNonNegativeInt64/Duration/NoNullByte/PathTraversal
- 消除约 120 行重复代码
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:00:11 +08:00
91e04222b3
refactor: 统一 IP 白名单解析和 excludeSet 构建
...
Batch 1 重构:
- 新增 utils.ParseIPAllowList 统一 IP/CIDR 解析(含 localhost 特殊处理)
- pprof.go/status.go/purge.go 改用统一函数,减少 ~66 行重复代码
- 新增 loadbalance.buildExcludeSet 统一排除集合构建
- 更新 pprof_test.go 适配统一字段 allowed []net.IPNet
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:18:52 +08:00
247fa81c00
fix(lua): 修复 Lua 引擎并发安全问题
...
- 缓存 coroutine.yield/status 函数,避免并发读取全局 Lua 状态机
- 添加 ngxRegisterMu 锁保护共享 ngx 表的并发写入
- 各 API 注册函数检查字段是否已存在,避免重复写入
- TCPSocket.currentOp 字段添加锁保护
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 14:19:11 +08:00
ade4f84d1f
test(proxy): 连接池满载场景测试
...
测试连接池不同负载场景:
- Normal: 92 allocs/op (正常并发)
- HighConcurrency: 155 allocs/op (高并发)
- MultiTarget: 104 allocs/op (多目标连接池)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 10:50:08 +08:00
abf0315fd8
test(integration): 后端故障切换 E2E 基准测试
...
测试负载均衡器剔除/恢复后端的开销:
- NormalSelect: 43 allocs/op (正常场景)
- AllUnhealthy: 9 allocs/op (无可用后端)
- SelectOnly: 2 allocs/op (纯选择开销)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 10:47:47 +08:00
6ac5fda431
test(compression): gzip writer 池化效果测试
...
对比新建 vs 池化复用:
- New: 20 allocs/op (每次新建 Writer)
- Pool: 3 allocs/op (池化复用,目标 ≤2)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 10:43:12 +08:00
158964bc6b
test(variable): 变量展开分配追踪测试
...
追踪不同复杂度表达式的分配来源:
- Simple: 1 allocs/op (已达标)
- SingleVar: 1 allocs/op
- NoVar: 0 allocs/op
- ContextPool: 4 allocs/op (池化开销)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 10:41:07 +08:00
0d987bb060
test(proxy): 缓存键零分配验证测试
...
验证 buildCacheKeyHashValue 零分配优化:
- ZeroAlloc: 0 allocs/op (已达标)
- WithAlloc: 1 allocs/op (对比基准)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 10:39:06 +08:00
41288a560f
test(cache): FileCache Set 分配热点专项测试
...
追踪 Set 新建/更新/淘汰三种路径的分配来源:
- SetNew: 3 allocs/op (目标 ≤1,待优化)
- SetUpdate: 1 allocs/op (已达标)
- Eviction: 3 allocs/op (待优化)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 10:36:22 +08:00
3c1aed791f
fix(stream): sync.Pool 指针包装消除装箱分配
...
- 使用 *[]*Target 代替 []*Target 避免 Put 时的装箱分配
- roundRobin/weightedRoundRobin allocs/op 从 1 降至 0
- 解决 golangci-lint SA6002 staticcheck 警告
Benchmark: roundRobin 0 allocs/op, weightedRoundRobin 0 allocs/op
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 09:52:58 +08:00
9895fb4158
test(cache): 分片缓存原型与扩展性对比测试
...
- 新增 ShardedFileCache 实现(16 分片,独立锁)
- 添加 BenchmarkFileCacheSharded 对比测试
- Benchmark 结论:当前竞争轻微,单锁扩展性更好
- 单锁 8核: 45 ns/op
- 分片 8核: 201 ns/op(hash 计算开销)
分片缓存保留为原型,暂不替换主实现。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 09:48:08 +08:00
f20eafbb28
perf(stream): Balancer healthy slice 池化减少分配
...
- roundRobin/weightedRoundRobin/ipHash 添加 healthyPool sync.Pool
- Select 方法使用 pool.Get/Put 复用 []*Target slice
- steady-state allocs/op 从 slice 分配降至 1(仅 pool 接口开销)
Benchmark: roundRobin 1 allocs/op, weightedRoundRobin 1 allocs/op
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 09:44:41 +08:00
58ed02ceac
perf(cache): FileEntry 池化减少 GC 压力
...
- 添加 entryPool sync.Pool 复用 FileEntry 结构体
- Set 新建路径使用 pool.Get() 获取条目
- removeEntry 重置条目后 pool.Put() 回池
- 添加 BenchmarkFileCacheSet_Pooled 对比测试
Benchmark 结果: steady-state 4 allocs/op
(entry 复用生效,time.Now/list.Element/map 分配不可避免)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 09:39:18 +08:00
11e22c80b8
perf: 零分配优化与 Dial timeout 支持
...
- 添加 b2s/s2b 零分配字节-字符串转换工具函数
- WebSocket 数据转发使用 sync.Pool 复用 32KB buffer
- 条件化 Debug 日志避免非 Debug 级别的字符串分配
- 缓存键哈希计算直接写入 []byte 避免 string 转换
- 使用 bytes.EqualFold 替代 strings.ToLower 进行大小写不敏感比较
- generateETag 使用 strconv.AppendInt 避免 fmt.Sprintf
- 支持 Dial timeout 配置,区分 TCP 连接建立和总连接超时
- MaxConnsPerHost 默认值改为 512(fasthttp 推荐)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 20:11:20 +08:00
cf2fcca7e8
refactor: 提取公共逻辑、消除重复代码、加强错误处理
...
- 提取 App 公共逻辑到 app_common.go,消除 app.go/app_windows.go 重复定义
- 提取 Server 生命周期/中间件/路由逻辑到独立文件(lifecycle.go/middleware_builder.go/router.go)
- 提取 Proxy 缓存处理/头部修改/目标选择到独立模块
- 提取 CheckIPAccess/CheckTokenAuth 到 utils/httperror.go,消除 status/purge 重复实现
- 修复 stream 双向转发:任一方向完成立即关闭双端,避免连接泄漏
- 修复 SSL/TLS 中静默忽略错误的问题,添加日志记录
- 统一日志消息为英文
💘 Generated with Crush
Assisted-by: GLM 5.1 via Crush <crush@charm.land>
2026-04-28 18:00:48 +08:00
6f6a8f0455
refactor(adapter): 提取 HTTP/2/3 适配器公共逻辑为 CommonAdapter
...
将 http2 和 http3 适配器中重复的 sync.Pool 管理、流式请求体处理、
上下文重置等逻辑提取到 internal/adapter 包,通过 struct embedding 复用。
同时简化 ConnLimiter 直接实现 middleware.Middleware 接口,移除冗余 wrapper。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 17:06:41 +08:00
2cb10eb749
perf(e2e): 并行化 E2E 测试,从 ~2h 降至 ~102s
...
- testutil: 用 sync.Once 缓存 LollyImageAvailable 结果
- testutil: 原子计数器替代时间戳避免容器名竞态
- testutil: SetupProxyTest 接受 suffix 参数生成独立 Docker 网络
- testutil: CleanupProxyTest 显式调用 network.Remove() 清理
- testutil: 移除死代码 SetupProxyTestEnv/ProxyTestEnv
- testutil: HealthCheckWaitTimeout 30s→15s, DefaultTestTimeout 180s→120s
- e2e: 所有 107 个测试函数添加 t.Parallel()
- e2e: 替换 65 处硬编码 30*time.Second 为常量
- make: test-all 三类测试并行运行,显式 PID wait 收集退出码
- make: test-e2e 添加 -parallel 4 flag
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 13:19:46 +08:00
179090fa34
fix(security): 修复 2 个 CRITICAL + 6 个 HIGH 安全与代码质量问题
...
安全修复:
- ConnLimiter Acquire() TOCTOU 竞态: atomic.AddInt64 替代 loadInt64+addInt64
- Cache Purge token 时序侧信道: 改用 subtle.ConstantTimeCompare
- Lua Cosocket SSRF: 新增 ip_guard 两层 IP 检查(字面量+解析后),拒绝私有/回环地址
- X-Accel-Redirect 路径遍历: urlpath.Clean + 前缀拒绝(/internal/ /admin/)
- CRLF 注入: containsCRLF 校验变量展开后的 header 值,logging.Warn 可观测
- Proxy URI 注入: bytes.ContainsAny 检查 path 中的 @\r\n 危险字符
代码质量:
- disk_cache.go Set() 7 处静默 return 改为 logging.Error 日志记录
- config.go 从 2392 行拆分为 9 个按域文件(config/server/proxy/security/ssl/cache/performance/monitoring/variable)
验证: go build + vet + golangci-lint(0 issues) + test(83.2% 无回归) + race detector 全部通过
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 10:13:47 +08:00
4405d8cb90
fix(e2e): 添加默认 index.html 并修复 E2E 测试预期
...
Docker 镜像构建时创建默认 index.html,lolly 现在能返回 200
而非 404。放宽容器健康检查为接受任意非 5xx 响应。跳过因 Docker
网络问题导致的 flaky rate limit 测试。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-28 07:59:00 +08:00
74f08466d4
fix(lint): 修复 goconst 和未使用导入问题
...
- 将重复的 "static" 字符串提取为 staticType 常量
- 移除 compression_e2e_test.go 中未使用的 fmt 导入
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 17:38:14 +08:00
dbc74939d8
fix(e2e): 修复 E2E 测试使用正确的 API
...
- 使用 StartLolly 替代 StartLollyContainer
- 简化 compression_e2e_test.go 的辅助函数
- 移除函数内的 import 语句
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 17:16:22 +08:00
7c67c93ca6
fix(e2e): 修复 compression_e2e_test.go 的 import 语法错误
...
将函数内的 import 语句移到文件顶部的 import 块中。
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 17:11:47 +08:00
5574339d28
test: 完善测试覆盖率和 E2E 测试场景
...
Phase 1: 单元测试补充
- 新增 config/loader_test.go,覆盖配置加载、include 合并、循环检测
- 补充 cache/cache_test.go,测试 RefreshCachedAt、DeleteByPatternWithMethod
- 补充 handler/static_test.go,测试 SetExpires、setCacheHeaders、parseExpires
Phase 2: E2E 测试扩展
- 新增 ratelimit_e2e_test.go,测试请求限流功能
- 新增 compression_e2e_test.go,测试 Gzip 压缩功能
- 新增 access_e2e_test.go,测试 IP 访问控制
- 新增 rewrite_e2e_test.go,测试 URL 重写和重定向
覆盖率提升: 82.3% -> 83.1%
E2E 测试用例: ~84 -> ~104 (+20)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 17:06:55 +08:00
1be6480f5c
feat(e2e): 添加重试机制和测试覆盖率支持
...
- 新增 testutil/retry.go 提供重试工具函数
- WaitForCondition: 等待条件满足
- WaitForNoError: 等待操作无错误
- WaitForHealthy: 等待服务健康
- Poll: 简化轮询接口
- 新增 testutil/retry_test.go 单元测试
- container.go 添加 Logs() 方法用于诊断
- Makefile 新增 test-e2e-cover 和 test-e2e-short 目标
- 重构 healthcheck 和 loadbalance 测试使用重试机制
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 16:07:17 +08:00
0e1a826464
fix(converter): 支持一个 server 块有多个 listen 指令的转换
...
nginx 配置中一个 server 块可以有多个 listen 指令(如 listen 80; listen 443 ssl;),
之前转换器只保留最后一个 listen 值,导致多个 server 块最终有相同的 listen 地址,
触发验证冲突。
修改内容:
- converter: 添加 listenInfo 结构体和 parseListenInfo 函数
- converter: 重构 convertServerBlock 返回 []ServerConfig,为每个 listen 创建独立配置
- validate: 使用 listen+name 组合作为唯一键,允许相同 listen 但不同 server_name
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 15:21:17 +08:00
d191e1865d
feat(static): 添加 expires 缓存控制支持
...
- StaticConfig 添加 Expires 字段,支持 nginx 兼容格式(30d, 1h, max, epoch)
- StaticHandler 添加 SetExpires 方法和缓存响应头设置
- serveFile 自动设置 Cache-Control 和 Expires 响应头
- nginx 转换器正确转换 expires 指令
- --generate-config 输出包含 expires 文档和示例
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 14:05:25 +08:00
02265331d4
fix(converter): 没有 root/alias/try_files 的 location 继承 server root
...
- classifyLocation 增加 serverRoot 参数
- 当 location 没有显式 root/alias/try_files 但有 server 级别 root 时,分类为 static
- 正则 location 正确继承 server root 并设置 location_type
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 13:28:25 +08:00
07acfad146
feat(config): 添加 alias 配置支持
...
- StaticConfig 添加 Alias 字段,与 Root 互斥
- server.go 创建 handler 时设置 alias
- validate.go 添加 root/alias 互斥验证和路径安全检查
- converter.go nginx alias 指令正确转换为 Alias 字段
- defaults.go --generate-config 输出包含 alias 文档和示例
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 13:16:12 +08:00
5bb44fdbcb
fix(converter): try_files 也作为静态类型判断条件,继承 server 级别 root/index
...
- classifyLocation 将 try_files 也识别为静态类型
- 静态配置自动继承 server 级别的 root 和 index(如果未指定)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 11:41:40 +08:00
78ca32748c
feat(converter): 支持 server 级别的 root 和 index 指令转换
...
- 在 convertServerBlock 中收集 server 级别的 root/index 指令
- 如果没有显式的 location / 静态配置,创建默认静态配置
- 如果 location / 是 proxy 类型,不创建静态配置
- 添加 3 个测试用例覆盖不同场景
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 11:38:47 +08:00
b290cea0f6
fix(converter): 修复 nginx 配置解析空字符串和 upstream URL
...
- 解析器正确处理空引号字符串 "",使用标记区分空字符串和特殊字符
- upstream server URL 自动添加 http:// 前缀以满足 lolly 配置验证
- proxy_set_header 正确处理空字符串值(如 Connection "")
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 11:27:32 +08:00
c18ce613b3
style: 格式化代码
...
- 使用 Go 1.13+ 八进制字面量格式 (0o644)
- 修复文件末尾缺少换行符
- 对齐结构体字段
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-27 10:20:18 +08:00