diff options
Diffstat (limited to 'vendor/google.golang.org/api/gensupport')
-rw-r--r-- | vendor/google.golang.org/api/gensupport/backoff.go | 51 | ||||
-rw-r--r-- | vendor/google.golang.org/api/gensupport/buffer.go | 79 | ||||
-rw-r--r-- | vendor/google.golang.org/api/gensupport/doc.go | 10 | ||||
-rw-r--r-- | vendor/google.golang.org/api/gensupport/header.go | 22 | ||||
-rw-r--r-- | vendor/google.golang.org/api/gensupport/json.go | 211 | ||||
-rw-r--r-- | vendor/google.golang.org/api/gensupport/jsonfloat.go | 57 | ||||
-rw-r--r-- | vendor/google.golang.org/api/gensupport/media.go | 342 | ||||
-rw-r--r-- | vendor/google.golang.org/api/gensupport/params.go | 51 | ||||
-rw-r--r-- | vendor/google.golang.org/api/gensupport/resumable.go | 216 | ||||
-rw-r--r-- | vendor/google.golang.org/api/gensupport/retry.go | 84 | ||||
-rw-r--r-- | vendor/google.golang.org/api/gensupport/send.go | 87 |
11 files changed, 1210 insertions, 0 deletions
diff --git a/vendor/google.golang.org/api/gensupport/backoff.go b/vendor/google.golang.org/api/gensupport/backoff.go new file mode 100644 index 0000000..94b7789 --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/backoff.go | |||
@@ -0,0 +1,51 @@ | |||
1 | // Copyright 2016 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package gensupport | ||
6 | |||
7 | import ( | ||
8 | "math/rand" | ||
9 | "time" | ||
10 | ) | ||
11 | |||
12 | // BackoffStrategy defines the set of functions that a backoff-er must | ||
13 | // implement. | ||
14 | type BackoffStrategy interface { | ||
15 | // Pause returns the duration of the next pause and true if the operation should be | ||
16 | // retried, or false if no further retries should be attempted. | ||
17 | Pause() (time.Duration, bool) | ||
18 | |||
19 | // Reset restores the strategy to its initial state. | ||
20 | Reset() | ||
21 | } | ||
22 | |||
23 | // ExponentialBackoff performs exponential backoff as per https://en.wikipedia.org/wiki/Exponential_backoff. | ||
24 | // The initial pause time is given by Base. | ||
25 | // Once the total pause time exceeds Max, Pause will indicate no further retries. | ||
26 | type ExponentialBackoff struct { | ||
27 | Base time.Duration | ||
28 | Max time.Duration | ||
29 | total time.Duration | ||
30 | n uint | ||
31 | } | ||
32 | |||
33 | // Pause returns the amount of time the caller should wait. | ||
34 | func (eb *ExponentialBackoff) Pause() (time.Duration, bool) { | ||
35 | if eb.total > eb.Max { | ||
36 | return 0, false | ||
37 | } | ||
38 | |||
39 | // The next pause is selected from randomly from [0, 2^n * Base). | ||
40 | d := time.Duration(rand.Int63n((1 << eb.n) * int64(eb.Base))) | ||
41 | eb.total += d | ||
42 | eb.n++ | ||
43 | return d, true | ||
44 | } | ||
45 | |||
46 | // Reset resets the backoff strategy such that the next Pause call will begin | ||
47 | // counting from the start. It is not safe to call concurrently with Pause. | ||
48 | func (eb *ExponentialBackoff) Reset() { | ||
49 | eb.n = 0 | ||
50 | eb.total = 0 | ||
51 | } | ||
diff --git a/vendor/google.golang.org/api/gensupport/buffer.go b/vendor/google.golang.org/api/gensupport/buffer.go new file mode 100644 index 0000000..3d0817e --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/buffer.go | |||
@@ -0,0 +1,79 @@ | |||
1 | // Copyright 2016 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package gensupport | ||
6 | |||
7 | import ( | ||
8 | "bytes" | ||
9 | "io" | ||
10 | |||
11 | "google.golang.org/api/googleapi" | ||
12 | ) | ||
13 | |||
14 | // MediaBuffer buffers data from an io.Reader to support uploading media in | ||
15 | // retryable chunks. It should be created with NewMediaBuffer. | ||
16 | type MediaBuffer struct { | ||
17 | media io.Reader | ||
18 | |||
19 | chunk []byte // The current chunk which is pending upload. The capacity is the chunk size. | ||
20 | err error // Any error generated when populating chunk by reading media. | ||
21 | |||
22 | // The absolute position of chunk in the underlying media. | ||
23 | off int64 | ||
24 | } | ||
25 | |||
26 | // NewMediaBuffer initializes a MediaBuffer. | ||
27 | func NewMediaBuffer(media io.Reader, chunkSize int) *MediaBuffer { | ||
28 | return &MediaBuffer{media: media, chunk: make([]byte, 0, chunkSize)} | ||
29 | } | ||
30 | |||
31 | // Chunk returns the current buffered chunk, the offset in the underlying media | ||
32 | // from which the chunk is drawn, and the size of the chunk. | ||
33 | // Successive calls to Chunk return the same chunk between calls to Next. | ||
34 | func (mb *MediaBuffer) Chunk() (chunk io.Reader, off int64, size int, err error) { | ||
35 | // There may already be data in chunk if Next has not been called since the previous call to Chunk. | ||
36 | if mb.err == nil && len(mb.chunk) == 0 { | ||
37 | mb.err = mb.loadChunk() | ||
38 | } | ||
39 | return bytes.NewReader(mb.chunk), mb.off, len(mb.chunk), mb.err | ||
40 | } | ||
41 | |||
42 | // loadChunk will read from media into chunk, up to the capacity of chunk. | ||
43 | func (mb *MediaBuffer) loadChunk() error { | ||
44 | bufSize := cap(mb.chunk) | ||
45 | mb.chunk = mb.chunk[:bufSize] | ||
46 | |||
47 | read := 0 | ||
48 | var err error | ||
49 | for err == nil && read < bufSize { | ||
50 | var n int | ||
51 | n, err = mb.media.Read(mb.chunk[read:]) | ||
52 | read += n | ||
53 | } | ||
54 | mb.chunk = mb.chunk[:read] | ||
55 | return err | ||
56 | } | ||
57 | |||
58 | // Next advances to the next chunk, which will be returned by the next call to Chunk. | ||
59 | // Calls to Next without a corresponding prior call to Chunk will have no effect. | ||
60 | func (mb *MediaBuffer) Next() { | ||
61 | mb.off += int64(len(mb.chunk)) | ||
62 | mb.chunk = mb.chunk[0:0] | ||
63 | } | ||
64 | |||
65 | type readerTyper struct { | ||
66 | io.Reader | ||
67 | googleapi.ContentTyper | ||
68 | } | ||
69 | |||
70 | // ReaderAtToReader adapts a ReaderAt to be used as a Reader. | ||
71 | // If ra implements googleapi.ContentTyper, then the returned reader | ||
72 | // will also implement googleapi.ContentTyper, delegating to ra. | ||
73 | func ReaderAtToReader(ra io.ReaderAt, size int64) io.Reader { | ||
74 | r := io.NewSectionReader(ra, 0, size) | ||
75 | if typer, ok := ra.(googleapi.ContentTyper); ok { | ||
76 | return readerTyper{r, typer} | ||
77 | } | ||
78 | return r | ||
79 | } | ||
diff --git a/vendor/google.golang.org/api/gensupport/doc.go b/vendor/google.golang.org/api/gensupport/doc.go new file mode 100644 index 0000000..752c4b4 --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/doc.go | |||
@@ -0,0 +1,10 @@ | |||
1 | // Copyright 2016 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | // Package gensupport is an internal implementation detail used by code | ||
6 | // generated by the google-api-go-generator tool. | ||
7 | // | ||
8 | // This package may be modified at any time without regard for backwards | ||
9 | // compatibility. It should not be used directly by API users. | ||
10 | package gensupport | ||
diff --git a/vendor/google.golang.org/api/gensupport/header.go b/vendor/google.golang.org/api/gensupport/header.go new file mode 100644 index 0000000..cb5e67c --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/header.go | |||
@@ -0,0 +1,22 @@ | |||
1 | // Copyright 2017 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package gensupport | ||
6 | |||
7 | import ( | ||
8 | "fmt" | ||
9 | "runtime" | ||
10 | "strings" | ||
11 | ) | ||
12 | |||
13 | // GoogleClientHeader returns the value to use for the x-goog-api-client | ||
14 | // header, which is used internally by Google. | ||
15 | func GoogleClientHeader(generatorVersion, clientElement string) string { | ||
16 | elts := []string{"gl-go/" + strings.Replace(runtime.Version(), " ", "_", -1)} | ||
17 | if clientElement != "" { | ||
18 | elts = append(elts, clientElement) | ||
19 | } | ||
20 | elts = append(elts, fmt.Sprintf("gdcl/%s", generatorVersion)) | ||
21 | return strings.Join(elts, " ") | ||
22 | } | ||
diff --git a/vendor/google.golang.org/api/gensupport/json.go b/vendor/google.golang.org/api/gensupport/json.go new file mode 100644 index 0000000..c01e321 --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/json.go | |||
@@ -0,0 +1,211 @@ | |||
1 | // Copyright 2015 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package gensupport | ||
6 | |||
7 | import ( | ||
8 | "encoding/json" | ||
9 | "fmt" | ||
10 | "reflect" | ||
11 | "strings" | ||
12 | ) | ||
13 | |||
14 | // MarshalJSON returns a JSON encoding of schema containing only selected fields. | ||
15 | // A field is selected if any of the following is true: | ||
16 | // * it has a non-empty value | ||
17 | // * its field name is present in forceSendFields and it is not a nil pointer or nil interface | ||
18 | // * its field name is present in nullFields. | ||
19 | // The JSON key for each selected field is taken from the field's json: struct tag. | ||
20 | func MarshalJSON(schema interface{}, forceSendFields, nullFields []string) ([]byte, error) { | ||
21 | if len(forceSendFields) == 0 && len(nullFields) == 0 { | ||
22 | return json.Marshal(schema) | ||
23 | } | ||
24 | |||
25 | mustInclude := make(map[string]bool) | ||
26 | for _, f := range forceSendFields { | ||
27 | mustInclude[f] = true | ||
28 | } | ||
29 | useNull := make(map[string]bool) | ||
30 | useNullMaps := make(map[string]map[string]bool) | ||
31 | for _, nf := range nullFields { | ||
32 | parts := strings.SplitN(nf, ".", 2) | ||
33 | field := parts[0] | ||
34 | if len(parts) == 1 { | ||
35 | useNull[field] = true | ||
36 | } else { | ||
37 | if useNullMaps[field] == nil { | ||
38 | useNullMaps[field] = map[string]bool{} | ||
39 | } | ||
40 | useNullMaps[field][parts[1]] = true | ||
41 | } | ||
42 | } | ||
43 | |||
44 | dataMap, err := schemaToMap(schema, mustInclude, useNull, useNullMaps) | ||
45 | if err != nil { | ||
46 | return nil, err | ||
47 | } | ||
48 | return json.Marshal(dataMap) | ||
49 | } | ||
50 | |||
51 | func schemaToMap(schema interface{}, mustInclude, useNull map[string]bool, useNullMaps map[string]map[string]bool) (map[string]interface{}, error) { | ||
52 | m := make(map[string]interface{}) | ||
53 | s := reflect.ValueOf(schema) | ||
54 | st := s.Type() | ||
55 | |||
56 | for i := 0; i < s.NumField(); i++ { | ||
57 | jsonTag := st.Field(i).Tag.Get("json") | ||
58 | if jsonTag == "" { | ||
59 | continue | ||
60 | } | ||
61 | tag, err := parseJSONTag(jsonTag) | ||
62 | if err != nil { | ||
63 | return nil, err | ||
64 | } | ||
65 | if tag.ignore { | ||
66 | continue | ||
67 | } | ||
68 | |||
69 | v := s.Field(i) | ||
70 | f := st.Field(i) | ||
71 | |||
72 | if useNull[f.Name] { | ||
73 | if !isEmptyValue(v) { | ||
74 | return nil, fmt.Errorf("field %q in NullFields has non-empty value", f.Name) | ||
75 | } | ||
76 | m[tag.apiName] = nil | ||
77 | continue | ||
78 | } | ||
79 | |||
80 | if !includeField(v, f, mustInclude) { | ||
81 | continue | ||
82 | } | ||
83 | |||
84 | // If map fields are explicitly set to null, use a map[string]interface{}. | ||
85 | if f.Type.Kind() == reflect.Map && useNullMaps[f.Name] != nil { | ||
86 | ms, ok := v.Interface().(map[string]string) | ||
87 | if !ok { | ||
88 | return nil, fmt.Errorf("field %q has keys in NullFields but is not a map[string]string", f.Name) | ||
89 | } | ||
90 | mi := map[string]interface{}{} | ||
91 | for k, v := range ms { | ||
92 | mi[k] = v | ||
93 | } | ||
94 | for k := range useNullMaps[f.Name] { | ||
95 | mi[k] = nil | ||
96 | } | ||
97 | m[tag.apiName] = mi | ||
98 | continue | ||
99 | } | ||
100 | |||
101 | // nil maps are treated as empty maps. | ||
102 | if f.Type.Kind() == reflect.Map && v.IsNil() { | ||
103 | m[tag.apiName] = map[string]string{} | ||
104 | continue | ||
105 | } | ||
106 | |||
107 | // nil slices are treated as empty slices. | ||
108 | if f.Type.Kind() == reflect.Slice && v.IsNil() { | ||
109 | m[tag.apiName] = []bool{} | ||
110 | continue | ||
111 | } | ||
112 | |||
113 | if tag.stringFormat { | ||
114 | m[tag.apiName] = formatAsString(v, f.Type.Kind()) | ||
115 | } else { | ||
116 | m[tag.apiName] = v.Interface() | ||
117 | } | ||
118 | } | ||
119 | return m, nil | ||
120 | } | ||
121 | |||
122 | // formatAsString returns a string representation of v, dereferencing it first if possible. | ||
123 | func formatAsString(v reflect.Value, kind reflect.Kind) string { | ||
124 | if kind == reflect.Ptr && !v.IsNil() { | ||
125 | v = v.Elem() | ||
126 | } | ||
127 | |||
128 | return fmt.Sprintf("%v", v.Interface()) | ||
129 | } | ||
130 | |||
131 | // jsonTag represents a restricted version of the struct tag format used by encoding/json. | ||
132 | // It is used to describe the JSON encoding of fields in a Schema struct. | ||
133 | type jsonTag struct { | ||
134 | apiName string | ||
135 | stringFormat bool | ||
136 | ignore bool | ||
137 | } | ||
138 | |||
139 | // parseJSONTag parses a restricted version of the struct tag format used by encoding/json. | ||
140 | // The format of the tag must match that generated by the Schema.writeSchemaStruct method | ||
141 | // in the api generator. | ||
142 | func parseJSONTag(val string) (jsonTag, error) { | ||
143 | if val == "-" { | ||
144 | return jsonTag{ignore: true}, nil | ||
145 | } | ||
146 | |||
147 | var tag jsonTag | ||
148 | |||
149 | i := strings.Index(val, ",") | ||
150 | if i == -1 || val[:i] == "" { | ||
151 | return tag, fmt.Errorf("malformed json tag: %s", val) | ||
152 | } | ||
153 | |||
154 | tag = jsonTag{ | ||
155 | apiName: val[:i], | ||
156 | } | ||
157 | |||
158 | switch val[i+1:] { | ||
159 | case "omitempty": | ||
160 | case "omitempty,string": | ||
161 | tag.stringFormat = true | ||
162 | default: | ||
163 | return tag, fmt.Errorf("malformed json tag: %s", val) | ||
164 | } | ||
165 | |||
166 | return tag, nil | ||
167 | } | ||
168 | |||
169 | // Reports whether the struct field "f" with value "v" should be included in JSON output. | ||
170 | func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string]bool) bool { | ||
171 | // The regular JSON encoding of a nil pointer is "null", which means "delete this field". | ||
172 | // Therefore, we could enable field deletion by honoring pointer fields' presence in the mustInclude set. | ||
173 | // However, many fields are not pointers, so there would be no way to delete these fields. | ||
174 | // Rather than partially supporting field deletion, we ignore mustInclude for nil pointer fields. | ||
175 | // Deletion will be handled by a separate mechanism. | ||
176 | if f.Type.Kind() == reflect.Ptr && v.IsNil() { | ||
177 | return false | ||
178 | } | ||
179 | |||
180 | // The "any" type is represented as an interface{}. If this interface | ||
181 | // is nil, there is no reasonable representation to send. We ignore | ||
182 | // these fields, for the same reasons as given above for pointers. | ||
183 | if f.Type.Kind() == reflect.Interface && v.IsNil() { | ||
184 | return false | ||
185 | } | ||
186 | |||
187 | return mustInclude[f.Name] || !isEmptyValue(v) | ||
188 | } | ||
189 | |||
190 | // isEmptyValue reports whether v is the empty value for its type. This | ||
191 | // implementation is based on that of the encoding/json package, but its | ||
192 | // correctness does not depend on it being identical. What's important is that | ||
193 | // this function return false in situations where v should not be sent as part | ||
194 | // of a PATCH operation. | ||
195 | func isEmptyValue(v reflect.Value) bool { | ||
196 | switch v.Kind() { | ||
197 | case reflect.Array, reflect.Map, reflect.Slice, reflect.String: | ||
198 | return v.Len() == 0 | ||
199 | case reflect.Bool: | ||
200 | return !v.Bool() | ||
201 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||
202 | return v.Int() == 0 | ||
203 | case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||
204 | return v.Uint() == 0 | ||
205 | case reflect.Float32, reflect.Float64: | ||
206 | return v.Float() == 0 | ||
207 | case reflect.Interface, reflect.Ptr: | ||
208 | return v.IsNil() | ||
209 | } | ||
210 | return false | ||
211 | } | ||
diff --git a/vendor/google.golang.org/api/gensupport/jsonfloat.go b/vendor/google.golang.org/api/gensupport/jsonfloat.go new file mode 100644 index 0000000..8377850 --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/jsonfloat.go | |||
@@ -0,0 +1,57 @@ | |||
1 | // Copyright 2016 Google LLC | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // | ||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | // See the License for the specific language governing permissions and | ||
13 | // limitations under the License. | ||
14 | |||
15 | package gensupport | ||
16 | |||
17 | import ( | ||
18 | "encoding/json" | ||
19 | "errors" | ||
20 | "fmt" | ||
21 | "math" | ||
22 | ) | ||
23 | |||
24 | // JSONFloat64 is a float64 that supports proper unmarshaling of special float | ||
25 | // values in JSON, according to | ||
26 | // https://developers.google.com/protocol-buffers/docs/proto3#json. Although | ||
27 | // that is a proto-to-JSON spec, it applies to all Google APIs. | ||
28 | // | ||
29 | // The jsonpb package | ||
30 | // (https://github.com/golang/protobuf/blob/master/jsonpb/jsonpb.go) has | ||
31 | // similar functionality, but only for direct translation from proto messages | ||
32 | // to JSON. | ||
33 | type JSONFloat64 float64 | ||
34 | |||
35 | func (f *JSONFloat64) UnmarshalJSON(data []byte) error { | ||
36 | var ff float64 | ||
37 | if err := json.Unmarshal(data, &ff); err == nil { | ||
38 | *f = JSONFloat64(ff) | ||
39 | return nil | ||
40 | } | ||
41 | var s string | ||
42 | if err := json.Unmarshal(data, &s); err == nil { | ||
43 | switch s { | ||
44 | case "NaN": | ||
45 | ff = math.NaN() | ||
46 | case "Infinity": | ||
47 | ff = math.Inf(1) | ||
48 | case "-Infinity": | ||
49 | ff = math.Inf(-1) | ||
50 | default: | ||
51 | return fmt.Errorf("google.golang.org/api/internal: bad float string %q", s) | ||
52 | } | ||
53 | *f = JSONFloat64(ff) | ||
54 | return nil | ||
55 | } | ||
56 | return errors.New("google.golang.org/api/internal: data not float or string") | ||
57 | } | ||
diff --git a/vendor/google.golang.org/api/gensupport/media.go b/vendor/google.golang.org/api/gensupport/media.go new file mode 100644 index 0000000..4cef4ad --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/media.go | |||
@@ -0,0 +1,342 @@ | |||
1 | // Copyright 2016 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package gensupport | ||
6 | |||
7 | import ( | ||
8 | "bytes" | ||
9 | "fmt" | ||
10 | "io" | ||
11 | "io/ioutil" | ||
12 | "mime/multipart" | ||
13 | "net/http" | ||
14 | "net/textproto" | ||
15 | "strings" | ||
16 | "sync" | ||
17 | |||
18 | "google.golang.org/api/googleapi" | ||
19 | ) | ||
20 | |||
21 | const sniffBuffSize = 512 | ||
22 | |||
23 | func newContentSniffer(r io.Reader) *contentSniffer { | ||
24 | return &contentSniffer{r: r} | ||
25 | } | ||
26 | |||
27 | // contentSniffer wraps a Reader, and reports the content type determined by sniffing up to 512 bytes from the Reader. | ||
28 | type contentSniffer struct { | ||
29 | r io.Reader | ||
30 | start []byte // buffer for the sniffed bytes. | ||
31 | err error // set to any error encountered while reading bytes to be sniffed. | ||
32 | |||
33 | ctype string // set on first sniff. | ||
34 | sniffed bool // set to true on first sniff. | ||
35 | } | ||
36 | |||
37 | func (cs *contentSniffer) Read(p []byte) (n int, err error) { | ||
38 | // Ensure that the content type is sniffed before any data is consumed from Reader. | ||
39 | _, _ = cs.ContentType() | ||
40 | |||
41 | if len(cs.start) > 0 { | ||
42 | n := copy(p, cs.start) | ||
43 | cs.start = cs.start[n:] | ||
44 | return n, nil | ||
45 | } | ||
46 | |||
47 | // We may have read some bytes into start while sniffing, even if the read ended in an error. | ||
48 | // We should first return those bytes, then the error. | ||
49 | if cs.err != nil { | ||
50 | return 0, cs.err | ||
51 | } | ||
52 | |||
53 | // Now we have handled all bytes that were buffered while sniffing. Now just delegate to the underlying reader. | ||
54 | return cs.r.Read(p) | ||
55 | } | ||
56 | |||
57 | // ContentType returns the sniffed content type, and whether the content type was succesfully sniffed. | ||
58 | func (cs *contentSniffer) ContentType() (string, bool) { | ||
59 | if cs.sniffed { | ||
60 | return cs.ctype, cs.ctype != "" | ||
61 | } | ||
62 | cs.sniffed = true | ||
63 | // If ReadAll hits EOF, it returns err==nil. | ||
64 | cs.start, cs.err = ioutil.ReadAll(io.LimitReader(cs.r, sniffBuffSize)) | ||
65 | |||
66 | // Don't try to detect the content type based on possibly incomplete data. | ||
67 | if cs.err != nil { | ||
68 | return "", false | ||
69 | } | ||
70 | |||
71 | cs.ctype = http.DetectContentType(cs.start) | ||
72 | return cs.ctype, true | ||
73 | } | ||
74 | |||
75 | // DetermineContentType determines the content type of the supplied reader. | ||
76 | // If the content type is already known, it can be specified via ctype. | ||
77 | // Otherwise, the content of media will be sniffed to determine the content type. | ||
78 | // If media implements googleapi.ContentTyper (deprecated), this will be used | ||
79 | // instead of sniffing the content. | ||
80 | // After calling DetectContentType the caller must not perform further reads on | ||
81 | // media, but rather read from the Reader that is returned. | ||
82 | func DetermineContentType(media io.Reader, ctype string) (io.Reader, string) { | ||
83 | // Note: callers could avoid calling DetectContentType if ctype != "", | ||
84 | // but doing the check inside this function reduces the amount of | ||
85 | // generated code. | ||
86 | if ctype != "" { | ||
87 | return media, ctype | ||
88 | } | ||
89 | |||
90 | // For backwards compatability, allow clients to set content | ||
91 | // type by providing a ContentTyper for media. | ||
92 | if typer, ok := media.(googleapi.ContentTyper); ok { | ||
93 | return media, typer.ContentType() | ||
94 | } | ||
95 | |||
96 | sniffer := newContentSniffer(media) | ||
97 | if ctype, ok := sniffer.ContentType(); ok { | ||
98 | return sniffer, ctype | ||
99 | } | ||
100 | // If content type could not be sniffed, reads from sniffer will eventually fail with an error. | ||
101 | return sniffer, "" | ||
102 | } | ||
103 | |||
104 | type typeReader struct { | ||
105 | io.Reader | ||
106 | typ string | ||
107 | } | ||
108 | |||
109 | // multipartReader combines the contents of multiple readers to create a multipart/related HTTP body. | ||
110 | // Close must be called if reads from the multipartReader are abandoned before reaching EOF. | ||
111 | type multipartReader struct { | ||
112 | pr *io.PipeReader | ||
113 | ctype string | ||
114 | mu sync.Mutex | ||
115 | pipeOpen bool | ||
116 | } | ||
117 | |||
118 | func newMultipartReader(parts []typeReader) *multipartReader { | ||
119 | mp := &multipartReader{pipeOpen: true} | ||
120 | var pw *io.PipeWriter | ||
121 | mp.pr, pw = io.Pipe() | ||
122 | mpw := multipart.NewWriter(pw) | ||
123 | mp.ctype = "multipart/related; boundary=" + mpw.Boundary() | ||
124 | go func() { | ||
125 | for _, part := range parts { | ||
126 | w, err := mpw.CreatePart(typeHeader(part.typ)) | ||
127 | if err != nil { | ||
128 | mpw.Close() | ||
129 | pw.CloseWithError(fmt.Errorf("googleapi: CreatePart failed: %v", err)) | ||
130 | return | ||
131 | } | ||
132 | _, err = io.Copy(w, part.Reader) | ||
133 | if err != nil { | ||
134 | mpw.Close() | ||
135 | pw.CloseWithError(fmt.Errorf("googleapi: Copy failed: %v", err)) | ||
136 | return | ||
137 | } | ||
138 | } | ||
139 | |||
140 | mpw.Close() | ||
141 | pw.Close() | ||
142 | }() | ||
143 | return mp | ||
144 | } | ||
145 | |||
146 | func (mp *multipartReader) Read(data []byte) (n int, err error) { | ||
147 | return mp.pr.Read(data) | ||
148 | } | ||
149 | |||
150 | func (mp *multipartReader) Close() error { | ||
151 | mp.mu.Lock() | ||
152 | if !mp.pipeOpen { | ||
153 | mp.mu.Unlock() | ||
154 | return nil | ||
155 | } | ||
156 | mp.pipeOpen = false | ||
157 | mp.mu.Unlock() | ||
158 | return mp.pr.Close() | ||
159 | } | ||
160 | |||
161 | // CombineBodyMedia combines a json body with media content to create a multipart/related HTTP body. | ||
162 | // It returns a ReadCloser containing the combined body, and the overall "multipart/related" content type, with random boundary. | ||
163 | // | ||
164 | // The caller must call Close on the returned ReadCloser if reads are abandoned before reaching EOF. | ||
165 | func CombineBodyMedia(body io.Reader, bodyContentType string, media io.Reader, mediaContentType string) (io.ReadCloser, string) { | ||
166 | mp := newMultipartReader([]typeReader{ | ||
167 | {body, bodyContentType}, | ||
168 | {media, mediaContentType}, | ||
169 | }) | ||
170 | return mp, mp.ctype | ||
171 | } | ||
172 | |||
173 | func typeHeader(contentType string) textproto.MIMEHeader { | ||
174 | h := make(textproto.MIMEHeader) | ||
175 | if contentType != "" { | ||
176 | h.Set("Content-Type", contentType) | ||
177 | } | ||
178 | return h | ||
179 | } | ||
180 | |||
181 | // PrepareUpload determines whether the data in the supplied reader should be | ||
182 | // uploaded in a single request, or in sequential chunks. | ||
183 | // chunkSize is the size of the chunk that media should be split into. | ||
184 | // | ||
185 | // If chunkSize is zero, media is returned as the first value, and the other | ||
186 | // two return values are nil, true. | ||
187 | // | ||
188 | // Otherwise, a MediaBuffer is returned, along with a bool indicating whether the | ||
189 | // contents of media fit in a single chunk. | ||
190 | // | ||
191 | // After PrepareUpload has been called, media should no longer be used: the | ||
192 | // media content should be accessed via one of the return values. | ||
193 | func PrepareUpload(media io.Reader, chunkSize int) (r io.Reader, mb *MediaBuffer, singleChunk bool) { | ||
194 | if chunkSize == 0 { // do not chunk | ||
195 | return media, nil, true | ||
196 | } | ||
197 | mb = NewMediaBuffer(media, chunkSize) | ||
198 | _, _, _, err := mb.Chunk() | ||
199 | // If err is io.EOF, we can upload this in a single request. Otherwise, err is | ||
200 | // either nil or a non-EOF error. If it is the latter, then the next call to | ||
201 | // mb.Chunk will return the same error. Returning a MediaBuffer ensures that this | ||
202 | // error will be handled at some point. | ||
203 | return nil, mb, err == io.EOF | ||
204 | } | ||
205 | |||
206 | // MediaInfo holds information for media uploads. It is intended for use by generated | ||
207 | // code only. | ||
208 | type MediaInfo struct { | ||
209 | // At most one of Media and MediaBuffer will be set. | ||
210 | media io.Reader | ||
211 | buffer *MediaBuffer | ||
212 | singleChunk bool | ||
213 | mType string | ||
214 | size int64 // mediaSize, if known. Used only for calls to progressUpdater_. | ||
215 | progressUpdater googleapi.ProgressUpdater | ||
216 | } | ||
217 | |||
218 | // NewInfoFromMedia should be invoked from the Media method of a call. It returns a | ||
219 | // MediaInfo populated with chunk size and content type, and a reader or MediaBuffer | ||
220 | // if needed. | ||
221 | func NewInfoFromMedia(r io.Reader, options []googleapi.MediaOption) *MediaInfo { | ||
222 | mi := &MediaInfo{} | ||
223 | opts := googleapi.ProcessMediaOptions(options) | ||
224 | if !opts.ForceEmptyContentType { | ||
225 | r, mi.mType = DetermineContentType(r, opts.ContentType) | ||
226 | } | ||
227 | mi.media, mi.buffer, mi.singleChunk = PrepareUpload(r, opts.ChunkSize) | ||
228 | return mi | ||
229 | } | ||
230 | |||
231 | // NewInfoFromResumableMedia should be invoked from the ResumableMedia method of a | ||
232 | // call. It returns a MediaInfo using the given reader, size and media type. | ||
233 | func NewInfoFromResumableMedia(r io.ReaderAt, size int64, mediaType string) *MediaInfo { | ||
234 | rdr := ReaderAtToReader(r, size) | ||
235 | rdr, mType := DetermineContentType(rdr, mediaType) | ||
236 | return &MediaInfo{ | ||
237 | size: size, | ||
238 | mType: mType, | ||
239 | buffer: NewMediaBuffer(rdr, googleapi.DefaultUploadChunkSize), | ||
240 | media: nil, | ||
241 | singleChunk: false, | ||
242 | } | ||
243 | } | ||
244 | |||
245 | // SetProgressUpdater sets the progress updater for the media info. | ||
246 | func (mi *MediaInfo) SetProgressUpdater(pu googleapi.ProgressUpdater) { | ||
247 | if mi != nil { | ||
248 | mi.progressUpdater = pu | ||
249 | } | ||
250 | } | ||
251 | |||
252 | // UploadType determines the type of upload: a single request, or a resumable | ||
253 | // series of requests. | ||
254 | func (mi *MediaInfo) UploadType() string { | ||
255 | if mi.singleChunk { | ||
256 | return "multipart" | ||
257 | } | ||
258 | return "resumable" | ||
259 | } | ||
260 | |||
261 | // UploadRequest sets up an HTTP request for media upload. It adds headers | ||
262 | // as necessary, and returns a replacement for the body and a function for http.Request.GetBody. | ||
263 | func (mi *MediaInfo) UploadRequest(reqHeaders http.Header, body io.Reader) (newBody io.Reader, getBody func() (io.ReadCloser, error), cleanup func()) { | ||
264 | cleanup = func() {} | ||
265 | if mi == nil { | ||
266 | return body, nil, cleanup | ||
267 | } | ||
268 | var media io.Reader | ||
269 | if mi.media != nil { | ||
270 | // This only happens when the caller has turned off chunking. In that | ||
271 | // case, we write all of media in a single non-retryable request. | ||
272 | media = mi.media | ||
273 | } else if mi.singleChunk { | ||
274 | // The data fits in a single chunk, which has now been read into the MediaBuffer. | ||
275 | // We obtain that chunk so we can write it in a single request. The request can | ||
276 | // be retried because the data is stored in the MediaBuffer. | ||
277 | media, _, _, _ = mi.buffer.Chunk() | ||
278 | } | ||
279 | if media != nil { | ||
280 | fb := readerFunc(body) | ||
281 | fm := readerFunc(media) | ||
282 | combined, ctype := CombineBodyMedia(body, "application/json", media, mi.mType) | ||
283 | if fb != nil && fm != nil { | ||
284 | getBody = func() (io.ReadCloser, error) { | ||
285 | rb := ioutil.NopCloser(fb()) | ||
286 | rm := ioutil.NopCloser(fm()) | ||
287 | r, _ := CombineBodyMedia(rb, "application/json", rm, mi.mType) | ||
288 | return r, nil | ||
289 | } | ||
290 | } | ||
291 | cleanup = func() { combined.Close() } | ||
292 | reqHeaders.Set("Content-Type", ctype) | ||
293 | body = combined | ||
294 | } | ||
295 | if mi.buffer != nil && mi.mType != "" && !mi.singleChunk { | ||
296 | reqHeaders.Set("X-Upload-Content-Type", mi.mType) | ||
297 | } | ||
298 | return body, getBody, cleanup | ||
299 | } | ||
300 | |||
301 | // readerFunc returns a function that always returns an io.Reader that has the same | ||
302 | // contents as r, provided that can be done without consuming r. Otherwise, it | ||
303 | // returns nil. | ||
304 | // See http.NewRequest (in net/http/request.go). | ||
305 | func readerFunc(r io.Reader) func() io.Reader { | ||
306 | switch r := r.(type) { | ||
307 | case *bytes.Buffer: | ||
308 | buf := r.Bytes() | ||
309 | return func() io.Reader { return bytes.NewReader(buf) } | ||
310 | case *bytes.Reader: | ||
311 | snapshot := *r | ||
312 | return func() io.Reader { r := snapshot; return &r } | ||
313 | case *strings.Reader: | ||
314 | snapshot := *r | ||
315 | return func() io.Reader { r := snapshot; return &r } | ||
316 | default: | ||
317 | return nil | ||
318 | } | ||
319 | } | ||
320 | |||
321 | // ResumableUpload returns an appropriately configured ResumableUpload value if the | ||
322 | // upload is resumable, or nil otherwise. | ||
323 | func (mi *MediaInfo) ResumableUpload(locURI string) *ResumableUpload { | ||
324 | if mi == nil || mi.singleChunk { | ||
325 | return nil | ||
326 | } | ||
327 | return &ResumableUpload{ | ||
328 | URI: locURI, | ||
329 | Media: mi.buffer, | ||
330 | MediaType: mi.mType, | ||
331 | Callback: func(curr int64) { | ||
332 | if mi.progressUpdater != nil { | ||
333 | mi.progressUpdater(curr, mi.size) | ||
334 | } | ||
335 | }, | ||
336 | } | ||
337 | } | ||
338 | |||
339 | // SetGetBody sets the GetBody field of req to f. | ||
340 | func SetGetBody(req *http.Request, f func() (io.ReadCloser, error)) { | ||
341 | req.GetBody = f | ||
342 | } | ||
diff --git a/vendor/google.golang.org/api/gensupport/params.go b/vendor/google.golang.org/api/gensupport/params.go new file mode 100644 index 0000000..0e878a4 --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/params.go | |||
@@ -0,0 +1,51 @@ | |||
1 | // Copyright 2015 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package gensupport | ||
6 | |||
7 | import ( | ||
8 | "net/url" | ||
9 | |||
10 | "google.golang.org/api/googleapi" | ||
11 | ) | ||
12 | |||
13 | // URLParams is a simplified replacement for url.Values | ||
14 | // that safely builds up URL parameters for encoding. | ||
15 | type URLParams map[string][]string | ||
16 | |||
17 | // Get returns the first value for the given key, or "". | ||
18 | func (u URLParams) Get(key string) string { | ||
19 | vs := u[key] | ||
20 | if len(vs) == 0 { | ||
21 | return "" | ||
22 | } | ||
23 | return vs[0] | ||
24 | } | ||
25 | |||
26 | // Set sets the key to value. | ||
27 | // It replaces any existing values. | ||
28 | func (u URLParams) Set(key, value string) { | ||
29 | u[key] = []string{value} | ||
30 | } | ||
31 | |||
32 | // SetMulti sets the key to an array of values. | ||
33 | // It replaces any existing values. | ||
34 | // Note that values must not be modified after calling SetMulti | ||
35 | // so the caller is responsible for making a copy if necessary. | ||
36 | func (u URLParams) SetMulti(key string, values []string) { | ||
37 | u[key] = values | ||
38 | } | ||
39 | |||
40 | // Encode encodes the values into ``URL encoded'' form | ||
41 | // ("bar=baz&foo=quux") sorted by key. | ||
42 | func (u URLParams) Encode() string { | ||
43 | return url.Values(u).Encode() | ||
44 | } | ||
45 | |||
46 | // SetOptions sets the URL params and any additional call options. | ||
47 | func SetOptions(u URLParams, opts ...googleapi.CallOption) { | ||
48 | for _, o := range opts { | ||
49 | u.Set(o.Get()) | ||
50 | } | ||
51 | } | ||
diff --git a/vendor/google.golang.org/api/gensupport/resumable.go b/vendor/google.golang.org/api/gensupport/resumable.go new file mode 100644 index 0000000..2552a6a --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/resumable.go | |||
@@ -0,0 +1,216 @@ | |||
1 | // Copyright 2016 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package gensupport | ||
6 | |||
7 | import ( | ||
8 | "context" | ||
9 | "errors" | ||
10 | "fmt" | ||
11 | "io" | ||
12 | "net/http" | ||
13 | "sync" | ||
14 | "time" | ||
15 | ) | ||
16 | |||
17 | const ( | ||
18 | // statusTooManyRequests is returned by the storage API if the | ||
19 | // per-project limits have been temporarily exceeded. The request | ||
20 | // should be retried. | ||
21 | // https://cloud.google.com/storage/docs/json_api/v1/status-codes#standardcodes | ||
22 | statusTooManyRequests = 429 | ||
23 | ) | ||
24 | |||
25 | // ResumableUpload is used by the generated APIs to provide resumable uploads. | ||
26 | // It is not used by developers directly. | ||
27 | type ResumableUpload struct { | ||
28 | Client *http.Client | ||
29 | // URI is the resumable resource destination provided by the server after specifying "&uploadType=resumable". | ||
30 | URI string | ||
31 | UserAgent string // User-Agent for header of the request | ||
32 | // Media is the object being uploaded. | ||
33 | Media *MediaBuffer | ||
34 | // MediaType defines the media type, e.g. "image/jpeg". | ||
35 | MediaType string | ||
36 | |||
37 | mu sync.Mutex // guards progress | ||
38 | progress int64 // number of bytes uploaded so far | ||
39 | |||
40 | // Callback is an optional function that will be periodically called with the cumulative number of bytes uploaded. | ||
41 | Callback func(int64) | ||
42 | |||
43 | // If not specified, a default exponential backoff strategy will be used. | ||
44 | Backoff BackoffStrategy | ||
45 | } | ||
46 | |||
47 | // Progress returns the number of bytes uploaded at this point. | ||
48 | func (rx *ResumableUpload) Progress() int64 { | ||
49 | rx.mu.Lock() | ||
50 | defer rx.mu.Unlock() | ||
51 | return rx.progress | ||
52 | } | ||
53 | |||
54 | // doUploadRequest performs a single HTTP request to upload data. | ||
55 | // off specifies the offset in rx.Media from which data is drawn. | ||
56 | // size is the number of bytes in data. | ||
57 | // final specifies whether data is the final chunk to be uploaded. | ||
58 | func (rx *ResumableUpload) doUploadRequest(ctx context.Context, data io.Reader, off, size int64, final bool) (*http.Response, error) { | ||
59 | req, err := http.NewRequest("POST", rx.URI, data) | ||
60 | if err != nil { | ||
61 | return nil, err | ||
62 | } | ||
63 | |||
64 | req.ContentLength = size | ||
65 | var contentRange string | ||
66 | if final { | ||
67 | if size == 0 { | ||
68 | contentRange = fmt.Sprintf("bytes */%v", off) | ||
69 | } else { | ||
70 | contentRange = fmt.Sprintf("bytes %v-%v/%v", off, off+size-1, off+size) | ||
71 | } | ||
72 | } else { | ||
73 | contentRange = fmt.Sprintf("bytes %v-%v/*", off, off+size-1) | ||
74 | } | ||
75 | req.Header.Set("Content-Range", contentRange) | ||
76 | req.Header.Set("Content-Type", rx.MediaType) | ||
77 | req.Header.Set("User-Agent", rx.UserAgent) | ||
78 | |||
79 | // Google's upload endpoint uses status code 308 for a | ||
80 | // different purpose than the "308 Permanent Redirect" | ||
81 | // since-standardized in RFC 7238. Because of the conflict in | ||
82 | // semantics, Google added this new request header which | ||
83 | // causes it to not use "308" and instead reply with 200 OK | ||
84 | // and sets the upload-specific "X-HTTP-Status-Code-Override: | ||
85 | // 308" response header. | ||
86 | req.Header.Set("X-GUploader-No-308", "yes") | ||
87 | |||
88 | return SendRequest(ctx, rx.Client, req) | ||
89 | } | ||
90 | |||
91 | func statusResumeIncomplete(resp *http.Response) bool { | ||
92 | // This is how the server signals "status resume incomplete" | ||
93 | // when X-GUploader-No-308 is set to "yes": | ||
94 | return resp != nil && resp.Header.Get("X-Http-Status-Code-Override") == "308" | ||
95 | } | ||
96 | |||
97 | // reportProgress calls a user-supplied callback to report upload progress. | ||
98 | // If old==updated, the callback is not called. | ||
99 | func (rx *ResumableUpload) reportProgress(old, updated int64) { | ||
100 | if updated-old == 0 { | ||
101 | return | ||
102 | } | ||
103 | rx.mu.Lock() | ||
104 | rx.progress = updated | ||
105 | rx.mu.Unlock() | ||
106 | if rx.Callback != nil { | ||
107 | rx.Callback(updated) | ||
108 | } | ||
109 | } | ||
110 | |||
111 | // transferChunk performs a single HTTP request to upload a single chunk from rx.Media. | ||
112 | func (rx *ResumableUpload) transferChunk(ctx context.Context) (*http.Response, error) { | ||
113 | chunk, off, size, err := rx.Media.Chunk() | ||
114 | |||
115 | done := err == io.EOF | ||
116 | if !done && err != nil { | ||
117 | return nil, err | ||
118 | } | ||
119 | |||
120 | res, err := rx.doUploadRequest(ctx, chunk, off, int64(size), done) | ||
121 | if err != nil { | ||
122 | return res, err | ||
123 | } | ||
124 | |||
125 | // We sent "X-GUploader-No-308: yes" (see comment elsewhere in | ||
126 | // this file), so we don't expect to get a 308. | ||
127 | if res.StatusCode == 308 { | ||
128 | return nil, errors.New("unexpected 308 response status code") | ||
129 | } | ||
130 | |||
131 | if res.StatusCode == http.StatusOK { | ||
132 | rx.reportProgress(off, off+int64(size)) | ||
133 | } | ||
134 | |||
135 | if statusResumeIncomplete(res) { | ||
136 | rx.Media.Next() | ||
137 | } | ||
138 | return res, nil | ||
139 | } | ||
140 | |||
141 | func contextDone(ctx context.Context) bool { | ||
142 | select { | ||
143 | case <-ctx.Done(): | ||
144 | return true | ||
145 | default: | ||
146 | return false | ||
147 | } | ||
148 | } | ||
149 | |||
150 | // Upload starts the process of a resumable upload with a cancellable context. | ||
151 | // It retries using the provided back off strategy until cancelled or the | ||
152 | // strategy indicates to stop retrying. | ||
153 | // It is called from the auto-generated API code and is not visible to the user. | ||
154 | // Before sending an HTTP request, Upload calls any registered hook functions, | ||
155 | // and calls the returned functions after the request returns (see send.go). | ||
156 | // rx is private to the auto-generated API code. | ||
157 | // Exactly one of resp or err will be nil. If resp is non-nil, the caller must call resp.Body.Close. | ||
158 | func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err error) { | ||
159 | var pause time.Duration | ||
160 | backoff := rx.Backoff | ||
161 | if backoff == nil { | ||
162 | backoff = DefaultBackoffStrategy() | ||
163 | } | ||
164 | |||
165 | for { | ||
166 | // Ensure that we return in the case of cancelled context, even if pause is 0. | ||
167 | if contextDone(ctx) { | ||
168 | return nil, ctx.Err() | ||
169 | } | ||
170 | select { | ||
171 | case <-ctx.Done(): | ||
172 | return nil, ctx.Err() | ||
173 | case <-time.After(pause): | ||
174 | } | ||
175 | |||
176 | resp, err = rx.transferChunk(ctx) | ||
177 | |||
178 | var status int | ||
179 | if resp != nil { | ||
180 | status = resp.StatusCode | ||
181 | } | ||
182 | |||
183 | // Check if we should retry the request. | ||
184 | if shouldRetry(status, err) { | ||
185 | var retry bool | ||
186 | pause, retry = backoff.Pause() | ||
187 | if retry { | ||
188 | if resp != nil && resp.Body != nil { | ||
189 | resp.Body.Close() | ||
190 | } | ||
191 | continue | ||
192 | } | ||
193 | } | ||
194 | |||
195 | // If the chunk was uploaded successfully, but there's still | ||
196 | // more to go, upload the next chunk without any delay. | ||
197 | if statusResumeIncomplete(resp) { | ||
198 | pause = 0 | ||
199 | backoff.Reset() | ||
200 | resp.Body.Close() | ||
201 | continue | ||
202 | } | ||
203 | |||
204 | // It's possible for err and resp to both be non-nil here, but we expose a simpler | ||
205 | // contract to our callers: exactly one of resp and err will be non-nil. This means | ||
206 | // that any response body must be closed here before returning a non-nil error. | ||
207 | if err != nil { | ||
208 | if resp != nil && resp.Body != nil { | ||
209 | resp.Body.Close() | ||
210 | } | ||
211 | return nil, err | ||
212 | } | ||
213 | |||
214 | return resp, nil | ||
215 | } | ||
216 | } | ||
diff --git a/vendor/google.golang.org/api/gensupport/retry.go b/vendor/google.golang.org/api/gensupport/retry.go new file mode 100644 index 0000000..fdde3f4 --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/retry.go | |||
@@ -0,0 +1,84 @@ | |||
1 | // Copyright 2017 Google LLC | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // | ||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | // See the License for the specific language governing permissions and | ||
13 | // limitations under the License. | ||
14 | |||
15 | package gensupport | ||
16 | |||
17 | import ( | ||
18 | "context" | ||
19 | "io" | ||
20 | "net" | ||
21 | "net/http" | ||
22 | "time" | ||
23 | ) | ||
24 | |||
25 | // Retry invokes the given function, retrying it multiple times if the connection failed or | ||
26 | // the HTTP status response indicates the request should be attempted again. ctx may be nil. | ||
27 | func Retry(ctx context.Context, f func() (*http.Response, error), backoff BackoffStrategy) (*http.Response, error) { | ||
28 | for { | ||
29 | resp, err := f() | ||
30 | |||
31 | var status int | ||
32 | if resp != nil { | ||
33 | status = resp.StatusCode | ||
34 | } | ||
35 | |||
36 | // Return if we shouldn't retry. | ||
37 | pause, retry := backoff.Pause() | ||
38 | if !shouldRetry(status, err) || !retry { | ||
39 | return resp, err | ||
40 | } | ||
41 | |||
42 | // Ensure the response body is closed, if any. | ||
43 | if resp != nil && resp.Body != nil { | ||
44 | resp.Body.Close() | ||
45 | } | ||
46 | |||
47 | // Pause, but still listen to ctx.Done if context is not nil. | ||
48 | var done <-chan struct{} | ||
49 | if ctx != nil { | ||
50 | done = ctx.Done() | ||
51 | } | ||
52 | select { | ||
53 | case <-done: | ||
54 | return nil, ctx.Err() | ||
55 | case <-time.After(pause): | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | |||
60 | // DefaultBackoffStrategy returns a default strategy to use for retrying failed upload requests. | ||
61 | func DefaultBackoffStrategy() BackoffStrategy { | ||
62 | return &ExponentialBackoff{ | ||
63 | Base: 250 * time.Millisecond, | ||
64 | Max: 16 * time.Second, | ||
65 | } | ||
66 | } | ||
67 | |||
68 | // shouldRetry returns true if the HTTP response / error indicates that the | ||
69 | // request should be attempted again. | ||
70 | func shouldRetry(status int, err error) bool { | ||
71 | if 500 <= status && status <= 599 { | ||
72 | return true | ||
73 | } | ||
74 | if status == statusTooManyRequests { | ||
75 | return true | ||
76 | } | ||
77 | if err == io.ErrUnexpectedEOF { | ||
78 | return true | ||
79 | } | ||
80 | if err, ok := err.(net.Error); ok { | ||
81 | return err.Temporary() | ||
82 | } | ||
83 | return false | ||
84 | } | ||
diff --git a/vendor/google.golang.org/api/gensupport/send.go b/vendor/google.golang.org/api/gensupport/send.go new file mode 100644 index 0000000..5799393 --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/send.go | |||
@@ -0,0 +1,87 @@ | |||
1 | // Copyright 2016 The Go Authors. All rights reserved. | ||
2 | // Use of this source code is governed by a BSD-style | ||
3 | // license that can be found in the LICENSE file. | ||
4 | |||
5 | package gensupport | ||
6 | |||
7 | import ( | ||
8 | "context" | ||
9 | "encoding/json" | ||
10 | "errors" | ||
11 | "net/http" | ||
12 | ) | ||
13 | |||
14 | // Hook is the type of a function that is called once before each HTTP request | ||
15 | // that is sent by a generated API. It returns a function that is called after | ||
16 | // the request returns. | ||
17 | // Hooks are not called if the context is nil. | ||
18 | type Hook func(ctx context.Context, req *http.Request) func(resp *http.Response) | ||
19 | |||
20 | var hooks []Hook | ||
21 | |||
22 | // RegisterHook registers a Hook to be called before each HTTP request by a | ||
23 | // generated API. Hooks are called in the order they are registered. Each | ||
24 | // hook can return a function; if it is non-nil, it is called after the HTTP | ||
25 | // request returns. These functions are called in the reverse order. | ||
26 | // RegisterHook should not be called concurrently with itself or SendRequest. | ||
27 | func RegisterHook(h Hook) { | ||
28 | hooks = append(hooks, h) | ||
29 | } | ||
30 | |||
31 | // SendRequest sends a single HTTP request using the given client. | ||
32 | // If ctx is non-nil, it calls all hooks, then sends the request with | ||
33 | // req.WithContext, then calls any functions returned by the hooks in | ||
34 | // reverse order. | ||
35 | func SendRequest(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { | ||
36 | // Disallow Accept-Encoding because it interferes with the automatic gzip handling | ||
37 | // done by the default http.Transport. See https://github.com/google/google-api-go-client/issues/219. | ||
38 | if _, ok := req.Header["Accept-Encoding"]; ok { | ||
39 | return nil, errors.New("google api: custom Accept-Encoding headers not allowed") | ||
40 | } | ||
41 | if ctx == nil { | ||
42 | return client.Do(req) | ||
43 | } | ||
44 | // Call hooks in order of registration, store returned funcs. | ||
45 | post := make([]func(resp *http.Response), len(hooks)) | ||
46 | for i, h := range hooks { | ||
47 | fn := h(ctx, req) | ||
48 | post[i] = fn | ||
49 | } | ||
50 | |||
51 | // Send request. | ||
52 | resp, err := send(ctx, client, req) | ||
53 | |||
54 | // Call returned funcs in reverse order. | ||
55 | for i := len(post) - 1; i >= 0; i-- { | ||
56 | if fn := post[i]; fn != nil { | ||
57 | fn(resp) | ||
58 | } | ||
59 | } | ||
60 | return resp, err | ||
61 | } | ||
62 | |||
63 | func send(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { | ||
64 | if client == nil { | ||
65 | client = http.DefaultClient | ||
66 | } | ||
67 | resp, err := client.Do(req.WithContext(ctx)) | ||
68 | // If we got an error, and the context has been canceled, | ||
69 | // the context's error is probably more useful. | ||
70 | if err != nil { | ||
71 | select { | ||
72 | case <-ctx.Done(): | ||
73 | err = ctx.Err() | ||
74 | default: | ||
75 | } | ||
76 | } | ||
77 | return resp, err | ||
78 | } | ||
79 | |||
80 | // DecodeResponse decodes the body of res into target. If there is no body, | ||
81 | // target is unchanged. | ||
82 | func DecodeResponse(target interface{}, res *http.Response) error { | ||
83 | if res.StatusCode == http.StatusNoContent { | ||
84 | return nil | ||
85 | } | ||
86 | return json.NewDecoder(res.Body).Decode(target) | ||
87 | } | ||