Skip to content

Commit 512169e

Browse files
authored
refactor routeRegexp, particularily newRouteRegexp. (#328)
The existing options matchPrefix, matchHost, and matchQueries are mutually exclusive so there's no point in having a separate boolean argument for each one. It's clearer if there's a single type variable. strictSlash and useEncodedPath were also being passed as naked bools so I've wrapped these in a struct called routeRegexpOptions for more clarity at the call site.
1 parent 5ab525f commit 512169e

File tree

4 files changed

+54
-45
lines changed

4 files changed

+54
-45
lines changed

mux_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func (r *Route) GoString() string {
2525
}
2626

2727
func (r *routeRegexp) GoString() string {
28-
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)
28+
return fmt.Sprintf("&routeRegexp{template: %q, regexpType: %v, options: %v, regexp: regexp.MustCompile(%q), reverse: %q, varsN: %v, varsR: %v", r.template, r.regexpType, r.options, r.regexp.String(), r.reverse, r.varsN, r.varsR)
2929
}
3030

3131
type routeTest struct {

old_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ func TestNewRegexp(t *testing.T) {
681681
}
682682

683683
for pattern, paths := range tests {
684-
p, _ = newRouteRegexp(pattern, false, false, false, false, false)
684+
p, _ = newRouteRegexp(pattern, regexpTypePath, routeRegexpOptions{})
685685
for path, result := range paths {
686686
matches = p.regexp.FindStringSubmatch(path)
687687
if result == nil {

regexp.go

+40-34
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ import (
1414
"strings"
1515
)
1616

17+
type routeRegexpOptions struct {
18+
strictSlash bool
19+
useEncodedPath bool
20+
}
21+
22+
type regexpType int
23+
24+
const (
25+
regexpTypePath regexpType = 0
26+
regexpTypeHost regexpType = 1
27+
regexpTypePrefix regexpType = 2
28+
regexpTypeQuery regexpType = 3
29+
)
30+
1731
// newRouteRegexp parses a route template and returns a routeRegexp,
1832
// used to match a host, a path or a query string.
1933
//
@@ -24,7 +38,7 @@ import (
2438
// Previously we accepted only Python-like identifiers for variable
2539
// names ([a-zA-Z_][a-zA-Z0-9_]*), but currently the only restriction is that
2640
// name and pattern can't be empty, and names can't contain a colon.
27-
func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash, useEncodedPath bool) (*routeRegexp, error) {
41+
func newRouteRegexp(tpl string, typ regexpType, options routeRegexpOptions) (*routeRegexp, error) {
2842
// Check if it is well-formed.
2943
idxs, errBraces := braceIndices(tpl)
3044
if errBraces != nil {
@@ -34,19 +48,18 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
3448
template := tpl
3549
// Now let's parse it.
3650
defaultPattern := "[^/]+"
37-
if matchQuery {
51+
if typ == regexpTypeQuery {
3852
defaultPattern = ".*"
39-
} else if matchHost {
53+
} else if typ == regexpTypeHost {
4054
defaultPattern = "[^.]+"
41-
matchPrefix = false
4255
}
4356
// Only match strict slash if not matching
44-
if matchPrefix || matchHost || matchQuery {
45-
strictSlash = false
57+
if typ != regexpTypePath {
58+
options.strictSlash = false
4659
}
4760
// Set a flag for strictSlash.
4861
endSlash := false
49-
if strictSlash && strings.HasSuffix(tpl, "/") {
62+
if options.strictSlash && strings.HasSuffix(tpl, "/") {
5063
tpl = tpl[:len(tpl)-1]
5164
endSlash = true
5265
}
@@ -88,16 +101,16 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
88101
// Add the remaining.
89102
raw := tpl[end:]
90103
pattern.WriteString(regexp.QuoteMeta(raw))
91-
if strictSlash {
104+
if options.strictSlash {
92105
pattern.WriteString("[/]?")
93106
}
94-
if matchQuery {
107+
if typ == regexpTypeQuery {
95108
// Add the default pattern if the query value is empty
96109
if queryVal := strings.SplitN(template, "=", 2)[1]; queryVal == "" {
97110
pattern.WriteString(defaultPattern)
98111
}
99112
}
100-
if !matchPrefix {
113+
if typ != regexpTypePrefix {
101114
pattern.WriteByte('$')
102115
}
103116
reverse.WriteString(raw)
@@ -118,15 +131,13 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
118131

119132
// Done!
120133
return &routeRegexp{
121-
template: template,
122-
matchHost: matchHost,
123-
matchQuery: matchQuery,
124-
strictSlash: strictSlash,
125-
useEncodedPath: useEncodedPath,
126-
regexp: reg,
127-
reverse: reverse.String(),
128-
varsN: varsN,
129-
varsR: varsR,
134+
template: template,
135+
regexpType: typ,
136+
options: options,
137+
regexp: reg,
138+
reverse: reverse.String(),
139+
varsN: varsN,
140+
varsR: varsR,
130141
}, nil
131142
}
132143

@@ -135,15 +146,10 @@ func newRouteRegexp(tpl string, matchHost, matchPrefix, matchQuery, strictSlash,
135146
type routeRegexp struct {
136147
// The unmodified template.
137148
template string
138-
// True for host match, false for path or query string match.
139-
matchHost bool
140-
// True for query string match, false for path and host match.
141-
matchQuery bool
142-
// The strictSlash value defined on the route, but disabled if PathPrefix was used.
143-
strictSlash bool
144-
// Determines whether to use encoded req.URL.EnscapedPath() or unencoded
145-
// req.URL.Path for path matching
146-
useEncodedPath bool
149+
// The type of match
150+
regexpType regexpType
151+
// Options for matching
152+
options routeRegexpOptions
147153
// Expanded regexp.
148154
regexp *regexp.Regexp
149155
// Reverse template.
@@ -156,12 +162,12 @@ type routeRegexp struct {
156162

157163
// Match matches the regexp against the URL host or path.
158164
func (r *routeRegexp) Match(req *http.Request, match *RouteMatch) bool {
159-
if !r.matchHost {
160-
if r.matchQuery {
165+
if r.regexpType != regexpTypeHost {
166+
if r.regexpType == regexpTypeQuery {
161167
return r.matchQueryString(req)
162168
}
163169
path := req.URL.Path
164-
if r.useEncodedPath {
170+
if r.options.useEncodedPath {
165171
path = req.URL.EscapedPath()
166172
}
167173
return r.regexp.MatchString(path)
@@ -178,7 +184,7 @@ func (r *routeRegexp) url(values map[string]string) (string, error) {
178184
if !ok {
179185
return "", fmt.Errorf("mux: missing route variable %q", v)
180186
}
181-
if r.matchQuery {
187+
if r.regexpType == regexpTypeQuery {
182188
value = url.QueryEscape(value)
183189
}
184190
urlValues[k] = value
@@ -203,7 +209,7 @@ func (r *routeRegexp) url(values map[string]string) (string, error) {
203209
// For a URL with foo=bar&baz=ding, we return only the relevant key
204210
// value pair for the routeRegexp.
205211
func (r *routeRegexp) getURLQuery(req *http.Request) string {
206-
if !r.matchQuery {
212+
if r.regexpType != regexpTypeQuery {
207213
return ""
208214
}
209215
templateKey := strings.SplitN(r.template, "=", 2)[0]
@@ -280,7 +286,7 @@ func (v *routeRegexpGroup) setMatch(req *http.Request, m *RouteMatch, r *Route)
280286
if len(matches) > 0 {
281287
extractVars(path, matches, v.path.varsN, m.Vars)
282288
// Check if we should redirect.
283-
if v.path.strictSlash {
289+
if v.path.options.strictSlash {
284290
p1 := strings.HasSuffix(path, "/")
285291
p2 := strings.HasSuffix(v.path.template, "/")
286292
if p1 != p2 {

route.go

+12-9
Original file line numberDiff line numberDiff line change
@@ -171,20 +171,23 @@ func (r *Route) addMatcher(m matcher) *Route {
171171
}
172172

173173
// addRegexpMatcher adds a host or path matcher and builder to a route.
174-
func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery bool) error {
174+
func (r *Route) addRegexpMatcher(tpl string, typ regexpType) error {
175175
if r.err != nil {
176176
return r.err
177177
}
178178
r.regexp = r.getRegexpGroup()
179-
if !matchHost && !matchQuery {
179+
if typ == regexpTypePath || typ == regexpTypePrefix {
180180
if len(tpl) > 0 && tpl[0] != '/' {
181181
return fmt.Errorf("mux: path must start with a slash, got %q", tpl)
182182
}
183183
if r.regexp.path != nil {
184184
tpl = strings.TrimRight(r.regexp.path.template, "/") + tpl
185185
}
186186
}
187-
rr, err := newRouteRegexp(tpl, matchHost, matchPrefix, matchQuery, r.strictSlash, r.useEncodedPath)
187+
rr, err := newRouteRegexp(tpl, typ, routeRegexpOptions{
188+
strictSlash: r.strictSlash,
189+
useEncodedPath: r.useEncodedPath,
190+
})
188191
if err != nil {
189192
return err
190193
}
@@ -193,7 +196,7 @@ func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery
193196
return err
194197
}
195198
}
196-
if matchHost {
199+
if typ == regexpTypeHost {
197200
if r.regexp.path != nil {
198201
if err = uniqueVars(rr.varsN, r.regexp.path.varsN); err != nil {
199202
return err
@@ -206,7 +209,7 @@ func (r *Route) addRegexpMatcher(tpl string, matchHost, matchPrefix, matchQuery
206209
return err
207210
}
208211
}
209-
if matchQuery {
212+
if typ == regexpTypeQuery {
210213
r.regexp.queries = append(r.regexp.queries, rr)
211214
} else {
212215
r.regexp.path = rr
@@ -289,7 +292,7 @@ func (r *Route) HeadersRegexp(pairs ...string) *Route {
289292
// Variable names must be unique in a given route. They can be retrieved
290293
// calling mux.Vars(request).
291294
func (r *Route) Host(tpl string) *Route {
292-
r.err = r.addRegexpMatcher(tpl, true, false, false)
295+
r.err = r.addRegexpMatcher(tpl, regexpTypeHost)
293296
return r
294297
}
295298

@@ -349,7 +352,7 @@ func (r *Route) Methods(methods ...string) *Route {
349352
// Variable names must be unique in a given route. They can be retrieved
350353
// calling mux.Vars(request).
351354
func (r *Route) Path(tpl string) *Route {
352-
r.err = r.addRegexpMatcher(tpl, false, false, false)
355+
r.err = r.addRegexpMatcher(tpl, regexpTypePath)
353356
return r
354357
}
355358

@@ -365,7 +368,7 @@ func (r *Route) Path(tpl string) *Route {
365368
// Also note that the setting of Router.StrictSlash() has no effect on routes
366369
// with a PathPrefix matcher.
367370
func (r *Route) PathPrefix(tpl string) *Route {
368-
r.err = r.addRegexpMatcher(tpl, false, true, false)
371+
r.err = r.addRegexpMatcher(tpl, regexpTypePrefix)
369372
return r
370373
}
371374

@@ -396,7 +399,7 @@ func (r *Route) Queries(pairs ...string) *Route {
396399
return nil
397400
}
398401
for i := 0; i < length; i += 2 {
399-
if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], false, false, true); r.err != nil {
402+
if r.err = r.addRegexpMatcher(pairs[i]+"="+pairs[i+1], regexpTypeQuery); r.err != nil {
400403
return r
401404
}
402405
}

0 commit comments

Comments
 (0)