Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions mux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,41 @@ func TestQueries(t *testing.T) {
queriesRegexp: "^foo=bar$,^baz=ding$",
shouldMatch: false,
},
{
title: "Queries route, match later duplicate key",
route: new(Route).Queries("sections", "130"),
request: newRequest("GET", "http://localhost?sections=90&sections=130"),
vars: nil,
host: "",
path: "",
query: "sections=130",
queriesTemplate: "sections=130",
queriesRegexp: "^sections=130$",
shouldMatch: true,
},
{
title: "Queries route, duplicate key none match",
route: new(Route).Queries("sections", "130"),
request: newRequest("GET", "http://localhost?sections=90&sections=91"),
vars: nil,
host: "",
path: "",
queriesTemplate: "sections=130",
queriesRegexp: "^sections=130$",
shouldMatch: false,
},
{
title: "Queries route, regexp matches later duplicate key",
route: new(Route).Queries("id", "{id:[0-9]+}"),
request: newRequest("GET", "http://localhost?id=abc&id=42"),
vars: map[string]string{"id": "42"},
host: "",
path: "",
query: "id=42",
queriesTemplate: "id={id:[0-9]+}",
queriesRegexp: "^id=(?P<v0>[0-9]+)$",
shouldMatch: true,
},
{
title: "Queries route with pattern, match",
route: new(Route).Queries("foo", "{v1}"),
Expand Down
34 changes: 26 additions & 8 deletions regexp.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,22 +239,39 @@ func (r *routeRegexp) url(values map[string]string) (string, error) {

// 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.
// value pair for the routeRegexp. If the key is repeated, the first
// value that matches the route regexp is used (issue #754).
func (r *routeRegexp) getURLQuery(req *http.Request) string {
if r.regexpType != regexpTypeQuery {
return ""
}
templateKey := strings.SplitN(r.template, "=", 2)[0]
val, ok := findFirstQueryKey(req.URL.RawQuery, templateKey)
if ok {
return templateKey + "=" + val
}
return ""
var found string
walkQueryKey(req.URL.RawQuery, templateKey, func(val string) bool {
pair := templateKey + "=" + val
if r.regexp.MatchString(pair) {
found = pair
return true
}
return false
})
return found
}

// 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) {
walkQueryKey(rawQuery, key, func(v string) bool {
value = v
ok = true
return true
})
return
}

// walkQueryKey calls fn for each unescaped value of key in rawQuery, in order.
// fn returning true stops the walk.
func walkQueryKey(rawQuery, key string, fn func(value string) bool) {
query := []byte(rawQuery)
for len(query) > 0 {
foundKey := query
Expand Down Expand Up @@ -285,9 +302,10 @@ func findFirstQueryKey(rawQuery, key string) (value string, ok bool) {
if err != nil {
continue
}
return valueString, true
if fn(valueString) {
return
}
}
return "", false
}

func (r *routeRegexp) matchQueryString(req *http.Request) bool {
Expand Down
Loading