diff --git a/api/handler/object_list.go b/api/handler/object_list.go index 870b20e3..5119707c 100644 --- a/api/handler/object_list.go +++ b/api/handler/object_list.go @@ -232,7 +232,7 @@ func (h *handler) ListBucketObjectVersionsHandler(w http.ResponseWriter, r *http return } - response := encodeListObjectVersionsToResponse(info, p.BktInfo.Name, h.cfg.MD5Enabled()) + response := encodeListObjectVersionsToResponse(p, info, p.BktInfo.Name, h.cfg.MD5Enabled()) if err = middleware.EncodeToResponse(w, response); err != nil { h.logAndSendError(w, "something went wrong", reqInfo, err) } @@ -264,24 +264,28 @@ func parseListObjectVersionsRequest(reqInfo *middleware.ReqInfo) (*layer.ListObj return &res, nil } -func encodeListObjectVersionsToResponse(info *layer.ListObjectVersionsInfo, bucketName string, md5Enabled bool) *ListObjectsVersionsResponse { +func encodeListObjectVersionsToResponse(p *layer.ListObjectVersionsParams, info *layer.ListObjectVersionsInfo, bucketName string, md5Enabled bool) *ListObjectsVersionsResponse { res := ListObjectsVersionsResponse{ Name: bucketName, IsTruncated: info.IsTruncated, - KeyMarker: info.KeyMarker, - NextKeyMarker: info.NextKeyMarker, + KeyMarker: s3PathEncode(info.KeyMarker, p.Encode), + NextKeyMarker: s3PathEncode(info.NextKeyMarker, p.Encode), NextVersionIDMarker: info.NextVersionIDMarker, VersionIDMarker: info.VersionIDMarker, + Prefix: s3PathEncode(p.Prefix, p.Encode), + Delimiter: s3PathEncode(p.Delimiter, p.Encode), + EncodingType: p.Encode, + MaxKeys: p.MaxKeys, } for _, prefix := range info.CommonPrefixes { - res.CommonPrefixes = append(res.CommonPrefixes, CommonPrefix{Prefix: prefix}) + res.CommonPrefixes = append(res.CommonPrefixes, CommonPrefix{Prefix: s3PathEncode(prefix, p.Encode)}) } for _, ver := range info.Version { res.Version = append(res.Version, ObjectVersionResponse{ IsLatest: ver.IsLatest, - Key: ver.NodeVersion.FilePath, + Key: s3PathEncode(ver.NodeVersion.FilePath, p.Encode), LastModified: ver.NodeVersion.Created.UTC().Format(time.RFC3339), Owner: Owner{ ID: ver.NodeVersion.Owner.String(), @@ -297,7 +301,7 @@ func encodeListObjectVersionsToResponse(info *layer.ListObjectVersionsInfo, buck for _, del := range info.DeleteMarker { res.DeleteMarker = append(res.DeleteMarker, DeleteMarkerEntry{ IsLatest: del.IsLatest, - Key: del.NodeVersion.FilePath, + Key: s3PathEncode(del.NodeVersion.FilePath, p.Encode), LastModified: del.NodeVersion.Created.UTC().Format(time.RFC3339), Owner: Owner{ ID: del.NodeVersion.Owner.String(), diff --git a/api/handler/object_list_test.go b/api/handler/object_list_test.go index bcafd010..4065d3e0 100644 --- a/api/handler/object_list_test.go +++ b/api/handler/object_list_test.go @@ -675,6 +675,49 @@ func TestMintVersioningListObjectVersionsVersionIDContinuation(t *testing.T) { require.Equal(t, page1.NextVersionIDMarker, page2.VersionIDMarker) } +func TestListObjectVersionsEncoding(t *testing.T) { + hc := prepareHandlerContext(t) + + bktName := "bucket-for-listing-versions-encoding" + bktInfo := createTestBucket(hc, bktName) + putBucketVersioning(t, hc, bktName, true) + + objects := []string{"foo()/bar", "foo()/bar/xyzzy", "auux ab/thud", "asdf+b"} + for _, objName := range objects { + createTestObject(hc, bktInfo, objName, encryption.Params{}) + } + deleteObject(t, hc, bktName, "auux ab/thud", "") + + listResponse := listObjectsVersionsURL(hc, bktName, "foo(", ")", "", "", -1) + + require.Len(t, listResponse.CommonPrefixes, 1) + require.Equal(t, "foo%28%29", listResponse.CommonPrefixes[0].Prefix) + require.Len(t, listResponse.Version, 0) + require.Len(t, listResponse.DeleteMarker, 0) + require.Equal(t, "foo%28", listResponse.Prefix) + require.Equal(t, "%29", listResponse.Delimiter) + require.Equal(t, "url", listResponse.EncodingType) + require.Equal(t, maxObjectList, listResponse.MaxKeys) + + listResponse = listObjectsVersions(hc, bktName, "", "", "", "", 1) + require.Empty(t, listResponse.EncodingType) + + listResponse = listObjectsVersionsURL(hc, bktName, "", "", listResponse.NextKeyMarker, listResponse.NextVersionIDMarker, 3) + + require.Len(t, listResponse.CommonPrefixes, 0) + require.Len(t, listResponse.Version, 2) + require.Equal(t, "auux%20ab/thud", listResponse.Version[0].Key) + require.False(t, listResponse.Version[0].IsLatest) + require.Equal(t, "foo%28%29/bar", listResponse.Version[1].Key) + require.Len(t, listResponse.DeleteMarker, 1) + require.Equal(t, "auux%20ab/thud", listResponse.DeleteMarker[0].Key) + require.True(t, listResponse.DeleteMarker[0].IsLatest) + require.Equal(t, "asdf%2Bb", listResponse.KeyMarker) + require.Equal(t, "foo%28%29/bar", listResponse.NextKeyMarker) + require.Equal(t, "url", listResponse.EncodingType) + require.Equal(t, 3, listResponse.MaxKeys) +} + func checkVersionsNames(t *testing.T, versions *ListObjectsVersionsResponse, names []string) { for i, v := range versions.Version { require.Equal(t, names[i], v.Key) @@ -777,6 +820,14 @@ func listObjectsV1(hc *handlerContext, bktName, prefix, delimiter, marker string } func listObjectsVersions(hc *handlerContext, bktName, prefix, delimiter, keyMarker, versionIDMarker string, maxKeys int) *ListObjectsVersionsResponse { + return listObjectsVersionsBase(hc, bktName, prefix, delimiter, keyMarker, versionIDMarker, maxKeys, false) +} + +func listObjectsVersionsURL(hc *handlerContext, bktName, prefix, delimiter, keyMarker, versionIDMarker string, maxKeys int) *ListObjectsVersionsResponse { + return listObjectsVersionsBase(hc, bktName, prefix, delimiter, keyMarker, versionIDMarker, maxKeys, true) +} + +func listObjectsVersionsBase(hc *handlerContext, bktName, prefix, delimiter, keyMarker, versionIDMarker string, maxKeys int, encode bool) *ListObjectsVersionsResponse { query := prepareCommonListObjectsQuery(prefix, delimiter, maxKeys) if len(keyMarker) != 0 { query.Add("key-marker", keyMarker) @@ -784,6 +835,9 @@ func listObjectsVersions(hc *handlerContext, bktName, prefix, delimiter, keyMark if len(versionIDMarker) != 0 { query.Add("version-id-marker", versionIDMarker) } + if encode { + query.Add("encoding-type", "url") + } w, r := prepareTestFullRequest(hc, bktName, "", query, nil) hc.Handler().ListBucketObjectVersionsHandler(w, r) diff --git a/api/handler/response.go b/api/handler/response.go index 9ff24557..8fdb5abd 100644 --- a/api/handler/response.go +++ b/api/handler/response.go @@ -176,6 +176,9 @@ type ListObjectsVersionsResponse struct { DeleteMarker []DeleteMarkerEntry `xml:"DeleteMarker"` Version []ObjectVersionResponse `xml:"Version"` CommonPrefixes []CommonPrefix `xml:"CommonPrefixes"` + Prefix string `xml:"Prefix"` + Delimiter string `xml:"Delimiter,omitempty"` + MaxKeys int `xml:"MaxKeys"` } // VersioningConfiguration contains VersioningConfiguration XML representation.