diff options
Diffstat (limited to 'vendor/github.com/gorilla/mux')
-rw-r--r-- | vendor/github.com/gorilla/mux/README.md | 50 | ||||
-rw-r--r-- | vendor/github.com/gorilla/mux/doc.go | 12 | ||||
-rw-r--r-- | vendor/github.com/gorilla/mux/mux.go | 48 | ||||
-rw-r--r-- | vendor/github.com/gorilla/mux/mux_test.go | 145 | ||||
-rw-r--r-- | vendor/github.com/gorilla/mux/old_test.go | 8 | ||||
-rw-r--r-- | vendor/github.com/gorilla/mux/regexp.go | 5 | ||||
-rw-r--r-- | vendor/github.com/gorilla/mux/route.go | 50 |
7 files changed, 282 insertions, 36 deletions
diff --git a/vendor/github.com/gorilla/mux/README.md b/vendor/github.com/gorilla/mux/README.md index 56c67134f..8dcd71887 100644 --- a/vendor/github.com/gorilla/mux/README.md +++ b/vendor/github.com/gorilla/mux/README.md @@ -15,7 +15,7 @@ The name mux stands for "HTTP request multiplexer". Like the standard `http.Serv * It implements the `http.Handler` interface so it is compatible with the standard `http.ServeMux`. * Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers. -* URL hosts and paths can have variables with an optional regular expression. +* URL hosts, paths and query values can have variables with an optional regular expression. * Registered URLs can be built, or "reversed", which helps maintaining references to resources. * Routes can be used as subrouters: nested routes are only tested if the parent route matches. This is useful to define groups of routes that share common conditions like a host, a path prefix or other repeated attributes. As a bonus, this optimizes request matching. @@ -24,9 +24,9 @@ The name mux stands for "HTTP request multiplexer". Like the standard `http.Serv * [Install](#install) * [Examples](#examples) * [Matching Routes](#matching-routes) -* [Listing Routes](#listing-routes) * [Static Files](#static-files) * [Registered URLs](#registered-urls) +* [Walking Routes](#walking-routes) * [Full Example](#full-example) --- @@ -168,7 +168,6 @@ s.HandleFunc("/{key}/", ProductHandler) // "/products/{key}/details" s.HandleFunc("/{key}/details", ProductDetailsHandler) ``` - ### Listing Routes Routes on a mux can be listed using the Router.Walk method—useful for generating documentation: @@ -191,9 +190,9 @@ func handler(w http.ResponseWriter, r *http.Request) { func main() { r := mux.NewRouter() r.HandleFunc("/", handler) - r.Methods("POST").HandleFunc("/products", handler) - r.Methods("GET").HandleFunc("/articles", handler) - r.Methods("GET", "PUT").HandleFunc("/articles/{id}", handler) + r.HandleFunc("/products", handler).Methods("POST") + r.HandleFunc("/articles", handler).Methods("GET") + r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT") r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { t, err := route.GetPathTemplate() if err != nil { @@ -269,19 +268,21 @@ url, err := r.Get("article").URL("category", "technology", "id", "42") "/articles/technology/42" ``` -This also works for host variables: +This also works for host and query value variables: ```go r := mux.NewRouter() r.Host("{subdomain}.domain.com"). Path("/articles/{category}/{id:[0-9]+}"). + Queries("filter", "{filter}"). HandlerFunc(ArticleHandler). 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", "category", "technology", - "id", "42") + "id", "42", + "filter", "gorilla") ``` All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a generated URL will always match a registered route -- the only exception is for explicitly defined "build-only" routes which never match. @@ -319,6 +320,37 @@ url, err := r.Get("article").URL("subdomain", "news", "id", "42") ``` +### Walking Routes + +The `Walk` function on `mux.Router` can be used to visit all of the routes that are registered on a router. For example, +the following prints all of the registered routes: + +```go +r := mux.NewRouter() +r.HandleFunc("/", handler) +r.HandleFunc("/products", handler).Methods("POST") +r.HandleFunc("/articles", handler).Methods("GET") +r.HandleFunc("/articles/{id}", handler).Methods("GET", "PUT") +r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { + t, err := route.GetPathTemplate() + if err != nil { + return err + } + // p will contain a regular expression that is compatible with regular expressions in Perl, Python, and other languages. + // For example, the regular expression for path '/articles/{id}' will be '^/articles/(?P<v0>[^/]+)$'. + p, err := route.GetPathRegexp() + if err != nil { + return err + } + m, err := route.GetMethods() + if err != nil { + return err + } + fmt.Println(strings.Join(m, ","), t, p) + return nil +}) +``` + ## Full Example Here's a complete, runnable example of a small `mux` based server: diff --git a/vendor/github.com/gorilla/mux/doc.go b/vendor/github.com/gorilla/mux/doc.go index 00daf4a72..cce30b2f0 100644 --- a/vendor/github.com/gorilla/mux/doc.go +++ b/vendor/github.com/gorilla/mux/doc.go @@ -12,8 +12,8 @@ or other conditions. The main features are: * Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers. - * URL hosts and paths can have variables with an optional regular - expression. + * URL hosts, paths and query values can have variables with an optional + regular expression. * Registered URLs can be built, or "reversed", which helps maintaining references to resources. * Routes can be used as subrouters: nested routes are only tested if the @@ -188,18 +188,20 @@ key/value pairs for the route variables. For the previous route, we would do: "/articles/technology/42" -This also works for host variables: +This also works for host and query value variables: r := mux.NewRouter() r.Host("{subdomain}.domain.com"). Path("/articles/{category}/{id:[0-9]+}"). + Queries("filter", "{filter}"). HandlerFunc(ArticleHandler). 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", "category", "technology", - "id", "42") + "id", "42", + "filter", "gorilla") All variables defined in the route are required, and their values must conform to the corresponding patterns. These requirements guarantee that a diff --git a/vendor/github.com/gorilla/mux/mux.go b/vendor/github.com/gorilla/mux/mux.go index d66ec3841..fb69196db 100644 --- a/vendor/github.com/gorilla/mux/mux.go +++ b/vendor/github.com/gorilla/mux/mux.go @@ -13,6 +13,10 @@ import ( "strings" ) +var ( + ErrMethodMismatch = errors.New("method is not allowed") +) + // NewRouter returns a new router instance. func NewRouter() *Router { return &Router{namedRoutes: make(map[string]*Route), KeepContext: false} @@ -39,6 +43,10 @@ func NewRouter() *Router { type Router struct { // Configurable Handler to be used when no route matches. NotFoundHandler http.Handler + + // Configurable Handler to be used when the request method does not match the route. + MethodNotAllowedHandler http.Handler + // Parent route, if this is a subrouter. parent parentRoute // Routes to be matched, in order. @@ -65,6 +73,11 @@ func (r *Router) Match(req *http.Request, match *RouteMatch) bool { } } + if match.MatchErr == ErrMethodMismatch && r.MethodNotAllowedHandler != nil { + match.Handler = r.MethodNotAllowedHandler + return true + } + // Closest match for a router (includes sub-routers) if r.NotFoundHandler != nil { match.Handler = r.NotFoundHandler @@ -105,9 +118,15 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { req = setVars(req, match.Vars) req = setCurrentRoute(req, match.Route) } + + if handler == nil && match.MatchErr == ErrMethodMismatch { + handler = methodNotAllowedHandler() + } + if handler == nil { handler = http.NotFoundHandler() } + if !r.KeepContext { defer contextClear(req) } @@ -176,6 +195,13 @@ func (r *Router) UseEncodedPath() *Router { // parentRoute // ---------------------------------------------------------------------------- +func (r *Router) getBuildScheme() string { + if r.parent != nil { + return r.parent.getBuildScheme() + } + return "" +} + // getNamedRoutes returns the map where named routes are registered. func (r *Router) getNamedRoutes() map[string]*Route { if r.namedRoutes == nil { @@ -299,10 +325,6 @@ 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 @@ -312,10 +334,12 @@ func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error { } for _, sr := range t.matchers { if h, ok := sr.(*Router); ok { + ancestors = append(ancestors, t) err := h.walk(walkFn, ancestors) if err != nil { return err } + ancestors = ancestors[:len(ancestors)-1] } } if h, ok := t.handler.(*Router); ok { @@ -339,6 +363,11 @@ type RouteMatch struct { Route *Route Handler http.Handler Vars map[string]string + + // MatchErr is set to appropriate matching error + // It is set to ErrMethodMismatch if there is a mismatch in + // the request method and route method + MatchErr error } type contextKey int @@ -458,7 +487,7 @@ func mapFromPairsToString(pairs ...string) (map[string]string, error) { return m, nil } -// mapFromPairsToRegex converts variadic string paramers to a +// mapFromPairsToRegex converts variadic string parameters to a // string to regex map. func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) { length, err := checkPairs(pairs...) @@ -540,3 +569,12 @@ func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]s } return true } + +// methodNotAllowed replies to the request with an HTTP status code 405. +func methodNotAllowed(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusMethodNotAllowed) +} + +// methodNotAllowedHandler returns a simple request handler +// that replies to each request with a status code 405. +func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) } diff --git a/vendor/github.com/gorilla/mux/mux_test.go b/vendor/github.com/gorilla/mux/mux_test.go index 19ef5a8cc..484fab431 100644 --- a/vendor/github.com/gorilla/mux/mux_test.go +++ b/vendor/github.com/gorilla/mux/mux_test.go @@ -11,6 +11,7 @@ import ( "fmt" "net/http" "net/url" + "reflect" "strings" "testing" ) @@ -35,6 +36,7 @@ type routeTest struct { scheme string // the expected scheme of the built URL host string // the expected host of the built URL path string // the expected path of the built URL + query string // the expected query string of the built URL pathTemplate string // the expected path template of the route hostTemplate string // the expected host template of the route methods []string // the expected route methods @@ -743,6 +745,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{}, host: "", path: "", + query: "foo=bar&baz=ding", shouldMatch: true, }, { @@ -752,6 +755,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{}, host: "", path: "", + query: "foo=bar&baz=ding", pathTemplate: `/api`, hostTemplate: `www.example.com`, shouldMatch: true, @@ -763,6 +767,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{}, host: "", path: "", + query: "foo=bar&baz=ding", pathTemplate: `/api`, hostTemplate: `www.example.com`, shouldMatch: true, @@ -783,6 +788,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{"v1": "bar"}, host: "", path: "", + query: "foo=bar", shouldMatch: true, }, { @@ -792,6 +798,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{"v1": "bar", "v2": "ding"}, host: "", path: "", + query: "foo=bar&baz=ding", shouldMatch: true, }, { @@ -801,6 +808,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{"v1": "10"}, host: "", path: "", + query: "foo=10", shouldMatch: true, }, { @@ -819,6 +827,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{"v1": "1"}, host: "", path: "", + query: "foo=1", shouldMatch: true, }, { @@ -828,6 +837,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{"v1": "1"}, host: "", path: "", + query: "foo=1", shouldMatch: true, }, { @@ -846,6 +856,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{"v1": "1a"}, host: "", path: "", + query: "foo=1a", shouldMatch: true, }, { @@ -864,6 +875,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{"v-1": "bar"}, host: "", path: "", + query: "foo=bar", shouldMatch: true, }, { @@ -873,6 +885,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{"v-1": "bar", "v-2": "ding"}, host: "", path: "", + query: "foo=bar&baz=ding", shouldMatch: true, }, { @@ -882,6 +895,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{"v-1": "10"}, host: "", path: "", + query: "foo=10", shouldMatch: true, }, { @@ -891,6 +905,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{"v-1": "1a"}, host: "", path: "", + query: "foo=1a", shouldMatch: true, }, { @@ -900,6 +915,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{}, host: "", path: "", + query: "foo=", shouldMatch: true, }, { @@ -918,6 +934,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{}, host: "", path: "", + query: "foo=", shouldMatch: true, }, { @@ -945,6 +962,7 @@ func TestQueries(t *testing.T) { vars: map[string]string{"foo": ""}, host: "", path: "", + query: "foo=", shouldMatch: true, }, { @@ -956,6 +974,16 @@ func TestQueries(t *testing.T) { path: "", shouldMatch: false, }, + { + title: "Queries route with pattern, match, escaped value", + route: new(Route).Queries("foo", "{v1}"), + request: newRequest("GET", "http://localhost?foo=%25bar%26%20%2F%3D%3F"), + vars: map[string]string{"v1": "%bar& /=?"}, + host: "", + path: "", + query: "foo=%25bar%26+%2F%3D%3F", + shouldMatch: true, + }, } for _, test := range tests { @@ -1187,6 +1215,28 @@ func TestSubRouter(t *testing.T) { pathTemplate: `/{category}`, shouldMatch: true, }, + { + title: "Build with scheme on parent router", + route: new(Route).Schemes("ftp").Host("google.com").Subrouter().Path("/"), + request: newRequest("GET", "ftp://google.com/"), + scheme: "ftp", + host: "google.com", + path: "/", + pathTemplate: `/`, + hostTemplate: `google.com`, + shouldMatch: true, + }, + { + title: "Prefer scheme on child route when building URLs", + route: new(Route).Schemes("https", "ftp").Host("google.com").Subrouter().Schemes("ftp").Path("/"), + request: newRequest("GET", "ftp://google.com/"), + scheme: "ftp", + host: "google.com", + path: "/", + pathTemplate: `/`, + hostTemplate: `google.com`, + shouldMatch: true, + }, } for _, test := range tests { @@ -1382,14 +1432,58 @@ func TestWalkNested(t *testing.T) { l2 := l1.PathPrefix("/l").Subrouter() l2.Path("/a") - paths := []string{"/g", "/g/o", "/g/o/r", "/g/o/r/i", "/g/o/r/i/l", "/g/o/r/i/l/l", "/g/o/r/i/l/l/a"} + testCases := []struct { + path string + ancestors []*Route + }{ + {"/g", []*Route{}}, + {"/g/o", []*Route{g.parent.(*Route)}}, + {"/g/o/r", []*Route{g.parent.(*Route), o.parent.(*Route)}}, + {"/g/o/r/i", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route)}}, + {"/g/o/r/i/l", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route), i.parent.(*Route)}}, + {"/g/o/r/i/l/l", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route), i.parent.(*Route), l1.parent.(*Route)}}, + {"/g/o/r/i/l/l/a", []*Route{g.parent.(*Route), o.parent.(*Route), r.parent.(*Route), i.parent.(*Route), l1.parent.(*Route), l2.parent.(*Route)}}, + } + idx := 0 err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error { - path := paths[idx] + path := testCases[idx].path tpl := route.regexp.path.template if tpl != path { t.Errorf(`Expected %s got %s`, path, tpl) } + currWantAncestors := testCases[idx].ancestors + if !reflect.DeepEqual(currWantAncestors, ancestors) { + t.Errorf(`Expected %+v got %+v`, currWantAncestors, ancestors) + } + idx++ + return nil + }) + if err != nil { + panic(err) + } + if idx != len(testCases) { + t.Errorf("Expected %d routes, found %d", len(testCases), idx) + } +} + +func TestWalkSubrouters(t *testing.T) { + router := NewRouter() + + g := router.Path("/g").Subrouter() + o := g.PathPrefix("/o").Subrouter() + o.Methods("GET") + o.Methods("PUT") + + // all 4 routes should be matched, but final 2 routes do not have path templates + paths := []string{"/g", "/g/o", "", ""} + idx := 0 + err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error { + path := paths[idx] + tpl, _ := route.GetPathTemplate() + if tpl != path { + t.Errorf(`Expected %s got %s`, path, tpl) + } idx++ return nil }) @@ -1492,6 +1586,7 @@ func testRoute(t *testing.T, test routeTest) { route := test.route vars := test.vars shouldMatch := test.shouldMatch + query := test.query shouldRedirect := test.shouldRedirect uri := url.URL{ Scheme: test.scheme, @@ -1561,6 +1656,13 @@ func testRoute(t *testing.T, test routeTest) { return } } + if query != "" { + u, _ := route.URL(mapToPairs(match.Vars)...) + if query != u.RawQuery { + t.Errorf("(%v) URL query not equal: expected %v, got %v", test.title, query, u.RawQuery) + return + } + } if shouldRedirect && match.Handler == nil { t.Errorf("(%v) Did not redirect", test.title) return @@ -1769,3 +1871,42 @@ func newRequest(method, url string) *http.Request { } return req } + +func TestNoMatchMethodErrorHandler(t *testing.T) { + func1 := func(w http.ResponseWriter, r *http.Request) {} + + r := NewRouter() + r.HandleFunc("/", func1).Methods("GET", "POST") + + req, _ := http.NewRequest("PUT", "http://localhost/", nil) + match := new(RouteMatch) + matched := r.Match(req, match) + + if matched { + t.Error("Should not have matched route for methods") + } + + if match.MatchErr != ErrMethodMismatch { + t.Error("Should get ErrMethodMismatch error") + } + + resp := NewRecorder() + r.ServeHTTP(resp, req) + if resp.Code != 405 { + t.Errorf("Expecting code %v", 405) + } + + // Add matching route + r.HandleFunc("/", func1).Methods("PUT") + + match = new(RouteMatch) + matched = r.Match(req, match) + + if !matched { + t.Error("Should have matched route for methods") + } + + if match.MatchErr != nil { + t.Error("Should not have any matching error. Found:", match.MatchErr) + } +} diff --git a/vendor/github.com/gorilla/mux/old_test.go b/vendor/github.com/gorilla/mux/old_test.go index 9bdc5e5d1..3751e4727 100644 --- a/vendor/github.com/gorilla/mux/old_test.go +++ b/vendor/github.com/gorilla/mux/old_test.go @@ -121,12 +121,7 @@ func TestRouteMatchers(t *testing.T) { var routeMatch RouteMatch matched := router.Match(request, &routeMatch) if matched != shouldMatch { - // Need better messages. :) - if matched { - t.Errorf("Should match.") - } else { - t.Errorf("Should not match.") - } + t.Errorf("Expected: %v\nGot: %v\nRequest: %v %v", shouldMatch, matched, request.Method, url) } if matched { @@ -188,7 +183,6 @@ func TestRouteMatchers(t *testing.T) { match(true) // 2nd route -------------------------------------------------------------- - // Everything match. reset2() match(true) diff --git a/vendor/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go index 0189ad346..80d1f7858 100644 --- a/vendor/github.com/gorilla/mux/regexp.go +++ b/vendor/github.com/gorilla/mux/regexp.go @@ -35,7 +35,7 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash, // Now let's parse it. defaultPattern := "[^/]+" if matchQuery { - defaultPattern = "[^?&]*" + defaultPattern = ".*" } else if matchHost { defaultPattern = "[^.]+" matchPrefix = false @@ -178,6 +178,9 @@ func (r *routeRegexp) url(values map[string]string) (string, error) { if !ok { return "", fmt.Errorf("mux: missing route variable %q", v) } + if r.matchQuery { + value = url.QueryEscape(value) + } urlValues[k] = value } rv := fmt.Sprintf(r.reverse, urlValues...) diff --git a/vendor/github.com/gorilla/mux/route.go b/vendor/github.com/gorilla/mux/route.go index 56dcbbdc5..6863adba5 100644 --- a/vendor/github.com/gorilla/mux/route.go +++ b/vendor/github.com/gorilla/mux/route.go @@ -52,12 +52,27 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool { if r.buildOnly || r.err != nil { return false } + + var matchErr error + // Match everything. for _, m := range r.matchers { if matched := m.Match(req, match); !matched { + if _, ok := m.(methodMatcher); ok { + matchErr = ErrMethodMismatch + continue + } + matchErr = nil return false } } + + if matchErr != nil { + match.MatchErr = matchErr + return false + } + + match.MatchErr = nil // Yay, we have a match. Let's collect some info about it. if match.Route == nil { match.Route = r @@ -68,6 +83,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool { if match.Vars == nil { match.Vars = make(map[string]string) } + // Set variables. if r.regexp != nil { r.regexp.setMatch(req, match, r) @@ -482,13 +498,14 @@ func (r *Route) URL(pairs ...string) (*url.URL, error) { return nil, err } var scheme, host, path string + queries := make([]string, 0, len(r.regexp.queries)) if r.regexp.host != nil { if host, err = r.regexp.host.url(values); err != nil { return nil, err } scheme = "http" - if r.buildScheme != "" { - scheme = r.buildScheme + if s := r.getBuildScheme(); s != "" { + scheme = s } } if r.regexp.path != nil { @@ -496,10 +513,18 @@ func (r *Route) URL(pairs ...string) (*url.URL, error) { 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{ - Scheme: scheme, - Host: host, - Path: path, + Scheme: scheme, + Host: host, + Path: path, + RawQuery: strings.Join(queries, "&"), }, nil } @@ -525,8 +550,8 @@ func (r *Route) URLHost(pairs ...string) (*url.URL, error) { Scheme: "http", Host: host, } - if r.buildScheme != "" { - u.Scheme = r.buildScheme + if s := r.getBuildScheme(); s != "" { + u.Scheme = s } return u, nil } @@ -640,11 +665,22 @@ func (r *Route) buildVars(m map[string]string) map[string]string { // 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 { |