forked from TrueCloudLab/frostfs-s3-gw
[#191] Improve copy object compatibility
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
7eb9713a67
commit
f3a6636efd
3 changed files with 36 additions and 20 deletions
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue