77 Commits

Author SHA1 Message Date
xfy
f33117b940 fix(handler,http2,loadbalance,logging,resolver,ssl): fix high severity issues
- handler/static.go: add sync.RWMutex to StaticHandler; protect Handle
  with RLock and all setters with Lock to prevent data races
- http2/server.go: delete empty connection slice keys from pool map to
  prevent memory leak under high client churn
- loadbalance/slow_start.go: recreate stopCh in Start() to support
  Start-Stop-Start cycles; guard double-close in Stop()
- resolver/resolver.go: recreate stopCh in Start() to support restart
- logging/logging.go: save *os.File handles from getOutput so Close()
  actually closes log files; exclude os.Stdout/os.Stderr from closing
- ssl/session_tickets.go: protect started/rotateTimer access in
  scheduleRotation with mu; support Start-Stop-Start cycles
- ssl/ssl.go: cache parsed default certificate to avoid re-parsing on
  every TLS handshake for OCSP stapling
2026-06-11 17:03:17 +08:00
xfy
27e00b84a8 fix(proxy,handler,server,stream,ratelimit): fix resource leaks and functional bugs
- proxy/proxy.go: decrement connection count on dangerous path rejection
  (line 724) to prevent connection count leak
- handler/sendfile_linux.go: return *os.File from getSocketFile and let
  linuxSendfile close it, fixing EBADF from deferred close in getSocketFd
- proxy/websocket.go: return bufio.Reader from readWebSocketUpgradeResponse
  and wrap targetConn with bufferedConn to consume pre-buffered frame data,
  preventing first-frame loss
- server/pool.go: use non-blocking send after starting new worker to avoid
  deadlock when queue is full
- stream/stream.go: check stopCh on non-timeout UDP read errors to prevent
  infinite loop and shutdown deadlock
- middleware/ratelimit: replace select-based close guard with sync.Once in
  StopCleanup to prevent double-close panic
2026-06-11 16:35:10 +08:00
xfy
148f43fcb3 perf(handler): enable file info cache and fix index file cache lookup
- Router now enables FileInfoCache with 2s TTL for all static handlers
  to reduce redundant os.Stat calls.
- FileInfoCache supports negative caching: missing files are cached
  with a shorter TTL to avoid repeated stat on non-existent paths.
- Fix missing fileCache lookup for directory index files (index.html).
  Previously handleStandard/handleTryFiles skipped fileCache when
  serving index files, causing os.ReadFile on every request even
  with file_cache configured.
- Extract tryServeFromFileCache() helper to unify cache hit logic
  across file and index-file serving paths.

Verified with wrk 200 conn / 20s on static /index.html:
- Throughput: 140k -> 242k req/sec (+73%)
- alloc_space: 2.6 GB -> 4.6 MB (-99.8%)
2026-06-11 14:43:04 +08:00
xfy
1128eb644f perf(static): enable FileInfoCache by default with negative caching
Production static file serving now uses FileInfoCache by default
with a 2-second TTL in router.go, dramatically reducing os.Stat
syscalls for missing files and repeated paths.

Changes:
- Add negative cache support to FileInfoCache (caches 'not found' results)
- Introduce statWithCache() helper in StaticHandler for uniform caching
- Make FileInfoCache TTL configurable via SetTTL()
- Default cacheTTL=0 disables caching in NewStaticHandler (tests compat)
- router.go enables fileInfoCache with 2s TTL for all static handlers

Benchmark (repeated 404s):
  No cache:    ~2651 ns/op, 2225 B/op, 15 allocs/op
  With cache:  ~1505 ns/op, 1905 B/op, 12 allocs/op
  Improvement: -43% latency, -14% allocations

This addresses the dominant allocation source in v0.4.0 profile
(os.statNolog at 74.95% of allocations).
2026-06-11 14:05:56 +08:00
xfy
5e3196c37e fix: resolve race conditions in handler sendfile and lua cosocket tests 2026-06-05 12:31:39 +08:00
xfy
e44cfc7128 perf(handler): eliminate read-lock upgrade in FileInfoCache.Get with approximate LRU
FileInfoCache.Get previously acquired TWO locks on every cache hit:
1. RLock → check existence + TTL
2. Release RLock → Lock → MoveToFront (LRU update) → Unlock

The MoveToFront on every hit forced a read-to-write lock upgrade,
creating contention under concurrent reads.

Apply approximate LRU: skip MoveToFront on Get (read) path entirely.
LRU position is only updated in Set (write) path. This is the same
pattern already used by internal/cache/file_cache.go.

