diff --git a/context.go b/context.go index 046f284e..f560402c 100644 --- a/context.go +++ b/context.go @@ -5,6 +5,7 @@ package gin import ( + "context" "errors" "fmt" "io" @@ -15,6 +16,7 @@ import ( "net/http" "net/url" "os" + "reflect" "strings" "time" @@ -41,9 +43,10 @@ const abortIndex int8 = math.MaxInt8 / 2 // Context is the most important part of gin. It allows us to pass variables between middleware, // manage the flow, validate the JSON of a request and render a JSON response for example. type Context struct { - writermem responseWriter - Request *http.Request - Writer ResponseWriter + context.Context //point to parent + writermem responseWriter + Request *http.Request + Writer ResponseWriter Params Params handlers HandlersChain @@ -86,6 +89,37 @@ func (c *Context) reset() { c.formCache = nil } +type gin_context string + +const gin_context_key gin_context = "gin_real_context" + +// GinContext try to Get gin.Context from context.Context +func GinContext(ctx context.Context) (*Context, error) { + v := ctx.Value(gin_context_key) + if gc, ok := v.(*Context); ok { + return gc, nil + } else { + return nil, fmt.Errorf("GinContext fail,TypeOf(v):%s", reflect.TypeOf(v)) + } +} + +// MustGinContext try to gin.Context from context.Context +// Will panic if fail +func MustGinContext(ctx context.Context) *Context { + if gc, err := GinContext(ctx); err == nil { + return gc + } else { + panic(fmt.Sprintf("MustGinContext fail:%s", err)) + } +} + +// NewContext wrap gin.Context with context.Context +func NewContext(e *Engine) *Context { + c := &Context{engine: e} + c.Context = context.WithValue(context.Background(), gin_context_key, c) //With a value point to gin.Context + return c +} + // Copy returns a copy of the current context that can be safely used outside the request's scope. // This has to be used when the context has to be passed to a goroutine. func (c *Context) Copy() *Context { @@ -1028,34 +1062,6 @@ func (c *Context) SetAccepted(formats ...string) { c.Accepted = formats } -/************************************/ -/***** GOLANG.ORG/X/NET/CONTEXT *****/ -/************************************/ - -// Deadline returns the time when work done on behalf of this context -// should be canceled. Deadline returns ok==false when no deadline is -// set. Successive calls to Deadline return the same results. -func (c *Context) Deadline() (deadline time.Time, ok bool) { - return -} - -// Done returns a channel that's closed when work done on behalf of this -// context should be canceled. Done may return nil if this context can -// never be canceled. Successive calls to Done return the same value. -func (c *Context) Done() <-chan struct{} { - return nil -} - -// Err returns a non-nil error value after Done is closed, -// successive calls to Err return the same error. -// If Done is not yet closed, Err returns nil. -// If Done is closed, Err returns a non-nil error explaining why: -// Canceled if the context was canceled -// or DeadlineExceeded if the context's deadline passed. -func (c *Context) Err() error { - return nil -} - // Value returns the value associated with this context for key, or nil // if no value is associated with key. Successive calls to Value with // the same key returns the same result. @@ -1064,8 +1070,10 @@ func (c *Context) Value(key interface{}) interface{} { return c.Request } if keyAsString, ok := key.(string); ok { - val, _ := c.Get(keyAsString) - return val + val, ok := c.Get(keyAsString) + if ok { + return val + } } - return nil + return c.Context.Value(key) } diff --git a/context_test.go b/context_test.go index f7bb0f51..c909e2dd 100644 --- a/context_test.go +++ b/context_test.go @@ -1868,6 +1868,42 @@ func TestContextResetInHandler(t *testing.T) { }) } + +func UserFunc2Mock(ctx context.Context, t *testing.T) { + v, ok := ctx.Value("user_self_defined_value").(string) + assert.Equal(t, true, ok) + assert.Equal(t, "vvvvv", v) + assert.NotPanics(t, func() { + gc := MustGinContext(ctx) //get gin.Context back as need + assert.NotEqual(t, gc, nil) + }) +} + +func UserFunc3Mock(ctx context.Context, t *testing.T) { + assert.Panics(t, func() { + gc := MustGinContext(ctx) //get gin.Context back as need + assert.Equal(t, gc, nil) + }) +} + +func UserFunc1Mock(ctx context.Context, t *testing.T) { + //add user defined value + ctx = context.WithValue(ctx, "user_self_defined_value", "vvvvv") + + //do user's business + UserFunc2Mock(ctx, t) +} + +func TestNewContextMustGin(t *testing.T) { + ctx := NewContext(nil) + UserFunc1Mock(ctx, t) //use gin.Context as standard context +} + +func TestStandardContext(t *testing.T) { + ctx := context.Background() + UserFunc3Mock(ctx, t) +} + func TestRaceParamsContextCopy(t *testing.T) { DefaultWriter = os.Stdout router := Default() diff --git a/gin.go b/gin.go index caf4cf2c..ed7b0013 100644 --- a/gin.go +++ b/gin.go @@ -156,7 +156,7 @@ func Default() *Engine { } func (engine *Engine) allocateContext() *Context { - return &Context{engine: engine} + return NewContext(engine) } // Delims sets template left and right delims and returns a Engine instance. diff --git a/go.mod b/go.mod index 34151852..6b8cc7d9 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/gin-gonic/gin go 1.12 require ( + github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 github.com/gin-contrib/sse v0.1.0 github.com/go-playground/locales v0.12.1 // indirect github.com/go-playground/universal-translator v0.16.0 // indirect @@ -12,6 +13,9 @@ require ( github.com/mattn/go-isatty v0.0.9 github.com/stretchr/testify v1.4.0 github.com/ugorji/go/codec v1.1.7 + golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 + golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect + golang.org/x/sys v0.0.0-20190412213103-97732733099d // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v9 v9.29.1 gopkg.in/yaml.v2 v2.2.2 diff --git a/go.sum b/go.sum index 129ad387..98c52d78 100644 --- a/go.sum +++ b/go.sum @@ -39,4 +39,4 @@ gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8 gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= \ No newline at end of file