mirror of https://github.com/golang/go.git
net/url: handle escaped paths in ResolveReference
Currently, path resolution is done using the un-escaped version of paths. This means that path elements like one%2ftwo%2fthree are handled incorrectly, and optional encodings (%2d vs. -) are dropped. This function makes escaped handling consistent with Parse: provided escapings are honoured, and RawPath is only set if necessary. A helper method setPath is introduced to handle the correct setting of Path and RawPath given the encoded path. Fixes #16947 Change-Id: I40b1215e9066e88ec868b41635066eee220fde37 Reviewed-on: https://go-review.googlesource.com/28343 Run-TryBot: Dave Day <djd@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
29f18d7983
commit
f27c1bda51
|
|
@ -492,16 +492,13 @@ func parse(rawurl string, viaRequest bool) (url *URL, err error) {
|
|||
goto Error
|
||||
}
|
||||
}
|
||||
if url.Path, err = unescape(rest, encodePath); err != nil {
|
||||
// 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
|
||||
// don't rely on it in general.
|
||||
if err := url.setPath(rest); err != nil {
|
||||
goto Error
|
||||
}
|
||||
// RawPath is a hint as to the encoding of Path to use
|
||||
// in url.EscapedPath. If that method already gets the
|
||||
// right answer without RawPath, leave it empty.
|
||||
// This will help make sure that people don't rely on it in general.
|
||||
if url.EscapedPath() != rest && validEncodedPath(rest) {
|
||||
url.RawPath = rest
|
||||
}
|
||||
return url, nil
|
||||
|
||||
Error:
|
||||
|
|
@ -586,6 +583,29 @@ func parseHost(host string) (string, error) {
|
|||
return host, nil
|
||||
}
|
||||
|
||||
// setPath sets the Path and RawPath fields of the URL based on the provided
|
||||
// escaped path p. It maintains the invariant that RawPath is only specified
|
||||
// when it differs from the default encoding of the path.
|
||||
// For example:
|
||||
// - setPath("/foo/bar") will set Path="/foo/bar" and RawPath=""
|
||||
// - setPath("/foo%2fbar") will set Path="/foo/bar" and RawPath="/foo%2fbar"
|
||||
// setPath will return an error only if the provided path contains an invalid
|
||||
// escaping.
|
||||
func (u *URL) setPath(p string) error {
|
||||
path, err := unescape(p, encodePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Path = path
|
||||
if escp := escape(path, encodePath); p == escp {
|
||||
// Default encoding is fine.
|
||||
u.RawPath = ""
|
||||
} else {
|
||||
u.RawPath = p
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EscapedPath returns the escaped form of u.Path.
|
||||
// In general there are multiple possible escaped forms of any path.
|
||||
// EscapedPath returns u.RawPath when it is a valid escaping of u.Path.
|
||||
|
|
@ -880,7 +900,9 @@ func (u *URL) ResolveReference(ref *URL) *URL {
|
|||
}
|
||||
if ref.Scheme != "" || ref.Host != "" || ref.User != nil {
|
||||
// The "absoluteURI" or "net_path" cases.
|
||||
url.Path = resolvePath(ref.Path, "")
|
||||
// We can ignore the error from setPath since we know we provided a
|
||||
// validly-escaped path.
|
||||
url.setPath(resolvePath(ref.EscapedPath(), ""))
|
||||
return &url
|
||||
}
|
||||
if ref.Opaque != "" {
|
||||
|
|
@ -900,7 +922,7 @@ func (u *URL) ResolveReference(ref *URL) *URL {
|
|||
// The "abs_path" or "rel_path" cases.
|
||||
url.Host = u.Host
|
||||
url.User = u.User
|
||||
url.Path = resolvePath(u.Path, ref.Path)
|
||||
url.setPath(resolvePath(u.EscapedPath(), ref.EscapedPath()))
|
||||
return &url
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -945,6 +945,15 @@ var resolveReferenceTests = []struct {
|
|||
// Fragment
|
||||
{"http://foo.com/bar", ".#frag", "http://foo.com/#frag"},
|
||||
|
||||
// Paths with escaping (issue 16947).
|
||||
{"http://foo.com/foo%2fbar/", "../baz", "http://foo.com/baz"},
|
||||
{"http://foo.com/1/2%2f/3%2f4/5", "../../a/b/c", "http://foo.com/1/a/b/c"},
|
||||
{"http://foo.com/1/2/3", "./a%2f../../b/..%2fc", "http://foo.com/1/2/b/..%2fc"},
|
||||
{"http://foo.com/1/2%2f/3%2f4/5", "./a%2f../b/../c", "http://foo.com/1/2%2f/3%2f4/a%2f../c"},
|
||||
{"http://foo.com/foo%20bar/", "../baz", "http://foo.com/baz"},
|
||||
{"http://foo.com/foo", "../bar%2fbaz", "http://foo.com/bar%2fbaz"},
|
||||
{"http://foo.com/foo%2dbar/", "./baz-quux", "http://foo.com/foo%2dbar/baz-quux"},
|
||||
|
||||
// RFC 3986: Normal Examples
|
||||
// http://tools.ietf.org/html/rfc3986#section-5.4.1
|
||||
{"http://a/b/c/d;p?q", "g:h", "g:h"},
|
||||
|
|
@ -1013,8 +1022,8 @@ func TestResolveReference(t *testing.T) {
|
|||
base := mustParse(test.base)
|
||||
rel := mustParse(test.rel)
|
||||
url := base.ResolveReference(rel)
|
||||
if url.String() != test.expected {
|
||||
t.Errorf("URL(%q).ResolveReference(%q) == %q, got %q", test.base, test.rel, test.expected, url.String())
|
||||
if got := url.String(); got != test.expected {
|
||||
t.Errorf("URL(%q).ResolveReference(%q)\ngot %q\nwant %q", test.base, test.rel, got, test.expected)
|
||||
}
|
||||
// Ensure that new instances are returned.
|
||||
if base == url {
|
||||
|
|
@ -1024,8 +1033,8 @@ func TestResolveReference(t *testing.T) {
|
|||
url, err := base.Parse(test.rel)
|
||||
if err != nil {
|
||||
t.Errorf("URL(%q).Parse(%q) failed: %v", test.base, test.rel, err)
|
||||
} else if url.String() != test.expected {
|
||||
t.Errorf("URL(%q).Parse(%q) == %q, got %q", test.base, test.rel, test.expected, url.String())
|
||||
} else if got := url.String(); got != test.expected {
|
||||
t.Errorf("URL(%q).Parse(%q)\ngot %q\nwant %q", test.base, test.rel, got, test.expected)
|
||||
} else if base == url {
|
||||
// Ensure that new instances are returned for the wrapper too.
|
||||
t.Errorf("Expected URL.Parse to return new URL instance.")
|
||||
|
|
|
|||
Loading…
Reference in New Issue