From 7b44ec9efa54af931fe4699c5f7486b469866798 Mon Sep 17 00:00:00 2001 From: zhangyi Date: Thu, 29 Aug 2024 12:21:22 +0800 Subject: [PATCH 1/2] Bug fix: - fatal error: concurrent map writes(#15) --- timeout.go | 8 +++++--- timeout_test.go | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/timeout.go b/timeout.go index 75165a9..2c869ee 100644 --- a/timeout.go +++ b/timeout.go @@ -85,9 +85,11 @@ func New(opts ...Option) gin.HandlerFunc { tw.FreeBuffer() bufPool.Put(buffer) - c.Writer = w - t.response(c) - c.Writer = tw + // Copy gin.Context here to avoid concurrent writes to c.ResponseWriter's header(which is a map), + // see https://github.com/gin-contrib/timeout/issues/15 + ctxCopy := c.Copy() + ctxCopy.Writer = w + t.response(ctxCopy) } } } diff --git a/timeout_test.go b/timeout_test.go index 63aa7bd..598595f 100644 --- a/timeout_test.go +++ b/timeout_test.go @@ -100,3 +100,26 @@ func TestPanic(t *testing.T) { assert.Equal(t, http.StatusInternalServerError, w.Code) assert.Equal(t, "", w.Body.String()) } + +func TestConcurrentHeaderWrites(t *testing.T) { + r := gin.New() + r.Use(gin.Recovery()) + r.Use(New( + WithTimeout(time.Millisecond*50), + WithHandler(func(c *gin.Context) { + c.Next() + }), + )) + r.GET("/", func(c *gin.Context) { + for { + c.Header("X-Foo", "bar") + } + }) + + w := httptest.NewRecorder() + req, err := http.NewRequest(http.MethodGet, "/", nil) + if err != nil { + t.Fatal("http NewRequest: ", err) + } + r.ServeHTTP(w, req) +} From 5a92f27d7af44e1d83a6a141483a6499b580676a Mon Sep 17 00:00:00 2001 From: zhangyi Date: Thu, 29 Aug 2024 12:55:57 +0800 Subject: [PATCH 2/2] fix lint warning --- timeout_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/timeout_test.go b/timeout_test.go index 598595f..0647719 100644 --- a/timeout_test.go +++ b/timeout_test.go @@ -117,7 +117,7 @@ func TestConcurrentHeaderWrites(t *testing.T) { }) w := httptest.NewRecorder() - req, err := http.NewRequest(http.MethodGet, "/", nil) + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "/", nil) if err != nil { t.Fatal("http NewRequest: ", err) }