1
votes

When accessing the gin-gonic server below, the HTTP client should receive the code 500, but receives the code 200.

package main

import (
    "github.com/gin-contrib/gzip"
    "github.com/gin-gonic/gin"
)

func main() {
    gin.SetMode(gin.ReleaseMode)
    r := gin.New()
    r.Use(gin.Logger())
    r.Use(gin.Recovery())
    r.Use(gzip.Gzip(gzip.DefaultCompression))

    r.POST("/test", func(c *gin.Context) {
        panic("test")                        // Server panic and client should receive code 500.
    })

    r.Run(":8080")
}

When accessing /test from a HTTP client, the go server log is as below and looks return the code 500.

[GIN] 2020/09/28 - 10:23:14 | 500 |     67.2995ms |             ::1 | POST     "/test"

2020/09/28 10:23:14 [Recovery] 2020/09/28 - 10:23:14 panic recovered:
test
C:/path/to/myproject/main.go:16 (0x8f193f)
    main.func1: panic("test")

But HTTP client receives the code 200.

enter image description here

When I remove r.Use(gzip.Gzip(gzip.DefaultCompression)), the HTTP client receives the code 500.

Why the client receives code 200 with r.Use(gzip.Gzip(gzip.DefaultCompression)), How can I fix this?

2
I couldn't reproduce this. Getting code 500 with and without the gzip middleware.user14311453
@LLawliet which version are you using? I use go 1.14.6, gin 1.6.3, gin-contrib/gzip 0.0.3N.F.
go 1.15.2, gin 1.6.3, gin-contrib/gzip 0.0.3user14311453
I tried go 1.15.2, but still gets code 200.N.F.
How are you executing the POST? It's probably my fault for using curl without accept headers.user14311453

2 Answers

1
votes

I've reproduce your case. Postman got code 200 but the server results 500 instead.

The server will call c.Next() to execute 4 handlers when receive post requests. The sequence is as follow:

gin.Logger
gin.Recovery
gzip.Gzip(gzip.DefaultCompression)
your handler

Here is gin responseWriter writes response header and it will write header only once.

func (w *responseWriter) WriteHeaderNow() {
    if !w.Written() {
        w.size = 0
        w.ResponseWriter.WriteHeader(w.status)
    }
}

Both gzip.Gzip(gzip.DefaultCompression) and gin.Recovery has defer func to write response header. Golang's deferred calls are executed in last-in-first-out order. So gzip.Gzip(gzip.DefaultCompression) will write response header to 200, and gin.Recovery won't write reponse header to 500 as expected.

So to solve this problem, you should change the order of handlers and make sure gin.Recovery is the last handler to load.

0
votes

Adding the recovery middleware last seems to fix this.

package main

import (
    "github.com/gin-contrib/gzip"
    "github.com/gin-gonic/gin"
)

func main() {
    gin.SetMode(gin.ReleaseMode)
    r := gin.New()
    r.Use(gin.Logger())
    r.Use(gzip.Gzip(gzip.DefaultCompression))
    r.Use(gin.Recovery())

    r.POST("/test", func(c *gin.Context) {
        panic("test")                        // Server panic and client should receive code 500.
    })

    r.Run(":8080")
}