package web import ( "log" "net/http" "regexp" "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 { // Theory: most real world routes have a string prefix which is both // cheap(-ish) to test against and pretty selective. And, conveniently, // both regexes and string patterns give us this out-of-box. prefix string method method pattern Pattern handler Handler } type router struct { lock sync.Mutex routes []route notFound Handler machine *routeMachine } // 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) } func parsePattern(p interface{}) Pattern { switch p.(type) { case Pattern: return p.(Pattern) case *regexp.Regexp: return parseRegexpPattern(p.(*regexp.Regexp)) case string: return parseStringPattern(p.(string)) default: log.Fatalf("Unknown pattern type %v. Expected a web.Pattern, "+ "regexp.Regexp, or a string.", p) } panic("log.Fatalf does not return") } type netHTTPWrap struct { http.Handler } func (h netHTTPWrap) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.Handler.ServeHTTP(w, r) } func (h netHTTPWrap) ServeHTTPC(c C, w http.ResponseWriter, r *http.Request) { h.Handler.ServeHTTP(w, r) } func parseHandler(h interface{}) Handler { switch h.(type) { case Handler: return h.(Handler) case http.Handler: return netHTTPWrap{h.(http.Handler)} case func(c C, w http.ResponseWriter, r *http.Request): f := h.(func(c C, w http.ResponseWriter, r *http.Request)) return HandlerFunc(f) case func(w http.ResponseWriter, r *http.Request): f := h.(func(w http.ResponseWriter, r *http.Request)) return netHTTPWrap{http.HandlerFunc(f)} default: log.Fatalf("Unknown handler type %v. Expected a web.Handler, "+ "a http.Handler, or a function with signature func(C, "+ "http.ResponseWriter, *http.Request) or "+ "func(http.ResponseWriter, http.Request)", h) } panic("log.Fatalf does not return") } func httpMethod(mname string) method { if method, ok := validMethodsMap[mname]; ok { return method } return mIDK } 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, bool) { m := httpMethod(r.Method) var methods method p := r.URL.Path if len(rm.sm) == 0 { return methods, false } var i int for { s := rm.sm[i] if s.mode&smSetCursor != 0 { p = r.URL.Path[s.i:] i++ continue } length := int(s.mode & smLengthMask) match := length <= len(p) for j := 0; match && j < length; j++ { match = match && p[j] == s.bs[j] } if match { p = p[length:] } if match && s.mode&smRoute != 0 { if matchRoute(rm.routes[s.i], m, &methods, r, c) { rm.routes[s.i].handler.ServeHTTPC(*c, w, r) return 0, true } else { i++ } } else if (match && s.mode&smJumpOnMatch != 0) || (!match && s.mode&smJumpOnMatch == 0) { if s.mode&smFail != 0 { return methods, false } i = int(s.i) } else { i++ } } return methods, false } // Compile 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 (rt *router) Compile() { rt.lock.Lock() defer rt.lock.Unlock() sm := routeMachine{ sm: compile(rt.routes), routes: rt.routes, } rt.setMachine(&sm) } func (rt *router) route(c *C, w http.ResponseWriter, r *http.Request) { if rt.machine == nil { rt.Compile() } methods, ok := rt.getMachine().route(c, w, r) if ok { return } if methods == 0 { rt.notFound.ServeHTTPC(*c, w, r) return } 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[string]interface{}{ ValidMethodsKey: methodsList, } } else { c.Env[ValidMethodsKey] = methodsList } rt.notFound.ServeHTTPC(*c, w, r) } func (rt *router) handleUntyped(p interface{}, m method, h interface{}) { pat := parsePattern(p) rt.handle(pat, 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 } // This is a bit silly, but I've renamed the method receivers in the public // functions here "m" instead of the standard "rt", since they will eventually // be shown on the documentation for the Mux that they are included in. /* Dispatch to the given handler when the pattern matches, regardless of HTTP method. See the documentation for type Mux for a description of what types are accepted for pattern and handler. 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 (rt *router) Handle(pattern interface{}, handler interface{}) { rt.handleUntyped(pattern, mALL, handler) } // Dispatch to the given handler when the pattern matches and the HTTP method is // CONNECT. See the documentation for type Mux for a description of what types // are accepted for pattern and handler. func (rt *router) Connect(pattern interface{}, handler interface{}) { rt.handleUntyped(pattern, mCONNECT, handler) } // Dispatch to the given handler when the pattern matches and the HTTP method is // DELETE. See the documentation for type Mux for a description of what types // are accepted for pattern and handler. func (rt *router) Delete(pattern interface{}, handler interface{}) { rt.handleUntyped(pattern, mDELETE, handler) } // Dispatch to the given handler when the pattern matches and the HTTP method is // GET. See the documentation for type Mux for a description of what types are // accepted for pattern and handler. // // 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 (rt *router) Get(pattern interface{}, handler interface{}) { rt.handleUntyped(pattern, mGET|mHEAD, handler) } // Dispatch to the given handler when the pattern matches and the HTTP method is // HEAD. See the documentation for type Mux for a description of what types are // accepted for pattern and handler. func (rt *router) Head(pattern interface{}, handler interface{}) { rt.handleUntyped(pattern, mHEAD, handler) } // Dispatch to the given handler when the pattern matches and the HTTP method is // OPTIONS. See the documentation for type Mux for a description of what types // are accepted for pattern and handler. func (rt *router) Options(pattern interface{}, handler interface{}) { rt.handleUntyped(pattern, mOPTIONS, handler) } // Dispatch to the given handler when the pattern matches and the HTTP method is // PATCH. See the documentation for type Mux for a description of what types are // accepted for pattern and handler. func (rt *router) Patch(pattern interface{}, handler interface{}) { rt.handleUntyped(pattern, mPATCH, handler) } // Dispatch to the given handler when the pattern matches and the HTTP method is // POST. See the documentation for type Mux for a description of what types are // accepted for pattern and handler. func (rt *router) Post(pattern interface{}, handler interface{}) { rt.handleUntyped(pattern, mPOST, handler) } // Dispatch to the given handler when the pattern matches and the HTTP method is // PUT. See the documentation for type Mux for a description of what types are // accepted for pattern and handler. func (rt *router) Put(pattern interface{}, handler interface{}) { rt.handleUntyped(pattern, mPUT, handler) } // Dispatch to the given handler when the pattern matches and the HTTP method is // TRACE. See the documentation for type Mux for a description of what types are // accepted for pattern and handler. func (rt *router) Trace(pattern interface{}, handler interface{}) { rt.handleUntyped(pattern, mTRACE, handler) } // Set the fallback (i.e., 404) handler for this mux. See the documentation for // type Mux for a description of what types are accepted for handler. // // 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 (rt *router) NotFound(handler interface{}) { rt.notFound = parseHandler(handler) }