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