Commit 9f26170a authored by Rick Arnold's avatar Rick Arnold Committed by Brad Fitzpatrick

net/url: support query string without values

Previously, RawQuery was used to indicate the presence of a query
string in url.URL. However, this approach was not able to differentiate
between URLs that have no query string at all (http://foo.bar/) and
those that have a query with no values (http://foo.bar/?).

Add a ForceQuery field to indicate the latter form of URL and use it
in URL.String to create a matching URL with a trailing '?'.

Fixes #13488

Change-Id: Ifac663c73d35759bc6c33a00f84ab116b9b81684
Reviewed-on: https://go-review.googlesource.com/19931Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent bdc14698
...@@ -307,14 +307,15 @@ func escape(s string, mode encoding) string { ...@@ -307,14 +307,15 @@ func escape(s string, mode encoding) string {
// construct a URL struct directly and set the Opaque field instead of Path. // construct a URL struct directly and set the Opaque field instead of Path.
// These still work as well. // These still work as well.
type URL struct { type URL struct {
Scheme string Scheme string
Opaque string // encoded opaque data Opaque string // encoded opaque data
User *Userinfo // username and password information User *Userinfo // username and password information
Host string // host or host:port Host string // host or host:port
Path string Path string
RawPath string // encoded path hint (Go 1.5 and later only; see EscapedPath method) RawPath string // encoded path hint (Go 1.5 and later only; see EscapedPath method)
RawQuery string // encoded query values, without '?' ForceQuery bool // append a query ('?') even if RawQuery is empty
Fragment string // fragment for references, without '#' RawQuery string // encoded query values, without '?'
Fragment string // fragment for references, without '#'
} }
// User returns a Userinfo containing the provided username // User returns a Userinfo containing the provided username
...@@ -459,7 +460,12 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) { ...@@ -459,7 +460,12 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
} }
url.Scheme = strings.ToLower(url.Scheme) url.Scheme = strings.ToLower(url.Scheme)
rest, url.RawQuery = split(rest, "?", true) if strings.HasSuffix(rest, "?") {
url.ForceQuery = true
rest = rest[:len(rest)-1]
} else {
rest, url.RawQuery = split(rest, "?", true)
}
if !strings.HasPrefix(rest, "/") { if !strings.HasPrefix(rest, "/") {
if url.Scheme != "" { if url.Scheme != "" {
...@@ -684,7 +690,7 @@ func (u *URL) String() string { ...@@ -684,7 +690,7 @@ func (u *URL) String() string {
} }
buf.WriteString(path) buf.WriteString(path)
} }
if u.RawQuery != "" { if u.ForceQuery || u.RawQuery != "" {
buf.WriteByte('?') buf.WriteByte('?')
buf.WriteString(u.RawQuery) buf.WriteString(u.RawQuery)
} }
...@@ -913,7 +919,7 @@ func (u *URL) RequestURI() string { ...@@ -913,7 +919,7 @@ func (u *URL) RequestURI() string {
result = u.Scheme + ":" + result result = u.Scheme + ":" + result
} }
} }
if u.RawQuery != "" { if u.ForceQuery || u.RawQuery != "" {
result += "?" + u.RawQuery result += "?" + u.RawQuery
} }
return result return result
......
...@@ -72,6 +72,17 @@ var urltests = []URLTest{ ...@@ -72,6 +72,17 @@ var urltests = []URLTest{
}, },
"ftp://john%20doe@www.google.com/", "ftp://john%20doe@www.google.com/",
}, },
// empty query
{
"http://www.google.com/?",
&URL{
Scheme: "http",
Host: "www.google.com",
Path: "/",
ForceQuery: true,
},
"",
},
// query // query
{ {
"http://www.google.com/?q=go+language", "http://www.google.com/?q=go+language",
...@@ -874,11 +885,13 @@ var resolveReferenceTests = []struct { ...@@ -874,11 +885,13 @@ var resolveReferenceTests = []struct {
// Absolute URL references // Absolute URL references
{"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"}, {"http://foo.com?a=b", "https://bar.com/", "https://bar.com/"},
{"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"}, {"http://foo.com/", "https://bar.com/?a=b", "https://bar.com/?a=b"},
{"http://foo.com/", "https://bar.com/?", "https://bar.com/?"},
{"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"}, {"http://foo.com/bar", "mailto:foo@example.com", "mailto:foo@example.com"},
// Path-absolute references // Path-absolute references
{"http://foo.com/bar", "/baz", "http://foo.com/baz"}, {"http://foo.com/bar", "/baz", "http://foo.com/baz"},
{"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"}, {"http://foo.com/bar?a=b#f", "/baz", "http://foo.com/baz"},
{"http://foo.com/bar?a=b", "/baz?", "http://foo.com/baz?"},
{"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"}, {"http://foo.com/bar?a=b", "/baz?c=d", "http://foo.com/baz?c=d"},
// Scheme-relative // Scheme-relative
...@@ -1217,6 +1230,15 @@ var requritests = []RequestURITest{ ...@@ -1217,6 +1230,15 @@ var requritests = []RequestURITest{
}, },
"//foo", "//foo",
}, },
{
&URL{
Scheme: "http",
Host: "example.com",
Path: "/foo",
ForceQuery: true,
},
"/foo?",
},
} }
func TestRequestURI(t *testing.T) { func TestRequestURI(t *testing.T) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment