gensupport: add retry for initial upload request (#528)

* gensupport: add retry for initial upload request

This adds a retry for the initial request in a resumable
or multipart upload.

Fixes #392
diff --git a/internal/gensupport/resumable.go b/internal/gensupport/resumable.go
index e67ccd9..dd46b79 100644
--- a/internal/gensupport/resumable.go
+++ b/internal/gensupport/resumable.go
@@ -160,21 +160,6 @@
 // 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 shouldRetry = func(status int, err error) bool {
-		if 500 <= status && status <= 599 {
-			return true
-		}
-		if status == statusTooManyRequests {
-			return true
-		}
-		if err == io.ErrUnexpectedEOF {
-			return true
-		}
-		if err, ok := err.(interface{ Temporary() bool }); ok {
-			return err.Temporary()
-		}
-		return false
-	}
 
 	// There are a couple of cases where it's possible for err and resp to both
 	// be non-nil. However, we expose a simpler contract to our callers: exactly
@@ -239,3 +224,21 @@
 		return prepareReturn(resp, err)
 	}
 }
+
+// shouldRetry indicates whether an error is retryable for the purposes of this
+// package.
+func shouldRetry(status int, err error) bool {
+	if 500 <= status && status <= 599 {
+		return true
+	}
+	if status == statusTooManyRequests {
+		return true
+	}
+	if err == io.ErrUnexpectedEOF {
+		return true
+	}
+	if err, ok := err.(interface{ Temporary() bool }); ok {
+		return err.Temporary()
+	}
+	return false
+}
diff --git a/internal/gensupport/resumable_test.go b/internal/gensupport/resumable_test.go
index c584d50..44544f7 100644
--- a/internal/gensupport/resumable_test.go
+++ b/internal/gensupport/resumable_test.go
@@ -301,57 +301,6 @@
 	}
 }
 
-func TestRetry_Bounded(t *testing.T) {
-	const (
-		chunkSize = 90
-		mediaSize = 300
-	)
-	media := strings.NewReader(strings.Repeat("a", mediaSize))
-
-	tr := &interruptibleTransport{
-		buf: make([]byte, 0, mediaSize),
-		events: []event{
-			{"bytes 0-89/*", http.StatusServiceUnavailable},
-			{"bytes 0-89/*", http.StatusServiceUnavailable},
-		},
-		bodies: bodyTracker{},
-	}
-
-	rx := &ResumableUpload{
-		Client:    &http.Client{Transport: tr},
-		Media:     NewMediaBuffer(media, chunkSize),
-		MediaType: "text/plain",
-		Callback:  func(int64) {},
-	}
-
-	oldRetryDeadline := retryDeadline
-	retryDeadline = time.Second
-	defer func() { retryDeadline = oldRetryDeadline }()
-
-	oldBackoff := backoff
-	backoff = func() Backoff { return new(PauseForeverBackoff) }
-	defer func() { backoff = oldBackoff }()
-
-	resCode := make(chan int, 1)
-	go func() {
-		resp, err := rx.Upload(context.Background())
-		if err != nil {
-			t.Error(err)
-			return
-		}
-		resCode <- resp.StatusCode
-	}()
-
-	select {
-	case <-time.After(5 * time.Second):
-		t.Fatal("timed out waiting for Upload to complete")
-	case got := <-resCode:
-		if want, got := http.StatusServiceUnavailable, got; got != want {
-			t.Fatalf("want %d, got %d", want, got)
-		}
-	}
-}
-
 func TestRetry_EachChunkHasItsOwnRetryDeadline(t *testing.T) {
 	const (
 		chunkSize = 90
diff --git a/internal/gensupport/send.go b/internal/gensupport/send.go
index 5799393..d54f07a 100644
--- a/internal/gensupport/send.go
+++ b/internal/gensupport/send.go
@@ -9,6 +9,7 @@
 	"encoding/json"
 	"errors"
 	"net/http"
+	"time"
 )
 
 // Hook is the type of a function that is called once before each HTTP request
@@ -64,14 +65,48 @@
 	if client == nil {
 		client = http.DefaultClient
 	}
-	resp, err := client.Do(req.WithContext(ctx))
-	// If we got an error, and the context has been canceled,
-	// the context's error is probably more useful.
-	if err != nil {
+
+	var resp *http.Response
+	var err error
+
+	// Loop to retry the request, up to the context deadline.
+	var pause time.Duration
+	bo := backoff()
+
+	for {
 		select {
 		case <-ctx.Done():
-			err = ctx.Err()
-		default:
+			// If we got an error, and the context has been canceled,
+			// the context's error is probably more useful.
+			if err == nil {
+				err = ctx.Err()
+			}
+			return resp, err
+		case <-time.After(pause):
+		}
+
+		resp, err = client.Do(req.WithContext(ctx))
+
+		var status int
+		if resp != nil {
+			status = resp.StatusCode
+		}
+
+		// Check if we can retry the request. A retry can only be done if the error
+		// is retryable and the request body can be re-created using GetBody (this
+		// will not be possible if the body was unbuffered).
+		if req.GetBody == nil || !shouldRetry(status, err) {
+			break
+		}
+		var errBody error
+		req.Body, errBody = req.GetBody()
+		if errBody != nil {
+			break
+		}
+
+		pause = bo.Pause()
+		if resp != nil && resp.Body != nil {
+			resp.Body.Close()
 		}
 	}
 	return resp, err
diff --git a/internal/gensupport/util_test.go b/internal/gensupport/util_test.go
index 11ef68b..bdfe14c 100644
--- a/internal/gensupport/util_test.go
+++ b/internal/gensupport/util_test.go
@@ -36,8 +36,3 @@
 type PauseOneSecond struct{}
 
 func (bo *PauseOneSecond) Pause() time.Duration { return time.Second }
-
-// PauseForeverBackoff implements backoff with infinite 1h pauses.
-type PauseForeverBackoff struct{}
-
-func (bo *PauseForeverBackoff) Pause() time.Duration { return time.Hour }