Two defects in the bidirectional proxy:
1. Goroutine/socket leak on client disconnect. proxyClientToBackend and
proxyBackendToClient each block on their own Read; when the client drops,
the client-direction goroutine returns but the backend-direction goroutine
stays blocked on backend.ReadMessage, so wg.Wait() never returns and the
goroutine plus backend socket leak until the backend independently closes
or sends. Each direction now closes the opposite connection on exit,
unblocking the peer.
2. Backend->client messages were unbounded. client.SetReadLimit only bound
what the client could send; a large or misbehaving backend could push
arbitrarily large frames through. backend.SetReadLimit now bounds it.
Tests: TestWebSocketProxy_TeardownOnClientDisconnect (backend must be closed
after client disconnect), TestWebSocketProxy_BackendReadLimit (connection
torn down when backend exceeds the limit).
BREAKING CHANGE: upgrades the HTTP framework from gofiber/fiber/v2 to gofiber/fiber/v3, and gofiber/websocket/v2 to gofiber/contrib/v3/websocket.
- Handlers now take fiber.Ctx (interface) instead of *fiber.Ctx. - DisableStartupMessage moved from fiber.Config to fiber.ListenConfig on the proxy/api/monitoring Listen calls. - cors AllowOrigins is now []string; c.BodyParser -> c.Bind().Body(). - app.Test takes fiber.TestConfig instead of an int timeout (tests updated). - WebSocket swapped to contrib/v3/websocket (same Conn/New/Config/IsCloseError/IsWebSocketUpgrade API; Conn.Query preserved). - Adopts fasthttp v1.71.0 (pulled by fiber v3), resolving the v1.71 Host-header enforcement that broke the prior pinned-v1.69 workaround.
fix(connpool): the background keep-alive goroutine no longer reads the mutable global cfg (captures HostGraphQL/HealthcheckGraphQL at pool creation). Under v3 timing it raced with parseConfig/test cfg reassignment; go test -race ./... is now clean (0 data races).
test: make the circuit-breaker cache-fallback and request-coalescing integration tests deterministic (cache TTL outlives the retry loop; coalescing asserts against a measured single-request baseline instead of a hard-coded count); drop the flaky timing-ratio assertion in TestTimingAttackResistance (constant-time guarantee comes from subtle.ConstantTimeCompare, not wall-clock timing).
Verified: go build, go vet, golangci-lint (0 issues), go test -race ./... (all packages pass, 0 races), govulncheck (no vulnerabilities). App + dashboard + WebSocket verified live under v3 in single-node and cluster modes.