-
Notifications
You must be signed in to change notification settings - Fork 1
/
http.go
116 lines (106 loc) · 3.58 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
package kutils
import (
"bytes"
"context"
"crypto/tls"
"net"
"net/http"
"time"
"github.com/go-resty/resty/v2"
"github.com/hashicorp/go-retryablehttp"
"golang.org/x/net/http2"
"github.com/KyberNetwork/kutils/internal/json"
)
// HttpCfg is the resty http client configs
type HttpCfg struct {
HttpClient *http.Client `json:"-"`
BaseUrl string // client's base url for all methods
Headers http.Header // default headers
Timeout time.Duration // request timeout, see http.Client's Timeout
MaxIdleConns int // max idle connections for all hosts, default 100
MaxIdleConnsPerHost int // max idle connections per host, default GOMAXPROCS+1
MaxConnsPerHost int // max total connections per host, default 0 (unlimited)
UseH2c bool // whether to use http2 h2c, default false
RetryCount int // retry count (exponential backoff), default 0
RetryWaitTime time.Duration // first exponential backoff, default 100ms
RetryMaxWaitTime time.Duration // max exponential backoff, default 2s
Debug bool // whether to log requests and responses
}
// NewRestyClient creates a new resty client with the given configs
func (h *HttpCfg) NewRestyClient() (client *resty.Client) {
if h == nil {
return resty.New()
}
hc := h.HttpClient
if hc == nil {
hc = &http.Client{Timeout: h.Timeout}
} else if hc.Timeout == 0 {
hc.Timeout = h.Timeout
}
client = resty.NewWithClient(hc)
if transport, err := client.Transport(); err == nil && transport != nil {
transport = transport.Clone()
if h.MaxIdleConns != 0 {
transport.MaxIdleConns = h.MaxIdleConns
}
if h.MaxIdleConnsPerHost != 0 {
transport.MaxIdleConnsPerHost = h.MaxIdleConnsPerHost
}
if h.MaxConnsPerHost != 0 {
transport.MaxConnsPerHost = h.MaxConnsPerHost
}
client.SetTransport(transport)
if h.UseH2c {
if h2cTransport, err := http2.ConfigureTransports(transport); err == nil {
h2cTransport.AllowHTTP = true
}
client.SetTransport(&http2.Transport{
AllowHTTP: true,
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
var d net.Dialer
return d.DialContext(ctx, network, addr)
},
})
}
}
client.SetBaseURL(h.BaseUrl).
SetRetryCount(h.RetryCount).
AddRetryCondition(retryableHttpError).
SetDebug(h.Debug)
for key, values := range h.Headers {
for _, value := range values {
client.Header.Add(key, value)
}
}
if waitTime := h.RetryWaitTime; waitTime != 0 {
client.SetRetryWaitTime(waitTime)
}
if maxWaitTime := h.RetryMaxWaitTime; maxWaitTime != 0 {
client.SetRetryMaxWaitTime(maxWaitTime)
}
client.JSONMarshal = JSONMarshal
client.JSONUnmarshal = JSONUnmarshal
return client
}
func retryableHttpError(r *resty.Response, err error) bool {
if r == nil {
return false
}
switch r.StatusCode() {
case http.StatusRequestTimeout, http.StatusMisdirectedRequest, http.StatusLocked, http.StatusTooManyRequests:
return true
default:
retry, _ := retryablehttp.DefaultRetryPolicy(context.Background(), r.RawResponse, err)
return retry
}
}
// JSONMarshal allows choosing the JSON marshalling implementation with build tag with the same logic as used by gin
func JSONMarshal(v any) ([]byte, error) {
return json.Marshal(v)
}
// JSONUnmarshal allows choosing the JSON unmarshalling implementation with build tag with the same logic as used by gin
func JSONUnmarshal(data []byte, v any) error {
decoder := json.NewDecoder(bytes.NewReader(data))
decoder.UseNumber()
return decoder.Decode(v)
}