"Fossies" - the Fresh Open Source Software Archive

Member "gdrive-2.1.1/vendor/google.golang.org/api/gensupport/resumable.go" (28 May 2021, 5543 Bytes) of package /linux/misc/old/gdrive-2.1.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2016 The Go Authors. All rights reserved.
    2 // Use of this source code is governed by a BSD-style
    3 // license that can be found in the LICENSE file.
    4 
    5 package gensupport
    6 
    7 import (
    8     "fmt"
    9     "io"
   10     "net/http"
   11     "sync"
   12     "time"
   13 
   14     "golang.org/x/net/context"
   15     "golang.org/x/net/context/ctxhttp"
   16 )
   17 
   18 const (
   19     // statusResumeIncomplete is the code returned by the Google uploader
   20     // when the transfer is not yet complete.
   21     statusResumeIncomplete = 308
   22 
   23     // statusTooManyRequests is returned by the storage API if the
   24     // per-project limits have been temporarily exceeded. The request
   25     // should be retried.
   26     // https://cloud.google.com/storage/docs/json_api/v1/status-codes#standardcodes
   27     statusTooManyRequests = 429
   28 )
   29 
   30 // ResumableUpload is used by the generated APIs to provide resumable uploads.
   31 // It is not used by developers directly.
   32 type ResumableUpload struct {
   33     Client *http.Client
   34     // URI is the resumable resource destination provided by the server after specifying "&uploadType=resumable".
   35     URI       string
   36     UserAgent string // User-Agent for header of the request
   37     // Media is the object being uploaded.
   38     Media *ResumableBuffer
   39     // MediaType defines the media type, e.g. "image/jpeg".
   40     MediaType string
   41 
   42     mu       sync.Mutex // guards progress
   43     progress int64      // number of bytes uploaded so far
   44 
   45     // Callback is an optional function that will be periodically called with the cumulative number of bytes uploaded.
   46     Callback func(int64)
   47 
   48     // If not specified, a default exponential backoff strategy will be used.
   49     Backoff BackoffStrategy
   50 }
   51 
   52 // Progress returns the number of bytes uploaded at this point.
   53 func (rx *ResumableUpload) Progress() int64 {
   54     rx.mu.Lock()
   55     defer rx.mu.Unlock()
   56     return rx.progress
   57 }
   58 
   59 // doUploadRequest performs a single HTTP request to upload data.
   60 // off specifies the offset in rx.Media from which data is drawn.
   61 // size is the number of bytes in data.
   62 // final specifies whether data is the final chunk to be uploaded.
   63 func (rx *ResumableUpload) doUploadRequest(ctx context.Context, data io.Reader, off, size int64, final bool) (*http.Response, error) {
   64     req, err := http.NewRequest("POST", rx.URI, data)
   65     if err != nil {
   66         return nil, err
   67     }
   68 
   69     req.ContentLength = size
   70     var contentRange string
   71     if final {
   72         if size == 0 {
   73             contentRange = fmt.Sprintf("bytes */%v", off)
   74         } else {
   75             contentRange = fmt.Sprintf("bytes %v-%v/%v", off, off+size-1, off+size)
   76         }
   77     } else {
   78         contentRange = fmt.Sprintf("bytes %v-%v/*", off, off+size-1)
   79     }
   80     req.Header.Set("Content-Range", contentRange)
   81     req.Header.Set("Content-Type", rx.MediaType)
   82     req.Header.Set("User-Agent", rx.UserAgent)
   83     return ctxhttp.Do(ctx, rx.Client, req)
   84 
   85 }
   86 
   87 // reportProgress calls a user-supplied callback to report upload progress.
   88 // If old==updated, the callback is not called.
   89 func (rx *ResumableUpload) reportProgress(old, updated int64) {
   90     if updated-old == 0 {
   91         return
   92     }
   93     rx.mu.Lock()
   94     rx.progress = updated
   95     rx.mu.Unlock()
   96     if rx.Callback != nil {
   97         rx.Callback(updated)
   98     }
   99 }
  100 
  101 // transferChunk performs a single HTTP request to upload a single chunk from rx.Media.
  102 func (rx *ResumableUpload) transferChunk(ctx context.Context) (*http.Response, error) {
  103     chunk, off, size, err := rx.Media.Chunk()
  104 
  105     done := err == io.EOF
  106     if !done && err != nil {
  107         return nil, err
  108     }
  109 
  110     res, err := rx.doUploadRequest(ctx, chunk, off, int64(size), done)
  111     if err != nil {
  112         return res, err
  113     }
  114 
  115     if res.StatusCode == statusResumeIncomplete || res.StatusCode == http.StatusOK {
  116         rx.reportProgress(off, off+int64(size))
  117     }
  118 
  119     if res.StatusCode == statusResumeIncomplete {
  120         rx.Media.Next()
  121     }
  122     return res, nil
  123 }
  124 
  125 func contextDone(ctx context.Context) bool {
  126     select {
  127     case <-ctx.Done():
  128         return true
  129     default:
  130         return false
  131     }
  132 }
  133 
  134 // Upload starts the process of a resumable upload with a cancellable context.
  135 // It retries using the provided back off strategy until cancelled or the
  136 // strategy indicates to stop retrying.
  137 // It is called from the auto-generated API code and is not visible to the user.
  138 // rx is private to the auto-generated API code.
  139 // Exactly one of resp or err will be nil.  If resp is non-nil, the caller must call resp.Body.Close.
  140 func (rx *ResumableUpload) Upload(ctx context.Context) (resp *http.Response, err error) {
  141     var pause time.Duration
  142     backoff := rx.Backoff
  143     if backoff == nil {
  144         backoff = DefaultBackoffStrategy()
  145     }
  146 
  147     for {
  148         // Ensure that we return in the case of cancelled context, even if pause is 0.
  149         if contextDone(ctx) {
  150             return nil, ctx.Err()
  151         }
  152         select {
  153         case <-ctx.Done():
  154             return nil, ctx.Err()
  155         case <-time.After(pause):
  156         }
  157 
  158         resp, err = rx.transferChunk(ctx)
  159 
  160         var status int
  161         if resp != nil {
  162             status = resp.StatusCode
  163         }
  164 
  165         // Check if we should retry the request.
  166         if shouldRetry(status, err) {
  167             var retry bool
  168             pause, retry = backoff.Pause()
  169             if retry {
  170                 if resp != nil && resp.Body != nil {
  171                     resp.Body.Close()
  172                 }
  173                 continue
  174             }
  175         }
  176 
  177         // If the chunk was uploaded successfully, but there's still
  178         // more to go, upload the next chunk without any delay.
  179         if status == statusResumeIncomplete {
  180             pause = 0
  181             backoff.Reset()
  182             resp.Body.Close()
  183             continue
  184         }
  185 
  186         // It's possible for err and resp to both be non-nil here, but we expose a simpler
  187         // contract to our callers: exactly one of resp and err will be non-nil.  This means
  188         // that any response body must be closed here before returning a non-nil error.
  189         if err != nil {
  190             if resp != nil && resp.Body != nil {
  191                 resp.Body.Close()
  192             }
  193             return nil, err
  194         }
  195 
  196         return resp, nil
  197     }
  198 }