144 Commits

Author SHA1 Message Date
xfy
f605ef3b44 feat(server): add /healthz and /readyz endpoints for Kubernetes probes
Add lightweight health check endpoints for container orchestration
(Kubernetes liveness/readiness probes, load balancer health checks).

New config under monitoring:
- healthz.enabled (default true), healthz.path (default /healthz)
- readyz.enabled (default true), readyz.path (default /readyz)

/healthz (liveness):
- Always returns 200 {"status":"ok"} if process is alive
- No dependency checks, minimal overhead

/readyz (readiness):
- Returns 200 {"status":"ready"} when server is running
- Returns 503 {"status":"not ready","reasons":[...]} when not ready
- Static-only servers (no proxies) always return 200

Registration:
- Registered alongside status/pprof endpoints
- Available in single mode (LocationEngine) and multi-server mode (Router)
- No IP allowlist required (K8s probes come from localhost)
- 6 unit tests covering all response scenarios
2026-06-11 23:41:45 +08:00
xfy
6c538a1a56 feat(server,proxy): integrate Request-ID into middleware chain and proxy forwarding
- Register requestid.New() as first middleware in buildMiddlewareChain
  (before AccessLog) so the ID is available for logging
- Add SetRequestIDHeader() in proxy/headers.go to propagate X-Request-ID
  to upstream via proxy forwarding
- Call SetRequestIDHeader in header_modifier.go after SetForwardedHeaders
- Import requestid package in middleware_builder and proxy/headers
2026-06-11 23:41:30 +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
fe0dee4da3 fix(compression,ssl,server,lua): resolve data races and concurrency bugs
- compression: move sync.Pool.New initialization into constructors to
  eliminate lazy-init race in Get()
- ssl/ocsp: copy response fields under RLock before releasing, preventing
  race with concurrent writers in refreshAll
- server: change proxiesMu from sync.Mutex to sync.RWMutex; protect
  getProxyCacheStats and purge handlers with RLock to prevent races
  with proxy registration
- lua/api_timer: fix double-decrement race in Cancel vs executeTimer
  by using timer.Stop() result to determine who decrements active
- lua/api_socket_tcp: fix nil pointer race in ConnectAsync by checking
  currentOp under lock before Connect returns
2026-06-11 16:30:11 +08:00
xfy
e733273139 fix(server,app,proxy,resolver,middleware,lua): add nil guards and safe defaults
- 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
2026-06-11 16:23:04 +08:00
xfy
dea5e28f5f test(server): fix data race in monitoring endpoint tests
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.
2026-06-11 15:23:46 +08:00
xfy
e8fbbf368c fix(config,server): merge defaults on Load and fix monitoring registration
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: /.
2026-06-11 15:08:57 +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
58e095a35b feat(pprof): add /debug/pprof/allocs endpoint for allocation profiling
- 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.
2026-06-11 13:47:47 +08:00
xfy
93c0c151d0 fix(lua): wait for SchedulerLoop exit before closing LState; lock cleanupResources 2026-06-05 13:48:04 +08:00
xfy
4789265ca8 fix: add synchronization for concurrent access in server/app/http3/stream 2026-06-05 12:31:41 +08:00
xfy
f73a761632 fix(server): protect accessLogMiddleware and accessControl from concurrent writes 2026-06-05 11:49:19 +08:00
xfy
2be6b67d0b fix(server): release MatchResult back to pool after use
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.
2026-06-04 11:14:32 +08:00
xfy
1eeab88c98 perf(server): pass ctx.Path() directly to Match, eliminate string alloc
Removes the string(ctx.Path()) conversion that caused one heap
allocation per request in the routing hot path.
2026-06-04 11:06:00 +08:00
xfy
9ae7a2b8ef test(server): 添加服务器模块覆盖测试(覆盖率 78.6% → 83.3%)
新建 internal/server/coverage_test.go,覆盖:

GetTLSConfig 测试(原 66.7% → 100%):
- 完整 TLS 配置生成
- HSTS 头部设置
- 自动 HTTP→HTTPS 重定向

registerLuaRoutesWithLocationEngine 测试(原 12.5% → 87.5%):
- Lua 路由注册到 location engine
- 多路由注册
- 无 Lua 路由时的处理

