From 6e320c97e83a61df3daa0f28695a736bece3104b Mon Sep 17 00:00:00 2001 From: Samuel Abreu Date: Mon, 27 May 2019 03:04:30 -0300 Subject: [PATCH] Fix context.Params race condition on Copy() (#1841) * Fix context.Params race condition on Copy() Using context.Param(key) on a context.Copy inside a goroutine may lead to incorrect value on a high load, where another request overwrite a Param * Using waitgroup to wait asynchronous test case --- context.go | 3 +++ context_test.go | 23 +++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/context.go b/context.go index 425b627f..3f1f8ca0 100644 --- a/context.go +++ b/context.go @@ -89,6 +89,9 @@ func (c *Context) Copy() *Context { for k, v := range c.Keys { cp.Keys[k] = v } + paramCopy := make([]Param, len(cp.Params)) + copy(paramCopy, cp.Params) + cp.Params = paramCopy return &cp } diff --git a/context_test.go b/context_test.go index e8dcd3dc..89cfb446 100644 --- a/context_test.go +++ b/context_test.go @@ -13,8 +13,10 @@ import ( "mime/multipart" "net/http" "net/http/httptest" + "os" "reflect" "strings" + "sync" "testing" "time" @@ -1821,3 +1823,24 @@ func TestContextResetInHandler(t *testing.T) { c.Next() }) } + +func TestRaceParamsContextCopy(t *testing.T) { + DefaultWriter = os.Stdout + router := Default() + nameGroup := router.Group("/:name") + var wg sync.WaitGroup + wg.Add(2) + { + nameGroup.GET("/api", func(c *Context) { + go func(c *Context, param string) { + defer wg.Done() + // First assert must be executed after the second request + time.Sleep(50 * time.Millisecond) + assert.Equal(t, c.Param("name"), param) + }(c.Copy(), c.Param("name")) + }) + } + performRequest(router, "GET", "/name1/api") + performRequest(router, "GET", "/name2/api") + wg.Wait() +}