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

New feature: generate curl cmd for request && some examples that can be tested #728

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
5 changes: 4 additions & 1 deletion .gitignore
Expand Up @@ -26,5 +26,8 @@ _testmain.go
coverage.out
coverage.txt

# Exclude intellij IDE folders
# Exclude IDE folders
.idea/*
.vscode/*
__debug_bin
.DS_Store
5 changes: 5 additions & 0 deletions README.md
Expand Up @@ -126,12 +126,17 @@ import "github.com/go-resty/resty/v2"

```go
// Create a Resty Client
var curlCmdExecuted string
client := resty.New()

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

// Explore curl command
fmt.Println("Curl Command:", curlCmdExecuted)

// Explore response object
fmt.Println("Response Info:")
fmt.Println(" Error :", err)
Expand Down
24 changes: 16 additions & 8 deletions client.go
Expand Up @@ -1129,9 +1129,7 @@ func (c *Client) GetClient() *http.Client {
// Client Unexported methods
//_______________________________________________________________________

// Executes method executes the given `Request` object and returns response
// error.
func (c *Client) execute(req *Request) (*Response, error) {
func (c *Client) executeBefore(req *Request) (error) {
// Lock the user-defined pre-request hooks.
c.udBeforeRequestLock.RLock()
defer c.udBeforeRequestLock.RUnlock()
Expand All @@ -1147,22 +1145,22 @@ func (c *Client) execute(req *Request) (*Response, error) {
// to modify the *resty.Request object
for _, f := range c.udBeforeRequest {
if err = f(c, req); err != nil {
return nil, wrapNoRetryErr(err)
return wrapNoRetryErr(err)
}
}

// If there is a rate limiter set for this client, the Execute call
// will return an error if the rate limit is exceeded.
if req.client.rateLimiter != nil {
if !req.client.rateLimiter.Allow() {
return nil, wrapNoRetryErr(ErrRateLimitExceeded)
return wrapNoRetryErr(ErrRateLimitExceeded)
}
}

// resty middlewares
for _, f := range c.beforeRequest {
if err = f(c, req); err != nil {
return nil, wrapNoRetryErr(err)
return wrapNoRetryErr(err)
}
}

Expand All @@ -1173,15 +1171,24 @@ func (c *Client) execute(req *Request) (*Response, error) {
// call pre-request if defined
if c.preReqHook != nil {
if err = c.preReqHook(c, req.RawRequest); err != nil {
return nil, wrapNoRetryErr(err)
return wrapNoRetryErr(err)
}
}

if err = requestLogger(c, req); err != nil {
return nil, wrapNoRetryErr(err)
return wrapNoRetryErr(err)
}

req.RawRequest.Body = newRequestBodyReleaser(req.RawRequest.Body, req.bodyBuf)
return nil
}

// Executes method executes the given `Request` object and returns response
// error.
func (c *Client) execute(req *Request) (*Response, error) {
if err:= c.executeBefore(req);err!=nil{
return nil, err
}

req.Time = time.Now()
resp, err := c.httpClient.Do(req.RawRequest)
Expand Down Expand Up @@ -1375,6 +1382,7 @@ func createClient(hc *http.Client) *Client {
parseRequestBody,
createHTTPRequest,
addCredentials,
createCurlCmd,
}

// user defined request middlewares
Expand Down
19 changes: 19 additions & 0 deletions conf/nginx.crt
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDIjCCAgqgAwIBAgIUPkbRM1znk+3fLI8g5eB5i8Ie3K0wDQYJKoZIhvcNAQEL
BQAwFTETMBEGA1UEAwwKbG9jYWwuc2VsZjAeFw0yMzAzMjgwMzQ1MDlaFw0yNDAz
MjcwMzQ1MDlaMBUxEzARBgNVBAMMCmxvY2FsLnNlbGYwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDIdnZmgBgAGOChpZGwikpQgTRTkqFNKQV5jRQXr7r7
3xV+PilkDShz+UWfYG3zszLX7L9jllIDj1V5YALV+f3tjDaPnptSUnNIKIdjripQ
ojX5639oCmHA4ZnYbDxx8GcNlfOLGW4oAVuY9PHaL69nmrHq58wQX2VPR6jCjzCY
T0putkMCRbCTbzeb0ntcEHNKrVuk8TlLxGnNNXyeF1BS6YHa9/3PwXmwsApZ1fY2
KepHclqVISz+E0Cj4KkVvGI++9KBBDcNMEyerhU0ocotXoqQxQKm+eeED44LKnnh
czPION+xRTfsqn+j8AyrShY+JkBt+VseOlOeNpn25p5dAgMBAAGjajBoMB0GA1Ud
DgQWBBQ8aBcGhCLZzEGztBxgChe+zIY88TAfBgNVHSMEGDAWgBQ8aBcGhCLZzEGz
tBxgChe+zIY88TAPBgNVHRMBAf8EBTADAQH/MBUGA1UdEQQOMAyCCmxvY2FsLnNl
bGYwDQYJKoZIhvcNAQELBQADggEBAHBKNQHNBlRdI6cICeEkBYpoJRg1UBCEpxPo
A7He5EN1vZReJcMoFoc86tPsvUaIwsgqiEu6S0sQNahJHKF0FwcB+A6F9kQmW7si
CrQw9hHneooEmYs2CldNV4w51HWKNDZ5Ra+gH5B2AKR1EWFDh4MMzPlL2MPgasFC
OeAOFrftD8hLLSvCvDsIall/pOg6wTP5vimndsjw+fPk7/SRAqZZzM1EQ4WZ7uZq
oRXdSOhtfyEZYsC04VQNT0KAD+m73ctYXn4EHRpHx5tA1ZkKuuOLOXSVYmHRkk72
ReutF5Khp+XIjc77fHX6KRCWxT/KE23a4aEmmJzKhV3A1bcvpQ4=
-----END CERTIFICATE-----
28 changes: 28 additions & 0 deletions conf/nginx.key
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDIdnZmgBgAGOCh
pZGwikpQgTRTkqFNKQV5jRQXr7r73xV+PilkDShz+UWfYG3zszLX7L9jllIDj1V5
YALV+f3tjDaPnptSUnNIKIdjripQojX5639oCmHA4ZnYbDxx8GcNlfOLGW4oAVuY
9PHaL69nmrHq58wQX2VPR6jCjzCYT0putkMCRbCTbzeb0ntcEHNKrVuk8TlLxGnN
NXyeF1BS6YHa9/3PwXmwsApZ1fY2KepHclqVISz+E0Cj4KkVvGI++9KBBDcNMEye
rhU0ocotXoqQxQKm+eeED44LKnnhczPION+xRTfsqn+j8AyrShY+JkBt+VseOlOe
Npn25p5dAgMBAAECggEAYB1AdrPbDgzfg1Gt0V309LWGX72xvhu5hsaoSBUXS5Vy
KjXmxZRzfWuawKhM/6g/a/0U5MxZpWBPhKMOqQa0g/WS4XiUEzv6DzJze80xd7jW
tW3/B+TWatMgXv7CWoT//CkV/O8j/GVuRB/JaCot6ThoLu+VzZHXstPya9qY26Vr
Evc6NF+MED/n2ciagBAJzSEG0L8Ry948Vbzz3yEu1p9Mdg31AVWu6/m4LfMWB7km
rtpSiPdZwqVCsTjGRb2T4hF3TmMgBC1+0meDMxd33xws1XaOoGwaqwdgMpIdowTK
DBonkT10De6cqjtVg/IqeZvmCZl4al4lkSzfGtwGRwKBgQDyx0kQw8D6PpMQ4YSk
cFgVgEwJ2ED2yVlOqnOpw+cFG+k9AlS9VuLOYDyuxsLCX4VdbGitmBnd70E3Zpnj
V74Tqu+nnUr/i8BDl8XG+EceBW+xpMgVIaMnKHbWa3RcIKU4CALbtDL0sl8l96Ch
NxjNje3E/E9hmAeFSBr6zsqPEwKBgQDTYTqzGMeBk7oNE0YonR8UxX1Ah5tSU9Eu
usY1vYGv3FoeJR1RexN7+o4rFxVN+cVy3Mwpml1ptPRZP1V3NGZCCObOaOU4Pplt
HjzhClBTeIsbmZ1Bq/MoRaXp4yykPkHaniR+d7/GjwnSrIzyupsWAe2fPfeIx1/K
VFg1eLcazwKBgQCuv/16wLy2I7ZuDzYPuwHcMCYLbAqO2K2c7xokF2vBhK6lCHmA
c/r7e9ASKeVkTadMcM0ELxhnZGD4BLU+LBkYRREOAC1MtgYlYSiuKGXgWR9lqeuP
MlAizoCDpIL0EVd7dmDATfvjoETWqmCHyoXi54c/JDHrWKgJKrao05J/2wKBgDxu
H4n0G5U/1oDGcdhKkwgtLZG1MwJmU/c4DlJuyxSrulfD5I3W0csv5lULVFvmfDxK
Q1PhfbMquHCLWrOpl1JpmRKJin555wL7EgyEFlLGs35AfGS589ofjz8+YxTRd6I4
c9Z0Ba+OVRCVo/YAwzWXd4d+/7VqykfdtRoUWMShAoGAVamX3xdVKVUH1vkbcW47
MZMvli5qeWFKkjNYzAIkHCOKgP+LcOhj2yywSliXZjBP3/AM4IyTf85rL7WWUHs9
M9V6iy4s0v95+NQ3gBhU8dqGZMlMAfHTdOYP5QZWKQQfF0iGHDH6faEsdBBWiqhI
laxSnUjUN5VMS/ViBExyBQs=
-----END PRIVATE KEY-----
32 changes: 32 additions & 0 deletions examples/auth_test.go
@@ -0,0 +1,32 @@
package examples

import (
"strings"
"testing"

"github.com/go-resty/resty/v3"
)

// Example about sending Authentication header
func TestAuth(t *testing.T) {
var curlCmdExecuted string
ts := createEchoServer()
defer ts.Close()
// Test authentication usernae and password
client := resty.New()
resp, err := client.R().
SetBasicAuth("USER", "PASSWORD").
SetResultCurlCmd(&curlCmdExecuted).
Get( ts.URL+"/echo",)
if err != nil {
t.Fatal(err)
}

if !strings.Contains(curlCmdExecuted, "Authorization: Basic ") {
t.Fatal("bad curl:", curlCmdExecuted)
}
if !strings.Contains(string(resp.Body()), "Authorization: Basic ") {
t.Fatal("bad auth body:\n" + resp.String())
}
t.Log(curlCmdExecuted)
}
120 changes: 120 additions & 0 deletions examples/context_test.go
@@ -0,0 +1,120 @@
package examples

import (
"context"
"net/http"
"net/http/httptrace"
"testing"
"time"

"github.com/go-resty/resty/v3"
)

// Example about cancel request with context
func TestSetContextCancelMulti(t *testing.T) {
// 0. Init test server
ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Microsecond)
n, err := w.Write([]byte("TestSetContextCancel: response"))
t.Logf("%s Server: wrote %d bytes", time.Now(), n)
t.Logf("%s Server: err is %v ", time.Now(), err)
}, 0)
defer ts.Close()

// 1. Create client
ctx, cancel := context.WithCancel(context.Background())
client := resty.New().R().SetContext(ctx)
go func() {
time.Sleep(1 * time.Microsecond)
cancel()
}()

// 2. First request
_, err := client.Get(ts.URL + "/get")
if !errIsContextCancel(err) {
t.Fatalf("Got unexpected error: %v", err)
}

// 3. Second request
_, err = client.Get(ts.URL + "/get")
if !errIsContextCancel(err) {
t.Fatalf("Got unexpected error: %v", err)
}
}

// Test context: cancel with chan
func TestSetContextCancelWithChan(t *testing.T) {
ch := make(chan struct{})
ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
defer func() {
ch <- struct{}{} // tell test request is finished
}()
t.Logf("%s Server: %v %v", time.Now(), r.Method, r.URL.Path)
ch <- struct{}{} // tell test request is canceld
t.Logf("%s Server: call canceld", time.Now())

<-ch // wait for client to finish request
n, err := w.Write([]byte("TestSetContextCancel: response"))
// FIXME? test server doesn't handle request cancellation
t.Logf("%s Server: wrote %d bytes", time.Now(), n)
t.Logf("%s Server: err is %v ", time.Now(), err)

}, 0)
defer ts.Close()

ctx, cancel := context.WithCancel(context.Background())
go func() {
<-ch // wait for server to start request handling
cancel()
}()

_, err := resty.New().R().SetContext(ctx).Get(ts.URL + "/get")
t.Logf("%s:client:is canceled", time.Now())

ch <- struct{}{} // tell server to continue request handling
t.Logf("%s:client:tell server to continue", time.Now())

<-ch // wait for server to finish request handling

if !errIsContextCancel(err) {
t.Fatalf("Got unexpected error: %v", err)
}
}

// test with trace context
func TestContextWithTrace(t *testing.T) {
ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("TestSetContextWithTrace: response"))
}, 0)
defer ts.Close()

//1. Create Trace context
traceInfo := struct {
dnsDone time.Time
connectDone time.Time
}{}

trace := &httptrace.ClientTrace{
ConnectStart: func(network, addr string) {
traceInfo.dnsDone = time.Now()
t.Log(time.Now(), "ConnectStart:", "network=", network, ",addr=", addr)
},
ConnectDone: func(network, addr string, err error) {
traceInfo.connectDone = time.Now()
t.Log(time.Now(), "ConnectDone:", "network=", network, ",addr=", addr)
},
}
ctx := httptrace.WithClientTrace(context.Background(), trace)

//2. Send request with Trace context
session := resty.New().R().SetContext(ctx)
params := MapString{"name": "ahuigo", "page": "1"}
_, err := session.SetQueryParams(params).Get(ts.URL+"/get")
if err != nil {
t.Fatal(err)
}
if traceInfo.connectDone.Sub(traceInfo.dnsDone) <= 0 {
t.Fatal("Bad trace info")
}

}