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.
Support loading config fragments from external files via include
directive. Servers and streams are appended, variables merged with
main config priority. Includes glob expansion, nested includes
(depth limit 10), and circular include detection.
Disk cache implementation was previously removed but config structs
remained. Remove ProxyCachePathConfig, Config.CachePath field, e2e
WithCachePath helper, and docs reference.
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.
- Server.connCount and Target.conns now use atomic.AddInt64/LoadInt64
instead of non-atomic ++ and --, fixing data races under concurrency
- UDP sessions now store a reference to their target and decrement
target.conns in close(), preventing monotonically increasing counts
that would break least_conn load balancing over time
refreshAll() was a no-op — it checked which entries needed refreshing
but never called fetchOCSP. Now it:
- Stores cert/issuer pairs when registering certificates
- Actually fetches fresh OCSP responses for stale/expired entries
- Updates error counts and marks entries as failed after max retries
The previous code used defer resp.Body.Close() inside a for loop,
causing all response bodies to remain open until the function returned.
Extract the per-attempt logic into singleOCSPAttempt so each response
body is closed immediately after processing.
Previously the error was silently swallowed, causing the proxy to
fall back to default TLS settings (no custom CA, no mTLS, no SNI)
without any indication. Now the error is logged at ERROR level.
Copy the request before spawning the background goroutine. The
fasthttp.RequestCtx is recycled after the handler returns, so passing
it to a goroutine causes data corruption under high concurrency.
The caller now AcquireRequest+CopyTo before go(), and the goroutine
releases it. backgroundRefresh no longer accepts ctx directly.
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>
- Align struct fields and constants in gjson/config.go
- Add missing newline at EOF in gjson/decode.go
- Remove trailing blank line in gjson/encode.go
- Remove extra blank line in internal/lua/coroutine.go
- Use modern for range syntax in internal/lua/pool.go
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>
Add glua.OpenPackage for require support and preload gjson module
to make JSON encoding/decoding available in Lua scripts.
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>
Create LuaRouteHandler that implements fasthttp.RequestHandler interface,
allowing Lua scripts to be registered as standalone route handlers.
Handles ngx.exit/ngx.redirect as normal exits, not errors.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add route-based matching support for Lua scripts as an alternative to
phase-based execution. Scripts can now be matched by path patterns.
Fields added:
- Route: path/pattern for route matching (mutually exclusive with Phase)
- RouteType: matching type (exact, prefix, prefix_priority, regex, regex_caseless)
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>
Replace the single LState + coroutine model with an LState pool to
eliminate concurrent map read/write issues in gopher-lua. Each request
now gets a completely independent LState with its own Global table.
Key changes:
- Add LStatePool for managing pooled LState instances
- Remove shared Engine.L and coroutine-based execution
- Simplify coroutine.go: remove yield handling, use direct PCall
- Remove ngxRegisterMu lock (no longer needed with isolated LStates)
- Update config.go: add LStatePoolInitialSize/MaxSize settings
- Update tests to work with the new architecture
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Use 198.18.0.1 (IANA reserved benchmark address) instead of
127.0.0.1:9999 to ensure reliable connection failure in tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Enhance parseCIDR in utils/ipallowlist.go to support single IP addresses
(without CIDR prefix) and ensure IP is in canonical form. This matches
the functionality previously in access.go.
- Add ParseCIDR as public function supporting CIDR and single IP
- Update access.go to use utils.ParseCIDR instead of local implementation
- Remove duplicate parseCIDR function from access.go
- Update tests to use utils.ParseCIDR
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
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>
Create setupTestLogger() helper to reduce duplicate logger initialization
in test files. Replace 20+ occurrences of logging.NewAppLogger calls.
- Create testutil.go with setupTestLogger() function
- Update app_test.go to use the new helper
- Remove unused logging import from app_test.go
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace three duplicate ValidateNonNegative* functions with a single
generic implementation using Go 1.18+ generics.
- Add SignedInteger type constraint for generic support
- Create ValidateNonNegative[T SignedInteger] as unified function
- Depprecate ValidateNonNegativeInt64 and ValidateNonNegativeDuration
- Both deprecated functions now call the generic version
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Extract duplicate b2s/s2b functions from proxy/utils.go into
internal/utils/bytes.go. These are zero-allocation unsafe conversions
for byte slice <-> string conversion.
- Create utils.B2s() and utils.S2b() as unified implementations
- Update proxy/utils.go to call utils functions
- Add safety documentation about shared memory warning
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
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 `set_forwarded_host` and `set_forwarded_proto` options to control
whether the proxy automatically sets these headers. This fixes issues
with upstream servers that validate X-Forwarded-Host against known hosts.
Changes:
- Add SetForwardedHost/SetForwardedProto fields to ProxyHeaders struct
- Modify SetForwardedHeaders and WriteForwardedHeaders function signatures
- Update modifyRequestHeaders to read config and pass control parameters
- Update WebSocket call chain to support new config
- Add unit tests for new functionality
- Update default config generation (-g) to include new options
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>