Skip to content

Document objectattribute mapping #1119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion api/handler/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/nspcc-dev/neofs-s3-gw/api/data"
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
"github.com/nspcc-dev/neofs-s3-gw/api/s3errors"
"github.com/nspcc-dev/neofs-s3-gw/api/s3headers"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -206,7 +207,7 @@ func encodeToObjectAttributesResponse(info *data.ObjectInfo, p *GetObjectAttribu
}

func formUploadAttributes(info *data.ObjectInfo, maxParts, marker int) (*ObjectParts, error) {
completedParts, ok := info.Headers[layer.UploadCompletedParts]
completedParts, ok := info.Headers[s3headers.UploadCompletedParts]
if !ok {
return nil, nil
}
Expand Down
7 changes: 4 additions & 3 deletions api/handler/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/nspcc-dev/neofs-s3-gw/api/data"
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
"github.com/nspcc-dev/neofs-s3-gw/api/s3errors"
"github.com/nspcc-dev/neofs-s3-gw/api/s3headers"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -88,8 +89,8 @@ func writeHeaders(h http.Header, requestHeader http.Header, info *data.ObjectInf
}
h.Set(api.LastModified, info.Created.UTC().Format(http.TimeFormat))

if len(info.Headers[layer.AttributeEncryptionAlgorithm]) > 0 {
h.Set(api.ContentLength, info.Headers[layer.AttributeDecryptedSize])
if len(info.Headers[s3headers.AttributeEncryptionAlgorithm]) > 0 {
h.Set(api.ContentLength, info.Headers[s3headers.AttributeDecryptedSize])
addSSECHeaders(h, requestHeader)
} else {
h.Set(api.ContentLength, strconv.FormatInt(info.Size, 10))
Expand Down Expand Up @@ -186,7 +187,7 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) {

fullSize := info.Size
if encryptionParams.Enabled() {
if fullSize, err = strconv.ParseInt(info.Headers[layer.AttributeDecryptedSize], 10, 64); err != nil {
if fullSize, err = strconv.ParseInt(info.Headers[s3headers.AttributeDecryptedSize], 10, 64); err != nil {
h.logAndSendError(w, "invalid decrypted size header", reqInfo, s3errors.GetAPIError(s3errors.ErrBadRequest))
return
}
Expand Down
20 changes: 10 additions & 10 deletions api/headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ package api

// Standard S3 HTTP request/response constants.
const (
MetadataPrefix = "X-Amz-Meta-"
NeoFSSystemMetadataPrefix = "S3-"
AmzMetadataDirective = "X-Amz-Metadata-Directive"
AmzTaggingDirective = "X-Amz-Tagging-Directive"
AmzVersionID = "X-Amz-Version-Id"
AmzTaggingCount = "X-Amz-Tagging-Count"
AmzTagging = "X-Amz-Tagging"
AmzDeleteMarker = "X-Amz-Delete-Marker"
AmzCopySource = "X-Amz-Copy-Source"
AmzCopySourceRange = "X-Amz-Copy-Source-Range"
MetadataPrefix = "X-Amz-Meta-"

AmzMetadataDirective = "X-Amz-Metadata-Directive"
AmzTaggingDirective = "X-Amz-Tagging-Directive"
AmzVersionID = "X-Amz-Version-Id"
AmzTaggingCount = "X-Amz-Tagging-Count"
AmzTagging = "X-Amz-Tagging"
AmzDeleteMarker = "X-Amz-Delete-Marker"
AmzCopySource = "X-Amz-Copy-Source"
AmzCopySourceRange = "X-Amz-Copy-Source-Range"

LastModified = "Last-Modified"
Date = "Date"
Expand Down
19 changes: 7 additions & 12 deletions api/layer/layer.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,8 @@ type (
const (
tagPrefix = "S3-Tag-"

AESEncryptionAlgorithm = "AES256"
AESKeySize = 32
AttributeEncryptionAlgorithm = api.NeoFSSystemMetadataPrefix + "Algorithm"
AttributeDecryptedSize = api.NeoFSSystemMetadataPrefix + "Decrypted-Size"
AttributeHMACSalt = api.NeoFSSystemMetadataPrefix + "HMAC-Salt"
AttributeHMACKey = api.NeoFSSystemMetadataPrefix + "HMAC-Key"

AESEncryptionAlgorithm = "AES256"
AESKeySize = 32
AttributeNeofsCopiesNumber = "neofs-copies-number" // such format to match X-Amz-Meta-Neofs-Copies-Number header
)

Expand Down Expand Up @@ -536,12 +531,12 @@ func getDecrypter(p *GetObjectParams) (*encryption.Decrypter, error) {
encRange = &encryption.Range{Start: p.Range.Start, End: p.Range.End}
}

header := p.ObjectInfo.Headers[UploadCompletedParts]
header := p.ObjectInfo.Headers[s3headers.UploadCompletedParts]
if len(header) == 0 {
return encryption.NewDecrypter(p.Encryption, uint64(p.ObjectInfo.Size), encRange)
}

decryptedObjectSize, err := strconv.ParseUint(p.ObjectInfo.Headers[AttributeDecryptedSize], 10, 64)
decryptedObjectSize, err := strconv.ParseUint(p.ObjectInfo.Headers[s3headers.AttributeDecryptedSize], 10, 64)
if err != nil {
return nil, fmt.Errorf("parse decrypted size: %w", err)
}
Expand Down Expand Up @@ -773,7 +768,7 @@ func (n *layer) GetIDForVersioningContainer(ctx context.Context, p *ShortInfoPar
object.AttributeFilePath,
object.FilterCreationEpoch,
object.AttributeTimestamp,
attrS3DeleteMarker,
s3headers.AttributeDeleteMarker,
}

opts client.SearchObjectsOptions
Expand All @@ -788,7 +783,7 @@ func (n *layer) GetIDForVersioningContainer(ctx context.Context, p *ShortInfoPar
filters.AddFilter(s3headers.MetaType, "", object.MatchNotPresent)

if !p.FindNullVersion {
filters.AddFilter(attrS3VersioningState, data.VersioningEnabled, object.MatchStringEqual)
filters.AddFilter(s3headers.AttributeVersioningState, data.VersioningEnabled, object.MatchStringEqual)
}

ids, err := n.neoFS.SearchObjectsV2(ctx, p.CID, filters, returningAttributes, opts)
Expand Down Expand Up @@ -1072,7 +1067,7 @@ func (n *layer) putDeleteMarker(ctx context.Context, bktInfo *data.BucketInfo, o
Object: objectName,
Reader: bytes.NewReader(nil),
Header: map[string]string{
attrS3DeleteMarker: ts,
s3headers.AttributeDeleteMarker: ts,
},
}
)
Expand Down
28 changes: 13 additions & 15 deletions api/layer/multipart_upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ import (
)

const (
UploadCompletedParts = "S3-Completed-Parts"

metaPrefix = "meta-"
aclPrefix = "acl-"

Expand Down Expand Up @@ -261,7 +259,7 @@ func (n *layer) uploadPart(ctx context.Context, multipartInfo *data.MultipartInf
if err != nil {
return nil, fmt.Errorf("failed to create ecnrypted reader: %w", err)
}
attributes[AttributeDecryptedSize] = strconv.FormatInt(p.Size, 10)
attributes[s3headers.AttributeDecryptedSize] = strconv.FormatInt(p.Size, 10)
payloadReader = r
p.Size = int64(encSize)
}
Expand Down Expand Up @@ -429,7 +427,7 @@ func (n *layer) uploadZeroPart(ctx context.Context, multipartInfo *data.Multipar
)

if p.Encryption.Enabled() {
attributes[AttributeDecryptedSize] = "0"
attributes[s3headers.AttributeDecryptedSize] = "0"
}

if n.neoFS.IsHomomorphicHashingEnabled() {
Expand All @@ -446,9 +444,9 @@ func (n *layer) uploadZeroPart(ctx context.Context, multipartInfo *data.Multipar
}

if encInfo.Enabled {
attrs = append(attrs, *object.NewAttribute(AttributeEncryptionAlgorithm, encInfo.Algorithm))
attrs = append(attrs, *object.NewAttribute(AttributeHMACKey, encInfo.HMACKey))
attrs = append(attrs, *object.NewAttribute(AttributeHMACSalt, encInfo.HMACSalt))
attrs = append(attrs, *object.NewAttribute(s3headers.AttributeEncryptionAlgorithm, encInfo.Algorithm))
attrs = append(attrs, *object.NewAttribute(s3headers.AttributeHMACKey, encInfo.HMACKey))
attrs = append(attrs, *object.NewAttribute(s3headers.AttributeHMACSalt, encInfo.HMACSalt))
}

var hashlessHeaderObject object.Object
Expand Down Expand Up @@ -948,10 +946,10 @@ func (n *layer) multipartMetaGetParts(ctx context.Context, bktInfo *data.BucketI
return nil, fmt.Errorf("convert multipart %s MultipartTotalSize %s: %w", uploadID, element.Attributes[s3headers.MultipartTotalSize], err)
}

if decSize, ok := element.Attributes[AttributeDecryptedSize]; ok && decSize != "" {
if decSize, ok := element.Attributes[s3headers.AttributeDecryptedSize]; ok && decSize != "" {
element.TotalSize, err = strconv.ParseInt(decSize, 10, 64)
if err != nil {
return nil, fmt.Errorf("convert multipart %s AttributeDecryptedSize %s: %w", uploadID, element.Attributes[AttributeDecryptedSize], err)
return nil, fmt.Errorf("convert multipart %s s3headers.AttributeDecryptedSize %s: %w", uploadID, element.Attributes[s3headers.AttributeDecryptedSize], err)
}
}

Expand Down Expand Up @@ -1029,7 +1027,7 @@ func (n *layer) multipartGetPartsList(ctx context.Context, bktInfo *data.BucketI
s3headers.MultipartPartHash,
s3headers.MultipartElementID,
s3headers.MultipartTotalSize,
AttributeDecryptedSize,
s3headers.AttributeDecryptedSize,
}
)

Expand Down Expand Up @@ -1517,7 +1515,7 @@ func (n *layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipar
}

initMetadata := make(map[string]string, len(multipartInfo.Meta)+1)
initMetadata[UploadCompletedParts] = completedPartsHeader.String()
initMetadata[s3headers.UploadCompletedParts] = completedPartsHeader.String()

uploadData := &UploadData{
TagSet: make(map[string]string),
Expand All @@ -1534,10 +1532,10 @@ func (n *layer) CompleteMultipartUpload(ctx context.Context, p *CompleteMultipar
}

if encInfo.Enabled {
initMetadata[AttributeEncryptionAlgorithm] = encInfo.Algorithm
initMetadata[AttributeHMACKey] = encInfo.HMACKey
initMetadata[AttributeHMACSalt] = encInfo.HMACSalt
initMetadata[AttributeDecryptedSize] = strconv.FormatInt(multipartObjetSize, 10)
initMetadata[s3headers.AttributeEncryptionAlgorithm] = encInfo.Algorithm
initMetadata[s3headers.AttributeHMACKey] = encInfo.HMACKey
initMetadata[s3headers.AttributeHMACSalt] = encInfo.HMACSalt
initMetadata[s3headers.AttributeDecryptedSize] = strconv.FormatInt(multipartObjetSize, 10)
n.log.Debug("big object", zap.Int64("size", multipartObjetSize), zap.Uint64("enc_size", encMultipartObjectSize))

multipartObjetSize = int64(encMultipartObjectSize)
Expand Down
6 changes: 3 additions & 3 deletions api/layer/neofs_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"time"

"github.com/nspcc-dev/neofs-s3-gw/api"
"github.com/nspcc-dev/neofs-s3-gw/api/s3headers"
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
"github.com/nspcc-dev/neofs-sdk-go/checksum"
"github.com/nspcc-dev/neofs-sdk-go/client"
Expand Down Expand Up @@ -48,8 +49,7 @@ type searchedItem struct {
}

const (
objectNonceSize = 8
objectNonceAttribute = "__NEOFS__NONCE"
objectNonceSize = 8
)

func NewTestNeoFS(signer neofscrypto.Signer) *TestNeoFS {
Expand Down Expand Up @@ -296,7 +296,7 @@ func (t *TestNeoFS) CreateObject(_ context.Context, prm PrmObjectCreate) (oid.ID
if _, err := rand.Read(nonce); err != nil {
return oid.ID{}, fmt.Errorf("object nonce: %w", err)
}
objectNonceAttr := object.NewAttribute(objectNonceAttribute, base64.StdEncoding.EncodeToString(nonce))
objectNonceAttr := object.NewAttribute(s3headers.AttributeObjectNonce, base64.StdEncoding.EncodeToString(nonce))
attrs = append(attrs, *objectNonceAttr)

obj := object.New()
Expand Down
37 changes: 16 additions & 21 deletions api/layer/object.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,6 @@ type (
}
)

const (
attrS3VersioningState = "S3-versioning-state"
attrS3DeleteMarker = "S3-delete-marker"
)

// objectHead returns all object's headers.
func (n *layer) objectHead(ctx context.Context, bktInfo *data.BucketInfo, idObj oid.ID) (*object.Object, error) {
prm := PrmObjectRead{
Expand Down Expand Up @@ -237,7 +232,7 @@ func (n *layer) PutObject(ctx context.Context, p *PutObjectParams) (*data.Extend

r := p.Reader
if p.Encryption.Enabled() {
p.Header[AttributeDecryptedSize] = strconv.FormatInt(p.Size, 10)
p.Header[s3headers.AttributeDecryptedSize] = strconv.FormatInt(p.Size, 10)
if err = addEncryptionHeaders(p.Header, p.Encryption); err != nil {
return nil, fmt.Errorf("add encryption header: %w", err)
}
Expand Down Expand Up @@ -281,7 +276,7 @@ func (n *layer) PutObject(ctx context.Context, p *PutObjectParams) (*data.Extend
}

if bktSettings.VersioningEnabled() {
prm.Attributes[attrS3VersioningState] = data.VersioningEnabled
prm.Attributes[s3headers.AttributeVersioningState] = data.VersioningEnabled
}

id, hash, err := n.objectPutAndHash(ctx, prm, p.BktInfo)
Expand Down Expand Up @@ -351,7 +346,7 @@ func (n *layer) prepareMultipartHeadObject(ctx context.Context, p *PutObjectPara
)

if p.Encryption.Enabled() {
p.Header[AttributeDecryptedSize] = strconv.FormatInt(p.Size, 10)
p.Header[s3headers.AttributeDecryptedSize] = strconv.FormatInt(p.Size, 10)
if err = addEncryptionHeaders(p.Header, p.Encryption); err != nil {
return nil, fmt.Errorf("add encryption header: %w", err)
}
Expand Down Expand Up @@ -391,7 +386,7 @@ func (n *layer) prepareMultipartHeadObject(ctx context.Context, p *PutObjectPara
}

if versioningEnabled {
attributes = append(attributes, *object.NewAttribute(attrS3VersioningState, data.VersioningEnabled))
attributes = append(attributes, *object.NewAttribute(s3headers.AttributeVersioningState, data.VersioningEnabled))
}

headerObject.SetAttributes(attributes...)
Expand All @@ -414,9 +409,9 @@ func (n *layer) searchAllVersionsInNeoFS(ctx context.Context, bkt *data.BucketIn
object.AttributeFilePath,
object.FilterCreationEpoch,
object.AttributeTimestamp,
attrS3VersioningState,
s3headers.AttributeVersioningState,
object.FilterPayloadSize,
attrS3DeleteMarker,
s3headers.AttributeDeleteMarker,
}

opts client.SearchObjectsOptions
Expand All @@ -436,7 +431,7 @@ func (n *layer) searchAllVersionsInNeoFS(ctx context.Context, bkt *data.BucketIn
filters.AddFilter(s3headers.MetaType, "", object.MatchNotPresent)

if onlyUnversioned {
filters.AddFilter(attrS3VersioningState, data.VersioningUnversioned, object.MatchNotPresent)
filters.AddFilter(s3headers.AttributeVersioningState, data.VersioningUnversioned, object.MatchNotPresent)
}

searchResultItems, err := n.neoFS.SearchObjectsV2(ctx, bkt.CID, filters, returningAttributes, opts)
Expand Down Expand Up @@ -517,9 +512,9 @@ func (n *layer) comprehensiveSearchAllVersionsInNeoFS(ctx context.Context, bkt *
object.AttributeFilePath,
object.FilterCreationEpoch,
object.AttributeTimestamp,
attrS3VersioningState,
s3headers.AttributeVersioningState,
object.FilterPayloadSize,
attrS3DeleteMarker,
s3headers.AttributeDeleteMarker,
s3headers.MetaType,
}

Expand All @@ -537,7 +532,7 @@ func (n *layer) comprehensiveSearchAllVersionsInNeoFS(ctx context.Context, bkt *
}

if onlyUnversioned {
filters.AddFilter(attrS3VersioningState, data.VersioningUnversioned, object.MatchNotPresent)
filters.AddFilter(s3headers.AttributeVersioningState, data.VersioningUnversioned, object.MatchNotPresent)
}

searchResultItems, err := n.neoFS.SearchObjectsV2(ctx, bkt.CID, filters, returningAttributes, opts)
Expand Down Expand Up @@ -646,8 +641,8 @@ func (n *layer) searchTagsAndLocksInNeoFS(ctx context.Context, bkt *data.BucketI
filters.AddFilter(object.AttributeFilePath, "", object.MatchCommonPrefix)
}

filters.AddFilter(attrS3VersioningState, data.VersioningEnabled, object.MatchStringEqual)
filters.AddFilter(AttributeObjectVersion, objectVersion, object.MatchStringEqual)
filters.AddFilter(s3headers.AttributeVersioningState, data.VersioningEnabled, object.MatchStringEqual)
filters.AddFilter(s3headers.AttributeObjectVersion, objectVersion, object.MatchStringEqual)

filters.AddFilter(s3headers.MetaType, "", object.MatchStringNotEqual)

Expand Down Expand Up @@ -701,8 +696,8 @@ func (n *layer) searchAllVersionsInNeoFSByPrefix(ctx context.Context, bkt *data.
object.AttributeFilePath,
object.FilterCreationEpoch,
object.AttributeTimestamp,
attrS3DeleteMarker,
AttributeDecryptedSize,
s3headers.AttributeDeleteMarker,
s3headers.AttributeDecryptedSize,
object.FilterPayloadSize,
object.FilterPayloadChecksum,
}
Expand All @@ -725,7 +720,7 @@ func (n *layer) searchAllVersionsInNeoFSByPrefix(ctx context.Context, bkt *data.
filters.AddFilter(s3headers.MetaType, "", object.MatchNotPresent)

if onlyUnversioned {
filters.AddFilter(attrS3VersioningState, "", object.MatchNotPresent)
filters.AddFilter(s3headers.AttributeVersioningState, "", object.MatchNotPresent)
}

searchResultItems, nextCursor, err := n.neoFS.SearchObjectsV2WithCursor(ctx, bkt.CID, filters, returningAttributes, cursor, opts)
Expand Down Expand Up @@ -1108,7 +1103,7 @@ func (n *layer) getAllObjectsVersions(ctx context.Context, bkt *data.BucketInfo,

func IsSystemHeader(key string) bool {
_, ok := api.SystemMetadata[key]
return ok || strings.HasPrefix(key, api.NeoFSSystemMetadataPrefix)
return ok || strings.HasPrefix(key, s3headers.NeoFSSystemMetadataPrefix)
}

func shouldSkip(result prefixSearchResult, p allObjectParams, existed map[string]struct{}) bool {
Expand Down
Loading
Loading