mirror of https://github.com/gofiber/fiber.git
🐛 Fix Mutability issue of UserContext in fiber.Ctx (#1522)
* existing tests clean up * concurrent test for UserContext Reused * comment on Ctx.userContext field * userContext tests edited & fixed, userContext field removed, constant of userContextKey definedpull/1525/head
parent
5d1f89942c
commit
842b63022d
15
ctx.go
15
ctx.go
|
@ -34,6 +34,9 @@ const maxParams = 30
|
|||
|
||||
const queryTag = "query"
|
||||
|
||||
// userContextKey define the key name for storing context.Context in *fasthttp.RequestCtx
|
||||
const userContextKey = "__local_user_context__"
|
||||
|
||||
// Ctx represents the Context which hold the HTTP request and response.
|
||||
// It has methods for the request query string, parameters, body, HTTP headers and so on.
|
||||
type Ctx struct {
|
||||
|
@ -53,7 +56,6 @@ type Ctx struct {
|
|||
values [maxParams]string // Route parameter values
|
||||
fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx
|
||||
matched bool // Non use route matched
|
||||
userContext context.Context
|
||||
}
|
||||
|
||||
// Range data for c.Range
|
||||
|
@ -342,15 +344,18 @@ func (c *Ctx) Context() *fasthttp.RequestCtx {
|
|||
// UserContext returns a context implementation that was set by
|
||||
// user earlier or returns a non-nil, empty context,if it was not set earlier.
|
||||
func (c *Ctx) UserContext() context.Context {
|
||||
if c.userContext == nil {
|
||||
c.userContext = context.Background()
|
||||
ctx, ok := c.fasthttp.UserValue(userContextKey).(context.Context)
|
||||
if !ok {
|
||||
ctx = context.Background()
|
||||
c.SetUserContext(ctx)
|
||||
}
|
||||
return c.userContext
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// SetUserContext sets a context implementation by user.
|
||||
func (c *Ctx) SetUserContext(ctx context.Context) {
|
||||
c.userContext = ctx
|
||||
c.fasthttp.SetUserValue(userContextKey, ctx)
|
||||
}
|
||||
|
||||
// Cookie sets a cookie by passing a cookie struct.
|
||||
|
|
85
ctx_test.go
85
ctx_test.go
|
@ -358,21 +358,21 @@ func Test_Ctx_BodyParser(t *testing.T) {
|
|||
Name string `json:"name" xml:"name" form:"name" query:"name"`
|
||||
}
|
||||
|
||||
{
|
||||
var gzipJSON bytes.Buffer
|
||||
w := gzip.NewWriter(&gzipJSON)
|
||||
_, _ = w.Write([]byte(`{"name":"john"}`))
|
||||
_ = w.Close()
|
||||
{
|
||||
var gzipJSON bytes.Buffer
|
||||
w := gzip.NewWriter(&gzipJSON)
|
||||
_, _ = w.Write([]byte(`{"name":"john"}`))
|
||||
_ = w.Close()
|
||||
|
||||
c.Request().Header.SetContentType(MIMEApplicationJSON)
|
||||
c.Request().Header.Set(HeaderContentEncoding, "gzip")
|
||||
c.Request().SetBody(gzipJSON.Bytes())
|
||||
c.Request().Header.SetContentLength(len(gzipJSON.Bytes()))
|
||||
d := new(Demo)
|
||||
utils.AssertEqual(t, nil, c.BodyParser(d))
|
||||
utils.AssertEqual(t, "john", d.Name)
|
||||
c.Request().Header.Del(HeaderContentEncoding)
|
||||
}
|
||||
c.Request().Header.SetContentType(MIMEApplicationJSON)
|
||||
c.Request().Header.Set(HeaderContentEncoding, "gzip")
|
||||
c.Request().SetBody(gzipJSON.Bytes())
|
||||
c.Request().Header.SetContentLength(len(gzipJSON.Bytes()))
|
||||
d := new(Demo)
|
||||
utils.AssertEqual(t, nil, c.BodyParser(d))
|
||||
utils.AssertEqual(t, "john", d.Name)
|
||||
c.Request().Header.Del(HeaderContentEncoding)
|
||||
}
|
||||
|
||||
testDecodeParser := func(contentType, body string) {
|
||||
c.Request().Header.SetContentType(contentType)
|
||||
|
@ -508,7 +508,10 @@ func Test_Ctx_Context(t *testing.T) {
|
|||
|
||||
// go test -run Test_Ctx_UserContext
|
||||
func Test_Ctx_UserContext(t *testing.T) {
|
||||
c := Ctx{}
|
||||
app := New()
|
||||
c := app.AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer app.ReleaseCtx(c)
|
||||
|
||||
t.Run("Nil_Context", func(t *testing.T) {
|
||||
ctx := c.UserContext()
|
||||
utils.AssertEqual(t, ctx, context.Background())
|
||||
|
@ -518,16 +521,56 @@ func Test_Ctx_UserContext(t *testing.T) {
|
|||
testKey := "Test Key"
|
||||
testValue := "Test Value"
|
||||
ctx := context.WithValue(context.Background(), testKey, testValue)
|
||||
utils.AssertEqual(t, ctx.Value(testKey), testValue)
|
||||
utils.AssertEqual(t, testValue, ctx.Value(testKey))
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// go test -run Test_Ctx_UserContext
|
||||
// go test -run Test_Ctx_SetUserContext
|
||||
func Test_Ctx_SetUserContext(t *testing.T) {
|
||||
c := Ctx{}
|
||||
c.SetUserContext(context.Background())
|
||||
utils.AssertEqual(t, c.UserContext(), context.Background())
|
||||
app := New()
|
||||
c := app.AcquireCtx(&fasthttp.RequestCtx{})
|
||||
defer app.ReleaseCtx(c)
|
||||
|
||||
testKey := "Test Key"
|
||||
testValue := "Test Value"
|
||||
ctx := context.WithValue(context.Background(), testKey, testValue)
|
||||
c.SetUserContext(ctx)
|
||||
utils.AssertEqual(t, testValue, c.UserContext().Value(testKey))
|
||||
}
|
||||
|
||||
// go test -run Test_Ctx_UserContext_Multiple_Requests
|
||||
func Test_Ctx_UserContext_Multiple_Requests(t *testing.T) {
|
||||
testKey := "foobar-key"
|
||||
testValue := "foobar-value"
|
||||
|
||||
app := New()
|
||||
app.Get("/", func(c *Ctx) error {
|
||||
ctx := c.UserContext()
|
||||
|
||||
if ctx.Value(testKey) != nil {
|
||||
return c.SendStatus(StatusInternalServerError)
|
||||
}
|
||||
|
||||
input := utils.CopyString(c.Query("input", "NO_VALUE"))
|
||||
ctx = context.WithValue(ctx, testKey, fmt.Sprintf("%s_%s", testValue, input))
|
||||
c.SetUserContext(ctx)
|
||||
|
||||
return c.Status(StatusOK).SendString(fmt.Sprintf("resp_%s_returned", input))
|
||||
})
|
||||
|
||||
// Consecutive Requests
|
||||
for i := 1; i <= 10; i++ {
|
||||
t.Run(fmt.Sprintf("request_%d", i), func(t *testing.T) {
|
||||
resp, err := app.Test(httptest.NewRequest(MethodGet, fmt.Sprintf("/?input=%d", i), nil))
|
||||
|
||||
utils.AssertEqual(t, nil, err, "Unexpected error from response")
|
||||
utils.AssertEqual(t, StatusOK, resp.StatusCode, "context.Context returned from c.UserContext() is reused")
|
||||
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
utils.AssertEqual(t, nil, err, "Unexpected error from reading response body")
|
||||
utils.AssertEqual(t, fmt.Sprintf("resp_%d_returned", i), string(b), "response text incorrect")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// go test -run Test_Ctx_Cookie
|
||||
|
|
Loading…
Reference in New Issue