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 }