Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: go-resty/resty
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v2.11.0
Choose a base ref
...
head repository: go-resty/resty
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2.12.0
Choose a head ref
  • 7 commits
  • 10 files changed
  • 7 contributors

Commits on Feb 19, 2024

  1. fix: doc typo (#769)

    victoraugustolls authored Feb 19, 2024
    Copy the full SHA
    16fc559 View commit details
  2. Update README.md (#772)

    purofle authored Feb 19, 2024
    Copy the full SHA
    bf77da8 View commit details
  3. Fixed JSONIndent request logging data race. (#775)

    buglloc authored Feb 19, 2024
    Copy the full SHA
    97187c4 View commit details

Commits on Mar 2, 2024

  1. fix: trailing NULL bytes in buffer while detecting a content type (#779)

    jeevatkm authored Mar 2, 2024
    Copy the full SHA
    0ac42a2 View commit details

Commits on Mar 3, 2024

  1. Add Client.Clone function (#774)

    * Change Client locks to pointer values
    
    * Add Clone function
    
    * Add test
    
    ---------
    
    Co-authored-by: fabiante <fabiante@users.noreply.github.com>
    fabiante and fabiante authored Mar 3, 2024
    Copy the full SHA
    37157fa View commit details

Commits on Mar 6, 2024

  1. fix: encode path params with BaseURL and the first param at index zero (

    sakateka authored Mar 6, 2024
    Copy the full SHA
    94e70d3 View commit details

Commits on Mar 17, 2024

  1. for v2.12.0 release (#783)

    jeevatkm authored Mar 17, 2024
    Copy the full SHA
    89d25d9 View commit details
Showing with 122 additions and 34 deletions.
  1. +7 −7 README.md
  2. +26 −5 client.go
  3. +49 −0 client_test.go
  4. +2 −2 go.mod
  5. +12 −8 go.sum
  6. +2 −2 middleware.go
  7. +13 −3 middleware_test.go
  8. +9 −5 request.go
  9. +1 −1 resty.go
  10. +1 −1 util.go
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
<p align="center"><a href="#features">Features</a> section describes in detail about Resty capabilities</p>
</p>
<p align="center">
<p align="center"><a href="https://github.com/go-resty/resty/actions/workflows/ci.yml?query=branch%3Amaster"><img src="https://github.com/go-resty/resty/actions/workflows/ci.yml/badge.svg" alt="Build Status"></a> <a href="https://codecov.io/gh/go-resty/resty/branch/master"><img src="https://codecov.io/gh/go-resty/resty/branch/master/graph/badge.svg" alt="Code Coverage"></a> <a href="https://goreportcard.com/report/go-resty/resty"><img src="https://goreportcard.com/badge/go-resty/resty" alt="Go Report Card"></a> <a href="https://github.com/go-resty/resty/releases/latest"><img src="https://img.shields.io/badge/version-2.11.0-blue.svg" alt="Release Version"></a> <a href="https://pkg.go.dev/github.com/go-resty/resty/v2"><img src="https://pkg.go.dev/badge/github.com/go-resty/resty" alt="GoDoc"></a> <a href="LICENSE"><img src="https://img.shields.io/github/license/go-resty/resty.svg" alt="License"></a> <a href="https://github.com/avelino/awesome-go"><img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Go"></a></p>
<p align="center"><a href="https://github.com/go-resty/resty/actions/workflows/ci.yml?query=branch%3Amaster"><img src="https://github.com/go-resty/resty/actions/workflows/ci.yml/badge.svg" alt="Build Status"></a> <a href="https://codecov.io/gh/go-resty/resty/branch/master"><img src="https://codecov.io/gh/go-resty/resty/branch/master/graph/badge.svg" alt="Code Coverage"></a> <a href="https://goreportcard.com/report/go-resty/resty"><img src="https://goreportcard.com/badge/go-resty/resty" alt="Go Report Card"></a> <a href="https://github.com/go-resty/resty/releases/latest"><img src="https://img.shields.io/badge/version-2.12.0-blue.svg" alt="Release Version"></a> <a href="https://pkg.go.dev/github.com/go-resty/resty/v2"><img src="https://pkg.go.dev/badge/github.com/go-resty/resty" alt="GoDoc"></a> <a href="LICENSE"><img src="https://img.shields.io/github/license/go-resty/resty.svg" alt="License"></a> <a href="https://github.com/avelino/awesome-go"><img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome Go"></a></p>
</p>
<p align="center">
<h4 align="center">Resty Communication Channels</h4>
@@ -13,7 +13,7 @@

## News

* v2.11.0 [released](https://github.com/go-resty/resty/releases/tag/v2.11.0) and tagged on Dec 27, 2023.
* v2.12.0 [released](https://github.com/go-resty/resty/releases/tag/v2.12.0) and tagged on Mar 17, 2024.
* v2.0.0 [released](https://github.com/go-resty/resty/releases/tag/v2.0.0) and tagged on Jul 16, 2019.
* v1.12.0 [released](https://github.com/go-resty/resty/releases/tag/v1.12.0) and tagged on Feb 27, 2019.
* v1.0 released and tagged on Sep 25, 2017. - Resty's first version was released on Sep 15, 2015 then it grew gradually as a very handy and helpful library. Its been a two years since first release. I'm very thankful to Resty users and its [contributors](https://github.com/go-resty/resty/graphs/contributors).
@@ -702,8 +702,8 @@ client.
})
```

By default, resty will retry requests that return a non-nil error during execution.
Therefore, the above setup will result in resty retrying requests with non-nil errors up to 3 times,
By default, resty will retry requests that return a non-nil error during execution.
Therefore, the above setup will result in resty retrying requests with non-nil errors up to 3 times,
with the delay increasing after each attempt.

You can optionally provide client with [custom retry conditions](https://pkg.go.dev/github.com/go-resty/resty/v2#RetryConditionFunc):
@@ -739,7 +739,7 @@ client.AddRetryCondition(
)
```

Multiple retry conditions can be added.
Multiple retry conditions can be added.
Note that if multiple conditions are specified, a retry will occur if any of the conditions are met.

It is also possible to use `resty.Backoff(...)` to get arbitrary retry scenarios
@@ -797,7 +797,7 @@ client.SetTimeout(1 * time.Minute)
// You can override all below settings and options at request level if you want to
//--------------------------------------------------------------------------------
// Host URL for all request. So you can use relative URL in the request
client.SetHostURL("http://httpbin.org")
client.SetBaseURL("http://httpbin.org")

// Headers for all request
client.SetHeader("Accept", "application/json")
@@ -861,7 +861,7 @@ client := resty.New()

// Set the previous transport that we created, set the scheme of the communication to the
// socket and set the unixSocket as the HostURL.
client.SetTransport(&transport).SetScheme("http").SetHostURL(unixSocket)
client.SetTransport(&transport).SetScheme("http").SetBaseURL(unixSocket)

// No need to write the host's URL on the request, just the path.
client.R().Get("http://localhost/index.html")
31 changes: 26 additions & 5 deletions client.go
Original file line number Diff line number Diff line change
@@ -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
@@ -1125,6 +1125,25 @@ func (c *Client) GetClient() *http.Client {
return c.httpClient
}

// Clone returns a clone of the original client.
//
// Be careful 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
//_______________________________________________________________________
@@ -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
49 changes: 49 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
@@ -766,6 +766,29 @@ func TestLogCallbacks(t *testing.T) {
assertNotNil(t, resp)
}

func TestDebugLogSimultaneously(t *testing.T) {
ts := createGetServer(t)

c := New().
SetDebug(true).
SetBaseURL(ts.URL).
outputLogTo(io.Discard)

t.Cleanup(ts.Close)
for i := 0; i < 50; i++ {
t.Run(fmt.Sprint(i), func(t *testing.T) {
t.Parallel()
resp, err := c.R().
SetBody([]int{1, 2, 3}).
SetHeader(hdrContentTypeKey, "application/json; charset=utf-8").
Post("/")

assertError(t, err)
assertEqual(t, http.StatusOK, resp.StatusCode())
})
}
}

func TestNewWithLocalAddr(t *testing.T) {
ts := createGetServer(t)
defer ts.Close()
@@ -1046,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)
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -3,6 +3,6 @@ module github.com/go-resty/resty/v2
go 1.16

require (
golang.org/x/net v0.17.0
golang.org/x/time v0.3.0
golang.org/x/net v0.22.0
golang.org/x/time v0.5.0
)
20 changes: 12 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -21,20 +23,22 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
4 changes: 2 additions & 2 deletions middleware.go
Original file line number Diff line number Diff line change
@@ -57,8 +57,8 @@ func parseRequestURL(c *Client, r *Request) error {
buf := acquireBuffer()
defer releaseBuffer(buf)
// search for the next or first opened curly bracket
for curr := strings.Index(r.URL, "{"); curr > prev; curr = prev + strings.Index(r.URL[prev:], "{") {
// write everything form the previous position up to the current
for curr := strings.Index(r.URL, "{"); curr == 0 || curr > prev; curr = prev + strings.Index(r.URL[prev:], "{") {
// write everything from the previous position up to the current
if curr > prev {
buf.WriteString(r.URL[prev:curr])
}
16 changes: 13 additions & 3 deletions middleware_test.go
Original file line number Diff line number Diff line change
@@ -145,6 +145,16 @@ func Test_parseRequestURL(t *testing.T) {
},
expectedURL: "https://example.com/1/2",
},
{
name: "using base url with path param at index 0",
init: func(c *Client, r *Request) {
c.SetBaseURL("https://example.com/prefix")
r.SetPathParam("first", "1").
SetPathParam("second", "2")
r.URL = "{first}/{second}"
},
expectedURL: "https://example.com/prefix/1/2",
},
{
name: "using BaseURL with absolute URL in request",
init: func(c *Client, r *Request) {
@@ -756,7 +766,7 @@ func Test_parseRequestBody(t *testing.T) {
expectedContentLength: "744",
},
{
name: "mulipart fields",
name: "multipart fields",
init: func(c *Client, r *Request) {
r.SetMultipartFields(
&MultipartField{
@@ -776,15 +786,15 @@ func Test_parseRequestBody(t *testing.T) {
expectedContentLength: "344",
},
{
name: "mulipart files",
name: "multipart files",
init: func(c *Client, r *Request) {
r.SetFileReader("foo", "foo.txt", strings.NewReader("1")).
SetFileReader("bar", "bar.txt", strings.NewReader("2")).
SetContentLength(true)
},
expectedBodyBuf: []byte(`{"bar":"2","foo":"1"}`),
expectedContentType: "multipart/form-data; boundary=",
expectedContentLength: "412",
expectedContentLength: "414",
},
{
name: "body with errorReader",
14 changes: 9 additions & 5 deletions request.go
Original file line number Diff line number Diff line change
@@ -1014,7 +1014,12 @@ func (r *Request) fmtBodyString(sl int64) (body string) {
contentType := r.Header.Get(hdrContentTypeKey)
kind := kindOf(r.Body)
if canJSONMarshal(contentType, kind) {
prtBodyBytes, err = noescapeJSONMarshalIndent(&r.Body)
var bodyBuf *bytes.Buffer
bodyBuf, err = noescapeJSONMarshalIndent(&r.Body)
if err == nil {
prtBodyBytes = bodyBuf.Bytes()
defer releaseBuffer(bodyBuf)
}
} else if IsXMLType(contentType) && (kind == reflect.Struct) {
prtBodyBytes, err = xml.MarshalIndent(&r.Body, "", " ")
} else if b, ok := r.Body.(string); ok {
@@ -1077,17 +1082,16 @@ var noescapeJSONMarshal = func(v interface{}) (*bytes.Buffer, error) {
return buf, nil
}

var noescapeJSONMarshalIndent = func(v interface{}) ([]byte, error) {
var noescapeJSONMarshalIndent = func(v interface{}) (*bytes.Buffer, error) {
buf := acquireBuffer()
defer releaseBuffer(buf)

encoder := json.NewEncoder(buf)
encoder.SetEscapeHTML(false)
encoder.SetIndent("", " ")

if err := encoder.Encode(v); err != nil {
releaseBuffer(buf)
return nil, err
}

return buf.Bytes(), nil
return buf, nil
}
2 changes: 1 addition & 1 deletion resty.go
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ import (
)

// Version # of resty
const Version = "2.10.0"
const Version = "2.12.0"

// New method creates a new Resty client.
func New() *Client {
2 changes: 1 addition & 1 deletion util.go
Original file line number Diff line number Diff line change
@@ -216,7 +216,7 @@ func writeMultipartFormFile(w *multipart.Writer, fieldName, fileName string, r i
return err
}

partWriter, err := w.CreatePart(createMultipartHeader(fieldName, fileName, http.DetectContentType(cbuf)))
partWriter, err := w.CreatePart(createMultipartHeader(fieldName, fileName, http.DetectContentType(cbuf[:size])))
if err != nil {
return err
}