--- /dev/null
+// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
+// Revel Framework source code and usage is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package revel
+
+import (
+ "log"
+ "reflect"
+ "sort"
+)
+
+// An InterceptorFunc is functionality invoked by the framework BEFORE or AFTER
+// an action.
+//
+// An interceptor may optionally return a Result (instead of nil). Depending on
+// when the interceptor was invoked, the response is different:
+// 1. BEFORE: No further interceptors are invoked, and neither is the action.
+// 2. AFTER: Further interceptors are still run.
+// In all cases, any returned Result will take the place of any existing Result.
+//
+// In the BEFORE case, that returned Result is guaranteed to be final, while
+// in the AFTER case it is possible that a further interceptor could emit its
+// own Result.
+//
+// Interceptors are called in the order that they are added.
+//
+// ***
+//
+// Two types of interceptors are provided: Funcs and Methods
+//
+// Func Interceptors may apply to any / all Controllers.
+//
+// func example(*revel.Controller) revel.Result
+//
+// Method Interceptors are provided so that properties can be set on application
+// controllers.
+//
+// func (c AppController) example() revel.Result
+// func (c *AppController) example() revel.Result
+//
+type InterceptorFunc func(*Controller) Result
+type InterceptorMethod interface{}
+type When int
+
+const (
+ BEFORE When = iota
+ AFTER
+ PANIC
+ FINALLY
+)
+
+type InterceptTarget int
+
+const (
+ AllControllers InterceptTarget = iota
+)
+
+type Interception struct {
+ When When
+
+ function InterceptorFunc
+ method InterceptorMethod
+
+ callable reflect.Value
+ target reflect.Type
+ interceptAll bool
+}
+
+// Invoke performs the given interception.
+// val is a pointer to the App Controller.
+func (i Interception) Invoke(val reflect.Value, target *reflect.Value) reflect.Value {
+ var arg reflect.Value
+ if i.function == nil {
+ // If it's an InterceptorMethod, then we have to pass in the target type.
+ arg = *target
+ } else {
+ // If it's an InterceptorFunc, then the type must be *Controller.
+ // We can find that by following the embedded types up the chain.
+ for val.Type() != controllerPtrType {
+ if val.Kind() == reflect.Ptr {
+ val = val.Elem()
+ }
+ val = val.Field(0)
+ }
+ arg = val
+ }
+
+ vals := i.callable.Call([]reflect.Value{arg})
+ return vals[0]
+}
+
+func InterceptorFilter(c *Controller, fc []Filter) {
+ defer invokeInterceptors(FINALLY, c)
+ defer func() {
+ if err := recover(); err != nil {
+ invokeInterceptors(PANIC, c)
+ panic(err)
+ }
+ }()
+
+ // Invoke the BEFORE interceptors and return early, if we get a result.
+ invokeInterceptors(BEFORE, c)
+ if c.Result != nil {
+ return
+ }
+
+ fc[0](c, fc[1:])
+ invokeInterceptors(AFTER, c)
+}
+
+func invokeInterceptors(when When, c *Controller) {
+ var (
+ app = reflect.ValueOf(c.AppController)
+ result Result
+ )
+
+ for _, intc := range getInterceptors(when, app) {
+ resultValue := intc.Interceptor.Invoke(app, &intc.Target)
+ if !resultValue.IsNil() {
+ result = resultValue.Interface().(Result)
+ }
+ if when == BEFORE && result != nil {
+ c.Result = result
+ return
+ }
+ }
+ if result != nil {
+ c.Result = result
+ }
+}
+
+var interceptors []*Interception
+
+// InterceptFunc installs a general interceptor.
+// This can be applied to any Controller.
+// It must have the signature of:
+// func example(c *revel.Controller) revel.Result
+func InterceptFunc(intc InterceptorFunc, when When, target interface{}) {
+ interceptors = append(interceptors, &Interception{
+ When: when,
+ function: intc,
+ callable: reflect.ValueOf(intc),
+ target: reflect.TypeOf(target),
+ interceptAll: target == AllControllers,
+ })
+}
+
+// InterceptMethod installs an interceptor method that applies to its own Controller.
+// func (c AppController) example() revel.Result
+// func (c *AppController) example() revel.Result
+func InterceptMethod(intc InterceptorMethod, when When) {
+ methodType := reflect.TypeOf(intc)
+ if methodType.Kind() != reflect.Func || methodType.NumOut() != 1 || methodType.NumIn() != 1 {
+ log.Fatalln("Interceptor method should have signature like",
+ "'func (c *AppController) example() revel.Result' but was", methodType)
+ }
+
+ interceptors = append(interceptors, &Interception{
+ When: when,
+ method: intc,
+ callable: reflect.ValueOf(intc),
+ target: methodType.In(0),
+ })
+}
+
+// This item is used to provide a sortable set to be returned to the caller. This ensures calls order is maintained
+//
+type interceptorItem struct {
+ Interceptor *Interception
+ Target reflect.Value
+ Level int
+}
+type interceptorItemList []*interceptorItem
+
+func (a interceptorItemList) Len() int { return len(a) }
+func (a interceptorItemList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a interceptorItemList) Less(i, j int) bool { return a[i].Level < a[j].Level }
+
+type reverseInterceptorItemList []*interceptorItem
+
+func (a reverseInterceptorItemList) Len() int { return len(a) }
+func (a reverseInterceptorItemList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a reverseInterceptorItemList) Less(i, j int) bool { return a[i].Level > a[j].Level }
+func getInterceptors(when When, val reflect.Value) interceptorItemList {
+ result := interceptorItemList{}
+ for _, intc := range interceptors {
+ if intc.When != when {
+ continue
+ }
+
+ level, target := findTarget(val, intc.target)
+ if intc.interceptAll || target.IsValid() {
+ result = append(result, &interceptorItem{intc, target, level})
+ }
+ }
+
+ // Before is deepest to highest
+ if when == BEFORE {
+ sort.Sort(result)
+ } else {
+ // Everything else is highest to deepest
+ sort.Sort(reverseInterceptorItemList(result))
+ }
+ return result
+}
+
+// Find the value of the target, starting from val and including embedded types.
+// Also, convert between any difference in indirection.
+// If the target couldn't be found, the returned Value will have IsValid() == false
+func findTarget(val reflect.Value, target reflect.Type) (int, reflect.Value) {
+ // Look through the embedded types (until we reach the *revel.Controller at the top).
+ valueQueue := []reflect.Value{val}
+ level := 0
+ for len(valueQueue) > 0 {
+ val, valueQueue = valueQueue[0], valueQueue[1:]
+
+ // Check if val is of a similar type to the target type.
+ if val.Type() == target {
+ return level, val
+ }
+ if val.Kind() == reflect.Ptr && val.Elem().Type() == target {
+ return level, val.Elem()
+ }
+ if target.Kind() == reflect.Ptr && target.Elem() == val.Type() {
+ return level, val.Addr()
+ }
+
+ // If we reached the *revel.Controller and still didn't find what we were
+ // looking for, give up.
+ if val.Type() == controllerPtrType {
+ continue
+ }
+
+ // Else, add each anonymous field to the queue.
+ if val.Kind() == reflect.Ptr {
+ val = val.Elem()
+ }
+
+ for i := 0; i < val.NumField(); i++ {
+ if val.Type().Field(i).Anonymous {
+ valueQueue = append(valueQueue, val.Field(i))
+ }
+ }
+ level--
+ }
+
+ return level, reflect.Value{}
+}