Repository: go-resty/resty (go)

Issue:
Warn-level logging on retries attempts
Current implementation of (r *Request) Execute(method, url string) (*Response, error) uses Backoff function with hardcoded attempts errors log on Error level:

`r.client.log.Errorf("%v, Attempt %v", err, r.Attempt)`

With enabled retry feature it may be unnecessary for someone to log unsuccessful attempts on Error level. Desired approach would be to log attempts errors on Warning level and only log on Error level if all retries are exceeded with no success.

Can it be achieved with current versions of resty?

Can this behavior be configurable? Maybe even add custom callbacks to handle retry errors?

Pull request:
Update log level to warning for errors within retry block
Closes #561
Closes #554

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9595781..52cbefb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -28,11 +28,15 @@ jobs:
     steps:
       - name: Checkout
         uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
 
       - name: Setup Go
-        uses: actions/setup-go@v2
+        uses: actions/setup-go@v3
         with:
           go-version: ${{ matrix.go }}
+          cache: true
+          cache-dependency-path: go.sum
 
       - name: Format
         run: diff -u <(echo -n) <(go fmt $(go list ./...))
diff --git a/README.md b/README.md
index d88c426..54b50e7 100644
--- a/README.md
+++ b/README.md
@@ -637,7 +637,7 @@ client.SetCertificates(cert1, cert2, cert3)
 
 ```go
 // Custom Root certificates from string
-// You can pass you certificates throught env variables as strings
+// You can pass you certificates through env variables as strings
 // you can add one or more root certificates, its get appended
 client.SetRootCertificateFromString("-----BEGIN CERTIFICATE-----content-----END CERTIFICATE-----")
 client.SetRootCertificateFromString("-----BEGIN CERTIFICATE-----content-----END CERTIFICATE-----")
@@ -656,7 +656,7 @@ if err != nil {
 client.SetCertificates(cert1, cert2, cert3)
 ```
 
-#### Proxy Settings - Client as well as at Request Level
+#### Proxy Settings
 
 Default `Go` supports Proxy via environment variable `HTTP_PROXY`. Resty provides support via `SetProxy` & `RemoveProxy`.
 Choose as per your need.
@@ -702,8 +702,9 @@ client.
     })
 ```
 
-Above setup will result in resty retrying requests returned non nil error up to
-3 times with delay increased after each attempt.
+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):
 
@@ -720,10 +721,26 @@ client.AddRetryCondition(
 )
 ```
 
-Above example will make resty retry requests ended with `429 Too Many Requests`
-status code.
+The above example will make resty retry requests that end with a `429 Too Many Requests` status code.
+It's important to note that when you specify conditions using `AddRetryCondition`,
+it will override the default retry behavior, which retries on errors encountered during the request.
+If you want to retry on errors encountered during the request, similar to the default behavior,
+you'll need to configure it as follows:
 
-Multiple retry conditions can be added.
+```go
+// Create a Resty Client
+client := resty.New()
+
+client.AddRetryCondition(
+    func(r *resty.Response, err error) bool {
+        // Including "err != nil" emulates the default retry behavior for errors encountered during the request.
+        return err != nil || r.StatusCode() == http.StatusTooManyRequests
+    },
+)
+```
+
+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
 implemented. [Reference](retry_test.go).
