1
0
mirror of https://github.com/golang/go synced 2024-11-19 23:14:47 -07:00

net/url: make Userinfo.String() escape ? and add test for shouldEscape

See RFC 3986 §3.2.1.
Fixes #6573.

LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/126560043
This commit is contained in:
Evan Kroske 2014-08-30 10:34:51 -07:00 committed by Brad Fitzpatrick
parent f4cbaa38ae
commit 07d86b1f2d
2 changed files with 62 additions and 5 deletions

View File

@ -64,7 +64,6 @@ func (e EscapeError) Error() string {
// Return true if the specified character should be escaped when // Return true if the specified character should be escaped when
// appearing in a URL string, according to RFC 3986. // appearing in a URL string, according to RFC 3986.
// When 'all' is true the full range of reserved characters are matched.
func shouldEscape(c byte, mode encoding) bool { func shouldEscape(c byte, mode encoding) bool {
// §2.3 Unreserved characters (alphanum) // §2.3 Unreserved characters (alphanum)
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' {
@ -86,10 +85,12 @@ func shouldEscape(c byte, mode encoding) bool {
// last two as well. That leaves only ? to escape. // last two as well. That leaves only ? to escape.
return c == '?' return c == '?'
case encodeUserPassword: // §3.2.2 case encodeUserPassword: // §3.2.1
// The RFC allows ; : & = + $ , in userinfo, so we must escape only @ and /. // The RFC allows ';', ':', '&', '=', '+', '$', and ',' in
// The parsing of userinfo treats : as special so we must escape that too. // userinfo, so we must escape only '@', '/', and '?'.
return c == '@' || c == '/' || c == ':' // The parsing of userinfo treats ':' as special so we must escape
// that too.
return c == '@' || c == '/' || c == '?' || c == ':'
case encodeQueryComponent: // §3.4 case encodeQueryComponent: // §3.4
// The RFC reserves (so we must escape) everything. // The RFC reserves (so we must escape) everything.

View File

@ -279,6 +279,16 @@ var urltests = []URLTest{
}, },
"a/b/c", "a/b/c",
}, },
// escaped '?' in username and password
{
"http://%3Fam:pa%3Fsword@google.com",
&URL{
Scheme: "http",
User: UserPassword("?am", "pa?sword"),
Host: "google.com",
},
"",
},
} }
// more useful string for debugging than fmt's struct printer // more useful string for debugging than fmt's struct printer
@ -903,3 +913,49 @@ func TestParseFailure(t *testing.T) {
t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh") t.Errorf(`ParseQuery(%q) returned error %q, want something containing %q"`, url, errStr, "%gh")
} }
} }
type shouldEscapeTest struct {
in byte
mode encoding
escape bool
}
var shouldEscapeTests = []shouldEscapeTest{
// Unreserved characters (§2.3)
{'a', encodePath, false},
{'a', encodeUserPassword, false},
{'a', encodeQueryComponent, false},
{'a', encodeFragment, false},
{'z', encodePath, false},
{'A', encodePath, false},
{'Z', encodePath, false},
{'0', encodePath, false},
{'9', encodePath, false},
{'-', encodePath, false},
{'-', encodeUserPassword, false},
{'-', encodeQueryComponent, false},
{'-', encodeFragment, false},
{'.', encodePath, false},
{'_', encodePath, false},
{'~', encodePath, false},
// User information (§3.2.1)
{':', encodeUserPassword, true},
{'/', encodeUserPassword, true},
{'?', encodeUserPassword, true},
{'@', encodeUserPassword, true},
{'$', encodeUserPassword, false},
{'&', encodeUserPassword, false},
{'+', encodeUserPassword, false},
{',', encodeUserPassword, false},
{';', encodeUserPassword, false},
{'=', encodeUserPassword, false},
}
func TestShouldEscape(t *testing.T) {
for _, tt := range shouldEscapeTests {
if shouldEscape(tt.in, tt.mode) != tt.escape {
t.Errorf("shouldEscape(%q, %v) returned %v; expected %v", tt.in, tt.mode, !tt.escape, tt.escape)
}
}
}