From f3a6636efd82446013ada7afe8df419d1fec0ac7 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Fri, 6 Aug 2021 18:08:09 +0300 Subject: [PATCH] [#191] Improve copy object compatibility Signed-off-by: Denis Kirillov --- api/handler/copy.go | 50 ++++++++++++++++++++++++++++----------------- api/handler/put.go | 3 +++ api/headers.go | 3 ++- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/api/handler/copy.go b/api/handler/copy.go index af4dbd27..7709654f 100644 --- a/api/handler/copy.go +++ b/api/handler/copy.go @@ -12,9 +12,12 @@ import ( ) type copyObjectArgs struct { - Conditional *conditionalArgs + Conditional *conditionalArgs + MetadataDirective string } +const replaceMetadataDirective = "REPLACE" + // path2BucketObject returns bucket and object. func path2BucketObject(path string) (bucket, prefix string) { path = strings.TrimPrefix(path, api.SlashSeparator) @@ -27,8 +30,9 @@ func path2BucketObject(path string) (bucket, prefix string) { func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { var ( - err error - inf *layer.ObjectInfo + err error + inf *layer.ObjectInfo + metadata map[string]string reqInfo = api.GetReqInfo(r.Context()) ) @@ -59,6 +63,13 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { return } + if args.MetadataDirective == replaceMetadataDirective { + metadata = parseMetadata(r) + } else if srcBucket == reqInfo.BucketName && srcObject == reqInfo.ObjectName { + h.logAndSendError(w, "could not copy to itself", reqInfo, api.GetAPIError(api.ErrInvalidRequest)) + return + } + if inf, err = h.obj.GetObjectInfo(r.Context(), srcBucket, srcObject); err != nil { h.logAndSendError(w, "could not find object", reqInfo, err) return @@ -69,20 +80,30 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { return } + if metadata == nil { + if len(inf.ContentType) > 0 { + inf.Headers[api.ContentType] = inf.ContentType + } + metadata = inf.Headers + } else if contentType := r.Header.Get(api.ContentType); len(contentType) > 0 { + metadata[api.ContentType] = contentType + } + params := &layer.CopyObjectParams{ SrcBucket: srcBucket, DstBucket: reqInfo.BucketName, SrcObject: srcObject, DstObject: reqInfo.ObjectName, SrcSize: inf.Size, - Header: inf.Headers, + Header: metadata, } + additional := []zap.Field{zap.String("src_bucket_name", srcBucket), zap.String("src_object_name", srcObject)} if inf, err = h.obj.CopyObject(r.Context(), params); err != nil { - writeErrorCopy(w, reqInfo, h.log, "could not copy object", srcBucket, srcObject, err) + h.logAndSendError(w, "couldn't copy object", reqInfo, err, additional...) return } else if err = api.EncodeToResponse(w, &CopyObjectResponse{LastModified: inf.Created.Format(time.RFC3339), ETag: inf.HashSum}); err != nil { - writeErrorCopy(w, reqInfo, h.log, "something went wrong", srcBucket, srcObject, err) + h.logAndSendError(w, "something went wrong", reqInfo, err, additional...) return } @@ -92,18 +113,6 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { zap.Stringer("object_id", inf.ID())) } -func writeErrorCopy(w http.ResponseWriter, reqInfo *api.ReqInfo, log *zap.Logger, msg, srcBucket, srcObject string, err error) { - log.Error(msg, - zap.String("request_id", reqInfo.RequestID), - zap.String("dst_bucket_name", reqInfo.BucketName), - zap.String("dst_object_name", reqInfo.ObjectName), - zap.String("src_bucket_name", srcBucket), - zap.String("src_object_name", srcObject), - zap.Error(err)) - - api.WriteErrorResponse(w, reqInfo, err) -} - func parseCopyObjectArgs(headers http.Header) (*copyObjectArgs, error) { var err error args := &conditionalArgs{ @@ -118,5 +127,8 @@ func parseCopyObjectArgs(headers http.Header) (*copyObjectArgs, error) { return nil, err } - return ©ObjectArgs{Conditional: args}, nil + copyArgs := ©ObjectArgs{Conditional: args} + copyArgs.MetadataDirective = headers.Get(api.AmzMetadataDirective) + + return copyArgs, nil } diff --git a/api/handler/put.go b/api/handler/put.go index d8e8d19f..0c402dd2 100644 --- a/api/handler/put.go +++ b/api/handler/put.go @@ -38,6 +38,9 @@ func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) { ) metadata := parseMetadata(r) + if contentType := r.Header.Get(api.ContentType); len(contentType) > 0 { + metadata[api.ContentType] = contentType + } params := &layer.PutObjectParams{ Bucket: reqInfo.BucketName, diff --git a/api/headers.go b/api/headers.go index b90e4035..090ffe94 100644 --- a/api/headers.go +++ b/api/headers.go @@ -2,7 +2,8 @@ package api // Standard S3 HTTP request/response constants. const ( - MetadataPrefix = "X-Amz-Meta-" + MetadataPrefix = "X-Amz-Meta-" + AmzMetadataDirective = "X-Amz-Metadata-Directive" LastModified = "Last-Modified" Date = "Date"