diff options
| author | Felix Hanley <felix@userspace.com.au> | 2016-12-05 08:16:58 +0000 |
|---|---|---|
| committer | Felix Hanley <felix@userspace.com.au> | 2016-12-05 08:16:58 +0000 |
| commit | b049991a46a2f619344bd6e915745703864d0134 (patch) | |
| tree | ec1d3897a7b69c7c63a3774d4c42dfbb8cb46432 /vendor/github.com/zenazn/goji/web | |
| parent | e1c3d6f7db06d592538f1162b2b6b9f1b6efa330 (diff) | |
| download | go-dict2rest-b049991a46a2f619344bd6e915745703864d0134.tar.gz go-dict2rest-b049991a46a2f619344bd6e915745703864d0134.tar.bz2 | |
Diffstat (limited to 'vendor/github.com/zenazn/goji/web')
29 files changed, 2300 insertions, 0 deletions
diff --git a/vendor/github.com/zenazn/goji/web/atomic.go b/vendor/github.com/zenazn/goji/web/atomic.go new file mode 100644 index 0000000..795d8e5 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/atomic.go @@ -0,0 +1,18 @@ +// +build !appengine + +package web + +import ( + "sync/atomic" + "unsafe" +) + +func (rt *router) getMachine() *routeMachine { + ptr := (*unsafe.Pointer)(unsafe.Pointer(&rt.machine)) + sm := (*routeMachine)(atomic.LoadPointer(ptr)) + return sm +} +func (rt *router) setMachine(m *routeMachine) { + ptr := (*unsafe.Pointer)(unsafe.Pointer(&rt.machine)) + atomic.StorePointer(ptr, unsafe.Pointer(m)) +} diff --git a/vendor/github.com/zenazn/goji/web/atomic_appengine.go b/vendor/github.com/zenazn/goji/web/atomic_appengine.go new file mode 100644 index 0000000..027127a --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/atomic_appengine.go @@ -0,0 +1,14 @@ +// +build appengine + +package web + +func (rt *router) getMachine() *routeMachine { + rt.lock.Lock() + defer rt.lock.Unlock() + return rt.machine +} + +// We always hold the lock when calling setMachine. +func (rt *router) setMachine(m *routeMachine) { + rt.machine = m +} diff --git a/vendor/github.com/zenazn/goji/web/bytecode_compiler.go b/vendor/github.com/zenazn/goji/web/bytecode_compiler.go new file mode 100644 index 0000000..b6f52b1 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/bytecode_compiler.go @@ -0,0 +1,265 @@ +package web + +/* +This file implements a fast router by encoding a list of routes first into a +pseudo-trie, then encoding that pseudo-trie into a state machine realized as +a routing bytecode. + +The most interesting part of this router is not its speed (it is quite fast), +but the guarantees it provides. In a naive router, routes are examined one after +another until a match is found, and this is the programming model we want to +support. For any given request ("GET /hello/carl"), there is a list of +"plausible" routes: routes which match the method ("GET"), and which have a +prefix that is a prefix of the requested path ("/" and "/hello/", for instance, +but not "/foobar"). Patterns also have some amount of arbitrary code associated +with them, which tells us whether or not the route matched. Just like the naive +router, our goal is to call each plausible pattern, in the order they were +added, until we find one that matches. The "fast" part here is being smart about +which non-plausible routes we can skip. + +First, we sort routes using a pairwise comparison function: sorting occurs as +normal on the prefixes, with the caveat that a route may not be moved past a +route that might also match the same string. Among other things, this means +we're forced to use particularly dumb sorting algorithms, but it only has to +happen once, and there probably aren't even that many routes to begin with. This +logic appears inline in the router's handle() function. + +We then build a pseudo-trie from the sorted list of routes. It's not quite a +normal trie because there are certain routes we cannot reorder around other +routes (since we're providing identical semantics to the naive router), but it's +close enough and the basic idea is the same. + +Finally, we lower this psuedo-trie from its tree representation to a state +machine bytecode. The bytecode is pretty simple: it contains up to three bytes, +a choice of a bunch of flags, and an index. The state machine is pretty simple: +if the bytes match the next few bytes after the cursor, the instruction matches, +and the state machine advances to the next instruction. If it does not match, it +jumps to the instruction at the index. Various flags modify this basic behavior, +the documentation for which can be found below. + +The thing we're optimizing for here over pretty much everything else is memory +locality. We make an effort to lay out both the trie child selection logic and +the matching of long strings consecutively in memory, making both operations +very cheap. In fact, our matching logic isn't particularly asymptotically good, +but in practice the benefits of memory locality outweigh just about everything +else. + +Unfortunately, the code implementing all of this is pretty bad (both inefficient +and hard to read). Maybe someday I'll come and take a second pass at it. +*/ +type state struct { + mode smMode + bs [3]byte + i int32 +} +type stateMachine []state + +type smMode uint8 + +// Many combinations of smModes don't make sense, but since this is interal to +// the library I don't feel like documenting them. +const ( + // The two low bits of the mode are used as a length of how many bytes + // of bs are used. If the length is 0, the node is treated as a + // wildcard. + smLengthMask smMode = 3 +) + +const ( + // Jump to the given index on a match. Ordinarily, the state machine + // will jump to the state given by the index if the characters do not + // match. + smJumpOnMatch smMode = 4 << iota + // The index is the index of a route to try. If running the route fails, + // the state machine advances by one. + smRoute + // Reset the state machine's cursor into the input string to the state's + // index value. + smSetCursor + // If this bit is set, the machine transitions into a non-accepting + // state if it matches. + smFail +) + +type trie struct { + prefix string + children []trieSegment +} + +// A trie segment is a route matching this point (or -1), combined with a list +// of trie children that follow that route. +type trieSegment struct { + route int + children []trie +} + +func buildTrie(routes []route, dp, dr int) trie { + var t trie + ts := trieSegment{-1, nil} + for i, r := range routes { + if len(r.prefix) != dp { + continue + } + + if i == 0 { + ts.route = 0 + } else { + subroutes := routes[ts.route+1 : i] + ts.children = buildTrieSegment(subroutes, dp, dr+ts.route+1) + t.children = append(t.children, ts) + ts = trieSegment{i, nil} + } + } + + // This could be a little DRYer... + subroutes := routes[ts.route+1:] + ts.children = buildTrieSegment(subroutes, dp, dr+ts.route+1) + t.children = append(t.children, ts) + + for i := range t.children { + if t.children[i].route != -1 { + t.children[i].route += dr + } + } + + return t +} + +func commonPrefix(s1, s2 string) string { + if len(s1) > len(s2) { + return commonPrefix(s2, s1) + } + for i := 0; i < len(s1); i++ { + if s1[i] != s2[i] { + return s1[:i] + } + } + return s1 +} + +func buildTrieSegment(routes []route, dp, dr int) []trie { + if len(routes) == 0 { + return nil + } + var tries []trie + + start := 0 + p := routes[0].prefix[dp:] + for i := 1; i < len(routes); i++ { + ip := routes[i].prefix[dp:] + cp := commonPrefix(p, ip) + if len(cp) == 0 { + t := buildTrie(routes[start:i], dp+len(p), dr+start) + t.prefix = p + tries = append(tries, t) + start = i + p = ip + } else { + p = cp + } + } + + t := buildTrie(routes[start:], dp+len(p), dr+start) + t.prefix = p + return append(tries, t) +} + +// This is a bit confusing, since the encode method on a trie deals exclusively +// with trieSegments (i.e., its children), and vice versa. +// +// These methods are also hideously inefficient, both in terms of memory usage +// and algorithmic complexity. If it ever becomes a problem, maybe we can do +// something smarter than stupid O(N^2) appends, but to be honest, I bet N is +// small (it almost always is :P) and we only do it once at boot anyways. + +func (t trie) encode(dp, off int) stateMachine { + ms := make([]stateMachine, len(t.children)) + subs := make([]stateMachine, len(t.children)) + var l, msl, subl int + + for i, ts := range t.children { + ms[i], subs[i] = ts.encode(dp, 0) + msl += len(ms[i]) + l += len(ms[i]) + len(subs[i]) + } + + l++ + + m := make(stateMachine, 0, l) + for i, mm := range ms { + for j := range mm { + if mm[j].mode&(smRoute|smSetCursor) != 0 { + continue + } + + mm[j].i += int32(off + msl + subl + 1) + } + m = append(m, mm...) + subl += len(subs[i]) + } + + m = append(m, state{mode: smJumpOnMatch, i: -1}) + + msl = 0 + for i, sub := range subs { + msl += len(ms[i]) + for j := range sub { + if sub[j].mode&(smRoute|smSetCursor) != 0 { + continue + } + if sub[j].i == -1 { + sub[j].i = int32(off + msl) + } else { + sub[j].i += int32(off + len(m)) + } + } + m = append(m, sub...) + } + + return m +} + +func (ts trieSegment) encode(dp, off int) (me stateMachine, sub stateMachine) { + o := 1 + if ts.route != -1 { + o++ + } + me = make(stateMachine, len(ts.children)+o) + + me[0] = state{mode: smSetCursor, i: int32(dp)} + if ts.route != -1 { + me[1] = state{mode: smRoute, i: int32(ts.route)} + } + + for i, t := range ts.children { + p := t.prefix + + bc := copy(me[i+o].bs[:], p) + me[i+o].mode = smMode(bc) | smJumpOnMatch + me[i+o].i = int32(off + len(sub)) + + for len(p) > bc { + var bs [3]byte + p = p[bc:] + bc = copy(bs[:], p) + sub = append(sub, state{bs: bs, mode: smMode(bc), i: -1}) + } + + sub = append(sub, t.encode(dp+len(t.prefix), off+len(sub))...) + } + return +} + +func compile(routes []route) stateMachine { + if len(routes) == 0 { + return nil + } + t := buildTrie(routes, 0, 0) + m := t.encode(0, 0) + for i := range m { + if m[i].i == -1 { + m[i].mode = m[i].mode | smFail + } + } + return m +} diff --git a/vendor/github.com/zenazn/goji/web/bytecode_runner.go b/vendor/github.com/zenazn/goji/web/bytecode_runner.go new file mode 100644 index 0000000..c32b16a --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/bytecode_runner.go @@ -0,0 +1,83 @@ +package web + +import "net/http" + +type routeMachine struct { + sm stateMachine + routes []route +} + +func matchRoute(route route, m method, ms *method, r *http.Request, c *C) bool { + if !route.pattern.Match(r, c) { + return false + } + *ms |= route.method + + if route.method&m != 0 { + route.pattern.Run(r, c) + return true + } + return false +} + +func (rm routeMachine) route(c *C, w http.ResponseWriter, r *http.Request) (method, *route) { + m := httpMethod(r.Method) + var methods method + p := r.URL.Path + + if len(rm.sm) == 0 { + return methods, nil + } + + var i int + for { + sm := rm.sm[i].mode + if sm&smSetCursor != 0 { + si := rm.sm[i].i + p = r.URL.Path[si:] + i++ + continue + } + + length := int(sm & smLengthMask) + match := false + if length <= len(p) { + bs := rm.sm[i].bs + switch length { + case 3: + if p[2] != bs[2] { + break + } + fallthrough + case 2: + if p[1] != bs[1] { + break + } + fallthrough + case 1: + if p[0] != bs[0] { + break + } + fallthrough + case 0: + p = p[length:] + match = true + } + } + + if match && sm&smRoute != 0 { + si := rm.sm[i].i + if matchRoute(rm.routes[si], m, &methods, r, c) { + return 0, &rm.routes[si] + } + i++ + } else if match != (sm&smJumpOnMatch == 0) { + if sm&smFail != 0 { + return methods, nil + } + i = int(rm.sm[i].i) + } else { + i++ + } + } +} diff --git a/vendor/github.com/zenazn/goji/web/chanpool.go b/vendor/github.com/zenazn/goji/web/chanpool.go new file mode 100644 index 0000000..6c53c74 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/chanpool.go @@ -0,0 +1,31 @@ +// +build !go1.3 + +package web + +// This is an alternate implementation of Go 1.3's sync.Pool. + +// Maximum size of the pool of spare middleware stacks +const cPoolSize = 32 + +type cPool chan *cStack + +func makeCPool() *cPool { + p := make(cPool, cPoolSize) + return &p +} + +func (c cPool) alloc() *cStack { + select { + case cs := <-c: + return cs + default: + return nil + } +} + +func (c cPool) release(cs *cStack) { + select { + case c <- cs: + default: + } +} diff --git a/vendor/github.com/zenazn/goji/web/cpool.go b/vendor/github.com/zenazn/goji/web/cpool.go new file mode 100644 index 0000000..59f8764 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/cpool.go @@ -0,0 +1,23 @@ +// +build go1.3 + +package web + +import "sync" + +type cPool sync.Pool + +func makeCPool() *cPool { + return &cPool{} +} + +func (c *cPool) alloc() *cStack { + cs := (*sync.Pool)(c).Get() + if cs == nil { + return nil + } + return cs.(*cStack) +} + +func (c *cPool) release(cs *cStack) { + (*sync.Pool)(c).Put(cs) +} diff --git a/vendor/github.com/zenazn/goji/web/func_equal.go b/vendor/github.com/zenazn/goji/web/func_equal.go new file mode 100644 index 0000000..9c8f7cb --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/func_equal.go @@ -0,0 +1,32 @@ +package web + +import ( + "reflect" +) + +/* +This is more than a little sketchtacular. Go's rules for function pointer +equality are pretty restrictive: nil function pointers always compare equal, and +all other pointer types never do. However, this is pretty limiting: it means +that we can't let people reference the middleware they've given us since we have +no idea which function they're referring to. + +To get better data out of Go, we sketch on the representation of interfaces. We +happen to know that interfaces are pairs of pointers: one to the real data, one +to data about the type. Therefore, two interfaces, including two function +interface{}'s, point to exactly the same objects iff their interface +representations are identical. And it turns out this is sufficient for our +purposes. + +If you're curious, you can read more about the representation of functions here: +http://golang.org/s/go11func +We're in effect comparing the pointers of the indirect layer. + +This function also works on non-function values. +*/ +func funcEqual(a, b interface{}) bool { + av := reflect.ValueOf(&a).Elem() + bv := reflect.ValueOf(&b).Elem() + + return av.InterfaceData() == bv.InterfaceData() +} diff --git a/vendor/github.com/zenazn/goji/web/handler.go b/vendor/github.com/zenazn/goji/web/handler.go new file mode 100644 index 0000000..746c9f0 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/handler.go @@ -0,0 +1,42 @@ +package web + +import ( + "log" + "net/http" +) + +const unknownHandler = `Unknown handler type %T. See http://godoc.org/github.com/zenazn/goji/web#HandlerType for a list of acceptable types.` + +type netHTTPHandlerWrap struct{ http.Handler } +type netHTTPHandlerFuncWrap struct { + fn func(http.ResponseWriter, *http.Request) +} +type handlerFuncWrap struct { + fn func(C, http.ResponseWriter, *http.Request) +} + +func (h netHTTPHandlerWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { + h.Handler.ServeHTTP(w, r) +} +func (h netHTTPHandlerFuncWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { + h.fn(w, r) +} +func (h handlerFuncWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { + h.fn(c, w, r) +} + +func parseHandler(h HandlerType) Handler { + switch f := h.(type) { + case func(c C, w http.ResponseWriter, r *http.Request): + return handlerFuncWrap{f} + case func(w http.ResponseWriter, r *http.Request): + return netHTTPHandlerFuncWrap{f} + case Handler: + return f + case http.Handler: + return netHTTPHandlerWrap{f} + default: + log.Fatalf(unknownHandler, h) + panic("log.Fatalf does not return") + } +} diff --git a/vendor/github.com/zenazn/goji/web/match.go b/vendor/github.com/zenazn/goji/web/match.go new file mode 100644 index 0000000..1a44144 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/match.go @@ -0,0 +1,66 @@ +package web + +// The key used to store route Matches in the Goji environment. If this key is +// present in the environment and contains a value of type Match, routing will +// not be performed, and the Match's Handler will be used instead. +const MatchKey = "goji.web.Match" + +// Match is the type of routing matches. It is inserted into C.Env under +// MatchKey when the Mux.Router middleware is invoked. If MatchKey is present at +// route dispatch time, the Handler of the corresponding Match will be called +// instead of performing routing as usual. +// +// By computing a Match and inserting it into the Goji environment as part of a +// middleware stack (see Mux.Router, for instance), it is possible to customize +// Goji's routing behavior or replace it entirely. +type Match struct { + // Pattern is the Pattern that matched during routing. Will be nil if no + // route matched (Handler will be set to the Mux's NotFound handler) + Pattern Pattern + // The Handler corresponding to the matched pattern. + Handler Handler +} + +// GetMatch returns the Match stored in the Goji environment, or an empty Match +// if none exists (valid Matches always have a Handler property). +func GetMatch(c C) Match { + if c.Env == nil { + return Match{} + } + mi, ok := c.Env[MatchKey] + if !ok { + return Match{} + } + if m, ok := mi.(Match); ok { + return m + } + return Match{} +} + +// RawPattern returns the PatternType that was originally passed to ParsePattern +// or any of the HTTP method functions (Get, Post, etc.). +func (m Match) RawPattern() PatternType { + switch v := m.Pattern.(type) { + case regexpPattern: + return v.re + case stringPattern: + return v.raw + default: + return v + } +} + +// RawHandler returns the HandlerType that was originally passed to the HTTP +// method functions (Get, Post, etc.). +func (m Match) RawHandler() HandlerType { + switch v := m.Handler.(type) { + case netHTTPHandlerWrap: + return v.Handler + case handlerFuncWrap: + return v.fn + case netHTTPHandlerFuncWrap: + return v.fn + default: + return v + } +} 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 +} diff --git a/vendor/github.com/zenazn/goji/web/middleware/envinit.go b/vendor/github.com/zenazn/goji/web/middleware/envinit.go new file mode 100644 index 0000000..ae3b683 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/envinit.go @@ -0,0 +1,27 @@ +package middleware + +import ( + "net/http" + + "github.com/zenazn/goji/web" +) + +type envInit struct { + c *web.C + h http.Handler +} + +func (e envInit) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if e.c.Env == nil { + e.c.Env = make(map[interface{}]interface{}) + } + e.h.ServeHTTP(w, r) +} + +// EnvInit is a middleware that allocates an environment map if it is nil. While +// it's impossible in general to ensure that Env is never nil in a middleware +// stack, in most common cases placing this middleware at the top of the stack +// will eliminate the need for repetative nil checks. +func EnvInit(c *web.C, h http.Handler) http.Handler { + return envInit{c, h} +} diff --git a/vendor/github.com/zenazn/goji/web/middleware/logger.go b/vendor/github.com/zenazn/goji/web/middleware/logger.go new file mode 100644 index 0000000..8bbcac8 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/logger.go @@ -0,0 +1,92 @@ +package middleware + +import ( + "bytes" + "log" + "net/http" + "time" + + "github.com/zenazn/goji/web" + "github.com/zenazn/goji/web/mutil" +) + +// Logger is a middleware that logs the start and end of each request, along +// with some useful data about what was requested, what the response status was, +// and how long it took to return. When standard output is a TTY, Logger will +// print in color, otherwise it will print in black and white. +// +// Logger prints a request ID if one is provided. +// +// Logger has been designed explicitly to be Good Enough for use in small +// applications and for people just getting started with Goji. It is expected +// that applications will eventually outgrow this middleware and replace it with +// a custom request logger, such as one that produces machine-parseable output, +// outputs logs to a different service (e.g., syslog), or formats lines like +// those printed elsewhere in the application. +func Logger(c *web.C, h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + reqID := GetReqID(*c) + + printStart(reqID, r) + + lw := mutil.WrapWriter(w) + + t1 := time.Now() + h.ServeHTTP(lw, r) + + if lw.Status() == 0 { + lw.WriteHeader(http.StatusOK) + } + t2 := time.Now() + + printEnd(reqID, lw, t2.Sub(t1)) + } + + return http.HandlerFunc(fn) +} + +func printStart(reqID string, r *http.Request) { + var buf bytes.Buffer + + if reqID != "" { + cW(&buf, bBlack, "[%s] ", reqID) + } + buf.WriteString("Started ") + cW(&buf, bMagenta, "%s ", r.Method) + cW(&buf, nBlue, "%q ", r.URL.String()) + buf.WriteString("from ") + buf.WriteString(r.RemoteAddr) + + log.Print(buf.String()) +} + +func printEnd(reqID string, w mutil.WriterProxy, dt time.Duration) { + var buf bytes.Buffer + + if reqID != "" { + cW(&buf, bBlack, "[%s] ", reqID) + } + buf.WriteString("Returning ") + status := w.Status() + if status < 200 { + cW(&buf, bBlue, "%03d", status) + } else if status < 300 { + cW(&buf, bGreen, "%03d", status) + } else if status < 400 { + cW(&buf, bCyan, "%03d", status) + } else if status < 500 { + cW(&buf, bYellow, "%03d", status) + } else { + cW(&buf, bRed, "%03d", status) + } + buf.WriteString(" in ") + if dt < 500*time.Millisecond { + cW(&buf, nGreen, "%s", dt) + } else if dt < 5*time.Second { + cW(&buf, nYellow, "%s", dt) + } else { + cW(&buf, nRed, "%s", dt) + } + + log.Print(buf.String()) +} diff --git a/vendor/github.com/zenazn/goji/web/middleware/middleware.go b/vendor/github.com/zenazn/goji/web/middleware/middleware.go new file mode 100644 index 0000000..23cfde2 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/middleware.go @@ -0,0 +1,4 @@ +/* +Package middleware provides several standard middleware implementations. +*/ +package middleware diff --git a/vendor/github.com/zenazn/goji/web/middleware/nocache.go b/vendor/github.com/zenazn/goji/web/middleware/nocache.go new file mode 100644 index 0000000..ae3d260 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/nocache.go @@ -0,0 +1,55 @@ +package middleware + +import ( + "net/http" + "time" +) + +// Unix epoch time +var epoch = time.Unix(0, 0).Format(time.RFC1123) + +// Taken from https://github.com/mytrile/nocache +var noCacheHeaders = map[string]string{ + "Expires": epoch, + "Cache-Control": "no-cache, private, max-age=0", + "Pragma": "no-cache", + "X-Accel-Expires": "0", +} + +var etagHeaders = []string{ + "ETag", + "If-Modified-Since", + "If-Match", + "If-None-Match", + "If-Range", + "If-Unmodified-Since", +} + +// NoCache is a simple piece of middleware that sets a number of HTTP headers to prevent +// a router (or subrouter) from being cached by an upstream proxy and/or client. +// +// As per http://wiki.nginx.org/HttpProxyModule - NoCache sets: +// Expires: Thu, 01 Jan 1970 00:00:00 UTC +// Cache-Control: no-cache, private, max-age=0 +// X-Accel-Expires: 0 +// Pragma: no-cache (for HTTP/1.0 proxies/clients) +func NoCache(h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + + // Delete any ETag headers that may have been set + for _, v := range etagHeaders { + if r.Header.Get(v) != "" { + r.Header.Del(v) + } + } + + // Set our NoCache headers + for k, v := range noCacheHeaders { + w.Header().Set(k, v) + } + + h.ServeHTTP(w, r) + } + + return http.HandlerFunc(fn) +} diff --git a/vendor/github.com/zenazn/goji/web/middleware/options.go b/vendor/github.com/zenazn/goji/web/middleware/options.go new file mode 100644 index 0000000..4bdce5f --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/options.go @@ -0,0 +1,97 @@ +package middleware + +import ( + "net/http" + "strings" + + "github.com/zenazn/goji/web" +) + +type autoOptionsState int + +const ( + aosInit autoOptionsState = iota + aosHeaderWritten + aosProxying +) + +// I originally used an httptest.ResponseRecorder here, but package httptest +// adds a flag which I'm not particularly eager to expose. This is essentially a +// ResponseRecorder that has been specialized for the purpose at hand to avoid +// the httptest dependency. +type autoOptionsProxy struct { + w http.ResponseWriter + c *web.C + state autoOptionsState +} + +func (p *autoOptionsProxy) Header() http.Header { + return p.w.Header() +} + +func (p *autoOptionsProxy) Write(buf []byte) (int, error) { + switch p.state { + case aosInit: + p.state = aosHeaderWritten + case aosProxying: + return len(buf), nil + } + return p.w.Write(buf) +} + +func (p *autoOptionsProxy) WriteHeader(code int) { + methods := getValidMethods(*p.c) + switch p.state { + case aosInit: + if methods != nil && code == http.StatusNotFound { + p.state = aosProxying + break + } + p.state = aosHeaderWritten + fallthrough + default: + p.w.WriteHeader(code) + return + } + + methods = addMethod(methods, "OPTIONS") + p.w.Header().Set("Allow", strings.Join(methods, ", ")) + p.w.WriteHeader(http.StatusOK) +} + +// AutomaticOptions automatically return an appropriate "Allow" header when the +// request method is OPTIONS and the request would have otherwise been 404'd. +func AutomaticOptions(c *web.C, h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + if r.Method == "OPTIONS" { + w = &autoOptionsProxy{c: c, w: w} + } + + h.ServeHTTP(w, r) + } + + return http.HandlerFunc(fn) +} + +func getValidMethods(c web.C) []string { + if c.Env == nil { + return nil + } + v, ok := c.Env[web.ValidMethodsKey] + if !ok { + return nil + } + if methods, ok := v.([]string); ok { + return methods + } + return nil +} + +func addMethod(methods []string, method string) []string { + for _, m := range methods { + if m == method { + return methods + } + } + return append(methods, method) +} diff --git a/vendor/github.com/zenazn/goji/web/middleware/realip.go b/vendor/github.com/zenazn/goji/web/middleware/realip.go new file mode 100644 index 0000000..ae5599f --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/realip.go @@ -0,0 +1,51 @@ +package middleware + +import ( + "net/http" + "strings" +) + +var xForwardedFor = http.CanonicalHeaderKey("X-Forwarded-For") +var xRealIP = http.CanonicalHeaderKey("X-Real-IP") + +// RealIP is a middleware that sets a http.Request's RemoteAddr to the results +// of parsing either the X-Forwarded-For header or the X-Real-IP header (in that +// order). +// +// This middleware should be inserted fairly early in the middleware stack to +// ensure that subsequent layers (e.g., request loggers) which examine the +// RemoteAddr will see the intended value. +// +// You should only use this middleware if you can trust the headers passed to +// you (in particular, the two headers this middleware uses), for example +// because you have placed a reverse proxy like HAProxy or nginx in front of +// Goji. If your reverse proxies are configured to pass along arbitrary header +// values from the client, or if you use this middleware without a reverse +// proxy, malicious clients will be able to make you very sad (or, depending on +// how you're using RemoteAddr, vulnerable to an attack of some sort). +func RealIP(h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + if rip := realIP(r); rip != "" { + r.RemoteAddr = rip + } + h.ServeHTTP(w, r) + } + + return http.HandlerFunc(fn) +} + +func realIP(r *http.Request) string { + var ip string + + if xff := r.Header.Get(xForwardedFor); xff != "" { + i := strings.Index(xff, ", ") + if i == -1 { + i = len(xff) + } + ip = xff[:i] + } else if xrip := r.Header.Get(xRealIP); xrip != "" { + ip = xrip + } + + return ip +} diff --git a/vendor/github.com/zenazn/goji/web/middleware/recoverer.go b/vendor/github.com/zenazn/goji/web/middleware/recoverer.go new file mode 100644 index 0000000..43ad648 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/recoverer.go @@ -0,0 +1,44 @@ +package middleware + +import ( + "bytes" + "log" + "net/http" + "runtime/debug" + + "github.com/zenazn/goji/web" +) + +// Recoverer is a middleware that recovers from panics, logs the panic (and a +// backtrace), and returns a HTTP 500 (Internal Server Error) status if +// possible. +// +// Recoverer prints a request ID if one is provided. +func Recoverer(c *web.C, h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + reqID := GetReqID(*c) + + defer func() { + if err := recover(); err != nil { + printPanic(reqID, err) + debug.PrintStack() + http.Error(w, http.StatusText(500), 500) + } + }() + + h.ServeHTTP(w, r) + } + + return http.HandlerFunc(fn) +} + +func printPanic(reqID string, err interface{}) { + var buf bytes.Buffer + + if reqID != "" { + cW(&buf, bBlack, "[%s] ", reqID) + } + cW(&buf, bRed, "panic: %+v", err) + + log.Print(buf.String()) +} diff --git a/vendor/github.com/zenazn/goji/web/middleware/request_id.go b/vendor/github.com/zenazn/goji/web/middleware/request_id.go new file mode 100644 index 0000000..834d8e3 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/request_id.go @@ -0,0 +1,88 @@ +package middleware + +import ( + "crypto/rand" + "encoding/base64" + "fmt" + "net/http" + "os" + "strings" + "sync/atomic" + + "github.com/zenazn/goji/web" +) + +// Key to use when setting the request ID. +const RequestIDKey = "reqID" + +var prefix string +var reqid uint64 + +/* +A quick note on the statistics here: we're trying to calculate the chance that +two randomly generated base62 prefixes will collide. We use the formula from +http://en.wikipedia.org/wiki/Birthday_problem + +P[m, n] \approx 1 - e^{-m^2/2n} + +We ballpark an upper bound for $m$ by imagining (for whatever reason) a server +that restarts every second over 10 years, for $m = 86400 * 365 * 10 = 315360000$ + +For a $k$ character base-62 identifier, we have $n(k) = 62^k$ + +Plugging this in, we find $P[m, n(10)] \approx 5.75%$, which is good enough for +our purposes, and is surely more than anyone would ever need in practice -- a +process that is rebooted a handful of times a day for a hundred years has less +than a millionth of a percent chance of generating two colliding IDs. +*/ + +func init() { + hostname, err := os.Hostname() + if hostname == "" || err != nil { + hostname = "localhost" + } + var buf [12]byte + var b64 string + for len(b64) < 10 { + rand.Read(buf[:]) + b64 = base64.StdEncoding.EncodeToString(buf[:]) + b64 = strings.NewReplacer("+", "", "/", "").Replace(b64) + } + + prefix = fmt.Sprintf("%s/%s", hostname, b64[0:10]) +} + +// RequestID is a middleware that injects a request ID into the context of each +// request. A request ID is a string of the form "host.example.com/random-0001", +// where "random" is a base62 random string that uniquely identifies this go +// process, and where the last number is an atomically incremented request +// counter. +func RequestID(c *web.C, h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + if c.Env == nil { + c.Env = make(map[interface{}]interface{}) + } + myid := atomic.AddUint64(&reqid, 1) + c.Env[RequestIDKey] = fmt.Sprintf("%s-%06d", prefix, myid) + + h.ServeHTTP(w, r) + } + + return http.HandlerFunc(fn) +} + +// GetReqID returns a request ID from the given context if one is present. +// Returns the empty string if a request ID cannot be found. +func GetReqID(c web.C) string { + if c.Env == nil { + return "" + } + v, ok := c.Env[RequestIDKey] + if !ok { + return "" + } + if reqID, ok := v.(string); ok { + return reqID + } + return "" +} diff --git a/vendor/github.com/zenazn/goji/web/middleware/subrouter.go b/vendor/github.com/zenazn/goji/web/middleware/subrouter.go new file mode 100644 index 0000000..e5b0921 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/subrouter.go @@ -0,0 +1,65 @@ +package middleware + +import ( + "net/http" + + "github.com/zenazn/goji/web" +) + +type subrouter struct { + c *web.C + h http.Handler +} + +func (s subrouter) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if s.c.URLParams != nil { + path, ok := s.c.URLParams["*"] + if !ok { + path, ok = s.c.URLParams["_"] + } + if ok { + oldpath := r.URL.Path + oldmatch := web.GetMatch(*s.c) + r.URL.Path = path + if oldmatch.Handler != nil { + delete(s.c.Env, web.MatchKey) + } + + defer func() { + r.URL.Path = oldpath + + if s.c.Env == nil { + return + } + if oldmatch.Handler != nil { + s.c.Env[web.MatchKey] = oldmatch + } else { + delete(s.c.Env, web.MatchKey) + } + }() + } + } + s.h.ServeHTTP(w, r) +} + +/* +SubRouter is a helper middleware that makes writing sub-routers easier. + +If you register a sub-router under a key like "/admin/*", Goji's router will +automatically set c.URLParams["*"] to the unmatched path suffix. This middleware +will help you set the request URL's Path to this unmatched suffix, allowing you +to write sub-routers with no knowledge of what routes the parent router matches. + +Since Go's regular expressions do not allow you to create a capturing group +named "*", SubRouter also accepts the string "_". For instance, to duplicate the +semantics of the string pattern "/foo/*", you might use the regular expression +"^/foo(?P<_>/.*)$". + +This middleware is Match-aware: it will un-set any explicit routing information +contained in the Goji context in order to prevent routing loops when using +explicit routing with sub-routers. See the documentation for Mux.Router for +more. +*/ +func SubRouter(c *web.C, h http.Handler) http.Handler { + return subrouter{c, h} +} diff --git a/vendor/github.com/zenazn/goji/web/middleware/terminal.go b/vendor/github.com/zenazn/goji/web/middleware/terminal.go new file mode 100644 index 0000000..db02917 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/terminal.go @@ -0,0 +1,60 @@ +package middleware + +import ( + "bytes" + "fmt" + "os" +) + +var ( + // Normal colors + nBlack = []byte{'\033', '[', '3', '0', 'm'} + nRed = []byte{'\033', '[', '3', '1', 'm'} + nGreen = []byte{'\033', '[', '3', '2', 'm'} + nYellow = []byte{'\033', '[', '3', '3', 'm'} + nBlue = []byte{'\033', '[', '3', '4', 'm'} + nMagenta = []byte{'\033', '[', '3', '5', 'm'} + nCyan = []byte{'\033', '[', '3', '6', 'm'} + nWhite = []byte{'\033', '[', '3', '7', 'm'} + // Bright colors + bBlack = []byte{'\033', '[', '3', '0', ';', '1', 'm'} + bRed = []byte{'\033', '[', '3', '1', ';', '1', 'm'} + bGreen = []byte{'\033', '[', '3', '2', ';', '1', 'm'} + bYellow = []byte{'\033', '[', '3', '3', ';', '1', 'm'} + bBlue = []byte{'\033', '[', '3', '4', ';', '1', 'm'} + bMagenta = []byte{'\033', '[', '3', '5', ';', '1', 'm'} + bCyan = []byte{'\033', '[', '3', '6', ';', '1', 'm'} + bWhite = []byte{'\033', '[', '3', '7', ';', '1', 'm'} + + reset = []byte{'\033', '[', '0', 'm'} +) + +var isTTY bool + +func init() { + // This is sort of cheating: if stdout is a character device, we assume + // that means it's a TTY. Unfortunately, there are many non-TTY + // character devices, but fortunately stdout is rarely set to any of + // them. + // + // We could solve this properly by pulling in a dependency on + // code.google.com/p/go.crypto/ssh/terminal, for instance, but as a + // heuristic for whether to print in color or in black-and-white, I'd + // really rather not. + fi, err := os.Stdout.Stat() + if err == nil { + m := os.ModeDevice | os.ModeCharDevice + isTTY = fi.Mode()&m == m + } +} + +// colorWrite +func cW(buf *bytes.Buffer, color []byte, s string, args ...interface{}) { + if isTTY { + buf.Write(color) + } + fmt.Fprintf(buf, s, args...) + if isTTY { + buf.Write(reset) + } +} diff --git a/vendor/github.com/zenazn/goji/web/middleware/urlquery.go b/vendor/github.com/zenazn/goji/web/middleware/urlquery.go new file mode 100644 index 0000000..36c8820 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/middleware/urlquery.go @@ -0,0 +1,24 @@ +package middleware + +import ( + "github.com/zenazn/goji/web" + "net/http" +) + +// URLQueryKey is the context key for the URL Query +const URLQueryKey string = "urlquery" + +// URLQuery is a middleware to parse the URL Query parameters just once, +// and store the resulting url.Values in the context. +func URLQuery(c *web.C, h http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + if c.Env == nil { + c.Env = make(map[interface{}]interface{}) + } + c.Env[URLQueryKey] = r.URL.Query() + + h.ServeHTTP(w, r) + } + + return http.HandlerFunc(fn) +} diff --git a/vendor/github.com/zenazn/goji/web/mutil/mutil.go b/vendor/github.com/zenazn/goji/web/mutil/mutil.go new file mode 100644 index 0000000..e8d5b28 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/mutil/mutil.go @@ -0,0 +1,3 @@ +// Package mutil contains various functions that are helpful when writing http +// middleware. +package mutil diff --git a/vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go b/vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go new file mode 100644 index 0000000..9f6d776 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/mutil/writer_proxy.go @@ -0,0 +1,139 @@ +package mutil + +import ( + "bufio" + "io" + "net" + "net/http" +) + +// WriterProxy is a proxy around an http.ResponseWriter that allows you to hook +// into various parts of the response process. +type WriterProxy interface { + http.ResponseWriter + // Status returns the HTTP status of the request, or 0 if one has not + // yet been sent. + Status() int + // BytesWritten returns the total number of bytes sent to the client. + BytesWritten() int + // Tee causes the response body to be written to the given io.Writer in + // addition to proxying the writes through. Only one io.Writer can be + // tee'd to at once: setting a second one will overwrite the first. + // Writes will be sent to the proxy before being written to this + // io.Writer. It is illegal for the tee'd writer to be modified + // concurrently with writes. + Tee(io.Writer) + // Unwrap returns the original proxied target. + Unwrap() http.ResponseWriter +} + +// WrapWriter wraps an http.ResponseWriter, returning a proxy that allows you to +// hook into various parts of the response process. +func WrapWriter(w http.ResponseWriter) WriterProxy { + _, cn := w.(http.CloseNotifier) + _, fl := w.(http.Flusher) + _, hj := w.(http.Hijacker) + _, rf := w.(io.ReaderFrom) + + bw := basicWriter{ResponseWriter: w} + if cn && fl && hj && rf { + return &fancyWriter{bw} + } + if fl { + return &flushWriter{bw} + } + return &bw +} + +// basicWriter wraps a http.ResponseWriter that implements the minimal +// http.ResponseWriter interface. +type basicWriter struct { + http.ResponseWriter + wroteHeader bool + code int + bytes int + tee io.Writer +} + +func (b *basicWriter) WriteHeader(code int) { + if !b.wroteHeader { + b.code = code + b.wroteHeader = true + b.ResponseWriter.WriteHeader(code) + } +} +func (b *basicWriter) Write(buf []byte) (int, error) { + b.WriteHeader(http.StatusOK) + n, err := b.ResponseWriter.Write(buf) + if b.tee != nil { + _, err2 := b.tee.Write(buf[:n]) + // Prefer errors generated by the proxied writer. + if err == nil { + err = err2 + } + } + b.bytes += n + return n, err +} +func (b *basicWriter) maybeWriteHeader() { + if !b.wroteHeader { + b.WriteHeader(http.StatusOK) + } +} +func (b *basicWriter) Status() int { + return b.code +} +func (b *basicWriter) BytesWritten() int { + return b.bytes +} +func (b *basicWriter) Tee(w io.Writer) { + b.tee = w +} +func (b *basicWriter) Unwrap() http.ResponseWriter { + return b.ResponseWriter +} + +// fancyWriter is a writer that additionally satisfies http.CloseNotifier, +// http.Flusher, http.Hijacker, and io.ReaderFrom. It exists for the common case +// of wrapping the http.ResponseWriter that package http gives you, in order to +// make the proxied object support the full method set of the proxied object. +type fancyWriter struct { + basicWriter +} + +func (f *fancyWriter) CloseNotify() <-chan bool { + cn := f.basicWriter.ResponseWriter.(http.CloseNotifier) + return cn.CloseNotify() +} +func (f *fancyWriter) Flush() { + fl := f.basicWriter.ResponseWriter.(http.Flusher) + fl.Flush() +} +func (f *fancyWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hj := f.basicWriter.ResponseWriter.(http.Hijacker) + return hj.Hijack() +} +func (f *fancyWriter) ReadFrom(r io.Reader) (int64, error) { + if f.basicWriter.tee != nil { + return io.Copy(&f.basicWriter, r) + } + rf := f.basicWriter.ResponseWriter.(io.ReaderFrom) + f.basicWriter.maybeWriteHeader() + return rf.ReadFrom(r) +} + +var _ http.CloseNotifier = &fancyWriter{} +var _ http.Flusher = &fancyWriter{} +var _ http.Hijacker = &fancyWriter{} +var _ io.ReaderFrom = &fancyWriter{} + +type flushWriter struct { + basicWriter +} + +func (f *flushWriter) Flush() { + fl := f.basicWriter.ResponseWriter.(http.Flusher) + fl.Flush() +} + +var _ http.Flusher = &flushWriter{} diff --git a/vendor/github.com/zenazn/goji/web/mux.go b/vendor/github.com/zenazn/goji/web/mux.go new file mode 100644 index 0000000..18b9991 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/mux.go @@ -0,0 +1,213 @@ +package web + +import ( + "net/http" +) + +/* +Mux is an HTTP multiplexer, much like net/http's ServeMux. It functions as both +a middleware stack and as an HTTP router. + +Middleware provide a great abstraction for actions that must be performed on +every request, such as request logging and authentication. To append, insert, +and remove middleware, you can call the Use, Insert, and Abandon functions +respectively. + +Routes may be added using any of the HTTP verb functions (Get, Post, etc.), or +through the generic Handle function. Goji's routing algorithm is very simple: +routes are processed in the order they are added, and the first matching route +will be executed. Routes match if their HTTP method and Pattern both match. +*/ +type Mux struct { + ms mStack + rt router +} + +// New creates a new Mux without any routes or middleware. +func New() *Mux { + mux := Mux{ + ms: mStack{ + stack: make([]mLayer, 0), + pool: makeCPool(), + }, + rt: router{ + routes: make([]route, 0), + notFound: parseHandler(http.NotFound), + }, + } + mux.ms.router = &mux.rt + return &mux +} + +// ServeHTTP processes HTTP requests. Satisfies net/http.Handler. +func (m *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) { + stack := m.ms.alloc() + stack.ServeHTTP(w, r) + m.ms.release(stack) +} + +// ServeHTTPC creates a context dependent request with the given Mux. Satisfies +// the Handler interface. +func (m *Mux) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { + stack := m.ms.alloc() + stack.ServeHTTPC(c, w, r) + m.ms.release(stack) +} + +// Middleware Stack functions + +// Use appends the given middleware to the middleware stack. +// +// 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 MiddlewareType) { + m.ms.Use(middleware) +} + +// Insert inserts the given middleware immediately before a given existing +// middleware in the stack. Returns an error if "before" cannot be found in the +// current stack. +// +// 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 MiddlewareType) error { + return m.ms.Insert(middleware, before) +} + +// Abandon removes 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 MiddlewareType) error { + return m.ms.Abandon(middleware) +} + +// Router functions + +type routerMiddleware struct { + m *Mux + c *C + h http.Handler +} + +func (rm routerMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if rm.c.Env == nil { + rm.c.Env = make(map[interface{}]interface{}, 1) + } + rm.c.Env[MatchKey] = rm.m.rt.getMatch(rm.c, w, r) + rm.h.ServeHTTP(w, r) +} + +/* +Router is a middleware that performs routing and stores the resulting Match in +Goji's environment. If a routing Match is present at the end of the middleware +stack, that Match is used instead of re-routing. + +This middleware is especially useful to create post-routing middleware, e.g. a +request logger which prints which pattern or handler was selected, or an +authentication middleware which only applies to certain routes. + +If you use nested Muxes with explicit routing, you should be aware that the +explicit routing information set by an outer Mux can be picked up by an inner +Mux, inadvertently causing an infinite routing loop. If you use both explicit +routing and nested Muxes, you should be sure to unset MatchKey before the inner +Mux performs routing (or attach a Router to the inner Mux as well). +*/ +func (m *Mux) Router(c *C, h http.Handler) http.Handler { + return routerMiddleware{m, c, h} +} + +/* +Handle dispatches to the given handler when the pattern matches, regardless of +HTTP method. + +This method is commonly used to implement sub-routing: an admin application, for +instance, can expose a single handler that is attached to the main Mux by +calling Handle("/admin/*", adminHandler) or similar. Note that this function +doesn't strip this prefix from the path before forwarding it on (e.g., the +handler will see the full path, including the "/admin/" part), but this +functionality can easily be performed by an extra middleware layer. +*/ +func (m *Mux) Handle(pattern PatternType, handler HandlerType) { + m.rt.handleUntyped(pattern, mALL, handler) +} + +// Connect dispatches to the given handler when the pattern matches and the HTTP +// method is CONNECT. +func (m *Mux) Connect(pattern PatternType, handler HandlerType) { + m.rt.handleUntyped(pattern, mCONNECT, handler) +} + +// Delete dispatches to the given handler when the pattern matches and the HTTP +// method is DELETE. +func (m *Mux) Delete(pattern PatternType, handler HandlerType) { + m.rt.handleUntyped(pattern, mDELETE, handler) +} + +// Get dispatches to the given handler when the pattern matches and the HTTP +// method is GET. +// +// All GET handlers also transparently serve HEAD requests, since net/http will +// take care of all the fiddly bits for you. If you wish to provide an alternate +// implementation of HEAD, you should add a handler explicitly and place it +// above your GET handler. +func (m *Mux) Get(pattern PatternType, handler HandlerType) { + m.rt.handleUntyped(pattern, mGET|mHEAD, handler) +} + +// Head dispatches to the given handler when the pattern matches and the HTTP +// method is HEAD. +func (m *Mux) Head(pattern PatternType, handler HandlerType) { + m.rt.handleUntyped(pattern, mHEAD, handler) +} + +// Options dispatches to the given handler when the pattern matches and the HTTP +// method is OPTIONS. +func (m *Mux) Options(pattern PatternType, handler HandlerType) { + m.rt.handleUntyped(pattern, mOPTIONS, handler) +} + +// Patch dispatches to the given handler when the pattern matches and the HTTP +// method is PATCH. +func (m *Mux) Patch(pattern PatternType, handler HandlerType) { + m.rt.handleUntyped(pattern, mPATCH, handler) +} + +// Post dispatches to the given handler when the pattern matches and the HTTP +// method is POST. +func (m *Mux) Post(pattern PatternType, handler HandlerType) { + m.rt.handleUntyped(pattern, mPOST, handler) +} + +// Put dispatches to the given handler when the pattern matches and the HTTP +// method is PUT. +func (m *Mux) Put(pattern PatternType, handler HandlerType) { + m.rt.handleUntyped(pattern, mPUT, handler) +} + +// Trace dispatches to the given handler when the pattern matches and the HTTP +// method is TRACE. +func (m *Mux) Trace(pattern PatternType, handler HandlerType) { + m.rt.handleUntyped(pattern, mTRACE, handler) +} + +// NotFound sets the fallback (i.e., 404) handler for this mux. +// +// As a convenience, the context environment variable "goji.web.validMethods" +// (also available as the constant ValidMethodsKey) will be set to the list of +// HTTP methods that could have been routed had they been provided on an +// otherwise identical request. +func (m *Mux) NotFound(handler HandlerType) { + m.rt.notFound = parseHandler(handler) +} + +// Compile compiles the list of routes into bytecode. This only needs to be done +// once after all the routes have been added, and will be called automatically +// for you (at some performance cost on the first request) if you do not call it +// explicitly. +func (m *Mux) Compile() { + m.rt.compile() +} diff --git a/vendor/github.com/zenazn/goji/web/pattern.go b/vendor/github.com/zenazn/goji/web/pattern.go new file mode 100644 index 0000000..9f9fc85 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/pattern.go @@ -0,0 +1,58 @@ +package web + +import ( + "log" + "net/http" + "regexp" +) + +// A Pattern determines whether or not a given request matches some criteria. +// They are often used in routes, which are essentially (pattern, methodSet, +// handler) tuples. If the method and pattern match, the given handler is used. +// +// Built-in implementations of this interface are used to implement regular +// expression and string matching. +type Pattern interface { + // In practice, most real-world routes have a string prefix that can be + // used to quickly determine if a pattern is an eligible match. The + // router uses the result of this function to optimize away calls to the + // full Match function, which is likely much more expensive to compute. + // If your Pattern does not support prefixes, this function should + // return the empty string. + Prefix() string + // Returns true if the request satisfies the pattern. This function is + // free to examine both the request and the context to make this + // decision. Match should not modify either argument, and since it will + // potentially be called several times over the course of matching a + // request, it should be reasonably efficient. + Match(r *http.Request, c *C) bool + // Run the pattern on the request and context, modifying the context as + // necessary to bind URL parameters or other parsed state. + Run(r *http.Request, c *C) +} + +const unknownPattern = `Unknown pattern type %T. See http://godoc.org/github.com/zenazn/goji/web#PatternType for a list of acceptable types.` + +/* +ParsePattern is used internally by Goji to parse route patterns. It is exposed +publicly to make it easier to write thin wrappers around the built-in Pattern +implementations. + +ParsePattern fatally exits (using log.Fatalf) if it is passed a value of an +unexpected type (see the documentation for PatternType for a list of which types +are accepted). It is the caller's responsibility to ensure that ParsePattern is +called in a type-safe manner. +*/ +func ParsePattern(raw PatternType) Pattern { + switch v := raw.(type) { + case Pattern: + return v + case *regexp.Regexp: + return parseRegexpPattern(v) + case string: + return parseStringPattern(v) + default: + log.Fatalf(unknownPattern, v) + panic("log.Fatalf does not return") + } +} diff --git a/vendor/github.com/zenazn/goji/web/regexp_pattern.go b/vendor/github.com/zenazn/goji/web/regexp_pattern.go new file mode 100644 index 0000000..95e7e09 --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/regexp_pattern.go @@ -0,0 +1,149 @@ +package web + +import ( + "bytes" + "fmt" + "log" + "net/http" + "regexp" + "regexp/syntax" +) + +type regexpPattern struct { + re *regexp.Regexp + prefix string + names []string +} + +func (p regexpPattern) Prefix() string { + return p.prefix +} +func (p regexpPattern) Match(r *http.Request, c *C) bool { + return p.match(r, c, false) +} +func (p regexpPattern) Run(r *http.Request, c *C) { + p.match(r, c, false) +} + +func (p regexpPattern) match(r *http.Request, c *C, dryrun bool) bool { + matches := p.re.FindStringSubmatch(r.URL.Path) + if matches == nil || len(matches) == 0 { + return false + } + + if c == nil || dryrun || len(matches) == 1 { + return true + } + + if c.URLParams == nil { + c.URLParams = make(map[string]string, len(matches)-1) + } + for i := 1; i < len(matches); i++ { + c.URLParams[p.names[i]] = matches[i] + } + return true +} + +func (p regexpPattern) String() string { + return fmt.Sprintf("regexpPattern(%v)", p.re) +} + +func (p regexpPattern) Raw() *regexp.Regexp { + return p.re +} + +/* +I'm sorry, dear reader. I really am. + +The problem here is to take an arbitrary regular expression and: +1. return a regular expression that is just like it, but left-anchored, + preferring to return the original if possible. +2. determine a string literal prefix that all matches of this regular expression + have, much like regexp.Regexp.Prefix(). Unfortunately, Prefix() does not work + in the presence of anchors, so we need to write it ourselves. + +What this actually means is that we need to sketch on the internals of the +standard regexp library to forcefully extract the information we want. + +Unfortunately, regexp.Regexp hides a lot of its state, so our abstraction is +going to be pretty leaky. The biggest leak is that we blindly assume that all +regular expressions are perl-style, not POSIX. This is probably Mostly True, and +I think most users of the library probably won't be able to notice. +*/ +func sketchOnRegex(re *regexp.Regexp) (*regexp.Regexp, string) { + rawRe := re.String() + sRe, err := syntax.Parse(rawRe, syntax.Perl) + if err != nil { + log.Printf("WARN(web): unable to parse regexp %v as perl. "+ + "This route might behave unexpectedly.", re) + return re, "" + } + sRe = sRe.Simplify() + p, err := syntax.Compile(sRe) + if err != nil { + log.Printf("WARN(web): unable to compile regexp %v. This "+ + "route might behave unexpectedly.", re) + return re, "" + } + if p.StartCond()&syntax.EmptyBeginText == 0 { + // I hope doing this is always legal... + newRe, err := regexp.Compile(`\A` + rawRe) + if err != nil { + log.Printf("WARN(web): unable to create a left-"+ + "anchored regexp from %v. This route might "+ + "behave unexpectedly", re) + return re, "" + } + re = newRe + } + + // Run the regular expression more or less by hand :( + pc := uint32(p.Start) + atStart := true + i := &p.Inst[pc] + var buf bytes.Buffer +Sadness: + for { + switch i.Op { + case syntax.InstEmptyWidth: + if !atStart { + break Sadness + } + case syntax.InstCapture, syntax.InstNop: + // nop! + case syntax.InstRune, syntax.InstRune1, syntax.InstRuneAny, + syntax.InstRuneAnyNotNL: + + atStart = false + if len(i.Rune) != 1 || + syntax.Flags(i.Arg)&syntax.FoldCase != 0 { + break Sadness + } + buf.WriteRune(i.Rune[0]) + default: + break Sadness + } + pc = i.Out + i = &p.Inst[pc] + } + return re, buf.String() +} + +func parseRegexpPattern(re *regexp.Regexp) regexpPattern { + re, prefix := sketchOnRegex(re) + rnames := re.SubexpNames() + // We have to make our own copy since package regexp forbids us + // from scribbling over the slice returned by SubexpNames(). + names := make([]string, len(rnames)) + for i, rname := range rnames { + if rname == "" { + rname = fmt.Sprintf("$%d", i) + } + names[i] = rname + } + return regexpPattern{ + re: re, + prefix: prefix, + names: names, + } +} diff --git a/vendor/github.com/zenazn/goji/web/router.go b/vendor/github.com/zenazn/goji/web/router.go new file mode 100644 index 0000000..1fbc41f --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/router.go @@ -0,0 +1,154 @@ +package web + +import ( + "net/http" + "sort" + "strings" + "sync" +) + +type method int + +const ( + mCONNECT method = 1 << iota + mDELETE + mGET + mHEAD + mOPTIONS + mPATCH + mPOST + mPUT + mTRACE + // We only natively support the methods above, but we pass through other + // methods. This constant pretty much only exists for the sake of mALL. + mIDK + + mALL method = mCONNECT | mDELETE | mGET | mHEAD | mOPTIONS | mPATCH | + mPOST | mPUT | mTRACE | mIDK +) + +// The key used to communicate to the NotFound handler what methods would have +// been allowed if they'd been provided. +const ValidMethodsKey = "goji.web.ValidMethods" + +var validMethodsMap = map[string]method{ + "CONNECT": mCONNECT, + "DELETE": mDELETE, + "GET": mGET, + "HEAD": mHEAD, + "OPTIONS": mOPTIONS, + "PATCH": mPATCH, + "POST": mPOST, + "PUT": mPUT, + "TRACE": mTRACE, +} + +type route struct { + prefix string + method method + pattern Pattern + handler Handler +} + +type router struct { + lock sync.Mutex + routes []route + notFound Handler + machine *routeMachine +} + +func httpMethod(mname string) method { + if method, ok := validMethodsMap[mname]; ok { + return method + } + return mIDK +} + +func (rt *router) compile() *routeMachine { + rt.lock.Lock() + defer rt.lock.Unlock() + sm := routeMachine{ + sm: compile(rt.routes), + routes: rt.routes, + } + rt.setMachine(&sm) + return &sm +} + +func (rt *router) getMatch(c *C, w http.ResponseWriter, r *http.Request) Match { + rm := rt.getMachine() + if rm == nil { + rm = rt.compile() + } + + methods, route := rm.route(c, w, r) + if route != nil { + return Match{ + Pattern: route.pattern, + Handler: route.handler, + } + } + + if methods == 0 { + return Match{Handler: rt.notFound} + } + + var methodsList = make([]string, 0) + for mname, meth := range validMethodsMap { + if methods&meth != 0 { + methodsList = append(methodsList, mname) + } + } + sort.Strings(methodsList) + + if c.Env == nil { + c.Env = map[interface{}]interface{}{ + ValidMethodsKey: methodsList, + } + } else { + c.Env[ValidMethodsKey] = methodsList + } + return Match{Handler: rt.notFound} +} + +func (rt *router) route(c *C, w http.ResponseWriter, r *http.Request) { + match := GetMatch(*c) + if match.Handler == nil { + match = rt.getMatch(c, w, r) + } + match.Handler.ServeHTTPC(*c, w, r) +} + +func (rt *router) handleUntyped(p PatternType, m method, h HandlerType) { + rt.handle(ParsePattern(p), m, parseHandler(h)) +} + +func (rt *router) handle(p Pattern, m method, h Handler) { + rt.lock.Lock() + defer rt.lock.Unlock() + + // Calculate the sorted insertion point, because there's no reason to do + // swapping hijinks if we're already making a copy. We need to use + // bubble sort because we can only compare adjacent elements. + pp := p.Prefix() + var i int + for i = len(rt.routes); i > 0; i-- { + rip := rt.routes[i-1].prefix + if rip <= pp || strings.HasPrefix(rip, pp) { + break + } + } + + newRoutes := make([]route, len(rt.routes)+1) + copy(newRoutes, rt.routes[:i]) + newRoutes[i] = route{ + prefix: pp, + method: m, + pattern: p, + handler: h, + } + copy(newRoutes[i+1:], rt.routes[i:]) + + rt.setMachine(nil) + rt.routes = newRoutes +} diff --git a/vendor/github.com/zenazn/goji/web/string_pattern.go b/vendor/github.com/zenazn/goji/web/string_pattern.go new file mode 100644 index 0000000..aa9b33a --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/string_pattern.go @@ -0,0 +1,137 @@ +package web + +import ( + "fmt" + "net/http" + "regexp" + "strings" +) + +// stringPattern is a struct describing +type stringPattern struct { + raw string + pats []string + breaks []byte + literals []string + wildcard bool +} + +func (s stringPattern) Prefix() string { + return s.literals[0] +} +func (s stringPattern) Match(r *http.Request, c *C) bool { + return s.match(r, c, true) +} +func (s stringPattern) Run(r *http.Request, c *C) { + s.match(r, c, false) +} +func (s stringPattern) match(r *http.Request, c *C, dryrun bool) bool { + path := r.URL.Path + var matches map[string]string + if !dryrun { + if s.wildcard { + matches = make(map[string]string, len(s.pats)+1) + } else if len(s.pats) != 0 { + matches = make(map[string]string, len(s.pats)) + } + } + for i, pat := range s.pats { + sli := s.literals[i] + if !strings.HasPrefix(path, sli) { + return false + } + path = path[len(sli):] + + m := 0 + bc := s.breaks[i] + for ; m < len(path); m++ { + if path[m] == bc || path[m] == '/' { + break + } + } + if m == 0 { + // Empty strings are not matches, otherwise routes like + // "/:foo" would match the path "/" + return false + } + if !dryrun { + matches[pat] = path[:m] + } + path = path[m:] + } + // There's exactly one more literal than pat. + tail := s.literals[len(s.pats)] + if s.wildcard { + if !strings.HasPrefix(path, tail) { + return false + } + if !dryrun { + matches["*"] = path[len(tail)-1:] + } + } else if path != tail { + return false + } + + if c == nil || dryrun { + return true + } + + if c.URLParams == nil { + c.URLParams = matches + } else { + for k, v := range matches { + c.URLParams[k] = v + } + } + return true +} + +func (s stringPattern) String() string { + return fmt.Sprintf("stringPattern(%q)", s.raw) +} + +func (s stringPattern) Raw() string { + return s.raw +} + +// "Break characters" are characters that can end patterns. They are not allowed +// to appear in pattern names. "/" was chosen because it is the standard path +// separator, and "." was chosen because it often delimits file extensions. ";" +// and "," were chosen because Section 3.3 of RFC 3986 suggests their use. +const bc = "/.;," + +var patternRe = regexp.MustCompile(`[` + bc + `]:([^` + bc + `]+)`) + +func parseStringPattern(s string) stringPattern { + raw := s + var wildcard bool + if strings.HasSuffix(s, "/*") { + s = s[:len(s)-1] + wildcard = true + } + + matches := patternRe.FindAllStringSubmatchIndex(s, -1) + pats := make([]string, len(matches)) + breaks := make([]byte, len(matches)) + literals := make([]string, len(matches)+1) + n := 0 + for i, match := range matches { + a, b := match[2], match[3] + literals[i] = s[n : a-1] // Need to leave off the colon + pats[i] = s[a:b] + if b == len(s) { + breaks[i] = '/' + } else { + breaks[i] = s[b] + } + n = b + } + literals[len(matches)] = s[n:] + return stringPattern{ + raw: raw, + pats: pats, + breaks: breaks, + literals: literals, + wildcard: wildcard, + } +} diff --git a/vendor/github.com/zenazn/goji/web/web.go b/vendor/github.com/zenazn/goji/web/web.go new file mode 100644 index 0000000..21f6fcc --- /dev/null +++ b/vendor/github.com/zenazn/goji/web/web.go @@ -0,0 +1,112 @@ +/* +Package web provides a fast and flexible middleware stack and mux. + +This package attempts to solve three problems that net/http does not. First, it +allows you to specify flexible patterns, including routes with named parameters +and regular expressions. Second, it allows you to write reconfigurable +middleware stacks. And finally, it allows you to attach additional context to +requests, in a manner that can be manipulated by both compliant middleware and +handlers. +*/ +package web + +import ( + "net/http" +) + +/* +C is a request-local context object which is threaded through all compliant +middleware layers and given to the final request handler. +*/ +type C struct { + // URLParams is a map of variables extracted from the URL (typically + // from the path portion) during routing. See the documentation for the + // URL Pattern you are using (or the documentation for PatternType for + // the case of standard pattern types) for more information about how + // variables are extracted and named. + URLParams map[string]string + // Env is a free-form environment for storing request-local data. Keys + // may be arbitrary types that support equality, however package-private + // types with type-safe accessors provide a convenient way for packages + // to mediate access to their request-local data. + Env map[interface{}]interface{} +} + +// Handler is similar to net/http's http.Handler, but also accepts a Goji +// context object. +type Handler interface { + ServeHTTPC(C, http.ResponseWriter, *http.Request) +} + +// HandlerFunc is similar to net/http's http.HandlerFunc, but supports a context +// object. Implements both http.Handler and Handler. +type HandlerFunc func(C, http.ResponseWriter, *http.Request) + +// ServeHTTP implements http.Handler, allowing HandlerFunc's to be used with +// net/http and other compliant routers. When used in this way, the underlying +// function will be passed an empty context. +func (h HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) { + h(C{}, w, r) +} + +// ServeHTTPC implements Handler. +func (h HandlerFunc) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { + h(c, w, r) +} + +/* +PatternType is the type denoting Patterns and types that Goji internally +converts to Pattern (via the ParsePattern function). In order to provide an +expressive API, this type is an alias for interface{} that is named for the +purposes of documentation, however only the following concrete types are +accepted: + - types that implement Pattern + - string, which is interpreted as a Sinatra-like URL pattern. In + particular, the following syntax is recognized: + - a path segment starting with a colon will match any + string placed at that position. e.g., "/:name" will match + "/carl", binding "name" to "carl". + - a pattern ending with "/*" will match any route with that + prefix. For instance, the pattern "/u/:name/*" will match + "/u/carl/" and "/u/carl/projects/123", but not "/u/carl" + (because there is no trailing slash). In addition to any names + bound in the pattern, the special key "*" is bound to the + unmatched tail of the match, but including the leading "/". So + for the two matching examples above, "*" would be bound to "/" + and "/projects/123" respectively. + Unlike http.ServeMux's patterns, string patterns support neither the + "rooted subtree" behavior nor Host-specific routes. Users who require + either of these features are encouraged to compose package http's mux + with the mux provided by this package. + - regexp.Regexp, which is assumed to be a Perl-style regular expression + that is anchored on the left (i.e., the beginning of the string). If + your regular expression is not anchored on the left, a + hopefully-identical left-anchored regular expression will be created + and used instead. + + Capturing groups will be converted into bound URL parameters in + URLParams. If the capturing group is named, that name will be used; + otherwise the special identifiers "$1", "$2", etc. will be used. +*/ +type PatternType interface{} + +/* +HandlerType is the type of Handlers and types that Goji internally converts to +Handler. In order to provide an expressive API, this type is an alias for +interface{} that is named for the purposes of documentation, however only the +following concrete types are accepted: + - types that implement http.Handler + - types that implement Handler + - func(http.ResponseWriter, *http.Request) + - func(web.C, http.ResponseWriter, *http.Request) +*/ +type HandlerType interface{} + +/* +MiddlewareType is the type of Goji middleware. In order to provide an expressive +API, this type is an alias for interface{} that is named for the purposes of +documentation, however only the following concrete types are accepted: + - func(http.Handler) http.Handler + - func(*web.C, http.Handler) http.Handler +*/ +type MiddlewareType interface{} |