diff --git a/client.go b/client.go
index 6f53ef7..d72c5ce 100644
--- a/client.go
+++ b/client.go
@@ -103,6 +103,7 @@ type Client struct {
 	QueryParam            url.Values
 	FormData              url.Values
 	PathParams            map[string]string
+	RawPathParams         map[string]string
 	Header                http.Header
 	UserInfo              *User
 	Token                 string
@@ -441,6 +442,7 @@ func (c *Client) R() *Request {
 		multipartFiles:  []*File{},
 		multipartFields: []*MultipartField{},
 		PathParams:      map[string]string{},
+		RawPathParams:   map[string]string{},
 		jsonEscapeHTML:  true,
 		log:             c.log,
 	}
@@ -964,6 +966,7 @@ func (c *Client) SetDoNotParseResponse(parse bool) *Client {
 //	   Composed URL - /v1/users/sample@sample.com/details
 //
 // It replaces the value of the key while composing the request URL.
+// The value will be escaped using `url.PathEscape` function.
 //
 // Also it can be overridden at request level Path Params options,
 // see `Request.SetPathParam` or `Request.SetPathParams`.
@@ -976,15 +979,17 @@ func (c *Client) SetPathParam(param, value string) *Client {
 // Resty client instance.
 //
 //	client.SetPathParams(map[string]string{
-//	   "userId": "sample@sample.com",
-//	   "subAccountId": "100002",
+//		"userId":       "sample@sample.com",
+//		"subAccountId": "100002",
+//		"path":         "groups/developers",
 //	})
 //
 //	Result:
-//	   URL - /v1/users/{userId}/{subAccountId}/details
-//	   Composed URL - /v1/users/sample@sample.com/100002/details
+//	   URL - /v1/users/{userId}/{subAccountId}/{path}/details
+//	   Composed URL - /v1/users/sample@sample.com/100002/groups%2Fdevelopers/details
 //
 // It replaces the value of the key while composing the request URL.
+// The values will be escaped using `url.PathEscape` function.
 //
 // Also it can be overridden at request level Path Params options,
 // see `Request.SetPathParam` or `Request.SetPathParams`.
@@ -995,6 +1000,56 @@ func (c *Client) SetPathParams(params map[string]string) *Client {
 	return c
 }
 
+// SetRawPathParam method sets single URL path key-value pair in the
+// Resty client instance.
+//
+//	client.SetPathParam("userId", "sample@sample.com")
+//
+//	Result:
+//	   URL - /v1/users/{userId}/details
+//	   Composed URL - /v1/users/sample@sample.com/details
+//
+//	client.SetPathParam("path", "groups/developers")
+//
+//	Result:
+//	   URL - /v1/users/{userId}/details
+//	   Composed URL - /v1/users/groups%2Fdevelopers/details
+//
+// It replaces the value of the key while composing the request URL.
+// The value will be used as it is and will not be escaped.
+//
+// Also it can be overridden at request level Path Params options,
+// see `Request.SetPathParam` or `Request.SetPathParams`.
+func (c *Client) SetRawPathParam(param, value string) *Client {
+	c.RawPathParams[param] = value
+	return c
+}
+
+// SetRawPathParams method sets multiple URL path key-value pairs at one go in the
+// Resty client instance.
+//
+//	client.SetPathParams(map[string]string{
+//		"userId":       "sample@sample.com",
+//		"subAccountId": "100002",
+//		"path":         "groups/developers",
+//	})
+//
+//	Result:
+//	   URL - /v1/users/{userId}/{subAccountId}/{path}/details
+//	   Composed URL - /v1/users/sample@sample.com/100002/groups/developers/details
+//
+// It replaces the value of the key while composing the request URL.
+// The values will be used as they are and will not be escaped.
+//
+// Also it can be overridden at request level Path Params options,
+// see `Request.SetPathParam` or `Request.SetPathParams`.
+func (c *Client) SetRawPathParams(params map[string]string) *Client {
+	for p, v := range params {
+		c.SetRawPathParam(p, v)
+	}
+	return c
+}
+
 // SetJSONEscapeHTML method is to enable/disable the HTML escape on JSON marshal.
 //
 // Note: This option only applicable to standard JSON Marshaller.
@@ -1257,6 +1312,7 @@ func createClient(hc *http.Client) *Client {
 		RetryWaitTime:          defaultWaitTime,
 		RetryMaxWaitTime:       defaultMaxWaitTime,
 		PathParams:             make(map[string]string),
+		RawPathParams:          make(map[string]string),
 		JSONMarshal:            json.Marshal,
 		JSONUnmarshal:          json.Unmarshal,
 		XMLMarshal:             xml.Marshal,
diff --git a/go.mod b/go.mod
index 909a1c4..148e7ad 100644
--- a/go.mod
+++ b/go.mod
@@ -1,5 +1,5 @@
 module github.com/go-resty/resty/v2
 
-require golang.org/x/net v0.8.0
-
 go 1.16
+
+require golang.org/x/net v0.10.0
diff --git a/go.sum b/go.sum
index 49f5966..798b6bc 100644
--- a/go.sum
+++ b/go.sum
@@ -7,8 +7,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 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.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 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=
@@ -18,16 +18,16 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 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.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
 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.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 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=
diff --git a/middleware.go b/middleware.go
index 4ffb9db..3293854 100644
--- a/middleware.go
+++ b/middleware.go
@@ -38,6 +38,18 @@ func parseRequestURL(c *Client, r *Request) error {
 		}
 	}
 
+	// GitHub #663 Raw Path Params
+	if len(r.RawPathParams) > 0 {
+		for p, v := range r.RawPathParams {
+			r.URL = strings.Replace(r.URL, "{"+p+"}", v, -1)
+		}
+	}
+	if len(c.RawPathParams) > 0 {
+		for p, v := range c.RawPathParams {
+			r.URL = strings.Replace(r.URL, "{"+p+"}", v, -1)
+		}
+	}
+
 	// Parsing request URL
 	reqURL, err := url.Parse(r.URL)
 	if err != nil {
@@ -163,7 +175,9 @@ CL:
 
 func createHTTPRequest(c *Client, r *Request) (err error) {
 	if r.bodyBuf == nil {
-		if c.setContentLength || r.setContentLength {
+		if reader, ok := r.Body.(io.Reader); ok && isPayloadSupported(r.Method, c.AllowGetMethodPayload) {
+			r.RawRequest, err = http.NewRequest(r.Method, r.URL, reader)
+		} else if c.setContentLength || r.setContentLength {
 			r.RawRequest, err = http.NewRequest(r.Method, r.URL, http.NoBody)
 		} else {
 			r.RawRequest, err = http.NewRequest(r.Method, r.URL, nil)
@@ -441,9 +455,14 @@ func handleRequestBody(c *Client, r *Request) (err error) {
 	r.bodyBuf = nil
 
 	if reader, ok := r.Body.(io.Reader); ok {
-		r.bodyBuf = acquireBuffer()
-		_, err = r.bodyBuf.ReadFrom(reader)
-		r.Body = nil
+		if c.setContentLength || r.setContentLength { // keep backward compatibility
+			r.bodyBuf = acquireBuffer()
+			_, err = r.bodyBuf.ReadFrom(reader)
+			r.Body = nil
+		} else {
+			// Otherwise buffer less processing for `io.Reader`, sounds good.
+			return
+		}
 	} else if b, ok := r.Body.([]byte); ok {
 		bodyBytes = b
 	} else if s, ok := r.Body.(string); ok {
diff --git a/request.go b/request.go
index cd4e77c..fed6d4f 100644
--- a/request.go
+++ b/request.go
@@ -27,22 +27,23 @@ import (
 // resty client. Request provides an options to override client level
 // settings and also an options for the request composition.
 type Request struct {
-	URL        string
-	Method     string
-	Token      string
-	AuthScheme string
-	QueryParam url.Values
-	FormData   url.Values
-	PathParams map[string]string
-	Header     http.Header
-	Time       time.Time
-	Body       interface{}
-	Result     interface{}
-	Error      interface{}
-	RawRequest *http.Request
-	SRV        *SRVRecord
-	UserInfo   *User
-	Cookies    []*http.Cookie
+	URL           string
+	Method        string
+	Token         string
+	AuthScheme    string
+	QueryParam    url.Values
+	FormData      url.Values
+	PathParams    map[string]string
+	RawPathParams map[string]string
+	Header        http.Header
+	Time          time.Time
+	Body          interface{}
+	Result        interface{}
+	Error         interface{}
+	RawRequest    *http.Request
+	SRV           *SRVRecord
+	UserInfo      *User
+	Cookies       []*http.Cookie
 
 	// Attempt is to represent the request attempt made during a Resty
 	// request execution flow, including retry count.
@@ -578,8 +579,17 @@ func (r *Request) SetDoNotParseResponse(parse bool) *Request {
 //	   URL - /v1/users/{userId}/details
 //	   Composed URL - /v1/users/sample@sample.com/details
 //
-// It replaces the value of the key while composing the request URL. Also you can
-// override Path Params value, which was set at client instance level.
+//	client.R().SetPathParam("path", "groups/developers")
+//
+//	Result:
+//	   URL - /v1/users/{userId}/details
+//	   Composed URL - /v1/users/groups%2Fdevelopers/details
+//
+// It replaces the value of the key while composing the request URL.
+// The values will be escaped using `url.PathEscape` function.
+//
+// Also you can override Path Params value, which was set at client instance
+// level.
 func (r *Request) SetPathParam(param, value string) *Request {
 	r.PathParams[param] = value
 	return r
@@ -589,16 +599,20 @@ func (r *Request) SetPathParam(param, value string) *Request {
 // Resty current request instance.
 //
 //	client.R().SetPathParams(map[string]string{
-//	   "userId": "sample@sample.com",
-//	   "subAccountId": "100002",
+//		"userId":       "sample@sample.com",
+//		"subAccountId": "100002",
+//		"path":         "groups/developers",
 //	})
 //
 //	Result:
-//	   URL - /v1/users/{userId}/{subAccountId}/details
-//	   Composed URL - /v1/users/sample@sample.com/100002/details
+//	   URL - /v1/users/{userId}/{subAccountId}/{path}/details
+//	   Composed URL - /v1/users/sample@sample.com/100002/groups%2Fdevelopers/details
 //
-// It replaces the value of the key while composing request URL. Also you can
-// override Path Params value, which was set at client instance level.
+// It replaces the value of the key while composing request URL.
+// The value will be used as it is and will not be escaped.
+//
+// Also you can override Path Params value, which was set at client instance
+// level.
 func (r *Request) SetPathParams(params map[string]string) *Request {
 	for p, v := range params {
 		r.SetPathParam(p, v)
@@ -606,6 +620,56 @@ func (r *Request) SetPathParams(params map[string]string) *Request {
 	return r
 }
 
+// SetRawPathParam method sets single URL path key-value pair in the
+// Resty current request instance.
+//
+//	client.R().SetPathParam("userId", "sample@sample.com")
+//
+//	Result:
+//	   URL - /v1/users/{userId}/details
+//	   Composed URL - /v1/users/sample@sample.com/details
+//
+//	client.R().SetPathParam("path", "groups/developers")
+//
+//	Result:
+//	   URL - /v1/users/{userId}/details
+//	   Composed URL - /v1/users/groups/developers/details
+//
+// It replaces the value of the key while composing the request URL.
+// The value will be used as it is and will not be escaped.
+//
+// Also you can override Path Params value, which was set at client instance
+// level.
+func (r *Request) SetRawPathParam(param, value string) *Request {
+	r.RawPathParams[param] = value
+	return r
+}
+
+// SetRawPathParams method sets multiple URL path key-value pairs at one go in the
+// Resty current request instance.
+//
+//	client.R().SetPathParams(map[string]string{
+//		"userId": "sample@sample.com",
+//		"subAccountId": "100002",
+//		"path":         "groups/developers",
+//	})
+//
+//	Result:
+//	   URL - /v1/users/{userId}/{subAccountId}/{path}/details
+//	   Composed URL - /v1/users/sample@sample.com/100002/groups/developers/details
+//
+// It replaces the value of the key while composing request URL.
+// The values will be used as they are and will not be escaped.
+//
+// Also you can override Path Params value, which was set at client instance
+// level.
+func (r *Request) SetRawPathParams(params map[string]string) *Request {
+	for p, v := range params {
+		r.SetRawPathParam(p, v)
+	}
+	return r
+}
+
 // ExpectContentType method allows to provide fallback `Content-Type` for automatic unmarshalling
 // when `Content-Type` response header is unavailable.
 func (r *Request) ExpectContentType(contentType string) *Request {
@@ -866,7 +930,7 @@ func (r *Request) Execute(method, url string) (*Response, error) {
 
 			resp, err = r.client.execute(r)
 			if err != nil {
-				r.log.Errorf("%v, Attempt %v", err, r.Attempt)
+				r.log.Warnf("%v, Attempt %v", err, r.Attempt)
 			}
 
 			return resp, err
@@ -879,6 +943,10 @@ func (r *Request) Execute(method, url string) (*Response, error) {
 		ResetMultipartReaders(r.client.RetryResetReaders),
 	)
 
+	if err != nil {
+		r.log.Errorf("%v", err)
+	}
+
 	r.client.onErrorHooks(r, resp, unwrapNoRetryErr(err))
 	return resp, unwrapNoRetryErr(err)
 }


Test patch:
diff --git a/client_test.go b/client_test.go
index 72dfd08..966e029 100644
--- a/client_test.go
+++ b/client_test.go
@@ -543,6 +543,22 @@ func TestClientAllowsGetMethodPayload(t *testing.T) {
 	assertEqual(t, payload, resp.String())
 }
 
+func TestClientAllowsGetMethodPayloadIoReader(t *testing.T) {
+	ts := createGetServer(t)
+	defer ts.Close()
+
+	c := dc()
+	c.SetAllowGetMethodPayload(true)
+
+	payload := "test-payload"
+	body := bytes.NewReader([]byte(payload))
+	resp, err := c.R().SetBody(body).Get(ts.URL + "/get-method-payload-test")
+
+	assertError(t, err)
+	assertEqual(t, http.StatusOK, resp.StatusCode())
+	assertEqual(t, payload, resp.String())
+}
+
 func TestClientAllowsGetMethodPayloadDisabled(t *testing.T) {
 	ts := createGetServer(t)
 	defer ts.Close()
@@ -836,6 +852,7 @@ func TestClientOnResponseError(t *testing.T) {
 
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
+			t.Parallel()
 			var assertErrorHook = func(r *Request, err error) {
 				assertNotNil(t, r)
 				v, ok := err.(*ResponseError)
diff --git a/request_test.go b/request_test.go
index 59809b4..2c06cae 100644
--- a/request_test.go
+++ b/request_test.go
@@ -1636,7 +1636,7 @@ func TestGetPathParamAndPathParams(t *testing.T) {
 	defer ts.Close()
 
 	c := dc().
-		SetHostURL(ts.URL).
+		SetBaseURL(ts.URL).
 		SetPathParam("userId", "sample@sample.com")
 
 	resp, err := c.R().SetPathParam("subAccountId", "100002").
@@ -1749,21 +1749,47 @@ func TestPathParamURLInput(t *testing.T) {
 	defer ts.Close()
 
 	c := dc().SetDebug(true).
-		SetHostURL(ts.URL).
+		SetBaseURL(ts.URL).
 		SetPathParams(map[string]string{
 			"userId": "sample@sample.com",
+			"path":   "users/developers",
 		})
 
 	resp, err := c.R().
 		SetPathParams(map[string]string{
 			"subAccountId": "100002",
 			"website":      "https://example.com",
-		}).Get("/v1/users/{userId}/{subAccountId}/{website}")
+		}).Get("/v1/users/{userId}/{subAccountId}/{path}/{website}")
 
 	assertError(t, err)
 	assertEqual(t, http.StatusOK, resp.StatusCode())
 	assertEqual(t, true, strings.Contains(resp.String(), "TestPathParamURLInput: text response"))
-	assertEqual(t, true, strings.Contains(resp.String(), "/v1/users/sample@sample.com/100002/https:%2F%2Fexample.com"))
+	assertEqual(t, true, strings.Contains(resp.String(), "/v1/users/sample@sample.com/100002/users%2Fdevelopers/https:%2F%2Fexample.com"))
+
+	logResponse(t, resp)
+}
+
+func TestRawPathParamURLInput(t *testing.T) {
+	ts := createGetServer(t)
+	defer ts.Close()
+
+	c := dc().SetDebug(true).
+		SetBaseURL(ts.URL).
+		SetRawPathParams(map[string]string{
+			"userId": "sample@sample.com",
+			"path":   "users/developers",
+		})
+
+	resp, err := c.R().
+		SetRawPathParams(map[string]string{
+			"subAccountId": "100002",
+			"website":      "https://example.com",
+		}).Get("/v1/users/{userId}/{subAccountId}/{path}/{website}")
+
+	assertError(t, err)
+	assertEqual(t, http.StatusOK, resp.StatusCode())
+	assertEqual(t, true, strings.Contains(resp.String(), "TestPathParamURLInput: text response"))
+	assertEqual(t, true, strings.Contains(resp.String(), "/v1/users/sample@sample.com/100002/users/developers/https://example.com"))
 
 	logResponse(t, resp)
 }