veza/veza-backend-api/internal/middleware/timeout.go
2025-12-12 21:34:34 -05:00

53 lines
1.5 KiB
Go

package middleware
import (
"context"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
// Timeout middleware wraps the context with a deadline
// MOD-P1-007: Fixed goroutine leak by ensuring cleanup on timeout
func Timeout(timeout time.Duration) gin.HandlerFunc {
return func(c *gin.Context) {
// Create a context with timeout
ctx, cancel := context.WithTimeout(c.Request.Context(), timeout)
defer cancel() // Always cancel to free resources
// Replace the request context
c.Request = c.Request.WithContext(ctx)
// Create a channel to signal completion of the handler
done := make(chan struct{})
// MOD-P1-007: Launch handler in goroutine with proper cleanup
go func() {
defer func() {
// Ensure channel is closed even if handler panics
close(done)
}()
c.Next()
}()
select {
case <-done:
// Handler completed within timeout
return
case <-ctx.Done():
// Timeout exceeded
// MOD-P1-007: Abort the request and ensure handler chain stops
// Note: We abort immediately without waiting for goroutine to avoid race conditions.
// The context cancellation (via defer cancel()) will signal the handler to stop.
// If handler tries to write after timeout, it's the handler's responsibility to check context.
c.AbortWithStatusJSON(http.StatusGatewayTimeout, gin.H{
"error": "Request Timeout",
"message": "The request took too long to process.",
})
// Return immediately - context cancellation will stop the handler goroutine
// The defer cancel() ensures resources are cleaned up
return
}
}
}