aboutsummaryrefslogtreecommitdiff
path: root/vendor/google.golang.org/api/gensupport
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/google.golang.org/api/gensupport')
-rw-r--r--vendor/google.golang.org/api/gensupport/backoff.go46
-rw-r--r--vendor/google.golang.org/api/gensupport/buffer.go77
-rw-r--r--vendor/google.golang.org/api/gensupport/doc.go10
-rw-r--r--vendor/google.golang.org/api/gensupport/json.go172
-rw-r--r--vendor/google.golang.org/api/gensupport/media.go199
-rw-r--r--vendor/google.golang.org/api/gensupport/params.go50
-rw-r--r--vendor/google.golang.org/api/gensupport/resumable.go198
-rw-r--r--vendor/google.golang.org/api/gensupport/retry.go77
8 files changed, 829 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..1356140
--- /dev/null
+++ b/vendor/google.golang.org/api/gensupport/backoff.go
@@ -0,0 +1,46 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gensupport
+
+import (
+ "math/rand"
+ "time"
+)
+
+type BackoffStrategy interface {
+ // Pause returns the duration of the next pause and true if the operation should be
+ // retried, or false if no further retries should be attempted.
+ Pause() (time.Duration, bool)
+
+ // Reset restores the strategy to its initial state.
+ Reset()
+}
+
+// ExponentialBackoff performs exponential backoff as per https://en.wikipedia.org/wiki/Exponential_backoff.
+// The initial pause time is given by Base.
+// Once the total pause time exceeds Max, Pause will indicate no further retries.
+type ExponentialBackoff struct {
+ Base time.Duration
+ Max time.Duration
+ total time.Duration
+ n uint
+}
+
+func (eb *ExponentialBackoff) Pause() (time.Duration, bool) {
+ if eb.total > eb.Max {
+ return 0, false
+ }
+
+ // The next pause is selected from randomly from [0, 2^n * Base).
+ d := time.Duration(rand.Int63n((1 << eb.n) * int64(eb.Base)))
+ eb.total += d
+ eb.n++
+ return d, true
+}
+
+func (eb *ExponentialBackoff) Reset() {
+ eb.n = 0
+ eb.total = 0
+}
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..9921049
--- /dev/null
+++ b/vendor/google.golang.org/api/gensupport/buffer.go
@@ -0,0 +1,77 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gensupport
+
+import (
+ "bytes"
+ "io"
+
+ "google.golang.org/api/googleapi"
+)
+
+// MediaBuffer buffers data from an io.Reader to support uploading media in retryable chunks.
+type MediaBuffer struct {
+ media io.Reader
+
+ chunk []byte // The current chunk which is pending upload. The capacity is the chunk size.
+ err error // Any error generated when populating chunk by reading media.
+
+ // The absolute position of chunk in the underlying media.
+ off int64
+}
+
+func NewMediaBuffer(media io.Reader, chunkSize int) *MediaBuffer {
+ return &MediaBuffer{media: media, chunk: make([]byte, 0, chunkSize)}
+}
+
+// Chunk returns the current buffered chunk, the offset in the underlying media
+// from which the chunk is drawn, and the size of the chunk.
+// Successive calls to Chunk return the same chunk between calls to Next.
+func (mb *MediaBuffer) Chunk() (chunk io.Reader, off int64, size int, err error) {
+ // There may already be data in chunk if Next has not been called since the previous call to Chunk.
+ if mb.err == nil && len(mb.chunk) == 0 {
+ mb.err = mb.loadChunk()
+ }
+ return bytes.NewReader(mb.chunk), mb.off, len(mb.chunk), mb.err
+}
+
+// loadChunk will read from media into chunk, up to the capacity of chunk.
+func (mb *MediaBuffer) loadChunk() error {
+ bufSize := cap(mb.chunk)
+ mb.chunk = mb.chunk[:bufSize]
+
+ read := 0
+ var err error
+ for err == nil && read < bufSize {
+ var n int
+ n, err = mb.media.Read(mb.chunk[read:])
+ read += n
+ }
+ mb.chunk = mb.chunk[:read]
+ return err
+}
+
+// Next advances to the next chunk, which will be returned by the next call to Chunk.
+// Calls to Next without a corresponding prior call to Chunk will have no effect.
+func (mb *MediaBuffer) Next() {
+ mb.off += int64(len(mb.chunk))
+ mb.chunk = mb.chunk[0:0]
+}
+
+type readerTyper struct {
+ io.Reader
+ googleapi.ContentTyper
+}
+
+// ReaderAtToReader adapts a ReaderAt to be used as a Reader.
+// If ra implements googleapi.ContentTyper, then the returned reader
+// will also implement googleapi.ContentTyper, delegating to ra.
+func ReaderAtToReader(ra io.ReaderAt, size int64) io.Reader {
+ r := io.NewSectionReader(ra, 0, size)
+ if typer, ok := ra.(googleapi.ContentTyper); ok {
+ return readerTyper{r, typer}
+ }
+ return r
+}
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 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package gensupport is an internal implementation detail used by code
+// generated by the google-api-go-generator tool.
+//
+// This package may be modified at any time without regard for backwards
+// compatibility. It should not be used directly by API users.
+package gensupport
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..dd7bcd2
--- /dev/null
+++ b/vendor/google.golang.org/api/gensupport/json.go
@@ -0,0 +1,172 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gensupport
+
+import (
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "strings"
+)
+
+// MarshalJSON returns a JSON encoding of schema containing only selected fields.
+// A field is selected if:
+// * it has a non-empty value, or
+// * its field name is present in forceSendFields, and
+// * it is not a nil pointer or nil interface.
+// The JSON key for each selected field is taken from the field's json: struct tag.
+func MarshalJSON(schema interface{}, forceSendFields []string) ([]byte, error) {
+ if len(forceSendFields) == 0 {
+ return json.Marshal(schema)
+ }
+
+ mustInclude := make(map[string]struct{})
+ for _, f := range forceSendFields {
+ mustInclude[f] = struct{}{}
+ }
+
+ dataMap, err := schemaToMap(schema, mustInclude)
+ if err != nil {
+ return nil, err
+ }
+ return json.Marshal(dataMap)
+}
+
+func schemaToMap(schema interface{}, mustInclude map[string]struct{}) (map[string]interface{}, error) {
+ m := make(map[string]interface{})
+ s := reflect.ValueOf(schema)
+ st := s.Type()
+
+ for i := 0; i < s.NumField(); i++ {
+ jsonTag := st.Field(i).Tag.Get("json")
+ if jsonTag == "" {
+ continue
+ }
+ tag, err := parseJSONTag(jsonTag)
+ if err != nil {
+ return nil, err
+ }
+ if tag.ignore {
+ continue
+ }
+
+ v := s.Field(i)
+ f := st.Field(i)
+ if !includeField(v, f, mustInclude) {
+ continue
+ }
+
+ // nil maps are treated as empty maps.
+ if f.Type.Kind() == reflect.Map && v.IsNil() {
+ m[tag.apiName] = map[string]string{}
+ continue
+ }
+
+ // nil slices are treated as empty slices.
+ if f.Type.Kind() == reflect.Slice && v.IsNil() {
+ m[tag.apiName] = []bool{}
+ continue
+ }
+
+ if tag.stringFormat {
+ m[tag.apiName] = formatAsString(v, f.Type.Kind())
+ } else {
+ m[tag.apiName] = v.Interface()
+ }
+ }
+ return m, nil
+}
+
+// formatAsString returns a string representation of v, dereferencing it first if possible.
+func formatAsString(v reflect.Value, kind reflect.Kind) string {
+ if kind == reflect.Ptr && !v.IsNil() {
+ v = v.Elem()
+ }
+
+ return fmt.Sprintf("%v", v.Interface())
+}
+
+// jsonTag represents a restricted version of the struct tag format used by encoding/json.
+// It is used to describe the JSON encoding of fields in a Schema struct.
+type jsonTag struct {
+ apiName string
+ stringFormat bool
+ ignore bool
+}
+
+// parseJSONTag parses a restricted version of the struct tag format used by encoding/json.
+// The format of the tag must match that generated by the Schema.writeSchemaStruct method
+// in the api generator.
+func parseJSONTag(val string) (jsonTag, error) {
+ if val == "-" {
+ return jsonTag{ignore: true}, nil
+ }
+
+ var tag jsonTag
+
+ i := strings.Index(val, ",")
+ if i == -1 || val[:i] == "" {
+ return tag, fmt.Errorf("malformed json tag: %s", val)
+ }
+
+ tag = jsonTag{
+ apiName: val[:i],
+ }
+
+ switch val[i+1:] {
+ case "omitempty":
+ case "omitempty,string":
+ tag.stringFormat = true
+ default:
+ return tag, fmt.Errorf("malformed json tag: %s", val)
+ }
+
+ return tag, nil
+}
+
+// Reports whether the struct field "f" with value "v" should be included in JSON output.
+func includeField(v reflect.Value, f reflect.StructField, mustInclude map[string]struct{}) bool {
+ // The regular JSON encoding of a nil pointer is "null", which means "delete this field".
+ // Therefore, we could enable field deletion by honoring pointer fields' presence in the mustInclude set.
+ // However, many fields are not pointers, so there would be no way to delete these fields.
+ // Rather than partially supporting field deletion, we ignore mustInclude for nil pointer fields.
+ // Deletion will be handled by a separate mechanism.
+ if f.Type.Kind() == reflect.Ptr && v.IsNil() {
+ return false
+ }
+
+ // The "any" type is represented as an interface{}. If this interface
+ // is nil, there is no reasonable representation to send. We ignore
+ // these fields, for the same reasons as given above for pointers.
+ if f.Type.Kind() == reflect.Interface && v.IsNil() {
+ return false
+ }
+
+ _, ok := mustInclude[f.Name]
+ return ok || !isEmptyValue(v)
+}
+
+// isEmptyValue reports whether v is the empty value for its type. This
+// implementation is based on that of the encoding/json package, but its
+// correctness does not depend on it being identical. What's important is that
+// this function return false in situations where v should not be sent as part
+// of a PATCH operation.
+func isEmptyValue(v reflect.Value) bool {
+ switch v.Kind() {
+ case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+ return v.Len() == 0
+ case reflect.Bool:
+ return !v.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return v.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return v.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return v.Float() == 0
+ case reflect.Interface, reflect.Ptr:
+ return v.IsNil()
+ }
+ return false
+}
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..c6410e8
--- /dev/null
+++ b/vendor/google.golang.org/api/gensupport/media.go
@@ -0,0 +1,199 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gensupport
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "mime/multipart"
+ "net/http"
+ "net/textproto"
+
+ "google.golang.org/api/googleapi"
+)
+
+const sniffBuffSize = 512
+
+func newContentSniffer(r io.Reader) *contentSniffer {
+ return &contentSniffer{r: r}
+}
+
+// contentSniffer wraps a Reader, and reports the content type determined by sniffing up to 512 bytes from the Reader.
+type contentSniffer struct {
+ r io.Reader
+ start []byte // buffer for the sniffed bytes.
+ err error // set to any error encountered while reading bytes to be sniffed.
+
+ ctype string // set on first sniff.
+ sniffed bool // set to true on first sniff.
+}
+
+func (cs *contentSniffer) Read(p []byte) (n int, err error) {
+ // Ensure that the content type is sniffed before any data is consumed from Reader.
+ _, _ = cs.ContentType()
+
+ if len(cs.start) > 0 {
+ n := copy(p, cs.start)
+ cs.start = cs.start[n:]
+ return n, nil
+ }
+
+ // We may have read some bytes into start while sniffing, even if the read ended in an error.
+ // We should first return those bytes, then the error.
+ if cs.err != nil {
+ return 0, cs.err
+ }
+
+ // Now we have handled all bytes that were buffered while sniffing. Now just delegate to the underlying reader.
+ return cs.r.Read(p)
+}
+
+// ContentType returns the sniffed content type, and whether the content type was succesfully sniffed.
+func (cs *contentSniffer) ContentType() (string, bool) {
+ if cs.sniffed {
+ return cs.ctype, cs.ctype != ""
+ }
+ cs.sniffed = true
+ // If ReadAll hits EOF, it returns err==nil.
+ cs.start, cs.err = ioutil.ReadAll(io.LimitReader(cs.r, sniffBuffSize))
+
+ // Don't try to detect the content type based on possibly incomplete data.
+ if cs.err != nil {
+ return "", false
+ }
+
+ cs.ctype = http.DetectContentType(cs.start)
+ return cs.ctype, true
+}
+
+// DetermineContentType determines the content type of the supplied reader.
+// If the content type is already known, it can be specified via ctype.
+// Otherwise, the content of media will be sniffed to determine the content type.
+// If media implements googleapi.ContentTyper (deprecated), this will be used
+// instead of sniffing the content.
+// After calling DetectContentType the caller must not perform further reads on
+// media, but rather read from the Reader that is returned.
+func DetermineContentType(media io.Reader, ctype string) (io.Reader, string) {
+ // Note: callers could avoid calling DetectContentType if ctype != "",
+ // but doing the check inside this function reduces the amount of
+ // generated code.
+ if ctype != "" {
+ return media, ctype
+ }
+
+ // For backwards compatability, allow clients to set content
+ // type by providing a ContentTyper for media.
+ if typer, ok := media.(googleapi.ContentTyper); ok {
+ return media, typer.ContentType()
+ }
+
+ sniffer := newContentSniffer(media)
+ if ctype, ok := sniffer.ContentType(); ok {
+ return sniffer, ctype
+ }
+ // If content type could not be sniffed, reads from sniffer will eventually fail with an error.
+ return sniffer, ""
+}
+
+type typeReader struct {
+ io.Reader
+ typ string
+}
+
+// multipartReader combines the contents of multiple readers to creat a multipart/related HTTP body.
+// Close must be called if reads from the multipartReader are abandoned before reaching EOF.
+type multipartReader struct {
+ pr *io.PipeReader
+ pipeOpen bool
+ ctype string
+}
+
+func newMultipartReader(parts []typeReader) *multipartReader {
+ mp := &multipartReader{pipeOpen: true}
+ var pw *io.PipeWriter
+ mp.pr, pw = io.Pipe()
+ mpw := multipart.NewWriter(pw)
+ mp.ctype = "multipart/related; boundary=" + mpw.Boundary()
+ go func() {
+ for _, part := range parts {
+ w, err := mpw.CreatePart(typeHeader(part.typ))
+ if err != nil {
+ mpw.Close()
+ pw.CloseWithError(fmt.Errorf("googleapi: CreatePart failed: %v", err))
+ return
+ }
+ _, err = io.Copy(w, part.Reader)
+ if err != nil {
+ mpw.Close()
+ pw.CloseWithError(fmt.Errorf("googleapi: Copy failed: %v", err))
+ return
+ }
+ }
+
+ mpw.Close()
+ pw.Close()
+ }()
+ return mp
+}
+
+func (mp *multipartReader) Read(data []byte) (n int, err error) {
+ return mp.pr.Read(data)
+}
+
+func (mp *multipartReader) Close() error {
+ if !mp.pipeOpen {
+ return nil
+ }
+ mp.pipeOpen = false
+ return mp.pr.Close()
+}
+
+// CombineBodyMedia combines a json body with media content to create a multipart/related HTTP body.
+// It returns a ReadCloser containing the combined body, and the overall "multipart/related" content type, with random boundary.
+//
+// The caller must call Close on the returned ReadCloser if reads are abandoned before reaching EOF.
+func CombineBodyMedia(body io.Reader, bodyContentType string, media io.Reader, mediaContentType string) (io.ReadCloser, string) {
+ mp := newMultipartReader([]typeReader{
+ {body, bodyContentType},
+ {media, mediaContentType},
+ })
+ return mp, mp.ctype
+}
+
+func typeHeader(contentType string) textproto.MIMEHeader {
+ h := make(textproto.MIMEHeader)
+ if contentType != "" {
+ h.Set("Content-Type", contentType)
+ }
+ return h
+}
+
+// PrepareUpload determines whether the data in the supplied reader should be
+// uploaded in a single request, or in sequential chunks.
+// chunkSize is the size of the chunk that media should be split into.
+// If chunkSize is non-zero and the contents of media do not fit in a single
+// chunk (or there is an error reading media), then media will be returned as a
+// MediaBuffer. Otherwise, media will be returned as a Reader.
+//
+// After PrepareUpload has been called, media should no longer be used: the
+// media content should be accessed via one of the return values.
+func PrepareUpload(media io.Reader, chunkSize int) (io.Reader, *MediaBuffer) {
+ if chunkSize == 0 { // do not chunk
+ return media, nil
+ }
+
+ mb := NewMediaBuffer(media, chunkSize)
+ rdr, _, _, err := mb.Chunk()
+
+ if err == io.EOF { // we can upload this in a single request
+ return rdr, nil
+ }
+ // err might be a non-EOF error. If it is, the next call to mb.Chunk will
+ // return the same error. Returning a MediaBuffer ensures that this error
+ // will be handled at some point.
+
+ return nil, mb
+}
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..3b3c743
--- /dev/null
+++ b/vendor/google.golang.org/api/gensupport/params.go
@@ -0,0 +1,50 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gensupport
+
+import (
+ "net/url"
+
+ "google.golang.org/api/googleapi"
+)
+
+// URLParams is a simplified replacement for url.Values
+// that safely builds up URL parameters for encoding.
+type URLParams map[string][]string
+
+// Get returns the first value for the given key, or "".
+func (u URLParams) Get(key string) string {
+ vs := u[key]
+ if len(vs) == 0 {
+ return ""
+ }
+ return vs[0]
+}
+
+// Set sets the key to value.
+// It replaces any existing values.
+func (u URLParams) Set(key, value string) {
+ u[key] = []string{value}
+}
+
+// SetMulti sets the key to an array of values.
+// It replaces any existing values.
+// Note that values must not be modified after calling SetMulti
+// so the caller is responsible for making a copy if necessary.
+func (u URLParams) SetMulti(key string, values []string) {
+ u[key] = values
+}
+
+// Encode encodes the values into ``URL encoded'' form
+// ("bar=baz&foo=quux") sorted by key.
+func (u URLParams) Encode() string {
+ return url.Values(u).Encode()
+}
+
+func SetOptions(u URLParams, opts ...googleapi.CallOption) {
+ for _, o := range opts {
+ u.Set(o.Get())
+ }
+}
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..ad16943
--- /dev/null
+++ b/vendor/google.golang.org/api/gensupport/resumable.go
@@ -0,0 +1,198 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gensupport
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "sync"
+ "time"
+
+ "golang.org/x/net/context"
+ "golang.org/x/net/context/ctxhttp"
+)
+
+const (
+ // statusResumeIncomplete is the code returned by the Google uploader
+ // when the transfer is not yet complete.
+ statusResumeIncomplete = 308
+
+ // statusTooManyRequests is returned by the storage API if the
+ // per-project limits have been temporarily exceeded. The request
+ // should be retried.
+ // https://cloud.google.com/storage/docs/json_api/v1/status-codes#standardcodes
+ statusTooManyRequests = 429
+)
+
+// ResumableUpload is used by the generated APIs to provide resumable uploads.
+// It is not used by developers directly.
+type ResumableUpload struct {
+ Client *http.Client
+ // URI is the resumable resource destination provided by the server after specifying "&uploadType=resumable".
+ URI string
+ UserAgent string // User-Agent for header of the request
+ // Media is the object being uploaded.
+ Media *MediaBuffer
+ // MediaType defines the media type, e.g. "image/jpeg".
+ MediaType string
+
+ mu sync.Mutex // guards progress
+ progress int64 // number of bytes uploaded so far
+
+ // Callback is an optional function that will be periodically called with the cumulative number of bytes uploaded.
+ Callback func(int64)
+
+ // If not specified, a default exponential backoff strategy will be used.
+ Backoff BackoffStrategy
+}
+
+// Progress returns the number of bytes uploaded at this point.
+func (rx *ResumableUpload) Progress() int64 {
+ rx.mu.Lock()
+ defer rx.mu.Unlock()
+ return rx.progress
+}
+
+// doUploadRequest performs a single HTTP request to upload data.
+// off specifies the offset in rx.Media from which data is drawn.
+// size is the number of bytes in data.
+// final specifies whether data is the final chunk to be uploaded.
+func (rx *ResumableUpload) doUploadRequest(ctx context.Context, data io.Reader, off, size int64, final bool) (*http.Response, error) {
+ req, err := http.NewRequest("POST", rx.URI, data)
+ if err != nil {
+ return nil, err
+ }
+
+ req.ContentLength = size
+ var contentRange string
+ if final {
+ if size == 0 {
+ contentRange = fmt.Sprintf("bytes */%v", off)
+ } else {
+ contentRange = fmt.Sprintf("bytes %v-%v/%v", off, off+size-1, off+size)
+ }
+ } else {
+ contentRange = fmt.Sprintf("bytes %v-%v/*", off, off+size-1)
+ }
+ req.Header.Set("Content-Range", contentRange)
+ req.Header.Set("Content-Type", rx.MediaType)
+ req.Header.Set("User-Agent", rx.UserAgent)
+ return ctxhttp.Do(ctx, rx.Client, req)
+
+}
+
+// reportProgress calls a user-supplied callback to report upload progress.
+// If old==updated, the callback is not called.
+func (rx *ResumableUpload) reportProgress(old, updated int64) {
+ if updated-old == 0 {
+ return
+ }
+ rx.mu.Lock()
+ rx.progress = updated
+ rx.mu.Unlock()
+ if rx.Callback != nil {
+ rx.Callback(updated)
+ }
+}
+
+// transferChunk performs a single HTTP request to upload a single chunk from rx.Media.
+func (rx *ResumableUpload) transferChunk(ctx context.Context) (*http.Response, error) {
+ chunk, off, size, err := rx.Media.Chunk()
+
+ done := err == io.EOF
+ if !done && err != nil {
+ return nil, err
+ }
+
+ res, err := rx.doUploadRequest(ctx, chunk, off, int64(size), done)
+ if err != nil {
+ return res, err
+ }
+
+ if res.StatusCode == statusResumeIncomplete || res.StatusCode == http.StatusOK {
+ rx.reportProgress(off, off+int64(size))
+ }
+
+ if res.StatusCode == statusResumeIncomplete {
+ rx.Media.Next()
+ }
+ return res, nil
+}
+
+func contextDone(ctx context.Context) bool {
+ select {
+ case <-ctx.Done():
+ return true
+ default:
+ return false
+ }
+}
+
+// Upload starts the process of a resumable upload with a cancellable context.
+// It retries using the provided back off strategy until cancelled or the
+// strategy indicates to stop retrying.
+// It is called from the auto-generated API code and is not visible to the user.
+// rx is private to the auto-generated API code.
+// Exactly one of resp or err will be nil. If resp is non-nil, the caller must call resp.Body.Close.
+func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err error) {
+ var pause time.Duration
+ backoff := rx.Backoff
+ if backoff == nil {
+ backoff = DefaultBackoffStrategy()
+ }
+
+ for {
+ // Ensure that we return in the case of cancelled context, even if pause is 0.
+ if contextDone(ctx) {
+ return nil, ctx.Err()
+ }
+ select {
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case <-time.After(pause):
+ }
+
+ resp, err = rx.transferChunk(ctx)
+
+ var status int
+ if resp != nil {
+ status = resp.StatusCode
+ }
+
+ // Check if we should retry the request.
+ if shouldRetry(status, err) {
+ var retry bool
+ pause, retry = backoff.Pause()
+ if retry {
+ if resp != nil && resp.Body != nil {
+ resp.Body.Close()
+ }
+ continue
+ }
+ }
+
+ // If the chunk was uploaded successfully, but there's still
+ // more to go, upload the next chunk without any delay.
+ if status == statusResumeIncomplete {
+ pause = 0
+ backoff.Reset()
+ resp.Body.Close()
+ continue
+ }
+
+ // It's possible for err and resp to both be non-nil here, but we expose a simpler
+ // contract to our callers: exactly one of resp and err will be non-nil. This means
+ // that any response body must be closed here before returning a non-nil error.
+ if err != nil {
+ if resp != nil && resp.Body != nil {
+ resp.Body.Close()
+ }
+ return nil, err
+ }
+
+ return resp, nil
+ }
+}
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..7f83d1d
--- /dev/null
+++ b/vendor/google.golang.org/api/gensupport/retry.go
@@ -0,0 +1,77 @@
+package gensupport
+
+import (
+ "io"
+ "net"
+ "net/http"
+ "time"
+
+ "golang.org/x/net/context"
+)
+
+// Retry invokes the given function, retrying it multiple times if the connection failed or
+// the HTTP status response indicates the request should be attempted again. ctx may be nil.
+func Retry(ctx context.Context, f func() (*http.Response, error), backoff BackoffStrategy) (*http.Response, error) {
+ for {
+ resp, err := f()
+
+ var status int
+ if resp != nil {
+ status = resp.StatusCode
+ }
+
+ // Return if we shouldn't retry.
+ pause, retry := backoff.Pause()
+ if !shouldRetry(status, err) || !retry {
+ return resp, err
+ }
+
+ // Ensure the response body is closed, if any.
+ if resp != nil && resp.Body != nil {
+ resp.Body.Close()
+ }
+
+ // Pause, but still listen to ctx.Done if context is not nil.
+ var done <-chan struct{}
+ if ctx != nil {
+ done = ctx.Done()
+ }
+ select {
+ case <-done:
+ return nil, ctx.Err()
+ case <-time.After(pause):
+ }
+ }
+}
+
+// DefaultBackoffStrategy returns a default strategy to use for retrying failed upload requests.
+func DefaultBackoffStrategy() BackoffStrategy {
+ return &ExponentialBackoff{
+ Base: 250 * time.Millisecond,
+ Max: 16 * time.Second,
+ }
+}
+
+// shouldRetry returns true if the HTTP response / error indicates that the
+// request should be attempted again.
+func shouldRetry(status int, err error) bool {
+ // Retry for 5xx response codes.
+ if 500 <= status && status < 600 {
+ return true
+ }
+
+ // Retry on statusTooManyRequests{
+ if status == statusTooManyRequests {
+ return true
+ }
+
+ // Retry on unexpected EOFs and temporary network errors.
+ if err == io.ErrUnexpectedEOF {
+ return true
+ }
+ if err, ok := err.(net.Error); ok {
+ return err.Temporary()
+ }
+
+ return false
+}