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 } } }