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:
Anis Elleuch 2019-02-24 07:14:24 +01:00 committed by Nitish Tiwari
parent dab314900d
commit 5efbe8a1b3
7 changed files with 124 additions and 93 deletions

View File

@ -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",

View File

@ -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
} }

View File

@ -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))

View File

@ -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.

View File

@ -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.

View File

@ -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}

View File

@ -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.