From 6ecb89ad63374698e24aee8b4cfdd8246c87fccd Mon Sep 17 00:00:00 2001 From: Saksham Arya Date: Wed, 20 Nov 2024 16:02:55 +0530 Subject: [PATCH 1/2] make headers thread safe add reader lock --- context.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/context.go b/context.go index c724daf344..0a2a5bec2e 100644 --- a/context.go +++ b/context.go @@ -21,6 +21,7 @@ import ( "time" "github.com/gin-contrib/sse" + "github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/render" ) @@ -71,6 +72,9 @@ type Context struct { // This mutex protects Keys map. mu sync.RWMutex + // This mutex protects headers map + hmu sync.RWMutex + // Keys is a key/value pair exclusively for the context of each request. Keys map[string]any @@ -954,6 +958,8 @@ func (c *Context) IsWebsocket() bool { } func (c *Context) requestHeader(key string) string { + c.hmu.Lock() + defer c.hmu.Unlock() return c.Request.Header.Get(key) } @@ -983,6 +989,8 @@ func (c *Context) Status(code int) { // It writes a header in the response. // If value == "", this method removes the header `c.Writer.Header().Del(key)` func (c *Context) Header(key, value string) { + c.hmu.Lock() + defer c.hmu.Unlock() if value == "" { c.Writer.Header().Del(key) return From 46b1c57c33966e75f27a7c5adaac47f29e85e0de Mon Sep 17 00:00:00 2001 From: Saksham Arya Date: Wed, 20 Nov 2024 16:18:37 +0530 Subject: [PATCH 2/2] add test case fix lint --- context_test.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/context_test.go b/context_test.go index 5b63a647c9..3d7818fbb5 100644 --- a/context_test.go +++ b/context_test.go @@ -166,7 +166,7 @@ func TestSaveUploadedFileWithPermission(t *testing.T) { require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request, _ = http.NewRequest(http.MethodPost, "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.FormFile("file") require.NoError(t, err) @@ -187,7 +187,7 @@ func TestSaveUploadedFileWithPermissionFailed(t *testing.T) { require.NoError(t, err) mw.Close() c, _ := CreateTestContext(httptest.NewRecorder()) - c.Request, _ = http.NewRequest("POST", "/", buf) + c.Request, _ = http.NewRequest(http.MethodPost, "/", buf) c.Request.Header.Set("Content-Type", mw.FormDataContentType()) f, err := c.FormFile("file") require.NoError(t, err) @@ -3123,3 +3123,23 @@ func TestContextNext(t *testing.T) { assert.True(t, exists) assert.Equal(t, "value3", value) } + +func TestParallelHeaderWrite(t *testing.T) { + c, _ := CreateTestContext(httptest.NewRecorder()) + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 1000; i++ { + c.Header("key", "value") + } + }() + wg.Add(1) + go func() { + defer wg.Done() + for i := 0; i < 1000; i++ { + c.Header("key", "value") + } + }() + wg.Wait() +}