// Do sends an HTTP request with the provided http.Client and returns an HTTP response. // If the client is nil, http.DefaultClient is used. // If the context is canceled or times out, ctx.Err() will be returned. func Do(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { if client == nil { client = http.DefaultClient } // Request cancelation changed in Go 1.5, see cancelreq.go and cancelreq_go14.go. cancel := canceler(client, req) type responseAndError struct { resp *http.Response err error } result := make(chan responseAndError, 1) go func() { resp, err := client.Do(req) result <- responseAndError{resp, err} }() select { case <-ctx.Done(): cancel() return nil, ctx.Err() case r := <-result: return r.resp, r.err } }
func (rx *ResumableUpload) transferChunks(ctx context.Context) (*http.Response, error) { var start int64 var err error res := &http.Response{} if rx.started { start, res, err = rx.transferStatus(ctx) if err != nil || res.StatusCode != statusResumeIncomplete { return res, err } } rx.started = true for { select { // Check for cancellation case <-ctx.Done(): res.StatusCode = http.StatusRequestTimeout return res, ctx.Err() default: } reqSize := rx.ContentLength - start if reqSize > chunkSize { reqSize = chunkSize } r := io.NewSectionReader(rx.Media, start, reqSize) req, _ := http.NewRequest("POST", rx.URI, r) req.ContentLength = reqSize req.Header.Set("Content-Range", fmt.Sprintf("bytes %v-%v/%v", start, start+reqSize-1, rx.ContentLength)) req.Header.Set("Content-Type", rx.MediaType) req.Header.Set("User-Agent", rx.UserAgent) res, err = ctxhttp.Do(ctx, rx.Client, req) start += reqSize if err == nil && (res.StatusCode == statusResumeIncomplete || res.StatusCode == http.StatusOK) { rx.mu.Lock() rx.progress = start // keep track of number of bytes sent so far rx.mu.Unlock() if rx.Callback != nil { rx.Callback(start, rx.ContentLength) } } if err != nil || res.StatusCode != statusResumeIncomplete { break } } return res, err }
// Upload starts the process of a resumable upload with a cancellable context. // It retries indefinitely (with a pause of uploadPause between attempts) until cancelled. // 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. func (rx *ResumableUpload) Upload(ctx context.Context) (*http.Response, error) { var res *http.Response var err error for { res, err = rx.transferChunks(ctx) if err != nil || res.StatusCode == http.StatusCreated || res.StatusCode == http.StatusOK { return res, err } select { // Check for cancellation case <-ctx.Done(): res.StatusCode = http.StatusRequestTimeout return res, ctx.Err() default: } sleep(uploadPause) } return res, err }