From 8d833201201ccd8cdac3324bceab97202fee0298 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Tue, 18 Oct 2022 12:49:06 +0300 Subject: [PATCH] [#79] Fix panic on get empty object Signed-off-by: Denis Kirillov --- cmd/neofs-rest-gw/integration_test.go | 23 +++++++- handlers/objects.go | 53 ++++++++++++------ handlers/objects_test.go | 77 +++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 17 deletions(-) create mode 100644 handlers/objects_test.go diff --git a/cmd/neofs-rest-gw/integration_test.go b/cmd/neofs-rest-gw/integration_test.go index 79206b0..213fb13 100644 --- a/cmd/neofs-rest-gw/integration_test.go +++ b/cmd/neofs-rest-gw/integration_test.go @@ -83,7 +83,9 @@ func runTestInContainer(rootCtx context.Context, t *testing.T, key *keys.Private aioImage := "nspccdev/neofs-aio-testcontainer:" versions := []string{ "0.29.0", - //"latest", + "0.30.0", + "0.32.0", + "latest", } 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) require.NoError(t, err) 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) { diff --git a/handlers/objects.go b/handlers/objects.go index 2224064..69bac0a 100644 --- a/handlers/objects.go +++ b/handlers/objects.go @@ -142,28 +142,26 @@ func (a *API) GetObjectInfo(params operations.GetObjectInfoParams, principal *mo } } - var prmRange pool.PrmObjectRange - prmRange.SetAddress(addr) - prmRange.UseBearer(btoken) - - var offset, length uint64 - if params.RangeOffset != nil || params.RangeLength != nil { - if params.RangeOffset == nil || params.RangeLength == nil { - errResp := a.logAndGetErrorResponse("invalid range param", errors.New("both offset and length musded")) - return errorResponse.WithPayload(errResp) - } - offset = uint64(*params.RangeOffset) - length = uint64(*params.RangeLength) - } else { - length = objInfo.PayloadSize() + if objInfo.PayloadSize() == 0 { + return operations.NewGetObjectInfoOK().WithPayload(&resp) + } + + offset, length, err := prepareOffsetLength(params, objInfo.PayloadSize()) + if err != nil { + errResp := a.logAndGetErrorResponse("invalid range param", err) + return errorResponse.WithPayload(errResp) } - prmRange.SetOffset(offset) - prmRange.SetLength(length) if uint64(*params.MaxPayloadSize) < length { 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) if err != nil { errResp := a.logAndGetErrorResponse("range object", err) @@ -436,3 +434,26 @@ func prepareBearerToken(bt *BearerToken, isWalletConnect, isFullToken bool) (bea 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 +} diff --git a/handlers/objects_test.go b/handlers/objects_test.go new file mode 100644 index 0000000..36e7abd --- /dev/null +++ b/handlers/objects_test.go @@ -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) + }) + } +}