fiber/middleware/timeout/timeout.go

57 lines
1.7 KiB
Go

package timeout
import (
"context"
"errors"
"time"
"github.com/gofiber/fiber/v3"
)
// New enforces a timeout for each incoming request. If the timeout expires or
// any of the specified errors occur, fiber.ErrRequestTimeout is returned.
func New(h fiber.Handler, timeout time.Duration, tErrs ...error) fiber.Handler {
return func(ctx fiber.Ctx) error {
// If timeout <= 0, skip context.WithTimeout and run the handler as-is.
if timeout <= 0 {
return runHandler(ctx, h, tErrs)
}
// Create a context with the specified timeout; any operation exceeding
// this deadline will be canceled automatically.
timeoutContext, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
// Run the handler and check for relevant errors.
err := runHandler(ctx, h, tErrs)
// If the context actually timed out, return a timeout error.
if errors.Is(timeoutContext.Err(), context.DeadlineExceeded) {
return fiber.ErrRequestTimeout
}
return err
}
}
// runHandler executes the handler and returns fiber.ErrRequestTimeout if it
// sees a deadline exceeded error or one of the custom "timeout-like" errors.
func runHandler(c fiber.Ctx, h fiber.Handler, tErrs []error) error {
// Execute the wrapped handler synchronously.
err := h(c)
// If the context has timed out, return a request timeout error.
if err != nil && (errors.Is(err, context.DeadlineExceeded) || isCustomError(err, tErrs)) {
return fiber.ErrRequestTimeout
}
return err
}
// isCustomError checks whether err matches any error in errList using errors.Is.
func isCustomError(err error, errList []error) bool {
for _, e := range errList {
if errors.Is(err, e) {
return true
}
}
return false
}