Regex capturing group.
This commit is contained in:
parent
afbad56012
commit
81f7aa9df2
6 changed files with 152 additions and 42 deletions
2
glide.lock
generated
2
glide.lock
generated
|
@ -89,7 +89,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: af6ea922f7683d9706834157e6b0610e22ccb2db
|
version: 06ccd3e75091eb659b1d720cda0e16bc7057954c
|
||||||
- name: github.com/containous/staert
|
- name: github.com/containous/staert
|
||||||
version: 1e26a71803e428fd933f5f9c8e50a26878f53147
|
version: 1e26a71803e428fd933f5f9c8e50a26878f53147
|
||||||
- name: github.com/coreos/etcd
|
- name: github.com/coreos/etcd
|
||||||
|
|
|
@ -136,6 +136,57 @@ func TestPriorites(t *testing.T) {
|
||||||
assert.NotEqual(t, foobarMatcher.Handler, fooHandler, "Error matching priority")
|
assert.NotEqual(t, foobarMatcher.Handler, fooHandler, "Error matching priority")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHostRegexp(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
hostExp string
|
||||||
|
urls map[string]bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "capturing group",
|
||||||
|
hostExp: "{subdomain:(foo\\.)?bar\\.com}",
|
||||||
|
urls: map[string]bool{
|
||||||
|
"http://foo.bar.com": true,
|
||||||
|
"http://bar.com": true,
|
||||||
|
"http://fooubar.com": false,
|
||||||
|
"http://barucom": false,
|
||||||
|
"http://barcom": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "non capturing group",
|
||||||
|
hostExp: "{subdomain:(?:foo\\.)?bar\\.com}",
|
||||||
|
urls: map[string]bool{
|
||||||
|
"http://foo.bar.com": true,
|
||||||
|
"http://bar.com": true,
|
||||||
|
"http://fooubar.com": false,
|
||||||
|
"http://barucom": false,
|
||||||
|
"http://barcom": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
rls := &Rules{
|
||||||
|
route: &serverRoute{
|
||||||
|
route: &mux.Route{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rt := rls.hostRegexp(test.hostExp)
|
||||||
|
|
||||||
|
for testURL, match := range test.urls {
|
||||||
|
req := testhelpers.MustNewRequest(http.MethodGet, testURL, nil)
|
||||||
|
assert.Equal(t, match, rt.Match(req, &mux.RouteMatch{}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type fakeHandler struct {
|
type fakeHandler struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
5
vendor/github.com/containous/mux/doc.go
generated
vendored
5
vendor/github.com/containous/mux/doc.go
generated
vendored
|
@ -57,11 +57,6 @@ 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.
|
||||||
|
|
||||||
|
|
58
vendor/github.com/containous/mux/mux.go
generated
vendored
58
vendor/github.com/containous/mux/mux.go
generated
vendored
|
@ -11,7 +11,10 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrMethodMismatch = errors.New("method is not allowed")
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewRouter returns a new router instance.
|
// NewRouter returns a new router instance.
|
||||||
|
@ -40,6 +43,10 @@ func NewRouter() *Router {
|
||||||
type Router struct {
|
type Router struct {
|
||||||
// Configurable Handler to be used when no route matches.
|
// Configurable Handler to be used when no route matches.
|
||||||
NotFoundHandler http.Handler
|
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 route, if this is a subrouter.
|
||||||
parent parentRoute
|
parent parentRoute
|
||||||
// Routes to be matched, in order.
|
// Routes to be matched, in order.
|
||||||
|
@ -66,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)
|
// Closest match for a router (includes sub-routers)
|
||||||
if r.NotFoundHandler != nil {
|
if r.NotFoundHandler != nil {
|
||||||
match.Handler = r.NotFoundHandler
|
match.Handler = r.NotFoundHandler
|
||||||
|
@ -82,7 +94,7 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
if !r.skipClean {
|
if !r.skipClean {
|
||||||
path := req.URL.Path
|
path := req.URL.Path
|
||||||
if r.useEncodedPath {
|
if r.useEncodedPath {
|
||||||
path = getPath(req)
|
path = req.URL.EscapedPath()
|
||||||
}
|
}
|
||||||
// Clean path to canonical form and redirect.
|
// Clean path to canonical form and redirect.
|
||||||
if p := cleanPath(path); p != path {
|
if p := cleanPath(path); p != path {
|
||||||
|
@ -106,9 +118,15 @@ func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||||
req = setVars(req, match.Vars)
|
req = setVars(req, match.Vars)
|
||||||
req = setCurrentRoute(req, match.Route)
|
req = setCurrentRoute(req, match.Route)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if handler == nil && match.MatchErr == ErrMethodMismatch {
|
||||||
|
handler = methodNotAllowedHandler()
|
||||||
|
}
|
||||||
|
|
||||||
if handler == nil {
|
if handler == nil {
|
||||||
handler = http.NotFoundHandler()
|
handler = http.NotFoundHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !r.KeepContext {
|
if !r.KeepContext {
|
||||||
defer contextClear(req)
|
defer contextClear(req)
|
||||||
}
|
}
|
||||||
|
@ -356,6 +374,11 @@ type RouteMatch struct {
|
||||||
Route *Route
|
Route *Route
|
||||||
Handler http.Handler
|
Handler http.Handler
|
||||||
Vars map[string]string
|
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
|
type contextKey int
|
||||||
|
@ -397,28 +420,6 @@ func setCurrentRoute(r *http.Request, val interface{}) *http.Request {
|
||||||
// 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 {
|
||||||
|
@ -557,3 +558,12 @@ func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]s
|
||||||
}
|
}
|
||||||
return true
|
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) }
|
||||||
|
|
24
vendor/github.com/containous/mux/regexp.go
generated
vendored
24
vendor/github.com/containous/mux/regexp.go
generated
vendored
|
@ -109,13 +109,6 @@ 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,
|
||||||
|
@ -141,7 +134,7 @@ 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
|
// Determines whether to use encoded req.URL.EnscapedPath() or unencoded
|
||||||
// req.URL.Path for path matching
|
// req.URL.Path for path matching
|
||||||
useEncodedPath bool
|
useEncodedPath bool
|
||||||
// Expanded regexp.
|
// Expanded regexp.
|
||||||
|
@ -162,7 +155,7 @@ func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
}
|
}
|
||||||
path := req.URL.Path
|
path := req.URL.Path
|
||||||
if r.useEncodedPath {
|
if r.useEncodedPath {
|
||||||
path = getPath(req)
|
path = req.URL.EscapedPath()
|
||||||
}
|
}
|
||||||
return r.regexp.MatchString(path)
|
return r.regexp.MatchString(path)
|
||||||
}
|
}
|
||||||
|
@ -272,7 +265,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
|
||||||
}
|
}
|
||||||
path := req.URL.Path
|
path := req.URL.Path
|
||||||
if r.useEncodedPath {
|
if r.useEncodedPath {
|
||||||
path = getPath(req)
|
path = req.URL.EscapedPath()
|
||||||
}
|
}
|
||||||
// Store path variables.
|
// Store path variables.
|
||||||
if v.path != nil {
|
if v.path != nil {
|
||||||
|
@ -320,7 +313,14 @@ 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) {
|
||||||
for i, name := range names {
|
matchesCount := 0
|
||||||
output[name] = input[matches[2*i+2]:matches[2*i+3]]
|
prevEnd := -1
|
||||||
|
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++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
54
vendor/github.com/containous/mux/route.go
generated
vendored
54
vendor/github.com/containous/mux/route.go
generated
vendored
|
@ -54,12 +54,27 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
if r.buildOnly || r.err != nil {
|
if r.buildOnly || r.err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var matchErr error
|
||||||
|
|
||||||
// Match everything.
|
// Match everything.
|
||||||
for _, m := range r.matchers {
|
for _, m := range r.matchers {
|
||||||
if matched := m.Match(req, match); !matched {
|
if matched := m.Match(req, match); !matched {
|
||||||
|
if _, ok := m.(methodMatcher); ok {
|
||||||
|
matchErr = ErrMethodMismatch
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matchErr = nil
|
||||||
return false
|
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.
|
// Yay, we have a match. Let's collect some info about it.
|
||||||
if match.Route == nil {
|
if match.Route == nil {
|
||||||
match.Route = r
|
match.Route = r
|
||||||
|
@ -70,6 +85,7 @@ func (r *Route) Match(req *http.Request, match *RouteMatch) bool {
|
||||||
if match.Vars == nil {
|
if match.Vars == nil {
|
||||||
match.Vars = make(map[string]string)
|
match.Vars = make(map[string]string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set variables.
|
// Set variables.
|
||||||
if r.regexp != nil {
|
if r.regexp != nil {
|
||||||
r.regexp.setMatch(req, match, r)
|
r.regexp.setMatch(req, match, r)
|
||||||
|
@ -607,6 +623,44 @@ func (r *Route) GetPathRegexp() (string, error) {
|
||||||
return r.regexp.path.regexp.String(), nil
|
return r.regexp.path.regexp.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetQueriesRegexp returns the expanded regular expressions used to match the
|
||||||
|
// 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.
|
||||||
|
func (r *Route) GetQueriesRegexp() ([]string, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
if r.regexp == nil || r.regexp.queries == nil {
|
||||||
|
return nil, errors.New("mux: route doesn't have queries")
|
||||||
|
}
|
||||||
|
var queries []string
|
||||||
|
for _, query := range r.regexp.queries {
|
||||||
|
queries = append(queries, query.regexp.String())
|
||||||
|
}
|
||||||
|
return queries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetQueriesTemplates returns the templates used to build the
|
||||||
|
// 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.
|
||||||
|
func (r *Route) GetQueriesTemplates() ([]string, error) {
|
||||||
|
if r.err != nil {
|
||||||
|
return nil, r.err
|
||||||
|
}
|
||||||
|
if r.regexp == nil || r.regexp.queries == nil {
|
||||||
|
return nil, errors.New("mux: route doesn't have queries")
|
||||||
|
}
|
||||||
|
var queries []string
|
||||||
|
for _, query := range r.regexp.queries {
|
||||||
|
queries = append(queries, query.template)
|
||||||
|
}
|
||||||
|
return queries, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetMethods returns the methods the route matches against
|
// GetMethods returns the methods the route matches against
|
||||||
// This is useful for building simple REST API documentation and for instrumentation
|
// This is useful for building simple REST API documentation and for instrumentation
|
||||||
// against third-party services.
|
// against third-party services.
|
||||||
|
|
Loading…
Reference in a new issue