summaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/gorilla/mux
diff options
context:
space:
mode:
author=Corey Hulen <corey@hulen.com>2015-11-23 15:53:48 -0800
committer=Corey Hulen <corey@hulen.com>2015-11-23 15:53:48 -0800
commit4f4cd5e63573da4d6edcc7d4213afaca67c19f88 (patch)
treecefbc7af53629d97644ca2f6b2369e9d879f0101 /Godeps/_workspace/src/github.com/gorilla/mux
parentf8a3c9a14edca6df0647d89cf225f2470cbe025c (diff)
downloadchat-4f4cd5e63573da4d6edcc7d4213afaca67c19f88.tar.gz
chat-4f4cd5e63573da4d6edcc7d4213afaca67c19f88.tar.bz2
chat-4f4cd5e63573da4d6edcc7d4213afaca67c19f88.zip
upgrading libs
Diffstat (limited to 'Godeps/_workspace/src/github.com/gorilla/mux')
-rw-r--r--Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml15
-rw-r--r--Godeps/_workspace/src/github.com/gorilla/mux/README.md232
-rw-r--r--Godeps/_workspace/src/github.com/gorilla/mux/doc.go19
-rw-r--r--Godeps/_workspace/src/github.com/gorilla/mux/mux.go124
-rw-r--r--Godeps/_workspace/src/github.com/gorilla/mux/mux_test.go324
-rw-r--r--Godeps/_workspace/src/github.com/gorilla/mux/old_test.go6
-rw-r--r--Godeps/_workspace/src/github.com/gorilla/mux/regexp.go79
-rw-r--r--Godeps/_workspace/src/github.com/gorilla/mux/route.go46
8 files changed, 793 insertions, 52 deletions
diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml b/Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml
index d87d46576..83ab8f59d 100644
--- a/Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml
+++ b/Godeps/_workspace/src/github.com/gorilla/mux/.travis.yml
@@ -1,7 +1,14 @@
language: go
-
+sudo: false
go:
- - 1.0
- - 1.1
- - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
- tip
+install:
+ - go get golang.org/x/tools/cmd/vet
+script:
+ - go get -t -v ./...
+ - diff -u <(echo -n) <(gofmt -d -s .)
+ - go tool vet .
+ - go test -v -race ./...
diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/README.md b/Godeps/_workspace/src/github.com/gorilla/mux/README.md
index e60301b03..55dd4e59a 100644
--- a/Godeps/_workspace/src/github.com/gorilla/mux/README.md
+++ b/Godeps/_workspace/src/github.com/gorilla/mux/README.md
@@ -1,7 +1,235 @@
mux
===
+[![GoDoc](https://godoc.org/github.com/gorilla/mux?status.svg)](https://godoc.org/github.com/gorilla/mux)
[![Build Status](https://travis-ci.org/gorilla/mux.png?branch=master)](https://travis-ci.org/gorilla/mux)
-gorilla/mux is a powerful URL router and dispatcher.
+Package gorilla/mux implements a request router and dispatcher.
-Read the full documentation here: http://www.gorillatoolkit.org/pkg/mux
+The name mux stands for "HTTP request multiplexer". Like the standard
+http.ServeMux, mux.Router matches incoming requests against a list of
+registered routes and calls a handler for the route that matches the URL
+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.
+ * 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.
+ * It implements the http.Handler interface so it is compatible with the
+ standard http.ServeMux.
+
+Let's start registering a couple of URL paths and handlers:
+
+ func main() {
+ r := mux.NewRouter()
+ r.HandleFunc("/", HomeHandler)
+ r.HandleFunc("/products", ProductsHandler)
+ r.HandleFunc("/articles", ArticlesHandler)
+ http.Handle("/", r)
+ }
+
+Here we register three routes mapping URL paths to handlers. This is
+equivalent to how http.HandleFunc() works: if an incoming request URL matches
+one of the paths, the corresponding handler is called passing
+(http.ResponseWriter, *http.Request) as parameters.
+
+Paths can have variables. They are defined using the format {name} or
+{name:pattern}. If a regular expression pattern is not defined, the matched
+variable will be anything until the next slash. For example:
+
+ r := mux.NewRouter()
+ r.HandleFunc("/products/{key}", ProductHandler)
+ r.HandleFunc("/articles/{category}/", ArticlesCategoryHandler)
+ r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler)
+
+The names are used to create a map of route variables which can be retrieved
+calling mux.Vars():
+
+ vars := mux.Vars(request)
+ category := vars["category"]
+
+And this is all you need to know about the basic usage. More advanced options
+are explained below.
+
+Routes can also be restricted to a domain or subdomain. Just define a host
+pattern to be matched. They can also have variables:
+
+ r := mux.NewRouter()
+ // Only matches if domain is "www.example.com".
+ r.Host("www.example.com")
+ // Matches a dynamic subdomain.
+ r.Host("{subdomain:[a-z]+}.domain.com")
+
+There are several other matchers that can be added. To match path prefixes:
+
+ r.PathPrefix("/products/")
+
+...or HTTP methods:
+
+ r.Methods("GET", "POST")
+
+...or URL schemes:
+
+ r.Schemes("https")
+
+...or header values:
+
+ r.Headers("X-Requested-With", "XMLHttpRequest")
+
+...or query values:
+
+ r.Queries("key", "value")
+
+...or to use a custom matcher function:
+
+ r.MatcherFunc(func(r *http.Request, rm *RouteMatch) bool {
+ return r.ProtoMajor == 0
+ })
+
+...and finally, it is possible to combine several matchers in a single route:
+
+ r.HandleFunc("/products", ProductsHandler).
+ Host("www.example.com").
+ Methods("GET").
+ Schemes("http")
+
+Setting the same matching conditions again and again can be boring, so we have
+a way to group several routes that share the same requirements.
+We call it "subrouting".
+
+For example, let's say we have several URLs that should only match when the
+host is `www.example.com`. Create a route for that host and get a "subrouter"
+from it:
+
+ r := mux.NewRouter()
+ s := r.Host("www.example.com").Subrouter()
+
+Then register routes in the subrouter:
+
+ s.HandleFunc("/products/", ProductsHandler)
+ s.HandleFunc("/products/{key}", ProductHandler)
+ s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
+
+The three URL paths we registered above will only be tested if the domain is
+`www.example.com`, because the subrouter is tested first. This is not
+only convenient, but also optimizes request matching. You can create
+subrouters combining any attribute matchers accepted by a route.
+
+Subrouters can be used to create domain or path "namespaces": you define
+subrouters in a central place and then parts of the app can register its
+paths relatively to a given subrouter.
+
+There's one more thing about subroutes. When a subrouter has a path prefix,
+the inner routes use it as base for their paths:
+
+ r := mux.NewRouter()
+ s := r.PathPrefix("/products").Subrouter()
+ // "/products/"
+ s.HandleFunc("/", ProductsHandler)
+ // "/products/{key}/"
+ s.HandleFunc("/{key}/", ProductHandler)
+ // "/products/{key}/details"
+ s.HandleFunc("/{key}/details", ProductDetailsHandler)
+
+Now let's see how to build registered URLs.
+
+Routes can be named. All routes that define a name can have their URLs built,
+or "reversed". We define a name calling Name() on a route. For example:
+
+ r := mux.NewRouter()
+ r.HandleFunc("/articles/{category}/{id:[0-9]+}", ArticleHandler).
+ Name("article")
+
+To build a URL, get the route and call the URL() method, passing a sequence of
+key/value pairs for the route variables. For the previous route, we would do:
+
+ url, err := r.Get("article").URL("category", "technology", "id", "42")
+
+...and the result will be a url.URL with the following path:
+
+ "/articles/technology/42"
+
+This also works for host variables:
+
+ r := mux.NewRouter()
+ r.Host("{subdomain}.domain.com").
+ Path("/articles/{category}/{id:[0-9]+}").
+ HandlerFunc(ArticleHandler).
+ Name("article")
+
+ // url.String() will be "http://news.domain.com/articles/technology/42"
+ url, err := r.Get("article").URL("subdomain", "news",
+ "category", "technology",
+ "id", "42")
+
+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.
+
+Regex support also exists for matching Headers within a route. For example, we could do:
+
+ r.HeadersRegexp("Content-Type", "application/(text|json)")
+
+...and the route will match both requests with a Content-Type of `application/json` as well as
+`application/text`
+
+There's also a way to build only the URL host or path for a route:
+use the methods URLHost() or URLPath() instead. For the previous route,
+we would do:
+
+ // "http://news.domain.com/"
+ host, err := r.Get("article").URLHost("subdomain", "news")
+
+ // "/articles/technology/42"
+ path, err := r.Get("article").URLPath("category", "technology", "id", "42")
+
+And if you use subrouters, host and path defined separately can be built
+as well:
+
+ r := mux.NewRouter()
+ s := r.Host("{subdomain}.domain.com").Subrouter()
+ s.Path("/articles/{category}/{id:[0-9]+}").
+ HandlerFunc(ArticleHandler).
+ Name("article")
+
+ // "http://news.domain.com/articles/technology/42"
+ url, err := r.Get("article").URL("subdomain", "news",
+ "category", "technology",
+ "id", "42")
+
+## Full Example
+
+Here's a complete, runnable example of a small mux based server:
+
+```go
+package main
+
+import (
+ "net/http"
+
+ "github.com/gorilla/mux"
+)
+
+func YourHandler(w http.ResponseWriter, r *http.Request) {
+ w.Write([]byte("Gorilla!\n"))
+}
+
+func main() {
+ r := mux.NewRouter()
+ // Routes consist of a path and a handler function.
+ r.HandleFunc("/", YourHandler)
+
+ // Bind to a port and pass our router in
+ http.ListenAndServe(":8000", r)
+}
+```
+
+## License
+
+BSD licensed. See the LICENSE file for details.
diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/doc.go b/Godeps/_workspace/src/github.com/gorilla/mux/doc.go
index 9a5e381a2..49798cb5c 100644
--- a/Godeps/_workspace/src/github.com/gorilla/mux/doc.go
+++ b/Godeps/_workspace/src/github.com/gorilla/mux/doc.go
@@ -60,8 +60,8 @@ Routes can also be restricted to a domain or subdomain. Just define a host
pattern to be matched. They can also have variables:
r := mux.NewRouter()
- // Only matches if domain is "www.domain.com".
- r.Host("www.domain.com")
+ // Only matches if domain is "www.example.com".
+ r.Host("www.example.com")
// Matches a dynamic subdomain.
r.Host("{subdomain:[a-z]+}.domain.com")
@@ -94,7 +94,7 @@ There are several other matchers that can be added. To match path prefixes:
...and finally, it is possible to combine several matchers in a single route:
r.HandleFunc("/products", ProductsHandler).
- Host("www.domain.com").
+ Host("www.example.com").
Methods("GET").
Schemes("http")
@@ -103,11 +103,11 @@ a way to group several routes that share the same requirements.
We call it "subrouting".
For example, let's say we have several URLs that should only match when the
-host is "www.domain.com". Create a route for that host and get a "subrouter"
+host is "www.example.com". Create a route for that host and get a "subrouter"
from it:
r := mux.NewRouter()
- s := r.Host("www.domain.com").Subrouter()
+ s := r.Host("www.example.com").Subrouter()
Then register routes in the subrouter:
@@ -116,7 +116,7 @@ Then register routes in the subrouter:
s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
The three URL paths we registered above will only be tested if the domain is
-"www.domain.com", because the subrouter is tested first. This is not
+"www.example.com", because the subrouter is tested first. This is not
only convenient, but also optimizes request matching. You can create
subrouters combining any attribute matchers accepted by a route.
@@ -172,6 +172,13 @@ 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.
+Regex support also exists for matching Headers within a route. For example, we could do:
+
+ r.HeadersRegexp("Content-Type", "application/(text|json)")
+
+...and the route will match both requests with a Content-Type of `application/json` as well as
+`application/text`
+
There's also a way to build only the URL host or path for a route:
use the methods URLHost() or URLPath() instead. For the previous route,
we would do:
diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/mux.go b/Godeps/_workspace/src/github.com/gorilla/mux/mux.go
index af31d2395..3c51cca5e 100644
--- a/Godeps/_workspace/src/github.com/gorilla/mux/mux.go
+++ b/Godeps/_workspace/src/github.com/gorilla/mux/mux.go
@@ -5,11 +5,13 @@
package mux
import (
+ "errors"
"fmt"
"net/http"
"path"
+ "regexp"
- "github.com/gorilla/context"
+ "github.com/mattermost/platform/Godeps/_workspace/src/github.com/gorilla/context"
)
// NewRouter returns a new router instance.
@@ -68,7 +70,7 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// Clean path to canonical form and redirect.
if p := cleanPath(req.URL.Path); p != req.URL.Path {
- // Added 3 lines (Philip Schlump) - It was droping 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:
// http://code.google.com/p/go/issues/detail?id=5252
url := *req.URL
@@ -237,6 +239,52 @@ 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
+ }
+ 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
// ----------------------------------------------------------------------------
@@ -264,6 +312,10 @@ func Vars(r *http.Request) map[string]string {
}
// 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 := context.Get(r, routeKey); rv != nil {
return rv.(*Route)
@@ -313,13 +365,24 @@ func uniqueVars(s1, s2 []string) error {
return nil
}
-// mapFromPairs converts variadic string parameters to a string map.
-func mapFromPairs(pairs ...string) (map[string]string, error) {
+// 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 nil, fmt.Errorf(
+ 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]
@@ -327,6 +390,24 @@ func mapFromPairs(pairs ...string) (map[string]string, error) {
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 {
@@ -337,9 +418,8 @@ func matchInArray(arr []string, value string) bool {
return false
}
-// matchMap returns true if the given key/value pairs exist in a given map.
-func matchMap(toCheck map[string]string, toMatch map[string][]string,
- canonicalKey bool) bool {
+// 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 {
@@ -364,3 +444,31 @@ func matchMap(toCheck map[string]string, toMatch map[string][]string,
}
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
+}
diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/mux_test.go b/Godeps/_workspace/src/github.com/gorilla/mux/mux_test.go
index 6b2c1d22f..0f9bde0c1 100644
--- a/Godeps/_workspace/src/github.com/gorilla/mux/mux_test.go
+++ b/Godeps/_workspace/src/github.com/gorilla/mux/mux_test.go
@@ -7,11 +7,24 @@ package mux
import (
"fmt"
"net/http"
+ "strings"
"testing"
- "github.com/gorilla/context"
+ "github.com/mattermost/platform/Godeps/_workspace/src/github.com/gorilla/context"
)
+func (r *Route) GoString() string {
+ matchers := make([]string, len(r.matchers))
+ for i, m := range r.matchers {
+ matchers[i] = fmt.Sprintf("%#v", m)
+ }
+ return fmt.Sprintf("&Route{matchers:[]matcher{%s}}", strings.Join(matchers, ", "))
+}
+
+func (r *routeRegexp) GoString() string {
+ return fmt.Sprintf("&routeRegexp{template: %q, matchHost: %t, matchQuery: %t, strictSlash: %t, regexp: regexp.MustCompile(%q), reverse: %q, varsN: %v, varsR: %v", r.template, r.matchHost, r.matchQuery, r.strictSlash, r.regexp.String(), r.reverse, r.varsN, r.varsR)
+}
+
type routeTest struct {
title string // title of the test
route *Route // the route being tested
@@ -109,6 +122,15 @@ func TestHost(t *testing.T) {
shouldMatch: true,
},
{
+ title: "Host route with pattern, additional capturing group, match",
+ route: new(Route).Host("aaa.{v1:[a-z]{2}(b|c)}.ccc"),
+ request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
+ vars: map[string]string{"v1": "bbb"},
+ host: "aaa.bbb.ccc",
+ path: "",
+ shouldMatch: true,
+ },
+ {
title: "Host route with pattern, wrong host in request URL",
route: new(Route).Host("aaa.{v1:[a-z]{3}}.ccc"),
request: newRequest("GET", "http://aaa.222.ccc/111/222/333"),
@@ -136,6 +158,33 @@ func TestHost(t *testing.T) {
shouldMatch: false,
},
{
+ title: "Host route with hyphenated name and pattern, match",
+ route: new(Route).Host("aaa.{v-1:[a-z]{3}}.ccc"),
+ request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
+ vars: map[string]string{"v-1": "bbb"},
+ host: "aaa.bbb.ccc",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Host route with hyphenated name and pattern, additional capturing group, match",
+ route: new(Route).Host("aaa.{v-1:[a-z]{2}(b|c)}.ccc"),
+ request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
+ vars: map[string]string{"v-1": "bbb"},
+ host: "aaa.bbb.ccc",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Host route with multiple hyphenated names and patterns, match",
+ route: new(Route).Host("{v-1:[a-z]{3}}.{v-2:[a-z]{3}}.{v-3:[a-z]{3}}"),
+ request: newRequest("GET", "http://aaa.bbb.ccc/111/222/333"),
+ vars: map[string]string{"v-1": "aaa", "v-2": "bbb", "v-3": "ccc"},
+ host: "aaa.bbb.ccc",
+ path: "",
+ shouldMatch: true,
+ },
+ {
title: "Path route with single pattern with pipe, match",
route: new(Route).Path("/{category:a|b/c}"),
request: newRequest("GET", "http://localhost/a"),
@@ -260,6 +309,42 @@ func TestPath(t *testing.T) {
path: "/111/222/333",
shouldMatch: false,
},
+ {
+ title: "Path route with multiple patterns with pipe, match",
+ route: new(Route).Path("/{category:a|(b/c)}/{product}/{id:[0-9]+}"),
+ request: newRequest("GET", "http://localhost/a/product_name/1"),
+ vars: map[string]string{"category": "a", "product": "product_name", "id": "1"},
+ host: "",
+ path: "/a/product_name/1",
+ shouldMatch: true,
+ },
+ {
+ title: "Path route with hyphenated name and pattern, match",
+ route: new(Route).Path("/111/{v-1:[0-9]{3}}/333"),
+ request: newRequest("GET", "http://localhost/111/222/333"),
+ vars: map[string]string{"v-1": "222"},
+ host: "",
+ path: "/111/222/333",
+ shouldMatch: true,
+ },
+ {
+ title: "Path route with multiple hyphenated names and patterns, match",
+ route: new(Route).Path("/{v-1:[0-9]{3}}/{v-2:[0-9]{3}}/{v-3:[0-9]{3}}"),
+ request: newRequest("GET", "http://localhost/111/222/333"),
+ vars: map[string]string{"v-1": "111", "v-2": "222", "v-3": "333"},
+ host: "",
+ path: "/111/222/333",
+ shouldMatch: true,
+ },
+ {
+ title: "Path route with multiple hyphenated names and patterns with pipe, match",
+ route: new(Route).Path("/{product-category:a|(b/c)}/{product-name}/{product-id:[0-9]+}"),
+ request: newRequest("GET", "http://localhost/a/product_name/1"),
+ vars: map[string]string{"product-category": "a", "product-name": "product_name", "product-id": "1"},
+ host: "",
+ path: "/a/product_name/1",
+ shouldMatch: true,
+ },
}
for _, test := range tests {
@@ -434,6 +519,24 @@ func TestHeaders(t *testing.T) {
path: "",
shouldMatch: false,
},
+ {
+ title: "Headers route, regex header values to match",
+ route: new(Route).Headers("foo", "ba[zr]"),
+ request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "bar"}),
+ vars: map[string]string{},
+ host: "",
+ path: "",
+ shouldMatch: false,
+ },
+ {
+ title: "Headers route, regex header values to match",
+ route: new(Route).HeadersRegexp("foo", "ba[zr]"),
+ request: newRequestHeaders("GET", "http://localhost", map[string]string{"foo": "baz"}),
+ vars: map[string]string{},
+ host: "",
+ path: "",
+ shouldMatch: true,
+ },
}
for _, test := range tests {
@@ -552,6 +655,150 @@ func TestQueries(t *testing.T) {
path: "",
shouldMatch: false,
},
+ {
+ title: "Queries route with regexp pattern with quantifier, match",
+ route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
+ request: newRequest("GET", "http://localhost?foo=1"),
+ vars: map[string]string{"v1": "1"},
+ host: "",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Queries route with regexp pattern with quantifier, additional variable in query string, match",
+ route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
+ request: newRequest("GET", "http://localhost?bar=2&foo=1"),
+ vars: map[string]string{"v1": "1"},
+ host: "",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Queries route with regexp pattern with quantifier, regexp does not match",
+ route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
+ request: newRequest("GET", "http://localhost?foo=12"),
+ vars: map[string]string{},
+ host: "",
+ path: "",
+ shouldMatch: false,
+ },
+ {
+ title: "Queries route with regexp pattern with quantifier, additional capturing group",
+ route: new(Route).Queries("foo", "{v1:[0-9]{1}(a|b)}"),
+ request: newRequest("GET", "http://localhost?foo=1a"),
+ vars: map[string]string{"v1": "1a"},
+ host: "",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Queries route with regexp pattern with quantifier, additional variable in query string, regexp does not match",
+ route: new(Route).Queries("foo", "{v1:[0-9]{1}}"),
+ request: newRequest("GET", "http://localhost?foo=12"),
+ vars: map[string]string{},
+ host: "",
+ path: "",
+ shouldMatch: false,
+ },
+ {
+ title: "Queries route with hyphenated name, match",
+ route: new(Route).Queries("foo", "{v-1}"),
+ request: newRequest("GET", "http://localhost?foo=bar"),
+ vars: map[string]string{"v-1": "bar"},
+ host: "",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Queries route with multiple hyphenated names, match",
+ route: new(Route).Queries("foo", "{v-1}", "baz", "{v-2}"),
+ request: newRequest("GET", "http://localhost?foo=bar&baz=ding"),
+ vars: map[string]string{"v-1": "bar", "v-2": "ding"},
+ host: "",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Queries route with hyphenate name and pattern, match",
+ route: new(Route).Queries("foo", "{v-1:[0-9]+}"),
+ request: newRequest("GET", "http://localhost?foo=10"),
+ vars: map[string]string{"v-1": "10"},
+ host: "",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Queries route with hyphenated name and pattern with quantifier, additional capturing group",
+ route: new(Route).Queries("foo", "{v-1:[0-9]{1}(a|b)}"),
+ request: newRequest("GET", "http://localhost?foo=1a"),
+ vars: map[string]string{"v-1": "1a"},
+ host: "",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Queries route with empty value, should match",
+ route: new(Route).Queries("foo", ""),
+ request: newRequest("GET", "http://localhost?foo=bar"),
+ vars: map[string]string{},
+ host: "",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Queries route with empty value and no parameter in request, should not match",
+ route: new(Route).Queries("foo", ""),
+ request: newRequest("GET", "http://localhost"),
+ vars: map[string]string{},
+ host: "",
+ path: "",
+ shouldMatch: false,
+ },
+ {
+ title: "Queries route with empty value and empty parameter in request, should match",
+ route: new(Route).Queries("foo", ""),
+ request: newRequest("GET", "http://localhost?foo="),
+ vars: map[string]string{},
+ host: "",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Queries route with overlapping value, should not match",
+ route: new(Route).Queries("foo", "bar"),
+ request: newRequest("GET", "http://localhost?foo=barfoo"),
+ vars: map[string]string{},
+ host: "",
+ path: "",
+ shouldMatch: false,
+ },
+ {
+ title: "Queries route with no parameter in request, should not match",
+ route: new(Route).Queries("foo", "{bar}"),
+ request: newRequest("GET", "http://localhost"),
+ vars: map[string]string{},
+ host: "",
+ path: "",
+ shouldMatch: false,
+ },
+ {
+ title: "Queries route with empty parameter in request, should match",
+ route: new(Route).Queries("foo", "{bar}"),
+ request: newRequest("GET", "http://localhost?foo="),
+ vars: map[string]string{"foo": ""},
+ host: "",
+ path: "",
+ shouldMatch: true,
+ },
+ {
+ title: "Queries route, bad submatch",
+ route: new(Route).Queries("foo", "bar", "baz", "ding"),
+ request: newRequest("GET", "http://localhost?fffoo=bar&baz=dingggg"),
+ vars: map[string]string{},
+ host: "",
+ path: "",
+ shouldMatch: false,
+ },
}
for _, test := range tests {
@@ -801,6 +1048,81 @@ func TestStrictSlash(t *testing.T) {
}
}
+func TestWalkSingleDepth(t *testing.T) {
+ r0 := NewRouter()
+ r1 := NewRouter()
+ r2 := NewRouter()
+
+ r0.Path("/g")
+ r0.Path("/o")
+ r0.Path("/d").Handler(r1)
+ r0.Path("/r").Handler(r2)
+ r0.Path("/a")
+
+ r1.Path("/z")
+ r1.Path("/i")
+ r1.Path("/l")
+ r1.Path("/l")
+
+ r2.Path("/i")
+ r2.Path("/l")
+ r2.Path("/l")
+
+ paths := []string{"g", "o", "r", "i", "l", "l", "a"}
+ depths := []int{0, 0, 0, 1, 1, 1, 0}
+ i := 0
+ err := r0.Walk(func(route *Route, router *Router, ancestors []*Route) error {
+ matcher := route.matchers[0].(*routeRegexp)
+ if matcher.template == "/d" {
+ return SkipRouter
+ }
+ if len(ancestors) != depths[i] {
+ t.Errorf(`Expected depth of %d at i = %d; got "%d"`, depths[i], i, len(ancestors))
+ }
+ if matcher.template != "/"+paths[i] {
+ t.Errorf(`Expected "/%s" at i = %d; got "%s"`, paths[i], i, matcher.template)
+ }
+ i++
+ return nil
+ })
+ if err != nil {
+ panic(err)
+ }
+ if i != len(paths) {
+ t.Errorf("Expected %d routes, found %d", len(paths), i)
+ }
+}
+
+func TestWalkNested(t *testing.T) {
+ router := NewRouter()
+
+ g := router.Path("/g").Subrouter()
+ o := g.PathPrefix("/o").Subrouter()
+ r := o.PathPrefix("/r").Subrouter()
+ i := r.PathPrefix("/i").Subrouter()
+ l1 := i.PathPrefix("/l").Subrouter()
+ 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"}
+ idx := 0
+ err := router.Walk(func(route *Route, router *Router, ancestors []*Route) error {
+ path := paths[idx]
+ tpl := route.regexp.path.template
+ if tpl != path {
+ t.Errorf(`Expected %s got %s`, path, tpl)
+ }
+ idx++
+ return nil
+ })
+ if err != nil {
+ panic(err)
+ }
+ if idx != len(paths) {
+ t.Errorf("Expected %d routes, found %d", len(paths), idx)
+ }
+}
+
// ----------------------------------------------------------------------------
// Helpers
// ----------------------------------------------------------------------------
diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/old_test.go b/Godeps/_workspace/src/github.com/gorilla/mux/old_test.go
index 1f7c190c0..755db483e 100644
--- a/Godeps/_workspace/src/github.com/gorilla/mux/old_test.go
+++ b/Godeps/_workspace/src/github.com/gorilla/mux/old_test.go
@@ -545,7 +545,7 @@ func TestMatchedRouteName(t *testing.T) {
router := NewRouter()
route := router.NewRoute().Path("/products/").Name(routeName)
- url := "http://www.domain.com/products/"
+ url := "http://www.example.com/products/"
request, _ := http.NewRequest("GET", url, nil)
var rv RouteMatch
ok := router.Match(request, &rv)
@@ -563,10 +563,10 @@ func TestMatchedRouteName(t *testing.T) {
func TestSubRouting(t *testing.T) {
// Example from docs.
router := NewRouter()
- subrouter := router.NewRoute().Host("www.domain.com").Subrouter()
+ subrouter := router.NewRoute().Host("www.example.com").Subrouter()
route := subrouter.NewRoute().Path("/products/").Name("products")
- url := "http://www.domain.com/products/"
+ url := "http://www.example.com/products/"
request, _ := http.NewRequest("GET", url, nil)
var rv RouteMatch
ok := router.Match(request, &rv)
diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/regexp.go b/Godeps/_workspace/src/github.com/gorilla/mux/regexp.go
index aa3067986..06728dd54 100644
--- a/Godeps/_workspace/src/github.com/gorilla/mux/regexp.go
+++ b/Godeps/_workspace/src/github.com/gorilla/mux/regexp.go
@@ -10,6 +10,7 @@ import (
"net/http"
"net/url"
"regexp"
+ "strconv"
"strings"
)
@@ -34,8 +35,7 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash
// Now let's parse it.
defaultPattern := "[^/]+"
if matchQuery {
- defaultPattern = "[^?&]+"
- matchPrefix = true
+ defaultPattern = "[^?&]*"
} else if matchHost {
defaultPattern = "[^.]+"
matchPrefix = false
@@ -53,9 +53,7 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash
varsN := make([]string, len(idxs)/2)
varsR := make([]*regexp.Regexp, len(idxs)/2)
pattern := bytes.NewBufferString("")
- if !matchQuery {
- pattern.WriteByte('^')
- }
+ pattern.WriteByte('^')
reverse := bytes.NewBufferString("")
var end int
var err error
@@ -75,12 +73,14 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash
tpl[idxs[i]:end])
}
// Build the regexp pattern.
- fmt.Fprintf(pattern, "%s(%s)", regexp.QuoteMeta(raw), patt)
+ varIdx := i / 2
+ fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(varIdx), patt)
// Build the reverse template.
fmt.Fprintf(reverse, "%s%%s", raw)
+
// Append variable name and compiled pattern.
- varsN[i/2] = name
- varsR[i/2], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
+ varsN[varIdx] = name
+ varsR[varIdx], err = regexp.Compile(fmt.Sprintf("^%s$", patt))
if err != nil {
return nil, err
}
@@ -91,6 +91,12 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash
if strictSlash {
pattern.WriteString("[/]?")
}
+ if matchQuery {
+ // Add the default pattern if the query value is empty
+ if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" {
+ pattern.WriteString(defaultPattern)
+ }
+ }
if !matchPrefix {
pattern.WriteByte('$')
}
@@ -141,7 +147,7 @@ type routeRegexp struct {
func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
if !r.matchHost {
if r.matchQuery {
- return r.regexp.MatchString(req.URL.RawQuery)
+ return r.matchQueryString(req)
} else {
return r.regexp.MatchString(req.URL.Path)
}
@@ -175,6 +181,26 @@ func (r *routeRegexp) url(values map[string]string) (string, error) {
return rv, nil
}
+// getUrlQuery returns a single query parameter from a request URL.
+// For a URL with foo=bar&baz=ding, we return only the relevant key
+// value pair for the routeRegexp.
+func (r *routeRegexp) getUrlQuery(req *http.Request) string {
+ if !r.matchQuery {
+ return ""
+ }
+ templateKey := strings.SplitN(r.template, "=", 2)[0]
+ for key, vals := range req.URL.Query() {
+ if key == templateKey && len(vals) > 0 {
+ return key + "=" + vals[0]
+ }
+ }
+ return ""
+}
+
+func (r *routeRegexp) matchQueryString(req *http.Request) bool {
+ return r.regexp.MatchString(r.getUrlQuery(req))
+}
+
// braceIndices returns the first level curly brace indices from a string.
// It returns an error in case of unbalanced braces.
func braceIndices(s string) ([]int, error) {
@@ -200,6 +226,11 @@ func braceIndices(s string) ([]int, error) {
return idxs, nil
}
+// varGroupName builds a capturing group name for the indexed variable.
+func varGroupName(idx int) string {
+ return "v" + strconv.Itoa(idx)
+}
+
// ----------------------------------------------------------------------------
// routeRegexpGroup
// ----------------------------------------------------------------------------
@@ -217,8 +248,13 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
if v.host != nil {
hostVars := v.host.regexp.FindStringSubmatch(getHost(req))
if hostVars != nil {
- for k, v := range v.host.varsN {
- m.Vars[v] = hostVars[k+1]
+ subexpNames := v.host.regexp.SubexpNames()
+ varName := 0
+ for i, name := range subexpNames[1:] {
+ if name != "" && name == varGroupName(varName) {
+ m.Vars[v.host.varsN[varName]] = hostVars[i+1]
+ varName++
+ }
}
}
}
@@ -226,8 +262,13 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
if v.path != nil {
pathVars := v.path.regexp.FindStringSubmatch(req.URL.Path)
if pathVars != nil {
- for k, v := range v.path.varsN {
- m.Vars[v] = pathVars[k+1]
+ subexpNames := v.path.regexp.SubexpNames()
+ varName := 0
+ for i, name := range subexpNames[1:] {
+ if name != "" && name == varGroupName(varName) {
+ m.Vars[v.path.varsN[varName]] = pathVars[i+1]
+ varName++
+ }
}
// Check if we should redirect.
if v.path.strictSlash {
@@ -246,12 +287,16 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
}
}
// Store query string variables.
- rawQuery := req.URL.RawQuery
for _, q := range v.queries {
- queryVars := q.regexp.FindStringSubmatch(rawQuery)
+ queryVars := q.regexp.FindStringSubmatch(q.getUrlQuery(req))
if queryVars != nil {
- for k, v := range q.varsN {
- m.Vars[v] = queryVars[k+1]
+ subexpNames := q.regexp.SubexpNames()
+ varName := 0
+ for i, name := range subexpNames[1:] {
+ if name != "" && name == varGroupName(varName) {
+ m.Vars[q.varsN[varName]] = queryVars[i+1]
+ varName++
+ }
}
}
}
diff --git a/Godeps/_workspace/src/github.com/gorilla/mux/route.go b/Godeps/_workspace/src/github.com/gorilla/mux/route.go
index d4f014688..913432c1c 100644
--- a/Godeps/_workspace/src/github.com/gorilla/mux/route.go
+++ b/Godeps/_workspace/src/github.com/gorilla/mux/route.go
@@ -9,6 +9,7 @@ import (
"fmt"
"net/http"
"net/url"
+ "regexp"
"strings"
)
@@ -188,7 +189,7 @@ func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery
type headerMatcher map[string]string
func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
- return matchMap(m, r.Header, true)
+ return matchMapWithString(m, r.Header, true)
}
// Headers adds a matcher for request header values.
@@ -199,22 +200,45 @@ func (m headerMatcher) Match(r *http.Request, match *RouteMatch) bool {
// "X-Requested-With", "XMLHttpRequest")
//
// The above route will only match if both request header values match.
-//
-// 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.
func (r *Route) Headers(pairs ...string) *Route {
if r.err == nil {
var headers map[string]string
- headers, r.err = mapFromPairs(pairs...)
+ headers, r.err = mapFromPairsToString(pairs...)
return r.addMatcher(headerMatcher(headers))
}
return r
}
+// headerRegexMatcher matches the request against the route given a regex for the header
+type headerRegexMatcher map[string]*regexp.Regexp
+
+func (m headerRegexMatcher) Match(r *http.Request, match *RouteMatch) bool {
+ return matchMapWithRegex(m, r.Header, true)
+}
+
+// Regular expressions can be used with headers as well.
+// It accepts a sequence of key/value pairs, where the value has regex support. For example
+// r := mux.NewRouter()
+// r.HeadersRegexp("Content-Type", "application/(text|json)",
+// "X-Requested-With", "XMLHttpRequest")
+//
+// The above route will only match if both the request header matches both regular expressions.
+// It the value is an empty string, it will match any value if the key is set.
+func (r *Route) HeadersRegexp(pairs ...string) *Route {
+ if r.err == nil {
+ var headers map[string]*regexp.Regexp
+ headers, r.err = mapFromPairsToRegex(pairs...)
+ return r.addMatcher(headerRegexMatcher(headers))
+ }
+ return r
+}
+
// Host -----------------------------------------------------------------------
// Host adds a matcher for the URL host.
// It accepts a template with zero or more URL variables enclosed by {}.
-// Variables can define an optional regexp pattern to me matched:
+// Variables can define an optional regexp pattern to be matched:
//
// - {name} matches anything until the next dot.
//
@@ -223,7 +247,7 @@ func (r *Route) Headers(pairs ...string) *Route {
// For example:
//
// r := mux.NewRouter()
-// r.Host("www.domain.com")
+// r.Host("www.example.com")
// r.Host("{subdomain}.domain.com")
// r.Host("{subdomain:[a-z]+}.domain.com")
//
@@ -272,7 +296,7 @@ func (r *Route) Methods(methods ...string) *Route {
// Path adds a matcher for the URL path.
// It accepts a template with zero or more URL variables enclosed by {}. The
// template must start with a "/".
-// Variables can define an optional regexp pattern to me matched:
+// Variables can define an optional regexp pattern to be matched:
//
// - {name} matches anything until the next slash.
//
@@ -323,7 +347,7 @@ func (r *Route) PathPrefix(tpl string) *Route {
//
// It the value is an empty string, it will match any value if the key is set.
//
-// Variables can define an optional regexp pattern to me matched:
+// Variables can define an optional regexp pattern to be matched:
//
// - {name} matches anything until the next slash.
//
@@ -336,7 +360,7 @@ func (r *Route) Queries(pairs ...string) *Route {
return nil
}
for i := 0; i < length; i += 2 {
- if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, true, true); r.err != nil {
+ if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil {
return r
}
}
@@ -382,7 +406,7 @@ func (r *Route) BuildVarsFunc(f BuildVarsFunc) *Route {
// It will test the inner routes only if the parent route matched. For example:
//
// r := mux.NewRouter()
-// s := r.Host("www.domain.com").Subrouter()
+// s := r.Host("www.example.com").Subrouter()
// s.HandleFunc("/products/", ProductsHandler)
// s.HandleFunc("/products/{key}", ProductHandler)
// s.HandleFunc("/articles/{category}/{id:[0-9]+}"), ArticleHandler)
@@ -511,7 +535,7 @@ func (r *Route) URLPath(pairs ...string) (*url.URL, error) {
// prepareVars converts the route variable pairs into a map. If the route has a
// BuildVarsFunc, it is invoked.
func (r *Route) prepareVars(pairs ...string) (map[string]string, error) {
- m, err := mapFromPairs(pairs...)
+ m, err := mapFromPairsToString(pairs...)
if err != nil {
return nil, err
}