Echo: Timeout middleware

Created on 13 Nov 2020  路  4Comments  路  Source: labstack/echo

Hi! I want to create timeout middleware

Checklist

  • [x] Dependencies installed
  • [x] No typos
  • [x] Searched existing issues and docs

Expected behaviour

middleware.Timeout()

Actual behaviour

middleware package do not produce timeout middleware

Working code to debug

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

Version/commit

latest

All 4 comments

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 :)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

spielstein picture spielstein  路  3Comments

neutronstein picture neutronstein  路  3Comments

younisshah picture younisshah  路  4Comments

danieldaeschle picture danieldaeschle  路  3Comments

toorop picture toorop  路  4Comments