-
-
Notifications
You must be signed in to change notification settings - Fork 72
/
context.go
132 lines (110 loc) · 3.46 KB
/
context.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package atreugo
import (
"context"
"fmt"
"sync"
"sync/atomic"
"github.com/fasthttp/router"
"github.com/savsgio/gotils/bytes"
"github.com/savsgio/gotils/strconv"
"github.com/valyala/fasthttp"
)
var (
attachedCtxKey = fmt.Sprintf("__attachedCtx::%s__", bytes.Rand(make([]byte, 15)))
requestCtxPool = sync.Pool{
New: func() any {
ctx := new(RequestCtx)
ctx.jsonMarshalFunc = defaultJSONMarshalFunc
return ctx
},
}
)
// AcquireRequestCtx returns an empty RequestCtx instance from request context pool.
//
// The returned RequestCtx instance may be passed to ReleaseRequestCtx when it is
// no longer needed. This allows RequestCtx recycling, reduces GC pressure
// and usually improves performance.
func AcquireRequestCtx(ctx *fasthttp.RequestCtx) *RequestCtx {
actx, _ := requestCtxPool.Get().(*RequestCtx)
actx.RequestCtx = ctx
return actx
}
// ReleaseRequestCtx returns ctx acquired via AcquireRequestCtx to request context pool.
//
// It is forbidden accessing ctx and/or its' members after returning
// it to request pool.
func ReleaseRequestCtx(ctx *RequestCtx) {
ctx.next = false
ctx.skipView = false
atomic.StoreInt32(&ctx.searchingOnAttachedCtx, 0)
ctx.RequestCtx = nil
requestCtxPool.Put(ctx)
}
// RequestID returns the "X-Request-ID" header value.
func (ctx *RequestCtx) RequestID() []byte {
return ctx.Request.Header.Peek(XRequestIDHeader)
}
// Next pass control to the next middleware/view function.
func (ctx *RequestCtx) Next() error {
ctx.next = true
return nil
}
// SkipView sets flag to skip view execution in the current request
//
// Use it in before middlewares.
func (ctx *RequestCtx) SkipView() {
ctx.skipView = true
}
// AttachContext attach a context.Context to the RequestCtx
//
// WARNING: The extra context could not be itself.
func (ctx *RequestCtx) AttachContext(extraCtx context.Context) {
if extraCtx == ctx {
panic("could not attach to itself")
}
ctx.SetUserValue(attachedCtxKey, extraCtx)
}
// AttachedContext returns the attached context.Context if exist.
func (ctx *RequestCtx) AttachedContext() context.Context {
if extraCtx, ok := ctx.UserValue(attachedCtxKey).(context.Context); ok {
return extraCtx
}
return nil
}
// MatchedRoutePath returns the matched route path
// if Atreugo.SaveMatchedRoutePath() is enabled.
func (ctx *RequestCtx) MatchedRoutePath() []byte {
if value, ok := ctx.UserValue(router.MatchedRoutePathParam).(string); ok {
return strconv.S2B(value)
}
return nil
}
// Value returns the value associated with attached context or 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.
//
// WARNING: The provided key should not be of type string or any other built-in
// to avoid extra allocating when assigning to an any, context keys often
// have concrete type struct{}. Alternatively, exported context key variables' static
// type should be a pointer or interface.
//
// If the key is of type string, try to use:
//
// ctx.SetUserValue("myKey", "myValue")
// ctx.UserValue("myKey")
//
// instead of:
//
// ctx.AttachContext(context.WithValue(myCtx, "myKey", "myValue"))
// ctx.Value("myKey")
//
// to avoid extra allocation.
func (ctx *RequestCtx) Value(key any) any {
if atomic.CompareAndSwapInt32(&ctx.searchingOnAttachedCtx, 0, 1) {
defer atomic.StoreInt32(&ctx.searchingOnAttachedCtx, 0)
if extraCtx := ctx.AttachedContext(); extraCtx != nil {
return extraCtx.Value(key)
}
}
return ctx.RequestCtx.Value(key)
}