Intercept

package revel

// An "interceptor" 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 (
    ALL_CONTROLLERS InterceptTarget = iota
)

type Interception struct {
    When When

}

// Perform the given interception.
// val is a pointer to the App Controller.
func (i Interception) Invoke(val 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 = findTarget(val, i.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)
}

// Install 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 == ALL_CONTROLLERS,
    })
}

// Install 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),
    })
}