675 Commits

Author SHA1 Message Date
xfy
bb1fe6853f chore: add MIT license
💘 Generated with Crush

Assisted-by: mimo-v2.5 via Crush <crush@charm.land>
2026-05-06 17:35:46 +08:00
xfy
c90dd10962 chore(ci): remove GitHub Actions workflow files
💘 Generated with Crush

Assisted-by: mimo-v2.5 via Crush <crush@charm.land>
2026-05-06 10:20:43 +08:00
xfy
1e01d4829d chore: release v0.2.2
Update version to 0.2.2 and add comprehensive CHANGELOG entry
covering 130+ commits since v0.2.1: autoindex module, nginx config
converter, ETag/304 support, layered caching, stale cache fallback,
slow start load balancing, performance optimizations, and more.

💘 Generated with Crush

Assisted-by: mimo-v2.5-pro via Crush <crush@charm.land>
v0.2.2
2026-04-30 16:56:14 +08:00
xfy
26d62c9fcd refactor(handler): improve autoindex code quality
Use fmt.Fprintf directly on buffer instead of buf.WriteString(fmt.Sprintf(...)),
handle dir.Close error in defer, use blank identifier for unused parameter,
use range-over-int, and remove trailing blank line.

💘 Generated with Crush

Assisted-by: GLM 5.1 via Crush <crush@charm.land>
2026-04-30 16:23:34 +08:00
xfy
5f470993ff perf(handler): use RWMutex for FileInfoCache
- Change FileInfoCache.mu from sync.Mutex to sync.RWMutex
- Get method uses RLock for concurrent read access
- Stats method uses RLock for read-only operation
- Double-check pattern for lock upgrades (TTL expiry, LRU move)

This improves concurrent read performance for the FileInfo cache,
which is read-heavy in static file serving scenarios.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 16:15:39 +08:00
xfy
3c96f12f74 feat(cache): store ContentType in FileEntry for cache hits
- Add ContentType field to FileEntry struct
- Update Set method signature to accept contentType parameter
- Use cached ContentType in static.go cache hit branches
- Update all test files to use new Set signature

This avoids redundant MIME type detection on cache hits,
reducing lock contention in mimeutil.DetectContentType.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 16:13:42 +08:00
xfy
23fdcf89ae perf(handler): use cached ETag to avoid regeneration on cache hits
- Use entry.ETag instead of generateETag() in cache hit branches
- Add 304 response check before returning cached data
- Reduces ETag computation overhead for cached files

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 16:03:59 +08:00
xfy
5593a729b8 perf(compression): use bytes operations to avoid string allocation
- Replace strings.ToLower(string(...)) with bytes.ToLower
- Reduces memory allocation in hot path for Accept-Encoding checks

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 15:59:42 +08:00
xfy
73ef3a938b perf(handler): add FileInfo cache to handleTryFiles and handleInternalRedirect
- Check fileInfoCache before os.Stat in handleTryFiles
- Check fileInfoCache before os.Stat in handleInternalRedirect
- Reduces system calls for try_files scenarios

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 15:56:42 +08:00
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
0de9922e7d fix(makefile): restrict bench target to internal directory
- Change ./... to ./internal/... to avoid scanning lib/fasthttp
- Add -run=^$$ to skip regular tests, run only benchmarks
- Properly escape $ in Makefile with $$

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 00:05:54 +08:00
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
0e8b99e17f docs: 精简并重构 README 文档结构
移除冗余的配置示例、生产清单和常见问题章节,新增核心模块依赖关系、
设计模式和性能优化设计等架构文档,统一 Markdown 表格格式。

💘 Generated with Crush

Assisted-by: mimo-v2.5-pro via Crush <crush@charm.land>
2026-04-29 15:47:28 +08:00
xfy
3a09ca4f48 chore: 添加 bench-*.txt 到 gitignore
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 14:57:45 +08:00
xfy
7d2c71c921 chore(build): 配置 TMPDIR 避免 tmpfs 空间不足
- Makefile 设置 TMPDIR 为项目 tmp 目录
- 更新 docs/prompts.md 基准测试命令示例

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 14:57:12 +08:00
xfy
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
xfy
dbeb2217a5 chore(deps): 升级 websocket 为直接依赖,更新 zerolog 等版本
- gorilla/websocket 从间接依赖改为直接依赖
- zerolog v1.35.0 → v1.35.1
- mattn/go-isatty v0.0.21 → v0.0.22
- oschwald/maxminddb-golang v1.13.0 → v1.13.1

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 11:04:30 +08:00
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
1424402d06 ci(bench): 添加基准测试 GitHub Actions workflow
- 触发于 master 分支 push 和 PR
- 运行 go test -bench -count=10 获取统计数据
- 使用 benchstat 对比基准线
- 调用 check_regression.py 检测回归
- 自动更新基准线并保存 artifacts

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 10:33:50 +08:00
xfy
7da9ffcb6b chore(bench): 扩展 Makefile 基准测试命令
- bench 目标输出到 bench-results.txt,count=5 采样
- 添加 bench-regression 目标,使用阈值配置检测回归

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 10:33:15 +08:00
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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
xfy
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