Skip to content

Commit 8adc2df

Browse files
authored
Query url parameters should not escape + or : (#22)
* pre-escape + and : in url query parameters (closes #18) * add test to ensure + does not cause a failed query * update changelog
1 parent 5d9af80 commit 8adc2df

File tree

3 files changed

+70
-1
lines changed

3 files changed

+70
-1
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ first. For more complete details see
1313
instead of a hard coded value when comparing response codes.
1414
* Updated AccountInfo.AccountID to be omitted of empty (such as when
1515
used in ApprovalInfo).
16+
* + and : in url parameters for queries are no longer escaped. This was
17+
causing `400 Bad Request` to be returned when the + symbol was
18+
included as part of the query. To match behavior with Gerrit's search
19+
handling, the : symbol was also excluded.
1620
* Fixed documentation for NewClient and moved fmt.Errorf call from
1721
inside the function to a `ErrNoInstanceGiven` variable so it's
1822
easier to compare against.

changes_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,30 @@ func ExampleChangesService_QueryChanges() {
3131
// Output:
3232
// Project: platform/art -> ART: Change return types of field access entrypoints -> https://android-review.googlesource.com/249244
3333
}
34+
35+
// Prior to fixing #18 this test would fail.
36+
func ExampleChangesService_QueryChangesWithSymbols() {
37+
instance := "https://android-review.googlesource.com/"
38+
client, err := gerrit.NewClient(instance, nil)
39+
if err != nil {
40+
panic(err)
41+
}
42+
43+
opt := &gerrit.QueryChangeOptions{}
44+
opt.Query = []string{
45+
"change:249244+status:merged",
46+
}
47+
opt.Limit = 2
48+
opt.AdditionalFields = []string{"LABELS"}
49+
changes, _, err := client.Changes.QueryChanges(opt)
50+
if err != nil {
51+
panic(err)
52+
}
53+
54+
for _, change := range *changes {
55+
fmt.Printf("Project: %s -> %s -> %s%d\n", change.Project, change.Subject, instance, change.Number)
56+
}
57+
58+
// Output:
59+
// Project: platform/art -> ART: Change return types of field access entrypoints -> https://android-review.googlesource.com/249244
60+
}

gerrit.go

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,14 @@ func CheckResponse(r *http.Response) error {
433433
return err
434434
}
435435

436+
// queryParameterReplacements are values in a url, specifically the query
437+
// portion of the url, which should not be escaped before being sent to
438+
// Gerrit. Note, Gerrit itself does not escape these values when using the
439+
// search box so we shouldn't escape them either.
440+
var queryParameterReplacements = map[string]string{
441+
"+": "GOGERRIT_URL_PLACEHOLDER_PLUS",
442+
":": "GOGERRIT_URL_PLACEHOLDER_COLON"}
443+
436444
// addOptions adds the parameters in opt as URL query parameters to s.
437445
// opt must be a struct whose fields may contain "url" tags.
438446
func addOptions(s string, opt interface{}) (string, error) {
@@ -451,7 +459,37 @@ func addOptions(s string, opt interface{}) (string, error) {
451459
return s, err
452460
}
453461

454-
u.RawQuery = qs.Encode()
462+
// If the url contained one or more query parameters (q) then we need
463+
// to do some escaping on these values before Encode() is called. By
464+
// doing so we're ensuring that : and + don't get encoded which means
465+
// they'll be passed along to Gerrit as raw ascii. Without this Gerrit
466+
// could return 400 Bad Request depending on the query parameters. For
467+
// more complete information see this issue on GitHub:
468+
// https://github.com/andygrunwald/go-gerrit/issues/18
469+
_, hasQuery := qs["q"]
470+
if hasQuery {
471+
values := []string{}
472+
for _, value := range qs["q"] {
473+
for key, replacement := range queryParameterReplacements {
474+
value = strings.Replace(value, key, replacement, -1)
475+
}
476+
values = append(values, value)
477+
}
478+
479+
qs.Del("q")
480+
for _, value := range values {
481+
qs.Add("q", value)
482+
}
483+
}
484+
encoded := qs.Encode()
485+
486+
if hasQuery {
487+
for key, replacement := range queryParameterReplacements {
488+
encoded = strings.Replace(encoded, replacement, key, -1)
489+
}
490+
}
491+
492+
u.RawQuery = encoded
455493
return u.String(), nil
456494
}
457495

0 commit comments

Comments
 (0)