mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
s3: Add support of encodingType parameter (#7265)
This commit honors encoding-type parameter in object listing, parts listing and multipart uploads listing.
This commit is contained in:
parent
dab314900d
commit
5efbe8a1b3
@ -73,6 +73,7 @@ const (
|
|||||||
ErrInvalidCopyPartRange
|
ErrInvalidCopyPartRange
|
||||||
ErrInvalidCopyPartRangeSource
|
ErrInvalidCopyPartRangeSource
|
||||||
ErrInvalidMaxKeys
|
ErrInvalidMaxKeys
|
||||||
|
ErrInvalidEncodingMethod
|
||||||
ErrInvalidMaxUploads
|
ErrInvalidMaxUploads
|
||||||
ErrInvalidMaxParts
|
ErrInvalidMaxParts
|
||||||
ErrInvalidPartNumberMarker
|
ErrInvalidPartNumberMarker
|
||||||
@ -357,6 +358,11 @@ var errorCodes = errorCodeMap{
|
|||||||
Description: "Argument maxKeys must be an integer between 0 and 2147483647",
|
Description: "Argument maxKeys must be an integer between 0 and 2147483647",
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
},
|
},
|
||||||
|
ErrInvalidEncodingMethod: {
|
||||||
|
Code: "InvalidArgument",
|
||||||
|
Description: "Invalid Encoding Method specified in Request",
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
},
|
||||||
ErrInvalidMaxParts: {
|
ErrInvalidMaxParts: {
|
||||||
Code: "InvalidArgument",
|
Code: "InvalidArgument",
|
||||||
Description: "Argument max-parts must be an integer between 0 and 2147483647",
|
Description: "Argument max-parts must be an integer between 0 and 2147483647",
|
||||||
|
@ -305,6 +305,21 @@ func getObjectLocation(r *http.Request, domains []string, bucket, object string)
|
|||||||
return u.String()
|
return u.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// s3EncodeName encodes string in response when encodingType
|
||||||
|
// is specified in AWS S3 requests.
|
||||||
|
func s3EncodeName(name string, encodingType string) (result string) {
|
||||||
|
// Quick path to exit
|
||||||
|
if encodingType == "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
encodingType = strings.ToLower(encodingType)
|
||||||
|
switch encodingType {
|
||||||
|
case "url":
|
||||||
|
return url.QueryEscape(name)
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
// generates ListBucketsResponse from array of BucketInfo which can be
|
// generates ListBucketsResponse from array of BucketInfo which can be
|
||||||
// serialized to match XML and JSON API spec output.
|
// serialized to match XML and JSON API spec output.
|
||||||
func generateListBucketsResponse(buckets []BucketInfo) ListBucketsResponse {
|
func generateListBucketsResponse(buckets []BucketInfo) ListBucketsResponse {
|
||||||
@ -327,7 +342,7 @@ func generateListBucketsResponse(buckets []BucketInfo) ListBucketsResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generates an ListObjectsV1 response for the said bucket with other enumerated options.
|
// generates an ListObjectsV1 response for the said bucket with other enumerated options.
|
||||||
func generateListObjectsV1Response(bucket, prefix, marker, delimiter string, maxKeys int, resp ListObjectsInfo) ListObjectsResponse {
|
func generateListObjectsV1Response(bucket, prefix, marker, delimiter, encodingType string, maxKeys int, resp ListObjectsInfo) ListObjectsResponse {
|
||||||
var contents []Object
|
var contents []Object
|
||||||
var prefixes []CommonPrefix
|
var prefixes []CommonPrefix
|
||||||
var owner = Owner{}
|
var owner = Owner{}
|
||||||
@ -339,7 +354,7 @@ func generateListObjectsV1Response(bucket, prefix, marker, delimiter string, max
|
|||||||
if object.Name == "" {
|
if object.Name == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
content.Key = object.Name
|
content.Key = s3EncodeName(object.Name, encodingType)
|
||||||
content.LastModified = object.ModTime.UTC().Format(timeFormatAMZLong)
|
content.LastModified = object.ModTime.UTC().Format(timeFormatAMZLong)
|
||||||
if object.ETag != "" {
|
if object.ETag != "" {
|
||||||
content.ETag = "\"" + object.ETag + "\""
|
content.ETag = "\"" + object.ETag + "\""
|
||||||
@ -349,20 +364,20 @@ func generateListObjectsV1Response(bucket, prefix, marker, delimiter string, max
|
|||||||
content.Owner = owner
|
content.Owner = owner
|
||||||
contents = append(contents, content)
|
contents = append(contents, content)
|
||||||
}
|
}
|
||||||
// TODO - support EncodingType in xml decoding
|
|
||||||
data.Name = bucket
|
data.Name = bucket
|
||||||
data.Contents = contents
|
data.Contents = contents
|
||||||
|
|
||||||
data.Prefix = prefix
|
data.EncodingType = encodingType
|
||||||
data.Marker = marker
|
data.Prefix = s3EncodeName(prefix, encodingType)
|
||||||
data.Delimiter = delimiter
|
data.Marker = s3EncodeName(marker, encodingType)
|
||||||
|
data.Delimiter = s3EncodeName(delimiter, encodingType)
|
||||||
data.MaxKeys = maxKeys
|
data.MaxKeys = maxKeys
|
||||||
|
|
||||||
data.NextMarker = resp.NextMarker
|
data.NextMarker = s3EncodeName(resp.NextMarker, encodingType)
|
||||||
data.IsTruncated = resp.IsTruncated
|
data.IsTruncated = resp.IsTruncated
|
||||||
for _, prefix := range resp.Prefixes {
|
for _, prefix := range resp.Prefixes {
|
||||||
var prefixItem = CommonPrefix{}
|
var prefixItem = CommonPrefix{}
|
||||||
prefixItem.Prefix = prefix
|
prefixItem.Prefix = s3EncodeName(prefix, encodingType)
|
||||||
prefixes = append(prefixes, prefixItem)
|
prefixes = append(prefixes, prefixItem)
|
||||||
}
|
}
|
||||||
data.CommonPrefixes = prefixes
|
data.CommonPrefixes = prefixes
|
||||||
@ -370,7 +385,7 @@ func generateListObjectsV1Response(bucket, prefix, marker, delimiter string, max
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generates an ListObjectsV2 response for the said bucket with other enumerated options.
|
// generates an ListObjectsV2 response for the said bucket with other enumerated options.
|
||||||
func generateListObjectsV2Response(bucket, prefix, token, nextToken, startAfter, delimiter string, fetchOwner, isTruncated bool, maxKeys int, objects []ObjectInfo, prefixes []string) ListObjectsV2Response {
|
func generateListObjectsV2Response(bucket, prefix, token, nextToken, startAfter, delimiter, encodingType string, fetchOwner, isTruncated bool, maxKeys int, objects []ObjectInfo, prefixes []string) ListObjectsV2Response {
|
||||||
var contents []Object
|
var contents []Object
|
||||||
var commonPrefixes []CommonPrefix
|
var commonPrefixes []CommonPrefix
|
||||||
var owner = Owner{}
|
var owner = Owner{}
|
||||||
@ -385,7 +400,7 @@ func generateListObjectsV2Response(bucket, prefix, token, nextToken, startAfter,
|
|||||||
if object.Name == "" {
|
if object.Name == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
content.Key = object.Name
|
content.Key = s3EncodeName(object.Name, encodingType)
|
||||||
content.LastModified = object.ModTime.UTC().Format(timeFormatAMZLong)
|
content.LastModified = object.ModTime.UTC().Format(timeFormatAMZLong)
|
||||||
if object.ETag != "" {
|
if object.ETag != "" {
|
||||||
content.ETag = "\"" + object.ETag + "\""
|
content.ETag = "\"" + object.ETag + "\""
|
||||||
@ -395,20 +410,20 @@ func generateListObjectsV2Response(bucket, prefix, token, nextToken, startAfter,
|
|||||||
content.Owner = owner
|
content.Owner = owner
|
||||||
contents = append(contents, content)
|
contents = append(contents, content)
|
||||||
}
|
}
|
||||||
// TODO - support EncodingType in xml decoding
|
|
||||||
data.Name = bucket
|
data.Name = bucket
|
||||||
data.Contents = contents
|
data.Contents = contents
|
||||||
|
|
||||||
|
data.EncodingType = encodingType
|
||||||
data.StartAfter = startAfter
|
data.StartAfter = startAfter
|
||||||
data.Delimiter = delimiter
|
data.Delimiter = s3EncodeName(delimiter, encodingType)
|
||||||
data.Prefix = prefix
|
data.Prefix = s3EncodeName(prefix, encodingType)
|
||||||
data.MaxKeys = maxKeys
|
data.MaxKeys = maxKeys
|
||||||
data.ContinuationToken = token
|
data.ContinuationToken = token
|
||||||
data.NextContinuationToken = nextToken
|
data.NextContinuationToken = nextToken
|
||||||
data.IsTruncated = isTruncated
|
data.IsTruncated = isTruncated
|
||||||
for _, prefix := range prefixes {
|
for _, prefix := range prefixes {
|
||||||
var prefixItem = CommonPrefix{}
|
var prefixItem = CommonPrefix{}
|
||||||
prefixItem.Prefix = prefix
|
prefixItem.Prefix = s3EncodeName(prefix, encodingType)
|
||||||
commonPrefixes = append(commonPrefixes, prefixItem)
|
commonPrefixes = append(commonPrefixes, prefixItem)
|
||||||
}
|
}
|
||||||
data.CommonPrefixes = commonPrefixes
|
data.CommonPrefixes = commonPrefixes
|
||||||
@ -452,11 +467,10 @@ func generateCompleteMultpartUploadResponse(bucket, key, location, etag string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generates ListPartsResponse from ListPartsInfo.
|
// generates ListPartsResponse from ListPartsInfo.
|
||||||
func generateListPartsResponse(partsInfo ListPartsInfo) ListPartsResponse {
|
func generateListPartsResponse(partsInfo ListPartsInfo, encodingType string) ListPartsResponse {
|
||||||
// TODO - support EncodingType in xml decoding
|
|
||||||
listPartsResponse := ListPartsResponse{}
|
listPartsResponse := ListPartsResponse{}
|
||||||
listPartsResponse.Bucket = partsInfo.Bucket
|
listPartsResponse.Bucket = partsInfo.Bucket
|
||||||
listPartsResponse.Key = partsInfo.Object
|
listPartsResponse.Key = s3EncodeName(partsInfo.Object, encodingType)
|
||||||
listPartsResponse.UploadID = partsInfo.UploadID
|
listPartsResponse.UploadID = partsInfo.UploadID
|
||||||
listPartsResponse.StorageClass = globalMinioDefaultStorageClass
|
listPartsResponse.StorageClass = globalMinioDefaultStorageClass
|
||||||
listPartsResponse.Initiator.ID = globalMinioDefaultOwnerID
|
listPartsResponse.Initiator.ID = globalMinioDefaultOwnerID
|
||||||
@ -480,29 +494,29 @@ func generateListPartsResponse(partsInfo ListPartsInfo) ListPartsResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generates ListMultipartUploadsResponse for given bucket and ListMultipartsInfo.
|
// generates ListMultipartUploadsResponse for given bucket and ListMultipartsInfo.
|
||||||
func generateListMultipartUploadsResponse(bucket string, multipartsInfo ListMultipartsInfo) ListMultipartUploadsResponse {
|
func generateListMultipartUploadsResponse(bucket string, multipartsInfo ListMultipartsInfo, encodingType string) ListMultipartUploadsResponse {
|
||||||
listMultipartUploadsResponse := ListMultipartUploadsResponse{}
|
listMultipartUploadsResponse := ListMultipartUploadsResponse{}
|
||||||
listMultipartUploadsResponse.Bucket = bucket
|
listMultipartUploadsResponse.Bucket = bucket
|
||||||
listMultipartUploadsResponse.Delimiter = multipartsInfo.Delimiter
|
listMultipartUploadsResponse.Delimiter = s3EncodeName(multipartsInfo.Delimiter, encodingType)
|
||||||
listMultipartUploadsResponse.IsTruncated = multipartsInfo.IsTruncated
|
listMultipartUploadsResponse.IsTruncated = multipartsInfo.IsTruncated
|
||||||
listMultipartUploadsResponse.EncodingType = multipartsInfo.EncodingType
|
listMultipartUploadsResponse.EncodingType = encodingType
|
||||||
listMultipartUploadsResponse.Prefix = multipartsInfo.Prefix
|
listMultipartUploadsResponse.Prefix = s3EncodeName(multipartsInfo.Prefix, encodingType)
|
||||||
listMultipartUploadsResponse.KeyMarker = multipartsInfo.KeyMarker
|
listMultipartUploadsResponse.KeyMarker = s3EncodeName(multipartsInfo.KeyMarker, encodingType)
|
||||||
listMultipartUploadsResponse.NextKeyMarker = multipartsInfo.NextKeyMarker
|
listMultipartUploadsResponse.NextKeyMarker = s3EncodeName(multipartsInfo.NextKeyMarker, encodingType)
|
||||||
listMultipartUploadsResponse.MaxUploads = multipartsInfo.MaxUploads
|
listMultipartUploadsResponse.MaxUploads = multipartsInfo.MaxUploads
|
||||||
listMultipartUploadsResponse.NextUploadIDMarker = multipartsInfo.NextUploadIDMarker
|
listMultipartUploadsResponse.NextUploadIDMarker = multipartsInfo.NextUploadIDMarker
|
||||||
listMultipartUploadsResponse.UploadIDMarker = multipartsInfo.UploadIDMarker
|
listMultipartUploadsResponse.UploadIDMarker = multipartsInfo.UploadIDMarker
|
||||||
listMultipartUploadsResponse.CommonPrefixes = make([]CommonPrefix, len(multipartsInfo.CommonPrefixes))
|
listMultipartUploadsResponse.CommonPrefixes = make([]CommonPrefix, len(multipartsInfo.CommonPrefixes))
|
||||||
for index, commonPrefix := range multipartsInfo.CommonPrefixes {
|
for index, commonPrefix := range multipartsInfo.CommonPrefixes {
|
||||||
listMultipartUploadsResponse.CommonPrefixes[index] = CommonPrefix{
|
listMultipartUploadsResponse.CommonPrefixes[index] = CommonPrefix{
|
||||||
Prefix: commonPrefix,
|
Prefix: s3EncodeName(commonPrefix, encodingType),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listMultipartUploadsResponse.Uploads = make([]Upload, len(multipartsInfo.Uploads))
|
listMultipartUploadsResponse.Uploads = make([]Upload, len(multipartsInfo.Uploads))
|
||||||
for index, upload := range multipartsInfo.Uploads {
|
for index, upload := range multipartsInfo.Uploads {
|
||||||
newUpload := Upload{}
|
newUpload := Upload{}
|
||||||
newUpload.UploadID = upload.UploadID
|
newUpload.UploadID = upload.UploadID
|
||||||
newUpload.Key = upload.Object
|
newUpload.Key = s3EncodeName(upload.Object, encodingType)
|
||||||
newUpload.Initiated = upload.Initiated.UTC().Format(timeFormatAMZLong)
|
newUpload.Initiated = upload.Initiated.UTC().Format(timeFormatAMZLong)
|
||||||
listMultipartUploadsResponse.Uploads[index] = newUpload
|
listMultipartUploadsResponse.Uploads[index] = newUpload
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/minio/minio/cmd/crypto"
|
"github.com/minio/minio/cmd/crypto"
|
||||||
@ -32,12 +33,19 @@ import (
|
|||||||
// - delimiter if set should be equal to '/', otherwise the request is rejected.
|
// - delimiter if set should be equal to '/', otherwise the request is rejected.
|
||||||
// - marker if set should have a common prefix with 'prefix' param, otherwise
|
// - marker if set should have a common prefix with 'prefix' param, otherwise
|
||||||
// the request is rejected.
|
// the request is rejected.
|
||||||
func validateListObjectsArgs(prefix, marker, delimiter string, maxKeys int) APIErrorCode {
|
func validateListObjectsArgs(prefix, marker, delimiter, encodingType string, maxKeys int) APIErrorCode {
|
||||||
// Max keys cannot be negative.
|
// Max keys cannot be negative.
|
||||||
if maxKeys < 0 {
|
if maxKeys < 0 {
|
||||||
return ErrInvalidMaxKeys
|
return ErrInvalidMaxKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if encodingType != "" {
|
||||||
|
// Only url encoding type is supported
|
||||||
|
if strings.ToLower(encodingType) != "url" {
|
||||||
|
return ErrInvalidEncodingMethod
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Minio special conditions for ListObjects.
|
/// Minio special conditions for ListObjects.
|
||||||
|
|
||||||
// Verify if delimiter is anything other than '/', which we do not support.
|
// Verify if delimiter is anything other than '/', which we do not support.
|
||||||
@ -78,7 +86,7 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
|
|||||||
urlValues := r.URL.Query()
|
urlValues := r.URL.Query()
|
||||||
|
|
||||||
// Extract all the listObjectsV2 query params to their native values.
|
// Extract all the listObjectsV2 query params to their native values.
|
||||||
prefix, token, startAfter, delimiter, fetchOwner, maxKeys, _, errCode := getListObjectsV2Args(urlValues)
|
prefix, token, startAfter, delimiter, fetchOwner, maxKeys, encodingType, errCode := getListObjectsV2Args(urlValues)
|
||||||
if errCode != ErrNone {
|
if errCode != ErrNone {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(errCode), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(errCode), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
@ -86,7 +94,7 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
|
|||||||
|
|
||||||
// Validate the query params before beginning to serve the request.
|
// Validate the query params before beginning to serve the request.
|
||||||
// fetch-owner is not validated since it is a boolean
|
// fetch-owner is not validated since it is a boolean
|
||||||
if s3Error := validateListObjectsArgs(prefix, token, delimiter, maxKeys); s3Error != ErrNone {
|
if s3Error := validateListObjectsArgs(prefix, token, delimiter, encodingType, maxKeys); s3Error != ErrNone {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -126,7 +134,7 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
|
|||||||
}
|
}
|
||||||
|
|
||||||
response := generateListObjectsV2Response(bucket, prefix, token, listObjectsV2Info.NextContinuationToken, startAfter,
|
response := generateListObjectsV2Response(bucket, prefix, token, listObjectsV2Info.NextContinuationToken, startAfter,
|
||||||
delimiter, fetchOwner, listObjectsV2Info.IsTruncated, maxKeys, listObjectsV2Info.Objects, listObjectsV2Info.Prefixes)
|
delimiter, encodingType, fetchOwner, listObjectsV2Info.IsTruncated, maxKeys, listObjectsV2Info.Objects, listObjectsV2Info.Prefixes)
|
||||||
|
|
||||||
// Write success response.
|
// Write success response.
|
||||||
writeSuccessResponseXML(w, encodeResponse(response))
|
writeSuccessResponseXML(w, encodeResponse(response))
|
||||||
@ -158,14 +166,14 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract all the litsObjectsV1 query params to their native values.
|
// Extract all the litsObjectsV1 query params to their native values.
|
||||||
prefix, marker, delimiter, maxKeys, _, s3Error := getListObjectsV1Args(r.URL.Query())
|
prefix, marker, delimiter, maxKeys, encodingType, s3Error := getListObjectsV1Args(r.URL.Query())
|
||||||
if s3Error != ErrNone {
|
if s3Error != ErrNone {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate all the query params before beginning to serve the request.
|
// Validate all the query params before beginning to serve the request.
|
||||||
if s3Error := validateListObjectsArgs(prefix, marker, delimiter, maxKeys); s3Error != ErrNone {
|
if s3Error := validateListObjectsArgs(prefix, marker, delimiter, encodingType, maxKeys); s3Error != ErrNone {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -204,7 +212,7 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response := generateListObjectsV1Response(bucket, prefix, marker, delimiter, maxKeys, listObjectsInfo)
|
response := generateListObjectsV1Response(bucket, prefix, marker, delimiter, encodingType, maxKeys, listObjectsInfo)
|
||||||
|
|
||||||
// Write success response.
|
// Write success response.
|
||||||
writeSuccessResponseXML(w, encodeResponse(response))
|
writeSuccessResponseXML(w, encodeResponse(response))
|
||||||
|
@ -155,7 +155,7 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, _, errCode := getBucketMultipartResources(r.URL.Query())
|
prefix, keyMarker, uploadIDMarker, delimiter, maxUploads, encodingType, errCode := getBucketMultipartResources(r.URL.Query())
|
||||||
if errCode != ErrNone {
|
if errCode != ErrNone {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(errCode), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(errCode), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
@ -180,7 +180,7 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// generate response
|
// generate response
|
||||||
response := generateListMultipartUploadsResponse(bucket, listMultipartsInfo)
|
response := generateListMultipartUploadsResponse(bucket, listMultipartsInfo, encodingType)
|
||||||
encodedSuccessResponse := encodeResponse(response)
|
encodedSuccessResponse := encodeResponse(response)
|
||||||
|
|
||||||
// write success response.
|
// write success response.
|
||||||
|
@ -2094,7 +2094,7 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadID, partNumberMarker, maxParts, _, s3Error := getObjectResources(r.URL.Query())
|
uploadID, partNumberMarker, maxParts, encodingType, s3Error := getObjectResources(r.URL.Query())
|
||||||
if s3Error != ErrNone {
|
if s3Error != ErrNone {
|
||||||
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
|
||||||
return
|
return
|
||||||
@ -2145,7 +2145,7 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
response := generateListPartsResponse(listPartsInfo)
|
response := generateListPartsResponse(listPartsInfo, encodingType)
|
||||||
encodedSuccessResponse := encodeResponse(response)
|
encodedSuccessResponse := encodeResponse(response)
|
||||||
|
|
||||||
// Write success response.
|
// Write success response.
|
||||||
|
@ -1658,62 +1658,59 @@ func (s *TestSuiteCommon) TestListObjectsHandler(c *check) {
|
|||||||
c.Assert(err, nil)
|
c.Assert(err, nil)
|
||||||
c.Assert(response.StatusCode, http.StatusOK)
|
c.Assert(response.StatusCode, http.StatusOK)
|
||||||
|
|
||||||
buffer1 := bytes.NewReader([]byte("Hello World"))
|
for _, objectName := range []string{"foo bar 1", "foo bar 2"} {
|
||||||
request, err = newTestSignedRequest("PUT", getPutObjectURL(s.endPoint, bucketName, "bar"),
|
buffer := bytes.NewReader([]byte("Hello World"))
|
||||||
int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer)
|
request, err = newTestSignedRequest("PUT", getPutObjectURL(s.endPoint, bucketName, objectName),
|
||||||
c.Assert(err, nil)
|
int64(buffer.Len()), buffer, s.accessKey, s.secretKey, s.signer)
|
||||||
|
c.Assert(err, nil)
|
||||||
|
|
||||||
client = http.Client{Transport: s.transport}
|
client = http.Client{Transport: s.transport}
|
||||||
response, err = client.Do(request)
|
response, err = client.Do(request)
|
||||||
c.Assert(err, nil)
|
c.Assert(err, nil)
|
||||||
c.Assert(response.StatusCode, http.StatusOK)
|
c.Assert(response.StatusCode, http.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
// create listObjectsV1 request with valid parameters
|
var testCases = []struct {
|
||||||
request, err = newTestSignedRequest("GET", getListObjectsV1URL(s.endPoint, bucketName, "1000"),
|
getURL string
|
||||||
0, nil, s.accessKey, s.secretKey, s.signer)
|
expectedStrings []string
|
||||||
c.Assert(err, nil)
|
}{
|
||||||
client = http.Client{Transport: s.transport}
|
{getListObjectsV1URL(s.endPoint, bucketName, "", "1000", ""), []string{"<Key>foo bar 1</Key>", "<Key>foo bar 2</Key>"}},
|
||||||
// execute the HTTP request.
|
{getListObjectsV1URL(s.endPoint, bucketName, "", "1000", "url"), []string{"<Key>foo+bar+1</Key>", "<Key>foo+bar+2</Key>"}},
|
||||||
response, err = client.Do(request)
|
{getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", ""),
|
||||||
c.Assert(err, nil)
|
[]string{
|
||||||
c.Assert(response.StatusCode, http.StatusOK)
|
"<Key>foo bar 1</Key>",
|
||||||
|
"<Key>foo bar 2</Key>",
|
||||||
|
"<Owner><ID></ID><DisplayName></DisplayName></Owner>",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "true", ""),
|
||||||
|
[]string{
|
||||||
|
"<Key>foo bar 1</Key>",
|
||||||
|
"<Key>foo bar 2</Key>",
|
||||||
|
fmt.Sprintf("<Owner><ID>%s</ID><DisplayName></DisplayName></Owner>", globalMinioDefaultOwnerID),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "url"), []string{"<Key>foo+bar+1</Key>", "<Key>foo+bar+2</Key>"}},
|
||||||
|
}
|
||||||
|
|
||||||
getContent, err := ioutil.ReadAll(response.Body)
|
for i, testCase := range testCases {
|
||||||
c.Assert(err, nil)
|
// create listObjectsV1 request with valid parameters
|
||||||
c.Assert(strings.Contains(string(getContent), "<Key>bar</Key>"), true)
|
request, err = newTestSignedRequest("GET", testCase.getURL, 0, nil, s.accessKey, s.secretKey, s.signer)
|
||||||
|
c.Assert(err, nil)
|
||||||
|
client = http.Client{Transport: s.transport}
|
||||||
|
// execute the HTTP request.
|
||||||
|
response, err = client.Do(request)
|
||||||
|
c.Assert(err, nil)
|
||||||
|
c.Assert(response.StatusCode, http.StatusOK)
|
||||||
|
|
||||||
// create listObjectsV2 request with valid parameters
|
getContent, err := ioutil.ReadAll(response.Body)
|
||||||
request, err = newTestSignedRequest("GET", getListObjectsV2URL(s.endPoint, bucketName, "1000", ""),
|
c.Assert(err, nil)
|
||||||
0, nil, s.accessKey, s.secretKey, s.signer)
|
|
||||||
c.Assert(err, nil)
|
|
||||||
client = http.Client{Transport: s.transport}
|
|
||||||
// execute the HTTP request.
|
|
||||||
response, err = client.Do(request)
|
|
||||||
c.Assert(err, nil)
|
|
||||||
c.Assert(response.StatusCode, http.StatusOK)
|
|
||||||
|
|
||||||
getContent, err = ioutil.ReadAll(response.Body)
|
|
||||||
c.Assert(err, nil)
|
|
||||||
c.Assert(strings.Contains(string(getContent), "<Key>bar</Key>"), true)
|
|
||||||
c.Assert(strings.Contains(string(getContent), "<Owner><ID></ID><DisplayName></DisplayName></Owner>"), true)
|
|
||||||
|
|
||||||
// create listObjectsV2 request with valid parameters and fetch-owner activated
|
|
||||||
request, err = newTestSignedRequest("GET", getListObjectsV2URL(s.endPoint, bucketName, "1000", "true"),
|
|
||||||
0, nil, s.accessKey, s.secretKey, s.signer)
|
|
||||||
c.Assert(err, nil)
|
|
||||||
client = http.Client{Transport: s.transport}
|
|
||||||
// execute the HTTP request.
|
|
||||||
response, err = client.Do(request)
|
|
||||||
c.Assert(err, nil)
|
|
||||||
c.Assert(response.StatusCode, http.StatusOK)
|
|
||||||
|
|
||||||
getContent, err = ioutil.ReadAll(response.Body)
|
|
||||||
c.Assert(err, nil)
|
|
||||||
|
|
||||||
c.Assert(strings.Contains(string(getContent), "<Key>bar</Key>"), true)
|
|
||||||
c.Assert(strings.Contains(string(getContent), fmt.Sprintf("<Owner><ID>%s</ID><DisplayName></DisplayName></Owner>",
|
|
||||||
globalMinioDefaultOwnerID)), true)
|
|
||||||
|
|
||||||
|
fmt.Printf("Test %d: %+v vs %+v\n", i+1, string(getContent), testCase.expectedStrings)
|
||||||
|
for _, expectedStr := range testCase.expectedStrings {
|
||||||
|
c.Assert(strings.Contains(string(getContent), expectedStr), true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestListObjectsHandlerErrors - Setting invalid parameters to List Objects
|
// TestListObjectsHandlerErrors - Setting invalid parameters to List Objects
|
||||||
@ -1733,7 +1730,7 @@ func (s *TestSuiteCommon) TestListObjectsHandlerErrors(c *check) {
|
|||||||
c.Assert(response.StatusCode, http.StatusOK)
|
c.Assert(response.StatusCode, http.StatusOK)
|
||||||
|
|
||||||
// create listObjectsV1 request with invalid value of max-keys parameter. max-keys is set to -2.
|
// create listObjectsV1 request with invalid value of max-keys parameter. max-keys is set to -2.
|
||||||
request, err = newTestSignedRequest("GET", getListObjectsV1URL(s.endPoint, bucketName, "-2"),
|
request, err = newTestSignedRequest("GET", getListObjectsV1URL(s.endPoint, bucketName, "", "-2", ""),
|
||||||
0, nil, s.accessKey, s.secretKey, s.signer)
|
0, nil, s.accessKey, s.secretKey, s.signer)
|
||||||
c.Assert(err, nil)
|
c.Assert(err, nil)
|
||||||
client = http.Client{Transport: s.transport}
|
client = http.Client{Transport: s.transport}
|
||||||
@ -1744,7 +1741,7 @@ func (s *TestSuiteCommon) TestListObjectsHandlerErrors(c *check) {
|
|||||||
verifyError(c, response, "InvalidArgument", "Argument maxKeys must be an integer between 0 and 2147483647", http.StatusBadRequest)
|
verifyError(c, response, "InvalidArgument", "Argument maxKeys must be an integer between 0 and 2147483647", http.StatusBadRequest)
|
||||||
|
|
||||||
// create listObjectsV2 request with invalid value of max-keys parameter. max-keys is set to -2.
|
// create listObjectsV2 request with invalid value of max-keys parameter. max-keys is set to -2.
|
||||||
request, err = newTestSignedRequest("GET", getListObjectsV2URL(s.endPoint, bucketName, "-2", ""),
|
request, err = newTestSignedRequest("GET", getListObjectsV2URL(s.endPoint, bucketName, "", "-2", "", ""),
|
||||||
0, nil, s.accessKey, s.secretKey, s.signer)
|
0, nil, s.accessKey, s.secretKey, s.signer)
|
||||||
c.Assert(err, nil)
|
c.Assert(err, nil)
|
||||||
client = http.Client{Transport: s.transport}
|
client = http.Client{Transport: s.transport}
|
||||||
|
@ -1458,16 +1458,19 @@ func getBucketLocationURL(endPoint, bucketName string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return URL for listing objects in the bucket with V1 legacy API.
|
// return URL for listing objects in the bucket with V1 legacy API.
|
||||||
func getListObjectsV1URL(endPoint, bucketName string, maxKeys string) string {
|
func getListObjectsV1URL(endPoint, bucketName, prefix, maxKeys, encodingType string) string {
|
||||||
queryValue := url.Values{}
|
queryValue := url.Values{}
|
||||||
if maxKeys != "" {
|
if maxKeys != "" {
|
||||||
queryValue.Set("max-keys", maxKeys)
|
queryValue.Set("max-keys", maxKeys)
|
||||||
}
|
}
|
||||||
return makeTestTargetURL(endPoint, bucketName, "", queryValue)
|
if encodingType != "" {
|
||||||
|
queryValue.Set("encoding-type", encodingType)
|
||||||
|
}
|
||||||
|
return makeTestTargetURL(endPoint, bucketName, prefix, queryValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// return URL for listing objects in the bucket with V2 API.
|
// return URL for listing objects in the bucket with V2 API.
|
||||||
func getListObjectsV2URL(endPoint, bucketName string, maxKeys string, fetchOwner string) string {
|
func getListObjectsV2URL(endPoint, bucketName, prefix, maxKeys, fetchOwner, encodingType string) string {
|
||||||
queryValue := url.Values{}
|
queryValue := url.Values{}
|
||||||
queryValue.Set("list-type", "2") // Enables list objects V2 URL.
|
queryValue.Set("list-type", "2") // Enables list objects V2 URL.
|
||||||
if maxKeys != "" {
|
if maxKeys != "" {
|
||||||
@ -1476,7 +1479,10 @@ func getListObjectsV2URL(endPoint, bucketName string, maxKeys string, fetchOwner
|
|||||||
if fetchOwner != "" {
|
if fetchOwner != "" {
|
||||||
queryValue.Set("fetch-owner", fetchOwner)
|
queryValue.Set("fetch-owner", fetchOwner)
|
||||||
}
|
}
|
||||||
return makeTestTargetURL(endPoint, bucketName, "", queryValue)
|
if encodingType != "" {
|
||||||
|
queryValue.Set("encoding-type", encodingType)
|
||||||
|
}
|
||||||
|
return makeTestTargetURL(endPoint, bucketName, prefix, queryValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// return URL for a new multipart upload.
|
// return URL for a new multipart upload.
|
||||||
|
Loading…
Reference in New Issue
Block a user