Skip to content

Timeout middleware should propagate context.Context on echo.Context http Request #1909

Closed
@pafuent

Description

@pafuent

Issue Description

When the Timeout middleware is used and the timeout is reached, the http.Request Context propagated isn't properly cancelled.

Expected behavior

With the server example below this logs should be seen

{"time":"2021-07-06T00:05:08.341357-03:00","level":"INFO","prefix":"echo","file":"main.go","line":"23","message":"Start Log Job..."}
{"time":"2021-07-06T00:05:10.342116-03:00","level":"WARN","prefix":"echo","file":"main.go","line":"28","message":"Long Job Cancelled!!!"}
{"time":"2021-07-06T00:05:16.342187-03:00","level":"INFO","prefix":"echo","file":"main.go","line":"40","message":"Previous Long Middleware Finished!!!"}

Actual behavior

With the server example below this logs are seen

{"time":"2021-07-06T00:07:50.818216-03:00","level":"INFO","prefix":"echo","file":"main.go","line":"22","message":"Start Log Job..."}
{"time":"2021-07-06T00:07:54.819956-03:00","level":"INFO","prefix":"echo","file":"main.go","line":"25","message":"Long Job Finished!!!"}
{"time":"2021-07-06T00:07:58.819891-03:00","level":"INFO","prefix":"echo","file":"main.go","line":"39","message":"Previous Long Middleware Finished!!!"}

Steps to reproduce

Perform this request to the server example below

curl --request GET --url http://127.0.0.1:8080/ --header 'content-type: application/json'
Timeout!!!

Working code to debug

package main

import (
	"net/http"
	"time"

	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

const longJobFinished string = "Long Job Finished!!!"

func main() {
	// Echo instance
	e := echo.New()
	e.Debug = true

	// Middleware
	e.Use(middleware.Recover())

	handlerFunc := func(c echo.Context) error {
		e.Logger.Info("Start Log Job...")
		select {
		case <-time.After(4 * time.Second):
			e.Logger.Info(longJobFinished)
		case <-c.Request().Context().Done():
			e.Logger.Warn("Long Job Cancelled!!!")
			return nil
		}
		return c.String(http.StatusOK, longJobFinished)
	}

	// This middleware that sleeps is needed because to avoid the original Request Context gets cancelled by Go stdlib
	// See https://golang.org/pkg/net/http/#Request.Context
	previousLongMiddleware := func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c echo.Context) error {
			next(c)
			time.Sleep(6 * time.Second)
			e.Logger.Info("Previous Long Middleware Finished!!!")
			return nil
		}
	}

	// Timeout handler middleware
	middlewareFunc := middleware.TimeoutWithConfig(middleware.TimeoutConfig{
		Timeout:      2 * time.Second,
		ErrorMessage: "Timeout!!!",
	})
	// Handler with timeout middleware
	e.GET("/", handlerFunc, previousLongMiddleware, middlewareFunc)

	// Start server
	e.Logger.Fatal(e.Start(":8080"))
}

Version/commit

4.3.0

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions