diff --git a/cmd/server_test.go b/cmd/server_test.go
index aa86368bb..100663702 100644
--- a/cmd/server_test.go
+++ b/cmd/server_test.go
@@ -107,6 +107,7 @@ func runAllTests(suite *TestSuiteCommon, c *check) {
suite.TestListObjectsHandler(c)
suite.TestListObjectVersionsOutputOrderHandler(c)
suite.TestListObjectsHandlerErrors(c)
+ suite.TestListObjectsV2HadoopUAHandler(c)
suite.TestPutBucketErrors(c)
suite.TestGetObjectLarge10MiB(c)
suite.TestGetObjectLarge11MiB(c)
@@ -1604,23 +1605,23 @@ func (s *TestSuiteCommon) TestListObjectsHandler(c *check) {
{getListObjectsV1URL(s.endPoint, bucketName, "", "1000", ""), []string{"foo bar 1", "foo bar 2"}},
{getListObjectsV1URL(s.endPoint, bucketName, "", "1000", "url"), []string{"foo+bar+1", "foo+bar+2"}},
{
- getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", ""),
+ getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "", ""),
[]string{
"foo bar 1",
"foo bar 2",
},
},
{
- getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "true", ""),
+ getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "true", "", ""),
[]string{
"foo bar 1",
"foo bar 2",
fmt.Sprintf("%sminio", globalMinioDefaultOwnerID),
},
},
- {getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "url"), []string{"foo+bar+1", "foo+bar+2"}},
+ {getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "url", ""), []string{"foo+bar+1", "foo+bar+2"}},
{
- getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", ""),
+ getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "", ""),
[]string{
"obj2",
"obj2/",
@@ -1646,6 +1647,131 @@ func (s *TestSuiteCommon) TestListObjectsHandler(c *check) {
}
}
+// TestListObjectsV2HadoopUAHandler - Test ListObjectsV2 call with max-keys=2 and Hadoop User-Agent
+func (s *TestSuiteCommon) TestListObjectsV2HadoopUAHandler(c *check) {
+ // generate a random bucket name.
+ bucketName := getRandomBucketName()
+ // HTTP request to create the bucket.
+ request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName),
+ 0, nil, s.accessKey, s.secretKey, s.signer)
+ c.Assert(err, nil)
+
+ // execute the HTTP request to create bucket.
+ response, err := s.client.Do(request)
+ c.Assert(err, nil)
+ c.Assert(response.StatusCode, http.StatusOK)
+
+ // enable versioning on the bucket.
+ enableVersioningBody := []byte("Enabled")
+ enableVersioningBucketRequest, err := newTestSignedRequest(http.MethodPut, getBucketVersioningConfigURL(s.endPoint, bucketName),
+ int64(len(enableVersioningBody)), bytes.NewReader(enableVersioningBody), s.accessKey, s.secretKey, s.signer)
+ c.Assert(err, nil)
+ // execute the HTTP request to create bucket.
+ response, err = s.client.Do(enableVersioningBucketRequest)
+ c.Assert(err, nil)
+ c.Assert(response.StatusCode, http.StatusOK)
+
+ for _, objectName := range []string{"pfx/a/1.txt", "pfx/b/2.txt", "pfx/", "pfx2/c/3.txt", "pfx2/d/3.txt", "pfx1/1.txt", "pfx2/"} {
+ buffer := bytes.NewReader([]byte(""))
+ request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName),
+ int64(buffer.Len()), buffer, s.accessKey, s.secretKey, s.signer)
+ c.Assert(err, nil)
+ response, err = s.client.Do(request)
+ c.Assert(err, nil)
+ c.Assert(response.StatusCode, http.StatusOK)
+ }
+ for _, objectName := range []string{"pfx2/c/3.txt", "pfx2/d/3.txt", "pfx2/"} {
+ delRequest, err := newTestSignedRequest(http.MethodDelete, getDeleteObjectURL(s.endPoint, bucketName, objectName),
+ 0, nil, s.accessKey, s.secretKey, s.signer)
+ c.Assert(err, nil)
+ response, err = s.client.Do(delRequest)
+ c.Assert(err, nil)
+ c.Assert(response.StatusCode, http.StatusNoContent)
+ }
+ testCases := []struct {
+ getURL string
+ expectedStrings []string
+ userAgent string
+ }{
+ {
+ getListObjectsV2URL(s.endPoint, bucketName, "pfx/a/", "2", "", "", "/"),
+ []string{
+ "pfx/a/1.txt",
+ "pfx/a",
+ },
+ "Hadoop 3.3.2, aws-sdk-java/1.12.262 Linux/5.14.0-362.24.1.el9_3.x86_64 OpenJDK_64-Bit_Server_VM/11.0.22+7 java/11.0.22 scala/2.12.15 vendor/Eclipse_Adoptium cfg/retry-mode/legacy",
+ },
+ {
+ getListObjectsV2URL(s.endPoint, bucketName, "pfx/a/", "2", "", "", "/"),
+ []string{
+ "pfx/a/",
+ "pfx/a/1.txt",
+ },
+ "",
+ },
+ {
+ getListObjectsV2URL(s.endPoint, bucketName, "pfx2/c", "2", "true", "", "/"),
+ []string{
+ "pfx2/c12/falsepfx2/c/",
+ },
+ "",
+ },
+
+ {
+ getListObjectsV2URL(s.endPoint, bucketName, "pfx2/c", "2", "true", "", "/"),
+ []string{
+ "pfx2/c02/false",
+ },
+ "Hadoop 3.3.2, aws-sdk-java/1.12.262 Linux/5.14.0-362.24.1.el9_3.x86_64 OpenJDK_64-Bit_Server_VM/11.0.22+7 java/11.0.22 scala/2.12.15 vendor/Eclipse_Adoptium cfg/retry-mode/legacy",
+ },
+ {
+ getListObjectsV2URL(s.endPoint, bucketName, "pfx2/c", "2", "true", "", "/"),
+ []string{
+ "pfx2/c02/false",
+ },
+ "Hadoop 3.3.2, aws-sdk-java/1.12.262 Linux/5.14.0-362.24.1.el9_3.x86_64 OpenJDK_64-Bit_Server_VM/11.0.22+7 java/11.0.22 scala/2.12.15 vendor/Eclipse_Adoptium cfg/retry-mode/legacy",
+ },
+ {
+ getListObjectsV2URL(s.endPoint, bucketName, "pfx2/", "2", "false", "", "/"),
+ []string{
+ "pfx2/02/false",
+ },
+ "Hadoop 3.3.2, aws-sdk-java/1.12.262 Linux/5.14.0-362.24.1.el9_3.x86_64 OpenJDK_64-Bit_Server_VM/11.0.22+7 java/11.0.22 scala/2.12.15 vendor/Eclipse_Adoptium cfg/retry-mode/legacy",
+ },
+ {
+ getListObjectsV2URL(s.endPoint, bucketName, "pfx2/", "2", "false", "", "/"),
+ []string{
+ "pfx2/c/",
+ },
+ "",
+ },
+ {
+ getListObjectsV2URL(s.endPoint, bucketName, "pfx2/", "2", "false", "", "/"),
+ []string{
+ "pfx2/02/false",
+ },
+ "Hadoop 3.3.2, aws-sdk-java/1.12.262 Linux/5.14.0-362.24.1.el9_3.x86_64 OpenJDK_64-Bit_Server_VM/11.0.22+7 java/11.0.22 scala/2.12.15 vendor/Eclipse_Adoptium cfg/retry-mode/legacy",
+ },
+ }
+ for _, testCase := range testCases {
+ // create listObjectsV1 request with valid parameters
+ request, err = newTestSignedRequest(http.MethodGet, testCase.getURL, 0, nil, s.accessKey, s.secretKey, s.signer)
+ request.Header.Set("User-Agent", testCase.userAgent)
+ c.Assert(err, nil)
+ // execute the HTTP request.
+ response, err = s.client.Do(request)
+ c.Assert(err, nil)
+ c.Assert(response.StatusCode, http.StatusOK)
+
+ getContent, err := io.ReadAll(response.Body)
+ c.Assert(err, nil)
+
+ for _, expectedStr := range testCase.expectedStrings {
+ c.Assert(strings.Contains(string(getContent), expectedStr), true)
+ }
+ }
+}
+
// TestListObjectVersionsHandler - checks the order of
// and XML tags in a version listing
func (s *TestSuiteCommon) TestListObjectVersionsOutputOrderHandler(c *check) {
@@ -1747,7 +1873,7 @@ func (s *TestSuiteCommon) TestListObjectsSpecialCharactersHandler(c *check) {
{getListObjectsV1URL(s.endPoint, bucketName, "", "1000", ""), []string{"foo bar 1", "foo bar 2", "foo bar"}},
{getListObjectsV1URL(s.endPoint, bucketName, "", "1000", "url"), []string{"foo+bar+1", "foo+bar+2", "foo+%01+bar"}},
{
- getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", ""),
+ getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "", ""),
[]string{
"foo bar 1",
"foo bar 2",
@@ -1756,7 +1882,7 @@ func (s *TestSuiteCommon) TestListObjectsSpecialCharactersHandler(c *check) {
},
},
{
- getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "true", ""),
+ getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "true", "", ""),
[]string{
"foo bar 1",
"foo bar 2",
@@ -1764,7 +1890,7 @@ func (s *TestSuiteCommon) TestListObjectsSpecialCharactersHandler(c *check) {
fmt.Sprintf("%sminio", globalMinioDefaultOwnerID),
},
},
- {getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "url"), []string{"foo+bar+1", "foo+bar+2", "foo+%01+bar"}},
+ {getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "url", ""), []string{"foo+bar+1", "foo+bar+2", "foo+%01+bar"}},
}
for _, testCase := range testCases {
@@ -1811,7 +1937,7 @@ func (s *TestSuiteCommon) TestListObjectsHandlerErrors(c *check) {
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.
- request, err = newTestSignedRequest(http.MethodGet, getListObjectsV2URL(s.endPoint, bucketName, "", "-2", "", ""),
+ request, err = newTestSignedRequest(http.MethodGet, getListObjectsV2URL(s.endPoint, bucketName, "", "-2", "", "", ""),
0, nil, s.accessKey, s.secretKey, s.signer)
c.Assert(err, nil)
// execute the HTTP request.
diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go
index c57e3ce1e..cf4643a3a 100644
--- a/cmd/test-utils_test.go
+++ b/cmd/test-utils_test.go
@@ -1399,7 +1399,7 @@ func getListObjectVersionsURL(endPoint, bucketName, prefix, maxKeys, encodingTyp
}
// return URL for listing objects in the bucket with V2 API.
-func getListObjectsV2URL(endPoint, bucketName, prefix, maxKeys, fetchOwner, encodingType string) string {
+func getListObjectsV2URL(endPoint, bucketName, prefix, maxKeys, fetchOwner, encodingType, delimiter string) string {
queryValue := url.Values{}
queryValue.Set("list-type", "2") // Enables list objects V2 URL.
if maxKeys != "" {
@@ -1411,7 +1411,13 @@ func getListObjectsV2URL(endPoint, bucketName, prefix, maxKeys, fetchOwner, enco
if encodingType != "" {
queryValue.Set("encoding-type", encodingType)
}
- return makeTestTargetURL(endPoint, bucketName, prefix, queryValue)
+ if prefix != "" {
+ queryValue.Set("prefix", prefix)
+ }
+ if delimiter != "" {
+ queryValue.Set("delimiter", delimiter)
+ }
+ return makeTestTargetURL(endPoint, bucketName, "", queryValue)
}
// return URL for a new multipart upload.