@ -14,6 +14,20 @@ import (
@@ -14,6 +14,20 @@ import (
"strings"
)
type routeRegexpOptions struct {
strictSlash bool
useEncodedPath bool
}
type regexpType int
const (
regexpTypePath regexpType = 0
regexpTypeHost regexpType = 1
regexpTypePrefix regexpType = 2
regexpTypeQuery regexpType = 3
)
// newRouteRegexp parses a route template and returns a routeRegexp,
// used to match a host, a path or a query string.
//
@ -24,7 +38,7 @@ import (
@@ -24,7 +38,7 @@ import (
// Previously we accepted only Python-like identifiers for variable
// 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.
func newRouteRegexp ( tpl string , matchHost , matchPrefix , matchQuery , strictSlash , useEncodedPath bool ) ( * routeRegexp , error ) {
func newRouteRegexp ( tpl string , typ regexpType , options routeRegexpOptions ) ( * routeRegexp , error ) {
// Check if it is well-formed.
idxs , errBraces := braceIndices ( tpl )
if errBraces != nil {
@ -34,19 +48,18 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
@@ -34,19 +48,18 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
template := tpl
// Now let's parse it.
defaultPattern := "[^/]+"
if match Query {
if typ == regexpType Query {
defaultPattern = ".*"
} else if match Host {
} else if typ == regexpType Host {
defaultPattern = "[^.]+"
matchPrefix = false
}
// Only match strict slash if not matching
if matchPrefix || matchHost || matchQuery {
strictSlash = false
if typ != regexpTypePath {
options . strictSlash = false
}
// Set a flag for strictSlash.
endSlash := false
if strictSlash && strings . HasSuffix ( tpl , "/" ) {
if options . strictSlash && strings . HasSuffix ( tpl , "/" ) {
tpl = tpl [ : len ( tpl ) - 1 ]
endSlash = true
}
@ -88,16 +101,16 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
@@ -88,16 +101,16 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
// Add the remaining.
raw := tpl [ end : ]
pattern . WriteString ( regexp . QuoteMeta ( raw ) )
if strictSlash {
if options . strictSlash {
pattern . WriteString ( "[/]?" )
}
if match Query {
if typ == regexpType Query {
// Add the default pattern if the query value is empty
if queryVal := strings . SplitN ( template , "=" , 2 ) [ 1 ] ; queryVal == "" {
pattern . WriteString ( defaultPattern )
}
}
if ! match Prefix {
if typ != regexpType Prefix {
pattern . WriteByte ( '$' )
}
reverse . WriteString ( raw )
@ -118,15 +131,13 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
@@ -118,15 +131,13 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
// Done!
return & routeRegexp {
template : template ,
matchHost : matchHost ,
matchQuery : matchQuery ,
strictSlash : strictSlash ,
useEncodedPath : useEncodedPath ,
regexp : reg ,
reverse : reverse . String ( ) ,
varsN : varsN ,
varsR : varsR ,
template : template ,
regexpType : typ ,
options : options ,
regexp : reg ,
reverse : reverse . String ( ) ,
varsN : varsN ,
varsR : varsR ,
} , nil
}
@ -135,15 +146,10 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
@@ -135,15 +146,10 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
type routeRegexp struct {
// The unmodified template.
template string
// True for host match, false for path or query string match.
matchHost bool
// True for query string match, false for path and host match.
matchQuery bool
// The strictSlash value defined on the route, but disabled if PathPrefix was used.
strictSlash bool
// Determines whether to use encoded req.URL.EnscapedPath() or unencoded
// req.URL.Path for path matching
useEncodedPath bool
// The type of match
regexpType regexpType
// Options for matching
options routeRegexpOptions
// Expanded regexp.
regexp * regexp . Regexp
// Reverse template.
@ -156,12 +162,12 @@ type routeRegexp struct {
@@ -156,12 +162,12 @@ type routeRegexp struct {
// Match matches the regexp against the URL host or path.
func ( r * routeRegexp ) Match ( req * http . Request , match * RouteMatch ) bool {
if ! r . match Host {
if r . match Query {
if r . regexpType != regexpType Host {
if r . regexpType == regexpType Query {
return r . matchQueryString ( req )
}
path := req . URL . Path
if r . useEncodedPath {
if r . options . useEncodedPath {
path = req . URL . EscapedPath ( )
}
return r . regexp . MatchString ( path )
@ -178,7 +184,7 @@ func (r *routeRegexp) url(values map[string]string) (string, error) {
@@ -178,7 +184,7 @@ func (r *routeRegexp) url(values map[string]string) (string, error) {
if ! ok {
return "" , fmt . Errorf ( "mux: missing route variable %q" , v )
}
if r . match Query {
if r . regexpType == regexpType Query {
value = url . QueryEscape ( value )
}
urlValues [ k ] = value
@ -203,7 +209,7 @@ func (r *routeRegexp) url(values map[string]string) (string, error) {
@@ -203,7 +209,7 @@ func (r *routeRegexp) url(values map[string]string) (string, error) {
// 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 . match Query {
if r . regexpType != regexpType Query {
return ""
}
templateKey := strings . SplitN ( r . template , "=" , 2 ) [ 0 ]
@ -280,7 +286,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
@@ -280,7 +286,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
if len ( matches ) > 0 {
extractVars ( path , matches , v . path . varsN , m . Vars )
// Check if we should redirect.
if v . path . strictSlash {
if v . path . options . strictSlash {
p1 := strings . HasSuffix ( path , "/" )
p2 := strings . HasSuffix ( v . path . template , "/" )
if p1 != p2 {