]> Cypherpunks repositories - gostls13.git/commitdiff
net/url: consider an empty base Path as equivalent to / in JoinPath
authorDamien Neil <dneil@google.com>
Tue, 21 Feb 2023 19:24:56 +0000 (11:24 -0800)
committerDamien Neil <dneil@google.com>
Fri, 8 Mar 2024 17:01:05 +0000 (17:01 +0000)
A Path that starts with / is absolute.
A Path that starts with any other character is relative.

The meaning of a Path of "" is not defined,
but RequestURI converts a "" Path to "/"
and an empty Path may represent a URL with just
a hostname and no trailing / such as "http://localhost".

Handle empty paths in the base URL of JoinPath consistently with
RequestURI, so that joining to an empty base produces an absolute
path rather than a relative one.

u, _ := url.Parse("http://localhost")
u = u.JoinPath("x")
fmt.Println(u.Path) // "/x", not "x"

Fixes #58605

Change-Id: Iacced9c173b0aa693800dd01caf774f3f9a66d56
Reviewed-on: https://go-review.googlesource.com/c/go/+/469935
Reviewed-by: Jonathan Amsterdam <jba@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/net/url/url.go
src/net/url/url_test.go

index f362958edd72a7f475a207eeb2f0ae9c4f0b8d80..1245c67aea46856aef540c5421f46c622f7a8c7e 100644 (file)
@@ -1200,7 +1200,12 @@ func (u *URL) UnmarshalBinary(text []byte) error {
 func (u *URL) JoinPath(elem ...string) *URL {
        elem = append([]string{u.EscapedPath()}, elem...)
        var p string
-       if !strings.HasPrefix(elem[0], "/") {
+       if elem[0] == "" {
+               // RequestURI converts an empty Path to /, so do the same
+               // here for consistency. See #58605.
+               elem[0] = "/"
+               p = path.Join(elem...)
+       } else if !strings.HasPrefix(elem[0], "/") {
                // Return a relative path if u is relative,
                // but ensure that it contains no ../ elements.
                elem[0] = "/" + elem[0]
index 4aa20bb95feb0bdf2cb5cba2f3e7330e55588e89..6672363da67ab62bbf3b7640726237f9cd25973c 100644 (file)
@@ -2066,128 +2066,155 @@ func BenchmarkPathUnescape(b *testing.B) {
 
 func TestJoinPath(t *testing.T) {
        tests := []struct {
-               base string
-               elem []string
-               out  string
+               base        string
+               elem        []string
+               out         string
+               wantPath    string
+               wantRawPath string
        }{
                {
-                       base: "https://go.googlesource.com",
-                       elem: []string{"go"},
-                       out:  "https://go.googlesource.com/go",
+                       base:     "https://go.googlesource.com",
+                       elem:     []string{"go"},
+                       out:      "https://go.googlesource.com/go",
+                       wantPath: "/go",
                },
                {
-                       base: "https://go.googlesource.com/a/b/c",
-                       elem: []string{"../../../go"},
-                       out:  "https://go.googlesource.com/go",
+                       base:     "https://go.googlesource.com/a/b/c",
+                       elem:     []string{"../../../go"},
+                       out:      "https://go.googlesource.com/go",
+                       wantPath: "/go",
                },
                {
-                       base: "https://go.googlesource.com/",
-                       elem: []string{"../go"},
-                       out:  "https://go.googlesource.com/go",
+                       base:     "https://go.googlesource.com/",
+                       elem:     []string{"../go"},
+                       out:      "https://go.googlesource.com/go",
+                       wantPath: "/go",
                },
                {
-                       base: "https://go.googlesource.com",
-                       elem: []string{"../go"},
-                       out:  "https://go.googlesource.com/go",
+                       base:     "https://go.googlesource.com",
+                       elem:     []string{"../go"},
+                       out:      "https://go.googlesource.com/go",
+                       wantPath: "/go",
                },
                {
-                       base: "https://go.googlesource.com",
-                       elem: []string{"../go", "../../go", "../../../go"},
-                       out:  "https://go.googlesource.com/go",
+                       base:     "https://go.googlesource.com",
+                       elem:     []string{"../go", "../../go", "../../../go"},
+                       out:      "https://go.googlesource.com/go",
+                       wantPath: "/go",
                },
                {
-                       base: "https://go.googlesource.com/../go",
-                       elem: nil,
-                       out:  "https://go.googlesource.com/go",
+                       base:     "https://go.googlesource.com/../go",
+                       elem:     nil,
+                       out:      "https://go.googlesource.com/go",
+                       wantPath: "/go",
                },
                {
-                       base: "https://go.googlesource.com/",
-                       elem: []string{"./go"},
-                       out:  "https://go.googlesource.com/go",
+                       base:     "https://go.googlesource.com/",
+                       elem:     []string{"./go"},
+                       out:      "https://go.googlesource.com/go",
+                       wantPath: "/go",
                },
                {
-                       base: "https://go.googlesource.com//",
-                       elem: []string{"/go"},
-                       out:  "https://go.googlesource.com/go",
+                       base:     "https://go.googlesource.com//",
+                       elem:     []string{"/go"},
+                       out:      "https://go.googlesource.com/go",
+                       wantPath: "/go",
                },
                {
-                       base: "https://go.googlesource.com//",
-                       elem: []string{"/go", "a", "b", "c"},
-                       out:  "https://go.googlesource.com/go/a/b/c",
+                       base:     "https://go.googlesource.com//",
+                       elem:     []string{"/go", "a", "b", "c"},
+                       out:      "https://go.googlesource.com/go/a/b/c",
+                       wantPath: "/go/a/b/c",
                },
                {
                        base: "http://[fe80::1%en0]:8080/",
                        elem: []string{"/go"},
                },
                {
-                       base: "https://go.googlesource.com",
-                       elem: []string{"go/"},
-                       out:  "https://go.googlesource.com/go/",
+                       base:     "https://go.googlesource.com",
+                       elem:     []string{"go/"},
+                       out:      "https://go.googlesource.com/go/",
+                       wantPath: "/go/",
                },
                {
-                       base: "https://go.googlesource.com",
-                       elem: []string{"go//"},
-                       out:  "https://go.googlesource.com/go/",
+                       base:     "https://go.googlesource.com",
+                       elem:     []string{"go//"},
+                       out:      "https://go.googlesource.com/go/",
+                       wantPath: "/go/",
                },
                {
-                       base: "https://go.googlesource.com",
-                       elem: nil,
-                       out:  "https://go.googlesource.com/",
+                       base:     "https://go.googlesource.com",
+                       elem:     nil,
+                       out:      "https://go.googlesource.com/",
+                       wantPath: "/",
                },
                {
-                       base: "https://go.googlesource.com/",
-                       elem: nil,
-                       out:  "https://go.googlesource.com/",
+                       base:     "https://go.googlesource.com/",
+                       elem:     nil,
+                       out:      "https://go.googlesource.com/",
+                       wantPath: "/",
                },
                {
-                       base: "https://go.googlesource.com/a%2fb",
-                       elem: []string{"c"},
-                       out:  "https://go.googlesource.com/a%2fb/c",
+                       base:        "https://go.googlesource.com/a%2fb",
+                       elem:        []string{"c"},
+                       out:         "https://go.googlesource.com/a%2fb/c",
+                       wantPath:    "/a/b/c",
+                       wantRawPath: "/a%2fb/c",
                },
                {
-                       base: "https://go.googlesource.com/a%2fb",
-                       elem: []string{"c%2fd"},
-                       out:  "https://go.googlesource.com/a%2fb/c%2fd",
+                       base:        "https://go.googlesource.com/a%2fb",
+                       elem:        []string{"c%2fd"},
+                       out:         "https://go.googlesource.com/a%2fb/c%2fd",
+                       wantPath:    "/a/b/c/d",
+                       wantRawPath: "/a%2fb/c%2fd",
                },
                {
-                       base: "https://go.googlesource.com/a/b",
-                       elem: []string{"/go"},
-                       out:  "https://go.googlesource.com/a/b/go",
+                       base:     "https://go.googlesource.com/a/b",
+                       elem:     []string{"/go"},
+                       out:      "https://go.googlesource.com/a/b/go",
+                       wantPath: "/a/b/go",
                },
                {
-                       base: "/",
-                       elem: nil,
-                       out:  "/",
+                       base:     "/",
+                       elem:     nil,
+                       out:      "/",
+                       wantPath: "/",
                },
                {
-                       base: "a",
-                       elem: nil,
-                       out:  "a",
+                       base:     "a",
+                       elem:     nil,
+                       out:      "a",
+                       wantPath: "a",
                },
                {
-                       base: "a",
-                       elem: []string{"b"},
-                       out:  "a/b",
+                       base:     "a",
+                       elem:     []string{"b"},
+                       out:      "a/b",
+                       wantPath: "a/b",
                },
                {
-                       base: "a",
-                       elem: []string{"../b"},
-                       out:  "b",
+                       base:     "a",
+                       elem:     []string{"../b"},
+                       out:      "b",
+                       wantPath: "b",
                },
                {
-                       base: "a",
-                       elem: []string{"../../b"},
-                       out:  "b",
+                       base:     "a",
+                       elem:     []string{"../../b"},
+                       out:      "b",
+                       wantPath: "b",
                },
                {
-                       base: "",
-                       elem: []string{"a"},
-                       out:  "a",
+                       base:     "",
+                       elem:     []string{"a"},
+                       out:      "/a",
+                       wantPath: "/a",
                },
                {
-                       base: "",
-                       elem: []string{"../a"},
-                       out:  "a",
+                       base:     "",
+                       elem:     []string{"../a"},
+                       out:      "/a",
+                       wantPath: "/a",
                },
        }
        for _, tt := range tests {
@@ -2207,5 +2234,14 @@ func TestJoinPath(t *testing.T) {
                if out != tt.out || (err == nil) != (tt.out != "") {
                        t.Errorf("Parse(%q).JoinPath(%q) = %q, %v, want %q, %v", tt.base, tt.elem, out, err, tt.out, wantErr)
                }
+               if u == nil {
+                       continue
+               }
+               if got, want := u.Path, tt.wantPath; got != want {
+                       t.Errorf("Parse(%q).JoinPath(%q).Path = %q, want %q", tt.base, tt.elem, got, want)
+               }
+               if got, want := u.RawPath, tt.wantRawPath; got != want {
+                       t.Errorf("Parse(%q).JoinPath(%q).RawPath = %q, want %q", tt.base, tt.elem, got, want)
+               }
        }
 }