Skip to content

Commit 6e320c9

Browse files
samuelabreuthinkerou
authored andcommitted
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
1 parent 35e33d3 commit 6e320c9

File tree

2 files changed

+26
-0
lines changed

2 files changed

+26
-0
lines changed

context.go

+3
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ func (c *Context) Copy() *Context {
8989
for k, v := range c.Keys {
9090
cp.Keys[k] = v
9191
}
92+
paramCopy := make([]Param, len(cp.Params))
93+
copy(paramCopy, cp.Params)
94+
cp.Params = paramCopy
9295
return &cp
9396
}
9497

context_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import (
1313
"mime/multipart"
1414
"net/http"
1515
"net/http/httptest"
16+
"os"
1617
"reflect"
1718
"strings"
19+
"sync"
1820
"testing"
1921
"time"
2022

@@ -1821,3 +1823,24 @@ func TestContextResetInHandler(t *testing.T) {
18211823
c.Next()
18221824
})
18231825
}
1826+
1827+
func TestRaceParamsContextCopy(t *testing.T) {
1828+
DefaultWriter = os.Stdout
1829+
router := Default()
1830+
nameGroup := router.Group("/:name")
1831+
var wg sync.WaitGroup
1832+
wg.Add(2)
1833+
{
1834+
nameGroup.GET("/api", func(c *Context) {
1835+
go func(c *Context, param string) {
1836+
defer wg.Done()
1837+
// First assert must be executed after the second request
1838+
time.Sleep(50 * time.Millisecond)
1839+
assert.Equal(t, c.Param("name"), param)
1840+
}(c.Copy(), c.Param("name"))
1841+
})
1842+
}
1843+
performRequest(router, "GET", "/name1/api")
1844+
performRequest(router, "GET", "/name2/api")
1845+
wg.Wait()
1846+
}

0 commit comments

Comments
 (0)