Skip to content

Commit

Permalink
Fix context.Params race condition on Copy() (#1841)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
samuelabreu authored and thinkerou committed May 27, 2019
1 parent 35e33d3 commit 6e320c9
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 0 deletions.
3 changes: 3 additions & 0 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
23 changes: 23 additions & 0 deletions context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import (
"mime/multipart"
"net/http"
"net/http/httptest"
"os"
"reflect"
"strings"
"sync"
"testing"
"time"

Expand Down Expand Up @@ -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()
}

0 comments on commit 6e320c9

Please sign in to comment.