The WebSocket route skipped the ban and rate-limit checks that the HTTP
path enforced, and returned errors set no custom status, so circuit-open
became HTTP 500. The route now runs the same ban and rate-limit checks
before the upgrade, and a custom error handler maps errors to the
correct status, including 503 for circuit-open. The API and admin
listeners honour the BIND_ADDRESS option.
The handler returned while the ping goroutine could still write to
a connection that gofiber had recycled into its pool. This caused
a nil dereference panic or a ping on an unrelated socket.
Join the ping goroutine through a done channel before the handler
returns. Compute effective keepalive values once in the
constructor, clamp pongTimeout to at least pingInterval with one
warning, and pass the effective values to both proxy directions.
A negative interval or timeout disables keepalive. New test drives
the real HandleWebSocket wiring.
PingInterval/PongTimeout were configured but unused, so a half-open peer
(TCP open but silent) left both proxy goroutines and the handler blocked
in ReadMessage forever. Wire both directions: read deadline refreshed by
real traffic or pong, plus a sidecar ping goroutine (WriteControl is
safe to call concurrently with WriteMessage). Add regression test
TestWebSocketProxy_ReapsSilentBackend_Keepalive proving a silent backend
is reaped via the read deadline instead of hanging.