github.com/artpar/rclone@v1.67.3/backend/imagekit/util.go (about)

     1  package imagekit
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"strconv"
     8  	"time"
     9  
    10  	"github.com/artpar/rclone/backend/imagekit/client"
    11  	"github.com/artpar/rclone/fs"
    12  	"github.com/artpar/rclone/fs/fserrors"
    13  	"github.com/artpar/rclone/lib/pacer"
    14  )
    15  
    16  func (f *Fs) getFiles(ctx context.Context, path string, includeVersions bool) (files []client.File, err error) {
    17  
    18  	files = make([]client.File, 0)
    19  
    20  	var hasMore = true
    21  
    22  	for hasMore {
    23  		err = f.pacer.Call(func() (bool, error) {
    24  			var data *[]client.File
    25  			var res *http.Response
    26  			res, data, err = f.ik.Files(ctx, client.FilesOrFolderParam{
    27  				Skip:  len(files),
    28  				Limit: 100,
    29  				Path:  path,
    30  			}, includeVersions)
    31  
    32  			hasMore = !(len(*data) == 0 || len(*data) < 100)
    33  
    34  			if len(*data) > 0 {
    35  				files = append(files, *data...)
    36  			}
    37  
    38  			return f.shouldRetry(ctx, res, err)
    39  		})
    40  	}
    41  
    42  	if err != nil {
    43  		return make([]client.File, 0), err
    44  	}
    45  
    46  	return files, nil
    47  }
    48  
    49  func (f *Fs) getFolders(ctx context.Context, path string) (folders []client.Folder, err error) {
    50  
    51  	folders = make([]client.Folder, 0)
    52  
    53  	var hasMore = true
    54  
    55  	for hasMore {
    56  		err = f.pacer.Call(func() (bool, error) {
    57  			var data *[]client.Folder
    58  			var res *http.Response
    59  			res, data, err = f.ik.Folders(ctx, client.FilesOrFolderParam{
    60  				Skip:  len(folders),
    61  				Limit: 100,
    62  				Path:  path,
    63  			})
    64  
    65  			hasMore = !(len(*data) == 0 || len(*data) < 100)
    66  
    67  			if len(*data) > 0 {
    68  				folders = append(folders, *data...)
    69  			}
    70  
    71  			return f.shouldRetry(ctx, res, err)
    72  		})
    73  	}
    74  
    75  	if err != nil {
    76  		return make([]client.Folder, 0), err
    77  	}
    78  
    79  	return folders, nil
    80  }
    81  
    82  func (f *Fs) getFileByName(ctx context.Context, path string, name string) (file *client.File) {
    83  
    84  	err := f.pacer.Call(func() (bool, error) {
    85  		res, data, err := f.ik.Files(ctx, client.FilesOrFolderParam{
    86  			Limit:       1,
    87  			Path:        path,
    88  			SearchQuery: fmt.Sprintf(`type = "file" AND name = %s`, strconv.Quote(name)),
    89  		}, false)
    90  
    91  		if len(*data) == 0 {
    92  			file = nil
    93  		} else {
    94  			file = &(*data)[0]
    95  		}
    96  
    97  		return f.shouldRetry(ctx, res, err)
    98  	})
    99  
   100  	if err != nil {
   101  		return nil
   102  	}
   103  
   104  	return file
   105  }
   106  
   107  func (f *Fs) getFolderByName(ctx context.Context, path string, name string) (folder *client.Folder, err error) {
   108  	err = f.pacer.Call(func() (bool, error) {
   109  		res, data, err := f.ik.Folders(ctx, client.FilesOrFolderParam{
   110  			Limit:       1,
   111  			Path:        path,
   112  			SearchQuery: fmt.Sprintf(`type = "folder" AND name = %s`, strconv.Quote(name)),
   113  		})
   114  
   115  		if len(*data) == 0 {
   116  			folder = nil
   117  		} else {
   118  			folder = &(*data)[0]
   119  		}
   120  
   121  		return f.shouldRetry(ctx, res, err)
   122  	})
   123  
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	return folder, nil
   129  }
   130  
   131  // retryErrorCodes is a slice of error codes that we will retry
   132  var retryErrorCodes = []int{
   133  	401, // Unauthorized (e.g. "Token has expired")
   134  	408, // Request Timeout
   135  	429, // Rate exceeded.
   136  	500, // Get occasional 500 Internal Server Error
   137  	503, // Service Unavailable
   138  	504, // Gateway Time-out
   139  }
   140  
   141  func shouldRetryHTTP(resp *http.Response, retryErrorCodes []int) bool {
   142  	if resp == nil {
   143  		return false
   144  	}
   145  	for _, e := range retryErrorCodes {
   146  		if resp.StatusCode == e {
   147  			return true
   148  		}
   149  	}
   150  	return false
   151  }
   152  
   153  func (f *Fs) shouldRetry(ctx context.Context, resp *http.Response, err error) (bool, error) {
   154  	if fserrors.ContextError(ctx, &err) {
   155  		return false, err
   156  	}
   157  
   158  	if resp != nil && (resp.StatusCode == 429 || resp.StatusCode == 503) {
   159  		var retryAfter = 1
   160  		retryAfterString := resp.Header.Get("X-RateLimit-Reset")
   161  		if retryAfterString != "" {
   162  			var err error
   163  			retryAfter, err = strconv.Atoi(retryAfterString)
   164  			if err != nil {
   165  				fs.Errorf(f, "Malformed %s header %q: %v", "X-RateLimit-Reset", retryAfterString, err)
   166  			}
   167  		}
   168  
   169  		return true, pacer.RetryAfterError(err, time.Duration(retryAfter)*time.Millisecond)
   170  	}
   171  
   172  	return fserrors.ShouldRetry(err) || shouldRetryHTTP(resp, retryErrorCodes), err
   173  }
   174  
   175  // EncodePath encapsulates the logic for encoding a path
   176  func (f *Fs) EncodePath(str string) string {
   177  	return f.opt.Enc.FromStandardPath(str)
   178  }
   179  
   180  // DecodePath encapsulates the logic for decoding a path
   181  func (f *Fs) DecodePath(str string) string {
   182  	return f.opt.Enc.ToStandardPath(str)
   183  }
   184  
   185  // EncodeFileName encapsulates the logic for encoding a file name
   186  func (f *Fs) EncodeFileName(str string) string {
   187  	return f.opt.Enc.FromStandardName(str)
   188  }
   189  
   190  // DecodeFileName encapsulates the logic for decoding a file name
   191  func (f *Fs) DecodeFileName(str string) string {
   192  	return f.opt.Enc.ToStandardName(str)
   193  }