1
2
3
4
5 package gensupport
6
7 import (
8 "context"
9 "encoding/json"
10 "errors"
11 "fmt"
12 "net/http"
13 "strings"
14 "time"
15
16 "github.com/google/uuid"
17 "github.com/googleapis/gax-go/v2"
18 "github.com/googleapis/gax-go/v2/callctx"
19 )
20
21
22
23 type wrappedCallErr struct {
24 ctxErr error
25 wrappedErr error
26 }
27
28 func (e wrappedCallErr) Error() string {
29 return fmt.Sprintf("retry failed with %v; last error: %v", e.ctxErr, e.wrappedErr)
30 }
31
32 func (e wrappedCallErr) Unwrap() error {
33 return e.wrappedErr
34 }
35
36
37
38 func (e wrappedCallErr) Is(target error) bool {
39 return errors.Is(e.ctxErr, target) || errors.Is(e.wrappedErr, target)
40 }
41
42
43
44
45
46 func SendRequest(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
47
48 if ctx != nil {
49 headers := callctx.HeadersFromContext(ctx)
50 for k, vals := range headers {
51 for _, v := range vals {
52 req.Header.Add(k, v)
53 }
54 }
55 }
56
57
58
59 if _, ok := req.Header["Accept-Encoding"]; ok {
60 return nil, errors.New("google api: custom Accept-Encoding headers not allowed")
61 }
62 if ctx == nil {
63 return client.Do(req)
64 }
65 return send(ctx, client, req)
66 }
67
68 func send(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
69 if client == nil {
70 client = http.DefaultClient
71 }
72 resp, err := client.Do(req.WithContext(ctx))
73
74
75 if err != nil {
76 select {
77 case <-ctx.Done():
78 err = ctx.Err()
79 default:
80 }
81 }
82 return resp, err
83 }
84
85
86
87
88
89
90 func SendRequestWithRetry(ctx context.Context, client *http.Client, req *http.Request, retry *RetryConfig) (*http.Response, error) {
91
92 if ctx != nil {
93 headers := callctx.HeadersFromContext(ctx)
94 for k, vals := range headers {
95 for _, v := range vals {
96 req.Header.Add(k, v)
97 }
98 }
99 }
100
101
102
103 if _, ok := req.Header["Accept-Encoding"]; ok {
104 return nil, errors.New("google api: custom Accept-Encoding headers not allowed")
105 }
106 if ctx == nil {
107 return client.Do(req)
108 }
109 return sendAndRetry(ctx, client, req, retry)
110 }
111
112 func sendAndRetry(ctx context.Context, client *http.Client, req *http.Request, retry *RetryConfig) (*http.Response, error) {
113 if client == nil {
114 client = http.DefaultClient
115 }
116
117 var resp *http.Response
118 var err error
119 attempts := 1
120 invocationID := uuid.New().String()
121 baseXGoogHeader := req.Header.Get("X-Goog-Api-Client")
122
123
124 var pause time.Duration
125 var bo Backoff
126 if retry != nil && retry.Backoff != nil {
127 bo = &gax.Backoff{
128 Initial: retry.Backoff.Initial,
129 Max: retry.Backoff.Max,
130 Multiplier: retry.Backoff.Multiplier,
131 }
132 } else {
133 bo = backoff()
134 }
135
136 var errorFunc = retry.errorFunc()
137
138 for {
139 t := time.NewTimer(pause)
140 select {
141 case <-ctx.Done():
142 t.Stop()
143
144
145 if err != nil {
146 return resp, wrappedCallErr{ctx.Err(), err}
147 }
148 return resp, ctx.Err()
149 case <-t.C:
150 }
151
152 if ctx.Err() != nil {
153
154
155
156
157 if err != nil {
158 return resp, wrappedCallErr{ctx.Err(), err}
159 }
160 return resp, ctx.Err()
161 }
162
163
164
165
166 invocationHeader := fmt.Sprintf("gccl-invocation-id/%s gccl-attempt-count/%d", invocationID, attempts)
167 xGoogHeader := strings.Join([]string{invocationHeader, baseXGoogHeader}, " ")
168 req.Header.Set("X-Goog-Api-Client", xGoogHeader)
169 req.Header.Set("X-Goog-Gcs-Idempotency-Token", invocationID)
170
171 resp, err = client.Do(req.WithContext(ctx))
172
173 var status int
174 if resp != nil {
175 status = resp.StatusCode
176 }
177
178
179
180
181 if req.GetBody == nil || !errorFunc(status, err) {
182 break
183 }
184 attempts++
185 var errBody error
186 req.Body, errBody = req.GetBody()
187 if errBody != nil {
188 break
189 }
190
191 pause = bo.Pause()
192 if resp != nil && resp.Body != nil {
193 resp.Body.Close()
194 }
195 }
196 return resp, err
197 }
198
199
200
201 func DecodeResponse(target interface{}, res *http.Response) error {
202 if res.StatusCode == http.StatusNoContent {
203 return nil
204 }
205 return json.NewDecoder(res.Body).Decode(target)
206 }
207
View as plain text