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 }
func (rx *ResumableUpload) transferStatus(ctx context.Context) (int64, *http.Response, error) { req, _ := http.NewRequest("POST", rx.URI, nil) req.ContentLength = 0 req.Header.Set("User-Agent", rx.UserAgent) req.Header.Set("Content-Range", fmt.Sprintf("bytes */%v", rx.ContentLength)) res, err := ctxhttp.Do(ctx, rx.Client, req) if err != nil || res.StatusCode != statusResumeIncomplete { return 0, res, err } var start int64 if m := rangeRE.FindStringSubmatch(res.Header.Get("Range")); len(m) == 2 { start, err = strconv.ParseInt(m[1], 10, 64) if err != nil { return 0, nil, fmt.Errorf("unable to parse range size %v", m[1]) } start += 1 // Start at the next byte } return start, res, nil }