From 011829989365218ba4532e86ab0b6702971daab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Author=20=E7=8E=8B=E6=B3=BD=E9=BE=99?= Date: Thu, 1 Jul 2021 14:24:34 +0800 Subject: [PATCH 1/7] net/url: add JoinPath, URL.JoinPath concatenates baseUrl and the elements Fixes #47005 Change-Id: I71d638be9d451435dddf135091d8b1db54d174fd --- api/except.txt | 2 ++ src/net/url/url.go | 22 +++++++++++++ src/net/url/url_test.go | 71 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/api/except.txt b/api/except.txt index b9972c121cfa4a..37c76a3607cb2b 100644 --- a/api/except.txt +++ b/api/except.txt @@ -505,3 +505,5 @@ pkg unicode, const Version = "6.3.0" pkg unicode, const Version = "7.0.0" pkg unicode, const Version = "8.0.0" pkg unicode, const Version = "9.0.0" +pkg net/url, func JoinPath(string, ...string) (string, error) +pkg net/url, method (*URL) JoinPath(...string) *URL \ No newline at end of file diff --git a/src/net/url/url.go b/src/net/url/url.go index f31aa08b5929db..05c36224af272c 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -13,6 +13,7 @@ package url import ( "errors" "fmt" + "path" "sort" "strconv" "strings" @@ -1176,6 +1177,15 @@ func (u *URL) UnmarshalBinary(text []byte) error { return nil } +func (u *URL) JoinPath(elem ...string) *URL { + url := *u + if len(elem) > 0 { + elem = append([]string{u.Path}, elem...) + url.Path = path.Join(elem...) + } + return &url +} + // validUserinfo reports whether s is a valid userinfo string per RFC 3986 // Section 3.2.1: // userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) @@ -1216,3 +1226,15 @@ func stringContainsCTLByte(s string) bool { } return false } + +// JoinPath concatenates baseUrl and the elements +// - check baseUrl format +// - concatenates baseUrl and the elements +func JoinPath(baseUrl string, elem ...string) (result string, err error) { + url, err := Parse(baseUrl) + if err != nil { + return + } + result = url.JoinPath(elem...).String() + return +} diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index 664757b832af04..268ba56d0846a7 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -2062,3 +2062,74 @@ func BenchmarkPathUnescape(b *testing.B) { }) } } + +func TestJoinPath(t *testing.T) { + type args struct { + baseUrl string + elem []string + } + tests := []struct { + name string + args args + wantResult string + wantErr bool + }{ + { + name: "test normal url", + args: args{ + baseUrl: "https://go.googlesource.com", + elem: []string{"go"}, + }, + wantResult: "https://go.googlesource.com/go", + wantErr: false, + }, + { + name: "test .. parent url", + args: args{ + baseUrl: "https://go.googlesource.com/a/b/c", + elem: []string{"../../../go"}, + }, + wantResult: "https://go.googlesource.com/go", + wantErr: false, + }, + { + name: "test . cul path", + args: args{ + baseUrl: "https://go.googlesource.com/", + elem: []string{"./go"}, + }, + wantResult: "https://go.googlesource.com/go", + wantErr: false, + }, + { + name: "test multiple Separator", + args: args{ + baseUrl: "https://go.googlesource.com//", + elem: []string{"/go"}, + }, + wantResult: "https://go.googlesource.com/go", + wantErr: false, + }, + { + name: "test more elems", + args: args{ + baseUrl: "https://go.googlesource.com//", + elem: []string{"/go", "a", "b", "c"}, + }, + wantResult: "https://go.googlesource.com/go/a/b/c", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotResult, err := JoinPath(tt.args.baseUrl, tt.args.elem...) + if (err != nil) != tt.wantErr { + t.Errorf("JoinPath() error = %v, wantErr %v", err, tt.wantErr) + return + } + if gotResult != tt.wantResult { + t.Errorf("JoinPath() = %v, want %v", gotResult, tt.wantResult) + } + }) + } +} From 45552590898948e0cd994eeda66ece1915d258d9 Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Tue, 28 Dec 2021 21:48:37 -0500 Subject: [PATCH 2/7] net/url: Docs for url.JoinPath --- api/except.txt | 2 -- src/net/url/url.go | 12 ++++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/api/except.txt b/api/except.txt index 37c76a3607cb2b..b9972c121cfa4a 100644 --- a/api/except.txt +++ b/api/except.txt @@ -505,5 +505,3 @@ pkg unicode, const Version = "6.3.0" pkg unicode, const Version = "7.0.0" pkg unicode, const Version = "8.0.0" pkg unicode, const Version = "9.0.0" -pkg net/url, func JoinPath(string, ...string) (string, error) -pkg net/url, method (*URL) JoinPath(...string) *URL \ No newline at end of file diff --git a/src/net/url/url.go b/src/net/url/url.go index 05c36224af272c..286e6aa330f67c 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -1177,11 +1177,13 @@ func (u *URL) UnmarshalBinary(text []byte) error { return nil } +// JoinPath returns a new URL with the provided path elements +// joined to any existing path and cleaned of any ./ or ../ elements. func (u *URL) JoinPath(elem ...string) *URL { url := *u if len(elem) > 0 { elem = append([]string{u.Path}, elem...) - url.Path = path.Join(elem...) + url.setPath(path.Join(elem...)) } return &url } @@ -1227,11 +1229,9 @@ func stringContainsCTLByte(s string) bool { return false } -// JoinPath concatenates baseUrl and the elements -// - check baseUrl format -// - concatenates baseUrl and the elements -func JoinPath(baseUrl string, elem ...string) (result string, err error) { - url, err := Parse(baseUrl) +// JoinPath concatenates the path elements to the base URL and cleans any ./ or ../ elements from the final URL string. +func JoinPath(base string, elem ...string) (result string, err error) { + url, err := Parse(base) if err != nil { return } From 252b742e0b61b9f19afe8b19931ed78bf27775c4 Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Thu, 3 Mar 2022 14:49:29 -0500 Subject: [PATCH 3/7] Incorporate feedback --- src/net/url/url.go | 7 ++-- src/net/url/url_test.go | 92 ++++++++++++++++++----------------------- 2 files changed, 45 insertions(+), 54 deletions(-) diff --git a/src/net/url/url.go b/src/net/url/url.go index 286e6aa330f67c..e047e8618379f5 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -1177,8 +1177,8 @@ func (u *URL) UnmarshalBinary(text []byte) error { return nil } -// JoinPath returns a new URL with the provided path elements -// joined to any existing path and cleaned of any ./ or ../ elements. +// JoinPath returns a new URL with the provided path elements joined to +// any existing path and the resulting path cleaned of any ./ or ../ elements. func (u *URL) JoinPath(elem ...string) *URL { url := *u if len(elem) > 0 { @@ -1229,7 +1229,8 @@ func stringContainsCTLByte(s string) bool { return false } -// JoinPath concatenates the path elements to the base URL and cleans any ./ or ../ elements from the final URL string. +// JoinPath returns a string with the provided path elements joined to +// the existing path of base and the resulting path cleaned of any ./ or ../ elements. func JoinPath(base string, elem ...string) (result string, err error) { url, err := Parse(base) if err != nil { diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index 268ba56d0846a7..8555a15e00b229 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -2064,72 +2064,62 @@ func BenchmarkPathUnescape(b *testing.B) { } func TestJoinPath(t *testing.T) { - type args struct { - baseUrl string - elem []string - } tests := []struct { - name string - args args - wantResult string - wantErr bool + base string + elem []string + out string + err bool }{ { - name: "test normal url", - args: args{ - baseUrl: "https://go.googlesource.com", - elem: []string{"go"}, - }, - wantResult: "https://go.googlesource.com/go", - wantErr: false, + base: "https://go.googlesource.com", + elem: []string{"go"}, + out: "https://go.googlesource.com/go", + err: false, }, { - name: "test .. parent url", - args: args{ - baseUrl: "https://go.googlesource.com/a/b/c", - elem: []string{"../../../go"}, - }, - wantResult: "https://go.googlesource.com/go", - wantErr: false, + base: "https://go.googlesource.com/a/b/c", + elem: []string{"../../../go"}, + out: "https://go.googlesource.com/go", + err: false, }, { - name: "test . cul path", - args: args{ - baseUrl: "https://go.googlesource.com/", - elem: []string{"./go"}, - }, - wantResult: "https://go.googlesource.com/go", - wantErr: false, + base: "https://go.googlesource.com/", + elem: []string{"./go"}, + out: "https://go.googlesource.com/go", + err: false, }, { - name: "test multiple Separator", - args: args{ - baseUrl: "https://go.googlesource.com//", - elem: []string{"/go"}, - }, - wantResult: "https://go.googlesource.com/go", - wantErr: false, + base: "https://go.googlesource.com//", + elem: []string{"/go"}, + out: "https://go.googlesource.com/go", + err: false, }, { - name: "test more elems", - args: args{ - baseUrl: "https://go.googlesource.com//", - elem: []string{"/go", "a", "b", "c"}, - }, - wantResult: "https://go.googlesource.com/go/a/b/c", - wantErr: false, + base: "https://go.googlesource.com//", + elem: []string{"/go", "a", "b", "c"}, + out: "https://go.googlesource.com/go/a/b/c", + err: false, }, } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotResult, err := JoinPath(tt.args.baseUrl, tt.args.elem...) - if (err != nil) != tt.wantErr { - t.Errorf("JoinPath() error = %v, wantErr %v", err, tt.wantErr) - return + if out, err := JoinPath(tt.base, tt.elem...); out != tt.out || (err != nil) != tt.err { + wantErr := "nil" + if tt.err { + wantErr = "non-nil error" } - if gotResult != tt.wantResult { - t.Errorf("JoinPath() = %v, want %v", gotResult, tt.wantResult) + t.Errorf("JoinPath(%q, %q) = %q, %v, want %q, %v", tt.base, tt.elem, out, err, tt.out, wantErr) + } + u, err := Parse(tt.base) + if err == nil { + u = u.JoinPath(tt.elem...) + } + out := u.String() + if out != tt.out || (err != nil) != tt.err { + wantErr := "nil" + if tt.err { + wantErr = "non-nil error" } - }) + t.Errorf("JoinPath(%q, %q) = %q, %v, want %q, %v", tt.base, tt.elem, out, err, tt.out, wantErr) + } } } From 4209abbf6420e0b1fd3021fedf5b4d0bce53a67d Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Thu, 3 Mar 2022 14:51:37 -0500 Subject: [PATCH 4/7] Minor wording tweak --- src/net/url/url.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/url/url.go b/src/net/url/url.go index e047e8618379f5..1571bf728ba914 100644 --- a/src/net/url/url.go +++ b/src/net/url/url.go @@ -1229,7 +1229,7 @@ func stringContainsCTLByte(s string) bool { return false } -// JoinPath returns a string with the provided path elements joined to +// JoinPath returns a URL string with the provided path elements joined to // the existing path of base and the resulting path cleaned of any ./ or ../ elements. func JoinPath(base string, elem ...string) (result string, err error) { url, err := Parse(base) From 82304b688289324f49060146f5e9dde1e53891a7 Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Thu, 3 Mar 2022 15:01:29 -0500 Subject: [PATCH 5/7] Add parse error case --- src/net/url/url_test.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index 8555a15e00b229..8779ee209d249c 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -2100,6 +2100,12 @@ func TestJoinPath(t *testing.T) { out: "https://go.googlesource.com/go/a/b/c", err: false, }, + { + base: "http://[fe80::1%en0]:8080/", + elem: []string{"/go"}, + out: "", + err: true, + }, } for _, tt := range tests { if out, err := JoinPath(tt.base, tt.elem...); out != tt.out || (err != nil) != tt.err { @@ -2109,17 +2115,18 @@ func TestJoinPath(t *testing.T) { } t.Errorf("JoinPath(%q, %q) = %q, %v, want %q, %v", tt.base, tt.elem, out, err, tt.out, wantErr) } + var out string u, err := Parse(tt.base) if err == nil { u = u.JoinPath(tt.elem...) + out = u.String() } - out := u.String() if out != tt.out || (err != nil) != tt.err { wantErr := "nil" if tt.err { wantErr = "non-nil error" } - t.Errorf("JoinPath(%q, %q) = %q, %v, want %q, %v", tt.base, tt.elem, out, err, tt.out, wantErr) + t.Errorf("Parse(%q).JoinPath(%q) = %q, %v, want %q, %v", tt.base, tt.elem, out, err, tt.out, wantErr) } } } From 4cd3ab195b967fa2df5b249e027a1bbaf95f24a0 Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Thu, 3 Mar 2022 15:50:28 -0500 Subject: [PATCH 6/7] Simplify error checking --- src/net/url/url_test.go | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go index 8779ee209d249c..84dba45c3c1366 100644 --- a/src/net/url/url_test.go +++ b/src/net/url/url_test.go @@ -2068,51 +2068,43 @@ func TestJoinPath(t *testing.T) { base string elem []string out string - err bool }{ { base: "https://go.googlesource.com", elem: []string{"go"}, out: "https://go.googlesource.com/go", - err: false, }, { base: "https://go.googlesource.com/a/b/c", elem: []string{"../../../go"}, out: "https://go.googlesource.com/go", - err: false, }, { base: "https://go.googlesource.com/", elem: []string{"./go"}, out: "https://go.googlesource.com/go", - err: false, }, { base: "https://go.googlesource.com//", elem: []string{"/go"}, out: "https://go.googlesource.com/go", - err: false, }, { base: "https://go.googlesource.com//", elem: []string{"/go", "a", "b", "c"}, out: "https://go.googlesource.com/go/a/b/c", - err: false, }, { base: "http://[fe80::1%en0]:8080/", elem: []string{"/go"}, - out: "", - err: true, }, } for _, tt := range tests { - if out, err := JoinPath(tt.base, tt.elem...); out != tt.out || (err != nil) != tt.err { - wantErr := "nil" - if tt.err { - wantErr = "non-nil error" - } + wantErr := "nil" + if tt.out == "" { + wantErr = "non-nil error" + } + if out, err := JoinPath(tt.base, tt.elem...); out != tt.out || (err == nil) != (tt.out != "") { t.Errorf("JoinPath(%q, %q) = %q, %v, want %q, %v", tt.base, tt.elem, out, err, tt.out, wantErr) } var out string @@ -2121,11 +2113,7 @@ func TestJoinPath(t *testing.T) { u = u.JoinPath(tt.elem...) out = u.String() } - if out != tt.out || (err != nil) != tt.err { - wantErr := "nil" - if tt.err { - wantErr = "non-nil error" - } + 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) } } From 51b735066eef74f5e67c3e8899c58f44c0383c61 Mon Sep 17 00:00:00 2001 From: Carl Johnson Date: Fri, 4 Mar 2022 09:47:59 -0500 Subject: [PATCH 7/7] Add new API to api/next.txt --- api/next.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/next.txt b/api/next.txt index 23fd98a9bac0c7..148cbffbfeb6fe 100644 --- a/api/next.txt +++ b/api/next.txt @@ -3,3 +3,5 @@ pkg encoding/binary, type AppendByteOrder interface, AppendUint16([]uint8, uint1 pkg encoding/binary, type AppendByteOrder interface, AppendUint32([]uint8, uint32) []uint8 pkg encoding/binary, type AppendByteOrder interface, AppendUint64([]uint8, uint64) []uint8 pkg encoding/binary, type AppendByteOrder interface, String() string +pkg net/url, func JoinPath(string, ...string) (string, error) +pkg net/url, method (*URL) JoinPath(...string) *URL