diff options
Diffstat (limited to 'vendor/github.com/zenazn/goji/web/middleware.go')
| -rw-r--r-- | vendor/github.com/zenazn/goji/web/middleware.go | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/vendor/github.com/zenazn/goji/web/middleware.go b/vendor/github.com/zenazn/goji/web/middleware.go new file mode 100644 index 0000000..7ec545d --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware.go @@ -0,0 +1,154 @@ +package web + +import ( + "fmt" + "log" + "net/http" + "sync" +) + +// mLayer is a single middleware stack layer. It contains a canonicalized +// middleware representation, as well as the original function as passed to us. +type mLayer struct { + fn func(*C, http.Handler) http.Handler + orig interface{} +} + +// mStack is an entire middleware stack. It contains a slice of middleware +// layers (outermost first) protected by a mutex, a cache of pre-built stack +// instances, and a final routing function. +type mStack struct { + lock sync.Mutex + stack []mLayer + pool *cPool + router internalRouter +} + +type internalRouter interface { + route(*C, http.ResponseWriter, *http.Request) +} + +/* +cStack is a cached middleware stack instance. Constructing a middleware stack +involves a lot of allocations: at the very least each layer will have to close +over the layer after (inside) it and a stack N levels deep will incur at least N +separate allocations. Instead of doing this on every request, we keep a pool of +pre-built stacks around for reuse. +*/ +type cStack struct { + C + m http.Handler + pool *cPool +} + +func (s *cStack) ServeHTTP(w http.ResponseWriter, r *http.Request) { + s.C = C{} + s.m.ServeHTTP(w, r) +} +func (s *cStack) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { + s.C = c + s.m.ServeHTTP(w, r) +} + +const unknownMiddleware = `Unknown middleware type %T. See http://godoc.org/github.com/zenazn/goji/web#MiddlewareType for a list of acceptable types.` + +func (m *mStack) appendLayer(fn interface{}) { + ml := mLayer{orig: fn} + switch f := fn.(type) { + case func(http.Handler) http.Handler: + ml.fn = func(c *C, h http.Handler) http.Handler { + return f(h) + } + case func(*C, http.Handler) http.Handler: + ml.fn = f + default: + log.Fatalf(unknownMiddleware, fn) + } + m.stack = append(m.stack, ml) +} + +func (m *mStack) findLayer(l interface{}) int { + for i, middleware := range m.stack { + if funcEqual(l, middleware.orig) { + return i + } + } + return -1 +} + +func (m *mStack) invalidate() { + m.pool = makeCPool() +} + +func (m *mStack) newStack() *cStack { + cs := cStack{} + router := m.router + + cs.m = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + router.route(&cs.C, w, r) + }) + for i := len(m.stack) - 1; i >= 0; i-- { + cs.m = m.stack[i].fn(&cs.C, cs.m) + } + + return &cs +} + +func (m *mStack) alloc() *cStack { + p := m.pool + cs := p.alloc() + if cs == nil { + cs = m.newStack() + } + + cs.pool = p + return cs +} + +func (m *mStack) release(cs *cStack) { + cs.C = C{} + if cs.pool != m.pool { + return + } + cs.pool.release(cs) + cs.pool = nil +} + +func (m *mStack) Use(middleware interface{}) { + m.lock.Lock() + defer m.lock.Unlock() + m.appendLayer(middleware) + m.invalidate() +} + +func (m *mStack) Insert(middleware, before interface{}) error { + m.lock.Lock() + defer m.lock.Unlock() + i := m.findLayer(before) + if i < 0 { + return fmt.Errorf("web: unknown middleware %v", before) + } + + m.appendLayer(middleware) + inserted := m.stack[len(m.stack)-1] + copy(m.stack[i+1:], m.stack[i:]) + m.stack[i] = inserted + + m.invalidate() + return nil +} + +func (m *mStack) Abandon(middleware interface{}) error { + m.lock.Lock() + defer m.lock.Unlock() + i := m.findLayer(middleware) + if i < 0 { + return fmt.Errorf("web: unknown middleware %v", middleware) + } + + copy(m.stack[i:], m.stack[i+1:]) + m.stack = m.stack[:len(m.stack)-1 : len(m.stack)] + + m.invalidate() + return nil +} |
