From eb3bafb1d2d95e31e99d3b39eabf84ad4e799bcb Mon Sep 17 00:00:00 2001 From: Jeevanandam M Date: Wed, 16 Aug 2017 21:18:41 -0700 Subject: [PATCH] Added option to expose response body without parsing response #87 (#88) --- .travis.yml | 6 +++--- client.go | 24 ++++++++++++++++++++++-- default.go | 6 ++++++ request.go | 11 +++++++++++ request16.go | 1 + request17.go | 1 + response.go | 13 +++++++++++++ resty.go | 2 +- resty_test.go | 43 ++++++++++++++++++++++++++++++++++++++++++- 9 files changed, 100 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8972c4cb..058c553b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,9 +16,9 @@ go: - 1.8 - tip -install: - - go get -v -t ./... - - go get -v golang.org/x/tools/cmd/cover +# install: +# - go get -v -t ./... +# - go get -v golang.org/x/tools/cmd/cover script: - go test ./... -coverprofile=coverage.txt -covermode=atomic diff --git a/client.go b/client.go index 96b79a3d..35b1fd87 100644 --- a/client.go +++ b/client.go @@ -98,6 +98,7 @@ type Client struct { scheme string proxyURL *url.URL closeConnection bool + notParseResponse bool beforeRequest []func(*Client, *Request) error udBeforeRequest []func(*Client, *Request) error preReqHook func(*Client, *Request) error @@ -109,6 +110,10 @@ type User struct { Username, Password string } +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// Client methods +//___________________________________ + // SetHostURL method is to set Host URL in the client instance. It will be used with request // raised from this client with relative URL // // Setting HTTP address @@ -661,18 +666,33 @@ func (c *Client) SetScheme(scheme string) *Client { return c } -// SetCloseConnection method sets variable Close in http request struct with the given +// SetCloseConnection method sets variable `Close` in http request struct with the given // value. More info: https://golang.org/src/net/http/request.go func (c *Client) SetCloseConnection(close bool) *Client { c.closeConnection = close return c } +// SetDoNotParseResponse method instructs `Resty` not to parse the response body automatically. +// Resty exposes the raw response body as `io.ReadCloser`. Also do not forget to close the body, +// otherwise you might get into connection leaks, no connection reuse. +// +// Please Note: Response middlewares are not applicable, if you use this option. Basically you have +// taken over the control of response parsing from `Resty`. +func (c *Client) SetDoNotParseResponse(parse bool) *Client { + c.notParseResponse = parse + return c +} + // IsProxySet method returns the true if proxy is set on client otherwise false. func (c *Client) IsProxySet() bool { return c.proxyURL != nil } +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// Client Unexported methods +//___________________________________ + // executes the given `Request` object and returns response func (c *Client) execute(req *Request) (*Response, error) { defer putBuffer(req.bodyBuf) @@ -710,7 +730,7 @@ func (c *Client) execute(req *Request) (*Response, error) { receivedAt: time.Now(), } - if err != nil { + if err != nil || req.notParseResponse || c.notParseResponse { return response, err } diff --git a/default.go b/default.go index d76f33d7..6aede6fb 100644 --- a/default.go +++ b/default.go @@ -272,6 +272,12 @@ func SetCloseConnection(close bool) *Client { return DefaultClient.SetCloseConnection(close) } +// SetDoNotParseResponse method instructs `Resty` not to parse the response body automatically. +// See `Client.SetDoNotParseResponse` for more information. +func SetDoNotParseResponse(parse bool) *Client { + return DefaultClient.SetDoNotParseResponse(parse) +} + // IsProxySet method returns the true if proxy is set on client otherwise false. // See `Client.IsProxySet` for more information. func IsProxySet() bool { diff --git a/request.go b/request.go index 7335cbcc..dc2ef7dd 100644 --- a/request.go +++ b/request.go @@ -343,6 +343,17 @@ func (r *Request) SetSRV(srv *SRVRecord) *Request { return r } +// SetDoNotParseResponse method instructs `Resty` not to parse the response body automatically. +// Resty exposes the raw response body as `io.ReadCloser`. Also do not forget to close the body, +// otherwise you might get into connection leaks, no connection reuse. +// +// Please Note: Response middlewares are not applicable, if you use this option. Basically you have +// taken over the control of response parsing from `Resty`. +func (r *Request) SetDoNotParseResponse(parse bool) *Request { + r.notParseResponse = parse + return r +} + // // HTTP verb method starts here // diff --git a/request16.go b/request16.go index cc7700ee..359d714d 100644 --- a/request16.go +++ b/request16.go @@ -43,6 +43,7 @@ type Request struct { isSaveResponse bool outputFile string multipartFiles []*File + notParseResponse bool } func (r *Request) addContextIfAvailable() { diff --git a/request17.go b/request17.go index 21105a35..64c0f076 100644 --- a/request17.go +++ b/request17.go @@ -44,6 +44,7 @@ type Request struct { isSaveResponse bool outputFile string multipartFiles []*File + notParseResponse bool ctx context.Context } diff --git a/response.go b/response.go index f522486f..6aab919a 100644 --- a/response.go +++ b/response.go @@ -6,6 +6,7 @@ package resty import ( "encoding/json" + "io" "net/http" "strings" "time" @@ -106,6 +107,18 @@ func (r *Response) Size() int64 { return r.size } +// RawBody method exposes the HTTP raw response body. Use this method in-conjunction with `SetDoNotParseResponse` +// option otherwise you get an error as `read err: http: read on closed response body`. +// +// Do not forget to close the body, otherwise you might get into connection leaks, no connection reuse. +// Basically you have taken over the control of response parsing from `Resty`. +func (r *Response) RawBody() io.ReadCloser { + if r.RawResponse == nil { + return nil + } + return r.RawResponse.Body +} + func (r *Response) fmtBodyString() string { bodyStr := "***** NO CONTENT *****" if r.body != nil { diff --git a/resty.go b/resty.go index 2605d905..ae97c002 100644 --- a/resty.go +++ b/resty.go @@ -6,4 +6,4 @@ package resty // Version # of resty -const Version = "0.13" +const Version = "1.0-dev" diff --git a/resty_test.go b/resty_test.go index 2b472b53..2dbab705 100644 --- a/resty_test.go +++ b/resty_test.go @@ -1123,7 +1123,10 @@ func TestSRV(t *testing.T) { resp, err := r.Get("/") assertError(t, err) - assertEqual(t, http.StatusOK, resp.StatusCode()) + assertEqual(t, true, (resp != nil)) + if resp != nil { + assertEqual(t, http.StatusOK, resp.StatusCode()) + } } func TestSRVInvalidService(t *testing.T) { @@ -1152,6 +1155,44 @@ func TestDeprecatedCodeCovergae(t *testing.T) { assertEqual(t, "testpass", user1.Password) } +func TestRequestDoNotParseResponse(t *testing.T) { + ts := createGetServer(t) + defer ts.Close() + + resp, err := dc().R(). + SetDoNotParseResponse(true). + SetQueryParam("request_no", strconv.FormatInt(time.Now().Unix(), 10)). + Get(ts.URL + "/") + + assertError(t, err) + + buf := getBuffer() + defer putBuffer(buf) + _, _ = io.Copy(buf, resp.RawBody()) + + assertEqual(t, "TestGet: text response", buf.String()) + _ = resp.RawBody().Close() + + // Manually setting RawResponse as nil + resp, err = dc().R(). + SetDoNotParseResponse(true). + Get(ts.URL + "/") + + assertError(t, err) + + resp.RawResponse = nil + assertEqual(t, true, resp.RawBody() == nil) + + // just set test part + SetDoNotParseResponse(true) + assertEqual(t, true, DefaultClient.notParseResponse) + SetDoNotParseResponse(false) +} + +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// Testing Unexported methods +//___________________________________ + func getTestDataPath() string { pwd, _ := os.Getwd() return pwd + "/test-data"