aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/gorilla/mux/route.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gorilla/mux/route.go')
-rw-r--r--vendor/github.com/gorilla/mux/route.go189
1 files changed, 82 insertions, 107 deletions
diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go
index 4ce098d..750afe5 100644
--- a/vendor/github.com/gorilla/mux/route.go
+++ b/vendor/github.com/gorilla/mux/route.go
@@ -15,24 +15,8 @@ import (
// Route stores information to match a request and build URLs.
type Route struct {
- // Parent where the route was registered (a Router).
- parent parentRoute
// Request handler for the route.
handler http.Handler
- // List of matchers.
- matchers []matcher
- // Manager for the variables from host and path.
- regexp *routeRegexpGroup
- // If true, when the path pattern is "/path/", accessing "/path" will
- // redirect to the former and vice versa.
- strictSlash bool
- // If true, when the path pattern is "/path//to", accessing "/path//to"
- // will not redirect
- skipClean bool
- // If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
- useEncodedPath bool
- // The scheme used when building URLs.
- buildScheme string
// If true, this route never matches: it is only used to build URLs.
buildOnly bool
// The name used to build URLs.
@@ -40,9 +24,15 @@ type Route struct {
// Error resulted from building a route.
err error
- buildVarsFunc BuildVarsFunc
+ // "global" reference to all named routes
+ namedRoutes map[string]*Route
+
+ // config possibly passed in from `Router`
+ routeConf
}
+// SkipClean reports whether path cleaning is enabled for this route via
+// Router.SkipClean.
func (r *Route) SkipClean() bool {
return r.skipClean
}
@@ -62,6 +52,18 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
matchErr = ErrMethodMismatch
continue
}
+
+ // Ignore ErrNotFound errors. These errors arise from match call
+ // to Subrouters.
+ //
+ // This prevents subsequent matching subrouters from failing to
+ // run middleware. If not ignored, the middleware would see a
+ // non-nil MatchErr and be skipped, even when there was a
+ // matching route.
+ if match.MatchErr == ErrNotFound {
+ match.MatchErr = nil
+ }
+
matchErr = nil
return false
}
@@ -72,7 +74,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
return false
}
- if match.MatchErr == ErrMethodMismatch {
+ if match.MatchErr == ErrMethodMismatch && r.handler != nil {
// We found a route which matches request method, clear MatchErr
match.MatchErr = nil
// Then override the mis-matched handler
@@ -91,9 +93,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
}
// Set variables.
- if r.regexp != nil {
- r.regexp.setMatch(req, match, r)
- }
+ r.regexp.setMatch(req, match, r)
return true
}
@@ -135,7 +135,7 @@ func (r *Route) GetHandler() http.Handler {
// Name -----------------------------------------------------------------------
// Name sets the name for the route, used to build URLs.
-// If the name was registered already it will be overwritten.
+// It is an error to call Name more than once on a route.
func (r *Route) Name(name string) *Route {
if r.name != "" {
r.err = fmt.Errorf("mux: route already has name %q, can't set %q",
@@ -143,7 +143,7 @@ func (r *Route) Name(name string) *Route {
}
if r.err == nil {
r.name = name
- r.getNamedRoutes()[name] = r
+ r.namedRoutes[name] = r
}
return r
}
@@ -175,7 +175,6 @@ func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error {
if r.err != nil {
return r.err
}
- r.regexp = r.getRegexpGroup()
if typ == regexpTypePath || typ == regexpTypePrefix {
if len(tpl) > 0 && tpl[0] != '/' {
return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
@@ -384,7 +383,7 @@ func (r *Route) PathPrefix(tpl string) *Route {
// The above route will only match if the URL contains the defined queries
// values, e.g.: ?foo=bar&id=42.
//
-// It the value is an empty string, it will match any value if the key is set.
+// If the value is an empty string, it will match any value if the key is set.
//
// Variables can define an optional regexp pattern to be matched:
//
@@ -413,16 +412,35 @@ func (r *Route) Queries(pairs ...string) *Route {
type schemeMatcher []string
func (m schemeMatcher) Match(r *http.Request, match *RouteMatch) bool {
- return matchInArray(m, r.URL.Scheme)
+ scheme := r.URL.Scheme
+ // https://golang.org/pkg/net/http/#Request
+ // "For [most] server requests, fields other than Path and RawQuery will be
+ // empty."
+ // Since we're an http muxer, the scheme is either going to be http or https
+ // though, so we can just set it based on the tls termination state.
+ if scheme == "" {
+ if r.TLS == nil {
+ scheme = "http"
+ } else {
+ scheme = "https"
+ }
+ }
+ return matchInArray(m, scheme)
}
// Schemes adds a matcher for URL schemes.
// It accepts a sequence of schemes to be matched, e.g.: "http", "https".
+// If the request's URL has a scheme set, it will be matched against.
+// Generally, the URL scheme will only be set if a previous handler set it,
+// such as the ProxyHeaders handler from gorilla/handlers.
+// If unset, the scheme will be determined based on the request's TLS
+// termination state.
+// The first argument to Schemes will be used when constructing a route URL.
func (r *Route) Schemes(schemes ...string) *Route {
for k, v := range schemes {
schemes[k] = strings.ToLower(v)
}
- if r.buildScheme == "" && len(schemes) > 0 {
+ if len(schemes) > 0 {
r.buildScheme = schemes[0]
}
return r.addMatcher(schemeMatcher(schemes))
@@ -437,7 +455,15 @@ type BuildVarsFunc func(map[string]string) map[string]string
// BuildVarsFunc adds a custom function to be used to modify build variables
// before a route's URL is built.
func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
- r.buildVarsFunc = f
+ if r.buildVarsFunc != nil {
+ // compose the old and new functions
+ old := r.buildVarsFunc
+ r.buildVarsFunc = func(m map[string]string) map[string]string {
+ return f(old(m))
+ }
+ } else {
+ r.buildVarsFunc = f
+ }
return r
}
@@ -456,7 +482,8 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
// Here, the routes registered in the subrouter won't be tested if the host
// doesn't match.
func (r *Route) Subrouter() *Router {
- router := &Router{parent: r, strictSlash: r.strictSlash}
+ // initialize a subrouter with a copy of the parent route's configuration
+ router := &Router{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes}
r.addMatcher(router)
return router
}
@@ -485,8 +512,8 @@ func (r *Route) Subrouter() *Router {
// This also works for host variables:
//
// r := mux.NewRouter()
-// r.Host("{subdomain}.domain.com").
-// HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+// r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+// Host("{subdomain}.domain.com").
// Name("article")
//
// // url.String() will be "http://news.domain.com/articles/technology/42"
@@ -494,15 +521,19 @@ func (r *Route) Subrouter() *Router {
// "category", "technology",
// "id", "42")
//
+// The scheme of the resulting url will be the first argument that was passed to Schemes:
+//
+// // url.String() will be "https://example.com"
+// r := mux.NewRouter()
+// url, err := r.Host("example.com")
+// .Schemes("https", "http").URL()
+//
// All variables defined in the route are required, and their values must
// conform to the corresponding patterns.
func (r *Route) URL(pairs ...string) (*url.URL, error) {
if r.err != nil {
return nil, r.err
}
- if r.regexp == nil {
- return nil, errors.New("mux: route doesn't have a host or path")
- }
values, err := r.prepareVars(pairs...)
if err != nil {
return nil, err
@@ -514,8 +545,8 @@ func (r *Route) URL(pairs ...string) (*url.URL, error) {
return nil, err
}
scheme = "http"
- if s := r.getBuildScheme(); s != "" {
- scheme = s
+ if r.buildScheme != "" {
+ scheme = r.buildScheme
}
}
if r.regexp.path != nil {
@@ -545,7 +576,7 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
if r.err != nil {
return nil, r.err
}
- if r.regexp == nil || r.regexp.host == nil {
+ if r.regexp.host == nil {
return nil, errors.New("mux: route doesn't have a host")
}
values, err := r.prepareVars(pairs...)
@@ -560,8 +591,8 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
Scheme: "http",
Host: host,
}
- if s := r.getBuildScheme(); s != "" {
- u.Scheme = s
+ if r.buildScheme != "" {
+ u.Scheme = r.buildScheme
}
return u, nil
}
@@ -573,7 +604,7 @@ func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
if r.err != nil {
return nil, r.err
}
- if r.regexp == nil || r.regexp.path == nil {
+ if r.regexp.path == nil {
return nil, errors.New("mux: route doesn't have a path")
}
values, err := r.prepareVars(pairs...)
@@ -598,7 +629,7 @@ func (r *Route) GetPathTemplate() (string, error) {
if r.err != nil {
return "", r.err
}
- if r.regexp == nil || r.regexp.path == nil {
+ if r.regexp.path == nil {
return "", errors.New("mux: route doesn't have a path")
}
return r.regexp.path.template, nil
@@ -612,7 +643,7 @@ func (r *Route) GetPathRegexp() (string, error) {
if r.err != nil {
return "", r.err
}
- if r.regexp == nil || r.regexp.path == nil {
+ if r.regexp.path == nil {
return "", errors.New("mux: route does not have a path")
}
return r.regexp.path.regexp.String(), nil
@@ -622,15 +653,15 @@ func (r *Route) GetPathRegexp() (string, error) {
// route queries.
// This is useful for building simple REST API documentation and for instrumentation
// against third-party services.
-// An empty list will be returned if the route does not have queries.
+// An error will be returned if the route does not have queries.
func (r *Route) GetQueriesRegexp() ([]string, error) {
if r.err != nil {
return nil, r.err
}
- if r.regexp == nil || r.regexp.queries == nil {
+ if r.regexp.queries == nil {
return nil, errors.New("mux: route doesn't have queries")
}
- var queries []string
+ queries := make([]string, 0, len(r.regexp.queries))
for _, query := range r.regexp.queries {
queries = append(queries, query.regexp.String())
}
@@ -641,15 +672,15 @@ func (r *Route) GetQueriesRegexp() ([]string, error) {
// query matching.
// This is useful for building simple REST API documentation and for instrumentation
// against third-party services.
-// An empty list will be returned if the route does not define queries.
+// An error will be returned if the route does not define queries.
func (r *Route) GetQueriesTemplates() ([]string, error) {
if r.err != nil {
return nil, r.err
}
- if r.regexp == nil || r.regexp.queries == nil {
+ if r.regexp.queries == nil {
return nil, errors.New("mux: route doesn't have queries")
}
- var queries []string
+ queries := make([]string, 0, len(r.regexp.queries))
for _, query := range r.regexp.queries {
queries = append(queries, query.template)
}
@@ -659,7 +690,7 @@ func (r *Route) GetQueriesTemplates() ([]string, error) {
// GetMethods returns the methods the route matches against
// This is useful for building simple REST API documentation and for instrumentation
// against third-party services.
-// An empty list will be returned if route does not have methods.
+// An error will be returned if route does not have methods.
func (r *Route) GetMethods() ([]string, error) {
if r.err != nil {
return nil, r.err
@@ -669,7 +700,7 @@ func (r *Route) GetMethods() ([]string, error) {
return []string(methods), nil
}
}
- return nil, nil
+ return nil, errors.New("mux: route doesn't have methods")
}
// GetHostTemplate returns the template used to build the
@@ -681,7 +712,7 @@ func (r *Route) GetHostTemplate() (string, error) {
if r.err != nil {
return "", r.err
}
- if r.regexp == nil || r.regexp.host == nil {
+ if r.regexp.host == nil {
return "", errors.New("mux: route doesn't have a host")
}
return r.regexp.host.template, nil
@@ -698,64 +729,8 @@ func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
}
func (r *Route) buildVars(m map[string]string) map[string]string {
- if r.parent != nil {
- m = r.parent.buildVars(m)
- }
if r.buildVarsFunc != nil {
m = r.buildVarsFunc(m)
}
return m
}
-
-// ----------------------------------------------------------------------------
-// parentRoute
-// ----------------------------------------------------------------------------
-
-// parentRoute allows routes to know about parent host and path definitions.
-type parentRoute interface {
- getBuildScheme() string
- getNamedRoutes() map[string]*Route
- getRegexpGroup() *routeRegexpGroup
- buildVars(map[string]string) map[string]string
-}
-
-func (r *Route) getBuildScheme() string {
- if r.buildScheme != "" {
- return r.buildScheme
- }
- if r.parent != nil {
- return r.parent.getBuildScheme()
- }
- return ""
-}
-
-// getNamedRoutes returns the map where named routes are registered.
-func (r *Route) getNamedRoutes() map[string]*Route {
- if r.parent == nil {
- // During tests router is not always set.
- r.parent = NewRouter()
- }
- return r.parent.getNamedRoutes()
-}
-
-// getRegexpGroup returns regexp definitions from this route.
-func (r *Route) getRegexpGroup() *routeRegexpGroup {
- if r.regexp == nil {
- if r.parent == nil {
- // During tests router is not always set.
- r.parent = NewRouter()
- }
- regexp := r.parent.getRegexpGroup()
- if regexp == nil {
- r.regexp = new(routeRegexpGroup)
- } else {
- // Copy.
- r.regexp = &routeRegexpGroup{
- host: regexp.host,
- path: regexp.path,
- queries: regexp.queries,
- }
- }
- }
- return r.regexp
-}
nihil fit ex nihilo