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>
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>
- 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>
- 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>
- 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>
- 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>
- 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>
- 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>
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>
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>
- 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>
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>
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>
- 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>
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>
- 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>
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>
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>
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>