mirror of
https://github.com/labstack/echo.git
synced 2024-12-28 21:08:39 +02:00
94 lines
2.4 KiB
Go
94 lines
2.4 KiB
Go
// +build go1.13
|
|
|
|
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/labstack/echo/v4"
|
|
"time"
|
|
)
|
|
|
|
type (
|
|
// TimeoutConfig defines the config for Timeout middleware.
|
|
TimeoutConfig struct {
|
|
// Skipper defines a function to skip middleware.
|
|
Skipper Skipper
|
|
// ErrorHandler defines a function which is executed for a timeout
|
|
// It can be used to define a custom timeout error
|
|
ErrorHandler TimeoutErrorHandlerWithContext
|
|
// Timeout configures a timeout for the middleware, defaults to 0 for no timeout
|
|
Timeout time.Duration
|
|
}
|
|
|
|
// TimeoutErrorHandlerWithContext is an error handler that is used with the timeout middleware so we can
|
|
// handle the error as we see fit
|
|
TimeoutErrorHandlerWithContext func(error, echo.Context) error
|
|
)
|
|
|
|
var (
|
|
// DefaultTimeoutConfig is the default Timeout middleware config.
|
|
DefaultTimeoutConfig = TimeoutConfig{
|
|
Skipper: DefaultSkipper,
|
|
Timeout: 0,
|
|
ErrorHandler: nil,
|
|
}
|
|
)
|
|
|
|
// Timeout returns a middleware which recovers from panics anywhere in the chain
|
|
// and handles the control to the centralized HTTPErrorHandler.
|
|
func Timeout() echo.MiddlewareFunc {
|
|
return TimeoutWithConfig(DefaultTimeoutConfig)
|
|
}
|
|
|
|
// TimeoutWithConfig returns a Timeout middleware with config.
|
|
// See: `Timeout()`.
|
|
func TimeoutWithConfig(config TimeoutConfig) echo.MiddlewareFunc {
|
|
// Defaults
|
|
if config.Skipper == nil {
|
|
config.Skipper = DefaultTimeoutConfig.Skipper
|
|
}
|
|
|
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
if config.Skipper(c) || config.Timeout == 0 {
|
|
return next(c)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(c.Request().Context(), config.Timeout)
|
|
defer cancel()
|
|
|
|
// this does a deep clone of the context, wondering if there is a better way to do this?
|
|
c.SetRequest(c.Request().Clone(ctx))
|
|
|
|
done := make(chan error, 1)
|
|
go func() {
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
err, ok := r.(error)
|
|
if !ok {
|
|
err = fmt.Errorf("panic recovered in timeout middleware: %v", r)
|
|
}
|
|
c.Logger().Error(err)
|
|
done <- err
|
|
}
|
|
}()
|
|
|
|
// This goroutine will keep running even if this middleware times out and
|
|
// will be stopped when ctx.Done() is called down the next(c) call chain
|
|
done <- next(c)
|
|
}()
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
if config.ErrorHandler != nil {
|
|
return config.ErrorHandler(ctx.Err(), c)
|
|
}
|
|
return ctx.Err()
|
|
case err := <-done:
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|