From: Uzondu Enudeme Date: Wed, 9 Mar 2022 23:08:52 +0000 (+0100) Subject: net/url: add OmitHost bool to url.URL X-Git-Tag: go1.19beta1~1082 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ab0f7611d739fe10d0265dbc6bdc17684423bfc8;p=gostls13.git net/url: add OmitHost bool to url.URL Previously, myscheme:/path and myscheme:///path were treated as the same URL although materially different. The distinction made clear by RFC 3986 sec. 5.3 where a different recomposition behavior is expected when a URI reference has an undefined host(authority) as in myscheme:/path vs. one with an empty host(authority) as in myscheme:///path. This change fixes the Parse/String roundtrip limitation for URLs with an undefined host and a single slash. Fixes #46059 Change-Id: I1b8d6042135513616374ff8c8dfb1cdb640f8efe Reviewed-on: https://go-review.googlesource.com/c/go/+/391294 Reviewed-by: Ian Lance Taylor Reviewed-by: Emmanuel Odeke Run-TryBot: Emmanuel Odeke TryBot-Result: Gopher Robot --- diff --git a/src/net/url/url.go b/src/net/url/url.go index 1571bf728b..ecfd1d9e94 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -363,6 +363,7 @@ type URL struct { Host string // host or host:port Path string // path (relative paths may omit leading slash) RawPath string // encoded path hint (see EscapedPath method) + OmitHost bool // do not emit empty host (authority) ForceQuery bool // append a query ('?') even if RawQuery is empty RawQuery string // encoded query values, without '?' Fragment string // fragment for references, without '#' @@ -556,7 +557,12 @@ func parse(rawURL string, viaRequest bool) (*URL, error) { if err != nil { return nil, err } + } else if url.Scheme != "" && strings.HasPrefix(rest, "/") { + // OmitHost is set to true when rawURL has an empty host (authority). + // See golang.org/issue/46059. + url.OmitHost = true } + // Set Path and, optionally, RawPath. // RawPath is a hint of the encoding of Path. We don't want to set it if // the default escaping of Path is equivalent, to help make sure that people @@ -806,15 +812,19 @@ func (u *URL) String() string { buf.WriteString(u.Opaque) } else { if u.Scheme != "" || u.Host != "" || u.User != nil { - if u.Host != "" || u.Path != "" || u.User != nil { - buf.WriteString("//") - } - if ui := u.User; ui != nil { - buf.WriteString(ui.String()) - buf.WriteByte('@') - } - if h := u.Host; h != "" { - buf.WriteString(escape(h, encodeHost)) + if u.OmitHost && u.Host == "" && u.User == nil { + // omit empty host + } else { + if u.Host != "" || u.Path != "" || u.User != nil { + buf.WriteString("//") + } + if ui := u.User; ui != nil { + buf.WriteString(ui.String()) + buf.WriteByte('@') + } + if h := u.Host; h != "" { + buf.WriteString(escape(h, encodeHost)) + } } } path := u.EscapedPath() diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index 84dba45c3c..18aa5f8a1c 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -163,14 +163,15 @@ var urltests = []URLTest{ }, "http:%2f%2fwww.google.com/?q=go+language", }, - // non-authority with path + // non-authority with path; see golang.org/issue/46059 { "mailto:/webmaster@golang.org", &URL{ - Scheme: "mailto", - Path: "/webmaster@golang.org", + Scheme: "mailto", + Path: "/webmaster@golang.org", + OmitHost: true, }, - "mailto:///webmaster@golang.org", // unfortunate compromise + "", }, // non-authority { @@ -625,8 +626,8 @@ func ufmt(u *URL) string { pass = p } } - return fmt.Sprintf("opaque=%q, scheme=%q, user=%#v, pass=%#v, host=%q, path=%q, rawpath=%q, rawq=%q, frag=%q, rawfrag=%q, forcequery=%v", - u.Opaque, u.Scheme, user, pass, u.Host, u.Path, u.RawPath, u.RawQuery, u.Fragment, u.RawFragment, u.ForceQuery) + return fmt.Sprintf("opaque=%q, scheme=%q, user=%#v, pass=%#v, host=%q, path=%q, rawpath=%q, rawq=%q, frag=%q, rawfrag=%q, forcequery=%v, omithost=%t", + u.Opaque, u.Scheme, user, pass, u.Host, u.Path, u.RawPath, u.RawQuery, u.Fragment, u.RawFragment, u.ForceQuery, u.OmitHost) } func BenchmarkString(b *testing.B) {