[gin-gonic/gin]重定向c.Render(-1设置http status code为-1会导致timeout超时控制panic

2023-12-08 141 views
5

Description

在给gin服务添加超时控制的时候,由于一个接口需要返回重定向,然后导致了进程panic,怀疑是

// Redirect returns an HTTP redirect to the specific location.
func (c *Context) Redirect(code int, location string) {
    c.Render(-1, render.Redirect{
        Code:     code,
        Location: location,
        Request:  c.Request,
    })
}

返回-1导致的,请问一下这里为啥写死是-1? 报错信息

Error:invalid http status code: -1
Call Stack:/gopath/pkg/mod/[github.com/gin-contrib/timeout@v0.0.3/timeout.go:63](http://github.com/gin-contrib/timeout@v0.0.3/timeout.go:63) (0xe89731)

How to reproduce

func timeoutResponse(c *gin.Context) {
    c.JSON(http.StatusGatewayTimeout, gin.H{
        "code": 12,
        "msg":  "timeout",
    })
}

// Timeout timeout中间件
// sample: [0, 100] 表示采样比例,可以通过修改采样比例来灰度
func Timeout(sample int32) gin.HandlerFunc {
    num := rand.Int31n(101)
    if num < sample {
        return timeout.New(
            timeout.WithTimeout(6*time.Second),
            timeout.WithHandler(func(c *gin.Context) {
                c.Next()
            }),
            timeout.WithResponse(timeoutResponse),
        )
    }

    return func(c *gin.Context) {
        c.Next()
    }
}

Expectations

返回504 timeout

Actual result

panic

Environment

  • go version: 1.18
  • gin version (or commit ref): 1.8.1
  • operating system: linux

回答

9

Test Code

package main

import (
    "fmt"
    "net/http"
    "net/http/httptest"
    "time"

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

func main() {
    r := gin.Default()
    r.Use(timeout.New(
        timeout.WithTimeout(time.Second),
        timeout.WithHandler(func(c *gin.Context) {
            c.Next()
        }),
        timeout.WithResponse(func(c *gin.Context) {
            c.AbortWithStatus(http.StatusGatewayTimeout)
        }),
    ))
    r.GET("/redirect", func(c *gin.Context) {
        c.Redirect(http.StatusFound, "https://www.github.com/")
    })

    w := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", "/redirect", nil)
    r.ServeHTTP(w, req)

    fmt.Print(w.Body.String())
}

Result:

[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /redirect                 --> main.main.func3 (4 handlers)

2023/06/01 12:01:27 [Recovery] 2023/06/01 - 12:01:27 panic recovered:
GET /redirect HTTP/1.1

invalid http status code: -1
/Users/xxx/go/pkg/mod/github.com/gin-contrib/timeout@v0.0.3/timeout.go:63 (0x1027f6be3)
        New.func1: panic(p)
/Users/xxx/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/context.go:174 (0x1027f110f)
        (*Context).Next: c.handlers[c.index](c)
/Users/xxx/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/recovery.go:102 (0x1027f10f0)
        CustomRecoveryWithWriter.func1: c.Next()
/Users/xxx/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/context.go:174 (0x1027f037f)
        (*Context).Next: c.handlers[c.index](c)
/Users/xxx/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/logger.go:240 (0x1027f035c)
        LoggerWithConfig.func1: c.Next()
/Users/xxx/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/context.go:174 (0x1027ef497)
        (*Context).Next: c.handlers[c.index](c)
/Users/xxx/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/gin.go:620 (0x1027ef16c)
        (*Engine).handleHTTPRequest: c.Next()
/Users/xxx/go/pkg/mod/github.com/gin-gonic/gin@v1.9.1/gin.go:576 (0x1027eeec7)
        (*Engine).ServeHTTP: engine.handleHTTPRequest(c)
/Users/xxx/Desktop/gorm-playground/main.go:30 (0x1027f79ef)
        main: r.ServeHTTP(w, req)
/usr/local/go/src/runtime/proc.go:250 (0x1025d180f)
        main: fn()
/usr/local/go/src/runtime/asm_arm64.s:1270 (0x1025fe833)
        goexit: MOVD    R0, R0  // NOP

[GIN] 2023/06/01 - 12:01:27 | 500 |    1.914833ms |                 | GET      "/redirect"
6
StatusGatewayTimeout

image

-1 is not a valid http status code

6

我看了是其实是github.com/gin-contrib/timeout库的问题,gin使用-1跳过状态码赋值,但是这个库没有处理,参考这个老哥的pr https://github.com/gin-contrib/timeout/pull/37/files

6

gin

// Redirect returns an HTTP redirect to the specific location.
func (c *Context) Redirect(code int, location string) {
    c.Render(-1, render.Redirect{
        Code:     code,
        Location: location,
        Request:  c.Request,
    })
}

写死的-1会修改吗?

3

是啊,github.com/gin-contrib/timeout这个库好多issue和pr都没有处理。相比之下github.com/vearne/gin-timeout活跃得多,相同的问题重定向异常,维护者一天就修复了。gin社区是否可以考虑将github.com/vearne/gin-timeout这个库加入到gin-contrib?