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.16.3
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.16.4
Choose a head ref
  • 3 commits
  • 8 files changed
  • 2 contributors

Commits on Jan 17, 2025

  1. fix: retry not reset multipartField reader (#953)

    wxip authored Jan 17, 2025
    Copy the full SHA
    414b364 View commit details

Commits on Jan 18, 2025

  1. Copy the full SHA
    4eae633 View commit details

Commits on Jan 20, 2025

  1. Copy the full SHA
    7ad1178 View commit details
Showing with 107 additions and 28 deletions.
  1. +3 −3 README.md
  2. +3 −0 client.go
  3. +3 −15 middleware.go
  4. +24 −6 request_test.go
  5. +1 −1 resty.go
  6. +3 −3 resty_test.go
  7. +15 −0 retry.go
  8. +55 −0 retry_test.go
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -4,12 +4,12 @@
<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%3Av2"><img src="https://github.com/go-resty/resty/actions/workflows/ci.yml/badge.svg?branch=v2" alt="Build Status"></a> <a href="https://app.codecov.io/gh/go-resty/resty/tree/v2"><img src="https://codecov.io/gh/go-resty/resty/branch/v2/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.16.3-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%3Av2"><img src="https://github.com/go-resty/resty/actions/workflows/ci.yml/badge.svg?branch=v2" alt="Build Status"></a> <a href="https://app.codecov.io/gh/go-resty/resty/tree/v2"><img src="https://codecov.io/gh/go-resty/resty/branch/v2/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.16.4-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>

## News

* v2.16.3 [released](https://github.com/go-resty/resty/releases/tag/v2.16.3) and tagged on Jan 08, 2025.
* v2.16.4 [released](https://github.com/go-resty/resty/releases/tag/v2.16.4) and tagged on Jan 20, 2025.
* 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).
@@ -105,7 +105,7 @@ Resty author also published following projects for Go Community.

```bash
# Go Modules
require github.com/go-resty/resty/v2 v2.16.3
require github.com/go-resty/resty/v2 v2.16.4
```

## Usage
3 changes: 3 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
@@ -450,6 +450,8 @@ func (c *Client) R() *Request {
PathParams: map[string]string{},
RawPathParams: map[string]string{},
Debug: c.Debug,
AuthScheme: c.AuthScheme,
Token: c.Token,

client: c,
multipartFiles: []*File{},
@@ -1464,6 +1466,7 @@ func createClient(hc *http.Client) *Client {
XMLMarshal: xml.Marshal,
XMLUnmarshal: xml.Unmarshal,
HeaderAuthorizationKey: http.CanonicalHeaderKey("Authorization"),
AuthScheme: "Bearer",

jsonEscapeHTML: true,
httpClient: hc,
18 changes: 3 additions & 15 deletions middleware.go
Original file line number Diff line number Diff line change
@@ -298,21 +298,9 @@ func addCredentials(c *Client, r *Request) error {
}
}

// Set the Authorization Header Scheme
var authScheme string
if !IsStringEmpty(r.AuthScheme) {
authScheme = r.AuthScheme
} else if !IsStringEmpty(c.AuthScheme) {
authScheme = c.AuthScheme
} else {
authScheme = "Bearer"
}

// Build the Token Auth header
if !IsStringEmpty(r.Token) { // takes precedence
r.RawRequest.Header.Set(c.HeaderAuthorizationKey, authScheme+" "+r.Token)
} else if !IsStringEmpty(c.Token) {
r.RawRequest.Header.Set(c.HeaderAuthorizationKey, authScheme+" "+c.Token)
// Build the token Auth header
if !IsStringEmpty(r.Token) {
r.RawRequest.Header.Set(c.HeaderAuthorizationKey, strings.TrimSpace(r.AuthScheme+" "+r.Token))
}

return nil
30 changes: 24 additions & 6 deletions request_test.go
Original file line number Diff line number Diff line change
@@ -681,13 +681,31 @@ func TestRequestAuthScheme(t *testing.T) {
SetAuthScheme("OAuth").
SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF")

resp, err := c.R().
SetAuthScheme("Bearer").
SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF-Request").
Get(ts.URL + "/profile")
t.Run("override auth scheme", func(t *testing.T) {
resp, err := c.R().
SetAuthScheme("Bearer").
SetAuthToken("004DDB79-6801-4587-B976-F093E6AC44FF-Request").
Get(ts.URL + "/profile")

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

t.Run("empty auth scheme GH954", func(t *testing.T) {
tokenValue := "004DDB79-6801-4587-B976-F093E6AC44FF"

// set client level
c.SetAuthScheme("").
SetAuthToken(tokenValue)

resp, err := c.R().
Get(ts.URL + "/profile")

assertError(t, err)
assertEqual(t, http.StatusOK, resp.StatusCode())
assertEqual(t, tokenValue, resp.Request.Header.Get(hdrAuthorizationKey))
})

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

func TestRequestDigestAuth(t *testing.T) {
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.16.3"
const Version = "2.16.4"

// New method creates a new Resty client.
func New() *Client {
6 changes: 3 additions & 3 deletions resty_test.go
Original file line number Diff line number Diff line change
@@ -501,19 +501,19 @@ func createAuthServerTLSOptional(t *testing.T, useTLS bool) *httptest.Server {
if r.URL.Path == "/profile" {
// 004DDB79-6801-4587-B976-F093E6AC44FF
auth := r.Header.Get("Authorization")
t.Logf("Bearer Auth: %v", auth)
t.Logf("Auth Header: %v", auth)

w.Header().Set(hdrContentTypeKey, "application/json; charset=utf-8")

if !strings.HasPrefix(auth, "Bearer ") {
if strings.HasPrefix(auth, "Basic ") {
w.Header().Set("Www-Authenticate", "Protected Realm")
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(`{ "id": "unauthorized", "message": "Invalid credentials" }`))

return
}

if auth[7:] == "004DDB79-6801-4587-B976-F093E6AC44FF" || auth[7:] == "004DDB79-6801-4587-B976-F093E6AC44FF-Request" {
if strings.Contains(auth, "004DDB79-6801-4587-B976-F093E6AC44FF") {
_, _ = w.Write([]byte(`{ "id": "success", "message": "login successful" }`))
}
}
15 changes: 15 additions & 0 deletions retry.go
Original file line number Diff line number Diff line change
@@ -139,6 +139,9 @@ func Backoff(operation func() (*Response, error), options ...Option) error {
if err := resetFileReaders(resp.Request.multipartFiles); err != nil {
return err
}
if err := resetFieldReaders(resp.Request.multipartFields); err != nil {
return err
}
}

for _, hook := range opts.retryHooks {
@@ -250,3 +253,15 @@ func resetFileReaders(files []*File) error {

return nil
}

func resetFieldReaders(fields []*MultipartField) error {
for _, f := range fields {
if rs, ok := f.Reader.(io.ReadSeeker); ok {
if _, err := rs.Seek(0, io.SeekStart); err != nil {
return err
}
}
}

return nil
}
55 changes: 55 additions & 0 deletions retry_test.go
Original file line number Diff line number Diff line change
@@ -823,3 +823,58 @@ func TestResetMultipartReaders(t *testing.T) {
assertEqual(t, 500, resp.StatusCode())
assertNil(t, err)
}

func TestResetMultipartFieldReaderSeekStartError(t *testing.T) {
ts := createFilePostServer(t)
defer ts.Close()

testSeeker := &failingSeeker{
bytes.NewReader([]byte("test")),
}

c := dc().
SetRetryCount(2).
SetTimeout(time.Second * 3).
SetRetryResetReaders(true).
AddRetryAfterErrorCondition()

resp, err := c.R().
SetMultipartField("file", "audio.wav", "audio/wav", testSeeker).
Post(ts.URL + "/set-reset-multipart-readers-test")

assertEqual(t, 500, resp.StatusCode())
assertEqual(t, err.Error(), errSeekFailure.Error())
}

func TestResetMultipartFieldReaders(t *testing.T) {
ts := createFilePostServer(t)
defer ts.Close()

str := "test"
buf := []byte(str)

bufReader := bytes.NewReader(buf)
bufCpy := make([]byte, len(buf))

c := dc().
SetRetryCount(2).
SetTimeout(time.Second * 3).
SetRetryResetReaders(true).
AddRetryAfterErrorCondition().
AddRetryHook(
func(response *Response, _ error) {
read, err := bufReader.Read(bufCpy)

assertNil(t, err)
assertEqual(t, len(buf), read)
assertEqual(t, str, string(bufCpy))
},
)

resp, err := c.R().
SetMultipartField("file", "audio.wav", "audio/wav", bufReader).
Post(ts.URL + "/set-reset-multipart-readers-test")

assertEqual(t, 500, resp.StatusCode())
assertNil(t, err)
}