github.com/cheikhshift/buffalo@v0.9.5/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) String() string {
    38  	s := []string{}
    39  	for _, m := range ms.stack {
    40  		s = append(s, funcKey(m))
    41  	}
    42  
    43  	return strings.Join(s, "\n")
    44  }
    45  
    46  func (ms *MiddlewareStack) clone() *MiddlewareStack {
    47  	n := newMiddlewareStack()
    48  	n.stack = append(n.stack, ms.stack...)
    49  	for k, v := range ms.skips {
    50  		n.skips[k] = v
    51  	}
    52  	return n
    53  }
    54  
    55  // Clear wipes out the current middleware stack for the App/Group,
    56  // any middleware previously defined will be removed leaving an empty
    57  // middleware stack.
    58  func (ms *MiddlewareStack) Clear() {
    59  	ms.stack = []MiddlewareFunc{}
    60  	ms.skips = map[string]bool{}
    61  }
    62  
    63  // Use the specified Middleware for the App.
    64  // When defined on an `*App` the specified middleware will be
    65  // inherited by any `Group` calls that are made on that on
    66  // the App.
    67  func (ms *MiddlewareStack) Use(mw ...MiddlewareFunc) {
    68  	ms.stack = append(ms.stack, mw...)
    69  }
    70  
    71  // Skip a specified piece of middleware the specified Handlers.
    72  // This is useful for things like wrapping your application in an
    73  // authorization middleware, but skipping it for things the home
    74  // page, the login page, etc...
    75  /*
    76  	a.Middleware.Skip(Authorization, HomeHandler, LoginHandler, RegistrationHandler)
    77  */
    78  func (ms *MiddlewareStack) Skip(mw MiddlewareFunc, handlers ...Handler) {
    79  	for _, h := range handlers {
    80  		key := funcKey(mw, h)
    81  		ms.skips[key] = true
    82  	}
    83  }
    84  
    85  // Replace a piece of middleware with another piece of middleware. Great for
    86  // testing.
    87  func (ms *MiddlewareStack) Replace(mw1 MiddlewareFunc, mw2 MiddlewareFunc) {
    88  	m1k := funcKey(mw1)
    89  	stack := []MiddlewareFunc{}
    90  	for _, mw := range ms.stack {
    91  		if funcKey(mw) == m1k {
    92  			stack = append(stack, mw2)
    93  		} else {
    94  			stack = append(stack, mw)
    95  		}
    96  	}
    97  	ms.stack = stack
    98  }
    99  
   100  func (ms *MiddlewareStack) handler(info RouteInfo) Handler {
   101  	h := info.Handler
   102  	if len(ms.stack) > 0 {
   103  		mh := func(_ Handler) Handler {
   104  			return h
   105  		}
   106  
   107  		tstack := []MiddlewareFunc{mh}
   108  
   109  		sl := len(ms.stack) - 1
   110  		for i := sl; i >= 0; i-- {
   111  			mw := ms.stack[i]
   112  			key := funcKey(mw, info)
   113  			if !ms.skips[key] {
   114  				tstack = append(tstack, mw)
   115  			}
   116  		}
   117  
   118  		for _, mw := range tstack {
   119  			h = mw(h)
   120  		}
   121  		return h
   122  	}
   123  	return h
   124  }
   125  
   126  func newMiddlewareStack(mws ...MiddlewareFunc) *MiddlewareStack {
   127  	return &MiddlewareStack{
   128  		stack: mws,
   129  		skips: map[string]bool{},
   130  	}
   131  }
   132  
   133  func funcKey(funcs ...interface{}) string {
   134  	names := []string{}
   135  	for _, f := range funcs {
   136  		if n, ok := f.(RouteInfo); ok {
   137  			names = append(names, n.HandlerName)
   138  			continue
   139  		}
   140  		rv := reflect.ValueOf(f)
   141  		ptr := rv.Pointer()
   142  		if n, ok := keyMap[ptr]; ok {
   143  			names = append(names, n)
   144  			continue
   145  		}
   146  		n := ptrName(ptr)
   147  		keyMap[ptr] = n
   148  		names = append(names, n)
   149  	}
   150  	return strings.Join(names, "/")
   151  }
   152  
   153  func ptrName(ptr uintptr) string {
   154  	fnc := runtime.FuncForPC(ptr)
   155  	n := fnc.Name()
   156  
   157  	n = strings.Replace(n, "-fm", "", 1)
   158  	n = strings.Replace(n, "(", "", 1)
   159  	n = strings.Replace(n, ")", "", 1)
   160  	return n
   161  }
   162  
   163  func setFuncKey(f interface{}, name string) {
   164  	rv := reflect.ValueOf(f)
   165  	if rv.Kind() == reflect.Ptr {
   166  		rv = rv.Elem()
   167  	}
   168  	ptr := rv.Pointer()
   169  	keyMap[ptr] = name
   170  }
   171  
   172  var keyMap = map[uintptr]string{}