注:start* 系列函数(startSingleMode、startMultiServerMode、startServer)
由于涉及真实网络监听,更适合由 integration/e2e 测试覆盖。
2026-06-04 08:33:39 +08:00
xfy
6612819f3a chore: remove stale AGENTS.md files, rewrite root AGENTS.md 2026-06-03 23:47:29 +08:00
xfy
29752f62bd fix: resolve golangci-lint issues across multiple packages
- stream: fix atomic.Int64 usage in tests and benchmarks
- server: fix errcheck, goconst ("tcp" -> constant), and govet shadow
- app: add missing ServerModeAuto case in requiresFullRestart
- lua: fix nolintlint unused directive warnings
- proxy: use `any` instead of `interface{}`
2026-06-03 18:17:07 +08:00
xfy
6f17bbad7e chore: remove trailing blank lines and clean up whitespace 2026-06-03 18:08:34 +08:00
xfy
5ee83f6a69 refactor: extract common functions from server startup modes 2026-06-03 18:03:23 +08:00
xfy
c1796dadc5 refactor: eliminate redundant switch blocks in router.go LocationEngine functions 2026-06-03 17:54:51 +08:00
xfy
041bc97578 refactor: remove unused code identified by staticcheck 2026-06-03 17:46:58 +08:00
xfy
1a6b5f9166 Merge origin/master into master 2026-06-03 16:36:23 +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
5dec128510
Merge pull request #3 from xfy911/improve-comments
docs: add comprehensive documentation comments
2026-06-03 15:41:36 +08:00
xfy911
05f434d5e5 docs(server): add package comments for server module
- Add package documentation for router, internal, lifecycle, and middleware_builder files
- Include author attribution (xfy)
2026-06-03 15:28:53 +08:00
xfy
8ae4add922 fix: address code review feedback
- 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
2026-06-03 14:29:30 +08:00
xfy
4678cb5483 refactor: use testutil helpers in server tests 2026-06-03 14:19:22 +08:00
xfy
684122dbf7 refactor: extract registerRoute helper to reduce repetition 2026-06-03 13:55:18 +08:00
xfy
728a9f454b fix(server,app,config): address code review findings
- 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
2026-06-03 13:16:05 +08:00
xfy
556d40ceb0 fix(matcher,server): use ConflictError in AddNamed and add tests
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.
2026-06-03 11:47:06 +08:00
xfy
f58f194752 fix(server): serialize listener creation in multi-server mode
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/::).
2026-06-03 11:44:14 +08:00
xfy
f3f78b24a8 feat(server,app): implement proper config hot reload via SIGHUP
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.
2026-06-03 11:42:45 +08:00
xfy
38bb743781 fix(server): handle LocationEngine registration errors properly
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.
2026-06-03 10:12:09 +08:00
xfy
ac66ea5534 fix(server): use atomic.Bool for Server.running to eliminate data race
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.
2026-06-03 01:19:23 +08:00
xfy
b6018ff9c9 test: add t.Parallel() to enable parallel test execution
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>
2026-05-09 17:57:23 +08:00
xfy
8643a0aff9 refactor: improve error handling and fix linter warnings
- 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>
2026-05-09 17:32:06 +08:00
xfy
c157be1ce5 refactor(cache): remove unused disk/tiered cache and add helper functions
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>
2026-05-09 17:26:19 +08:00
xfy
25d93c25fa refactor: remove unused code and fix formatting
- 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>
2026-05-09 16:58:45 +08:00
xfy
986ebdf207 fix(lua): enable file watch by default for hot reload
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>
2026-05-09 13:45:55 +08:00
xfy
8f3c304837 fix(lua): register Lua routes in multi-server mode
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>
2026-05-09 13:41:26 +08:00
xfy
a5b5a085cc feat(lua): add Lua route registration in server startup
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>
2026-05-09 11:44:39 +08:00
xfy
669f9c975b feat(lua): add registerLuaRoutesWithLocationEngine for Lua route handling
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>
2026-05-09 11:42:06 +08:00
xfy
dff88449d5 feat(lua): skip routed scripts in middleware builder
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>
2026-05-09 11:38:40 +08:00
xfy
f3f92c7922 feat(lua): add LState pool configuration and fix coroutine error handling
- 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>
2026-05-09 11:13:23 +08:00
xfy
5d9ef8e611 refactor(server): extract static handler configuration
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>
2026-05-08 18:10:52 +08:00
xfy
8c96c4384f fix(status): use version package instead of hardcoded version
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>
2026-05-07 17:46:32 +08:00
xfy
9c46c8bab8 fix(server): register status and pprof handlers in multi-server mode
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>
2026-05-07 17:44:57 +08:00
xfy
03c27c8d95 fix(static): add missing AutoIndex and Internal settings in registerStaticHandlers
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>
2026-05-07 16:32:41 +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
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