Mishandling 404?
I have a simple program
package main
import (
"github.com/gin-contrib/gzip"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.New()
r.Use( gzip.Gzip(gzip.DefaultCompression))
r.GET("/x", func(c *gin.Context) {
c.JSON(200, gin.H{"hello": "world"})
})
r.Run()
}
when i do curl localhost:8080 -H 'accept-encoding: gzip' -v I get a 200 response and when I do curl localhost:8080 -v I get a 404 response.
I am not sure what I am doing wrong but it seems like the gzip buffer is being closed before gin writes its 404 which somehow messes up the eventual response object.
hitting the localhost:8080/x endpoint works fine
It looks like this was introduced between v1.0.1 and v1.1.0
Same stuff
Hello, same here
Also with debug mode I got the following error with a 200 answer instead of 404 while hitting bad route (since v1.10) :
[GIN-debug] cannot write message to writer during serve error: flate: closed writer
I don't know if it's related
module gingz
go 1.23.6
require (
github.com/gin-contrib/gzip v1.2.2
github.com/gin-gonic/gin v1.10.0
)
It's work
It's work
The bug is that http://localhost:8080/ should return a 404 but it returns a 200. Requests to http://localhost:8080/x work as expected.
It's work
The bug is that
http://localhost:8080/should return a 404 but it returns a 200. Requests tohttp://localhost:8080/xwork as expected.
Look at it's nothing?
?body response is large ,the gzip will error?
I'm working on a test case:
func Test404WithGzip(t *testing.T) {
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/bar", nil)
req.Header.Add(headerAcceptEncoding, "gzip")
router := gin.New()
router.Use(Gzip(DefaultCompression, WithDecompressFn(DefaultDecompressHandle)))
router.GET("/foo", func(c *gin.Context) {
c.String(200, "ok")
})
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusNotFound, w.Code)
assert.Equal(t, "gzip", w.Header().Get(headerContentEncoding))
assert.Equal(t, "Accept-Encoding", w.Header().Get(headerVary))
assert.Equal(t, "23", w.Header().Get("Content-Length"))
gr, err := gzip.NewReader(w.Body)
assert.NoError(t, err)
defer gr.Close()
body, _ := io.ReadAll(gr)
assert.Equal(t, "404 page not found", string(body))
}
I found that the setting of the 200 seems to be coming from gz.Reset(io.Discard)
Same
I try to add g.WriteHeaderNow(), it worked.
func (g *gzipWriter) Write(data []byte) (int, error) {
g.Header().Del("Content-Length")
+ g.WriteHeaderNow()
return g.writer.Write(data)
}
compare with default writer responseWriter:
func (w *responseWriter) Write(data []byte) (n int, err error) {
w.WriteHeaderNow()
n, err = w.ResponseWriter.Write(data)
w.size += n
return
}
Have added a test here to the existing suite which demonstrates the issue
func TestHandleGzip(t *testing.T) {
gin.SetMode(gin.TestMode)
tests := []struct {
name string
path string
acceptEncoding string
expectedContentEncoding string
expectedBody string
expectedStatus int
}{
{
name: "Gzip compression",
path: "/",
acceptEncoding: "gzip",
expectedContentEncoding: "gzip",
expectedBody: "Gzip Test Response",
expectedStatus: http.StatusOK,
},
{
name: "No compression",
path: "/",
acceptEncoding: "",
expectedContentEncoding: "",
expectedBody: "Gzip Test Response",
expectedStatus: http.StatusOK,
},
{
name: "Bad path, no compression",
path: "/bad-path",
acceptEncoding: "",
expectedContentEncoding: "",
expectedBody: "404 page not found",
expectedStatus: http.StatusNotFound,
},
{
name: "Bad path, with compression",
path: "/bad-path",
acceptEncoding: "gzip",
expectedContentEncoding: "gzip",
expectedBody: "404 page not found",
expectedStatus: http.StatusNotFound,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
router := gin.New()
router.Use(Gzip(DefaultCompression))
router.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "Gzip Test Response")
})
req, _ := http.NewRequestWithContext(context.Background(), "GET", tt.path, nil)
req.Header.Set(headerAcceptEncoding, tt.acceptEncoding)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, tt.expectedStatus, w.Code)
assert.Equal(t, tt.expectedContentEncoding, w.Header().Get("Content-Encoding"))
if tt.expectedContentEncoding == "gzip" {
gr, err := gzip.NewReader(w.Body)
assert.NoError(t, err)
defer gr.Close()
body, _ := io.ReadAll(gr)
assert.Equal(t, tt.expectedBody, string(body))
} else {
assert.Equal(t, tt.expectedBody, w.Body.String())
}
})
}
}
[GIN-debug] cannot write message to writer during serve error: flate: closed writer
I also encountered this problem. Has this problem been fixed in version 1.2.3?
@adcwb no, it still present in 1.2.3
@appleboy any change to take a loot at this?
Hi, i got the same issue.
I will take it.