Result: Get fast path reduced from 2 lock acquisitions to 1 RLock.
TTL-expired entries still use double-check locking for safe removal.
2026-06-04 00:13:29 +08:00
xfy
6612819f3a chore: remove stale AGENTS.md files, rewrite root AGENTS.md 2026-06-03 23:47:29 +08:00
xfy
6f17bbad7e chore: remove trailing blank lines and clean up whitespace 2026-06-03 18:08:34 +08:00
xfy
66f608f25b refactor: remove redundant generateETag wrappers, use utils.GenerateETag directly 2026-06-03 17:51:50 +08:00
xfy
2734b04d8f refactor: remove 16.8k lines of dead code across all internal packages
- Delete unused files: tempfile subsystem, matcher variants, server/internal
- Remove 200+ unused functions across proxy, ssl, lua, http2/3, stream, variable
- Fix proxy test type errors (backgroundRefresh ctx→Request)
- Move bench/tools mock backend into internal/testutil
- Remove corresponding test functions for all deleted code
2026-06-03 16:15:43 +08:00
xfy
70d6488fc6 refactor(handler): extract path processing methods
Extract duplicate path processing logic from handleTryFiles,
handleInternalRedirect, and handleStandard into two new methods:

- stripPathPrefix(): zero-allocation path prefix stripping
- buildFilePath(): build full file path supporting alias/root modes

This reduces code duplication and makes the path handling logic
easier to maintain.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 18:14:51 +08:00
xfy
3b2360162c refactor(utils): add unified ETag generation function
Extract duplicate generateETag function from handler/static.go and
cache/file_cache.go into internal/utils/etag.go. Both functions were
identical, using strconv.AppendInt for zero-allocation ETag generation.

- Create utils.GenerateETag(modTime, size) as the unified implementation
- Update handler/static.go to call utils.GenerateETag
- Update cache/file_cache.go to call utils.GenerateETag
- Remove unused strconv import from static.go

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 18:06:13 +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
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
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
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
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
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
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
xfy
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
xfy
65cdab60f9 feat(handler): 静态文件支持 ETag 和 304 Not Modified
添加 generateETag 和 isNotModified 函数,在所有响应路径设置
ETag/Last-Modified 头,支持 If-None-Match 和 If-Modified-Since
条件请求返回 304,减少不必要的文件传输。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 13:13:31 +08:00
xfy
8f79fb6797 test(config,handler,loadbalance,proxy): 扩展单元测试覆盖率
添加以下测试:
- validate_test.go: Rewrite、NextUpstream、DefaultServer、Mode、
  ListenConflicts、HTTP2、RedirectRewrite 验证测试
- sendfile_test.go: 无效文件描述符、零长度传输、部分传输、
  带偏移量传输测试
- balancer_test.go: ConsistentHash Select/SelectExcluding、
  RandomBalancer 边界条件和 Power of Two Choices 测试
- health_test.go: MarkHealthy/MarkUnhealthy 与 SlowStartManager
  集成测试

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 18:28:28 +08:00
xfy
2bdc8f3b3b refactor(handler,middleware,server): 增强预压缩配置灵活性
- NewGzipStatic 增加 precompressedExtensions 参数,支持自定义预压缩扩展名
- SetGzipStatic 分离源文件扩展名和预压缩扩展名参数
- 更新相关测试用例

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 13:35:03 +08:00
xfy
9f7090df67 test(handler,middleware,server,ssl,proxy): 扩展测试覆盖率
- handler: 添加 sendfile 和 static 处理器测试
- middleware/security: 添加访问控制、认证、请求头、限流测试
- server: 添加池、pprof、清理、状态、升级、vhost 测试
- ssl: 添加客户端验证、OCSP、SSL 测试
- proxy: 添加代理覆盖率补充测试

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 10:42:05 +08:00
xfy
ae0bec6c3b feat(internal): 实现 internal 指令
- 新增 IsInternalRedirect 检测内部重定向请求
- static handler 支持 internal 访问限制
- proxy handler 支持 internal 访问限制
- 支持 X-Accel-Redirect 内部重定向

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 18:09:06 +08:00
xfy
2458ac1ed1 docs: 为其余模块添加标准化 godoc 注释
为剩余模块添加完整文档注释:
- app: 应用生命周期管理
- cache: 文件缓存
- config: 配置加载器
- handler: 静态文件处理和错误页面
- http2/http3: HTTP/2 和 HTTP/3 适配器
- loadbalance: 负载均衡算法和均衡器
- middleware: bodylimit、compression、rewrite、security
- mimeutil: MIME 类型检测
- netutil: URL 处理工具
- resolver: DNS 解析器
- server: 服务器升级处理
- ssl: SSL/TLS 和 OCSP
- stream: 流处理
- testutil: 测试工具
- variable: 变量池和 SSL 变量

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 10:59:53 +08:00
xfy
04f6caa40d test(handler): 添加静态文件发送处理测试
- 测试 SendFile 基本功能和错误处理
- 测试 Range 请求支持
- 测试不同文件类型和 MIME 类型

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 18:12:28 +08:00
xfy
8ed800271d test: 迁移基准测试循环到 Go 1.24 b.Loop() API
- 所有 *_bench_test.go 文件从 for i := 0; i < b.N; i++ 改为 for b.Loop()
- 部分测试文件从 for i := 0; i < N; ... 改为 for range N 或 for i := range N
- 涵盖模块: cache, handler, http2, http3, loadbalance, logging, lua,
  middleware/accesslog, middleware/bodylimit, middleware/rewrite,
  middleware/security, netutil, resolver, server, ssl, stream

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 13:50:15 +08:00
xfy
d874f97765 fix(handler): 修复 sendfile 高并发下的连接断开处理
- EPIPE/ECONNRESET 不再 fallback,直接返回错误避免响应混乱
- 正确处理 EAGAIN/EWOULDBLOCK socket 缓冲区满,等待重试
- EINTR 信号中断正确重试
- 改用 SetBodyStream 确保 HTTP 头先发送再 sendfile
- 添加重试限制(100次)防止无限循环

