aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/zenazn/goji/web/router.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/zenazn/goji/web/router.go')
-rw-r--r--vendor/github.com/zenazn/goji/web/router.go154
1 files changed, 154 insertions, 0 deletions
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
+}