Hi! I want to create timeout middleware
middleware.Timeout()
middleware package do not produce timeout middleware
I try something like this, yes i know it's wrong way:)
func timeout(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
ctx, _ := context.WithTimeout(context.Background(), time.Second*2)
go func() {
select {
case <-ctx.Done():
_ = c.JSON(http.StatusGatewayTimeout, ctx.Err())
}
}()
c.SetRequest(c.Request().WithContext(ctx))
return next(c)
}
}
latest
I migrate my app from gin to echo, because gin has timeout middleware, but it panics.
Hi, I hope this example helps you on your Timeout middleware journey
package main
import (
"context"
"net/http"
"time"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/labstack/gommon/log"
)
func main() {
// Echo instance
e := echo.New()
e.Logger.SetLevel(log.INFO)
// Middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// Route
e.GET("/sleep", func(c echo.Context) (err error) {
// This select make the trick of finish this request when the middleware timeouts
select {
case <-time.After(5 * time.Second):
c.Logger().Info("Done")
return c.JSON(http.StatusOK, "Done")
case <-c.Request().Context().Done():
c.Logger().Info("Timeout")
return nil
}
}, timeoutMiddleware)
// Start server
e.Logger.Fatal(e.Start(":8080"))
}
func timeoutMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Just to play easily with the middleware using a query parameter
timeout := 2 * time.Second
if t, err := time.ParseDuration(c.QueryParam("timeout")); err == nil {
timeout = t
}
// This is the context that controls the timeout. Its parent is the original
// http.Request context
ctx, cancel := context.WithTimeout(c.Request().Context(), timeout)
defer cancel() // releases resources if next(c) completes before timeout elapses
// A channel and a goroutine to run next(c) and know if its ends
done := make(chan error, 1)
go func() {
// This goroutine will not stop even this middleware timeouts,
// unless someone in the next(c) call chain handle ctx.Done() properly
c.SetRequest(c.Request().Clone(ctx))
done <- next(c)
}()
// The real timeout logic
select {
case <-ctx.Done():
return c.JSON(http.StatusGatewayTimeout, ctx.Err())
case err := <-done:
return err
}
}
}
Here are some curl that you can use to play with the example
# Timeout
curl -i --request GET --url 'http://127.0.0.1:8080/sleep'
# No timeout
curl -i --request GET --url 'http://127.0.0.1:8080/sleep?timeout=6s'
Please, when you have your Timeout middleware woking don't forget to submit a PR whit it 馃槈
Hi @jeyldii, Was my comment helpfull?
Hi @jeyldii, Was my comment helpfull?
Hey! Thank you for your answer! I make something like this:
func Timeout(timeout time.Duration) func(next echo.HandlerFunc) echo.HandlerFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
go func() {
<-ctx.Done()
cancel()
}()
c.SetRequest(c.Request().WithContext(ctx))
return next(c)
}
}
}
And i manage context.Deadline in my handlers. I think your decision better. I think i can make a PR with middleware soon. Sorry, for late answer :)