Skip to content

Commit

Permalink
Merge pull request #374 from go-resty/few-enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
jeevatkm committed Sep 15, 2020
2 parents 3c6c188 + 7643bc9 commit 608c8d7
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 64 deletions.
106 changes: 56 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

* v2.3.0 [released](https://github.com/go-resty/resty/releases/tag/v2.3.0) and tagged on May 20, 2020.
* 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.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).

## Features
Expand Down Expand Up @@ -55,11 +55,12 @@
* Option to specify expected `Content-Type` when response `Content-Type` header missing. Refer to [#92](https://github.com/go-resty/resty/issues/92)
* Resty design
* Have client level settings & options and also override at Request level if you want to
* Request and Response middlewares
* Request and Response middleware
* Create Multiple clients if you want to `resty.New()`
* Supports `http.RoundTripper` implementation, see [SetTransport](https://godoc.org/github.com/go-resty/resty#Client.SetTransport)
* goroutine concurrent safe
* Resty Client trace, see [Client.EnableTrace](https://godoc.org/github.com/go-resty/resty#Client.EnableTrace) and [Request.EnableTrace](https://godoc.org/github.com/go-resty/resty#Request.EnableTrace)
* Since v2.4.0, `RequestAttempt` value supported in trace info also Request instance contains `Attempt` attribute
* Debug mode - clean and informative logging presentation
* Gzip - Go does it automatically also resty has fallback handling too
* Works fine with `HTTP/2` and `HTTP/1.1`
Expand All @@ -83,7 +84,7 @@

#### Supported Go Versions

Initially Resty started supporting `go modules` since `v1.10.0` release.
Initially Resty started supporting `go modules` since `v1.10.0` release.

Starting Resty v2 and higher versions, it fully embraces [go modules](https://github.com/golang/go/wiki/Modules) package release. It requires a Go version capable of understanding `/vN` suffixed imports:

Expand Down Expand Up @@ -124,65 +125,70 @@ import "github.com/go-resty/resty/v2"
client := resty.New()

resp, err := client.R().
EnableTrace().
Get("https://httpbin.org/get")
EnableTrace().
Get("https://httpbin.org/get")

// Explore response object
fmt.Println("Response Info:")
fmt.Println("Error :", err)
fmt.Println("Status Code:", resp.StatusCode())
fmt.Println("Status :", resp.Status())
fmt.Println("Proto :", resp.Proto())
fmt.Println("Time :", resp.Time())
fmt.Println("Received At:", resp.ReceivedAt())
fmt.Println("Body :\n", resp)
fmt.Println(" Error :", err)
fmt.Println(" Status Code:", resp.StatusCode())
fmt.Println(" Status :", resp.Status())
fmt.Println(" Proto :", resp.Proto())
fmt.Println(" Time :", resp.Time())
fmt.Println(" Received At:", resp.ReceivedAt())
fmt.Println(" Body :\n", resp)
fmt.Println()

// Explore trace info
fmt.Println("Request Trace Info:")
ti := resp.Request.TraceInfo()
fmt.Println("DNSLookup :", ti.DNSLookup)
fmt.Println("ConnTime :", ti.ConnTime)
fmt.Println("TCPConnTime :", ti.TCPConnTime)
fmt.Println("TLSHandshake :", ti.TLSHandshake)
fmt.Println("ServerTime :", ti.ServerTime)
fmt.Println("ResponseTime :", ti.ResponseTime)
fmt.Println("TotalTime :", ti.TotalTime)
fmt.Println("IsConnReused :", ti.IsConnReused)
fmt.Println("IsConnWasIdle:", ti.IsConnWasIdle)
fmt.Println("ConnIdleTime :", ti.ConnIdleTime)
fmt.Println(" DNSLookup :", ti.DNSLookup)
fmt.Println(" ConnTime :", ti.ConnTime)
fmt.Println(" TCPConnTime :", ti.TCPConnTime)
fmt.Println(" TLSHandshake :", ti.TLSHandshake)
fmt.Println(" ServerTime :", ti.ServerTime)
fmt.Println(" ResponseTime :", ti.ResponseTime)
fmt.Println(" TotalTime :", ti.TotalTime)
fmt.Println(" IsConnReused :", ti.IsConnReused)
fmt.Println(" IsConnWasIdle :", ti.IsConnWasIdle)
fmt.Println(" ConnIdleTime :", ti.ConnIdleTime)
fmt.Println(" RequestAttempt:", ti.RequestAttempt)
fmt.Println(" RemoteAddr :", ti.RemoteAddr.String())

/* Output
Response Info:
Error : <nil>
Status Code: 200
Status : 200 OK
Proto : HTTP/2.0
Time : 475.611189ms
Received At: 2020-05-19 00:11:06.828188 -0700 PDT m=+0.476510773
Body :
{
"args": {},
"headers": {
"Accept-Encoding": "gzip",
"Host": "httpbin.org",
"User-Agent": "go-resty/2.3.0 (https://github.com/go-resty/resty)"
},
"origin": "0.0.0.0",
"url": "https://httpbin.org/get"
}
Error : <nil>
Status Code: 200
Status : 200 OK
Proto : HTTP/2.0
Time : 457.034718ms
Received At: 2020-09-14 15:35:29.784681 -0700 PDT m=+0.458137045
Body :
{
"args": {},
"headers": {
"Accept-Encoding": "gzip",
"Host": "httpbin.org",
"User-Agent": "go-resty/2.3.0-dev (https://github.com/go-resty/resty)",
"X-Amzn-Trace-Id": "Root=1-5f5ff031-000ff6292204aa6898e4de49"
},
"origin": "0.0.0.0",
"url": "https://httpbin.org/get"
}
Request Trace Info:
DNSLookup : 4.870246ms
ConnTime : 393.95373ms
TCPConnTime : 78.360432ms
TLSHandshake : 310.032859ms
ServerTime : 81.648284ms
ResponseTime : 124.266µs
TotalTime : 475.611189ms
IsConnReused : false
IsConnWasIdle: false
ConnIdleTime : 0s
DNSLookup : 4.074657ms
ConnTime : 381.709936ms
TCPConnTime : 77.428048ms
TLSHandshake : 299.623597ms
ServerTime : 75.414703ms
ResponseTime : 79.337µs
TotalTime : 457.034718ms
IsConnReused : false
IsConnWasIdle : false
ConnIdleTime : 0s
RequestAttempt: 1
RemoteAddr : 3.221.81.55:443
*/
```

Expand Down Expand Up @@ -831,7 +837,7 @@ More detailed example of mocking resty http requests using ginko could be found
Resty releases versions according to [Semantic Versioning](http://semver.org)

* Resty v2 does not use `gopkg.in` service for library versioning.
* Resty fully adapted to `go mod` capabilities since `v1.10.0` release.
* Resty fully adapted to `go mod` capabilities since `v1.10.0` release.
* Resty v1 series was using `gopkg.in` to provide versioning. `gopkg.in/resty.vX` points to appropriate tagged versions; `X` denotes version series number and it's a stable release for production use. For e.g. `gopkg.in/resty.v0`.
* Development takes place at the master branch. Although the code in master should always compile and test successfully, it might break API's. I aim to maintain backwards compatibility, but sometimes API's and behavior might be changed to fix a bug.

Expand Down
2 changes: 1 addition & 1 deletion middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ func responseLogger(c *Client, res *Response) error {
"HEADERS :\n" +
composeHeaders(c, res.Request, rl.Header) + "\n"
if res.Request.isSaveResponse {
debugLog += fmt.Sprintf("BODY :\n***** RESPONSE WRITTEN INTO FILE *****\n")
debugLog += "BODY :\n***** RESPONSE WRITTEN INTO FILE *****\n"
} else {
debugLog += fmt.Sprintf("BODY :\n%v\n", rl.Body)
}
Expand Down
34 changes: 24 additions & 10 deletions request.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ type Request struct {
UserInfo *User
Cookies []*http.Cookie

// Attempt is to represent the request attempt made during a Resty
// request execution flow, including retry count.
//
// Since v2.4.0
Attempt int

isMultiPart bool
isFormData bool
setContentLength bool
Expand Down Expand Up @@ -576,14 +582,17 @@ func (r *Request) TraceInfo() TraceInfo {
}

ti := TraceInfo{
DNSLookup: ct.dnsDone.Sub(ct.dnsStart),
TLSHandshake: ct.tlsHandshakeDone.Sub(ct.tlsHandshakeStart),
ServerTime: ct.gotFirstResponseByte.Sub(ct.gotConn),
IsConnReused: ct.gotConnInfo.Reused,
IsConnWasIdle: ct.gotConnInfo.WasIdle,
ConnIdleTime: ct.gotConnInfo.IdleTime,
DNSLookup: ct.dnsDone.Sub(ct.dnsStart),
TLSHandshake: ct.tlsHandshakeDone.Sub(ct.tlsHandshakeStart),
ServerTime: ct.gotFirstResponseByte.Sub(ct.gotConn),
IsConnReused: ct.gotConnInfo.Reused,
IsConnWasIdle: ct.gotConnInfo.WasIdle,
ConnIdleTime: ct.gotConnInfo.IdleTime,
RequestAttempt: r.Attempt,
}

// Calculate the total time accordingly,
// when connection is reused
if ct.gotConnInfo.Reused {
ti.TotalTime = ct.endTime.Sub(ct.getConn)
} else {
Expand All @@ -605,6 +614,11 @@ func (r *Request) TraceInfo() TraceInfo {
ti.ResponseTime = ct.endTime.Sub(ct.gotFirstResponseByte)
}

// Capture remote address info when connection is non-nil
if ct.gotConnInfo.Conn != nil {
ti.RemoteAddr = ct.gotConnInfo.Conn.RemoteAddr()
}

return ti
}

Expand Down Expand Up @@ -680,20 +694,20 @@ func (r *Request) Execute(method, url string) (*Response, error) {
r.URL = r.selectAddr(addrs, url, 0)

if r.client.RetryCount == 0 {
r.Attempt = 1
resp, err = r.client.execute(r)
return resp, unwrapNoRetryErr(err)
}

attempt := 0
err = Backoff(
func() (*Response, error) {
attempt++
r.Attempt++

r.URL = r.selectAddr(addrs, url, attempt)
r.URL = r.selectAddr(addrs, url, r.Attempt)

resp, err = r.client.execute(r)
if err != nil {
r.client.log.Errorf("%v, Attempt %v", err, attempt)
r.client.log.Errorf("%v, Attempt %v", err, r.Attempt)
}

return resp, err
Expand Down
4 changes: 4 additions & 0 deletions request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1644,6 +1644,8 @@ func TestTraceInfo(t *testing.T) {
ts := createGetServer(t)
defer ts.Close()

serverAddr := ts.URL[strings.LastIndex(ts.URL, "/")+1:]

client := dc()
client.SetHostURL(ts.URL).EnableTrace()
for _, u := range []string{"/", "/json", "/long-text", "/long-json"} {
Expand All @@ -1660,6 +1662,7 @@ func TestTraceInfo(t *testing.T) {
assertEqual(t, true, tr.TotalTime >= 0)
assertEqual(t, true, tr.TotalTime < time.Hour)
assertEqual(t, true, tr.TotalTime == resp.Time())
assertEqual(t, tr.RemoteAddr.String(), serverAddr)
}

client.DisableTrace()
Expand All @@ -1677,6 +1680,7 @@ func TestTraceInfo(t *testing.T) {
assertEqual(t, true, tr.ResponseTime >= 0)
assertEqual(t, true, tr.TotalTime >= 0)
assertEqual(t, true, tr.TotalTime == resp.Time())
assertEqual(t, tr.RemoteAddr.String(), serverAddr)
}

// for sake of hook funcs
Expand Down
2 changes: 1 addition & 1 deletion resty.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
)

// Version # of resty
const Version = "2.3.0"
const Version = "2.3.0-dev"

// New method creates a new Resty client.
func New() *Client {
Expand Down
5 changes: 4 additions & 1 deletion retry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ func TestClientRetryWaitCallbackSwitchToDefault(t *testing.T) {
}

c := dc().
EnableTrace().
SetRetryCount(retryCount).
SetRetryWaitTime(retryWaitTime).
SetRetryMaxWaitTime(retryMaxWaitTime).
Expand All @@ -456,10 +457,12 @@ func TestClientRetryWaitCallbackSwitchToDefault(t *testing.T) {
return true
},
)
_, _ = c.R().Get(ts.URL + "/set-retrywaittime-test")
resp, _ := c.R().Get(ts.URL + "/set-retrywaittime-test")

// 6 attempts were made
assertEqual(t, attempt, 6)
assertEqual(t, resp.Request.Attempt, 6)
assertEqual(t, resp.Request.TraceInfo().RequestAttempt, 6)

// Initial attempt has 0 time slept since last request
assertEqual(t, retryIntervals[0], uint64(0))
Expand Down
10 changes: 9 additions & 1 deletion trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package resty
import (
"context"
"crypto/tls"
"net"
"net/http/httptrace"
"time"
)
Expand Down Expand Up @@ -54,10 +55,17 @@ type TraceInfo struct {
// ConnIdleTime is a duration how long the connection was previously
// idle, if IsConnWasIdle is true.
ConnIdleTime time.Duration

// RequestAttempt is to represent the request attempt made during a Resty
// request execution flow, including retry count.
RequestAttempt int

// RemoteAddr returns the remote network address.
RemoteAddr net.Addr
}

//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
// CientTrace struct and its methods
// ClientTrace struct and its methods
//_______________________________________________________________________

// tracer struct maps the `httptrace.ClientTrace` hooks into Fields
Expand Down

0 comments on commit 608c8d7

Please sign in to comment.