github.com/res-am/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{}