Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Client.Clone function #774

Merged
merged 3 commits into from Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 {
fabiante marked this conversation as resolved.
Show resolved Hide resolved
// 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
fabiante marked this conversation as resolved.
Show resolved Hide resolved
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// 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)
}