Skip to content

Commit 37157fa

Browse files
authored
Add Client.Clone function (#774)
* Change Client locks to pointer values * Add Clone function * Add test --------- Co-authored-by: fabiante <[email protected]>
1 parent 0ac42a2 commit 37157fa

File tree

2 files changed

+52
-5
lines changed

2 files changed

+52
-5
lines changed

client.go

+26-5
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,11 @@ type Client struct {
142142
proxyURL *url.URL
143143
beforeRequest []RequestMiddleware
144144
udBeforeRequest []RequestMiddleware
145-
udBeforeRequestLock sync.RWMutex
145+
udBeforeRequestLock *sync.RWMutex
146146
preReqHook PreRequestHook
147147
successHooks []SuccessHook
148148
afterResponse []ResponseMiddleware
149-
afterResponseLock sync.RWMutex
149+
afterResponseLock *sync.RWMutex
150150
requestLog RequestLogCallback
151151
responseLog ResponseLogCallback
152152
errorHooks []ErrorHook
@@ -1125,6 +1125,25 @@ func (c *Client) GetClient() *http.Client {
11251125
return c.httpClient
11261126
}
11271127

1128+
// Clone returns a clone of the original client.
1129+
//
1130+
// Be carefull when using this function:
1131+
// - Interface values are not deeply cloned. Thus, both the original and the clone will use the
1132+
// same value.
1133+
// - This function is not safe for concurrent use. You should only use this when you are sure that
1134+
// the client is not being used by any other goroutine.
1135+
//
1136+
// Since v2.12.0
1137+
func (c *Client) Clone() *Client {
1138+
// dereference the pointer and copy the value
1139+
cc := *c
1140+
1141+
// lock values should not be copied - thus new values are used.
1142+
cc.afterResponseLock = &sync.RWMutex{}
1143+
cc.udBeforeRequestLock = &sync.RWMutex{}
1144+
return &cc
1145+
}
1146+
11281147
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
11291148
// Client Unexported methods
11301149
//_______________________________________________________________________
@@ -1360,9 +1379,11 @@ func createClient(hc *http.Client) *Client {
13601379
XMLUnmarshal: xml.Unmarshal,
13611380
HeaderAuthorizationKey: http.CanonicalHeaderKey("Authorization"),
13621381

1363-
jsonEscapeHTML: true,
1364-
httpClient: hc,
1365-
debugBodySizeLimit: math.MaxInt32,
1382+
jsonEscapeHTML: true,
1383+
httpClient: hc,
1384+
debugBodySizeLimit: math.MaxInt32,
1385+
udBeforeRequestLock: &sync.RWMutex{},
1386+
afterResponseLock: &sync.RWMutex{},
13661387
}
13671388

13681389
// Logger

client_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -1069,3 +1069,29 @@ func TestUnixSocket(t *testing.T) {
10691069
assertNil(t, err)
10701070
assertEqual(t, "Hello resty client from a server running on endpoint /hello!", res.String())
10711071
}
1072+
1073+
func TestClone(t *testing.T) {
1074+
parent := New()
1075+
1076+
// set a non-interface field
1077+
parent.SetBaseURL("http://localhost")
1078+
1079+
// set an interface field
1080+
parent.UserInfo = &User{
1081+
Username: "parent",
1082+
}
1083+
1084+
clone := parent.Clone()
1085+
// update value of non-interface type - change will only happen on clone
1086+
clone.SetBaseURL("https://local.host")
1087+
// update value of interface type - change will also happen on parent
1088+
clone.UserInfo.Username = "clone"
1089+
1090+
// asert non-interface type
1091+
assertEqual(t, "http://localhost", parent.BaseURL)
1092+
assertEqual(t, "https://local.host", clone.BaseURL)
1093+
1094+
// assert interface type
1095+
assertEqual(t, "clone", parent.UserInfo.Username)
1096+
assertEqual(t, "clone", clone.UserInfo.Username)
1097+
}

0 commit comments

Comments
 (0)