All checks were successful
/ DCO (pull_request) Successful in 27s
/ Vulncheck (pull_request) Successful in 50s
/ Builds (pull_request) Successful in 1m19s
/ OCI image (pull_request) Successful in 1m26s
/ Lint (pull_request) Successful in 2m40s
/ Tests (pull_request) Successful in 56s
/ Integration tests (pull_request) Successful in 6m1s
Simplify tree listing (we need only nodes in exactly the same parent level) Signed-off-by: Denis Kirillov <d.kirillov@yadro.com>
695 lines
22 KiB
Go
695 lines
22 KiB
Go
package handler
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/cache"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/data"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/handler/middleware"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/internal/templates"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/resolver"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tokens"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/tree"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-http-gw/utils"
|
|
v2container "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/container"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/acl"
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/panjf2000/ants/v2"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/valyala/fasthttp"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zaptest"
|
|
)
|
|
|
|
type configMock struct {
|
|
additionalFilenameSearch bool
|
|
additionalSlashSearch bool
|
|
indexEnabled bool
|
|
cors *data.CORSRule
|
|
}
|
|
|
|
func (c *configMock) DefaultTimestamp() bool {
|
|
return false
|
|
}
|
|
|
|
func (c *configMock) ArchiveCompression() bool {
|
|
return false
|
|
}
|
|
|
|
func (c *configMock) IndexPageEnabled() bool {
|
|
return c.indexEnabled
|
|
}
|
|
|
|
func (c *configMock) IndexPageTemplate() string {
|
|
return templates.DefaultIndexTemplate
|
|
}
|
|
|
|
func (c *configMock) IndexPageNativeTemplate() string {
|
|
return ""
|
|
}
|
|
|
|
func (c *configMock) ClientCut() bool {
|
|
return false
|
|
}
|
|
|
|
func (c *configMock) BufferMaxSizeForPut() uint64 {
|
|
return 0
|
|
}
|
|
|
|
func (c *configMock) NamespaceHeader() string {
|
|
return ""
|
|
}
|
|
|
|
func (c *configMock) EnableFilepathFallback() bool {
|
|
return c.additionalFilenameSearch
|
|
}
|
|
|
|
func (c *configMock) EnableFilepathSlashFallback() bool {
|
|
return c.additionalSlashSearch
|
|
}
|
|
|
|
func (c *configMock) FormContainerZone(string) string {
|
|
return v2container.SysAttributeZoneDefault
|
|
}
|
|
|
|
func (c *configMock) CORS() *data.CORSRule {
|
|
return c.cors
|
|
}
|
|
|
|
type handlerContext struct {
|
|
key *keys.PrivateKey
|
|
owner user.ID
|
|
corsCnr cid.ID
|
|
|
|
h *Handler
|
|
frostfs *TestFrostFS
|
|
tree *treeServiceClientMock
|
|
cfg *configMock
|
|
}
|
|
|
|
func (hc *handlerContext) Handler() *Handler {
|
|
return hc.h
|
|
}
|
|
|
|
func prepareHandlerContext(t *testing.T) *handlerContext {
|
|
hc, err := prepareHandlerContextBase(zaptest.NewLogger(t))
|
|
require.NoError(t, err)
|
|
return hc
|
|
}
|
|
|
|
func prepareHandlerContextBase(logger *zap.Logger) (*handlerContext, error) {
|
|
key, err := keys.NewPrivateKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var owner user.ID
|
|
user.IDFromKey(&owner, key.PrivateKey.PublicKey)
|
|
|
|
testFrostFS := NewTestFrostFS(key)
|
|
|
|
testResolver := &resolver.Resolver{Name: "test_resolver"}
|
|
testResolver.SetResolveFunc(func(_ context.Context, _, name string) (*cid.ID, error) {
|
|
return testFrostFS.ContainerID(name)
|
|
})
|
|
|
|
cnrID := createCORSContainer(owner, testFrostFS)
|
|
|
|
params := &AppParams{
|
|
Logger: logger,
|
|
FrostFS: testFrostFS,
|
|
Owner: &owner,
|
|
Resolver: testResolver,
|
|
Cache: cache.NewBucketCache(&cache.Config{
|
|
Size: 1,
|
|
Lifetime: 1,
|
|
Logger: logger,
|
|
}, false),
|
|
CORSCnrID: cnrID,
|
|
CORSCache: cache.NewCORSCache(&cache.Config{
|
|
Size: 1,
|
|
Lifetime: 1,
|
|
Logger: logger,
|
|
}),
|
|
}
|
|
|
|
treeMock := newTreeServiceClientMock()
|
|
cfgMock := &configMock{}
|
|
|
|
workerPool, err := ants.NewPool(1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
handler := New(params, cfgMock, tree.NewTree(treeMock, logger), workerPool)
|
|
|
|
return &handlerContext{
|
|
key: key,
|
|
owner: owner,
|
|
corsCnr: cnrID,
|
|
h: handler,
|
|
frostfs: testFrostFS,
|
|
tree: treeMock,
|
|
cfg: cfgMock,
|
|
}, nil
|
|
}
|
|
|
|
func createCORSContainer(owner user.ID, frostfs *TestFrostFS) cid.ID {
|
|
var cnr container.Container
|
|
cnr.Init()
|
|
cnr.SetOwner(owner)
|
|
|
|
cnrID := cidtest.ID()
|
|
frostfs.SetContainer(cnrID, &cnr)
|
|
frostfs.AllowUserOperation(cnrID, owner, acl.OpObjectSearch, oid.ID{})
|
|
frostfs.AllowUserOperation(cnrID, owner, acl.OpObjectHead, oid.ID{})
|
|
frostfs.AllowUserOperation(cnrID, owner, acl.OpObjectGet, oid.ID{})
|
|
|
|
return cnrID
|
|
}
|
|
|
|
func (hc *handlerContext) prepareContainer(name string, basicACL acl.Basic) (cid.ID, *container.Container, error) {
|
|
var pp netmap.PlacementPolicy
|
|
err := pp.DecodeString("REP 1")
|
|
if err != nil {
|
|
return cid.ID{}, nil, err
|
|
}
|
|
|
|
var cnr container.Container
|
|
cnr.Init()
|
|
cnr.SetOwner(hc.owner)
|
|
cnr.SetPlacementPolicy(pp)
|
|
cnr.SetBasicACL(basicACL)
|
|
|
|
var domain container.Domain
|
|
domain.SetName(name)
|
|
container.WriteDomain(&cnr, domain)
|
|
container.SetName(&cnr, name)
|
|
container.SetCreationTime(&cnr, time.Now())
|
|
|
|
cnrID := cidtest.ID()
|
|
|
|
for op := acl.OpObjectGet; op < acl.OpObjectHash; op++ {
|
|
hc.frostfs.AllowUserOperation(cnrID, hc.owner, op, oid.ID{})
|
|
if basicACL.IsOpAllowed(op, acl.RoleOthers) {
|
|
hc.frostfs.AllowUserOperation(cnrID, user.ID{}, op, oid.ID{})
|
|
}
|
|
}
|
|
|
|
return cnrID, &cnr, nil
|
|
}
|
|
|
|
func TestBasic(t *testing.T) {
|
|
hc := prepareHandlerContext(t)
|
|
|
|
bktName := "bucket"
|
|
cnrID, cnr, err := hc.prepareContainer(bktName, acl.PublicRWExtended)
|
|
require.NoError(t, err)
|
|
hc.frostfs.SetContainer(cnrID, cnr)
|
|
|
|
ctx := context.Background()
|
|
ctx = middleware.SetNamespace(ctx, "")
|
|
|
|
content := "hello"
|
|
r, err := prepareUploadRequest(ctx, cnrID.EncodeToString(), content)
|
|
require.NoError(t, err)
|
|
|
|
hc.Handler().Upload(r)
|
|
require.Equal(t, r.Response.StatusCode(), http.StatusOK)
|
|
|
|
var putRes putResponse
|
|
err = json.Unmarshal(r.Response.Body(), &putRes)
|
|
require.NoError(t, err)
|
|
|
|
hc.cfg.additionalFilenameSearch = true
|
|
obj := hc.frostfs.objects[putRes.ContainerID+"/"+putRes.ObjectID]
|
|
fileName := prepareObjectAttributes(object.AttributeFileName, objFileName)
|
|
filePath := prepareObjectAttributes(object.AttributeFilePath, objFilePath)
|
|
obj.SetAttributes(append(obj.Attributes(), fileName)...)
|
|
obj.SetAttributes(append(obj.Attributes(), filePath)...)
|
|
|
|
t.Run("get", func(t *testing.T) {
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), putRes.ObjectID)
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, content, string(r.Response.Body()))
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), objFilePath)
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, content, string(r.Response.Body()))
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), objFileName)
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, content, string(r.Response.Body()))
|
|
})
|
|
|
|
t.Run("head", func(t *testing.T) {
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), putRes.ObjectID)
|
|
hc.Handler().HeadByAddressOrBucketName(r)
|
|
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
|
|
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), objFilePath)
|
|
hc.Handler().HeadByAddressOrBucketName(r)
|
|
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
|
|
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), objFileName)
|
|
hc.Handler().HeadByAddressOrBucketName(r)
|
|
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
|
|
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
|
|
})
|
|
|
|
t.Run("get by attribute", func(t *testing.T) {
|
|
r = prepareGetByAttributeRequest(ctx, bktName, keyAttr, valAttr)
|
|
hc.Handler().DownloadByAttribute(r)
|
|
require.Equal(t, content, string(r.Response.Body()))
|
|
|
|
r = prepareGetByAttributeRequest(ctx, bktName, attrFileName, objFilePath)
|
|
hc.Handler().DownloadByAttribute(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
r = prepareGetByAttributeRequest(ctx, bktName, attrFilePath, objFileName)
|
|
hc.Handler().DownloadByAttribute(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
})
|
|
|
|
t.Run("head by attribute", func(t *testing.T) {
|
|
r = prepareGetByAttributeRequest(ctx, bktName, keyAttr, valAttr)
|
|
hc.Handler().HeadByAttribute(r)
|
|
require.Equal(t, putRes.ObjectID, string(r.Response.Header.Peek(hdrObjectID)))
|
|
require.Equal(t, putRes.ContainerID, string(r.Response.Header.Peek(hdrContainerID)))
|
|
|
|
r = prepareGetByAttributeRequest(ctx, bktName, attrFileName, objFilePath)
|
|
hc.Handler().HeadByAttribute(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
r = prepareGetByAttributeRequest(ctx, bktName, attrFilePath, objFileName)
|
|
hc.Handler().HeadByAttribute(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
})
|
|
|
|
t.Run("zip", func(t *testing.T) {
|
|
r = prepareGetZipped(ctx, bktName, "")
|
|
hc.Handler().DownloadZip(r)
|
|
|
|
readerAt := bytes.NewReader(r.Response.Body())
|
|
zipReader, err := zip.NewReader(readerAt, int64(len(r.Response.Body())))
|
|
require.NoError(t, err)
|
|
require.Len(t, zipReader.File, 1)
|
|
require.Equal(t, objFilePath, zipReader.File[0].Name)
|
|
f, err := zipReader.File[0].Open()
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
inErr := f.Close()
|
|
require.NoError(t, inErr)
|
|
}()
|
|
data, err := io.ReadAll(f)
|
|
require.NoError(t, err)
|
|
require.Equal(t, content, string(data))
|
|
})
|
|
}
|
|
|
|
func prepareHandlerAndBucket(t *testing.T) (*handlerContext, cid.ID) {
|
|
hc := prepareHandlerContext(t)
|
|
|
|
bktName := "bucket"
|
|
cnrID, cnr, err := hc.prepareContainer(bktName, acl.PublicRWExtended)
|
|
require.NoError(t, err)
|
|
hc.frostfs.SetContainer(cnrID, cnr)
|
|
|
|
return hc, cnrID
|
|
}
|
|
|
|
func TestGetObjectWithFallback(t *testing.T) {
|
|
ctx := middleware.SetNamespace(context.Background(), "")
|
|
|
|
t.Run("by oid", func(t *testing.T) {
|
|
hc, cnrID := prepareHandlerAndBucket(t)
|
|
|
|
obj1ID := oidtest.ID()
|
|
obj1 := object.New()
|
|
obj1.SetID(obj1ID)
|
|
obj1.SetPayload([]byte("obj1"))
|
|
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
|
|
|
|
r := prepareGetRequest(ctx, cnrID.EncodeToString(), obj1ID.String())
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
|
|
})
|
|
|
|
t.Run("by filepath as it is", func(t *testing.T) {
|
|
hc, cnrID := prepareHandlerAndBucket(t)
|
|
|
|
obj1ID := oidtest.ID()
|
|
obj1 := object.New()
|
|
obj1.SetID(obj1ID)
|
|
obj1.SetPayload([]byte("obj1"))
|
|
obj1.SetAttributes(prepareObjectAttributes(object.AttributeFilePath, "filepath/obj1"))
|
|
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
|
|
|
|
obj2ID := oidtest.ID()
|
|
obj2 := object.New()
|
|
obj2.SetID(obj2ID)
|
|
obj2.SetPayload([]byte("obj2"))
|
|
obj2.SetAttributes(prepareObjectAttributes(object.AttributeFilePath, "/filepath/obj2"))
|
|
hc.frostfs.objects[cnrID.String()+"/"+obj2ID.String()] = obj2
|
|
|
|
r := prepareGetRequest(ctx, cnrID.EncodeToString(), "filepath/obj1")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "/filepath/obj2")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, string(obj2.Payload()), string(r.Response.Body()))
|
|
})
|
|
|
|
t.Run("by filepath slash fallback", func(t *testing.T) {
|
|
hc, cnrID := prepareHandlerAndBucket(t)
|
|
|
|
obj1ID := oidtest.ID()
|
|
obj1 := object.New()
|
|
obj1.SetID(obj1ID)
|
|
obj1.SetPayload([]byte("obj1"))
|
|
obj1.SetAttributes(prepareObjectAttributes(object.AttributeFilePath, "filepath/obj1"))
|
|
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
|
|
|
|
r := prepareGetRequest(ctx, cnrID.EncodeToString(), "/filepath/obj1")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
hc.cfg.additionalSlashSearch = true
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "/filepath/obj1")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
|
|
})
|
|
|
|
t.Run("by filename fallback", func(t *testing.T) {
|
|
hc, cnrID := prepareHandlerAndBucket(t)
|
|
|
|
obj1ID := oidtest.ID()
|
|
obj1 := object.New()
|
|
obj1.SetID(obj1ID)
|
|
obj1.SetPayload([]byte("obj1"))
|
|
obj1.SetAttributes(prepareObjectAttributes(object.AttributeFileName, "filename/obj1"))
|
|
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
|
|
|
|
r := prepareGetRequest(ctx, cnrID.EncodeToString(), "filename/obj1")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
hc.cfg.additionalFilenameSearch = true
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filename/obj1")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
|
|
})
|
|
|
|
t.Run("by filename and slash fallback", func(t *testing.T) {
|
|
hc, cnrID := prepareHandlerAndBucket(t)
|
|
|
|
obj1ID := oidtest.ID()
|
|
obj1 := object.New()
|
|
obj1.SetID(obj1ID)
|
|
obj1.SetPayload([]byte("obj1"))
|
|
obj1.SetAttributes(prepareObjectAttributes(object.AttributeFileName, "filename/obj1"))
|
|
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
|
|
|
|
r := prepareGetRequest(ctx, cnrID.EncodeToString(), "/filename/obj1")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
hc.cfg.additionalFilenameSearch = true
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "/filename/obj1")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
hc.cfg.additionalSlashSearch = true
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "/filename/obj1")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
|
|
})
|
|
|
|
t.Run("index fallback", func(t *testing.T) {
|
|
hc, cnrID := prepareHandlerAndBucket(t)
|
|
|
|
obj1ID := oidtest.ID()
|
|
obj1 := object.New()
|
|
obj1.SetID(obj1ID)
|
|
obj1.SetPayload([]byte("obj1"))
|
|
obj1.SetAttributes(prepareObjectAttributes(object.AttributeFilePath, "filepath/index.html"))
|
|
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
|
|
|
|
r := prepareGetRequest(ctx, cnrID.EncodeToString(), "filepath/")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filepath")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
hc.cfg.indexEnabled = true
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filepath")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filepath/")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
|
|
})
|
|
|
|
t.Run("index filename fallback", func(t *testing.T) {
|
|
hc, cnrID := prepareHandlerAndBucket(t)
|
|
|
|
obj1ID := oidtest.ID()
|
|
obj1 := object.New()
|
|
obj1.SetID(obj1ID)
|
|
obj1.SetPayload([]byte("obj1"))
|
|
obj1.SetAttributes(prepareObjectAttributes(object.AttributeFileName, "filename/index.html"))
|
|
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
|
|
|
|
r := prepareGetRequest(ctx, cnrID.EncodeToString(), "filename/")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filename")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
hc.cfg.indexEnabled = true
|
|
hc.cfg.additionalFilenameSearch = true
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filename")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "filename/")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, string(obj1.Payload()), string(r.Response.Body()))
|
|
})
|
|
}
|
|
|
|
func TestIndex(t *testing.T) {
|
|
ctx := middleware.SetNamespace(context.Background(), "")
|
|
|
|
t.Run("s3", func(t *testing.T) {
|
|
hc, cnrID := prepareHandlerAndBucket(t)
|
|
|
|
obj1ID := oidtest.ID()
|
|
obj1 := object.New()
|
|
obj1.SetID(obj1ID)
|
|
obj1.SetPayload([]byte("obj1"))
|
|
obj1.SetAttributes(prepareObjectAttributes(object.AttributeFilePath, "prefix/obj1"))
|
|
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
|
|
|
|
hc.tree.containers[cnrID.String()] = containerInfo{
|
|
trees: map[string]map[string]nodeResponse{
|
|
"system": {"bucket-settings": nodeResponse{nodeID: 1}},
|
|
"version": {
|
|
"": nodeResponse{}, //root
|
|
"prefix": nodeResponse{
|
|
nodeID: 1,
|
|
meta: []nodeMeta{{key: tree.FileNameKey, value: []byte("prefix")}}},
|
|
"obj1": nodeResponse{
|
|
parentID: 1,
|
|
nodeID: 2,
|
|
meta: []nodeMeta{
|
|
{key: tree.FileNameKey, value: []byte("obj1")},
|
|
{key: "OID", value: []byte(obj1ID.String())},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
r := prepareGetRequest(ctx, cnrID.EncodeToString(), "prefix/")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "prefix")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
hc.cfg.indexEnabled = true
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "prefix")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Contains(t, string(r.Response.Body()), "Index of s3://bucket/prefix")
|
|
require.Contains(t, string(r.Response.Body()), obj1ID.String())
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "prefix/")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Contains(t, string(r.Response.Body()), "Index of s3://bucket/prefix")
|
|
require.Contains(t, string(r.Response.Body()), obj1ID.String())
|
|
|
|
r = prepareGetRequest(ctx, "bucket", "dummy")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Contains(t, string(r.Response.Body()), "Index of s3://bucket/dummy")
|
|
})
|
|
|
|
t.Run("native", func(t *testing.T) {
|
|
hc, cnrID := prepareHandlerAndBucket(t)
|
|
|
|
obj1ID := oidtest.ID()
|
|
obj1 := object.New()
|
|
obj1.SetID(obj1ID)
|
|
obj1.SetPayload([]byte("obj1"))
|
|
obj1.SetAttributes(prepareObjectAttributes(object.AttributeFilePath, "prefix/obj1"))
|
|
hc.frostfs.objects[cnrID.String()+"/"+obj1ID.String()] = obj1
|
|
|
|
r := prepareGetRequest(ctx, cnrID.EncodeToString(), "prefix/")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "prefix")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Equal(t, fasthttp.StatusNotFound, r.Response.StatusCode())
|
|
|
|
hc.cfg.indexEnabled = true
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "prefix")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Contains(t, string(r.Response.Body()), "Index of frostfs://"+cnrID.String()+"/prefix")
|
|
require.Contains(t, string(r.Response.Body()), obj1ID.String())
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "prefix/")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Contains(t, string(r.Response.Body()), "Index of frostfs://"+cnrID.String()+"/prefix")
|
|
require.Contains(t, string(r.Response.Body()), obj1ID.String())
|
|
|
|
r = prepareGetRequest(ctx, cnrID.EncodeToString(), "dummy")
|
|
hc.Handler().DownloadByAddressOrBucketName(r)
|
|
require.Contains(t, string(r.Response.Body()), "Index of frostfs://"+cnrID.String()+"/dummy")
|
|
})
|
|
}
|
|
|
|
func prepareUploadRequest(ctx context.Context, bucket, content string) (*fasthttp.RequestCtx, error) {
|
|
r := new(fasthttp.RequestCtx)
|
|
utils.SetContextToRequest(ctx, r)
|
|
r.SetUserValue("cid", bucket)
|
|
return r, fillMultipartBody(r, content)
|
|
}
|
|
|
|
func prepareGetRequest(ctx context.Context, bucket, objID string) *fasthttp.RequestCtx {
|
|
r := new(fasthttp.RequestCtx)
|
|
utils.SetContextToRequest(ctx, r)
|
|
r.SetUserValue("cid", bucket)
|
|
r.SetUserValue("oid", objID)
|
|
return r
|
|
}
|
|
|
|
func prepareCORSRequest(t *testing.T, bucket string, headers map[string]string) *fasthttp.RequestCtx {
|
|
ctx := context.Background()
|
|
ctx = middleware.SetNamespace(ctx, "")
|
|
|
|
r := new(fasthttp.RequestCtx)
|
|
r.SetUserValue("cid", bucket)
|
|
|
|
for k, v := range headers {
|
|
r.Request.Header.Set(k, v)
|
|
}
|
|
|
|
ctx, err := tokens.StoreBearerTokenAppCtx(ctx, r)
|
|
require.NoError(t, err)
|
|
|
|
utils.SetContextToRequest(ctx, r)
|
|
|
|
return r
|
|
}
|
|
|
|
func prepareGetByAttributeRequest(ctx context.Context, bucket, attrKey, attrVal string) *fasthttp.RequestCtx {
|
|
r := new(fasthttp.RequestCtx)
|
|
utils.SetContextToRequest(ctx, r)
|
|
r.SetUserValue("cid", bucket)
|
|
r.SetUserValue("attr_key", attrKey)
|
|
r.SetUserValue("attr_val", attrVal)
|
|
return r
|
|
}
|
|
|
|
func prepareGetZipped(ctx context.Context, bucket, prefix string) *fasthttp.RequestCtx {
|
|
r := new(fasthttp.RequestCtx)
|
|
utils.SetContextToRequest(ctx, r)
|
|
r.SetUserValue("cid", bucket)
|
|
r.SetUserValue("prefix", prefix)
|
|
return r
|
|
}
|
|
|
|
func prepareObjectAttributes(attrKey, attrValue string) object.Attribute {
|
|
attr := object.NewAttribute()
|
|
attr.SetKey(attrKey)
|
|
attr.SetValue(attrValue)
|
|
return *attr
|
|
}
|
|
|
|
const (
|
|
keyAttr = "User-Attribute"
|
|
valAttr = "user value"
|
|
objFileName = "newFile.txt"
|
|
objFilePath = "/newFile.txt"
|
|
)
|
|
|
|
func fillMultipartBody(r *fasthttp.RequestCtx, content string) error {
|
|
attributes := map[string]string{
|
|
object.AttributeFileName: objFileName,
|
|
keyAttr: valAttr,
|
|
}
|
|
|
|
var buff bytes.Buffer
|
|
w := multipart.NewWriter(&buff)
|
|
fw, err := w.CreateFormFile("file", attributes[object.AttributeFileName])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err = io.Copy(fw, bytes.NewBufferString(content)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = w.Close(); err != nil {
|
|
return err
|
|
}
|
|
|
|
r.Request.SetBodyStream(&buff, buff.Len())
|
|
r.Request.Header.Set("Content-Type", w.FormDataContentType())
|
|
r.Request.Header.Set("X-Attribute-"+keyAttr, valAttr)
|
|
|
|
return nil
|
|
}
|