From 661ac69e128b9f939c5b7b73b963a74808ec3220 Mon Sep 17 00:00:00 2001 From: Dragoslav Mitrinovic Date: Fri, 5 Jun 2015 12:18:39 -0500 Subject: [PATCH] google-api-go-client: resumable upl: skip initial status request Status requests are used to ask the server for the offset at which the upload should be resumed. However, there is no need to do this at the very start of the upload, prior to sending any content to the server. Change-Id: I610b347a9a88fe66bfe5c0e4ae020d0a9182caa8 Reviewed-on: https://code-review.googlesource.com/2761 Reviewed-by: Glenn Lewis Reviewed-by: Brad Fitzpatrick --- google-api-go-generator/storage_test.go | 16 +++++++++++++--- googleapi/googleapi.go | 13 ++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/google-api-go-generator/storage_test.go b/google-api-go-generator/storage_test.go index 6a572ee14bb..1cfc437ea15 100644 --- a/google-api-go-generator/storage_test.go +++ b/google-api-go-generator/storage_test.go @@ -127,6 +127,10 @@ func TestResumableMedia(t *testing.T) { ContentEncoding: "utf-8", ContentLanguage: "en", } + // If all goes well, this will cause two POST requests to be sent to our fake GCS server: + // 1. Resumable upload session initiation request, to which server will respond with 200 OK and Location header. + // 2. Upload of the first and only chunk, with Content-Range header set to specify entire content. + // The test below verifies the content and headers of this second POST request. _, err = s.Objects.Insert("mybucket", o).Name("filename").ResumableMedia(context.Background(), f, int64(len(data)), "text/plain").Do() if err != nil { t.Fatalf("unable to insert object: %v", err) @@ -147,15 +151,21 @@ func TestResumableMedia(t *testing.T) { if w, k := "google-api-go-client/0.5", "User-Agent"; len(g.Header[k]) != 1 || g.Header[k][0] != w { t.Errorf("header %q = %#v; want %q", k, g.Header[k], w) } - if k := "Content-Type"; len(g.Header[k]) != 0 { - t.Errorf("header %q = %#v; want nil", k, g.Header[k]) + if w, k := "text/plain", "Content-Type"; len(g.Header[k]) != 1 || g.Header[k][0] != w { + t.Errorf("header %q = %#v; want %v", k, g.Header[k], w) + } + if w, k := fmt.Sprintf("bytes 0-%v/%v", len(data)-1, len(data)), "Content-Range"; len(g.Header[k]) != 1 || g.Header[k][0] != w { + t.Errorf("header %q = %#v; want %v", k, g.Header[k], w) } if w, k := "gzip", "Accept-Encoding"; len(g.Header[k]) != 1 || g.Header[k][0] != w { t.Errorf("header %q = %#v; want %q", k, g.Header[k], w) } - if w := int64(0); g.ContentLength != w { + if w := int64(len(data)); g.ContentLength != w { t.Errorf("ContentLength = %v; want %v", g.ContentLength, w) } + if s := string(handler.body); s != data { + t.Errorf("body = %q; want %q", s, data) + } if len(g.TransferEncoding) != 0 { t.Errorf("TransferEncoding = %#v; want nil", g.TransferEncoding) } diff --git a/googleapi/googleapi.go b/googleapi/googleapi.go index 5fd2ef629f6..3219a5fa7cc 100644 --- a/googleapi/googleapi.go +++ b/googleapi/googleapi.go @@ -307,6 +307,7 @@ type ResumableUpload struct { mu sync.Mutex // guards progress progress int64 // number of bytes uploaded so far + started bool // whether the upload has been started // Callback is an optional function that will be called upon every progress update. Callback ProgressUpdater @@ -354,10 +355,16 @@ type chunk struct { } func (rx *ResumableUpload) transferChunks(ctx context.Context) (*http.Response, error) { - start, res, err := rx.transferStatus() - if err != nil || res.StatusCode != statusResumeIncomplete { - return res, err + var start int64 + var err error + res := &http.Response{} + if rx.started { + start, res, err = rx.transferStatus() + if err != nil || res.StatusCode != statusResumeIncomplete { + return res, err + } } + rx.started = true for { select { // Check for cancellation