timeout icon indicating copy to clipboard operation
timeout copied to clipboard

Headers get overwritten if using multiple custom middlewares

Open david-alza opened this issue 3 years ago • 0 comments

If you use multiple custom middlewares then the timeout middleware will overwrite previous status codes.

package main

import (
	"log"
	"net/http"
	"time"

	"github.com/gin-contrib/timeout"
	"github.com/gin-gonic/gin"
)

func testResponse(c *gin.Context) {
	c.String(http.StatusRequestTimeout, "timeout")
}

// custom middleware straight from example
func timeoutMiddleware() gin.HandlerFunc {
	return timeout.New(
		timeout.WithTimeout(500*time.Millisecond),
		timeout.WithHandler(func(c *gin.Context) {
			c.Next()
		}),
		timeout.WithResponse(testResponse),
	)
}

// simple middleware to always throw a 401
func authMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		c.AbortWithStatus(401)
		return
	}
}

func main() {
	r := gin.New()

	// middleware
	r.Use(gin.Logger())
	r.Use(timeoutMiddleware()) // 1. timeout middleware
	r.Use(authMiddleware())    // 2. auth middleware
	r.Use(gin.Recovery())      // recommend to use this middleware to recover from any panics in the handlers.

	r.GET("/", func(c *gin.Context) {
		time.Sleep(1000 * time.Millisecond)
		c.String(http.StatusOK, "Hello world!")
	})
	if err := r.Run(":8080"); err != nil {
		log.Fatal(err)
	}
}

result:

HTTP/1.1 200 OK

gin logs:

[GIN-debug] [WARNING] Headers were already written. Wanted to override status code 200 with 401
[GIN] 2022/08/25 - 16:58:19 | 401 |      25.583µs |       127.0.0.1 | GET      "/"

If you change the order of the middlewares then the timeout never applies correctly.

We swapped to https://github.com/vearne/gin-timeout and the issue doesn't occur there.

david-alza avatar Aug 25 '22 21:08 david-alza