gin-contrib / timeout

Timeout middleware for Gin
MIT License
185 stars 38 forks source link

Headers get overwritten if using multiple custom middlewares #32

Open david-alza opened 2 years ago

david-alza commented 2 years ago

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.