forked from TrueCloudLab/frostfs-rest-gw
[#79] Fix panic on get empty object
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
e79189045e
commit
8d83320120
3 changed files with 136 additions and 17 deletions
|
@ -83,7 +83,9 @@ func runTestInContainer(rootCtx context.Context, t *testing.T, key *keys.Private
|
||||||
aioImage := "nspccdev/neofs-aio-testcontainer:"
|
aioImage := "nspccdev/neofs-aio-testcontainer:"
|
||||||
versions := []string{
|
versions := []string{
|
||||||
"0.29.0",
|
"0.29.0",
|
||||||
//"latest",
|
"0.30.0",
|
||||||
|
"0.32.0",
|
||||||
|
"latest",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, version := range versions {
|
for _, version := range versions {
|
||||||
|
@ -607,6 +609,25 @@ func restObjectGet(ctx context.Context, t *testing.T, p *pool.Pool, ownerID *use
|
||||||
contentData, err = base64.StdEncoding.DecodeString(objInfo.Payload)
|
contentData, err = base64.StdEncoding.DecodeString(objInfo.Payload)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, content[:rangeLength], contentData)
|
require.Equal(t, content[:rangeLength], contentData)
|
||||||
|
|
||||||
|
// check empty object
|
||||||
|
objID2 := createObject(ctx, t, p, ownerID, cnrID, map[string]string{}, []byte{})
|
||||||
|
|
||||||
|
query2 := make(url.Values)
|
||||||
|
query2.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
|
|
||||||
|
request2, err := http.NewRequest(http.MethodGet, testHost+"/v1/objects/"+cnrID.EncodeToString()+"/"+objID2.EncodeToString()+"?"+query2.Encode(), nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
prepareCommonHeaders(request2.Header, bearerToken)
|
||||||
|
|
||||||
|
objInfo2 := &models.ObjectInfo{}
|
||||||
|
doRequest(t, httpClient, request2, http.StatusOK, objInfo2)
|
||||||
|
|
||||||
|
require.Equal(t, cnrID.EncodeToString(), *objInfo2.ContainerID)
|
||||||
|
require.Equal(t, objID2.EncodeToString(), *objInfo2.ObjectID)
|
||||||
|
require.Equal(t, ownerID.EncodeToString(), *objInfo2.OwnerID)
|
||||||
|
require.Equal(t, 0, len(objInfo2.Attributes))
|
||||||
|
require.Equal(t, int64(0), *objInfo2.ObjectSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func restObjectGetFullBearer(ctx context.Context, t *testing.T, p *pool.Pool, ownerID *user.ID, cnrID cid.ID) {
|
func restObjectGetFullBearer(ctx context.Context, t *testing.T, p *pool.Pool, ownerID *user.ID, cnrID cid.ID) {
|
||||||
|
|
|
@ -142,28 +142,26 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var prmRange pool.PrmObjectRange
|
if objInfo.PayloadSize() == 0 {
|
||||||
prmRange.SetAddress(addr)
|
return operations.NewGetObjectInfoOK().WithPayload(&resp)
|
||||||
prmRange.UseBearer(btoken)
|
}
|
||||||
|
|
||||||
var offset, length uint64
|
offset, length, err := prepareOffsetLength(params, objInfo.PayloadSize())
|
||||||
if params.RangeOffset != nil || params.RangeLength != nil {
|
if err != nil {
|
||||||
if params.RangeOffset == nil || params.RangeLength == nil {
|
errResp := a.logAndGetErrorResponse("invalid range param", err)
|
||||||
errResp := a.logAndGetErrorResponse("invalid range param", errors.New("both offset and length musded"))
|
|
||||||
return errorResponse.WithPayload(errResp)
|
return errorResponse.WithPayload(errResp)
|
||||||
}
|
}
|
||||||
offset = uint64(*params.RangeOffset)
|
|
||||||
length = uint64(*params.RangeLength)
|
|
||||||
} else {
|
|
||||||
length = objInfo.PayloadSize()
|
|
||||||
}
|
|
||||||
prmRange.SetOffset(offset)
|
|
||||||
prmRange.SetLength(length)
|
|
||||||
|
|
||||||
if uint64(*params.MaxPayloadSize) < length {
|
if uint64(*params.MaxPayloadSize) < length {
|
||||||
return operations.NewGetObjectInfoOK().WithPayload(&resp)
|
return operations.NewGetObjectInfoOK().WithPayload(&resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var prmRange pool.PrmObjectRange
|
||||||
|
prmRange.SetAddress(addr)
|
||||||
|
prmRange.UseBearer(btoken)
|
||||||
|
prmRange.SetOffset(offset)
|
||||||
|
prmRange.SetLength(length)
|
||||||
|
|
||||||
rangeRes, err := a.pool.ObjectRange(ctx, prmRange)
|
rangeRes, err := a.pool.ObjectRange(ctx, prmRange)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errResp := a.logAndGetErrorResponse("range object", err)
|
errResp := a.logAndGetErrorResponse("range object", err)
|
||||||
|
@ -436,3 +434,26 @@ func prepareBearerToken(bt *BearerToken, isWalletConnect, isFullToken bool) (bea
|
||||||
|
|
||||||
return btoken, nil
|
return btoken, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prepareOffsetLength(params operations.GetObjectInfoParams, objSize uint64) (uint64, uint64, error) {
|
||||||
|
var offset, length uint64
|
||||||
|
if params.RangeOffset != nil || params.RangeLength != nil {
|
||||||
|
if params.RangeOffset == nil || params.RangeLength == nil {
|
||||||
|
return 0, 0, errors.New("both offset and length must be provided")
|
||||||
|
}
|
||||||
|
offset = uint64(*params.RangeOffset)
|
||||||
|
length = uint64(*params.RangeLength)
|
||||||
|
} else {
|
||||||
|
length = objSize
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset >= objSize {
|
||||||
|
return 0, 0, fmt.Errorf("offset '%d' must be less than object size '%d'", offset, objSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
if offset+length > objSize {
|
||||||
|
return 0, 0, fmt.Errorf("end of range '%d' must be less or equal object size '%d'", offset+length, objSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
return offset, length, nil
|
||||||
|
}
|
||||||
|
|
77
handlers/objects_test.go
Normal file
77
handlers/objects_test.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neofs-rest-gw/gen/restapi/operations"
|
||||||
|
"github.com/nspcc-dev/neofs-rest-gw/internal/util"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrepareOffset(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
err bool
|
||||||
|
expectedOffset uint64
|
||||||
|
expectedLength uint64
|
||||||
|
params operations.GetObjectInfoParams
|
||||||
|
objSize uint64
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
params: operations.GetObjectInfoParams{
|
||||||
|
RangeLength: util.NewInteger(1),
|
||||||
|
RangeOffset: util.NewInteger(0),
|
||||||
|
},
|
||||||
|
objSize: 1,
|
||||||
|
expectedOffset: 0,
|
||||||
|
expectedLength: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
params: operations.GetObjectInfoParams{
|
||||||
|
RangeLength: util.NewInteger(3),
|
||||||
|
RangeOffset: util.NewInteger(1),
|
||||||
|
},
|
||||||
|
objSize: 5,
|
||||||
|
expectedOffset: 1,
|
||||||
|
expectedLength: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
objSize: 1,
|
||||||
|
expectedOffset: 0,
|
||||||
|
expectedLength: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
err: true,
|
||||||
|
params: operations.GetObjectInfoParams{
|
||||||
|
RangeLength: util.NewInteger(1),
|
||||||
|
RangeOffset: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
err: true,
|
||||||
|
params: operations.GetObjectInfoParams{
|
||||||
|
RangeLength: nil,
|
||||||
|
RangeOffset: util.NewInteger(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
err: true,
|
||||||
|
params: operations.GetObjectInfoParams{
|
||||||
|
RangeLength: util.NewInteger(1),
|
||||||
|
RangeOffset: util.NewInteger(0),
|
||||||
|
},
|
||||||
|
objSize: 0,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run("", func(t *testing.T) {
|
||||||
|
offset, length, err := prepareOffsetLength(tc.params, tc.objSize)
|
||||||
|
if tc.err {
|
||||||
|
require.Error(t, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, tc.expectedOffset, offset)
|
||||||
|
require.Equal(t, tc.expectedLength, length)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue