1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
13 // An InterceptorFunc is functionality invoked by the framework BEFORE or AFTER
16 // An interceptor may optionally return a Result (instead of nil). Depending on
17 // when the interceptor was invoked, the response is different:
18 // 1. BEFORE: No further interceptors are invoked, and neither is the action.
19 // 2. AFTER: Further interceptors are still run.
20 // In all cases, any returned Result will take the place of any existing Result.
22 // In the BEFORE case, that returned Result is guaranteed to be final, while
23 // in the AFTER case it is possible that a further interceptor could emit its
26 // Interceptors are called in the order that they are added.
30 // Two types of interceptors are provided: Funcs and Methods
32 // Func Interceptors may apply to any / all Controllers.
34 // func example(*revel.Controller) revel.Result
36 // Method Interceptors are provided so that properties can be set on application
39 // func (c AppController) example() revel.Result
40 // func (c *AppController) example() revel.Result
42 type InterceptorFunc func(*Controller) Result
43 type InterceptorMethod interface{}
53 type InterceptTarget int
56 AllControllers InterceptTarget = iota
59 type Interception struct {
62 function InterceptorFunc
63 method InterceptorMethod
65 callable reflect.Value
70 // Invoke performs the given interception.
71 // val is a pointer to the App Controller.
72 func (i Interception) Invoke(val reflect.Value, target *reflect.Value) reflect.Value {
74 if i.function == nil {
75 // If it's an InterceptorMethod, then we have to pass in the target type.
78 // If it's an InterceptorFunc, then the type must be *Controller.
79 // We can find that by following the embedded types up the chain.
80 for val.Type() != controllerPtrType {
81 if val.Kind() == reflect.Ptr {
89 vals := i.callable.Call([]reflect.Value{arg})
93 func InterceptorFilter(c *Controller, fc []Filter) {
94 defer invokeInterceptors(FINALLY, c)
96 if err := recover(); err != nil {
97 invokeInterceptors(PANIC, c)
102 // Invoke the BEFORE interceptors and return early, if we get a result.
103 invokeInterceptors(BEFORE, c)
109 invokeInterceptors(AFTER, c)
112 func invokeInterceptors(when When, c *Controller) {
114 app = reflect.ValueOf(c.AppController)
118 for _, intc := range getInterceptors(when, app) {
119 resultValue := intc.Interceptor.Invoke(app, &intc.Target)
120 if !resultValue.IsNil() {
121 result = resultValue.Interface().(Result)
123 if when == BEFORE && result != nil {
133 var interceptors []*Interception
135 // InterceptFunc installs a general interceptor.
136 // This can be applied to any Controller.
137 // It must have the signature of:
138 // func example(c *revel.Controller) revel.Result
139 func InterceptFunc(intc InterceptorFunc, when When, target interface{}) {
140 interceptors = append(interceptors, &Interception{
143 callable: reflect.ValueOf(intc),
144 target: reflect.TypeOf(target),
145 interceptAll: target == AllControllers,
149 // InterceptMethod installs an interceptor method that applies to its own Controller.
150 // func (c AppController) example() revel.Result
151 // func (c *AppController) example() revel.Result
152 func InterceptMethod(intc InterceptorMethod, when When) {
153 methodType := reflect.TypeOf(intc)
154 if methodType.Kind() != reflect.Func || methodType.NumOut() != 1 || methodType.NumIn() != 1 {
155 log.Fatalln("Interceptor method should have signature like",
156 "'func (c *AppController) example() revel.Result' but was", methodType)
159 interceptors = append(interceptors, &Interception{
162 callable: reflect.ValueOf(intc),
163 target: methodType.In(0),
167 // This item is used to provide a sortable set to be returned to the caller. This ensures calls order is maintained
169 type interceptorItem struct {
170 Interceptor *Interception
174 type interceptorItemList []*interceptorItem
176 func (a interceptorItemList) Len() int { return len(a) }
177 func (a interceptorItemList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
178 func (a interceptorItemList) Less(i, j int) bool { return a[i].Level < a[j].Level }
180 type reverseInterceptorItemList []*interceptorItem
182 func (a reverseInterceptorItemList) Len() int { return len(a) }
183 func (a reverseInterceptorItemList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
184 func (a reverseInterceptorItemList) Less(i, j int) bool { return a[i].Level > a[j].Level }
185 func getInterceptors(when When, val reflect.Value) interceptorItemList {
186 result := interceptorItemList{}
187 for _, intc := range interceptors {
188 if intc.When != when {
192 level, target := findTarget(val, intc.target)
193 if intc.interceptAll || target.IsValid() {
194 result = append(result, &interceptorItem{intc, target, level})
198 // Before is deepest to highest
202 // Everything else is highest to deepest
203 sort.Sort(reverseInterceptorItemList(result))
208 // Find the value of the target, starting from val and including embedded types.
209 // Also, convert between any difference in indirection.
210 // If the target couldn't be found, the returned Value will have IsValid() == false
211 func findTarget(val reflect.Value, target reflect.Type) (int, reflect.Value) {
212 // Look through the embedded types (until we reach the *revel.Controller at the top).
213 valueQueue := []reflect.Value{val}
215 for len(valueQueue) > 0 {
216 val, valueQueue = valueQueue[0], valueQueue[1:]
218 // Check if val is of a similar type to the target type.
219 if val.Type() == target {
222 if val.Kind() == reflect.Ptr && val.Elem().Type() == target {
223 return level, val.Elem()
225 if target.Kind() == reflect.Ptr && target.Elem() == val.Type() {
226 return level, val.Addr()
229 // If we reached the *revel.Controller and still didn't find what we were
230 // looking for, give up.
231 if val.Type() == controllerPtrType {
235 // Else, add each anonymous field to the queue.
236 if val.Kind() == reflect.Ptr {
240 for i := 0; i < val.NumField(); i++ {
241 if val.Type().Field(i).Anonymous {
242 valueQueue = append(valueQueue, val.Field(i))
248 return level, reflect.Value{}