Browse Source

Start moving away from struct embedding

Instead of using struct embedding to build web.Mux, start moving towards
explicit mappings. This doesn't actually change the public API of
web.Mux, but feels a little cleaner to me.

The longer-term thing here is to get rid of the functions defined on
Muxes in the public documentation that are defined on "rt *Mux", which
is just plain ugly.
Carl Jackson 11 years ago
parent
commit
2c106c958b
2 changed files with 50 additions and 40 deletions
  1. +10
    -33
      web/middleware.go
  2. +40
    -7
      web/mux.go

+ 10
- 33
web/middleware.go View File

@ -7,11 +7,16 @@ import (
"sync" "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 { type mLayer struct {
fn func(*C, http.Handler) http.Handler fn func(*C, http.Handler) http.Handler
orig interface{} 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 { type mStack struct {
lock sync.Mutex lock sync.Mutex
stack []mLayer 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 { type cStack struct {
C C
@ -120,11 +115,6 @@ func (m *mStack) release(cs *cStack) {
cs.pool = nil 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{}) { func (m *mStack) Use(middleware interface{}) {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
@ -132,13 +122,6 @@ func (m *mStack) Use(middleware interface{}) {
m.invalidate() 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 { func (m *mStack) Insert(middleware, before interface{}) error {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()
@ -156,12 +139,6 @@ func (m *mStack) Insert(middleware, before interface{}) error {
return nil 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 { func (m *mStack) Abandon(middleware interface{}) error {
m.lock.Lock() m.lock.Lock()
defer m.lock.Unlock() defer m.lock.Unlock()


+ 40
- 7
web/mux.go View File

@ -52,14 +52,14 @@ Handler must be one of the following types:
- func(c web.C, w http.ResponseWriter, r *http.Request) - func(c web.C, w http.ResponseWriter, r *http.Request)
*/ */
type Mux struct { type Mux struct {
mStack
ms mStack
router router
} }
// New creates a new Mux without any routes or middleware. // New creates a new Mux without any routes or middleware.
func New() *Mux { func New() *Mux {
mux := Mux{ mux := Mux{
mStack: mStack{
ms: mStack{
stack: make([]mLayer, 0), stack: make([]mLayer, 0),
pool: makeCPool(), pool: makeCPool(),
}, },
@ -68,20 +68,53 @@ func New() *Mux {
notFound: parseHandler(http.NotFound), notFound: parseHandler(http.NotFound),
}, },
} }
mux.mStack.router = &mux.router
mux.ms.router = &mux.router
return &mux return &mux
} }
// ServeHTTP processes HTTP requests. It make Muxes satisfy net/http.Handler.
func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
stack := m.mStack.alloc()
stack := m.ms.alloc()
stack.ServeHTTP(w, r) stack.ServeHTTP(w, r)
m.mStack.release(stack)
m.ms.release(stack)
} }
// ServeHTTPC creates a context dependent request with the given Mux. Satisfies // ServeHTTPC creates a context dependent request with the given Mux. Satisfies
// the web.Handler interface. // the web.Handler interface.
func (m *Mux) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { 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) 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)
} }

Loading…
Cancel
Save