forked from xiaojiaoyu100/wxpay
-
Notifications
You must be signed in to change notification settings - Fork 0
/
do_request.go
115 lines (105 loc) · 2.66 KB
/
do_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
package wxpay
import (
"bytes"
"context"
"encoding/xml"
"io/ioutil"
"net/http"
"reflect"
"time"
)
func (c *Client) request(url string, in interface{}, out interface{}) ([]byte, error) {
const (
max = 1000 * time.Millisecond
)
var (
tempDelay time.Duration
tryNum = 0
err error
body []byte
)
tryLoop:
for {
if tempDelay == 0 {
tempDelay = 100 * time.Millisecond
} else {
tempDelay *= 2
}
if tempDelay > max {
tempDelay = max
}
body, err = c.doRequest(url, in, out)
tryNum++
if tryNum > 3 {
return body, err
}
switch {
case shouldRetry(err):
notifyAsync("doRequest err: ", err)
time.Sleep(tempDelay)
continue tryLoop
default:
for i := true; i; i = false {
if out == nil {
break
}
ot := reflect.TypeOf(out)
if ot.Kind() != reflect.Ptr && ot.Kind() != reflect.Interface {
break
}
value := reflect.ValueOf(out).Elem()
if reflect.TypeOf(value).Kind() != reflect.Struct {
break
}
metaItf := value.FieldByName("Meta").Interface()
if m, ok := metaItf.(Meta); ok {
if m.IsSystemErr() ||
m.IsBizerrNeedRetry() {
notifyAsync("doRequest err: ", m.ErrCode)
time.Sleep(tempDelay)
continue tryLoop
}
}
}
return body, err
}
}
}
func (c *Client) doRequest(url string, in interface{}, out interface{}) ([]byte, error) {
body, err := xml.Marshal(in)
if err != nil {
globalLogger.printf("%s xml marshal err: %s", url, err.Error())
return nil, err
}
req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer([]byte(body)))
if err != nil {
globalLogger.printf("%s new request err: %s", url, err.Error())
return nil, err
}
req.Header.Set("Content-Type", "application/xml; charset=utf-8")
ctx, cancel := context.WithTimeout(context.Background(), 6*time.Second)
defer cancel()
req = req.WithContext(ctx)
globalLogger.printf("%s %s %s", req.Method, req.URL.String(), string(body))
resp, err := selectedClient(url).Do(req)
if err != nil {
globalLogger.printf("%s %s do err: %s", req.Method, req.URL.String(), err.Error())
return nil, err
}
defer resp.Body.Close()
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
globalLogger.printf("%s %s read resp body err: %s", req.Method, req.URL.String(), err.Error())
return nil, err
}
globalLogger.printf("%s %s %s", req.Method, req.URL.String(), string(body))
if err := xml.Unmarshal(body, &out); err != nil {
globalLogger.printf("unmarshal body err: %s, body: %s", err.Error(), string(body))
return nil, err
}
if err := checkSign(body, c.apiKey); err != nil {
globalLogger.printf("checkSign err: %s", err.Error())
return nil, err
}
return body, nil
}