Repository: go-resty/resty
Pull request #780 (2024-03-03 01:34:53)

Sync v2 branch into branch main


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6cbba59..0b95bed 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,12 +3,13 @@ name: CI
 on:
   push:
     branches:
-      - master
+      - v2
     paths-ignore:
       - '**.md'
   pull_request:
     branches:
-      - master
+      - main
+      - v2
     paths-ignore:
       - '**.md'
 
diff --git a/README.md b/README.md
index 294a288..c478a55 100644
--- a/README.md
+++ b/README.md
@@ -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.9.1-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.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>
 <p align="center">
 <h4 align="center">Resty Communication Channels</h4>
@@ -13,7 +13,7 @@
 
 ## News
 
-  * v2.9.1 [released](https://github.com/go-resty/resty/releases/tag/v2.9.1) and tagged on Sep 30, 2023.
+  * v2.11.0 [released](https://github.com/go-resty/resty/releases/tag/v2.11.0) and tagged on Dec 27, 2023.
   * 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).
@@ -110,7 +110,7 @@ Resty author also published following projects for Go Community.
 
 ```bash
 # Go Modules
-require github.com/go-resty/resty/v2 v2.7.0
+require github.com/go-resty/resty/v2 v2.11.0
 ```
 
 ## Usage
@@ -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")
diff --git a/digest.go b/digest.go
index ba099ad..9dd3a13 100644
--- a/digest.go
+++ b/digest.go
@@ -55,6 +55,17 @@ func (dt *digestTransport) RoundTrip(req *http.Request) (*http.Response, error)
 		req2.Header[k] = s
 	}
 
