- 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
Replace concurrent polling of srv.GetListeners() with pre-created
net.Listener injected via srv.SetListeners(). The previous approach
raced with the Start() goroutine writing to s.listeners and calling
net.Listen().
Also remove unused waitForServerRunning call.
Two related fixes that must land together:
1. config.Load() now starts from DefaultConfig() before unmarshaling
YAML. This ensures missing top-level fields (Performance,
Monitoring, Resolver) use their documented defaults instead of
zero values. Most importantly, file_cache is no longer silently
disabled when users omit the performance: section.
2. startSingleMode() now checks Monitoring.Status.Enabled instead of
Path/Allow to decide whether to register the status endpoint.
Without this change, fix#1 would have caused a regression where
the status handler is registered even when monitoring is disabled,
because DefaultConfig() sets Path and Allow defaults.
Also replace remaining log.Printf in status.go and lua/api_timer.go
with zerolog to follow project logging conventions.
Added tests:
- config/load_test.go: verifies defaults are applied, explicit values
override defaults, and monitoring stays disabled by default.
- server/monitoring_registration_test.go: verifies /_status is only
registered when enabled and remains reachable with static handler
on path: /.
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).
- Add writeAllocsProfile() helper in pprof_impl.go
- Register /allocs route in PprofHandler.ServeHTTP
- Add handleAllocs() method with proper streaming response
- Update index page to list the new allocs profile link
This aligns lolly's pprof endpoints with net/http/pprof and enables
allocation hotspot analysis during performance benchmarking.
Add matcher.ReleaseMatchResult(result) in the base handler to prevent
sync.Pool object leak. Every Match() call acquires from pool but the
caller never returned objects, causing unbounded pool growth.
- proxyDebugLog: move Enabled() guard to call sites to avoid allocations
- proxyDebugLog: add default case for unsupported types
- static routes: remove unintended regex support to match original behavior
- Fix FD leak in DupListener: close *os.File after net.FileListener
- Add cleanup of partially-duped listeners on DupListener failure
- Make reload timeout configurable via shutdown.reload_timeout
- Handle filepath.Abs errors in processIncludes instead of ignoring
- Use net.ParseIP in isAnyAddr for robust IPv6 support
Make AddNamed return *ConflictError for consistency with other Add*
methods so handleRegistrationError treats named location conflicts as
warnings instead of fatal errors. Add tests for handleRegistrationError
covering both conflict and fatal error paths.
Remove VHost fallback during graceful upgrade. Serialize listener
creation before parallel router/middleware setup to prevent concurrent
inherited listener consumption. Fix tcpAddrMatch to match when either
side is any-addr (0.0.0.0/::).
createListener now checks pre-set s.listeners (Path 2) for hot reload,
not just upgradeManager.IsChild() (Path 1). Add DupListener to dup FDs
so old/new servers own independent listeners. Reload rebuilds HTTP/2
and HTTP/3. Add matchInheritedListener with TCP any-addr matching.
Add requiresFullRestart with VHost server count detection.
Add typed ConflictError for path conflicts, change register functions
to return errors, handle conflicts as warnings and fatal errors as
startup failures. Remove all 20 instances of ignored Add* return values.
Server.running was a plain bool accessed from multiple goroutines
(start/stop/signal handlers). Convert to atomic.Bool with
Store/Load to make all accesses safe for concurrent use.
Updates all test files to use the new atomic API.
Add t.Parallel() to 110 test functions across 3 test files:
- internal/loadbalance/balancer_test.go (42 tests)
- internal/config/validate_test.go (21 tests)
- internal/server/status_test.go (47 tests)
This reduces total test time from ~3 minutes to ~34 seconds (5.4x faster).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add nolint comments for type assertion errcheck in gjson/encode.go
(switch case guarantees type safety)
- Handle fasthttp.Serve errors in benchmark mock backends
- Rename error variables to avoid shadowing in server.go
- Use underscore for unused loop variables
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Remove unused disk cache, tiered cache, purge, and config loader code.
Add HashPathWithMethod and MatchPattern helpers for future cache purge API.
Update test to use new mock backend API with ResponseBody field.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Remove unused benchmark/tools package
- Make ValidAlgorithms private (validAlgorithms) in loadbalance
- Remove dead code (_ = result) in lua/api_socket_tcp.go
- Fix code formatting with goimports
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
EnableFileWatch was false by default (Go bool zero value) when
global_settings was not configured. Now defaults to true to enable
Lua script hot reload without server restart.
Also fix indentation in init.go default value settings.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add registerLuaRoutes method for router-based route registration
and call it in startMultiServerMode to fix Lua routes not working
when multiple servers are configured.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add call to registerLuaRoutesWithLocationEngine between proxy and static
route registration, ensuring correct routing order: proxy -> lua -> static.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add two new functions to router.go:
- registerLuaRoutesWithLocationEngine: registers Lua scripts with Route
config to LocationEngine with support for exact/prefix/regex matching
- wrapRoutedHandler: wraps route handlers with basic middleware chain
(accesslog + errorintercept)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Scripts with Route config are handled by LocationEngine, so skip them
in buildLuaMiddlewares to avoid duplicate processing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add LStatePoolInitialSize and LStatePoolMaxSize config fields
- Set pool defaults to 100 initial / 1000 max (matching MaxConcurrentCoroutines)
- Fix middleware to return 500 on coroutine init failure instead of continuing
- Pass pool config from server init to Lua engine with zero-value fallbacks
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Extract duplicate static handler configuration logic from
registerStaticHandlersWithLocationEngine and registerStaticHandlers
into a new configureStaticHandler method.
- Create configureStaticHandler() to handle alias, cache, gzip,
symlink, internal, expires, and autoIndex configuration
- Both registration functions now call the shared configuration method
- Registration logic remains separate (LocationEngine vs Router)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace hardcoded "1.0.0" with version.Version to show the actual
build version injected via -ldflags.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
In startMultiServerMode, status and pprof handlers were not registered,
causing /_status and /debug/pprof endpoints to return 404. Now these
handlers are registered on the server with default: true, consistent
with startVHostMode behavior. Also fixed cache API registration to
use default server instead of first server.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The registerStaticHandlers function (used in multi-server mode) was missing
AutoIndex and Internal configuration that registerStaticHandlersWithLocationEngine
already had. This caused auto_index to not work in multi-server mode.
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>
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>