forked from go-eagle/eagle
/
client.go
114 lines (93 loc) · 3.15 KB
/
client.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
// http 客户端
package httpclient
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"time"
"github.com/oliver258/eagle/pkg/utils"
"github.com/pkg/errors"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)
// see: https://github.com/iiinsomnia/gochat/blob/master/utils/http.go
const (
// ContentTypeJSON json format
ContentTypeJSON = "application/json; charset=utf-8"
// ContentTypeForm form format
ContentTypeForm = "application/x-www-form-urlencoded; charset=utf-8"
// DefaultTimeout max exec time for a request
DefaultTimeout = 3 * time.Second
)
// ------------------ JSON ------------------
// GetJSON get json data by get method
func GetJSON(ctx context.Context, url string, options ...Option) ([]byte, error) {
return withJSONBody(ctx, http.MethodGet, url, nil, options...)
}
// PostJSON send json data by post method
func PostJSON(ctx context.Context, url string, data json.RawMessage, options ...Option) ([]byte, error) {
return withJSONBody(ctx, http.MethodPost, url, data, options...)
}
func withJSONBody(ctx context.Context, method, url string, raw json.RawMessage, options ...Option) (body []byte, err error) {
opt := defaultOptions()
for _, o := range options {
o(opt)
}
opt.header["Content-Type"] = []string{ContentTypeJSON}
return doRequest(ctx, method, url, raw, opt)
}
// ------------------ request form ------------------
// PostForm send form data by post method
func PostForm(ctx context.Context, url string, form url.Values, options ...Option) ([]byte, error) {
return withFormBody(ctx, http.MethodPost, url, form, options...)
}
func withFormBody(ctx context.Context, method, url string, form url.Values, options ...Option) (body []byte, err error) {
opt := defaultOptions()
for _, o := range options {
o(opt)
}
opt.header["Content-Type"] = []string{ContentTypeForm}
formValue := form.Encode()
return doRequest(ctx, method, url, utils.StringToBytes(formValue), opt)
}
func doRequest(ctx context.Context, method, url string, payload []byte, opt *options) (ret []byte, err error) {
req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewBuffer(payload))
if err != nil {
return nil, errors.Wrapf(err, "[httpClient] get req err")
}
client := &http.Client{
// add header and set response status for tracing
Transport: otelhttp.NewTransport(http.DefaultTransport),
Timeout: opt.timeout,
}
// set header
for key, value := range opt.header {
req.Header.Set(key, value[0])
}
ctx, span := tracer.Start(req.Context(), fmt.Sprintf("HTTP %s", method))
defer span.End()
resp, err := client.Do(req)
if err != nil {
return nil, errors.Wrapf(err, "[httpClient] do request from [%s %s] err", method, url)
}
if resp != nil {
defer func() {
_ = resp.Body.Close()
}()
}
if !isSuccess(resp.StatusCode) {
return nil, errors.Errorf("[httpClient] status code is %d", resp.StatusCode)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, errors.Wrapf(err, "[httpClient] read resp body from [%s %s] err", method, url)
}
return body, nil
}
// isSuccess check is success
func isSuccess(statusCode int) bool {
return statusCode < http.StatusBadRequest
}