github.com/jasonish/buffalo@v0.8.2-0.20170413145823-bacbdd415f1b/middleware.go (about)

     1  package buffalo
     2  
     3  import (
     4  	"reflect"
     5  	"runtime"
     6  	"strings"
     7  )
     8  
     9  // MiddlewareFunc defines the interface for a piece of Buffalo
    10  // Middleware.
    11  /*
    12  	func DoSomething(next Handler) Handler {
    13  		return func(c Context) error {
    14  			// do something before calling the next handler
    15  			err := next(c)
    16  			// do something after call the handler
    17  			return err
    18  		}
    19  	}
    20  */
    21  type MiddlewareFunc func(Handler) Handler
    22  
    23  // Use the specified Middleware for the App.
    24  // When defined on an `*App` the specified middleware will be
    25  // inherited by any `Group` calls that are made on that on
    26  // the App.
    27  func (a *App) Use(mw ...MiddlewareFunc) {
    28  	a.Middleware.Use(mw...)
    29  }
    30  
    31  // MiddlewareStack manages the middleware stack for an App/Group.
    32  type MiddlewareStack struct {
    33  	stack []MiddlewareFunc
    34  	skips map[string]bool
    35  }
    36  
    37  func (ms *MiddlewareStack) clone() *MiddlewareStack {
    38  	n := newMiddlewareStack()
    39  	for _, s := range ms.stack {
    40  		n.stack = append(n.stack, s)
    41  	}
    42  	for k, v := range ms.skips {
    43  		n.skips[k] = v
    44  	}
    45  	return n
    46  }
    47  
    48  // Clear wipes out the current middleware stack for the App/Group,
    49  // any middleware previously defined will be removed leaving an empty
    50  // middleware stack.
    51  func (ms *MiddlewareStack) Clear() {
    52  	ms.stack = []MiddlewareFunc{}
    53  	ms.skips = map[string]bool{}
    54  }
    55  
    56  // Use the specified Middleware for the App.
    57  // When defined on an `*App` the specified middleware will be
    58  // inherited by any `Group` calls that are made on that on
    59  // the App.
    60  func (ms *MiddlewareStack) Use(mw ...MiddlewareFunc) {
    61  	ms.stack = append(ms.stack, mw...)
    62  }
    63  
    64  // Skip a specified piece of middleware the specified Handlers.
    65  // This is useful for things like wrapping your application in an
    66  // authorization middleare, but skipping it for things the home
    67  // page, the login page, etc...
    68  /*
    69  	a.Middleware.Skip(Authorization, HomeHandler, LoginHandler, RegistrationHandler)
    70  */
    71  // NOTE: When skipping Resource handlers, you need to first declare your
    72  // resource handler as a type of buffalo.Resource for the Skip function to
    73  // properly recognize and match it.
    74  /*
    75  	// Works:
    76  	var ur Resource
    77  	cr = &carsResource{&buffaloBaseResource{}}
    78  	g = a.Resource("/cars", cr)
    79  	g.Use(SomeMiddleware)
    80  	g.Middleware.Skip(SomeMiddleware, cr.Show)
    81  
    82  	// Doesn't Work:
    83  	cr := &carsResource{&buffaloBaseResource{}}
    84  	g = a.Resource("/cars", cr)
    85  	g.Use(SomeMiddleware)
    86  	g.Middleware.Skip(SomeMiddleware, cr.Show)
    87  */
    88  func (ms *MiddlewareStack) Skip(mw MiddlewareFunc, handlers ...Handler) {
    89  	for _, h := range handlers {
    90  		key := funcKey(mw, h)
    91  		ms.skips[key] = true
    92  	}
    93  }
    94  
    95  // Replace a piece of middleware with another piece of middleware. Great for
    96  // testing.
    97  func (ms *MiddlewareStack) Replace(mw1 MiddlewareFunc, mw2 MiddlewareFunc) {
    98  	m1k := funcKey(mw1)
    99  	stack := []MiddlewareFunc{}
   100  	for _, mw := range ms.stack {
   101  		if funcKey(mw) == m1k {
   102  			stack = append(stack, mw2)
   103  		} else {
   104  			stack = append(stack, mw)
   105  		}
   106  	}
   107  	ms.stack = stack
   108  }
   109  
   110  func (ms *MiddlewareStack) handler(h Handler) Handler {
   111  	if len(ms.stack) > 0 {
   112  		mh := func(_ Handler) Handler {
   113  			return h
   114  		}
   115  
   116  		tstack := []MiddlewareFunc{mh}
   117  
   118  		sl := len(ms.stack) - 1
   119  		for i := sl; i >= 0; i-- {
   120  			mw := ms.stack[i]
   121  			key := funcKey(mw, h)
   122  			if !ms.skips[key] {
   123  				tstack = append(tstack, mw)
   124  			}
   125  		}
   126  
   127  		for _, mw := range tstack {
   128  			h = mw(h)
   129  		}
   130  		return h
   131  	}
   132  	return h
   133  }
   134  
   135  func newMiddlewareStack(mws ...MiddlewareFunc) *MiddlewareStack {
   136  	return &MiddlewareStack{
   137  		stack: mws,
   138  		skips: map[string]bool{},
   139  	}
   140  }
   141  
   142  func funcKey(funcs ...interface{}) string {
   143  	names := []string{}
   144  	for _, f := range funcs {
   145  		rv := reflect.ValueOf(f)
   146  		ptr := rv.Pointer()
   147  		if n, ok := keyMap[ptr]; ok {
   148  			names = append(names, n)
   149  			continue
   150  		}
   151  		fnc := runtime.FuncForPC(ptr)
   152  		n := fnc.Name()
   153  		keyMap[ptr] = n
   154  		names = append(names, n)
   155  	}
   156  	return strings.Join(names, "/")
   157  }
   158  
   159  func setFuncKey(f interface{}, name string) {
   160  	rv := reflect.ValueOf(f)
   161  	ptr := rv.Pointer()
   162  	keyMap[ptr] = name
   163  }
   164  
   165  var keyMap = map[uintptr]string{}