Skip to content

Commit

Permalink
Add Client.Clone function (#774)
Browse files Browse the repository at this point in the history
* Change Client locks to pointer values

* Add Clone function

* Add test

---------

Co-authored-by: fabiante <fabiante@users.noreply.github.com>
  • Loading branch information
fabiante and fabiante committed Mar 3, 2024
1 parent 0ac42a2 commit 37157fa
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 5 deletions.
31 changes: 26 additions & 5 deletions client.go
Expand Up @@ -142,11 +142,11 @@ type Client struct {
proxyURL *url.URL
beforeRequest []RequestMiddleware
udBeforeRequest []RequestMiddleware
udBeforeRequestLock sync.RWMutex
udBeforeRequestLock *sync.RWMutex
preReqHook PreRequestHook
successHooks []SuccessHook
afterResponse []ResponseMiddleware
afterResponseLock sync.RWMutex
afterResponseLock *sync.RWMutex
requestLog RequestLogCallback
responseLog ResponseLogCallback
errorHooks []ErrorHook
Expand Down Expand Up @@ -1125,6 +1125,25 @@ func (c *Client) GetClient() *http.Client {
return c.httpClient
}

// Clone returns a clone of the original client.
//
// Be carefull when using this function:
// - Interface values are not deeply cloned. Thus, both the original and the clone will use the
// same value.
// - This function is not safe for concurrent use. You should only use this when you are sure that
// the client is not being used by any other goroutine.
//
// Since v2.12.0
func (c *Client) Clone() *Client {
// dereference the pointer and copy the value
cc := *c

// lock values should not be copied - thus new values are used.
cc.afterResponseLock = &sync.RWMutex{}
cc.udBeforeRequestLock = &sync.RWMutex{}
return &cc
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// Client Unexported methods
//_______________________________________________________________________
Expand Down Expand Up @@ -1360,9 +1379,11 @@ func createClient(hc *http.Client) *Client {
XMLUnmarshal: xml.Unmarshal,
HeaderAuthorizationKey: http.CanonicalHeaderKey("Authorization"),

jsonEscapeHTML: true,
httpClient: hc,
debugBodySizeLimit: math.MaxInt32,
jsonEscapeHTML: true,
httpClient: hc,
debugBodySizeLimit: math.MaxInt32,
udBeforeRequestLock: &sync.RWMutex{},
afterResponseLock: &sync.RWMutex{},
}

// Logger
Expand Down
26 changes: 26 additions & 0 deletions client_test.go
Expand Up @@ -1069,3 +1069,29 @@ func TestUnixSocket(t *testing.T) {
assertNil(t, err)
assertEqual(t, "Hello resty client from a server running on endpoint /hello!", res.String())
}

func TestClone(t *testing.T) {
parent := New()

// set a non-interface field
parent.SetBaseURL("http://localhost")

// set an interface field
parent.UserInfo = &User{
Username: "parent",
}

clone := parent.Clone()
// update value of non-interface type - change will only happen on clone
clone.SetBaseURL("https://local.host")
// update value of interface type - change will also happen on parent
clone.UserInfo.Username = "clone"

// asert non-interface type
assertEqual(t, "http://localhost", parent.BaseURL)
assertEqual(t, "https://local.host", clone.BaseURL)

// assert interface type
assertEqual(t, "clone", parent.UserInfo.Username)
assertEqual(t, "clone", clone.UserInfo.Username)
}

0 comments on commit 37157fa

Please sign in to comment.