if i := strings.Index(authority, "/"); i >= 0 {
authority, rest = authority[:i], authority[i:]
}
- url.User, url.Host, err = parseAuthority(authority)
+ url.User, url.Host, err = parseAuthority(url.Scheme, authority)
if err != nil {
return nil, err
}
return url, nil
}
-func parseAuthority(authority string) (user *Userinfo, host string, err error) {
+func parseAuthority(scheme, authority string) (user *Userinfo, host string, err error) {
i := strings.LastIndex(authority, "@")
if i < 0 {
- host, err = parseHost(authority)
+ host, err = parseHost(scheme, authority)
} else {
- host, err = parseHost(authority[i+1:])
+ host, err = parseHost(scheme, authority[i+1:])
}
if err != nil {
return nil, "", err
// parseHost parses host as an authority without user
// information. That is, as host[:port].
-func parseHost(host string) (string, error) {
+func parseHost(scheme, host string) (string, error) {
if openBracketIdx := strings.LastIndex(host, "["); openBracketIdx != -1 {
// Parse an IP-Literal in RFC 3986 and RFC 6874.
// E.g., "[fe80::1]", "[fe80::1%25en0]", "[fe80::1]:80".
}
return "[" + unescapedHostname + "]" + unescapedColonPort, nil
} else if i := strings.Index(host, ":"); i != -1 {
- if j := strings.LastIndex(host, ":"); urlstrictcolons.Value() == "0" && j != i {
- urlstrictcolons.IncNonDefault()
- i = j
+ lastColon := strings.LastIndex(host, ":")
+ if lastColon != i {
+ if scheme == "postgresql" || scheme == "postgres" {
+ // PostgreSQL relies on non-RFC-3986 parsing to accept
+ // a comma-separated list of hosts (with optional ports)
+ // in the host subcomponent:
+ // https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS
+ //
+ // Since we historically permitted colons to appear in the host,
+ // continue to permit it for postgres:// URLs only.
+ // https://go.dev/issue/75223
+ i = lastColon
+ } else if urlstrictcolons.Value() == "0" {
+ urlstrictcolons.IncNonDefault()
+ i = lastColon
+ }
}
colonPort := host[i:]
if !validOptionalPort(colonPort) {
},
"mailto:?subject=hi",
},
+ // PostgreSQL URLs can include a comma-separated list of host:post hosts.
+ // https://go.dev/issue/75859
+ {
+ "postgres://host1:1,host2:2,host3:3",
+ &URL{
+ Scheme: "postgres",
+ Host: "host1:1,host2:2,host3:3",
+ Path: "",
+ },
+ "postgres://host1:1,host2:2,host3:3",
+ },
+ {
+ "postgresql://host1:1,host2:2,host3:3",
+ &URL{
+ Scheme: "postgresql",
+ Host: "host1:1,host2:2,host3:3",
+ Path: "",
+ },
+ "postgresql://host1:1,host2:2,host3:3",
+ },
}
// more useful string for debugging than fmt's struct printer