mirror of
https://github.com/minio/minio.git
synced 2025-11-07 12:52:58 -05:00
Filter Expires header from user metadata (#7269)
Instead save it as a struct field in ObjectInfo as it is a standard HTTP header - Fixes minio/mc#2690
This commit is contained in:
@@ -87,6 +87,9 @@ func setObjectHeaders(w http.ResponseWriter, objInfo ObjectInfo, rs *HTTPRangeSp
|
||||
w.Header().Set("Content-Encoding", objInfo.ContentEncoding)
|
||||
}
|
||||
|
||||
if !objInfo.Expires.IsZero() {
|
||||
w.Header().Set("Expires", objInfo.Expires.UTC().Format(http.TimeFormat))
|
||||
}
|
||||
// Set all other user defined metadata.
|
||||
for k, v := range objInfo.UserDefined {
|
||||
if hasPrefix(k, ReservedMetadataPrefix) {
|
||||
|
||||
@@ -22,8 +22,10 @@ import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
pathutil "path"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
"github.com/minio/minio/pkg/lock"
|
||||
@@ -169,7 +171,15 @@ func (m fsMetaV1) ToObjectInfo(bucket, object string, fi os.FileInfo) ObjectInfo
|
||||
} else {
|
||||
objInfo.StorageClass = globalMinioDefaultStorageClass
|
||||
}
|
||||
|
||||
var (
|
||||
t time.Time
|
||||
e error
|
||||
)
|
||||
if exp, ok := m.Meta["expires"]; ok {
|
||||
if t, e = time.Parse(http.TimeFormat, exp); e == nil {
|
||||
objInfo.Expires = t.UTC()
|
||||
}
|
||||
}
|
||||
// etag/md5Sum has already been extracted. We need to
|
||||
// remove to avoid it from appearing as part of
|
||||
// response headers. e.g, X-Minio-* or X-Amz-*.
|
||||
|
||||
@@ -38,6 +38,9 @@ func TestFSV1MetadataObjInfo(t *testing.T) {
|
||||
if objInfo.IsDir {
|
||||
t.Fatal("Unexpected object info value for IsDir", objInfo.IsDir)
|
||||
}
|
||||
if !objInfo.Expires.IsZero() {
|
||||
t.Fatal("Unexpected object info value for Expires ", objInfo.Expires)
|
||||
}
|
||||
}
|
||||
|
||||
// TestReadFSMetadata - readFSMetadata testing with a healthy and faulty disk
|
||||
|
||||
@@ -166,6 +166,7 @@ func FromMinioClientObjectInfo(bucket string, oi minio.ObjectInfo) ObjectInfo {
|
||||
ContentType: oi.ContentType,
|
||||
ContentEncoding: oi.Metadata.Get("Content-Encoding"),
|
||||
StorageClass: oi.StorageClass,
|
||||
Expires: oi.Expires,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -797,12 +797,22 @@ func fromGCSAttrsToObjectInfo(attrs *storage.ObjectAttrs) minio.ObjectInfo {
|
||||
// All google cloud storage objects have a CRC32c hash, whereas composite objects may not have a MD5 hash
|
||||
// Refer https://cloud.google.com/storage/docs/hashes-etags. Use CRC32C for ETag
|
||||
metadata := make(map[string]string)
|
||||
var (
|
||||
expiry time.Time
|
||||
e error
|
||||
)
|
||||
for k, v := range attrs.Metadata {
|
||||
k = http.CanonicalHeaderKey(k)
|
||||
// Translate the GCS custom metadata prefix
|
||||
if strings.HasPrefix(k, "X-Goog-Meta-") {
|
||||
k = strings.Replace(k, "X-Goog-Meta-", "X-Amz-Meta-", 1)
|
||||
}
|
||||
if k == "Expires" {
|
||||
if expiry, e = time.Parse(http.TimeFormat, v); e == nil {
|
||||
expiry = expiry.UTC()
|
||||
}
|
||||
continue
|
||||
}
|
||||
metadata[k] = v
|
||||
}
|
||||
if attrs.ContentType != "" {
|
||||
@@ -829,6 +839,7 @@ func fromGCSAttrsToObjectInfo(attrs *storage.ObjectAttrs) minio.ObjectInfo {
|
||||
UserDefined: metadata,
|
||||
ContentType: attrs.ContentType,
|
||||
ContentEncoding: attrs.ContentEncoding,
|
||||
Expires: expiry,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
minio "github.com/minio/minio/cmd"
|
||||
@@ -83,6 +84,7 @@ func (m gwMetaV1) ToObjectInfo(bucket, object string) minio.ObjectInfo {
|
||||
"Content-Length",
|
||||
"Last-Modified",
|
||||
"Content-Type",
|
||||
"Expires",
|
||||
}, defaultFilterKeys...)
|
||||
objInfo := minio.ObjectInfo{
|
||||
IsDir: false,
|
||||
@@ -100,6 +102,15 @@ func (m gwMetaV1) ToObjectInfo(bucket, object string) minio.ObjectInfo {
|
||||
if sc, ok := m.Meta["x-amz-storage-class"]; ok {
|
||||
objInfo.StorageClass = sc
|
||||
}
|
||||
var (
|
||||
t time.Time
|
||||
e error
|
||||
)
|
||||
if exp, ok := m.Meta["expires"]; ok {
|
||||
if t, e = time.Parse(http.TimeFormat, exp); e == nil {
|
||||
objInfo.Expires = t.UTC()
|
||||
}
|
||||
}
|
||||
// Success.
|
||||
return objInfo
|
||||
}
|
||||
|
||||
@@ -96,6 +96,9 @@ type ObjectInfo struct {
|
||||
// by the Content-Type header field.
|
||||
ContentEncoding string
|
||||
|
||||
// Date and time at which the object is no longer able to be cached
|
||||
Expires time.Time
|
||||
|
||||
// Specify object storage class
|
||||
StorageClass string
|
||||
|
||||
|
||||
@@ -207,8 +207,8 @@ func getCompleteMultipartMD5(ctx context.Context, parts []CompletePart) (string,
|
||||
func cleanMetadata(metadata map[string]string) map[string]string {
|
||||
// Remove STANDARD StorageClass
|
||||
metadata = removeStandardStorageClass(metadata)
|
||||
// Clean meta etag keys 'md5Sum', 'etag'.
|
||||
return cleanMetadataKeys(metadata, "md5Sum", "etag")
|
||||
// Clean meta etag keys 'md5Sum', 'etag', "expires".
|
||||
return cleanMetadataKeys(metadata, "md5Sum", "etag", "expires")
|
||||
}
|
||||
|
||||
// Filter X-Amz-Storage-Class field only if it is set to STANDARD.
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"path"
|
||||
"sort"
|
||||
"sync"
|
||||
@@ -226,7 +227,16 @@ func (m xlMetaV1) ToObjectInfo(bucket, object string) ObjectInfo {
|
||||
ContentType: m.Meta["content-type"],
|
||||
ContentEncoding: m.Meta["content-encoding"],
|
||||
}
|
||||
|
||||
// Update expires
|
||||
var (
|
||||
t time.Time
|
||||
e error
|
||||
)
|
||||
if exp, ok := m.Meta["expires"]; ok {
|
||||
if t, e = time.Parse(http.TimeFormat, exp); e == nil {
|
||||
objInfo.Expires = t.UTC()
|
||||
}
|
||||
}
|
||||
objInfo.backendType = BackendErasure
|
||||
|
||||
// Extract etag from metadata.
|
||||
|
||||
Reference in New Issue
Block a user