forked from koding/kite
-
Notifications
You must be signed in to change notification settings - Fork 0
/
method.go
256 lines (208 loc) · 7.44 KB
/
method.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package kite
import (
"sync"
"time"
"github.com/juju/ratelimit"
)
// MethodHandling defines how to handle chaining of kite.Handler middlewares.
// An error breaks the chain regardless of what handling is used. Note that all
// Pre and Post handlers are executed regardless the handling logic, only the
// return paramater is defined by the handling mode.
type MethodHandling int
const (
// ReturnMethod returns main method's response. This is the standard default.
ReturnMethod MethodHandling = iota
// ReturnFirst returns the first non-nil response.
ReturnFirst
// ReturnLatest returns the latest response (waterfall behaviour)
ReturnLatest
)
// Objects implementing the Handler interface can be registered to a method.
// The returned result must be marshalable with json package.
type Handler interface {
ServeKite(*Request) (result interface{}, err error)
}
// HandlerFunc is a type adapter to allow the use of ordinary functions as
// Kite handlers. If h is a function with the appropriate signature,
// HandlerFunc(h) is a Handler object that calls h.
type HandlerFunc func(*Request) (result interface{}, err error)
// ServeKite calls h(r)
func (h HandlerFunc) ServeKite(r *Request) (interface{}, error) {
return h(r)
}
// Method defines a method and the Handler it is bind to. By default
// "ReturnMethod" handling is used.
type Method struct {
// name is the method name. Unnamed methods can exist
name string
// handler contains the related Handler for the given method
handler Handler // handler is the base handler, the response of it is returned as the final
preHandlers []Handler // a list of handlers that are executed before the main handler
postHandlers []Handler // a list of handlers that are executed after the main handler
// authenticate defines if a given authenticator function is enabled for
// the given auth type in the request.
authenticate bool
// handling defines how to handle chaining of kite.Handler middlewares.
handling MethodHandling
// initialized is used to indicate whether all pre and post handlers are
// initialized.
initialized bool
// bucket is used for throttling the method by certain rule
bucket *ratelimit.Bucket
mu sync.Mutex // protects handler slices
}
// addHandle is an internal method to add a handler
func (k *Kite) addHandle(method string, handler Handler) *Method {
authenticate := true
if k.Config.DisableAuthentication {
authenticate = false
}
m := &Method{
name: method,
handler: handler,
preHandlers: make([]Handler, 0),
postHandlers: make([]Handler, 0),
authenticate: authenticate,
handling: k.MethodHandling,
}
k.handlers[method] = m
return m
}
// DisableAuthentication disables authentication check for this method.
func (m *Method) DisableAuthentication() *Method {
m.authenticate = false
return m
}
// Throttle throttles the method for each incoming request. The throttle
// algorithm is based on token bucket implementation:
// http://en.wikipedia.org/wiki/Token_bucket. Rate determines the number of
// request which are allowed per frequency. Example: A capacity of 50 and
// fillInterval of two seconds means that initially it can handle 50 requests
// and every two seconds the bucket will be filled with one token until it hits
// the capacity. If there is a burst API calls, all tokens will be exhausted
// and clients need to be wait until the bucket is filled with time. For
// example to have throttle with 30 req/second, you need to have a fillinterval
// of 33.33 milliseconds.
func (m *Method) Throttle(fillInterval time.Duration, capacity int64) *Method {
// don't do anything if the bucket is initialized already
if m.bucket != nil {
return m
}
m.bucket = ratelimit.NewBucket(
fillInterval, // interval
capacity, // token per interval
)
return m
}
// PreHandler adds a new kite handler which is executed before the method.
func (m *Method) PreHandle(handler Handler) *Method {
m.preHandlers = append(m.preHandlers, handler)
return m
}
// PreHandlerFunc adds a new kite handlerfunc which is executed before the
// method.
func (m *Method) PreHandleFunc(handler HandlerFunc) *Method {
return m.PreHandle(handler)
}
// PostHandle adds a new kite handler which is executed after the method.
func (m *Method) PostHandle(handler Handler) *Method {
m.postHandlers = append(m.postHandlers, handler)
return m
}
// PostHandlerFunc adds a new kite handlerfunc which is executed before the
// method.
func (m *Method) PostHandleFunc(handler HandlerFunc) *Method {
return m.PostHandle(handler)
}
// Handle registers the handler for the given method. The handler is called
// when a method call is received from a Kite.
func (k *Kite) Handle(method string, handler Handler) *Method {
return k.addHandle(method, handler)
}
// HandleFunc registers a handler to run when a method call is received from a
// Kite. It returns a *Method option to further modify certain options on a
// method call
func (k *Kite) HandleFunc(method string, handler HandlerFunc) *Method {
return k.addHandle(method, handler)
}
// PreHandle registers an handler which is executed before a kite.Handler
// method is executed. Calling PreHandle multiple times registers multiple
// handlers. A non-error return triggers the execution of the next handler. The
// execution order is FIFO.
func (k *Kite) PreHandle(handler Handler) {
k.preHandlers = append(k.preHandlers, handler)
}
// PreHandleFunc is the same as PreHandle. It accepts a HandlerFunc.
func (k *Kite) PreHandleFunc(handler HandlerFunc) {
k.PreHandle(handler)
}
// PostHandle registers an handler which is executed after a kite.Handler
// method is executed. Calling PostHandler multiple times registers multiple
// handlers. A non-error return triggers the execution of the next handler. The
// execution order is FIFO.
func (k *Kite) PostHandle(handler Handler) {
k.postHandlers = append(k.postHandlers, handler)
}
// PostHandleFunc is the same as PostHandle. It accepts a HandlerFunc.
func (k *Kite) PostHandleFunc(handler HandlerFunc) {
k.PostHandle(handler)
}
func (m *Method) ServeKite(r *Request) (interface{}, error) {
var firstResp interface{}
var resp interface{}
var err error
// first execute preHandlers. make a copy of the handler to avoid race
// conditions
m.mu.Lock()
preHandlers := make([]Handler, len(m.preHandlers))
for i, handler := range m.preHandlers {
preHandlers[i] = handler
}
m.mu.Unlock()
for _, handler := range preHandlers {
resp, err = handler.ServeKite(r)
if err != nil {
return nil, err
}
if m.handling == ReturnFirst && resp != nil && firstResp == nil {
firstResp = resp
}
}
preHandlers = nil // garbage collect it
// now call our base handler
resp, err = m.handler.ServeKite(r)
if err != nil {
return nil, err
}
// also save it dependent on the handling mechanism
methodResp := resp
if m.handling == ReturnFirst && resp != nil && firstResp == nil {
firstResp = resp
}
// and finally return our postHandlers
m.mu.Lock()
postHandlers := make([]Handler, len(m.postHandlers))
for i, handler := range m.postHandlers {
postHandlers[i] = handler
}
m.mu.Unlock()
for _, handler := range postHandlers {
resp, err = handler.ServeKite(r)
if err != nil {
return nil, err
}
if m.handling == ReturnFirst && resp != nil && firstResp == nil {
firstResp = resp
}
}
postHandlers = nil // garbage collect it
switch m.handling {
case ReturnMethod:
return methodResp, nil
case ReturnFirst:
return firstResp, nil
case ReturnLatest:
return resp, nil
}
return resp, nil
}