+	// Fix http: ContentLength=xxx with Body length 0
+	if req2.Body == nil {
+		req2.ContentLength = 0
+	} else if req2.GetBody != nil {
+		var err error
+		req2.Body, err = req2.GetBody()
+		if err != nil {
+			return nil, err
+		}
+	}
+
 	// Make a request to get the 401 that contains the challenge.
 	resp, err := dt.transport.RoundTrip(req)
 	if err != nil || resp.StatusCode != http.StatusUnauthorized {
@@ -122,14 +133,17 @@ func parseChallenge(input string) (*challenge, error) {
 		return nil, ErrDigestBadChallenge
 	}
 	s = strings.Trim(s[7:], ws)
-	sl := strings.Split(s, ", ")
+	sl := strings.Split(s, ",")
 	c := &challenge{}
 	var r []string
 	for i := range sl {
+		sl[i] = strings.TrimSpace(sl[i])
 		r = strings.SplitN(sl[i], "=", 2)
 		if len(r) != 2 {
 			return nil, ErrDigestBadChallenge
 		}
+		r[0] = strings.TrimSpace(r[0])
+		r[1] = strings.TrimSpace(r[1])
 		switch r[0] {
 		case "realm":
 			c.realm = strings.Trim(r[1], qs)
@@ -140,9 +154,9 @@ func parseChallenge(input string) (*challenge, error) {
 		case "opaque":
 			c.opaque = strings.Trim(r[1], qs)
 		case "stale":
-			c.stale = r[1]
+			c.stale = strings.Trim(r[1], qs)
 		case "algorithm":
-			c.algorithm = r[1]
+			c.algorithm = strings.Trim(r[1], qs)
 		case "qop":
 			c.qop = strings.Trim(r[1], qs)
 		case "charset":
diff --git a/go.mod b/go.mod
index f5c4112..aa7fd26 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,8 @@
-module github.com/go-resty/resty/v3
+module github.com/go-resty/resty/v2
 
-go 1.19
+go 1.16
 
 require (
-	golang.org/x/net v0.15.0
+	golang.org/x/net v0.17.0
 	golang.org/x/time v0.3.0
 )
diff --git a/go.sum b/go.sum
index 1d60843..e20bf02 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,42 @@
-golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
-golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
+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/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/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=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.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.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+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/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=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/middleware.go b/middleware.go
index 15cbc49..1fccbb7 100644
--- a/middleware.go
+++ b/middleware.go
@@ -58,7 +58,7 @@ func parseRequestURL(c *Client, r *Request) error {
 			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
+				// write everything from the previous position up to the current
 				if curr > prev {
 					buf.WriteString(r.URL[prev:curr])
 				}
@@ -310,13 +310,23 @@ func addCredentials(c *Client, r *Request) error {
 func requestLogger(c *Client, r *Request) error {
 	if r.Debug {
 		rr := r.RawRequest
-		rl := &RequestLog{Header: copyHeaders(rr.Header), Body: r.fmtBodyString(c.debugBodySizeLimit)}
+		rh := copyHeaders(rr.Header)
+		if c.GetClient().Jar != nil {
+			for _, cookie := range c.GetClient().Jar.Cookies(r.RawRequest.URL) {
+				s := fmt.Sprintf("%s=%s", cookie.Name, cookie.Value)
+				if c := rh.Get("Cookie"); c != "" {
+					rh.Set("Cookie", c+"; "+s)
+				} else {
+					rh.Set("Cookie", s)
+				}
+			}
+		}
+		rl := &RequestLog{Header: rh, Body: r.fmtBodyString(c.debugBodySizeLimit)}
 		if c.requestLog != nil {
 			if err := c.requestLog(rl); err != nil {
 				return err
 			}
 		}
-		// fmt.Sprintf("COOKIES:\n%s\n", composeCookies(c.GetClient().Jar, *rr.URL)) +
 
 		reqLog := "\n==============================================================================\n" +
 			"~~~ REQUEST ~~~\n" +
@@ -471,7 +481,6 @@ func handleContentType(c *Client, r *Request) {
 
 func handleRequestBody(c *Client, r *Request) error {
 	var bodyBytes []byte
-	releaseBuffer(r.bodyBuf)
 	r.bodyBuf = nil
 
 	switch body := r.Body.(type) {
diff --git a/request.go b/request.go
index fec0976..4e13ff0 100644
--- a/request.go
+++ b/request.go
@@ -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
 }
diff --git a/resty.go b/resty.go
index 8b3d48b..21dcd56 100644
--- a/resty.go
+++ b/resty.go
@@ -14,7 +14,7 @@ import (
 )
 
 // Version # of resty
-const Version = "3.0.0-dev"
+const Version = "2.10.0"
 
 // New method creates a new Resty client.
 func New() *Client {
diff --git a/util.go b/util.go
index 3279116..5a69e4f 100644
--- a/util.go
+++ b/util.go
@@ -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
 	}
@@ -339,25 +339,7 @@ func silently(_ ...interface{}) {}
 func composeHeaders(c *Client, r *Request, hdrs http.Header) string {
 	str := make([]string, 0, len(hdrs))
 	for _, k := range sortHeaderKeys(hdrs) {
-		var v string
-		if k == "Cookie" {
-			cv := strings.TrimSpace(strings.Join(hdrs[k], ", "))
-			if c.GetClient().Jar != nil {
-				for _, c := range c.GetClient().Jar.Cookies(r.RawRequest.URL) {
-					if cv != "" {
-						cv = cv + "; " + c.String()
-					} else {
-						cv = c.String()
-					}
-				}
-			}
-			v = strings.TrimSpace(fmt.Sprintf("%25s: %s", k, cv))
-		} else {
-			v = strings.TrimSpace(fmt.Sprintf("%25s: %s", k, strings.Join(hdrs[k], ", ")))
-		}
-		if v != "" {
-			str = append(str, "\t"+v)
-		}
+		str = append(str, "\t"+strings.TrimSpace(fmt.Sprintf("%25s: %s", k, strings.Join(hdrs[k], ", "))))
 	}
 	return strings.Join(str, "\n")
 }


Test patch:
diff --git a/client_test.go b/client_test.go
index ad02f62..1f291d1 100644
--- a/client_test.go
+++ b/client_test.go
@@ -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()
diff --git a/example_test.go b/example_test.go
index d7d91a1..7608f1d 100644
--- a/example_test.go
+++ b/example_test.go
@@ -15,7 +15,7 @@ import (
 
 	"golang.org/x/net/proxy"
 
-	"github.com/go-resty/resty/v3"
+	"github.com/go-resty/resty/v2"
 )
 
 type DropboxError struct {
diff --git a/middleware_test.go b/middleware_test.go
index f4e5c69..f5924cb 100644
--- a/middleware_test.go
+++ b/middleware_test.go
@@ -756,7 +756,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,7 +776,7 @@ 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")).
@@ -784,7 +784,7 @@ func Test_parseRequestBody(t *testing.T) {
 			},
 			expectedBodyBuf:       []byte(`{"bar":"2","foo":"1"}`),
 			expectedContentType:   "multipart/form-data; boundary=",
-			expectedContentLength: "412",
+			expectedContentLength: "414",
 		},
 		{
 			name: "body with errorReader",
diff --git a/request_test.go b/request_test.go
index fd1222c..7312128 100644
--- a/request_test.go
+++ b/request_test.go
@@ -722,6 +722,25 @@ func TestRequestDigestAuthFail(t *testing.T) {
 	logResponse(t, resp)
 }
 
+func TestRequestDigestAuthWithBody(t *testing.T) {
+	conf := defaultDigestServerConf()
+	ts := createDigestServer(t, nil)
+	defer ts.Close()
+
+	resp, err := dclr().
+		SetDigestAuth(conf.username, conf.password).
+		SetResult(&AuthSuccess{}).
+		SetHeader(hdrContentTypeKey, "application/json").
+		SetBody(map[string]interface{}{"zip_code": "00000", "city": "Los Angeles"}).
+		Post(ts.URL + conf.uri)
+
+	assertError(t, err)
+	assertEqual(t, http.StatusOK, resp.StatusCode())
+
+	t.Logf("Result Success: %q", resp.Result().(*AuthSuccess))
+	logResponse(t, resp)
+}
+
 func TestFormData(t *testing.T) {
 	ts := createFormPostServer(t)
 	defer ts.Close()