feat(deps)!: migrate from fiber v2 to fiber v3

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.
This commit is contained in:
2026-06-21 13:15:14 +01:00
parent 877325a633
commit 55fc2ae1de
31 changed files with 265 additions and 240 deletions
+14 -15
View File
@@ -9,7 +9,7 @@ import (
"time"
"github.com/goccy/go-json"
fiber "github.com/gofiber/fiber/v2"
fiber "github.com/gofiber/fiber/v3"
"github.com/gofrs/flock"
libpack_cache "github.com/lukaszraczylo/graphql-monitoring-proxy/cache"
libpack_config "github.com/lukaszraczylo/graphql-monitoring-proxy/config"
@@ -20,7 +20,7 @@ import (
var bannedUsersIDs sync.Map // key: userID string, value: reason string
// authMiddleware provides API key authentication for admin endpoints
func authMiddleware(c *fiber.Ctx) error {
func authMiddleware(c fiber.Ctx) error {
apiKey := c.Get("X-API-Key")
// Get expected key from config (try GMP_ prefix first, then fallback)
@@ -76,8 +76,7 @@ func enableApi(ctx context.Context) error {
}
apiserver := fiber.New(fiber.Config{
DisableStartupMessage: true,
AppName: fmt.Sprintf("GraphQL Monitoring Proxy - %s v%s", libpack_config.PKG_NAME, libpack_config.PKG_VERSION),
AppName: fmt.Sprintf("GraphQL Monitoring Proxy - %s v%s", libpack_config.PKG_NAME, libpack_config.PKG_VERSION),
})
api := apiserver.Group("/api")
@@ -97,7 +96,7 @@ func enableApi(ctx context.Context) error {
// Start server in a goroutine and handle shutdown
errCh := make(chan error, 1)
go func() {
if err := apiserver.Listen(fmt.Sprintf(":%d", cfg.Server.ApiPort)); err != nil {
if err := apiserver.Listen(fmt.Sprintf(":%d", cfg.Server.ApiPort), fiber.ListenConfig{DisableStartupMessage: true}); err != nil {
errCh <- err
}
}()
@@ -135,7 +134,7 @@ func periodicallyReloadBannedUsers(ctx context.Context) {
}
}
func checkIfUserIsBanned(c *fiber.Ctx, userID string) bool {
func checkIfUserIsBanned(c fiber.Ctx, userID string) bool {
_, found := bannedUsersIDs.Load(userID)
cfg.Logger.Debug(&libpack_logger.LogMessage{
@@ -158,7 +157,7 @@ func checkIfUserIsBanned(c *fiber.Ctx, userID string) bool {
return found
}
func apiClearCache(c *fiber.Ctx) error {
func apiClearCache(c fiber.Ctx) error {
cfg.Logger.Debug(&libpack_logger.LogMessage{
Message: "Clearing cache via API",
})
@@ -169,12 +168,12 @@ func apiClearCache(c *fiber.Ctx) error {
return c.SendString("OK: cache cleared")
}
func apiCacheStats(c *fiber.Ctx) error {
func apiCacheStats(c fiber.Ctx) error {
return c.JSON(libpack_cache.GetCacheStats())
}
// apiCircuitBreakerHealth returns the health status of the circuit breaker
func apiCircuitBreakerHealth(c *fiber.Ctx) error {
func apiCircuitBreakerHealth(c fiber.Ctx) error {
if cb == nil {
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
"status": "disabled",
@@ -232,9 +231,9 @@ type apiBanUserRequest struct {
Reason string `json:"reason"`
}
func apiBanUser(c *fiber.Ctx) error {
func apiBanUser(c fiber.Ctx) error {
var req apiBanUserRequest
if err := c.BodyParser(&req); err != nil {
if err := c.Bind().Body(&req); err != nil {
cfg.Logger.Error(&libpack_logger.LogMessage{
Message: "Can't parse the ban user request",
Pairs: map[string]any{"error": err.Error()},
@@ -260,9 +259,9 @@ func apiBanUser(c *fiber.Ctx) error {
return c.SendString("OK: user banned")
}
func apiUnbanUser(c *fiber.Ctx) error {
func apiUnbanUser(c fiber.Ctx) error {
var req apiBanUserRequest
if err := c.BodyParser(&req); err != nil {
if err := c.Bind().Body(&req); err != nil {
cfg.Logger.Error(&libpack_logger.LogMessage{
Message: "Can't parse the unban user request",
Pairs: map[string]any{"error": err.Error()},
@@ -463,7 +462,7 @@ func lockFileRead(fileLock *flock.Flock) error {
}
// apiBackendHealth returns the health status of the GraphQL backend
func apiBackendHealth(c *fiber.Ctx) error {
func apiBackendHealth(c fiber.Ctx) error {
healthMgr := GetBackendHealthManager()
if healthMgr == nil {
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
@@ -499,7 +498,7 @@ func apiBackendHealth(c *fiber.Ctx) error {
}
// apiConnectionPoolHealth returns the health status of the connection pool
func apiConnectionPoolHealth(c *fiber.Ctx) error {
func apiConnectionPoolHealth(c fiber.Ctx) error {
poolMgr := GetConnectionPoolManager()
if poolMgr == nil {
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{