mirror of
https://github.com/golang/go.git
synced 2026-04-02 09:20:29 +09:00
net/url: add (*URL).Clone
This change adds URL.Clone which creates a deep copy of the URL's fields including the .User and tests these. In a separate CL I shall send Values.Clone too. Updates #73450 Change-Id: Ifea4bfc4ddd0640247544ec111ec83bd9bbe9104 Reviewed-on: https://go-review.googlesource.com/c/go/+/746800 Reviewed-by: Nicholas Husin <husin@google.com> Auto-Submit: Emmanuel Odeke <emmanuel@orijtech.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Nicholas Husin <nsh@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Damien Neil <dneil@google.com>
This commit is contained in:
committed by
Gopher Robot
parent
839cd82fa5
commit
09c3cfbc20
1
api/next/73450.txt
Normal file
1
api/next/73450.txt
Normal file
@@ -0,0 +1 @@
|
||||
pkg net/url, method (*URL) Clone() *URL #73450
|
||||
1
doc/next/6-stdlib/99-minor/net/url/73450.md
Normal file
1
doc/next/6-stdlib/99-minor/net/url/73450.md
Normal file
@@ -0,0 +1 @@
|
||||
The new [URL.Clone] method creates a deep copy of a URL.
|
||||
@@ -39,16 +39,7 @@ func cloneURLValues(v url.Values) url.Values {
|
||||
//
|
||||
//go:linkname cloneURL
|
||||
func cloneURL(u *url.URL) *url.URL {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
u2 := new(url.URL)
|
||||
*u2 = *u
|
||||
if u.User != nil {
|
||||
u2.User = new(url.Userinfo)
|
||||
*u2.User = *u.User
|
||||
}
|
||||
return u2
|
||||
return u.Clone()
|
||||
}
|
||||
|
||||
// cloneMultipartForm should be an internal detail,
|
||||
|
||||
@@ -1322,3 +1322,16 @@ func JoinPath(base string, elem ...string) (result string, err error) {
|
||||
}
|
||||
return res.String(), nil
|
||||
}
|
||||
|
||||
// Clone creates a deep copy of the fields of the subject [URL].
|
||||
func (u *URL) Clone() *URL {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
uc := new(*u)
|
||||
if u.User != nil {
|
||||
uc.User = new(*u.User)
|
||||
}
|
||||
return uc
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"internal/diff"
|
||||
"io"
|
||||
"net"
|
||||
"reflect"
|
||||
@@ -2361,3 +2362,78 @@ func TestParseStrictIpv6(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestURLClone(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in *URL
|
||||
}{
|
||||
{"nil", nil},
|
||||
{"zero value", &URL{}},
|
||||
{
|
||||
"Populated but nil .User",
|
||||
&URL{
|
||||
User: nil,
|
||||
Host: "foo",
|
||||
Path: "/path",
|
||||
RawQuery: "a=b",
|
||||
},
|
||||
},
|
||||
{
|
||||
"non-nil .User",
|
||||
&URL{
|
||||
User: User("user"),
|
||||
Host: "foo",
|
||||
Path: "/path",
|
||||
RawQuery: "a=b",
|
||||
},
|
||||
},
|
||||
{
|
||||
"non-nil .User: user and password set",
|
||||
&URL{
|
||||
User: UserPassword("user", "password"),
|
||||
Host: "foo",
|
||||
Path: "/path",
|
||||
RawQuery: "a=b",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// 1. The cloned URL must always deep equal the input, but never the same pointer.
|
||||
cloned := tt.in.Clone()
|
||||
if !reflect.DeepEqual(tt.in, cloned) {
|
||||
t.Fatalf("Differing values\n%s",
|
||||
diff.Diff("original", []byte(tt.in.String()), "cloned", []byte(cloned.String())))
|
||||
}
|
||||
if tt.in == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure that their pointer values are not the same.
|
||||
if tt.in == cloned {
|
||||
t.Fatalf("URL: same pointer returned: %p", cloned)
|
||||
}
|
||||
|
||||
// 2. Test out malleability of URL fields.
|
||||
cloned.Scheme = "https"
|
||||
if cloned.Scheme == tt.in.Scheme {
|
||||
t.Error("Inconsistent state: cloned.scheme changed and reflected in the input's scheme")
|
||||
}
|
||||
if reflect.DeepEqual(tt.in, cloned) {
|
||||
t.Fatal("Inconsistent state: cloned and input are somehow the same")
|
||||
}
|
||||
|
||||
// 3. Ensure that the .User object deep equals but not the same pointer.
|
||||
if !reflect.DeepEqual(tt.in.User, cloned.User) {
|
||||
t.Fatalf("Differing .User\n%s",
|
||||
diff.Diff("original", []byte(tt.in.String()), "cloned", []byte(cloned.String())))
|
||||
}
|
||||
bothNil := tt.in.User == nil && cloned.User == nil
|
||||
if !bothNil && tt.in.User == cloned.User {
|
||||
t.Fatalf(".User: same pointer returned: %p", cloned.User)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user