Bump gorilla/mux version.

This commit is contained in:
Ludovic Fernandez 2017-08-21 20:40:02 +02:00 committed by Traefiker
parent 3f650bbd11
commit 32b2736efd
7 changed files with 273 additions and 62 deletions

2
glide.lock generated
View file

@ -87,7 +87,7 @@ imports:
- name: github.com/containous/flaeg - name: github.com/containous/flaeg
version: b5d2dc5878df07c2d74413348186982e7b865871 version: b5d2dc5878df07c2d74413348186982e7b865871
- name: github.com/containous/mux - name: github.com/containous/mux
version: a819b77bba13f0c0cbe36e437bc2e948411b3996 version: af6ea922f7683d9706834157e6b0610e22ccb2db
- name: github.com/containous/staert - name: github.com/containous/staert
version: 1e26a71803e428fd933f5f9c8e50a26878f53147 version: 1e26a71803e428fd933f5f9c8e50a26878f53147
- name: github.com/coreos/etcd - name: github.com/coreos/etcd

26
vendor/github.com/containous/mux/context_gorilla.go generated vendored Normal file
View file

@ -0,0 +1,26 @@
// +build !go1.7
package mux
import (
"net/http"
"github.com/gorilla/context"
)
func contextGet(r *http.Request, key interface{}) interface{} {
return context.Get(r, key)
}
func contextSet(r *http.Request, key, val interface{}) *http.Request {
if val == nil {
return r
}
context.Set(r, key, val)
return r
}
func contextClear(r *http.Request) {
context.Clear(r)
}

24
vendor/github.com/containous/mux/context_native.go generated vendored Normal file
View file

@ -0,0 +1,24 @@
// +build go1.7
package mux
import (
"context"
"net/http"
)
func contextGet(r *http.Request, key interface{}) interface{} {
return r.Context().Value(key)
}
func contextSet(r *http.Request, key, val interface{}) *http.Request {
if val == nil {
return r
}
return r.WithContext(context.WithValue(r.Context(), key, val))
}
func contextClear(r *http.Request) {
return
}

View file

@ -12,8 +12,8 @@ or other conditions. The main features are:
* Requests can be matched based on URL host, path, path prefix, schemes, * Requests can be matched based on URL host, path, path prefix, schemes,
header and query values, HTTP methods or using custom matchers. header and query values, HTTP methods or using custom matchers.
* URL hosts and paths can have variables with an optional regular * URL hosts, paths and query values can have variables with an optional
expression. regular expression.
* Registered URLs can be built, or "reversed", which helps maintaining * Registered URLs can be built, or "reversed", which helps maintaining
references to resources. references to resources.
* Routes can be used as subrouters: nested routes are only tested if the * Routes can be used as subrouters: nested routes are only tested if the
@ -47,12 +47,21 @@ variable will be anything until the next slash. For example:
r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler) r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler) r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
Groups can be used inside patterns, as long as they are non-capturing (?:re). For example:
r.HandleFunc("/articles/{category}/{sort:(?:asc|desc|new)}", ArticlesCategoryHandler)
The names are used to create a map of route variables which can be retrieved The names are used to create a map of route variables which can be retrieved
calling mux.Vars(): calling mux.Vars():
vars := mux.Vars(request) vars := mux.Vars(request)
category := vars["category"] category := vars["category"]
Note that if any capturing groups are present, mux will panic() during parsing. To prevent
this, convert any capturing groups to non-capturing, e.g. change "/{sort:(asc|desc)}" to
"/{sort:(?:asc|desc)}". This is a change from prior versions which behaved unpredictably
when capturing groups were present.
And this is all you need to know about the basic usage. More advanced options And this is all you need to know about the basic usage. More advanced options
are explained below. are explained below.
@ -136,6 +145,31 @@ the inner routes use it as base for their paths:
// "/products/{key}/details" // "/products/{key}/details"
s.HandleFunc("/{key}/details", ProductDetailsHandler) s.HandleFunc("/{key}/details", ProductDetailsHandler)
Note that the path provided to PathPrefix() represents a "wildcard": calling
PathPrefix("/static/").Handler(...) means that the handler will be passed any
request that matches "/static/*". This makes it easy to serve static files with mux:
func main() {
var dir string
flag.StringVar(&dir, "dir", ".", "the directory to serve files from. Defaults to the current dir")
flag.Parse()
r := mux.NewRouter()
// This will serve files under http://localhost:8000/static/<filename>
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(dir))))
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:8000",
// Good practice: enforce timeouts for servers you create!
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}
Now let's see how to build registered URLs. Now let's see how to build registered URLs.
Routes can be named. All routes that define a name can have their URLs built, Routes can be named. All routes that define a name can have their URLs built,
@ -154,18 +188,20 @@ key/value pairs for the route variables. For the previous route, we would do:
"/articles/technology/42" "/articles/technology/42"
This also works for host variables: This also works for host and query value variables:
r := mux.NewRouter() r := mux.NewRouter()
r.Host("{subdomain}.domain.com"). r.Host("{subdomain}.domain.com").
Path("/articles/{category}/{id:[0-9]+}"). Path("/articles/{category}/{id:[0-9]+}").
Queries("filter", "{filter}").
HandlerFunc(ArticleHandler). HandlerFunc(ArticleHandler).
Name("article") Name("article")
// url.String() will be "http://news.domain.com/articles/technology/42" // url.String() will be "http://news.domain.com/articles/technology/42?filter=gorilla"
url, err := r.Get("article").URL("subdomain", "news", url, err := r.Get("article").URL("subdomain", "news",
"category", "technology", "category", "technology",
"id", "42") "id", "42",
"filter", "gorilla")
All variables defined in the route are required, and their values must All variables defined in the route are required, and their values must
conform to the corresponding patterns. These requirements guarantee that a conform to the corresponding patterns. These requirements guarantee that a

