1
2
3
4
5
6
7 package googleapi
8
9 import (
10 "bytes"
11 "encoding/json"
12 "fmt"
13 "io"
14 "net/http"
15 "net/url"
16 "strings"
17 "time"
18
19 "google.golang.org/api/internal/third_party/uritemplates"
20 )
21
22
23
24
25
26 type ContentTyper interface {
27 ContentType() string
28 }
29
30
31
32 type SizeReaderAt interface {
33 io.ReaderAt
34 Size() int64
35 }
36
37
38
39 type ServerResponse struct {
40
41
42 HTTPStatusCode int
43
44 Header http.Header
45 }
46
47 const (
48
49
50 Version = "0.5"
51
52
53 UserAgent = "google-api-go-client/" + Version
54
55
56
57 DefaultUploadChunkSize = 16 * 1024 * 1024
58
59
60
61
62 MinUploadChunkSize = 256 * 1024
63 )
64
65
66 type Error struct {
67
68 Code int `json:"code"`
69
70
71 Message string `json:"message"`
72
73 Details []interface{} `json:"details"`
74
75
76 Body string
77
78 Header http.Header
79
80 Errors []ErrorItem
81
82
83 err error
84 }
85
86
87 type ErrorItem struct {
88
89 Reason string `json:"reason"`
90
91 Message string `json:"message"`
92 }
93
94 func (e *Error) Error() string {
95 if len(e.Errors) == 0 && e.Message == "" {
96 return fmt.Sprintf("googleapi: got HTTP response code %d with body: %v", e.Code, e.Body)
97 }
98 var buf bytes.Buffer
99 fmt.Fprintf(&buf, "googleapi: Error %d: ", e.Code)
100 if e.Message != "" {
101 fmt.Fprintf(&buf, "%s", e.Message)
102 }
103 if len(e.Details) > 0 {
104 var detailBuf bytes.Buffer
105 enc := json.NewEncoder(&detailBuf)
106 enc.SetIndent("", " ")
107 if err := enc.Encode(e.Details); err == nil {
108 fmt.Fprint(&buf, "\nDetails:")
109 fmt.Fprintf(&buf, "\n%s", detailBuf.String())
110
111 }
112 }
113 if len(e.Errors) == 0 {
114 return strings.TrimSpace(buf.String())
115 }
116 if len(e.Errors) == 1 && e.Errors[0].Message == e.Message {
117 fmt.Fprintf(&buf, ", %s", e.Errors[0].Reason)
118 return buf.String()
119 }
120 fmt.Fprintln(&buf, "\nMore details:")
121 for _, v := range e.Errors {
122 fmt.Fprintf(&buf, "Reason: %s, Message: %s\n", v.Reason, v.Message)
123 }
124 return buf.String()
125 }
126
127
128 func (e *Error) Wrap(err error) {
129 e.err = err
130 }
131
132 func (e *Error) Unwrap() error {
133 return e.err
134 }
135
136 type errorReply struct {
137 Error *Error `json:"error"`
138 }
139
140
141
142 func CheckResponse(res *http.Response) error {
143 if res.StatusCode >= 200 && res.StatusCode <= 299 {
144 return nil
145 }
146 slurp, err := io.ReadAll(res.Body)
147 if err == nil {
148 jerr := new(errorReply)
149 err = json.Unmarshal(slurp, jerr)
150 if err == nil && jerr.Error != nil {
151 if jerr.Error.Code == 0 {
152 jerr.Error.Code = res.StatusCode
153 }
154 jerr.Error.Body = string(slurp)
155 jerr.Error.Header = res.Header
156 return jerr.Error
157 }
158 }
159 return &Error{
160 Code: res.StatusCode,
161 Body: string(slurp),
162 Header: res.Header,
163 }
164 }
165
166
167
168
169
170 func IsNotModified(err error) bool {
171 if err == nil {
172 return false
173 }
174 ae, ok := err.(*Error)
175 return ok && ae.Code == http.StatusNotModified
176 }
177
178
179
180
181
182 func CheckMediaResponse(res *http.Response) error {
183 if res.StatusCode >= 200 && res.StatusCode <= 299 {
184 return nil
185 }
186 slurp, _ := io.ReadAll(io.LimitReader(res.Body, 1<<20))
187 return &Error{
188 Code: res.StatusCode,
189 Body: string(slurp),
190 Header: res.Header,
191 }
192 }
193
194
195 type MarshalStyle bool
196
197
198 var WithDataWrapper = MarshalStyle(true)
199
200
201 var WithoutDataWrapper = MarshalStyle(false)
202
203 func (wrap MarshalStyle) JSONReader(v interface{}) (io.Reader, error) {
204 buf := new(bytes.Buffer)
205 if wrap {
206 buf.Write([]byte(`{"data": `))
207 }
208 err := json.NewEncoder(buf).Encode(v)
209 if err != nil {
210 return nil, err
211 }
212 if wrap {
213 buf.Write([]byte(`}`))
214 }
215 return buf, nil
216 }
217
218
219
220
221 type ProgressUpdater func(current, total int64)
222
223
224 type MediaOption interface {
225 setOptions(o *MediaOptions)
226 }
227
228 type contentTypeOption string
229
230 func (ct contentTypeOption) setOptions(o *MediaOptions) {
231 o.ContentType = string(ct)
232 if o.ContentType == "" {
233 o.ForceEmptyContentType = true
234 }
235 }
236
237
238
239 func ContentType(ctype string) MediaOption {
240 return contentTypeOption(ctype)
241 }
242
243 type chunkSizeOption int
244
245 func (cs chunkSizeOption) setOptions(o *MediaOptions) {
246 size := int(cs)
247 if size%MinUploadChunkSize != 0 {
248 size += MinUploadChunkSize - (size % MinUploadChunkSize)
249 }
250 o.ChunkSize = size
251 }
252
253
254
255
256
257
258 func ChunkSize(size int) MediaOption {
259 return chunkSizeOption(size)
260 }
261
262 type chunkRetryDeadlineOption time.Duration
263
264 func (cd chunkRetryDeadlineOption) setOptions(o *MediaOptions) {
265 o.ChunkRetryDeadline = time.Duration(cd)
266 }
267
268
269
270
271
272
273
274
275
276 func ChunkRetryDeadline(deadline time.Duration) MediaOption {
277 return chunkRetryDeadlineOption(deadline)
278 }
279
280
281 type MediaOptions struct {
282 ContentType string
283 ForceEmptyContentType bool
284 ChunkSize int
285 ChunkRetryDeadline time.Duration
286 }
287
288
289
290 func ProcessMediaOptions(opts []MediaOption) *MediaOptions {
291 mo := &MediaOptions{ChunkSize: DefaultUploadChunkSize}
292 for _, o := range opts {
293 o.setOptions(mo)
294 }
295 return mo
296 }
297
298
299
300
301
302
303
304
305 func ResolveRelative(basestr, relstr string) string {
306 u, err := url.Parse(basestr)
307 if err != nil {
308 panic(fmt.Sprintf("failed to parse %q", basestr))
309 }
310 afterColonPath := ""
311 if i := strings.IndexRune(relstr, ':'); i > 0 {
312 afterColonPath = relstr[i+1:]
313 relstr = relstr[:i]
314 }
315 rel, err := url.Parse(relstr)
316 if err != nil {
317 panic(fmt.Sprintf("failed to parse %q", relstr))
318 }
319 u = u.ResolveReference(rel)
320 us := u.String()
321 if afterColonPath != "" {
322 us = fmt.Sprintf("%s:%s", us, afterColonPath)
323 }
324 us = strings.Replace(us, "%7B", "{", -1)
325 us = strings.Replace(us, "%7D", "}", -1)
326 us = strings.Replace(us, "%2A", "*", -1)
327 return us
328 }
329
330
331
332
333
334 func Expand(u *url.URL, expansions map[string]string) {
335 escaped, unescaped, err := uritemplates.Expand(u.Path, expansions)
336 if err == nil {
337 u.Path = unescaped
338 u.RawPath = escaped
339 }
340 }
341
342
343
344
345 func CloseBody(res *http.Response) {
346 if res == nil || res.Body == nil {
347 return
348 }
349
350
351
352
353
354 buf := make([]byte, 1)
355 for i := 0; i < 3; i++ {
356 _, err := res.Body.Read(buf)
357 if err != nil {
358 break
359 }
360 }
361 res.Body.Close()
362
363 }
364
365
366
367
368 func VariantType(t map[string]interface{}) string {
369 s, _ := t["type"].(string)
370 return s
371 }
372
373
374
375
376 func ConvertVariant(v map[string]interface{}, dst interface{}) bool {
377 var buf bytes.Buffer
378 err := json.NewEncoder(&buf).Encode(v)
379 if err != nil {
380 return false
381 }
382 return json.Unmarshal(buf.Bytes(), dst) == nil
383 }
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405 type Field string
406
407
408 func CombineFields(s []Field) string {
409 r := make([]string, len(s))
410 for i, v := range s {
411 r[i] = string(v)
412 }
413 return strings.Join(r, ",")
414 }
415
416
417
418
419
420
421
422 type CallOption interface {
423 Get() (key, value string)
424 }
425
426
427
428
429 type MultiCallOption interface {
430 CallOption
431 GetMulti() (key string, value []string)
432 }
433
434
435
436
437
438 func QuotaUser(u string) CallOption { return quotaUser(u) }
439
440 type quotaUser string
441
442 func (q quotaUser) Get() (string, string) { return "quotaUser", string(q) }
443
444
445
446 func UserIP(ip string) CallOption { return userIP(ip) }
447
448 type userIP string
449
450 func (i userIP) Get() (string, string) { return "userIp", string(i) }
451
452
453
454 func Trace(traceToken string) CallOption { return traceTok(traceToken) }
455
456 type traceTok string
457
458 func (t traceTok) Get() (string, string) { return "trace", "token:" + string(t) }
459
460 type queryParameter struct {
461 key string
462 values []string
463 }
464
465
466 func QueryParameter(key string, values ...string) CallOption {
467 return queryParameter{key: key, values: append([]string{}, values...)}
468 }
469
470
471 func (q queryParameter) Get() (string, string) {
472 return "", ""
473 }
474
475
476 func (q queryParameter) GetMulti() (string, []string) {
477 return q.key, q.values
478 }
479
480
481
View as plain text