diff --git a/web/middleware.go b/web/middleware.go index 6ebf379..db766ad 100644 --- a/web/middleware.go +++ b/web/middleware.go @@ -7,11 +7,16 @@ import ( "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 @@ -24,21 +29,11 @@ type internalRouter interface { } /* -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 perhaps a -context object. Instead of doing this on every request, let's cache fully -assembled middleware stacks (the "c" stands for "cached"). - -A lot of the complexity here (in particular the "pool" parameter, and the -behavior of release() and invalidate() below) is due to the fact that when the -middleware stack is mutated we need to create a "cache barrier," where no -cStack created before the middleware stack mutation is returned to the active -cache pool (and is therefore eligible for subsequent reuse). The way we do this -is a bit ugly: each cStack maintains a pointer to the pool it originally came -from, and will only return itself to that pool. If the mStack's pool has been -rotated since then (meaning that this cStack is invalid), it will either try -(and likely fail) to insert itself into the stale pool, or it will drop the -cStack on the floor. +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 @@ -120,11 +115,6 @@ func (m *mStack) release(cs *cStack) { cs.pool = nil } -// Append the given middleware to the middleware stack. See the documentation -// for type Mux for a list of valid middleware types. -// -// No attempt is made to enforce the uniqueness of middlewares. It is illegal to -// call this function concurrently with active requests. func (m *mStack) Use(middleware interface{}) { m.lock.Lock() defer m.lock.Unlock() @@ -132,13 +122,6 @@ func (m *mStack) Use(middleware interface{}) { m.invalidate() } -// Insert the given middleware immediately before a given existing middleware in -// the stack. See the documentation for type Mux for a list of valid middleware -// types. Returns an error if no middleware has the name given by "before." -// -// No attempt is made to enforce the uniqueness of middlewares. If the insertion -// point is ambiguous, the first (outermost) one is chosen. It is illegal to -// call this function concurrently with active requests. func (m *mStack) Insert(middleware, before interface{}) error { m.lock.Lock() defer m.lock.Unlock() @@ -156,12 +139,6 @@ func (m *mStack) Insert(middleware, before interface{}) error { return nil } -// Remove the given middleware from the middleware stack. Returns an error if -// no such middleware can be found. -// -// If the name of the middleware to delete is ambiguous, the first (outermost) -// one is chosen. It is illegal to call this function concurrently with active -// requests. func (m *mStack) Abandon(middleware interface{}) error { m.lock.Lock() defer m.lock.Unlock() diff --git a/web/mux.go b/web/mux.go index de5a032..8c38551 100644 --- a/web/mux.go +++ b/web/mux.go @@ -52,14 +52,14 @@ Handler must be one of the following types: - func(c web.C, w http.ResponseWriter, r *http.Request) */ type Mux struct { - mStack + ms mStack router } // New creates a new Mux without any routes or middleware. func New() *Mux { mux := Mux{ - mStack: mStack{ + ms: mStack{ stack: make([]mLayer, 0), pool: makeCPool(), }, @@ -68,20 +68,53 @@ func New() *Mux { notFound: parseHandler(http.NotFound), }, } - mux.mStack.router = &mux.router + mux.ms.router = &mux.router return &mux } +// ServeHTTP processes HTTP requests. It make Muxes satisfy net/http.Handler. func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { - stack := m.mStack.alloc() + stack := m.ms.alloc() stack.ServeHTTP(w, r) - m.mStack.release(stack) + m.ms.release(stack) } // ServeHTTPC creates a context dependent request with the given Mux. Satisfies // the web.Handler interface. func (m *Mux) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { - stack := m.mStack.alloc() + stack := m.ms.alloc() stack.ServeHTTPC(c, w, r) - m.mStack.release(stack) + m.ms.release(stack) +} + +// Middleware Stack functions + +// Append the given middleware to the middleware stack. See the documentation +// for type Mux for a list of valid middleware types. +// +// No attempt is made to enforce the uniqueness of middlewares. It is illegal to +// call this function concurrently with active requests. +func (m *Mux) Use(middleware interface{}) { + m.ms.Use(middleware) +} + +// Insert the given middleware immediately before a given existing middleware in +// the stack. See the documentation for type Mux for a list of valid middleware +// types. Returns an error if no middleware has the name given by "before." +// +// No attempt is made to enforce the uniqueness of middlewares. If the insertion +// point is ambiguous, the first (outermost) one is chosen. It is illegal to +// call this function concurrently with active requests. +func (m *Mux) Insert(middleware, before interface{}) error { + return m.ms.Insert(middleware, before) +} + +// Remove the given middleware from the middleware stack. Returns an error if +// no such middleware can be found. +// +// If the name of the middleware to delete is ambiguous, the first (outermost) +// one is chosen. It is illegal to call this function concurrently with active +// requests. +func (m *Mux) Abandon(middleware interface{}) error { + return m.ms.Abandon(middleware) }