|  | // Copyright 2012 The Gorilla Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package mux | 
|  |  | 
|  | import ( | 
|  | "errors" | 
|  | "fmt" | 
|  | "net/http" | 
|  | "path" | 
|  | "regexp" | 
|  | "strings" | 
|  | ) | 
|  |  | 
|  | // NewRouter returns a new router instance. | 
|  | func NewRouter() *Router { | 
|  | return &Router{namedRoutes: make(map[string]*Route), KeepContext: false} | 
|  | } | 
|  |  | 
|  | // Router registers routes to be matched and dispatches a handler. | 
|  | // | 
|  | // It implements the http.Handler interface, so it can be registered to serve | 
|  | // requests: | 
|  | // | 
|  | //     var router = mux.NewRouter() | 
|  | // | 
|  | //     func main() { | 
|  | //         http.Handle("/", router) | 
|  | //     } | 
|  | // | 
|  | // Or, for Google App Engine, register it in a init() function: | 
|  | // | 
|  | //     func init() { | 
|  | //         http.Handle("/", router) | 
|  | //     } | 
|  | // | 
|  | // This will send all incoming requests to the router. | 
|  | type Router struct { | 
|  | // Configurable Handler to be used when no route matches. | 
|  | NotFoundHandler http.Handler | 
|  | // Parent route, if this is a subrouter. | 
|  | parent parentRoute | 
|  | // Routes to be matched, in order. | 
|  | routes []*Route | 
|  | // Routes by name for URL building. | 
|  | namedRoutes map[string]*Route | 
|  | // See Router.StrictSlash(). This defines the flag for new routes. | 
|  | strictSlash bool | 
|  | // See Router.SkipClean(). This defines the flag for new routes. | 
|  | skipClean bool | 
|  | // If true, do not clear the request context after handling the request. | 
|  | // This has no effect when go1.7+ is used, since the context is stored | 
|  | // on the request itself. | 
|  | KeepContext bool | 
|  | // see Router.UseEncodedPath(). This defines a flag for all routes. | 
|  | useEncodedPath bool | 
|  | } | 
|  |  | 
|  | // Match matches registered routes against the request. | 
|  | func (r *Router) Match(req *http.Request, match *RouteMatch) bool { | 
|  | for _, route := range r.routes { | 
|  | if route.Match(req, match) { | 
|  | return true | 
|  | } | 
|  | } | 
|  |  | 
|  | // Closest match for a router (includes sub-routers) | 
|  | if r.NotFoundHandler != nil { | 
|  | match.Handler = r.NotFoundHandler | 
|  | return true | 
|  | } | 
|  | return false | 
|  | } | 
|  |  | 
|  | // ServeHTTP dispatches the handler registered in the matched route. | 
|  | // | 
|  | // When there is a match, the route variables can be retrieved calling | 
|  | // mux.Vars(request). | 
|  | func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { | 
|  | if !r.skipClean { | 
|  | path := req.URL.Path | 
|  | if r.useEncodedPath { | 
|  | path = getPath(req) | 
|  | } | 
|  | // Clean path to canonical form and redirect. | 
|  | if p := cleanPath(path); p != path { | 
|  |  | 
|  | // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query. | 
|  | // This matches with fix in go 1.2 r.c. 4 for same problem.  Go Issue: | 
|  | // http://code.google.com/p/go/issues/detail?id=5252 | 
|  | url := *req.URL | 
|  | url.Path = p | 
|  | p = url.String() | 
|  |  | 
|  | w.Header().Set("Location", p) | 
|  | w.WriteHeader(http.StatusMovedPermanently) | 
|  | return | 
|  | } | 
|  | } | 
|  | var match RouteMatch | 
|  | var handler http.Handler | 
|  | if r.Match(req, &match) { | 
|  | handler = match.Handler | 
|  | req = setVars(req, match.Vars) | 
|  | req = setCurrentRoute(req, match.Route) | 
|  | } | 
|  | if handler == nil { | 
|  | handler = http.NotFoundHandler() | 
|  | } | 
|  | if !r.KeepContext { | 
|  | defer contextClear(req) | 
|  | } | 
|  | handler.ServeHTTP(w, req) | 
|  | } | 
|  |  | 
|  | // Get returns a route registered with the given name. | 
|  | func (r *Router) Get(name string) *Route { | 
|  | return r.getNamedRoutes()[name] | 
|  | } | 
|  |  | 
|  | // GetRoute returns a route registered with the given name. This method | 
|  | // was renamed to Get() and remains here for backwards compatibility. | 
|  | func (r *Router) GetRoute(name string) *Route { | 
|  | return r.getNamedRoutes()[name] | 
|  | } | 
|  |  | 
|  | // StrictSlash defines the trailing slash behavior for new routes. The initial | 
|  | // value is false. | 
|  | // | 
|  | // When true, if the route path is "/path/", accessing "/path" will redirect | 
|  | // to the former and vice versa. In other words, your application will always | 
|  | // see the path as specified in the route. | 
|  | // | 
|  | // When false, if the route path is "/path", accessing "/path/" will not match | 
|  | // this route and vice versa. | 
|  | // | 
|  | // Special case: when a route sets a path prefix using the PathPrefix() method, | 
|  | // strict slash is ignored for that route because the redirect behavior can't | 
|  | // be determined from a prefix alone. However, any subrouters created from that | 
|  | // route inherit the original StrictSlash setting. | 
|  | func (r *Router) StrictSlash(value bool) *Router { | 
|  | r.strictSlash = value | 
|  | return r | 
|  | } | 
|  |  | 
|  | // SkipClean defines the path cleaning behaviour for new routes. The initial | 
|  | // value is false. Users should be careful about which routes are not cleaned | 
|  | // | 
|  | // When true, if the route path is "/path//to", it will remain with the double | 
|  | // slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/ | 
|  | // | 
|  | // When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will | 
|  | // become /fetch/http/xkcd.com/534 | 
|  | func (r *Router) SkipClean(value bool) *Router { | 
|  | r.skipClean = value | 
|  | return r | 
|  | } | 
|  |  | 
|  | // UseEncodedPath tells the router to match the encoded original path | 
|  | // to the routes. | 
|  | // For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to". | 
|  | // This behavior has the drawback of needing to match routes against | 
|  | // r.RequestURI instead of r.URL.Path. Any modifications (such as http.StripPrefix) | 
|  | // to r.URL.Path will not affect routing when this flag is on and thus may | 
|  | // induce unintended behavior. | 
|  | // | 
|  | // If not called, the router will match the unencoded path to the routes. | 
|  | // For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to" | 
|  | func (r *Router) UseEncodedPath() *Router { | 
|  | r.useEncodedPath = true | 
|  | return r | 
|  | } | 
|  |  | 
|  | // ---------------------------------------------------------------------------- | 
|  | // parentRoute | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | // getNamedRoutes returns the map where named routes are registered. | 
|  | func (r *Router) getNamedRoutes() map[string]*Route { | 
|  | if r.namedRoutes == nil { | 
|  | if r.parent != nil { | 
|  | r.namedRoutes = r.parent.getNamedRoutes() | 
|  | } else { | 
|  | r.namedRoutes = make(map[string]*Route) | 
|  | } | 
|  | } | 
|  | return r.namedRoutes | 
|  | } | 
|  |  | 
|  | // getRegexpGroup returns regexp definitions from the parent route, if any. | 
|  | func (r *Router) getRegexpGroup() *routeRegexpGroup { | 
|  | if r.parent != nil { | 
|  | return r.parent.getRegexpGroup() | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func (r *Router) buildVars(m map[string]string) map[string]string { | 
|  | if r.parent != nil { | 
|  | m = r.parent.buildVars(m) | 
|  | } | 
|  | return m | 
|  | } | 
|  |  | 
|  | // ---------------------------------------------------------------------------- | 
|  | // Route factories | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | // NewRoute registers an empty route. | 
|  | func (r *Router) NewRoute() *Route { | 
|  | route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean, useEncodedPath: r.useEncodedPath} | 
|  | r.routes = append(r.routes, route) | 
|  | return route | 
|  | } | 
|  |  | 
|  | // Handle registers a new route with a matcher for the URL path. | 
|  | // See Route.Path() and Route.Handler(). | 
|  | func (r *Router) Handle(path string, handler http.Handler) *Route { | 
|  | return r.NewRoute().Path(path).Handler(handler) | 
|  | } | 
|  |  | 
|  | // HandleFunc registers a new route with a matcher for the URL path. | 
|  | // See Route.Path() and Route.HandlerFunc(). | 
|  | func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, | 
|  | *http.Request)) *Route { | 
|  | return r.NewRoute().Path(path).HandlerFunc(f) | 
|  | } | 
|  |  | 
|  | // Headers registers a new route with a matcher for request header values. | 
|  | // See Route.Headers(). | 
|  | func (r *Router) Headers(pairs ...string) *Route { | 
|  | return r.NewRoute().Headers(pairs...) | 
|  | } | 
|  |  | 
|  | // Host registers a new route with a matcher for the URL host. | 
|  | // See Route.Host(). | 
|  | func (r *Router) Host(tpl string) *Route { | 
|  | return r.NewRoute().Host(tpl) | 
|  | } | 
|  |  | 
|  | // MatcherFunc registers a new route with a custom matcher function. | 
|  | // See Route.MatcherFunc(). | 
|  | func (r *Router) MatcherFunc(f MatcherFunc) *Route { | 
|  | return r.NewRoute().MatcherFunc(f) | 
|  | } | 
|  |  | 
|  | // Methods registers a new route with a matcher for HTTP methods. | 
|  | // See Route.Methods(). | 
|  | func (r *Router) Methods(methods ...string) *Route { | 
|  | return r.NewRoute().Methods(methods...) | 
|  | } | 
|  |  | 
|  | // Path registers a new route with a matcher for the URL path. | 
|  | // See Route.Path(). | 
|  | func (r *Router) Path(tpl string) *Route { | 
|  | return r.NewRoute().Path(tpl) | 
|  | } | 
|  |  | 
|  | // PathPrefix registers a new route with a matcher for the URL path prefix. | 
|  | // See Route.PathPrefix(). | 
|  | func (r *Router) PathPrefix(tpl string) *Route { | 
|  | return r.NewRoute().PathPrefix(tpl) | 
|  | } | 
|  |  | 
|  | // Queries registers a new route with a matcher for URL query values. | 
|  | // See Route.Queries(). | 
|  | func (r *Router) Queries(pairs ...string) *Route { | 
|  | return r.NewRoute().Queries(pairs...) | 
|  | } | 
|  |  | 
|  | // Schemes registers a new route with a matcher for URL schemes. | 
|  | // See Route.Schemes(). | 
|  | func (r *Router) Schemes(schemes ...string) *Route { | 
|  | return r.NewRoute().Schemes(schemes...) | 
|  | } | 
|  |  | 
|  | // BuildVarsFunc registers a new route with a custom function for modifying | 
|  | // route variables before building a URL. | 
|  | func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route { | 
|  | return r.NewRoute().BuildVarsFunc(f) | 
|  | } | 
|  |  | 
|  | // Walk walks the router and all its sub-routers, calling walkFn for each route | 
|  | // in the tree. The routes are walked in the order they were added. Sub-routers | 
|  | // are explored depth-first. | 
|  | func (r *Router) Walk(walkFn WalkFunc) error { | 
|  | return r.walk(walkFn, []*Route{}) | 
|  | } | 
|  |  | 
|  | // SkipRouter is used as a return value from WalkFuncs to indicate that the | 
|  | // router that walk is about to descend down to should be skipped. | 
|  | var SkipRouter = errors.New("skip this router") | 
|  |  | 
|  | // WalkFunc is the type of the function called for each route visited by Walk. | 
|  | // At every invocation, it is given the current route, and the current router, | 
|  | // and a list of ancestor routes that lead to the current route. | 
|  | type WalkFunc func(route *Route, router *Router, ancestors []*Route) error | 
|  |  | 
|  | func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error { | 
|  | for _, t := range r.routes { | 
|  | if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" { | 
|  | continue | 
|  | } | 
|  |  | 
|  | err := walkFn(t, r, ancestors) | 
|  | if err == SkipRouter { | 
|  | continue | 
|  | } | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | for _, sr := range t.matchers { | 
|  | if h, ok := sr.(*Router); ok { | 
|  | err := h.walk(walkFn, ancestors) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | } | 
|  | } | 
|  | if h, ok := t.handler.(*Router); ok { | 
|  | ancestors = append(ancestors, t) | 
|  | err := h.walk(walkFn, ancestors) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | ancestors = ancestors[:len(ancestors)-1] | 
|  | } | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // ---------------------------------------------------------------------------- | 
|  | // Context | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | // RouteMatch stores information about a matched route. | 
|  | type RouteMatch struct { | 
|  | Route   *Route | 
|  | Handler http.Handler | 
|  | Vars    map[string]string | 
|  | } | 
|  |  | 
|  | type contextKey int | 
|  |  | 
|  | const ( | 
|  | varsKey contextKey = iota | 
|  | routeKey | 
|  | ) | 
|  |  | 
|  | // Vars returns the route variables for the current request, if any. | 
|  | func Vars(r *http.Request) map[string]string { | 
|  | if rv := contextGet(r, varsKey); rv != nil { | 
|  | return rv.(map[string]string) | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // CurrentRoute returns the matched route for the current request, if any. | 
|  | // This only works when called inside the handler of the matched route | 
|  | // because the matched route is stored in the request context which is cleared | 
|  | // after the handler returns, unless the KeepContext option is set on the | 
|  | // Router. | 
|  | func CurrentRoute(r *http.Request) *Route { | 
|  | if rv := contextGet(r, routeKey); rv != nil { | 
|  | return rv.(*Route) | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | func setVars(r *http.Request, val interface{}) *http.Request { | 
|  | return contextSet(r, varsKey, val) | 
|  | } | 
|  |  | 
|  | func setCurrentRoute(r *http.Request, val interface{}) *http.Request { | 
|  | return contextSet(r, routeKey, val) | 
|  | } | 
|  |  | 
|  | // ---------------------------------------------------------------------------- | 
|  | // Helpers | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | // getPath returns the escaped path if possible; doing what URL.EscapedPath() | 
|  | // which was added in go1.5 does | 
|  | func getPath(req *http.Request) string { | 
|  | if req.RequestURI != "" { | 
|  | // Extract the path from RequestURI (which is escaped unlike URL.Path) | 
|  | // as detailed here as detailed in https://golang.org/pkg/net/url/#URL | 
|  | // for < 1.5 server side workaround | 
|  | // http://localhost/path/here?v=1 -> /path/here | 
|  | path := req.RequestURI | 
|  | path = strings.TrimPrefix(path, req.URL.Scheme+`://`) | 
|  | path = strings.TrimPrefix(path, req.URL.Host) | 
|  | if i := strings.LastIndex(path, "?"); i > -1 { | 
|  | path = path[:i] | 
|  | } | 
|  | if i := strings.LastIndex(path, "#"); i > -1 { | 
|  | path = path[:i] | 
|  | } | 
|  | return path | 
|  | } | 
|  | return req.URL.Path | 
|  | } | 
|  |  | 
|  | // cleanPath returns the canonical path for p, eliminating . and .. elements. | 
|  | // Borrowed from the net/http package. | 
|  | func cleanPath(p string) string { | 
|  | if p == "" { | 
|  | return "/" | 
|  | } | 
|  | if p[0] != '/' { | 
|  | p = "/" + p | 
|  | } | 
|  | np := path.Clean(p) | 
|  | // path.Clean removes trailing slash except for root; | 
|  | // put the trailing slash back if necessary. | 
|  | if p[len(p)-1] == '/' && np != "/" { | 
|  | np += "/" | 
|  | } | 
|  |  | 
|  | return np | 
|  | } | 
|  |  | 
|  | // uniqueVars returns an error if two slices contain duplicated strings. | 
|  | func uniqueVars(s1, s2 []string) error { | 
|  | for _, v1 := range s1 { | 
|  | for _, v2 := range s2 { | 
|  | if v1 == v2 { | 
|  | return fmt.Errorf("mux: duplicated route variable %q", v2) | 
|  | } | 
|  | } | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // checkPairs returns the count of strings passed in, and an error if | 
|  | // the count is not an even number. | 
|  | func checkPairs(pairs ...string) (int, error) { | 
|  | length := len(pairs) | 
|  | if length%2 != 0 { | 
|  | return length, fmt.Errorf( | 
|  | "mux: number of parameters must be multiple of 2, got %v", pairs) | 
|  | } | 
|  | return length, nil | 
|  | } | 
|  |  | 
|  | // mapFromPairsToString converts variadic string parameters to a | 
|  | // string to string map. | 
|  | func mapFromPairsToString(pairs ...string) (map[string]string, error) { | 
|  | length, err := checkPairs(pairs...) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | m := make(map[string]string, length/2) | 
|  | for i := 0; i < length; i += 2 { | 
|  | m[pairs[i]] = pairs[i+1] | 
|  | } | 
|  | return m, nil | 
|  | } | 
|  |  | 
|  | // mapFromPairsToRegex converts variadic string paramers to a | 
|  | // string to regex map. | 
|  | func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) { | 
|  | length, err := checkPairs(pairs...) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | m := make(map[string]*regexp.Regexp, length/2) | 
|  | for i := 0; i < length; i += 2 { | 
|  | regex, err := regexp.Compile(pairs[i+1]) | 
|  | if err != nil { | 
|  | return nil, err | 
|  | } | 
|  | m[pairs[i]] = regex | 
|  | } | 
|  | return m, nil | 
|  | } | 
|  |  | 
|  | // matchInArray returns true if the given string value is in the array. | 
|  | func matchInArray(arr []string, value string) bool { | 
|  | for _, v := range arr { | 
|  | if v == value { | 
|  | return true | 
|  | } | 
|  | } | 
|  | return false | 
|  | } | 
|  |  | 
|  | // matchMapWithString returns true if the given key/value pairs exist in a given map. | 
|  | func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool { | 
|  | for k, v := range toCheck { | 
|  | // Check if key exists. | 
|  | if canonicalKey { | 
|  | k = http.CanonicalHeaderKey(k) | 
|  | } | 
|  | if values := toMatch[k]; values == nil { | 
|  | return false | 
|  | } else if v != "" { | 
|  | // If value was defined as an empty string we only check that the | 
|  | // key exists. Otherwise we also check for equality. | 
|  | valueExists := false | 
|  | for _, value := range values { | 
|  | if v == value { | 
|  | valueExists = true | 
|  | break | 
|  | } | 
|  | } | 
|  | if !valueExists { | 
|  | return false | 
|  | } | 
|  | } | 
|  | } | 
|  | return true | 
|  | } | 
|  |  | 
|  | // matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against | 
|  | // the given regex | 
|  | func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool { | 
|  | for k, v := range toCheck { | 
|  | // Check if key exists. | 
|  | if canonicalKey { | 
|  | k = http.CanonicalHeaderKey(k) | 
|  | } | 
|  | if values := toMatch[k]; values == nil { | 
|  | return false | 
|  | } else if v != nil { | 
|  | // If value was defined as an empty string we only check that the | 
|  | // key exists. Otherwise we also check for equality. | 
|  | valueExists := false | 
|  | for _, value := range values { | 
|  | if v.MatchString(value) { | 
|  | valueExists = true | 
|  | break | 
|  | } | 
|  | } | 
|  | if !valueExists { | 
|  | return false | 
|  | } | 
|  | } | 
|  | } | 
|  | return true | 
|  | } |