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{}