diff options
| author | Paul Zabelin | 2016-04-17 03:22:31 -0700 | 
|---|---|---|
| committer | Paul Zabelin | 2016-04-17 03:22:31 -0700 | 
| commit | b5eb2866cfceb69b0d4dd4948273d679a884fbb2 (patch) | |
| tree | 1fdb61a7138642a1612bb201434e8ebda141cc8a /vendor/google.golang.org/api/gensupport | |
| parent | 8de8e05c483c6b5f23b14742315f1860211dcef7 (diff) | |
| download | gdrive-b5eb2866cfceb69b0d4dd4948273d679a884fbb2.tar.bz2 | |
add Go dependencies by godep
see https://github.com/tools/godep
Diffstat (limited to 'vendor/google.golang.org/api/gensupport')
| -rw-r--r-- | vendor/google.golang.org/api/gensupport/backoff.go | 46 | ||||
| -rw-r--r-- | vendor/google.golang.org/api/gensupport/buffer.go | 77 | ||||
| -rw-r--r-- | vendor/google.golang.org/api/gensupport/doc.go | 10 | ||||
| -rw-r--r-- | vendor/google.golang.org/api/gensupport/json.go | 172 | ||||
| -rw-r--r-- | vendor/google.golang.org/api/gensupport/media.go | 200 | ||||
| -rw-r--r-- | vendor/google.golang.org/api/gensupport/params.go | 50 | ||||
| -rw-r--r-- | vendor/google.golang.org/api/gensupport/resumable.go | 198 | ||||
| -rw-r--r-- | vendor/google.golang.org/api/gensupport/retry.go | 77 | 
8 files changed, 830 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..4b8ec14 --- /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" +) + +// ResumableBuffer buffers data from an io.Reader to support uploading media in retryable chunks. +type ResumableBuffer 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 NewResumableBuffer(media io.Reader, chunkSize int) *ResumableBuffer { +	return &ResumableBuffer{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 (rb *ResumableBuffer) 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 rb.err == nil && len(rb.chunk) == 0 { +		rb.err = rb.loadChunk() +	} +	return bytes.NewReader(rb.chunk), rb.off, len(rb.chunk), rb.err +} + +// loadChunk will read from media into chunk, up to the capacity of chunk. +func (rb *ResumableBuffer) loadChunk() error { +	bufSize := cap(rb.chunk) +	rb.chunk = rb.chunk[:bufSize] + +	read := 0 +	var err error +	for err == nil && read < bufSize { +		var n int +		n, err = rb.media.Read(rb.chunk[read:]) +		read += n +	} +	rb.chunk = rb.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 (rb *ResumableBuffer) Next() { +	rb.off += int64(len(rb.chunk)) +	rb.chunk = rb.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..817f46f --- /dev/null +++ b/vendor/google.golang.org/api/gensupport/media.go @@ -0,0 +1,200 @@ +// 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 +// ResumableBuffer.  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, +	*ResumableBuffer) { +	if chunkSize == 0 { // do not chunk +		return media, nil +	} + +	rb := NewResumableBuffer(media, chunkSize) +	rdr, _, _, err := rb.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 rb.Chunk will +	// return the same error. Returning a ResumableBuffer ensures that this error +	// will be handled at some point. + +	return nil, rb +} 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..b3e774a --- /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 *ResumableBuffer +	// 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 +} | 
