-
Notifications
You must be signed in to change notification settings - Fork 46
/
http_request.go
198 lines (166 loc) · 5.22 KB
/
http_request.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
package candiutils
import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"net/http"
"net/http/httputil"
"time"
"github.com/golangid/candi/tracer"
)
type (
// HTTPRequest interface
HTTPRequest interface {
DoRequest(ctx context.Context, method, url string, requestBody []byte, headers map[string]string) (result *HTTPRequestResult, err error)
Do(context context.Context, method, url string, reqBody []byte, headers map[string]string) (respBody []byte, respCode int, err error)
}
httpClientDo interface {
Do(req *http.Request) (*http.Response, error)
}
// httpRequestImpl struct
httpRequestImpl struct {
client httpClientDo
breakerName string
timeout time.Duration
retries int
sleepBetweenRetry time.Duration
tlsConfig *tls.Config
minHTTPErrorCodeThreshold int
}
// HTTPRequestResult struct
HTTPRequestResult struct {
*bytes.Buffer
RespCode int
}
// HTTPRequestOption func type
HTTPRequestOption func(*httpRequestImpl)
)
// HTTPRequestSetRetries option func
func HTTPRequestSetRetries(retries int) HTTPRequestOption {
return func(h *httpRequestImpl) {
h.retries = retries
}
}
// HTTPRequestSetSleepBetweenRetry option func
func HTTPRequestSetSleepBetweenRetry(sleepBetweenRetry time.Duration) HTTPRequestOption {
return func(h *httpRequestImpl) {
h.sleepBetweenRetry = sleepBetweenRetry
}
}
// HTTPRequestSetTLS option func
func HTTPRequestSetTLS(tlsConfig *tls.Config) HTTPRequestOption {
return func(h *httpRequestImpl) {
h.tlsConfig = tlsConfig
}
}
// HTTPRequestSetHTTPErrorCodeThreshold option func, set minimum http response code for return error when exec client request
func HTTPRequestSetHTTPErrorCodeThreshold(minHTTPStatusCode int) HTTPRequestOption {
return func(h *httpRequestImpl) {
h.minHTTPErrorCodeThreshold = minHTTPStatusCode
}
}
// HTTPRequestSetTimeout option func
func HTTPRequestSetTimeout(timeout time.Duration) HTTPRequestOption {
return func(h *httpRequestImpl) {
h.timeout = timeout
}
}
// HTTPRequestSetBreakerName option func
func HTTPRequestSetBreakerName(breakerName string) HTTPRequestOption {
return func(h *httpRequestImpl) {
h.breakerName = breakerName
}
}
// HTTPRequestSetClient option func
func HTTPRequestSetClient(cl *http.Client) HTTPRequestOption {
return func(h *httpRequestImpl) {
h.client = cl
}
}
// NewHTTPRequest function
// Request's Constructor
func NewHTTPRequest(opts ...HTTPRequestOption) HTTPRequest {
httpReq := new(httpRequestImpl)
// set default value
httpReq.retries = 5
httpReq.sleepBetweenRetry = 500 * time.Millisecond
httpReq.minHTTPErrorCodeThreshold = http.StatusBadRequest
httpReq.timeout = 10 * time.Second
httpReq.breakerName = "default"
for _, o := range opts {
o(httpReq)
}
// set http client
if httpReq.client == nil {
client := &http.Client{
Timeout: httpReq.timeout,
}
if httpReq.tlsConfig != nil {
client.Transport = &http.Transport{TLSClientConfig: httpReq.tlsConfig}
}
httpReq.client = client
}
return httpReq
}
// Do function, for http client call
func (req *httpRequestImpl) Do(ctx context.Context, method, url string, requestBody []byte, headers map[string]string) (respBody []byte, respCode int, err error) {
httpResult, err := req.DoRequest(ctx, method, url, requestBody, headers)
if err != nil {
if httpResult != nil {
respBody, respCode = httpResult.Bytes(), httpResult.RespCode
}
return respBody, respCode, err
}
return httpResult.Bytes(), httpResult.RespCode, nil
}
func (req *httpRequestImpl) DoRequest(ctx context.Context, method, url string, requestBody []byte, headers map[string]string) (result *HTTPRequestResult, err error) {
// set request http
httpReq, err := http.NewRequestWithContext(ctx, method, url, bytes.NewBuffer(requestBody))
if err != nil {
tracer.SetError(ctx, err)
return nil, err
}
// set tracer
trace, ctx := tracer.StartTraceWithContext(ctx, fmt.Sprintf("HTTP Request: %s %s", method, httpReq.URL.Host))
defer func() { trace.Finish(tracer.FinishWithError(err)) }()
if headers == nil {
headers = map[string]string{}
}
trace.InjectRequestHeader(headers)
// iterate optional data of headers
for key, value := range headers {
httpReq.Header.Set(key, value)
}
trace.SetTag("http.method", httpReq.Method)
trace.SetTag("http.url", httpReq.URL.String())
trace.SetTag("http.url_path", httpReq.URL.Path)
trace.SetTag("http.timeout", req.timeout.String())
trace.SetTag("http.breaker_name", req.breakerName)
dumpRequest, _ := httputil.DumpRequest(httpReq, false)
trace.SetTag("http.request", dumpRequest)
if requestBody != nil {
trace.Log("request.body", requestBody)
}
resp, err := req.client.Do(httpReq)
if err != nil && resp == nil {
return nil, err
}
defer resp.Body.Close()
result = &HTTPRequestResult{
Buffer: &bytes.Buffer{},
RespCode: resp.StatusCode,
}
io.Copy(result.Buffer, resp.Body)
dumpResponse, _ := httputil.DumpResponse(resp, false)
trace.SetTag("http.response", dumpResponse)
trace.SetTag("response.code", resp.StatusCode)
trace.SetTag("response.status", resp.Status)
trace.Log("response.body", result.Bytes())
if req.minHTTPErrorCodeThreshold != 0 && resp.StatusCode >= req.minHTTPErrorCodeThreshold {
err = errors.New(resp.Status)
}
return
}