github.com/conradwt/buffalo@v0.11.1/middleware.go (about)

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