测试结果:0 错误/100000 并发请求

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 09:54:08 +08:00
xfy
fa55bfd497 feat(cache): 添加缓存 TTL 新鲜度验证优化
在 FileEntry 中新增 CachedAt 字段记录缓存时间,实现 TTL 窗口内的缓存新鲜度验证:
- TTL 内跳过 ModTime 验证,减少 os.Stat 调用
- TTL 过期后验证 ModTime,文件未修改时刷新 CachedAt
- 默认 TTL 设置为 5 秒

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 17:53:51 +08:00
xfy
73ef7f4916 fix(lint): 修复剩余 lint 错误
- 统一八进制权限格式为 Go 1.13+ 风格 (0o644/0o755)
- 调整 Target 结构体字段顺序优化内存对齐
- 合并相邻的全局变量声明
- 删除多余空行
- 更新 Makefile 使用 gofumpt 替代 goimports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 16:50:14 +08:00
xfy
b722cbf884 style: 统一八进制权限格式和代码格式 2026-04-13 16:40:50 +08:00
xfy
ef652cdab1 feat(static): 添加符号链接安全检查功能 2026-04-13 16:40:50 +08:00
xfy
d21e27fbac fix(lint): 修复 golangci-lint 错误 (119 -> 0 issues)
主要修复:
- errcheck: defer Close 使用 //nolint:errcheck,类型断言改为 ok 检查
- govet fieldalignment: 调整结构体字段顺序优化内存布局
- revive unused-parameter: 将未使用参数改为 _
- exhaustive: 添加缺失的 switch case 或 default
- goconst: 提取重复字符串为常量 (accessAllow, accessDeny 等)
- staticcheck SA9003: 修复空分支逻辑
- gofmt: 运行 gofmt -w 格式化
- nolintlint: 修复 nolint 注释格式

其他改进:
- 更新 .golangci.yml 配置,启用更严格的检查
- 移除未使用的代码和导入
- 简化测试辅助函数调用

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 16:15:31 +08:00
xfy
e646cc5d05 refactor(test): 提取 testutil 包统一测试辅助函数
- 新增 NewRequestCtx 和 NewRequestCtxWithHeader 辅助函数
- 简化各测试文件中 RequestCtx 创建代码
- 减少测试代码重复,提高可维护性

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 13:15:20 +08:00
xfy
e1df0ec205 refactor(utils): 提取 HTTP 错误响应辅助函数
新增 SendError 和 SendErrorWithDetail 函数,统一处理 HTTP 错误响应,减少重复代码。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 11:12:49 +08:00
xfy
bb77fa6a98 fix(lint): 修复 handler 和 http2 模块 lint 错误
- 添加 nolint:errcheck 注释到 defer Close 调用
- 修复 websocket.go 中重复的 nolint 注释格式
- 添加 staticcheck SA1019 nolint 注释到 deprecated WriteScheduler
- 移除 sendfile_linux.go 中未使用的 platformLinux 常量
- 添加文件末尾换行符

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 09:51:30 +08:00
xfy
8b382606df Merge branch 'lint-fix' - resolve sendfile.go conflict
Conflict: sendfile.go (!linux build tag) was incorrectly modified to
include linuxSendfile and getSocketFd functions which already exist
in sendfile_linux.go.

Resolution: Keep HEAD version (simple fallback returning ENOTSUP) as
Linux implementation is properly separated in sendfile_linux.go.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 09:26:48 +08:00
xfy
92ef122226 refactor(handler): 拆分 sendfile 实现为平台特定文件
- Linux 平台保留 sendfile 系统调用的零拷贝实现
- 非 Linux 平台使用普通 IO fallback
- 分离平台特定测试到独立文件

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 11:45:46 +08:00
xfy
76c2a6028c feat(handler,config): 支持 try_files 动态后缀 ($uri.<ext>)
- 实现 $uri.<ext> 模式解析(如 $uri.html, $uri.json)
- 添加扩展名安全验证:白名单字符、危险后缀黑名单
- 根路径边界处理:避免生成 "/.html" 隐藏文件名
- 增强配置文档注释,说明 nginx 兼容性和安全限制
- 完整单元测试覆盖

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 18:30:28 +08:00