Skip to content

Commit 65261a9

Browse files
committed
feat: Adds the Content-Disposition, Content-Language, Cache-Control and Expires object meta properties support in the gateway.
Closes #1128 Adds `Content-Disposition`, `Content-Language`, `Cache-Control` and `Expires` object meta properties support in posix and azure backends. Changes the `PutObject` and `CreateMultipartUpload` actions backend input type to custom `s3response` types to be able to store `Expires` as any string.
1 parent 39c8edd commit 65261a9

File tree

10 files changed

+590
-283
lines changed

10 files changed

+590
-283
lines changed

backend/azure/azure.go

+53-23
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ const (
6363
keyBucketLock key = "Bucketlock"
6464
keyObjRetention key = "Objectretention"
6565
keyObjLegalHold key = "Objectlegalhold"
66+
keyExpires key = "Vgwexpires"
6667
onameAttr key = "Objname"
6768
onameAttrLower key = "objname"
6869
metaTmpMultipartPrefix key = ".sgwtmp" + "/multipart"
@@ -76,6 +77,7 @@ func (key) Table() map[string]struct{} {
7677
"policy": {},
7778
"bucketlock": {},
7879
"objectretention": {},
80+
"vgwexpires": {},
7981
"objectlegalhold": {},
8082
"objname": {},
8183
".sgwtmp/multipart": {},
@@ -292,21 +294,36 @@ func (az *Azure) DeleteBucketOwnershipControls(ctx context.Context, bucket strin
292294
return az.deleteContainerMetaData(ctx, bucket, string(keyOwnership))
293295
}
294296

295-
func (az *Azure) PutObject(ctx context.Context, po *s3.PutObjectInput) (s3response.PutObjectOutput, error) {
297+
func (az *Azure) PutObject(ctx context.Context, po s3response.PutObjectInput) (s3response.PutObjectOutput, error) {
296298
tags, err := parseTags(po.Tagging)
297299
if err != nil {
298300
return s3response.PutObjectOutput{}, err
299301
}
300302

303+
metadata := parseMetadata(po.Metadata)
304+
305+
// Store the "Expires" property in the object metadata
306+
if getString(po.Expires) != "" {
307+
if metadata == nil {
308+
metadata = map[string]*string{
309+
string(keyExpires): po.Expires,
310+
}
311+
} else {
312+
metadata[string(keyExpires)] = po.Expires
313+
}
314+
}
315+
301316
opts := &blockblob.UploadStreamOptions{
302-
Metadata: parseMetadata(po.Metadata),
317+
Metadata: metadata,
303318
Tags: tags,
304319
}
305320

306321
opts.HTTPHeaders = &blob.HTTPHeaders{}
307322
opts.HTTPHeaders.BlobContentEncoding = po.ContentEncoding
308323
opts.HTTPHeaders.BlobContentLanguage = po.ContentLanguage
309324
opts.HTTPHeaders.BlobContentDisposition = po.ContentDisposition
325+
opts.HTTPHeaders.BlobContentLanguage = po.ContentLanguage
326+
opts.HTTPHeaders.BlobCacheControl = po.CacheControl
310327
if strings.HasSuffix(*po.Key, "/") {
311328
// Hardcode "application/x-directory" for direcoty objects
312329
opts.HTTPHeaders.BlobContentType = backend.GetPtrFromString(backend.DirContentType)
@@ -430,17 +447,21 @@ func (az *Azure) GetObject(ctx context.Context, input *s3.GetObjectInput) (*s3.G
430447
}
431448

432449
return &s3.GetObjectOutput{
433-
AcceptRanges: backend.GetPtrFromString("bytes"),
434-
ContentLength: blobDownloadResponse.ContentLength,
435-
ContentEncoding: blobDownloadResponse.ContentEncoding,
436-
ContentType: contentType,
437-
ETag: (*string)(blobDownloadResponse.ETag),
438-
LastModified: blobDownloadResponse.LastModified,
439-
Metadata: parseAzMetadata(blobDownloadResponse.Metadata),
440-
TagCount: &tagcount,
441-
ContentRange: blobDownloadResponse.ContentRange,
442-
Body: blobDownloadResponse.Body,
443-
StorageClass: types.StorageClassStandard,
450+
AcceptRanges: backend.GetPtrFromString("bytes"),
451+
ContentLength: blobDownloadResponse.ContentLength,
452+
ContentEncoding: blobDownloadResponse.ContentEncoding,
453+
ContentType: contentType,
454+
ContentDisposition: blobDownloadResponse.ContentDisposition,
455+
ContentLanguage: blobDownloadResponse.ContentLanguage,
456+
CacheControl: blobDownloadResponse.CacheControl,
457+
ExpiresString: blobDownloadResponse.Metadata[string(keyExpires)],
458+
ETag: (*string)(blobDownloadResponse.ETag),
459+
LastModified: blobDownloadResponse.LastModified,
460+
Metadata: parseAzMetadata(blobDownloadResponse.Metadata),
461+
TagCount: &tagcount,
462+
ContentRange: blobDownloadResponse.ContentRange,
463+
Body: blobDownloadResponse.Body,
464+
StorageClass: types.StorageClassStandard,
444465
}, nil
445466
}
446467

@@ -494,10 +515,11 @@ func (az *Azure) HeadObject(ctx context.Context, input *s3.HeadObjectInput) (*s3
494515
ContentEncoding: resp.ContentEncoding,
495516
ContentLanguage: resp.ContentLanguage,
496517
ContentDisposition: resp.ContentDisposition,
518+
CacheControl: resp.CacheControl,
519+
ExpiresString: resp.Metadata[string(keyExpires)],
497520
ETag: (*string)(resp.ETag),
498521
LastModified: resp.LastModified,
499522
Metadata: parseAzMetadata(resp.Metadata),
500-
Expires: resp.ExpiresOn,
501523
StorageClass: types.StorageClassStandard,
502524
}
503525

@@ -826,7 +848,7 @@ func (az *Azure) DeleteObjectTagging(ctx context.Context, bucket, object string)
826848
return nil
827849
}
828850

829-
func (az *Azure) CreateMultipartUpload(ctx context.Context, input *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) {
851+
func (az *Azure) CreateMultipartUpload(ctx context.Context, input s3response.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) {
830852
if input.ObjectLockLegalHoldStatus != "" || input.ObjectLockMode != "" {
831853
bucketLock, err := az.getContainerMetaData(ctx, *input.Bucket, string(keyBucketLock))
832854
if err != nil {
@@ -850,6 +872,10 @@ func (az *Azure) CreateMultipartUpload(ctx context.Context, input *s3.CreateMult
850872
meta := parseMetadata(input.Metadata)
851873
meta[string(onameAttr)] = input.Key
852874

875+
if getString(input.Expires) != "" {
876+
meta[string(keyExpires)] = input.Expires
877+
}
878+
853879
// parse object tags
854880
tagsStr := getString(input.Tagging)
855881
tags := map[string]string{}
@@ -892,12 +918,13 @@ func (az *Azure) CreateMultipartUpload(ctx context.Context, input *s3.CreateMult
892918
opts := &blockblob.UploadBufferOptions{
893919
Metadata: meta,
894920
Tags: tags,
895-
}
896-
if getString(input.ContentType) != "" {
897-
opts.HTTPHeaders = &blob.HTTPHeaders{
898-
BlobContentType: input.ContentType,
899-
BlobContentEncoding: input.ContentEncoding,
900-
}
921+
HTTPHeaders: &blob.HTTPHeaders{
922+
BlobContentType: input.ContentType,
923+
BlobContentEncoding: input.ContentEncoding,
924+
BlobCacheControl: input.CacheControl,
925+
BlobContentDisposition: input.ContentDisposition,
926+
BlobContentLanguage: input.ContentLanguage,
927+
},
901928
}
902929

903930
// Create and empty blob in .sgwtmp/multipart/<uploadId>/<object hash>
@@ -1260,8 +1287,11 @@ func (az *Azure) CompleteMultipartUpload(ctx context.Context, input *s3.Complete
12601287
Tags: parseAzTags(tags.BlobTagSet),
12611288
}
12621289
opts.HTTPHeaders = &blob.HTTPHeaders{
1263-
BlobContentType: props.ContentType,
1264-
BlobContentEncoding: props.ContentEncoding,
1290+
BlobContentType: props.ContentType,
1291+
BlobContentEncoding: props.ContentEncoding,
1292+
BlobContentDisposition: props.ContentDisposition,
1293+
BlobContentLanguage: props.ContentLanguage,
1294+
BlobCacheControl: props.CacheControl,
12651295
}
12661296

12671297
resp, err := client.CommitBlockList(ctx, blockIds, opts)

backend/backend.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type Backend interface {
4848
DeleteBucketOwnershipControls(_ context.Context, bucket string) error
4949

5050
// multipart operations
51-
CreateMultipartUpload(context.Context, *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error)
51+
CreateMultipartUpload(context.Context, s3response.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error)
5252
CompleteMultipartUpload(context.Context, *s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error)
5353
AbortMultipartUpload(context.Context, *s3.AbortMultipartUploadInput) error
5454
ListMultipartUploads(context.Context, *s3.ListMultipartUploadsInput) (s3response.ListMultipartUploadsResult, error)
@@ -57,7 +57,7 @@ type Backend interface {
5757
UploadPartCopy(context.Context, *s3.UploadPartCopyInput) (s3response.CopyPartResult, error)
5858

5959
// standard object operations
60-
PutObject(context.Context, *s3.PutObjectInput) (s3response.PutObjectOutput, error)
60+
PutObject(context.Context, s3response.PutObjectInput) (s3response.PutObjectOutput, error)
6161
HeadObject(context.Context, *s3.HeadObjectInput) (*s3.HeadObjectOutput, error)
6262
GetObject(context.Context, *s3.GetObjectInput) (*s3.GetObjectOutput, error)
6363
GetObjectAcl(context.Context, *s3.GetObjectAclInput) (*s3.GetObjectAclOutput, error)
@@ -151,7 +151,7 @@ func (BackendUnsupported) DeleteBucketOwnershipControls(_ context.Context, bucke
151151
return s3err.GetAPIError(s3err.ErrNotImplemented)
152152
}
153153

154-
func (BackendUnsupported) CreateMultipartUpload(context.Context, *s3.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) {
154+
func (BackendUnsupported) CreateMultipartUpload(context.Context, s3response.CreateMultipartUploadInput) (s3response.InitiateMultipartUploadResult, error) {
155155
return s3response.InitiateMultipartUploadResult{}, s3err.GetAPIError(s3err.ErrNotImplemented)
156156
}
157157
func (BackendUnsupported) CompleteMultipartUpload(context.Context, *s3.CompleteMultipartUploadInput) (*s3.CompleteMultipartUploadOutput, error) {
@@ -173,7 +173,7 @@ func (BackendUnsupported) UploadPartCopy(context.Context, *s3.UploadPartCopyInpu
173173
return s3response.CopyPartResult{}, s3err.GetAPIError(s3err.ErrNotImplemented)
174174
}
175175

176-
func (BackendUnsupported) PutObject(context.Context, *s3.PutObjectInput) (s3response.PutObjectOutput, error) {
176+
func (BackendUnsupported) PutObject(context.Context, s3response.PutObjectInput) (s3response.PutObjectOutput, error) {
177177
return s3response.PutObjectOutput{}, s3err.GetAPIError(s3err.ErrNotImplemented)
178178
}
179179
func (BackendUnsupported) HeadObject(context.Context, *s3.HeadObjectInput) (*s3.HeadObjectOutput, error) {

0 commit comments

Comments
 (0)