-
Notifications
You must be signed in to change notification settings - Fork 2
/
http.go
177 lines (149 loc) · 5.06 KB
/
http.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
package otk
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"io"
"net/http"
"net/url"
"strings"
"golang.org/x/xerrors"
)
type HTTPClient struct {
DefaultHeaders http.Header
DefaultQuery url.Values
http.Client
}
func (c *HTTPClient) AllowInsecureTLS(v bool) (old bool) {
tr, ok := c.Transport.(*http.Transport)
if !ok {
tr = &http.Transport{}
c.Transport = tr
}
if tr.TLSClientConfig == nil {
tr.TLSClientConfig = &tls.Config{}
}
old, tr.TLSClientConfig.InsecureSkipVerify = tr.TLSClientConfig.InsecureSkipVerify, v
return
}
// RequestCtx is a smart wrapper to handle http requests.
// - ctx: is a context.Context in case you want more control over canceling the request.
// - method: http method (GET, PUT, POST, etc..), if empty it defaults to GET.
// - contentType: request content-type.
// - url: the request's url.
// - reqData: data to pass to POST/PUT requests, if it's an `io.Reader`, a `[]byte` or a `string`, it will be passed as-is,
// `url.Values` will be encoded as "application/x-www-form-urlencoded", any other object will be encoded as JSON.
// - respData: data object to get the response or `nil`, can be , `io.Writer`, `func(io.Reader) error`
// to read the body directly, `func(*http.Response) error` to process the actual response,
// or a pointer to an object to decode a JSON body into.
func (c *HTTPClient) RequestCtx(ctx context.Context, method, contentType, uri string, reqData, respData interface{}) (err error) {
var h http.Header
if contentType != "" {
h = http.Header{}
h.Set("Content-Type", contentType)
}
return c.RequestHeadersCtx(ctx, method, uri, h, reqData, respData)
}
// RequestHeaderCtx is a smart wrapper to handle http requests.
// - ctx: is a context.Context in case you want more control over canceling the request.
// - method: http method (GET, PUT, POST, etc..), if empty it defaults to GET.
// - url: the request's url.
// - header: headers to pass to the request, ex User-Agent.
// - reqData: data to pass to POST/PUT requests, if it's an `io.Reader`, a `[]byte` or a `string`, it will be passed as-is,
// `url.Values` will be encoded as "application/x-www-form-urlencoded", any other object will be encoded as JSON.
// - respData: data object to get the response or `nil`, can be , `io.Writer`, `func(io.Reader) error`
// to read the body directly, `func(*http.Response) error` to process the actual response,
// or a pointer to an object to decode a JSON body into.
func (c *HTTPClient) RequestHeadersCtx(ctx context.Context, method, uri string, header http.Header, reqData, respData interface{}) (err error) {
var r io.Reader
contentType := ""
switch in := reqData.(type) {
case nil:
case io.Reader:
r = in
case []byte:
r = bytes.NewReader(in)
case string:
r = strings.NewReader(in)
case url.Values:
r = strings.NewReader(in.Encode())
if header.Get("Content-Type") == "" {
contentType = "application/x-www-form-urlencoded"
}
default:
var buf bytes.Buffer
if err := json.NewEncoder(&buf).Encode(reqData); err != nil {
return err
}
r = &buf
if header.Get("Content-Type") == "" {
contentType = "application/json"
}
}
var req *http.Request
if req, err = http.NewRequestWithContext(ctx, method, uri, r); err != nil {
return err
}
if header != nil {
req.Header = header
}
if contentType != "" {
req.Header.Set("Content-Type", contentType)
}
resp, err := c.Do(req)
if err != nil {
return xerrors.Errorf("%s error: %w", req.URL, err)
}
defer resp.Body.Close()
switch out := respData.(type) {
case nil:
case io.Writer:
_, err = io.Copy(out, resp.Body)
case io.ReaderFrom:
_, err = out.ReadFrom(resp.Body)
case func(r io.Reader) error:
err = out(resp.Body)
case func(status int, r io.Reader) error:
err = out(resp.StatusCode, resp.Body)
case func(r *http.Response) error:
err = out(resp)
default:
err = json.NewDecoder(resp.Body).Decode(out)
}
return err
}
func (c *HTTPClient) Do(req *http.Request) (*http.Response, error) {
if len(c.DefaultHeaders) > 0 {
h := req.Header
for k, vs := range c.DefaultHeaders {
if h[k] == nil {
h[k] = vs
}
}
}
if len(c.DefaultQuery) > 0 {
q := req.URL.Query()
for k, vs := range c.DefaultQuery {
if q[k] == nil {
q[k] = vs
}
}
req.URL.RawQuery = q.Encode()
}
return c.Client.Do(req)
}
// Request is a wrapper for `RequestCtx(context.Background(), method, ct, url, reqData, respData)`
func (c *HTTPClient) Request(method, ct, url string, reqData, respData interface{}) error {
return c.RequestCtx(context.Background(), method, ct, url, reqData, respData)
}
var DefaultClient HTTPClient
func RequestHeadersCtx(ctx context.Context, method, url string, header http.Header, reqData, respData interface{}) error {
return DefaultClient.RequestHeadersCtx(ctx, method, url, header, reqData, respData)
}
func RequestCtx(ctx context.Context, method, ct, url string, reqData, respData interface{}) error {
return DefaultClient.RequestCtx(ctx, method, ct, url, reqData, respData)
}
func Request(method, ct, url string, reqData, respData interface{}) error {
return DefaultClient.Request(method, ct, url, reqData, respData)
}