-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtransport.go
129 lines (121 loc) · 2.91 KB
/
transport.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
package vk
import (
"context"
"encoding/json"
"fmt"
"github.com/avast/retry-go"
"io/ioutil"
"net/http"
"time"
)
type FailedRequest struct {
request *Request
response string
responseError *ResponseError
info string
}
func (r *FailedRequest) Error() string {
return r.response
}
type transport struct {
baseUrl string
httpClient *http.Client
retryOptions []retry.Option
}
func newTransport(baseUrl string, retryAttempts uint, retryDelay time.Duration) *transport {
httpClient := &http.Client{
Timeout: 30 * time.Second,
}
retryOptions := []retry.Option{
retry.Attempts(retryAttempts),
retry.Delay(retryDelay),
retry.DelayType(retry.BackOffDelay),
retry.LastErrorOnly(true),
}
t := &transport{
baseUrl: baseUrl,
httpClient: httpClient,
retryOptions: retryOptions,
}
return t
}
func (t *transport) Send(ctx context.Context, request *Request) (response string, failedRequest *FailedRequest) {
sendRequest := func() error {
r, err := t.doRequest(ctx, request)
response = r
if err != nil {
return &FailedRequest{
request: request,
response: response,
responseError: nil,
info: err.Error(),
}
}
responseError, err := t.getErrorFromResponse(response)
if err != nil {
failedRequest = &FailedRequest{
request: request,
response: response,
responseError: nil,
info: err.Error(),
}
return nil
}
if responseError != nil {
if responseError.HasRetryErrorCode() {
return &FailedRequest{
request: request,
response: response,
responseError: responseError,
info: "rate limit",
}
}
failedRequest = &FailedRequest{
request: request,
response: response,
responseError: responseError,
}
return nil
}
return nil
}
err := retry.Do(sendRequest, append(t.retryOptions, retry.Context(ctx))...)
if err != nil {
failedRequest, ok := err.(*FailedRequest)
if ok {
return response, failedRequest
}
}
return response, failedRequest
}
func (t *transport) doRequest(ctx context.Context, request *Request) (string, error) {
req, _ := http.NewRequestWithContext(ctx, http.MethodPost, request.getFullUrl(t.baseUrl), request.getParams())
resp, err := t.httpClient.Do(req)
defer func() {
if resp != nil {
_ = resp.Body.Close()
}
}()
if err != nil {
return err.Error(), err
}
body, _ := ioutil.ReadAll(resp.Body)
result := string(body)
if resp.StatusCode != 200 {
return result, fmt.Errorf("expected status code 200, got %d", resp.StatusCode)
}
return result, nil
}
func (t *transport) getErrorFromResponse(response string) (*ResponseError, error) {
responseError := &ResponseError{}
if responseError.HasErrorCode(response) {
err := json.Unmarshal([]byte(response), responseError)
if err != nil {
return nil, err
}
if responseError.Error != nil {
return responseError, nil
}
}
return nil, nil
}