src/net/url/url.go | 23 ++++++++++++----------- src/net/url/url_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ diff --git a/src/net/url/url.go b/src/net/url/url.go index 1cec43b899c4bfabaeeb69fbf4b80dc8aeb386f8..efbb4c36e9bf0419619d4dfdffc26d578a8a0f9a 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -75,6 +75,18 @@ if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' { return false } + if mode == encodeHost { + // §3.2.2 Host allows + // sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" + // as part of reg-name. + // We add : because we include :port as part of host. + // We add [ ] because we include [ipv6]:port as part of host + switch c { + case '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=', ':', '[', ']': + return false + } + } + switch c { case '-', '_', '.', '~': // §2.3 Unreserved characters (mark) return false @@ -97,10 +109,6 @@ // The parsing of userinfo treats ':' as special so we must escape // that too. return c == '@' || c == '/' || c == '?' || c == ':' - case encodeHost: // §3.2.1 - // The RFC allows ':'. - return c != ':' - case encodeQueryComponent: // §3.4 // The RFC reserves (so we must escape) everything. return true @@ -108,13 +116,6 @@ case encodeFragment: // §4.1 // The RFC text is silent but the grammar allows // everything, so escape nothing. - return false - } - - case '[', ']': // §2.2 Reserved characters (reserved) - switch mode { - case encodeHost: // §3.2.1 - // The RFC allows '[', ']'. return false } } diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index 2db2d72e7cdcda57cec720e1e6fc386dac15e53d..80a2b80efad32e80994f54a2fa1a05db9750c930 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -382,6 +382,26 @@ RawQuery: "alt=media", }, "", }, + // issue 12036 + { + "mysql://a,b,c/bar", + &URL{ + Scheme: "mysql", + Host: "a,b,c", + Path: "/bar", + }, + "", + }, + // worst case host + { + "scheme://!$&'()*+,;=hello!:port/path", + &URL{ + Scheme: "scheme", + Host: "!$&'()*+,;=hello!:port", + Path: "/path", + }, + "", + }, } // more useful string for debugging than fmt's struct printer @@ -1130,6 +1150,7 @@ {'a', encodePath, false}, {'a', encodeUserPassword, false}, {'a', encodeQueryComponent, false}, {'a', encodeFragment, false}, + {'a', encodeHost, false}, {'z', encodePath, false}, {'A', encodePath, false}, {'Z', encodePath, false}, @@ -1154,6 +1175,29 @@ {'+', encodeUserPassword, false}, {',', encodeUserPassword, false}, {';', encodeUserPassword, false}, {'=', encodeUserPassword, false}, + + // Host (IP address, IPv6 address, registered name, port suffix; §3.2.2) + {'!', encodeHost, false}, + {'$', encodeHost, false}, + {'&', encodeHost, false}, + {'\'', encodeHost, false}, + {'(', encodeHost, false}, + {')', encodeHost, false}, + {'*', encodeHost, false}, + {'+', encodeHost, false}, + {',', encodeHost, false}, + {';', encodeHost, false}, + {'=', encodeHost, false}, + {':', encodeHost, false}, + {'[', encodeHost, false}, + {']', encodeHost, false}, + {'0', encodeHost, false}, + {'9', encodeHost, false}, + {'A', encodeHost, false}, + {'z', encodeHost, false}, + {'_', encodeHost, false}, + {'-', encodeHost, false}, + {'.', encodeHost, false}, } func TestShouldEscape(t *testing.T) {