View file

@ -11,8 +11,7 @@ import (
"path" "path"
"regexp" "regexp"
"sort" "sort"
"strings"
"github.com/gorilla/context"
) )
// NewRouter returns a new router instance. // NewRouter returns a new router instance.
@ -51,8 +50,12 @@ type Router struct {
strictSlash bool strictSlash bool
// See Router.SkipClean(). This defines the flag for new routes. // See Router.SkipClean(). This defines the flag for new routes.
skipClean bool skipClean bool
// If true, do not clear the request context after handling the request // 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 KeepContext bool
// see Router.UseEncodedPath(). This defines a flag for all routes.
useEncodedPath bool
} }
// Match matches registered routes against the request. // Match matches registered routes against the request.
@ -77,8 +80,12 @@ func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
// mux.Vars(request). // mux.Vars(request).
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if !r.skipClean { if !r.skipClean {
path := req.URL.Path
if r.useEncodedPath {
path = getPath(req)
}
// Clean path to canonical form and redirect. // Clean path to canonical form and redirect.
if p := cleanPath(req.URL.Path); p != req.URL.Path { if p := cleanPath(path); p != path {
// Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query. // 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: // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue:
@ -96,14 +103,14 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
var handler http.Handler var handler http.Handler
if r.Match(req, &match) { if r.Match(req, &match) {
handler = match.Handler handler = match.Handler
setVars(req, match.Vars) req = setVars(req, match.Vars)
setCurrentRoute(req, match.Route) req = setCurrentRoute(req, match.Route)
} }
if handler == nil { if handler == nil {
handler = http.NotFoundHandler() handler = http.NotFoundHandler()
} }
if !r.KeepContext { if !r.KeepContext {
defer context.Clear(req) defer contextClear(req)
} }
handler.ServeHTTP(w, req) handler.ServeHTTP(w, req)
} }
@ -151,10 +158,32 @@ func (r *Router) SkipClean(value bool) *Router {
return r 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 // parentRoute
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
func (r *Router) getBuildScheme() string {
if r.parent != nil {
return r.parent.getBuildScheme()
}
return ""
}
// getNamedRoutes returns the map where named routes are registered. // getNamedRoutes returns the map where named routes are registered.
func (r *Router) getNamedRoutes() map[string]*Route { func (r *Router) getNamedRoutes() map[string]*Route {
if r.namedRoutes == nil { if r.namedRoutes == nil {
@ -188,7 +217,7 @@ func (r *Router) buildVars(m map[string]string) map[string]string {
// NewRoute registers an empty route. // NewRoute registers an empty route.
func (r *Router) NewRoute() *Route { func (r *Router) NewRoute() *Route {
route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean} route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean, useEncodedPath: r.useEncodedPath}
r.routes = append(r.routes, route) r.routes = append(r.routes, route)
return route return route
} }
@ -278,20 +307,21 @@ type WalkFunc func(route *Route, router *Router, ancestors []*Route) error
func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error { func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
for _, t := range r.routes { for _, t := range r.routes {
if t.regexp == nil || t.regexp.path == nil || t.regexp.path.template == "" {
continue
}
err := walkFn(t, r, ancestors) err := walkFn(t, r, ancestors)
if err == SkipRouter { if err == SkipRouter {
continue continue
} }
if err != nil {
return err
}
for _, sr := range t.matchers { for _, sr := range t.matchers {
if h, ok := sr.(*Router); ok { if h, ok := sr.(*Router); ok {
ancestors = append(ancestors, t)
err := h.walk(walkFn, ancestors) err := h.walk(walkFn, ancestors)
if err != nil { if err != nil {
return err return err
} }
ancestors = ancestors[:len(ancestors)-1]
} }
} }
if h, ok := t.handler.(*Router); ok { if h, ok := t.handler.(*Router); ok {
@ -337,7 +367,7 @@ const (
// Vars returns the route variables for the current request, if any. // Vars returns the route variables for the current request, if any.
func Vars(r *http.Request) map[string]string { func Vars(r *http.Request) map[string]string {
if rv := context.Get(r, varsKey); rv != nil { if rv := contextGet(r, varsKey); rv != nil {
return rv.(map[string]string) return rv.(map[string]string)
} }
return nil return nil
@ -349,28 +379,46 @@ func Vars(r *http.Request) map[string]string {
// after the handler returns, unless the KeepContext option is set on the // after the handler returns, unless the KeepContext option is set on the
// Router. // Router.
func CurrentRoute(r *http.Request) *Route { func CurrentRoute(r *http.Request) *Route {
if rv := context.Get(r, routeKey); rv != nil { if rv := contextGet(r, routeKey); rv != nil {
return rv.(*Route) return rv.(*Route)
} }
return nil return nil
} }
func setVars(r *http.Request, val interface{}) { func setVars(r *http.Request, val interface{}) *http.Request {
if val != nil { return contextSet(r, varsKey, val)
context.Set(r, varsKey, val)
}
} }
func setCurrentRoute(r *http.Request, val interface{}) { func setCurrentRoute(r *http.Request, val interface{}) *http.Request {
if val != nil { return contextSet(r, routeKey, val)
context.Set(r, routeKey, val)
}
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Helpers // 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. // cleanPath returns the canonical path for p, eliminating . and .. elements.
// Borrowed from the net/http package. // Borrowed from the net/http package.
func cleanPath(p string) string { func cleanPath(p string) string {
@ -427,7 +475,7 @@ func mapFromPairsToString(pairs ...string) (map[string]string, error) {
return m, nil return m, nil
} }
// mapFromPairsToRegex converts variadic string paramers to a // mapFromPairsToRegex converts variadic string parameters to a
// string to regex map. // string to regex map.
func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) { func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) {
length, err := checkPairs(pairs...) length, err := checkPairs(pairs...)

View file

@ -24,7 +24,7 @@ import (
// Previously we accepted only Python-like identifiers for variable // Previously we accepted only Python-like identifiers for variable
// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that // names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that
// name and pattern can't be empty, and names can't contain a colon. // name and pattern can't be empty, and names can't contain a colon.
func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash bool) (*routeRegexp, error) { func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash, useEncodedPath bool) (*routeRegexp, error) {
// Check if it is well-formed. // Check if it is well-formed.
idxs, errBraces := braceIndices(tpl) idxs, errBraces := braceIndices(tpl)
if errBraces != nil { if errBraces != nil {
@ -35,7 +35,7 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash
// Now let's parse it. // Now let's parse it.
defaultPattern := "[^/]+" defaultPattern := "[^/]+"
if matchQuery { if matchQuery {
defaultPattern = "[^?&]*" defaultPattern = ".*"
} else if matchHost { } else if matchHost {
defaultPattern = "[^.]+" defaultPattern = "[^.]+"
matchPrefix = false matchPrefix = false
@ -109,12 +109,20 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash
if errCompile != nil { if errCompile != nil {
return nil, errCompile return nil, errCompile
} }
// Check for capturing groups which used to work in older versions
if reg.NumSubexp() != len(idxs)/2 {
panic(fmt.Sprintf("route %s contains capture groups in its regexp. ", template) +
"Only non-capturing groups are accepted: e.g. (?:pattern) instead of (pattern)")
}
// Done! // Done!
return &routeRegexp{ return &routeRegexp{
template: template, template: template,
matchHost: matchHost, matchHost: matchHost,
matchQuery: matchQuery, matchQuery: matchQuery,
strictSlash: strictSlash, strictSlash: strictSlash,
useEncodedPath: useEncodedPath,
regexp: reg, regexp: reg,
reverse: reverse.String(), reverse: reverse.String(),
varsN: varsN, varsN: varsN,
@ -133,6 +141,9 @@ type routeRegexp struct {
matchQuery bool matchQuery bool
// The strictSlash value defined on the route, but disabled if PathPrefix was used. // The strictSlash value defined on the route, but disabled if PathPrefix was used.
strictSlash bool strictSlash bool
// Determines whether to use encoded path from getPath function or unencoded
// req.URL.Path for path matching
useEncodedPath bool
// Expanded regexp. // Expanded regexp.
regexp *regexp.Regexp regexp *regexp.Regexp
// Reverse template. // Reverse template.
@ -149,8 +160,11 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
if r.matchQuery { if r.matchQuery {
return r.matchQueryString(req) return r.matchQueryString(req)
} }
path := req.URL.Path
return r.regexp.MatchString(req.URL.Path) if r.useEncodedPath {
path = getPath(req)
}
return r.regexp.MatchString(path)
} }
return r.regexp.MatchString(getHost(req)) return r.regexp.MatchString(getHost(req))
@ -164,6 +178,9 @@ func (r *routeRegexp) url(values map[string]string) (string, error) {
if !ok { if !ok {
return "", fmt.Errorf("mux: missing route variable %q", v) return "", fmt.Errorf("mux: missing route variable %q", v)
} }
if r.matchQuery {
value = url.QueryEscape(value)
}
urlValues[k] = value urlValues[k] = value
} }
rv := fmt.Sprintf(r.reverse, urlValues...) rv := fmt.Sprintf(r.reverse, urlValues...)
@ -253,14 +270,18 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
extractVars(host, matches, v.host.varsN, m.Vars) extractVars(host, matches, v.host.varsN, m.Vars)
} }
} }
path := req.URL.Path
if r.useEncodedPath {
path = getPath(req)
}
// Store path variables. // Store path variables.
if v.path != nil { if v.path != nil {
matches := v.path.regexp.FindStringSubmatchIndex(req.URL.Path) matches := v.path.regexp.FindStringSubmatchIndex(path)
if len(matches) > 0 { if len(matches) > 0 {
extractVars(req.URL.Path, matches, v.path.varsN, m.Vars) extractVars(path, matches, v.path.varsN, m.Vars)
// Check if we should redirect. // Check if we should redirect.
if v.path.strictSlash { if v.path.strictSlash {
p1 := strings.HasSuffix(req.URL.Path, "/") p1 := strings.HasSuffix(path, "/")
p2 := strings.HasSuffix(v.path.template, "/") p2 := strings.HasSuffix(v.path.template, "/")
if p1 != p2 { if p1 != p2 {
u, _ := url.Parse(req.URL.String()) u, _ := url.Parse(req.URL.String())
@ -299,14 +320,7 @@ func getHost(r *http.Request) string {
} }
func extractVars(input string, matches []int, names []string, output map[string]string) { func extractVars(input string, matches []int, names []string, output map[string]string) {
matchesCount := 0 for i, name := range names {
prevEnd := -1 output[name] = input[matches[2*i+2]:matches[2*i+3]]
for i := 2; i < len(matches) && matchesCount < len(names); i += 2 {
if prevEnd < matches[i+1] {
value := input[matches[i]:matches[i+1]]
output[names[matchesCount]] = value
prevEnd = matches[i+1]
matchesCount++
}
} }
} }

View file

@ -29,6 +29,10 @@ type Route struct {
// If true, when the path pattern is "/path//to", accessing "/path//to" // If true, when the path pattern is "/path//to", accessing "/path//to"
// will not redirect // will not redirect
skipClean bool 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. // If true, this route never matches: it is only used to build URLs.
buildOnly bool buildOnly bool
// The name used to build URLs. // The name used to build URLs.
@ -166,14 +170,14 @@ func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery
} }
r.regexp = r.getRegexpGroup() r.regexp = r.getRegexpGroup()
if !matchHost && !matchQuery { if !matchHost && !matchQuery {
if len(tpl) == 0 || tpl[0] != '/' { if len(tpl) > 0 && tpl[0] != '/' {
return fmt.Errorf("mux: path must start with a slash, got %q", tpl) return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
} }
if r.regexp.path != nil { if r.regexp.path != nil {
tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
} }
} }
rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash) rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash, r.useEncodedPath)
if err != nil { if err != nil {
return err return err
} }
@ -407,6 +411,9 @@ func (r *Route) Schemes(schemes ...string) *Route {
for k, v := range schemes { for k, v := range schemes {
schemes[k] = strings.ToLower(v) schemes[k] = strings.ToLower(v)
} }
if r.buildScheme == "" && len(schemes) > 0 {
r.buildScheme = schemes[0]
}
return r.addMatcher(schemeMatcher(schemes)) return r.addMatcher(schemeMatcher(schemes))
} }
@ -490,22 +497,33 @@ func (r *Route) URL(pairs ...string) (*url.URL, error) {
return nil, err return nil, err
} }
var scheme, host, path string var scheme, host, path string
queries := make([]string, 0, len(r.regexp.queries))
if r.regexp.host != nil { if r.regexp.host != nil {
// Set a default scheme.
scheme = "http"
if host, err = r.regexp.host.url(values); err != nil { if host, err = r.regexp.host.url(values); err != nil {
return nil, err return nil, err
} }
scheme = "http"
if s := r.getBuildScheme(); s != "" {
scheme = s
}
} }
if r.regexp.path != nil { if r.regexp.path != nil {
if path, err = r.regexp.path.url(values); err != nil { if path, err = r.regexp.path.url(values); err != nil {
return nil, err return nil, err
} }
} }
for _, q := range r.regexp.queries {
var query string
if query, err = q.url(values); err != nil {
return nil, err
}
queries = append(queries, query)
}
return &url.URL{ return &url.URL{
Scheme: scheme, Scheme: scheme,
Host: host, Host: host,
Path: path, Path: path,
RawQuery: strings.Join(queries, "&"),
}, nil }, nil
} }
@ -527,10 +545,14 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &url.URL{ u := &url.URL{
Scheme: "http", Scheme: "http",
Host: host, Host: host,
}, nil }
if s := r.getBuildScheme(); s != "" {
u.Scheme = s
}
return u, nil
} }
// URLPath builds the path part of the URL for a route. See Route.URL(). // URLPath builds the path part of the URL for a route. See Route.URL().
@ -571,6 +593,36 @@ func (r *Route) GetPathTemplate() (string, error) {
return r.regexp.path.template, nil return r.regexp.path.template, nil
} }
// GetPathRegexp returns the expanded regular expression used to match route path.
// This is useful for building simple REST API documentation and for instrumentation
// against third-party services.
// An error will be returned if the route does not define a path.
func (r *Route) GetPathRegexp() (string, error) {
if r.err != nil {
return "", r.err
}
if r.regexp == nil || r.regexp.path == nil {
return "", errors.New("mux: route does not have a path")
}
return r.regexp.path.regexp.String(), nil
}
// 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.
func (r *Route) GetMethods() ([]string, error) {
if r.err != nil {
return nil, r.err
}
for _, m := range r.matchers {
if methods, ok := m.(methodMatcher); ok {
return []string(methods), nil
}
}
return nil, nil
}
// GetHostTemplate returns the template used to build the // GetHostTemplate returns the template used to build the
// route match. // route match.
// This is useful for building simple REST API documentation and for instrumentation // This is useful for building simple REST API documentation and for instrumentation
@ -612,11 +664,22 @@ func (r *Route) buildVars(m map[string]string) map[string]string {
// parentRoute allows routes to know about parent host and path definitions. // parentRoute allows routes to know about parent host and path definitions.
type parentRoute interface { type parentRoute interface {
getBuildScheme() string
getNamedRoutes() map[string]*Route getNamedRoutes() map[string]*Route
getRegexpGroup() *routeRegexpGroup getRegexpGroup() *routeRegexpGroup
buildVars(map[string]string) map[string]string 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. // getNamedRoutes returns the map where named routes are registered.
func (r *Route) getNamedRoutes() map[string]*Route { func (r *Route) getNamedRoutes() map[string]*Route {
if r.parent == nil { if r.parent == nil {