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 {
|
type copyObjectArgs struct {
|
||||||
Conditional *conditionalArgs
|
Conditional *conditionalArgs
|
||||||
|
MetadataDirective string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const replaceMetadataDirective = "REPLACE"
|
||||||
|
|
||||||
// path2BucketObject returns bucket and object.
|
// path2BucketObject returns bucket and object.
|
||||||
func path2BucketObject(path string) (bucket, prefix string) {
|
func path2BucketObject(path string) (bucket, prefix string) {
|
||||||
path = strings.TrimPrefix(path, api.SlashSeparator)
|
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) {
|
func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
inf *layer.ObjectInfo
|
inf *layer.ObjectInfo
|
||||||
|
metadata map[string]string
|
||||||
|
|
||||||
reqInfo = api.GetReqInfo(r.Context())
|
reqInfo = api.GetReqInfo(r.Context())
|
||||||
)
|
)
|
||||||
|
@ -59,6 +63,13 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
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 {
|
if inf, err = h.obj.GetObjectInfo(r.Context(), srcBucket, srcObject); err != nil {
|
||||||
h.logAndSendError(w, "could not find object", reqInfo, err)
|
h.logAndSendError(w, "could not find object", reqInfo, err)
|
||||||
return
|
return
|
||||||
|
@ -69,20 +80,30 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
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{
|
params := &layer.CopyObjectParams{
|
||||||
SrcBucket: srcBucket,
|
SrcBucket: srcBucket,
|
||||||
DstBucket: reqInfo.BucketName,
|
DstBucket: reqInfo.BucketName,
|
||||||
SrcObject: srcObject,
|
SrcObject: srcObject,
|
||||||
DstObject: reqInfo.ObjectName,
|
DstObject: reqInfo.ObjectName,
|
||||||
SrcSize: inf.Size,
|
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 {
|
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
|
return
|
||||||
} else if err = api.EncodeToResponse(w, &CopyObjectResponse{LastModified: inf.Created.Format(time.RFC3339), ETag: inf.HashSum}); err != nil {
|
} 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,18 +113,6 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
zap.Stringer("object_id", inf.ID()))
|
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) {
|
func parseCopyObjectArgs(headers http.Header) (*copyObjectArgs, error) {
|
||||||
var err error
|
var err error
|
||||||
args := &conditionalArgs{
|
args := &conditionalArgs{
|
||||||
|
@ -118,5 +127,8 @@ func parseCopyObjectArgs(headers http.Header) (*copyObjectArgs, error) {
|
||||||
return nil, err
|
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)
|
metadata := parseMetadata(r)
|
||||||
|
if contentType := r.Header.Get(api.ContentType); len(contentType) > 0 {
|
||||||
|
metadata[api.ContentType] = contentType
|
||||||
|
}
|
||||||
|
|
||||||
params := &layer.PutObjectParams{
|
params := &layer.PutObjectParams{
|
||||||
Bucket: reqInfo.BucketName,
|
Bucket: reqInfo.BucketName,
|
||||||
|
|
|
@ -2,7 +2,8 @@ package api
|
||||||
|
|
||||||
// Standard S3 HTTP request/response constants.
|
// Standard S3 HTTP request/response constants.
|
||||||
const (
|
const (
|
||||||
MetadataPrefix = "X-Amz-Meta-"
|
MetadataPrefix = "X-Amz-Meta-"
|
||||||
|
AmzMetadataDirective = "X-Amz-Metadata-Directive"
|
||||||
|
|
||||||
LastModified = "Last-Modified"
|
LastModified = "Last-Modified"
|
||||||
Date = "Date"
|
Date = "Date"
|
||||||
|
|
Loading…
Reference in a new issue