@@ -3,6 +3,7 @@ package request
3
3
import (
4
4
"bytes"
5
5
"encoding/json"
6
+ "fmt"
6
7
"io"
7
8
"io/ioutil"
8
9
"net/http"
@@ -12,20 +13,20 @@ import (
12
13
"github.com/pkg/errors"
13
14
)
14
15
15
- // client is the global client instance.
16
- var client * http.Client
16
+ // cachedClient is the global client instance.
17
+ var cachedClient * http.Client
17
18
18
19
// defaultTimeout is the timeout applied if there is none provided.
19
20
var defaultTimeout = 30 * time .Second
20
21
21
22
// getCachedClient returns the client instance or creates it if it did not exist.
22
23
// The client does not follow redirects and has a timeout of defaultTimeout.
23
24
func getCachedClient () * http.Client {
24
- if client == nil {
25
- client = GetClient ()
25
+ if cachedClient == nil {
26
+ cachedClient = GetClient ()
26
27
}
27
28
28
- return client
29
+ return cachedClient
29
30
}
30
31
31
32
// GetClient returns an http client that does not follow redirects and has a timeout of defaultTimeout.
@@ -40,12 +41,13 @@ func GetClient() *http.Client {
40
41
41
42
// Params holds all information necessary to set up the request instance.
42
43
type Params struct {
43
- Method string
44
- URL string
45
- Headers map [string ]string
46
- Body interface {}
47
- Query map [string ]string
48
- Timeout time.Duration
44
+ URL string
45
+ Method string
46
+ Headers map [string ]string
47
+ Body interface {}
48
+ Query map [string ]string
49
+ Timeout time.Duration
50
+ ExpectedResponseCode int
49
51
}
50
52
51
53
// Do executes the request as specified in the request params
@@ -56,14 +58,7 @@ func Do(params Params, responseBody interface{}) (returnErr error) {
56
58
return err
57
59
}
58
60
59
- var client * http.Client
60
- if params .Timeout != 0 {
61
- client = GetClient ()
62
- client .Timeout = params .Timeout
63
- } else {
64
- client = getCachedClient ()
65
- }
66
-
61
+ client := selectClient (params .Timeout )
67
62
res , err := client .Do (req )
68
63
if err != nil {
69
64
return errors .Wrap (err , "failed to send request" )
@@ -75,13 +70,9 @@ func Do(params Params, responseBody interface{}) (returnErr error) {
75
70
}
76
71
}()
77
72
78
- if ! isSuccessCode (res .StatusCode ) {
79
- bodyBytes , err := ioutil .ReadAll (res .Body )
80
- if err != nil || len (bodyBytes ) == 0 {
81
- return httperrors .New (res .StatusCode , nil )
82
- }
83
-
84
- return httperrors .New (res .StatusCode , string (bodyBytes ))
73
+ err = checkResponseCode (res , params .ExpectedResponseCode )
74
+ if err != nil {
75
+ return err
85
76
}
86
77
87
78
if responseBody == nil {
@@ -91,6 +82,39 @@ func Do(params Params, responseBody interface{}) (returnErr error) {
91
82
return json .NewDecoder (res .Body ).Decode (responseBody )
92
83
}
93
84
85
+ // DoWithStringResponse is the same as Do but the response body is returned as string
86
+ // instead of being parsed into the provided struct.
87
+ func DoWithStringResponse (params Params ) (result string , returnErr error ) {
88
+ req , err := createRequest (params )
89
+ if err != nil {
90
+ return "" , err
91
+ }
92
+
93
+ client := selectClient (params .Timeout )
94
+ res , err := client .Do (req )
95
+ if err != nil {
96
+ return "" , errors .Wrap (err , "failed to send request" )
97
+ }
98
+
99
+ defer func () {
100
+ if cErr := res .Body .Close (); cErr != nil && returnErr == nil {
101
+ returnErr = cErr
102
+ }
103
+ }()
104
+
105
+ err = checkResponseCode (res , params .ExpectedResponseCode )
106
+ if err != nil {
107
+ return "" , err
108
+ }
109
+
110
+ bodyBytes , err := ioutil .ReadAll (res .Body )
111
+ if err != nil {
112
+ return "" , fmt .Errorf ("failed to read response body: %w" , err )
113
+ }
114
+
115
+ return string (bodyBytes ), nil
116
+ }
117
+
94
118
func createRequest (params Params ) (* http.Request , error ) {
95
119
reader , err := convertToReader (params .Body )
96
120
if err != nil {
@@ -120,16 +144,12 @@ func createRequest(params Params) (*http.Request, error) {
120
144
121
145
// Get is a convience wrapper for "Do" to execute GET requests
122
146
func Get (url string , responseBody interface {}) error {
123
- return Do (Params {Method : "GET" , URL : url }, responseBody )
147
+ return Do (Params {Method : http . MethodGet , URL : url }, responseBody )
124
148
}
125
149
126
150
// Post is a convience wrapper for "Do" to execute POST requests
127
151
func Post (url string , requestBody interface {}, responseBody interface {}) error {
128
- return Do (Params {Method : "POST" , URL : url , Body : requestBody }, responseBody )
129
- }
130
-
131
- func isSuccessCode (statusCode int ) bool {
132
- return 200 <= statusCode && statusCode <= 299
152
+ return Do (Params {Method : http .MethodPost , URL : url , Body : requestBody }, responseBody )
133
153
}
134
154
135
155
func convertToReader (body interface {}) (io.Reader , error ) {
@@ -150,3 +170,34 @@ func convertToReader(body interface{}) (io.Reader, error) {
150
170
151
171
return buffer , nil
152
172
}
173
+
174
+ func selectClient (timeout time.Duration ) * http.Client {
175
+ if timeout != 0 {
176
+ client := GetClient ()
177
+ client .Timeout = timeout
178
+ return client
179
+ }
180
+
181
+ return getCachedClient ()
182
+ }
183
+
184
+ func checkResponseCode (res * http.Response , expectedResponseCode int ) error {
185
+ if expectedResponseCode != 0 && res .StatusCode != expectedResponseCode {
186
+ return fmt .Errorf ("expected response code %d but got %d" , expectedResponseCode , res .StatusCode )
187
+ }
188
+
189
+ if ! isSuccessCode (res .StatusCode ) {
190
+ bodyBytes , err := ioutil .ReadAll (res .Body )
191
+ if err != nil || len (bodyBytes ) == 0 {
192
+ return httperrors .New (res .StatusCode , nil )
193
+ }
194
+
195
+ return httperrors .New (res .StatusCode , string (bodyBytes ))
196
+ }
197
+
198
+ return nil
199
+ }
200
+
201
+ func isSuccessCode (statusCode int ) bool {
202
+ return 200 <= statusCode && statusCode <= 299
203
+ }
0 commit comments