Przeglądaj źródła

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
Samuel Abreu 6 lat temu
rodzic
commit
6e320c97e8
2 zmienionych plików z 26 dodań i 0 usunięć
  1. 3 0
      context.go
  2. 23 0
      context_test.go

+ 3 - 0
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
 }
 

+ 23 - 0
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()
+}