aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/gorilla/mux/regexp.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/gorilla/mux/regexp.go')
-rw-r--r--vendor/github.com/gorilla/mux/regexp.go116
1 files changed, 86 insertions, 30 deletions
diff --git a/vendor/github.com/gorilla/mux/regexp.go b/vendor/github.com/gorilla/mux/regexp.go
index 2b57e56..0144842 100644
--- a/vendor/github.com/gorilla/mux/regexp.go
+++ b/vendor/github.com/gorilla/mux/regexp.go
@@ -113,6 +113,13 @@ func newRouteRegexp(tpl string, typ regexpType, options routeRegexpOptions) (*ro
if typ != regexpTypePrefix {
pattern.WriteByte('$')
}
+
+ var wildcardHostPort bool
+ if typ == regexpTypeHost {
+ if !strings.Contains(pattern.String(), ":") {
+ wildcardHostPort = true
+ }
+ }
reverse.WriteString(raw)
if endSlash {
reverse.WriteByte('/')
@@ -131,13 +138,14 @@ func newRouteRegexp(tpl string, typ regexpType, options routeRegexpOptions) (*ro
// Done!
return &routeRegexp{
- template: template,
- regexpType: typ,
- options: options,
- regexp: reg,
- reverse: reverse.String(),
- varsN: varsN,
- varsR: varsR,
+ template: template,
+ regexpType: typ,
+ options: options,
+ regexp: reg,
+ reverse: reverse.String(),
+ varsN: varsN,
+ varsR: varsR,
+ wildcardHostPort: wildcardHostPort,
}, nil
}
@@ -158,27 +166,36 @@ type routeRegexp struct {
varsN []string
// Variable regexps (validators).
varsR []*regexp.Regexp
+ // Wildcard host-port (no strict port match in hostname)
+ wildcardHostPort bool
}
// Match matches the regexp against the URL host or path.
func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
- if r.regexpType != regexpTypeHost {
- if r.regexpType == regexpTypeQuery {
- return r.matchQueryString(req)
- }
- path := req.URL.Path
- if r.options.useEncodedPath {
- path = req.URL.EscapedPath()
+ if r.regexpType == regexpTypeHost {
+ host := getHost(req)
+ if r.wildcardHostPort {
+ // Don't be strict on the port match
+ if i := strings.Index(host, ":"); i != -1 {
+ host = host[:i]
+ }
}
- return r.regexp.MatchString(path)
+ return r.regexp.MatchString(host)
}
- return r.regexp.MatchString(getHost(req))
+ if r.regexpType == regexpTypeQuery {
+ return r.matchQueryString(req)
+ }
+ path := req.URL.Path
+ if r.options.useEncodedPath {
+ path = req.URL.EscapedPath()
+ }
+ return r.regexp.MatchString(path)
}
// url builds a URL part using the given values.
func (r *routeRegexp) url(values map[string]string) (string, error) {
- urlValues := make([]interface{}, len(r.varsN))
+ urlValues := make([]interface{}, len(r.varsN), len(r.varsN))
for k, v := range r.varsN {
value, ok := values[v]
if !ok {
@@ -213,14 +230,51 @@ func (r *routeRegexp) getURLQuery(req *http.Request) string {
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]
- }
+ val, ok := findFirstQueryKey(req.URL.RawQuery, templateKey)
+ if ok {
+ return templateKey + "=" + val
}
return ""
}
+// findFirstQueryKey returns the same result as (*url.URL).Query()[key][0].
+// If key was not found, empty string and false is returned.
+func findFirstQueryKey(rawQuery, key string) (value string, ok bool) {
+ query := []byte(rawQuery)
+ for len(query) > 0 {
+ foundKey := query
+ if i := bytes.IndexAny(foundKey, "&;"); i >= 0 {
+ foundKey, query = foundKey[:i], foundKey[i+1:]
+ } else {
+ query = query[:0]
+ }
+ if len(foundKey) == 0 {
+ continue
+ }
+ var value []byte
+ if i := bytes.IndexByte(foundKey, '='); i >= 0 {
+ foundKey, value = foundKey[:i], foundKey[i+1:]
+ }
+ if len(foundKey) < len(key) {
+ // Cannot possibly be key.
+ continue
+ }
+ keyString, err := url.QueryUnescape(string(foundKey))
+ if err != nil {
+ continue
+ }
+ if keyString != key {
+ continue
+ }
+ valueString, err := url.QueryUnescape(string(value))
+ if err != nil {
+ continue
+ }
+ return valueString, true
+ }
+ return "", false
+}
+
func (r *routeRegexp) matchQueryString(req *http.Request) bool {
return r.regexp.MatchString(r.getURLQuery(req))
}
@@ -267,10 +321,16 @@ type routeRegexpGroup struct {
}
// setMatch extracts the variables from the URL once a route matches.
-func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
+func (v routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route) {
// Store host variables.
if v.host != nil {
host := getHost(req)
+ if v.host.wildcardHostPort {
+ // Don't be strict on the port match
+ if i := strings.Index(host, ":"); i != -1 {
+ host = host[:i]
+ }
+ }
matches := v.host.regexp.FindStringSubmatchIndex(host)
if len(matches) > 0 {
extractVars(host, matches, v.host.varsN, m.Vars)
@@ -296,7 +356,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
} else {
u.Path += "/"
}
- m.Handler = http.RedirectHandler(u.String(), 301)
+ m.Handler = http.RedirectHandler(u.String(), http.StatusMovedPermanently)
}
}
}
@@ -312,17 +372,13 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
}
// getHost tries its best to return the request host.
+// According to section 14.23 of RFC 2616 the Host header
+// can include the port number if the default value of 80 is not used.
func getHost(r *http.Request) string {
if r.URL.IsAbs() {
return r.URL.Host
}
- host := r.Host
- // Slice off any port information.
- if i := strings.Index(host, ":"); i != -1 {
- host = host[:i]
- }
- return host
-
+ return r.Host
}
func extractVars(input string, matches []int, names []string, output map[string]string) {
nihil fit ex nihilo