53 lines
1.5 KiB
Go
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
|
|
}
|
|
}
|
|
}
|