Batch 1 - Crash/data corruption:
- Add nil/empty config guards in server and app
- Protect against fasthttp buffer mutation in logging
- Ensure mimeutil falls back before caching empty MIME
- Make proxy health checker and resolver tolerate nil cfg
- Prevent Lua EMERG/ALERT/CRIT log levels from killing process
Batch 2 - Concurrency/race conditions:
- Add focused race tests for compression pool, ssl/ocsp,
server proxies slice, Lua timer cancel, and TCP socket connect
- Fix Lua timer active race and socket ConnectAsync race
Batch 3 - Resource leaks/functional damage:
- Track and stop rate limiter cleanup goroutines on shutdown
- Add tests for proxy connection count leaks, server pool deadlock,
Linux sendfile integrity, WebSocket frame data loss,
stream UDP stop deadlock, and upstream name mismatch
- Fix stream ListenTCP/upstream lookup and UDP shutdown
Batch 4 - High severity technical debt:
- Add nil guard in security headers middleware
- Validate sliding window divisor and add tests
- Protect static handler fields with existing RWMutex
- Avoid mutating live HostClient.Addr on DNS updates
- Fix slow start Start/Stop and resolver restart
- Initialize variable fallback context maps
- Add tests for request_id/time_local and logging file handle close
Also:
- Relax integration variable performance threshold under race detector
- Update benchmark plan docs for new stream ListenTCP signature
Verification:
- make test: pass
- go test -race -count=1 ./internal/...: pass
- make lint: 0 issues
- make build: success
- 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
- server: reject Start() when config is nil to prevent panic
- app_common: guard empty Servers slice in initHTTP2/3 and logServerAddresses
- proxy/health: handle nil HealthCheckConfig with defaults
- resolver: handle nil ResolverConfig by returning noopResolver
- middleware/headers: skip UpdateConfig when cfg is nil
- middleware/sliding_window: enforce minimum window duration of 1s
- lua/api_log: map EMERG/ALERT/CRIT to Error() instead of Fatal()
to prevent Lua scripts from killing the entire server process
- errcheck: check type assertions from sync.Pool.Get() in
loadbalance/balancer.go and matcher/radix.go
- errcheck: check type assertion from list.Element.Value in
resolver/resolver.go evictLRULocked
- revive: add doc comment for exported ReleaseMatchResult function
DNS resolver LRU cache used []string slice for access ordering:
- moveToFrontLocked: O(n) linear scan + slice重组 per cache update
- storeCache held exclusive write lock during entire O(n) operation,
blocking all concurrent reads
Replace with container/list + map[string]*list.Element:
- storeCache: O(1) MoveToFront via list.Element pointer
- evictLRULocked: O(1) Back() + Remove()
- DeleteCacheEntry: O(1) instead of O(n) scan
This matches the LRU pattern already used by FileCache and
FileInfoCache. Write lock duration reduced from O(n) to O(1),
significantly reducing read contention under mixed workloads.
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>
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>