github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/config/cache/remote.go (about)

     1  package cache
     2  
     3  import (
     4  	"net/http"
     5  	"regexp"
     6  	"strconv"
     7  	"time"
     8  
     9  	"github.com/minio/minio/internal/amztime"
    10  	xhttp "github.com/minio/minio/internal/http"
    11  )
    12  
    13  //go:generate msgp -file=$GOFILE
    14  
    15  // ObjectInfo represents the object information cached remotely
    16  type ObjectInfo struct {
    17  	Key        string    `json:"key"`
    18  	Bucket     string    `json:"bucket"`
    19  	ETag       string    `json:"etag"`
    20  	ModTime    time.Time `json:"modTime"`
    21  	StatusCode int       `json:"statusCode"`
    22  
    23  	// Optional elements
    24  	CacheControl string            `json:"cacheControl,omitempty" msg:",omitempty"`
    25  	Expires      string            `json:"expires,omitempty" msg:",omitempty"`
    26  	Metadata     map[string]string `json:"metadata,omitempty" msg:",omitempty"`
    27  	Range        string            `json:"range,omitempty" msg:",omitempty"`
    28  	PartNumber   int               `json:"partNumber,omitempty" msg:",omitempty"`
    29  	Size         int64             `json:"size,omitempty" msg:",omitempty"` // Full size of the object
    30  	Data         []byte            `json:"data,omitempty" msg:",omitempty"` // Data can container full data of the object or partial
    31  }
    32  
    33  // WriteHeaders writes the response headers for conditional requests
    34  func (oi ObjectInfo) WriteHeaders(w http.ResponseWriter, preamble, statusCode func()) {
    35  	preamble()
    36  
    37  	if !oi.ModTime.IsZero() {
    38  		w.Header().Set(xhttp.LastModified, oi.ModTime.UTC().Format(http.TimeFormat))
    39  	}
    40  
    41  	if oi.ETag != "" {
    42  		w.Header()[xhttp.ETag] = []string{"\"" + oi.ETag + "\""}
    43  	}
    44  
    45  	if oi.Expires != "" {
    46  		w.Header().Set(xhttp.Expires, oi.Expires)
    47  	}
    48  
    49  	if oi.CacheControl != "" {
    50  		w.Header().Set(xhttp.CacheControl, oi.CacheControl)
    51  	}
    52  
    53  	statusCode()
    54  }
    55  
    56  // CondCheck represents the conditional request made to the remote cache
    57  // for validation during GET/HEAD object requests.
    58  type CondCheck struct {
    59  	ObjectInfo
    60  	IfMatch           string     `json:"ifMatch,omitempty" msg:",omitempty"`
    61  	IfNoneMatch       string     `json:"ifNoneMatch,omitempty" msg:",omitempty"`
    62  	IfModifiedSince   *time.Time `json:"ifModSince,omitempty" msg:",omitempty"`
    63  	IfUnModifiedSince *time.Time `json:"ifUnmodSince,omitempty" msg:",omitempty"`
    64  	IfRange           string     `json:"ifRange,omitempty" msg:",omitempty"`
    65  	IfPartNumber      int        `json:"ifPartNumber,omitempty" msg:",omitempty"`
    66  }
    67  
    68  // IsSet tells the cache lookup to avoid sending a request
    69  func (r *CondCheck) IsSet() bool {
    70  	if r == nil {
    71  		return false
    72  	}
    73  	return r.IfMatch != "" || r.IfNoneMatch != "" || r.IfModifiedSince != nil || r.IfUnModifiedSince != nil || r.IfRange != ""
    74  }
    75  
    76  var etagRegex = regexp.MustCompile("\"*?([^\"]*?)\"*?$")
    77  
    78  // canonicalizeETag returns ETag with leading and trailing double-quotes removed,
    79  // if any present
    80  func canonicalizeETag(etag string) string {
    81  	return etagRegex.ReplaceAllString(etag, "$1")
    82  }
    83  
    84  // Init - populates the input values, initializes CondCheck
    85  // before sending the request remotely.
    86  func (r *CondCheck) Init(bucket, object string, header http.Header) {
    87  	r.Key = object
    88  	r.Bucket = bucket
    89  
    90  	ifModifiedSinceHeader := header.Get(xhttp.IfModifiedSince)
    91  	if ifModifiedSinceHeader != "" {
    92  		if givenTime, err := amztime.ParseHeader(ifModifiedSinceHeader); err == nil {
    93  			r.IfModifiedSince = &givenTime
    94  		}
    95  	}
    96  	ifUnmodifiedSinceHeader := header.Get(xhttp.IfUnmodifiedSince)
    97  	if ifUnmodifiedSinceHeader != "" {
    98  		if givenTime, err := amztime.ParseHeader(ifUnmodifiedSinceHeader); err == nil {
    99  			r.IfUnModifiedSince = &givenTime
   100  		}
   101  	}
   102  	r.IfMatch = canonicalizeETag(header.Get(xhttp.IfMatch))
   103  	r.IfNoneMatch = canonicalizeETag(header.Get(xhttp.IfNoneMatch))
   104  	r.IfRange = header.Get(xhttp.Range)
   105  	ifPartNumberHeader := header.Get(xhttp.PartNumber)
   106  	if ifPartNumberHeader != "" {
   107  		if partNumber, err := strconv.Atoi(ifPartNumberHeader); err == nil {
   108  			r.IfPartNumber = partNumber
   109  		}
   110  	}
   111  }