diff --git a/CHANGELOG.md b/CHANGELOG.md index 30fcf7a..51080bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ This document outlines major changes between releases. ### Added - Add handling quota limit reached error (#187) +- Add slash clipping for FileName attribute (#174) ## [0.32.2] - 2025-02-03 diff --git a/internal/handler/handler.go b/internal/handler/handler.go index 69aecbf..532cdc4 100644 --- a/internal/handler/handler.go +++ b/internal/handler/handler.go @@ -253,6 +253,10 @@ func (h *Handler) byAttribute(c *fasthttp.RequestCtx, handler func(context.Conte return } + if key == attrFileName { + val = prepareFileName(val) + } + log = log.With(zap.String("cid", cidParam), zap.String("attr_key", key), zap.String("attr_val", val)) bktInfo, err := h.getBucketInfo(ctx, cidParam, log) @@ -294,7 +298,7 @@ func (h *Handler) findObjectByAttribute(ctx context.Context, log *zap.Logger, cn switch { case errors.Is(err, io.EOF) && h.needSearchByFileName(attrKey, attrVal): log.Debug(logs.ObjectNotFoundByFilePathTrySearchByFileName, logs.TagField(logs.TagExternalStorage)) - return h.findObjectByAttribute(ctx, log, cnrID, attrFileName, attrVal) + return h.findObjectByAttribute(ctx, log, cnrID, attrFileName, prepareFileName(attrVal)) case errors.Is(err, io.EOF): log.Error(logs.ObjectNotFound, zap.Error(err), logs.TagField(logs.TagExternalStorage)) return oid.ID{}, fmt.Errorf("object not found: %w", err) @@ -315,6 +319,14 @@ func (h *Handler) needSearchByFileName(key, val string) bool { return strings.HasPrefix(val, "/") && strings.Count(val, "/") == 1 || !strings.Contains(val, "/") } +func prepareFileName(fileName string) string { + if strings.HasPrefix(fileName, "/") { + return fileName[1:] + } + + return fileName +} + // resolveContainer decode container id, if it's not a valid container id // then trey to resolve name using provided resolver. func (h *Handler) resolveContainer(ctx context.Context, containerID string) (*cid.ID, error) { diff --git a/internal/handler/handler_test.go b/internal/handler/handler_test.go index 53c9739..1638f9f 100644 --- a/internal/handler/handler_test.go +++ b/internal/handler/handler_test.go @@ -239,6 +239,10 @@ func TestBasic(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, "/"+objFileName) + hc.Handler().DownloadByAttribute(r) + require.Equal(t, content, string(r.Response.Body())) }) t.Run("head by attribute", func(t *testing.T) { @@ -246,6 +250,11 @@ func TestBasic(t *testing.T) { 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, "/"+objFileName) + 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))) }) t.Run("zip", func(t *testing.T) { @@ -293,8 +302,8 @@ func TestFindObjectByAttribute(t *testing.T) { err = json.Unmarshal(r.Response.Body(), &putRes) require.NoError(t, err) - testAttrVal1 := "test-attr-val1" - testAttrVal2 := "test-attr-val2" + testAttrVal1 := "/folder/cat.jpg" + testAttrVal2 := "cat.jpg" testAttrVal3 := "test-attr-val3" for _, tc := range []struct { @@ -340,6 +349,14 @@ func TestFindObjectByAttribute(t *testing.T) { err: "not found", additionalSearch: true, }, + { + name: "success search by FilePath with leading slash (with additional search)", + firstAttr: prepareObjectAttributes(attrFilePath, testAttrVal1), + secondAttr: prepareObjectAttributes(attrFileName, testAttrVal2), + reqAttrKey: attrFilePath, + reqAttrValue: "/cat.jpg", + additionalSearch: true, + }, } { t.Run(tc.name, func(t *testing.T) { obj := hc.frostfs.objects[putRes.ContainerID+"/"+putRes.ObjectID] @@ -422,6 +439,17 @@ func TestNeedSearchByFileName(t *testing.T) { } } +func TestPrepareFileName(t *testing.T) { + fileName := "/cat.jpg" + expected := "cat.jpg" + actual := prepareFileName(fileName) + require.Equal(t, expected, actual) + + fileName = "cat.jpg" + actual = prepareFileName(fileName) + require.Equal(t, expected, actual) +} + func prepareUploadRequest(ctx context.Context, bucket, content string) (*fasthttp.RequestCtx, error) { r := new(fasthttp.RequestCtx) utils.SetContextToRequest(ctx, r)