storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/server_test.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2015, 2016, 2017, 2018 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "bytes" 21 "encoding/xml" 22 "fmt" 23 "io" 24 "io/ioutil" 25 "math/rand" 26 "net/http" 27 "net/url" 28 "reflect" 29 "strings" 30 "sync" 31 "testing" 32 "time" 33 34 humanize "github.com/dustin/go-humanize" 35 "github.com/minio/minio-go/v7/pkg/set" 36 37 xhttp "storj.io/minio/cmd/http" 38 "storj.io/minio/pkg/bucket/policy" 39 ) 40 41 // API suite container common to both FS and Erasure. 42 type TestSuiteCommon struct { 43 serverType string 44 testServer TestServer 45 endPoint string 46 accessKey string 47 secretKey string 48 signer signerType 49 secure bool 50 client *http.Client 51 } 52 53 type check struct { 54 *testing.T 55 testType string 56 } 57 58 // Assert - checks if gotValue is same as expectedValue, if not fails the test. 59 func (c *check) Assert(gotValue interface{}, expectedValue interface{}) { 60 if !reflect.DeepEqual(gotValue, expectedValue) { 61 c.Fatalf("Test %s:%s expected %v, got %v", getSource(2), c.testType, expectedValue, gotValue) 62 } 63 } 64 65 func verifyError(c *check, response *http.Response, code, description string, statusCode int) { 66 data, err := ioutil.ReadAll(response.Body) 67 c.Assert(err, nil) 68 errorResponse := APIErrorResponse{} 69 err = xml.Unmarshal(data, &errorResponse) 70 c.Assert(err, nil) 71 c.Assert(errorResponse.Code, code) 72 c.Assert(errorResponse.Message, description) 73 c.Assert(response.StatusCode, statusCode) 74 } 75 76 func runAllTests(suite *TestSuiteCommon, c *check) { 77 suite.SetUpSuite(c) 78 suite.TestCors(c) 79 suite.TestObjectDir(c) 80 suite.TestBucketPolicy(c) 81 suite.TestDeleteBucket(c) 82 suite.TestDeleteBucketNotEmpty(c) 83 suite.TestDeleteMultipleObjects(c) 84 suite.TestDeleteObject(c) 85 suite.TestNonExistentBucket(c) 86 suite.TestEmptyObject(c) 87 suite.TestBucket(c) 88 suite.TestObjectGetAnonymous(c) 89 suite.TestMultipleObjects(c) 90 suite.TestHeader(c) 91 suite.TestPutBucket(c) 92 suite.TestCopyObject(c) 93 suite.TestPutObject(c) 94 suite.TestListBuckets(c) 95 suite.TestValidateSignature(c) 96 suite.TestSHA256Mismatch(c) 97 suite.TestPutObjectLongName(c) 98 suite.TestNotBeAbleToCreateObjectInNonexistentBucket(c) 99 suite.TestHeadOnObjectLastModified(c) 100 suite.TestHeadOnBucket(c) 101 suite.TestContentTypePersists(c) 102 suite.TestPartialContent(c) 103 suite.TestListObjectsHandler(c) 104 suite.TestListObjectsHandlerErrors(c) 105 suite.TestPutBucketErrors(c) 106 suite.TestGetObjectLarge10MiB(c) 107 suite.TestGetObjectLarge11MiB(c) 108 suite.TestGetPartialObjectMisAligned(c) 109 suite.TestGetPartialObjectLarge11MiB(c) 110 suite.TestGetPartialObjectLarge10MiB(c) 111 suite.TestGetObjectErrors(c) 112 suite.TestGetObjectRangeErrors(c) 113 suite.TestObjectMultipartAbort(c) 114 suite.TestBucketMultipartList(c) 115 suite.TestValidateObjectMultipartUploadID(c) 116 suite.TestObjectMultipartListError(c) 117 suite.TestObjectValidMD5(c) 118 suite.TestObjectMultipart(c) 119 suite.TearDownSuite(c) 120 } 121 122 func TestServerSuite(t *testing.T) { 123 testCases := []*TestSuiteCommon{ 124 // Init and run test on FS backend with signature v4. 125 {serverType: "FS", signer: signerV4}, 126 // Init and run test on FS backend with signature v2. 127 {serverType: "FS", signer: signerV2}, 128 // Init and run test on FS backend, with tls enabled. 129 {serverType: "FS", signer: signerV4, secure: true}, 130 // Init and run test on Erasure backend. 131 {serverType: "Erasure", signer: signerV4}, 132 // Init and run test on ErasureSet backend. 133 {serverType: "ErasureSet", signer: signerV4}, 134 } 135 GlobalCLIContext.StrictS3Compat = true 136 for i, testCase := range testCases { 137 t.Run(fmt.Sprintf("Test: %d, ServerType: %s", i+1, testCase.serverType), func(t *testing.T) { 138 runAllTests(testCase, &check{t, testCase.serverType}) 139 }) 140 } 141 } 142 143 // Setting up the test suite. 144 // Starting the Test server with temporary FS backend. 145 func (s *TestSuiteCommon) SetUpSuite(c *check) { 146 if s.secure { 147 cert, key, err := generateTLSCertKey("127.0.0.1") 148 c.Assert(err, nil) 149 150 s.testServer = StartTestTLSServer(c, s.serverType, cert, key) 151 } else { 152 s.testServer = StartTestServer(c, s.serverType) 153 } 154 155 s.client = s.testServer.Server.Client() 156 s.endPoint = s.testServer.Server.URL 157 s.accessKey = s.testServer.AccessKey 158 s.secretKey = s.testServer.SecretKey 159 } 160 161 // Called implicitly by "gopkg.in/check.v1" after all tests are run. 162 func (s *TestSuiteCommon) TearDownSuite(c *check) { 163 s.testServer.Stop() 164 } 165 166 func (s *TestSuiteCommon) TestBucketSQSNotificationWebHook(c *check) { 167 // Sample bucket notification. 168 bucketNotificationBuf := `<NotificationConfiguration><QueueConfiguration><Event>s3:ObjectCreated:Put</Event><Filter><S3Key><FilterRule><Name>prefix</Name><Value>images/</Value></FilterRule></S3Key></Filter><Id>1</Id><Queue>arn:minio:sqs:us-east-1:444455556666:webhook</Queue></QueueConfiguration></NotificationConfiguration>` 169 // generate a random bucket Name. 170 bucketName := getRandomBucketName() 171 // HTTP request to create the bucket. 172 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 173 0, nil, s.accessKey, s.secretKey, s.signer) 174 c.Assert(err, nil) 175 176 // execute the request. 177 response, err := s.client.Do(request) 178 c.Assert(err, nil) 179 180 // assert the http response status code. 181 c.Assert(response.StatusCode, http.StatusOK) 182 183 request, err = newTestSignedRequest(http.MethodPut, getPutNotificationURL(s.endPoint, bucketName), 184 int64(len(bucketNotificationBuf)), bytes.NewReader([]byte(bucketNotificationBuf)), s.accessKey, s.secretKey, s.signer) 185 c.Assert(err, nil) 186 187 // execute the HTTP request. 188 response, err = s.client.Do(request) 189 190 c.Assert(err, nil) 191 verifyError(c, response, "InvalidArgument", "A specified destination ARN does not exist or is not well-formed. Verify the destination ARN.", http.StatusBadRequest) 192 } 193 194 func (s *TestSuiteCommon) TestCors(c *check) { 195 expectedMap := http.Header{} 196 expectedMap.Set("Access-Control-Allow-Credentials", "true") 197 expectedMap.Set("Access-Control-Allow-Origin", "http://foobar.com") 198 expectedMap["Access-Control-Expose-Headers"] = []string{ 199 "Date", 200 "Etag", 201 "Server", 202 "Connection", 203 "Accept-Ranges", 204 "Content-Range", 205 "Content-Encoding", 206 "Content-Length", 207 "Content-Type", 208 "Content-Disposition", 209 "Last-Modified", 210 "Content-Language", 211 "Cache-Control", 212 "Retry-After", 213 "X-Amz-Bucket-Region", 214 "Expires", 215 "X-Amz*", 216 "X-Amz*", 217 "*", 218 } 219 expectedMap.Set("Vary", "Origin") 220 221 req, _ := http.NewRequest(http.MethodOptions, s.endPoint, nil) 222 req.Header.Set("Origin", "http://foobar.com") 223 res, err := s.client.Do(req) 224 if err != nil { 225 c.Fatal(err) 226 } 227 228 for k := range expectedMap { 229 if v, ok := res.Header[k]; !ok { 230 c.Errorf("Expected key %s missing from %v", k, res.Header) 231 } else { 232 expectedSet := set.CreateStringSet(expectedMap[k]...) 233 gotSet := set.CreateStringSet(strings.Split(v[0], ", ")...) 234 if !expectedSet.Equals(gotSet) { 235 c.Errorf("Expected value %v, got %v", strings.Join(expectedMap[k], ", "), v) 236 } 237 } 238 } 239 240 } 241 242 func (s *TestSuiteCommon) TestObjectDir(c *check) { 243 bucketName := getRandomBucketName() 244 // HTTP request to create the bucket. 245 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 246 0, nil, s.accessKey, s.secretKey, s.signer) 247 c.Assert(err, nil) 248 249 // execute the request. 250 response, err := s.client.Do(request) 251 c.Assert(err, nil) 252 253 // assert the http response status code. 254 c.Assert(response.StatusCode, http.StatusOK) 255 256 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, "my-object-directory/"), 257 0, nil, s.accessKey, s.secretKey, s.signer) 258 c.Assert(err, nil) 259 260 // execute the HTTP request. 261 response, err = s.client.Do(request) 262 263 c.Assert(err, nil) 264 // assert the http response status code. 265 c.Assert(response.StatusCode, http.StatusOK) 266 267 request, err = newTestSignedRequest(http.MethodHead, getHeadObjectURL(s.endPoint, bucketName, "my-object-directory/"), 268 0, nil, s.accessKey, s.secretKey, s.signer) 269 c.Assert(err, nil) 270 271 // execute the HTTP request. 272 response, err = s.client.Do(request) 273 274 c.Assert(err, nil) 275 c.Assert(response.StatusCode, http.StatusOK) 276 277 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, "my-object-directory/"), 278 0, nil, s.accessKey, s.secretKey, s.signer) 279 c.Assert(err, nil) 280 281 // execute the HTTP request. 282 response, err = s.client.Do(request) 283 284 c.Assert(err, nil) 285 c.Assert(response.StatusCode, http.StatusOK) 286 287 request, err = newTestSignedRequest(http.MethodDelete, getDeleteObjectURL(s.endPoint, bucketName, "my-object-directory/"), 288 0, nil, s.accessKey, s.secretKey, s.signer) 289 c.Assert(err, nil) 290 291 // execute the HTTP request. 292 response, err = s.client.Do(request) 293 294 c.Assert(err, nil) 295 c.Assert(response.StatusCode, http.StatusNoContent) 296 } 297 298 func (s *TestSuiteCommon) TestBucketSQSNotificationAMQP(c *check) { 299 // Sample bucket notification. 300 bucketNotificationBuf := `<NotificationConfiguration><QueueConfiguration><Event>s3:ObjectCreated:Put</Event><Filter><S3Key><FilterRule><Name>prefix</Name><Value>images/</Value></FilterRule></S3Key></Filter><Id>1</Id><Queue>arn:minio:sqs:us-east-1:444455556666:amqp</Queue></QueueConfiguration></NotificationConfiguration>` 301 // generate a random bucket Name. 302 bucketName := getRandomBucketName() 303 // HTTP request to create the bucket. 304 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 305 0, nil, s.accessKey, s.secretKey, s.signer) 306 c.Assert(err, nil) 307 308 // execute the request. 309 response, err := s.client.Do(request) 310 c.Assert(err, nil) 311 312 // assert the http response status code. 313 c.Assert(response.StatusCode, http.StatusOK) 314 315 request, err = newTestSignedRequest(http.MethodPut, getPutNotificationURL(s.endPoint, bucketName), 316 int64(len(bucketNotificationBuf)), bytes.NewReader([]byte(bucketNotificationBuf)), s.accessKey, s.secretKey, s.signer) 317 c.Assert(err, nil) 318 319 // execute the HTTP request. 320 response, err = s.client.Do(request) 321 322 c.Assert(err, nil) 323 verifyError(c, response, "InvalidArgument", "A specified destination ARN does not exist or is not well-formed. Verify the destination ARN.", http.StatusBadRequest) 324 } 325 326 // TestBucketPolicy - Inserts the bucket policy and verifies it by fetching the policy back. 327 // Deletes the policy and verifies the deletion by fetching it back. 328 func (s *TestSuiteCommon) TestBucketPolicy(c *check) { 329 // Sample bucket policy. 330 bucketPolicyBuf := `{"Version":"2012-10-17","Statement":[{"Action":["s3:GetBucketLocation","s3:ListBucket"],"Effect":"Allow","Principal":{"AWS":["*"]},"Resource":["arn:aws:s3:::%s"]},{"Action":["s3:GetObject"],"Effect":"Allow","Principal":{"AWS":["*"]},"Resource":["arn:aws:s3:::%s/this*"]}]}` 331 332 // generate a random bucket Name. 333 bucketName := getRandomBucketName() 334 // create the policy statement string with the randomly generated bucket name. 335 bucketPolicyStr := fmt.Sprintf(bucketPolicyBuf, bucketName, bucketName) 336 // HTTP request to create the bucket. 337 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 338 0, nil, s.accessKey, s.secretKey, s.signer) 339 c.Assert(err, nil) 340 341 // execute the request. 342 response, err := s.client.Do(request) 343 c.Assert(err, nil) 344 // assert the http response status code. 345 c.Assert(response.StatusCode, http.StatusOK) 346 347 /// Put a new bucket policy. 348 request, err = newTestSignedRequest(http.MethodPut, getPutPolicyURL(s.endPoint, bucketName), 349 int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), s.accessKey, s.secretKey, s.signer) 350 c.Assert(err, nil) 351 352 // execute the HTTP request to create bucket. 353 response, err = s.client.Do(request) 354 c.Assert(err, nil) 355 c.Assert(response.StatusCode, http.StatusNoContent) 356 357 // Fetch the uploaded policy. 358 request, err = newTestSignedRequest(http.MethodGet, getGetPolicyURL(s.endPoint, bucketName), 0, nil, 359 s.accessKey, s.secretKey, s.signer) 360 c.Assert(err, nil) 361 362 response, err = s.client.Do(request) 363 c.Assert(err, nil) 364 c.Assert(response.StatusCode, http.StatusOK) 365 366 bucketPolicyReadBuf, err := ioutil.ReadAll(response.Body) 367 c.Assert(err, nil) 368 // Verify if downloaded policy matches with previously uploaded. 369 expectedPolicy, err := policy.ParseConfig(strings.NewReader(bucketPolicyStr), bucketName) 370 c.Assert(err, nil) 371 gotPolicy, err := policy.ParseConfig(bytes.NewReader(bucketPolicyReadBuf), bucketName) 372 c.Assert(err, nil) 373 c.Assert(reflect.DeepEqual(expectedPolicy, gotPolicy), true) 374 375 // Delete policy. 376 request, err = newTestSignedRequest(http.MethodDelete, getDeletePolicyURL(s.endPoint, bucketName), 0, nil, 377 s.accessKey, s.secretKey, s.signer) 378 c.Assert(err, nil) 379 380 response, err = s.client.Do(request) 381 c.Assert(err, nil) 382 c.Assert(response.StatusCode, http.StatusNoContent) 383 384 // Verify if the policy was indeed deleted. 385 request, err = newTestSignedRequest(http.MethodGet, getGetPolicyURL(s.endPoint, bucketName), 386 0, nil, s.accessKey, s.secretKey, s.signer) 387 c.Assert(err, nil) 388 389 response, err = s.client.Do(request) 390 c.Assert(err, nil) 391 c.Assert(response.StatusCode, http.StatusNotFound) 392 } 393 394 // TestDeleteBucket - validates DELETE bucket operation. 395 func (s *TestSuiteCommon) TestDeleteBucket(c *check) { 396 bucketName := getRandomBucketName() 397 398 // HTTP request to create the bucket. 399 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 400 0, nil, s.accessKey, s.secretKey, s.signer) 401 c.Assert(err, nil) 402 403 response, err := s.client.Do(request) 404 c.Assert(err, nil) 405 // assert the response status code. 406 c.Assert(response.StatusCode, http.StatusOK) 407 408 // construct request to delete the bucket. 409 request, err = newTestSignedRequest(http.MethodDelete, getDeleteBucketURL(s.endPoint, bucketName), 410 0, nil, s.accessKey, s.secretKey, s.signer) 411 c.Assert(err, nil) 412 413 response, err = s.client.Do(request) 414 c.Assert(err, nil) 415 // Assert the response status code. 416 c.Assert(response.StatusCode, http.StatusNoContent) 417 } 418 419 // TestDeleteBucketNotEmpty - Validates the operation during an attempt to delete a non-empty bucket. 420 func (s *TestSuiteCommon) TestDeleteBucketNotEmpty(c *check) { 421 // generate a random bucket name. 422 bucketName := getRandomBucketName() 423 424 // HTTP request to create the bucket. 425 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 426 0, nil, s.accessKey, s.secretKey, s.signer) 427 c.Assert(err, nil) 428 429 // execute the request. 430 response, err := s.client.Do(request) 431 c.Assert(err, nil) 432 // assert the response status code. 433 c.Assert(response.StatusCode, http.StatusOK) 434 435 // generate http request for an object upload. 436 // "test-object" is the object name. 437 objectName := "test-object" 438 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 439 0, nil, s.accessKey, s.secretKey, s.signer) 440 c.Assert(err, nil) 441 442 // execute the request to complete object upload. 443 response, err = s.client.Do(request) 444 c.Assert(err, nil) 445 // assert the status code of the response. 446 c.Assert(response.StatusCode, http.StatusOK) 447 448 // constructing http request to delete the bucket. 449 // making an attempt to delete an non-empty bucket. 450 // expected to fail. 451 request, err = newTestSignedRequest(http.MethodDelete, getDeleteBucketURL(s.endPoint, bucketName), 452 0, nil, s.accessKey, s.secretKey, s.signer) 453 c.Assert(err, nil) 454 455 response, err = s.client.Do(request) 456 c.Assert(err, nil) 457 c.Assert(response.StatusCode, http.StatusConflict) 458 459 } 460 461 func (s *TestSuiteCommon) TestListenNotificationHandler(c *check) { 462 // generate a random bucket name. 463 bucketName := getRandomBucketName() 464 // HTTP request to create the bucket. 465 req, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 466 0, nil, s.accessKey, s.secretKey, s.signer) 467 c.Assert(err, nil) 468 469 // execute the request. 470 response, err := s.client.Do(req) 471 c.Assert(err, nil) 472 // assert the http response status code. 473 c.Assert(response.StatusCode, http.StatusOK) 474 475 invalidBucket := "Invalid\\Bucket" 476 tooByte := bytes.Repeat([]byte("a"), 1025) 477 tooBigPrefix := string(tooByte) 478 validEvents := []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:*"} 479 invalidEvents := []string{"invalidEvent"} 480 481 req, err = newTestSignedRequest(http.MethodGet, 482 getListenNotificationURL(s.endPoint, invalidBucket, []string{}, []string{}, []string{}), 483 0, nil, s.accessKey, s.secretKey, s.signer) 484 c.Assert(err, nil) 485 486 // execute the request. 487 response, err = s.client.Do(req) 488 c.Assert(err, nil) 489 verifyError(c, response, "InvalidBucketName", "The specified bucket is not valid.", http.StatusBadRequest) 490 491 req, err = newTestSignedRequest(http.MethodGet, 492 getListenNotificationURL(s.endPoint, bucketName, []string{}, []string{}, invalidEvents), 493 0, nil, s.accessKey, s.secretKey, s.signer) 494 c.Assert(err, nil) 495 496 // execute the request. 497 response, err = s.client.Do(req) 498 c.Assert(err, nil) 499 verifyError(c, response, "InvalidArgument", "A specified event is not supported for notifications.", http.StatusBadRequest) 500 501 req, err = newTestSignedRequest(http.MethodGet, 502 getListenNotificationURL(s.endPoint, bucketName, []string{tooBigPrefix}, []string{}, validEvents), 503 0, nil, s.accessKey, s.secretKey, s.signer) 504 c.Assert(err, nil) 505 506 // execute the request. 507 response, err = s.client.Do(req) 508 c.Assert(err, nil) 509 verifyError(c, response, "InvalidArgument", "Size of filter rule value cannot exceed 1024 bytes in UTF-8 representation", http.StatusBadRequest) 510 511 req, err = newTestSignedBadSHARequest(http.MethodGet, 512 getListenNotificationURL(s.endPoint, bucketName, []string{}, []string{}, validEvents), 513 0, nil, s.accessKey, s.secretKey, s.signer) 514 c.Assert(err, nil) 515 516 // execute the request. 517 response, err = s.client.Do(req) 518 c.Assert(err, nil) 519 if s.signer == signerV4 { 520 verifyError(c, response, "XAmzContentSHA256Mismatch", "The provided 'x-amz-content-sha256' header does not match what was computed.", http.StatusBadRequest) 521 } 522 } 523 524 // Test deletes multiple objects and verifies server response. 525 func (s *TestSuiteCommon) TestDeleteMultipleObjects(c *check) { 526 // generate a random bucket name. 527 bucketName := getRandomBucketName() 528 // HTTP request to create the bucket. 529 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 530 0, nil, s.accessKey, s.secretKey, s.signer) 531 c.Assert(err, nil) 532 533 // execute the request. 534 response, err := s.client.Do(request) 535 c.Assert(err, nil) 536 // assert the http response status code. 537 c.Assert(response.StatusCode, http.StatusOK) 538 539 objectName := "prefix/myobject" 540 delObjReq := DeleteObjectsRequest{ 541 Quiet: false, 542 } 543 for i := 0; i < 10; i++ { 544 // Obtain http request to upload object. 545 // object Name contains a prefix. 546 objName := fmt.Sprintf("%d/%s", i, objectName) 547 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objName), 548 0, nil, s.accessKey, s.secretKey, s.signer) 549 c.Assert(err, nil) 550 551 // execute the http request. 552 response, err = s.client.Do(request) 553 c.Assert(err, nil) 554 // assert the status of http response. 555 c.Assert(response.StatusCode, http.StatusOK) 556 // Append all objects. 557 delObjReq.Objects = append(delObjReq.Objects, ObjectToDelete{ 558 ObjectName: objName, 559 }) 560 } 561 // Marshal delete request. 562 deleteReqBytes, err := xml.Marshal(delObjReq) 563 c.Assert(err, nil) 564 565 // Delete list of objects. 566 request, err = newTestSignedRequest(http.MethodPost, getMultiDeleteObjectURL(s.endPoint, bucketName), 567 int64(len(deleteReqBytes)), bytes.NewReader(deleteReqBytes), s.accessKey, s.secretKey, s.signer) 568 c.Assert(err, nil) 569 response, err = s.client.Do(request) 570 c.Assert(err, nil) 571 c.Assert(response.StatusCode, http.StatusOK) 572 573 var deleteResp = DeleteObjectsResponse{} 574 delRespBytes, err := ioutil.ReadAll(response.Body) 575 c.Assert(err, nil) 576 err = xml.Unmarshal(delRespBytes, &deleteResp) 577 c.Assert(err, nil) 578 for i := 0; i < 10; i++ { 579 // All the objects should be under deleted list (including non-existent object) 580 c.Assert(deleteResp.DeletedObjects[i], DeletedObject{ 581 ObjectName: delObjReq.Objects[i].ObjectName, 582 VersionID: delObjReq.Objects[i].VersionID, 583 }) 584 } 585 c.Assert(len(deleteResp.Errors), 0) 586 587 // Attempt second time results should be same, NoSuchKey for objects not found 588 // shouldn't be set. 589 request, err = newTestSignedRequest(http.MethodPost, getMultiDeleteObjectURL(s.endPoint, bucketName), 590 int64(len(deleteReqBytes)), bytes.NewReader(deleteReqBytes), s.accessKey, s.secretKey, s.signer) 591 c.Assert(err, nil) 592 response, err = s.client.Do(request) 593 c.Assert(err, nil) 594 c.Assert(response.StatusCode, http.StatusOK) 595 596 deleteResp = DeleteObjectsResponse{} 597 delRespBytes, err = ioutil.ReadAll(response.Body) 598 c.Assert(err, nil) 599 err = xml.Unmarshal(delRespBytes, &deleteResp) 600 c.Assert(err, nil) 601 c.Assert(len(deleteResp.DeletedObjects), len(delObjReq.Objects)) 602 for i := 0; i < 10; i++ { 603 c.Assert(deleteResp.DeletedObjects[i], DeletedObject{ 604 ObjectName: delObjReq.Objects[i].ObjectName, 605 VersionID: delObjReq.Objects[i].VersionID, 606 }) 607 } 608 c.Assert(len(deleteResp.Errors), 0) 609 } 610 611 // Tests delete object responses and success. 612 func (s *TestSuiteCommon) TestDeleteObject(c *check) { 613 // generate a random bucket name. 614 bucketName := getRandomBucketName() 615 // HTTP request to create the bucket. 616 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 617 0, nil, s.accessKey, s.secretKey, s.signer) 618 c.Assert(err, nil) 619 620 // execute the request. 621 response, err := s.client.Do(request) 622 c.Assert(err, nil) 623 // assert the http response status code. 624 c.Assert(response.StatusCode, http.StatusOK) 625 626 objectName := "prefix/myobject" 627 // obtain http request to upload object. 628 // object Name contains a prefix. 629 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 630 0, nil, s.accessKey, s.secretKey, s.signer) 631 c.Assert(err, nil) 632 633 // execute the http request. 634 response, err = s.client.Do(request) 635 c.Assert(err, nil) 636 // assert the status of http response. 637 c.Assert(response.StatusCode, http.StatusOK) 638 639 // object name was "prefix/myobject", an attempt to delete "prefix" 640 // Should not delete "prefix/myobject" 641 request, err = newTestSignedRequest(http.MethodDelete, getDeleteObjectURL(s.endPoint, bucketName, "prefix"), 642 0, nil, s.accessKey, s.secretKey, s.signer) 643 c.Assert(err, nil) 644 response, err = s.client.Do(request) 645 c.Assert(err, nil) 646 c.Assert(response.StatusCode, http.StatusNoContent) 647 648 // create http request to HEAD on the object. 649 // this helps to validate the existence of the bucket. 650 request, err = newTestSignedRequest(http.MethodHead, getHeadObjectURL(s.endPoint, bucketName, objectName), 651 0, nil, s.accessKey, s.secretKey, s.signer) 652 c.Assert(err, nil) 653 654 response, err = s.client.Do(request) 655 c.Assert(err, nil) 656 // Assert the HTTP response status code. 657 c.Assert(response.StatusCode, http.StatusOK) 658 659 // create HTTP request to delete the object. 660 request, err = newTestSignedRequest(http.MethodDelete, getDeleteObjectURL(s.endPoint, bucketName, objectName), 661 0, nil, s.accessKey, s.secretKey, s.signer) 662 c.Assert(err, nil) 663 // execute the http request. 664 response, err = s.client.Do(request) 665 c.Assert(err, nil) 666 // assert the http response status code. 667 c.Assert(response.StatusCode, http.StatusNoContent) 668 669 // Delete of non-existent data should return success. 670 request, err = newTestSignedRequest(http.MethodDelete, getDeleteObjectURL(s.endPoint, bucketName, "prefix/myobject1"), 671 0, nil, s.accessKey, s.secretKey, s.signer) 672 c.Assert(err, nil) 673 // execute the http request. 674 response, err = s.client.Do(request) 675 c.Assert(err, nil) 676 // assert the http response status. 677 c.Assert(response.StatusCode, http.StatusNoContent) 678 } 679 680 // TestNonExistentBucket - Asserts response for HEAD on non-existent bucket. 681 func (s *TestSuiteCommon) TestNonExistentBucket(c *check) { 682 // generate a random bucket name. 683 bucketName := getRandomBucketName() 684 // create request to HEAD on the bucket. 685 // HEAD on an bucket helps validate the existence of the bucket. 686 request, err := newTestSignedRequest(http.MethodHead, getHEADBucketURL(s.endPoint, bucketName), 687 0, nil, s.accessKey, s.secretKey, s.signer) 688 c.Assert(err, nil) 689 690 // execute the http request. 691 response, err := s.client.Do(request) 692 c.Assert(err, nil) 693 // Assert the response. 694 c.Assert(response.StatusCode, http.StatusNotFound) 695 } 696 697 // TestEmptyObject - Asserts the response for operation on a 0 byte object. 698 func (s *TestSuiteCommon) TestEmptyObject(c *check) { 699 // generate a random bucket name. 700 bucketName := getRandomBucketName() 701 // HTTP request to create the bucket. 702 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 703 0, nil, s.accessKey, s.secretKey, s.signer) 704 c.Assert(err, nil) 705 706 // execute the http request. 707 response, err := s.client.Do(request) 708 c.Assert(err, nil) 709 // assert the http response status code. 710 c.Assert(response.StatusCode, http.StatusOK) 711 712 objectName := "test-object" 713 // construct http request for uploading the object. 714 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 715 0, nil, s.accessKey, s.secretKey, s.signer) 716 c.Assert(err, nil) 717 718 // execute the upload request. 719 response, err = s.client.Do(request) 720 c.Assert(err, nil) 721 // assert the http response. 722 c.Assert(response.StatusCode, http.StatusOK) 723 724 // make HTTP request to fetch the object. 725 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 726 0, nil, s.accessKey, s.secretKey, s.signer) 727 c.Assert(err, nil) 728 729 // execute the http request to fetch object. 730 response, err = s.client.Do(request) 731 c.Assert(err, nil) 732 // assert the http response status code. 733 c.Assert(response.StatusCode, http.StatusOK) 734 735 var buffer bytes.Buffer 736 // extract the body of the response. 737 responseBody, err := ioutil.ReadAll(response.Body) 738 c.Assert(err, nil) 739 // assert the http response body content. 740 c.Assert(true, bytes.Equal(responseBody, buffer.Bytes())) 741 } 742 743 func (s *TestSuiteCommon) TestBucket(c *check) { 744 // generate a random bucket name. 745 bucketName := getRandomBucketName() 746 747 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 748 0, nil, s.accessKey, s.secretKey, s.signer) 749 c.Assert(err, nil) 750 751 response, err := s.client.Do(request) 752 c.Assert(err, nil) 753 c.Assert(response.StatusCode, http.StatusOK) 754 755 request, err = newTestSignedRequest(http.MethodHead, getMakeBucketURL(s.endPoint, bucketName), 756 0, nil, s.accessKey, s.secretKey, s.signer) 757 c.Assert(err, nil) 758 759 response, err = s.client.Do(request) 760 c.Assert(err, nil) 761 c.Assert(response.StatusCode, http.StatusOK) 762 } 763 764 // Tests get anonymous object. 765 func (s *TestSuiteCommon) TestObjectGetAnonymous(c *check) { 766 // generate a random bucket name. 767 bucketName := getRandomBucketName() 768 buffer := bytes.NewReader([]byte("hello world")) 769 // HTTP request to create the bucket. 770 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 771 0, nil, s.accessKey, s.secretKey, s.signer) 772 c.Assert(err, nil) 773 774 // execute the make bucket http request. 775 response, err := s.client.Do(request) 776 c.Assert(err, nil) 777 // assert the response http status code. 778 c.Assert(response.StatusCode, http.StatusOK) 779 780 objectName := "testObject" 781 // create HTTP request to upload the object. 782 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 783 int64(buffer.Len()), buffer, s.accessKey, s.secretKey, s.signer) 784 c.Assert(err, nil) 785 786 // execute the HTTP request to upload the object. 787 response, err = s.client.Do(request) 788 c.Assert(err, nil) 789 // assert the HTTP response status code. 790 c.Assert(response.StatusCode, http.StatusOK) 791 792 // initiate anonymous HTTP request to fetch the object which does not exist. We need to return AccessDenied. 793 response, err = s.client.Get(getGetObjectURL(s.endPoint, bucketName, objectName+".1")) 794 c.Assert(err, nil) 795 // assert the http response status code. 796 verifyError(c, response, "AccessDenied", "Access Denied.", http.StatusForbidden) 797 798 // initiate anonymous HTTP request to fetch the object which does exist. We need to return AccessDenied. 799 response, err = s.client.Get(getGetObjectURL(s.endPoint, bucketName, objectName)) 800 c.Assert(err, nil) 801 // assert the http response status code. 802 verifyError(c, response, "AccessDenied", "Access Denied.", http.StatusForbidden) 803 } 804 805 // TestMultipleObjects - Validates upload and fetching of multiple object into the bucket. 806 func (s *TestSuiteCommon) TestMultipleObjects(c *check) { 807 // generate a random bucket name. 808 bucketName := getRandomBucketName() 809 // HTTP request to create the bucket. 810 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 811 0, nil, s.accessKey, s.secretKey, s.signer) 812 c.Assert(err, nil) 813 814 // execute the HTTP request to create the bucket. 815 response, err := s.client.Do(request) 816 c.Assert(err, nil) 817 c.Assert(response.StatusCode, http.StatusOK) 818 819 // constructing HTTP request to fetch a non-existent object. 820 // expected to fail, error response asserted for expected error values later. 821 objectName := "testObject" 822 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 823 0, nil, s.accessKey, s.secretKey, s.signer) 824 c.Assert(err, nil) 825 826 // execute the HTTP request. 827 response, err = s.client.Do(request) 828 c.Assert(err, nil) 829 // Asserting the error response with the expected values. 830 verifyError(c, response, "NoSuchKey", "The specified key does not exist.", http.StatusNotFound) 831 832 objectName = "testObject1" 833 // content for the object to be uploaded. 834 buffer1 := bytes.NewReader([]byte("hello one")) 835 // create HTTP request for the object upload. 836 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 837 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 838 c.Assert(err, nil) 839 840 // execute the HTTP request for object upload. 841 response, err = s.client.Do(request) 842 c.Assert(err, nil) 843 // assert the returned values. 844 c.Assert(response.StatusCode, http.StatusOK) 845 846 // create HTTP request to fetch the object which was uploaded above. 847 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 848 0, nil, s.accessKey, s.secretKey, s.signer) 849 c.Assert(err, nil) 850 851 // execute the HTTP request. 852 response, err = s.client.Do(request) 853 c.Assert(err, nil) 854 // assert whether 200 OK response status is obtained. 855 c.Assert(response.StatusCode, http.StatusOK) 856 857 // extract the response body. 858 responseBody, err := ioutil.ReadAll(response.Body) 859 c.Assert(err, nil) 860 // assert the content body for the expected object data. 861 c.Assert(true, bytes.Equal(responseBody, []byte("hello one"))) 862 863 // data for new object to be uploaded. 864 buffer2 := bytes.NewReader([]byte("hello two")) 865 objectName = "testObject2" 866 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 867 int64(buffer2.Len()), buffer2, s.accessKey, s.secretKey, s.signer) 868 c.Assert(err, nil) 869 870 // execute the HTTP request for object upload. 871 response, err = s.client.Do(request) 872 c.Assert(err, nil) 873 // assert the response status code for expected value 200 OK. 874 c.Assert(response.StatusCode, http.StatusOK) 875 // fetch the object which was uploaded above. 876 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 877 0, nil, s.accessKey, s.secretKey, s.signer) 878 c.Assert(err, nil) 879 880 // execute the HTTP request to fetch the object. 881 response, err = s.client.Do(request) 882 c.Assert(err, nil) 883 // assert the response status code for expected value 200 OK. 884 c.Assert(response.StatusCode, http.StatusOK) 885 886 // verify response data 887 responseBody, err = ioutil.ReadAll(response.Body) 888 c.Assert(err, nil) 889 c.Assert(true, bytes.Equal(responseBody, []byte("hello two"))) 890 891 // data for new object to be uploaded. 892 buffer3 := bytes.NewReader([]byte("hello three")) 893 objectName = "testObject3" 894 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 895 int64(buffer3.Len()), buffer3, s.accessKey, s.secretKey, s.signer) 896 c.Assert(err, nil) 897 898 // execute HTTP request. 899 response, err = s.client.Do(request) 900 c.Assert(err, nil) 901 // verify the response code with the expected value of 200 OK. 902 c.Assert(response.StatusCode, http.StatusOK) 903 904 // fetch the object which was uploaded above. 905 request, err = newTestSignedRequest(http.MethodGet, getPutObjectURL(s.endPoint, bucketName, objectName), 906 0, nil, s.accessKey, s.secretKey, s.signer) 907 c.Assert(err, nil) 908 909 response, err = s.client.Do(request) 910 c.Assert(err, nil) 911 c.Assert(response.StatusCode, http.StatusOK) 912 913 // verify object. 914 responseBody, err = ioutil.ReadAll(response.Body) 915 c.Assert(err, nil) 916 c.Assert(true, bytes.Equal(responseBody, []byte("hello three"))) 917 } 918 919 // TestHeader - Validates the error response for an attempt to fetch non-existent object. 920 func (s *TestSuiteCommon) TestHeader(c *check) { 921 // generate a random bucket name. 922 bucketName := getRandomBucketName() 923 // obtain HTTP request to fetch an object from non-existent bucket/object. 924 request, err := newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, "testObject"), 925 0, nil, s.accessKey, s.secretKey, s.signer) 926 c.Assert(err, nil) 927 928 response, err := s.client.Do(request) 929 c.Assert(err, nil) 930 // asserting for the expected error response. 931 verifyError(c, response, "NoSuchBucket", "The specified bucket does not exist", http.StatusNotFound) 932 } 933 934 func (s *TestSuiteCommon) TestPutBucket(c *check) { 935 // generate a random bucket name. 936 bucketName := getRandomBucketName() 937 // Block 1: Testing for racy access 938 // The assertion is removed from this block since the purpose of this block is to find races 939 // The purpose this block is not to check for correctness of functionality 940 // Run the test with -race flag to utilize this 941 var wg sync.WaitGroup 942 for i := 0; i < testConcurrencyLevel; i++ { 943 wg.Add(1) 944 go func() { 945 defer wg.Done() 946 // HTTP request to create the bucket. 947 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 948 0, nil, s.accessKey, s.secretKey, s.signer) 949 c.Assert(err, nil) 950 951 response, err := s.client.Do(request) 952 if err != nil { 953 c.Errorf("Put bucket Failed: <ERROR> %s", err) 954 return 955 } 956 defer response.Body.Close() 957 }() 958 } 959 wg.Wait() 960 961 bucketName = getRandomBucketName() 962 //Block 2: testing for correctness of the functionality 963 // HTTP request to create the bucket. 964 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 965 0, nil, s.accessKey, s.secretKey, s.signer) 966 c.Assert(err, nil) 967 968 response, err := s.client.Do(request) 969 c.Assert(err, nil) 970 c.Assert(response.StatusCode, http.StatusOK) 971 response.Body.Close() 972 } 973 974 // TestCopyObject - Validates copy object. 975 // The following is the test flow. 976 // 1. Create bucket. 977 // 2. Insert Object. 978 // 3. Use "X-Amz-Copy-Source" header to copy the previously created object. 979 // 4. Validate the content of copied object. 980 func (s *TestSuiteCommon) TestCopyObject(c *check) { 981 // generate a random bucket name. 982 bucketName := getRandomBucketName() 983 // HTTP request to create the bucket. 984 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 985 0, nil, s.accessKey, s.secretKey, s.signer) 986 c.Assert(err, nil) 987 988 // execute the HTTP request to create bucket. 989 response, err := s.client.Do(request) 990 c.Assert(err, nil) 991 c.Assert(response.StatusCode, http.StatusOK) 992 993 // content for the object to be created. 994 buffer1 := bytes.NewReader([]byte("hello world")) 995 objectName := "testObject" 996 // create HTTP request for object upload. 997 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 998 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 999 request.Header.Set("Content-Type", "application/json") 1000 if s.signer == signerV2 { 1001 c.Assert(err, nil) 1002 err = signRequestV2(request, s.accessKey, s.secretKey) 1003 } 1004 c.Assert(err, nil) 1005 // execute the HTTP request for object upload. 1006 response, err = s.client.Do(request) 1007 c.Assert(err, nil) 1008 c.Assert(response.StatusCode, http.StatusOK) 1009 1010 objectName2 := "testObject2" 1011 // Unlike the actual PUT object request, the request to Copy Object doesn't contain request body, 1012 // empty body with the "X-Amz-Copy-Source" header pointing to the object to copies it in the backend. 1013 request, err = newTestRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName2), 0, nil) 1014 c.Assert(err, nil) 1015 // setting the "X-Amz-Copy-Source" to allow copying the content of previously uploaded object. 1016 request.Header.Set("X-Amz-Copy-Source", url.QueryEscape(SlashSeparator+bucketName+SlashSeparator+objectName)) 1017 if s.signer == signerV4 { 1018 err = signRequestV4(request, s.accessKey, s.secretKey) 1019 } else { 1020 err = signRequestV2(request, s.accessKey, s.secretKey) 1021 } 1022 c.Assert(err, nil) 1023 // execute the HTTP request. 1024 // the content is expected to have the content of previous disk. 1025 response, err = s.client.Do(request) 1026 c.Assert(err, nil) 1027 c.Assert(response.StatusCode, http.StatusOK) 1028 1029 // creating HTTP request to fetch the previously uploaded object. 1030 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName2), 1031 0, nil, s.accessKey, s.secretKey, s.signer) 1032 c.Assert(err, nil) 1033 // executing the HTTP request. 1034 response, err = s.client.Do(request) 1035 c.Assert(err, nil) 1036 // validating the response status code. 1037 c.Assert(response.StatusCode, http.StatusOK) 1038 // reading the response body. 1039 // response body is expected to have the copied content of the first uploaded object. 1040 object, err := ioutil.ReadAll(response.Body) 1041 c.Assert(err, nil) 1042 c.Assert(string(object), "hello world") 1043 } 1044 1045 // TestPutObject - Tests successful put object request. 1046 func (s *TestSuiteCommon) TestPutObject(c *check) { 1047 // generate a random bucket name. 1048 bucketName := getRandomBucketName() 1049 // HTTP request to create the bucket. 1050 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1051 0, nil, s.accessKey, s.secretKey, s.signer) 1052 c.Assert(err, nil) 1053 1054 // execute the HTTP request to create bucket. 1055 response, err := s.client.Do(request) 1056 c.Assert(err, nil) 1057 c.Assert(response.StatusCode, http.StatusOK) 1058 1059 // content for new object upload. 1060 buffer1 := bytes.NewReader([]byte("hello world")) 1061 objectName := "testObject" 1062 // creating HTTP request for object upload. 1063 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 1064 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 1065 c.Assert(err, nil) 1066 // execute the HTTP request for object upload. 1067 response, err = s.client.Do(request) 1068 c.Assert(err, nil) 1069 c.Assert(response.StatusCode, http.StatusOK) 1070 1071 // fetch the object back and verify its contents. 1072 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 1073 0, nil, s.accessKey, s.secretKey, s.signer) 1074 c.Assert(err, nil) 1075 // execute the HTTP request to fetch the object. 1076 response, err = s.client.Do(request) 1077 c.Assert(err, nil) 1078 c.Assert(response.StatusCode, http.StatusOK) 1079 c.Assert(response.ContentLength, int64(len([]byte("hello world")))) 1080 var buffer2 bytes.Buffer 1081 // retrieve the contents of response body. 1082 n, err := io.Copy(&buffer2, response.Body) 1083 c.Assert(err, nil) 1084 c.Assert(n, int64(len([]byte("hello world")))) 1085 // asserted the contents of the fetched object with the expected result. 1086 c.Assert(true, bytes.Equal(buffer2.Bytes(), []byte("hello world"))) 1087 1088 // Test the response when object name ends with a slash. 1089 // This is a special case with size as '0' and object ends with 1090 // a slash separator, we treat it like a valid operation and 1091 // return success. 1092 // The response Etag headers should contain Md5Sum of empty string. 1093 objectName = "objectwith/" 1094 // create HTTP request for object upload. 1095 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 1096 0, nil, s.accessKey, s.secretKey, s.signer) 1097 if s.signer == signerV2 { 1098 c.Assert(err, nil) 1099 err = signRequestV2(request, s.accessKey, s.secretKey) 1100 } 1101 c.Assert(err, nil) 1102 // execute the HTTP request for object upload. 1103 response, err = s.client.Do(request) 1104 c.Assert(err, nil) 1105 c.Assert(response.StatusCode, http.StatusOK) 1106 // The response Etag header should contain Md5sum of an empty string. 1107 c.Assert(response.Header.Get(xhttp.ETag), "\""+emptyETag+"\"") 1108 } 1109 1110 // TestListBuckets - Make request for listing of all buckets. 1111 // XML response is parsed. 1112 // Its success verifies the format of the response. 1113 func (s *TestSuiteCommon) TestListBuckets(c *check) { 1114 // generate a random bucket name. 1115 bucketName := getRandomBucketName() 1116 // HTTP request to create the bucket. 1117 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1118 0, nil, s.accessKey, s.secretKey, s.signer) 1119 c.Assert(err, nil) 1120 // execute the HTTP request to list buckets. 1121 response, err := s.client.Do(request) 1122 c.Assert(err, nil) 1123 c.Assert(response.StatusCode, http.StatusOK) 1124 1125 // create HTTP request for listing buckets. 1126 request, err = newTestSignedRequest(http.MethodGet, getListBucketURL(s.endPoint), 1127 0, nil, s.accessKey, s.secretKey, s.signer) 1128 c.Assert(err, nil) 1129 1130 // execute the HTTP request to list buckets. 1131 response, err = s.client.Do(request) 1132 c.Assert(err, nil) 1133 c.Assert(response.StatusCode, http.StatusOK) 1134 1135 var results ListBucketsResponse 1136 // parse the list bucket response. 1137 decoder := xml.NewDecoder(response.Body) 1138 err = decoder.Decode(&results) 1139 // validating that the xml-decoding/parsing was successful. 1140 c.Assert(err, nil) 1141 1142 // Fetch the bucket created above 1143 var createdBucket Bucket 1144 for _, b := range results.Buckets.Buckets { 1145 if b.Name == bucketName { 1146 createdBucket = b 1147 } 1148 } 1149 c.Assert(createdBucket.Name != "", true) 1150 1151 // Parse the bucket modtime 1152 creationTime, err := time.Parse(iso8601TimeFormat, createdBucket.CreationDate) 1153 c.Assert(err, nil) 1154 1155 // Check if bucket modtime is consistent (not less than current time and not late more than 5 minutes) 1156 timeNow := time.Now().UTC() 1157 c.Assert(creationTime.Before(timeNow), true) 1158 c.Assert(timeNow.Sub(creationTime) < time.Minute*5, true) 1159 } 1160 1161 // This tests validate if PUT handler can successfully detect signature mismatch. 1162 func (s *TestSuiteCommon) TestValidateSignature(c *check) { 1163 // generate a random bucket name. 1164 bucketName := getRandomBucketName() 1165 // HTTP request to create the bucket. 1166 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1167 0, nil, s.accessKey, s.secretKey, s.signer) 1168 c.Assert(err, nil) 1169 1170 // Execute the HTTP request to create bucket. 1171 response, err := s.client.Do(request) 1172 c.Assert(err, nil) 1173 c.Assert(response.StatusCode, http.StatusOK) 1174 1175 objName := "test-object" 1176 1177 // Body is on purpose set to nil so that we get payload generated for empty bytes. 1178 1179 // Create new HTTP request with incorrect secretKey to generate an incorrect signature. 1180 secretKey := s.secretKey + "a" 1181 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objName), 0, nil, s.accessKey, secretKey, s.signer) 1182 c.Assert(err, nil) 1183 response, err = s.client.Do(request) 1184 c.Assert(err, nil) 1185 verifyError(c, response, "SignatureDoesNotMatch", "The request signature we calculated does not match the signature you provided. Check your key and signing method.", http.StatusForbidden) 1186 } 1187 1188 // This tests validate if PUT handler can successfully detect SHA256 mismatch. 1189 func (s *TestSuiteCommon) TestSHA256Mismatch(c *check) { 1190 // generate a random bucket name. 1191 bucketName := getRandomBucketName() 1192 // HTTP request to create the bucket. 1193 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1194 0, nil, s.accessKey, s.secretKey, s.signer) 1195 c.Assert(err, nil) 1196 1197 // Execute the HTTP request to create bucket. 1198 response, err := s.client.Do(request) 1199 c.Assert(err, nil) 1200 c.Assert(response.StatusCode, http.StatusOK) 1201 1202 objName := "test-object" 1203 1204 // Body is on purpose set to nil so that we get payload generated for empty bytes. 1205 1206 // Create new HTTP request with incorrect secretKey to generate an incorrect signature. 1207 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objName), 0, nil, s.accessKey, s.secretKey, s.signer) 1208 if s.signer == signerV4 { 1209 c.Assert(request.Header.Get("x-amz-content-sha256"), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") 1210 } 1211 // Set the body to generate signature mismatch. 1212 helloReader := bytes.NewReader([]byte("Hello, World")) 1213 request.ContentLength = helloReader.Size() 1214 request.Body = ioutil.NopCloser(helloReader) 1215 c.Assert(err, nil) 1216 1217 // execute the HTTP request. 1218 response, err = s.client.Do(request) 1219 c.Assert(err, nil) 1220 if s.signer == signerV4 { 1221 verifyError(c, response, "XAmzContentSHA256Mismatch", "The provided 'x-amz-content-sha256' header does not match what was computed.", http.StatusBadRequest) 1222 } 1223 } 1224 1225 // TestPutObjectLongName - Validates the error response 1226 // on an attempt to upload an object with long name. 1227 func (s *TestSuiteCommon) TestPutObjectLongName(c *check) { 1228 // generate a random bucket name. 1229 bucketName := getRandomBucketName() 1230 // HTTP request to create the bucket. 1231 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1232 0, nil, s.accessKey, s.secretKey, s.signer) 1233 c.Assert(err, nil) 1234 1235 // Execute the HTTP request to create bucket. 1236 response, err := s.client.Do(request) 1237 c.Assert(err, nil) 1238 c.Assert(response.StatusCode, http.StatusOK) 1239 // Content for the object to be uploaded. 1240 buffer := bytes.NewReader([]byte("hello world")) 1241 // make long object name. 1242 longObjName := fmt.Sprintf("%0255d/%0255d/%0255d", 1, 1, 1) 1243 if IsDocker() || IsKubernetes() { 1244 longObjName = fmt.Sprintf("%0242d/%0242d/%0242d", 1, 1, 1) 1245 } 1246 // create new HTTP request to insert the object. 1247 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, longObjName), 1248 int64(buffer.Len()), buffer, s.accessKey, s.secretKey, s.signer) 1249 c.Assert(err, nil) 1250 // execute the HTTP request. 1251 response, err = s.client.Do(request) 1252 c.Assert(err, nil) 1253 c.Assert(response.StatusCode, http.StatusOK) 1254 1255 //make long object name. 1256 longObjName = fmt.Sprintf("%0255d/%0255d/%0255d/%0255d/%0255d", 1, 1, 1, 1, 1) 1257 if IsDocker() || IsKubernetes() { 1258 longObjName = fmt.Sprintf("%0242d/%0242d/%0242d/%0242d/%0242d", 1, 1, 1, 1, 1) 1259 } 1260 // create new HTTP request to insert the object. 1261 buffer = bytes.NewReader([]byte("hello world")) 1262 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, longObjName), 1263 int64(buffer.Len()), buffer, s.accessKey, s.secretKey, s.signer) 1264 c.Assert(err, nil) 1265 // execute the HTTP request. 1266 response, err = s.client.Do(request) 1267 c.Assert(err, nil) 1268 c.Assert(response.StatusCode, http.StatusBadRequest) 1269 verifyError(c, response, "KeyTooLongError", "Your key is too long", http.StatusBadRequest) 1270 1271 // make object name as unsupported 1272 longObjName = fmt.Sprintf("%0256d", 1) 1273 buffer = bytes.NewReader([]byte("hello world")) 1274 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, longObjName), 1275 int64(buffer.Len()), buffer, s.accessKey, s.secretKey, s.signer) 1276 c.Assert(err, nil) 1277 1278 response, err = s.client.Do(request) 1279 c.Assert(err, nil) 1280 verifyError(c, response, "XMinioInvalidObjectName", "Object name contains unsupported characters.", http.StatusBadRequest) 1281 } 1282 1283 // TestNotBeAbleToCreateObjectInNonexistentBucket - Validates the error response 1284 // on an attempt to upload an object into a non-existent bucket. 1285 func (s *TestSuiteCommon) TestNotBeAbleToCreateObjectInNonexistentBucket(c *check) { 1286 // generate a random bucket name. 1287 bucketName := getRandomBucketName() 1288 // content of the object to be uploaded. 1289 buffer1 := bytes.NewReader([]byte("hello world")) 1290 1291 // preparing for upload by generating the upload URL. 1292 objectName := "test-object" 1293 request, err := newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 1294 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 1295 c.Assert(err, nil) 1296 1297 // Execute the HTTP request. 1298 response, err := s.client.Do(request) 1299 c.Assert(err, nil) 1300 // Assert the response error message. 1301 verifyError(c, response, "NoSuchBucket", "The specified bucket does not exist", http.StatusNotFound) 1302 } 1303 1304 // TestHeadOnObjectLastModified - Asserts response for HEAD on an object. 1305 // HEAD requests on an object validates the existence of the object. 1306 // The responses for fetching the object when If-Modified-Since 1307 // and If-Unmodified-Since headers set are validated. 1308 // If-Modified-Since - Return the object only if it has been modified since the specified time, else return a 304 (not modified). 1309 // If-Unmodified-Since - Return the object only if it has not been modified since the specified time, else return a 412 (precondition failed). 1310 func (s *TestSuiteCommon) TestHeadOnObjectLastModified(c *check) { 1311 // generate a random bucket name. 1312 bucketName := getRandomBucketName() 1313 // HTTP request to create the bucket. 1314 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1315 0, nil, s.accessKey, s.secretKey, s.signer) 1316 c.Assert(err, nil) 1317 1318 // execute the HTTP request to create bucket. 1319 response, err := s.client.Do(request) 1320 c.Assert(err, nil) 1321 c.Assert(response.StatusCode, http.StatusOK) 1322 1323 // preparing for object upload. 1324 objectName := "test-object" 1325 // content for the object to be uploaded. 1326 buffer1 := bytes.NewReader([]byte("hello world")) 1327 // obtaining URL for uploading the object. 1328 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 1329 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 1330 c.Assert(err, nil) 1331 1332 // executing the HTTP request to download the object. 1333 response, err = s.client.Do(request) 1334 c.Assert(err, nil) 1335 c.Assert(response.StatusCode, http.StatusOK) 1336 // make HTTP request to obtain object info. 1337 request, err = newTestSignedRequest(http.MethodHead, getHeadObjectURL(s.endPoint, bucketName, objectName), 1338 0, nil, s.accessKey, s.secretKey, s.signer) 1339 c.Assert(err, nil) 1340 // execute the HTTP request. 1341 response, err = s.client.Do(request) 1342 c.Assert(err, nil) 1343 // verify the status of the HTTP response. 1344 c.Assert(response.StatusCode, http.StatusOK) 1345 1346 // retrieve the info of last modification time of the object from the response header. 1347 lastModified := response.Header.Get("Last-Modified") 1348 // Parse it into time.Time structure. 1349 t, err := time.Parse(http.TimeFormat, lastModified) 1350 c.Assert(err, nil) 1351 1352 // make HTTP request to obtain object info. 1353 // But this time set the "If-Modified-Since" header to be 10 minute more than the actual 1354 // last modified time of the object. 1355 request, err = newTestSignedRequest(http.MethodHead, getHeadObjectURL(s.endPoint, bucketName, objectName), 1356 0, nil, s.accessKey, s.secretKey, s.signer) 1357 c.Assert(err, nil) 1358 request.Header.Set("If-Modified-Since", t.Add(10*time.Minute).UTC().Format(http.TimeFormat)) 1359 response, err = s.client.Do(request) 1360 c.Assert(err, nil) 1361 // Since the "If-Modified-Since" header was ahead in time compared to the actual 1362 // modified time of the object expecting the response status to be http.StatusNotModified. 1363 c.Assert(response.StatusCode, http.StatusNotModified) 1364 1365 // Again, obtain the object info. 1366 // This time setting "If-Unmodified-Since" to a time after the object is modified. 1367 // As documented above, expecting http.StatusPreconditionFailed. 1368 request, err = newTestSignedRequest(http.MethodHead, getHeadObjectURL(s.endPoint, bucketName, objectName), 1369 0, nil, s.accessKey, s.secretKey, s.signer) 1370 c.Assert(err, nil) 1371 request.Header.Set("If-Unmodified-Since", t.Add(-10*time.Minute).UTC().Format(http.TimeFormat)) 1372 response, err = s.client.Do(request) 1373 c.Assert(err, nil) 1374 c.Assert(response.StatusCode, http.StatusPreconditionFailed) 1375 1376 // make HTTP request to obtain object info. 1377 // But this time set a date with unrecognized format to the "If-Modified-Since" header 1378 request, err = newTestSignedRequest(http.MethodHead, getHeadObjectURL(s.endPoint, bucketName, objectName), 1379 0, nil, s.accessKey, s.secretKey, s.signer) 1380 c.Assert(err, nil) 1381 request.Header.Set("If-Unmodified-Since", "Mon, 02 Jan 2006 15:04:05 +00:00") 1382 response, err = s.client.Do(request) 1383 c.Assert(err, nil) 1384 // Since the "If-Modified-Since" header was ahead in time compared to the actual 1385 // modified time of the object expecting the response status to be http.StatusNotModified. 1386 c.Assert(response.StatusCode, http.StatusOK) 1387 1388 } 1389 1390 // TestHeadOnBucket - Validates response for HEAD on the bucket. 1391 // HEAD request on the bucket validates the existence of the bucket. 1392 func (s *TestSuiteCommon) TestHeadOnBucket(c *check) { 1393 // generate a random bucket name. 1394 bucketName := getRandomBucketName() 1395 // HTTP request to create the bucket. 1396 request, err := newTestSignedRequest(http.MethodPut, getHEADBucketURL(s.endPoint, bucketName), 1397 0, nil, s.accessKey, s.secretKey, s.signer) 1398 c.Assert(err, nil) 1399 1400 // execute the HTTP request to create bucket. 1401 response, err := s.client.Do(request) 1402 c.Assert(err, nil) 1403 c.Assert(response.StatusCode, http.StatusOK) 1404 // make HEAD request on the bucket. 1405 request, err = newTestSignedRequest(http.MethodHead, getHEADBucketURL(s.endPoint, bucketName), 1406 0, nil, s.accessKey, s.secretKey, s.signer) 1407 c.Assert(err, nil) 1408 // execute the HTTP request. 1409 response, err = s.client.Do(request) 1410 c.Assert(err, nil) 1411 // Asserting the response status for expected value of http.StatusOK. 1412 c.Assert(response.StatusCode, http.StatusOK) 1413 } 1414 1415 // TestContentTypePersists - Object upload with different Content-type is first done. 1416 // And then a HEAD and GET request on these objects are done to validate if the same Content-Type set during upload persists. 1417 func (s *TestSuiteCommon) TestContentTypePersists(c *check) { 1418 // generate a random bucket name. 1419 bucketName := getRandomBucketName() 1420 // HTTP request to create the bucket. 1421 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1422 0, nil, s.accessKey, s.secretKey, s.signer) 1423 c.Assert(err, nil) 1424 1425 // execute the HTTP request to create bucket. 1426 response, err := s.client.Do(request) 1427 c.Assert(err, nil) 1428 c.Assert(response.StatusCode, http.StatusOK) 1429 1430 // Uploading a new object with Content-Type "image/png". 1431 // content for the object to be uploaded. 1432 buffer1 := bytes.NewReader([]byte("hello world")) 1433 objectName := "test-object.png" 1434 // constructing HTTP request for object upload. 1435 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 1436 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 1437 c.Assert(err, nil) 1438 request.Header.Set("Content-Type", "image/png") 1439 if s.signer == signerV2 { 1440 err = signRequestV2(request, s.accessKey, s.secretKey) 1441 c.Assert(err, nil) 1442 } 1443 1444 // execute the HTTP request for object upload. 1445 response, err = s.client.Do(request) 1446 c.Assert(err, nil) 1447 c.Assert(response.StatusCode, http.StatusOK) 1448 1449 // Fetching the object info using HEAD request for the object which was uploaded above. 1450 request, err = newTestSignedRequest(http.MethodHead, getHeadObjectURL(s.endPoint, bucketName, objectName), 1451 0, nil, s.accessKey, s.secretKey, s.signer) 1452 c.Assert(err, nil) 1453 1454 // Execute the HTTP request. 1455 response, err = s.client.Do(request) 1456 c.Assert(err, nil) 1457 // Verify if the Content-Type header is set during the object persists. 1458 c.Assert(response.Header.Get("Content-Type"), "image/png") 1459 1460 // Fetching the object itself and then verify the Content-Type header. 1461 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 1462 0, nil, s.accessKey, s.secretKey, s.signer) 1463 c.Assert(err, nil) 1464 1465 // Execute the HTTP to fetch the object. 1466 response, err = s.client.Do(request) 1467 c.Assert(err, nil) 1468 c.Assert(response.StatusCode, http.StatusOK) 1469 // Verify if the Content-Type header is set during the object persists. 1470 c.Assert(response.Header.Get("Content-Type"), "image/png") 1471 1472 // Uploading a new object with Content-Type "application/json". 1473 objectName = "test-object.json" 1474 buffer2 := bytes.NewReader([]byte("hello world")) 1475 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 1476 int64(buffer2.Len()), buffer2, s.accessKey, s.secretKey, s.signer) 1477 c.Assert(err, nil) 1478 // setting the request header to be application/json. 1479 request.Header.Set("Content-Type", "application/json") 1480 if s.signer == signerV2 { 1481 err = signRequestV2(request, s.accessKey, s.secretKey) 1482 c.Assert(err, nil) 1483 } 1484 1485 // Execute the HTTP request to upload the object. 1486 response, err = s.client.Do(request) 1487 c.Assert(err, nil) 1488 c.Assert(response.StatusCode, http.StatusOK) 1489 1490 // Obtain the info of the object which was uploaded above using HEAD request. 1491 request, err = newTestSignedRequest(http.MethodHead, getHeadObjectURL(s.endPoint, bucketName, objectName), 1492 0, nil, s.accessKey, s.secretKey, s.signer) 1493 c.Assert(err, nil) 1494 // Execute the HTTP request. 1495 response, err = s.client.Do(request) 1496 c.Assert(err, nil) 1497 // Assert if the content-type header set during the object upload persists. 1498 c.Assert(response.Header.Get("Content-Type"), "application/json") 1499 1500 // Fetch the object and assert whether the Content-Type header persists. 1501 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 1502 0, nil, s.accessKey, s.secretKey, s.signer) 1503 c.Assert(err, nil) 1504 1505 // Execute the HTTP request. 1506 response, err = s.client.Do(request) 1507 c.Assert(err, nil) 1508 // Assert if the content-type header set during the object upload persists. 1509 c.Assert(response.Header.Get("Content-Type"), "application/json") 1510 } 1511 1512 // TestPartialContent - Validating for GetObject with partial content request. 1513 // By setting the Range header, A request to send specific bytes range of data from an 1514 // already uploaded object can be done. 1515 func (s *TestSuiteCommon) TestPartialContent(c *check) { 1516 bucketName := getRandomBucketName() 1517 1518 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1519 0, nil, s.accessKey, s.secretKey, s.signer) 1520 c.Assert(err, nil) 1521 1522 response, err := s.client.Do(request) 1523 c.Assert(err, nil) 1524 c.Assert(response.StatusCode, http.StatusOK) 1525 1526 buffer1 := bytes.NewReader([]byte("Hello World")) 1527 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, "bar"), 1528 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 1529 c.Assert(err, nil) 1530 1531 response, err = s.client.Do(request) 1532 c.Assert(err, nil) 1533 c.Assert(response.StatusCode, http.StatusOK) 1534 1535 // Prepare request 1536 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, "bar"), 1537 0, nil, s.accessKey, s.secretKey, s.signer) 1538 c.Assert(err, nil) 1539 request.Header.Set("Range", "bytes=6-7") 1540 1541 response, err = s.client.Do(request) 1542 c.Assert(err, nil) 1543 c.Assert(response.StatusCode, http.StatusPartialContent) 1544 partialObject, err := ioutil.ReadAll(response.Body) 1545 c.Assert(err, nil) 1546 1547 c.Assert(string(partialObject), "Wo") 1548 } 1549 1550 // TestListObjectsHandler - Setting valid parameters to List Objects 1551 // and then asserting the response with the expected one. 1552 func (s *TestSuiteCommon) TestListObjectsHandler(c *check) { 1553 // generate a random bucket name. 1554 bucketName := getRandomBucketName() 1555 // HTTP request to create the bucket. 1556 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1557 0, nil, s.accessKey, s.secretKey, s.signer) 1558 c.Assert(err, nil) 1559 1560 // execute the HTTP request to create bucket. 1561 response, err := s.client.Do(request) 1562 c.Assert(err, nil) 1563 c.Assert(response.StatusCode, http.StatusOK) 1564 1565 for _, objectName := range []string{"foo bar 1", "foo bar 2"} { 1566 buffer := bytes.NewReader([]byte("Hello World")) 1567 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 1568 int64(buffer.Len()), buffer, s.accessKey, s.secretKey, s.signer) 1569 c.Assert(err, nil) 1570 1571 response, err = s.client.Do(request) 1572 c.Assert(err, nil) 1573 c.Assert(response.StatusCode, http.StatusOK) 1574 } 1575 1576 var testCases = []struct { 1577 getURL string 1578 expectedStrings []string 1579 }{ 1580 {getListObjectsV1URL(s.endPoint, bucketName, "", "1000", ""), []string{"<Key>foo bar 1</Key>", "<Key>foo bar 2</Key>"}}, 1581 {getListObjectsV1URL(s.endPoint, bucketName, "", "1000", "url"), []string{"<Key>foo+bar+1</Key>", "<Key>foo+bar+2</Key>"}}, 1582 {getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", ""), 1583 []string{ 1584 "<Key>foo bar 1</Key>", 1585 "<Key>foo bar 2</Key>", 1586 fmt.Sprintf("<Owner><ID>%s</ID><DisplayName>minio</DisplayName></Owner>", GlobalMinioDefaultOwnerID), 1587 }, 1588 }, 1589 {getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "true", ""), 1590 []string{ 1591 "<Key>foo bar 1</Key>", 1592 "<Key>foo bar 2</Key>", 1593 fmt.Sprintf("<Owner><ID>%s</ID><DisplayName>minio</DisplayName></Owner>", GlobalMinioDefaultOwnerID), 1594 }, 1595 }, 1596 {getListObjectsV2URL(s.endPoint, bucketName, "", "1000", "", "url"), []string{"<Key>foo+bar+1</Key>", "<Key>foo+bar+2</Key>"}}, 1597 } 1598 1599 for _, testCase := range testCases { 1600 // create listObjectsV1 request with valid parameters 1601 request, err = newTestSignedRequest(http.MethodGet, testCase.getURL, 0, nil, s.accessKey, s.secretKey, s.signer) 1602 c.Assert(err, nil) 1603 // execute the HTTP request. 1604 response, err = s.client.Do(request) 1605 c.Assert(err, nil) 1606 c.Assert(response.StatusCode, http.StatusOK) 1607 1608 getContent, err := ioutil.ReadAll(response.Body) 1609 c.Assert(err, nil) 1610 1611 for _, expectedStr := range testCase.expectedStrings { 1612 c.Assert(strings.Contains(string(getContent), expectedStr), true) 1613 } 1614 } 1615 } 1616 1617 // TestListObjectsHandlerErrors - Setting invalid parameters to List Objects 1618 // and then asserting the error response with the expected one. 1619 func (s *TestSuiteCommon) TestListObjectsHandlerErrors(c *check) { 1620 // generate a random bucket name. 1621 bucketName := getRandomBucketName() 1622 // HTTP request to create the bucket. 1623 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1624 0, nil, s.accessKey, s.secretKey, s.signer) 1625 c.Assert(err, nil) 1626 1627 // execute the HTTP request to create bucket. 1628 response, err := s.client.Do(request) 1629 c.Assert(err, nil) 1630 c.Assert(response.StatusCode, http.StatusOK) 1631 1632 // create listObjectsV1 request with invalid value of max-keys parameter. max-keys is set to -2. 1633 request, err = newTestSignedRequest(http.MethodGet, getListObjectsV1URL(s.endPoint, bucketName, "", "-2", ""), 1634 0, nil, s.accessKey, s.secretKey, s.signer) 1635 c.Assert(err, nil) 1636 // execute the HTTP request. 1637 response, err = s.client.Do(request) 1638 c.Assert(err, nil) 1639 // validating the error response. 1640 verifyError(c, response, "InvalidArgument", "Argument maxKeys must be an integer between 0 and 2147483647", http.StatusBadRequest) 1641 1642 // create listObjectsV2 request with invalid value of max-keys parameter. max-keys is set to -2. 1643 request, err = newTestSignedRequest(http.MethodGet, getListObjectsV2URL(s.endPoint, bucketName, "", "-2", "", ""), 1644 0, nil, s.accessKey, s.secretKey, s.signer) 1645 c.Assert(err, nil) 1646 // execute the HTTP request. 1647 response, err = s.client.Do(request) 1648 c.Assert(err, nil) 1649 // validating the error response. 1650 verifyError(c, response, "InvalidArgument", "Argument maxKeys must be an integer between 0 and 2147483647", http.StatusBadRequest) 1651 1652 } 1653 1654 // TestPutBucketErrors - request for non valid bucket operation 1655 // and validate it with expected error result. 1656 func (s *TestSuiteCommon) TestPutBucketErrors(c *check) { 1657 // generate a random bucket name. 1658 bucketName := getRandomBucketName() 1659 // generating a HTTP request to create bucket. 1660 // using invalid bucket name. 1661 request, err := newTestSignedRequest(http.MethodPut, s.endPoint+"/putbucket-.", 1662 0, nil, s.accessKey, s.secretKey, s.signer) 1663 c.Assert(err, nil) 1664 1665 response, err := s.client.Do(request) 1666 c.Assert(err, nil) 1667 // expected to fail with error message "InvalidBucketName". 1668 verifyError(c, response, "InvalidBucketName", "The specified bucket is not valid.", http.StatusBadRequest) 1669 // HTTP request to create the bucket. 1670 request, err = newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1671 0, nil, s.accessKey, s.secretKey, s.signer) 1672 c.Assert(err, nil) 1673 1674 // execute the HTTP request to create bucket. 1675 response, err = s.client.Do(request) 1676 c.Assert(err, nil) 1677 c.Assert(response.StatusCode, http.StatusOK) 1678 // make HTTP request to create the same bucket again. 1679 // expected to fail with error message "BucketAlreadyOwnedByYou". 1680 request, err = newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1681 0, nil, s.accessKey, s.secretKey, s.signer) 1682 c.Assert(err, nil) 1683 1684 response, err = s.client.Do(request) 1685 c.Assert(err, nil) 1686 verifyError(c, response, "BucketAlreadyOwnedByYou", "Your previous request to create the named bucket succeeded and you already own it.", 1687 http.StatusConflict) 1688 } 1689 1690 func (s *TestSuiteCommon) TestGetObjectLarge10MiB(c *check) { 1691 // generate a random bucket name. 1692 bucketName := getRandomBucketName() 1693 // form HTTP request to create the bucket. 1694 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1695 0, nil, s.accessKey, s.secretKey, s.signer) 1696 c.Assert(err, nil) 1697 1698 // execute the HTTP request to create the bucket. 1699 response, err := s.client.Do(request) 1700 c.Assert(err, nil) 1701 c.Assert(response.StatusCode, http.StatusOK) 1702 1703 var buffer bytes.Buffer 1704 line := `1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1705 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1706 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1707 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1708 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1709 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1710 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1711 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1712 1234567890,1234567890,1234567890,1234567890,1234567890,123"` 1713 // Create 10MiB content where each line contains 1024 characters. 1714 for i := 0; i < 10*1024; i++ { 1715 buffer.WriteString(fmt.Sprintf("[%05d] %s\n", i, line)) 1716 } 1717 putContent := buffer.String() 1718 1719 buf := bytes.NewReader([]byte(putContent)) 1720 1721 objectName := "test-big-object" 1722 // create HTTP request for object upload. 1723 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 1724 int64(buf.Len()), buf, s.accessKey, s.secretKey, s.signer) 1725 c.Assert(err, nil) 1726 1727 // execute the HTTP request. 1728 response, err = s.client.Do(request) 1729 c.Assert(err, nil) 1730 // Assert the status code to verify successful upload. 1731 c.Assert(response.StatusCode, http.StatusOK) 1732 1733 // prepare HTTP requests to download the object. 1734 request, err = newTestSignedRequest(http.MethodGet, getPutObjectURL(s.endPoint, bucketName, objectName), 1735 0, nil, s.accessKey, s.secretKey, s.signer) 1736 c.Assert(err, nil) 1737 1738 // execute the HTTP request to download the object. 1739 response, err = s.client.Do(request) 1740 c.Assert(err, nil) 1741 c.Assert(response.StatusCode, http.StatusOK) 1742 // extract the content from response body. 1743 getContent, err := ioutil.ReadAll(response.Body) 1744 c.Assert(err, nil) 1745 1746 // Compare putContent and getContent. 1747 c.Assert(string(getContent), putContent) 1748 } 1749 1750 // TestGetObjectLarge11MiB - Tests validate fetching of an object of size 11MB. 1751 func (s *TestSuiteCommon) TestGetObjectLarge11MiB(c *check) { 1752 // generate a random bucket name. 1753 bucketName := getRandomBucketName() 1754 // HTTP request to create the bucket. 1755 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1756 0, nil, s.accessKey, s.secretKey, s.signer) 1757 c.Assert(err, nil) 1758 1759 // execute the HTTP request. 1760 response, err := s.client.Do(request) 1761 c.Assert(err, nil) 1762 c.Assert(response.StatusCode, http.StatusOK) 1763 1764 var buffer bytes.Buffer 1765 line := `1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1766 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1767 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1768 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1769 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1770 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1771 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1772 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1773 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1774 1234567890,1234567890,1234567890,123` 1775 // Create 11MiB content where each line contains 1024 characters. 1776 for i := 0; i < 11*1024; i++ { 1777 buffer.WriteString(fmt.Sprintf("[%05d] %s\n", i, line)) 1778 } 1779 putMD5 := getMD5Hash(buffer.Bytes()) 1780 1781 objectName := "test-11Mb-object" 1782 // Put object 1783 buf := bytes.NewReader(buffer.Bytes()) 1784 // create HTTP request foe object upload. 1785 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 1786 int64(buf.Len()), buf, s.accessKey, s.secretKey, s.signer) 1787 c.Assert(err, nil) 1788 1789 // execute the HTTP request for object upload. 1790 response, err = s.client.Do(request) 1791 c.Assert(err, nil) 1792 c.Assert(response.StatusCode, http.StatusOK) 1793 1794 // create HTTP request to download the object. 1795 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 1796 0, nil, s.accessKey, s.secretKey, s.signer) 1797 c.Assert(err, nil) 1798 1799 // execute the HTTP request. 1800 response, err = s.client.Do(request) 1801 c.Assert(err, nil) 1802 c.Assert(response.StatusCode, http.StatusOK) 1803 // fetch the content from response body. 1804 getContent, err := ioutil.ReadAll(response.Body) 1805 c.Assert(err, nil) 1806 1807 // Get etag of the response content. 1808 getMD5 := getMD5Hash(getContent) 1809 1810 // Compare putContent and getContent. 1811 c.Assert(putMD5, getMD5) 1812 } 1813 1814 // TestGetPartialObjectMisAligned - tests get object partially mis-aligned. 1815 // create a large buffer of mis-aligned data and upload it. 1816 // then make partial range requests to while fetching it back and assert the response content. 1817 func (s *TestSuiteCommon) TestGetPartialObjectMisAligned(c *check) { 1818 // generate a random bucket name. 1819 bucketName := getRandomBucketName() 1820 // HTTP request to create the bucket. 1821 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1822 0, nil, s.accessKey, s.secretKey, s.signer) 1823 c.Assert(err, nil) 1824 1825 // execute the HTTP request to create the bucket. 1826 response, err := s.client.Do(request) 1827 c.Assert(err, nil) 1828 c.Assert(response.StatusCode, http.StatusOK) 1829 1830 var buffer bytes.Buffer 1831 // data to be written into buffer. 1832 data := "1234567890" 1833 // seed the random number generator once. 1834 rand.Seed(3) 1835 // generate a random number between 13 and 200. 1836 randInt := getRandomRange(13, 200, -1) 1837 // write into buffer till length of the buffer is greater than the generated random number. 1838 for i := 0; i <= randInt; i += 10 { 1839 buffer.WriteString(data) 1840 } 1841 // String content which is used for put object range test. 1842 putBytes := buffer.Bytes() 1843 putBytes = putBytes[:randInt] 1844 // randomize the order of bytes in the byte array and create a reader. 1845 putBytes = randomizeBytes(putBytes, -1) 1846 buf := bytes.NewReader(putBytes) 1847 putContent := string(putBytes) 1848 objectName := "test-big-file" 1849 // HTTP request to upload the object. 1850 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 1851 int64(buf.Len()), buf, s.accessKey, s.secretKey, s.signer) 1852 c.Assert(err, nil) 1853 1854 // execute the HTTP request to upload the object. 1855 response, err = s.client.Do(request) 1856 c.Assert(err, nil) 1857 c.Assert(response.StatusCode, http.StatusOK) 1858 1859 // test Cases containing data to make partial range requests. 1860 // also has expected response data. 1861 var testCases = []struct { 1862 byteRange string 1863 expectedString string 1864 }{ 1865 // request for byte range 10-11. 1866 // expecting the result to contain only putContent[10:12] bytes. 1867 {"10-11", putContent[10:12]}, 1868 // request for object data after the first byte. 1869 {"1-", putContent[1:]}, 1870 // request for object data after the first byte. 1871 {"6-", putContent[6:]}, 1872 // request for last 2 bytes of the object. 1873 {"-2", putContent[len(putContent)-2:]}, 1874 // request for last 7 bytes of the object. 1875 {"-7", putContent[len(putContent)-7:]}, 1876 } 1877 1878 for _, t := range testCases { 1879 // HTTP request to download the object. 1880 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 1881 0, nil, s.accessKey, s.secretKey, s.signer) 1882 c.Assert(err, nil) 1883 // Get partial content based on the byte range set. 1884 request.Header.Set("Range", "bytes="+t.byteRange) 1885 1886 // execute the HTTP request. 1887 response, err = s.client.Do(request) 1888 c.Assert(err, nil) 1889 // Since only part of the object is requested, expecting response status to be http.StatusPartialContent . 1890 c.Assert(response.StatusCode, http.StatusPartialContent) 1891 // parse the HTTP response body. 1892 getContent, err := ioutil.ReadAll(response.Body) 1893 c.Assert(err, nil) 1894 1895 // Compare putContent and getContent. 1896 c.Assert(string(getContent), t.expectedString) 1897 } 1898 } 1899 1900 // TestGetPartialObjectLarge11MiB - Test validates partial content request for a 11MiB object. 1901 func (s *TestSuiteCommon) TestGetPartialObjectLarge11MiB(c *check) { 1902 // generate a random bucket name. 1903 bucketName := getRandomBucketName() 1904 // HTTP request to create the bucket. 1905 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1906 0, nil, s.accessKey, s.secretKey, s.signer) 1907 c.Assert(err, nil) 1908 1909 // execute the HTTP request to create the bucket. 1910 response, err := s.client.Do(request) 1911 c.Assert(err, nil) 1912 c.Assert(response.StatusCode, http.StatusOK) 1913 1914 var buffer bytes.Buffer 1915 line := `234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1916 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1917 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1918 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1919 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1920 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1921 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1922 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1923 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1924 1234567890,1234567890,1234567890,123` 1925 // Create 11MiB content where each line contains 1024 1926 // characters. 1927 for i := 0; i < 11*1024; i++ { 1928 buffer.WriteString(fmt.Sprintf("[%05d] %s\n", i, line)) 1929 } 1930 putContent := buffer.String() 1931 1932 objectName := "test-large-11Mb-object" 1933 1934 buf := bytes.NewReader([]byte(putContent)) 1935 // HTTP request to upload the object. 1936 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 1937 int64(buf.Len()), buf, s.accessKey, s.secretKey, s.signer) 1938 c.Assert(err, nil) 1939 1940 // execute the HTTP request to upload the object. 1941 response, err = s.client.Do(request) 1942 c.Assert(err, nil) 1943 c.Assert(response.StatusCode, http.StatusOK) 1944 1945 // HTTP request to download the object. 1946 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 1947 0, nil, s.accessKey, s.secretKey, s.signer) 1948 c.Assert(err, nil) 1949 // This range spans into first two blocks. 1950 request.Header.Set("Range", "bytes=10485750-10485769") 1951 1952 // execute the HTTP request. 1953 response, err = s.client.Do(request) 1954 c.Assert(err, nil) 1955 // Since only part of the object is requested, expecting response status to be http.StatusPartialContent . 1956 c.Assert(response.StatusCode, http.StatusPartialContent) 1957 // read the downloaded content from the response body. 1958 getContent, err := ioutil.ReadAll(response.Body) 1959 c.Assert(err, nil) 1960 1961 // Compare putContent and getContent. 1962 c.Assert(string(getContent), putContent[10485750:10485770]) 1963 } 1964 1965 // TestGetPartialObjectLarge11MiB - Test validates partial content request for a 10MiB object. 1966 func (s *TestSuiteCommon) TestGetPartialObjectLarge10MiB(c *check) { 1967 // generate a random bucket name. 1968 bucketName := getRandomBucketName() 1969 // HTTP request to create the bucket. 1970 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 1971 0, nil, s.accessKey, s.secretKey, s.signer) 1972 c.Assert(err, nil) 1973 1974 // execute the HTTP request to create bucket. 1975 response, err := s.client.Do(request) 1976 // expecting the error to be nil. 1977 c.Assert(err, nil) 1978 // expecting the HTTP response status code to 200 OK. 1979 c.Assert(response.StatusCode, http.StatusOK) 1980 1981 var buffer bytes.Buffer 1982 line := `1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1983 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1984 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1985 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1986 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1987 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1988 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1989 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1990 1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890,1234567890, 1991 1234567890,1234567890,1234567890,123` 1992 // Create 10MiB content where each line contains 1024 characters. 1993 for i := 0; i < 10*1024; i++ { 1994 buffer.WriteString(fmt.Sprintf("[%05d] %s\n", i, line)) 1995 } 1996 1997 putContent := buffer.String() 1998 buf := bytes.NewReader([]byte(putContent)) 1999 2000 objectName := "test-big-10Mb-file" 2001 // HTTP request to upload the object. 2002 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 2003 int64(buf.Len()), buf, s.accessKey, s.secretKey, s.signer) 2004 c.Assert(err, nil) 2005 2006 // execute the HTTP request to upload the object. 2007 response, err = s.client.Do(request) 2008 c.Assert(err, nil) 2009 // verify whether upload was successful. 2010 c.Assert(response.StatusCode, http.StatusOK) 2011 2012 // HTTP request to download the object. 2013 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 2014 0, nil, s.accessKey, s.secretKey, s.signer) 2015 c.Assert(err, nil) 2016 // Get partial content based on the byte range set. 2017 request.Header.Set("Range", "bytes=2048-2058") 2018 2019 // execute the HTTP request to download the partial content. 2020 response, err = s.client.Do(request) 2021 c.Assert(err, nil) 2022 // Since only part of the object is requested, expecting response status to be http.StatusPartialContent . 2023 c.Assert(response.StatusCode, http.StatusPartialContent) 2024 // read the downloaded content from the response body. 2025 getContent, err := ioutil.ReadAll(response.Body) 2026 c.Assert(err, nil) 2027 2028 // Compare putContent and getContent. 2029 c.Assert(string(getContent), putContent[2048:2059]) 2030 } 2031 2032 // TestGetObjectErrors - Tests validate error response for invalid object operations. 2033 func (s *TestSuiteCommon) TestGetObjectErrors(c *check) { 2034 // generate a random bucket name. 2035 bucketName := getRandomBucketName() 2036 2037 // HTTP request to create the bucket. 2038 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 2039 0, nil, s.accessKey, s.secretKey, s.signer) 2040 c.Assert(err, nil) 2041 2042 // execute the HTTP request to create bucket. 2043 response, err := s.client.Do(request) 2044 c.Assert(err, nil) 2045 c.Assert(response.StatusCode, http.StatusOK) 2046 2047 objectName := "test-non-exitent-object" 2048 // HTTP request to download the object. 2049 // Since the specified object doesn't exist in the given bucket, 2050 // expected to fail with error message "NoSuchKey" 2051 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 2052 0, nil, s.accessKey, s.secretKey, s.signer) 2053 c.Assert(err, nil) 2054 2055 response, err = s.client.Do(request) 2056 c.Assert(err, nil) 2057 verifyError(c, response, "NoSuchKey", "The specified key does not exist.", http.StatusNotFound) 2058 2059 // request to download an object, but an invalid bucket name is set. 2060 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, "getobjecterrors-.", objectName), 2061 0, nil, s.accessKey, s.secretKey, s.signer) 2062 c.Assert(err, nil) 2063 2064 // execute the HTTP request. 2065 response, err = s.client.Do(request) 2066 c.Assert(err, nil) 2067 // expected to fail with "InvalidBucketName". 2068 verifyError(c, response, "InvalidBucketName", "The specified bucket is not valid.", http.StatusBadRequest) 2069 } 2070 2071 // TestGetObjectRangeErrors - Validate error response when object is fetched with incorrect byte range value. 2072 func (s *TestSuiteCommon) TestGetObjectRangeErrors(c *check) { 2073 // generate a random bucket name. 2074 bucketName := getRandomBucketName() 2075 // HTTP request to create the bucket. 2076 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 2077 0, nil, s.accessKey, s.secretKey, s.signer) 2078 c.Assert(err, nil) 2079 2080 // execute the HTTP request to create bucket. 2081 response, err := s.client.Do(request) 2082 c.Assert(err, nil) 2083 c.Assert(response.StatusCode, http.StatusOK) 2084 2085 // content for the object to be uploaded. 2086 buffer1 := bytes.NewReader([]byte("Hello World")) 2087 2088 objectName := "test-object" 2089 // HTTP request to upload the object. 2090 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 2091 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 2092 c.Assert(err, nil) 2093 2094 // execute the HTTP request to upload the object. 2095 response, err = s.client.Do(request) 2096 c.Assert(err, nil) 2097 // verify whether upload was successful. 2098 c.Assert(response.StatusCode, http.StatusOK) 2099 2100 // HTTP request to download the object. 2101 request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 2102 0, nil, s.accessKey, s.secretKey, s.signer) 2103 // Invalid byte range set. 2104 request.Header.Set("Range", "bytes=-0") 2105 c.Assert(err, nil) 2106 2107 // execute the HTTP request. 2108 response, err = s.client.Do(request) 2109 c.Assert(err, nil) 2110 // expected to fail with "InvalidRange" error message. 2111 verifyError(c, response, "InvalidRange", "The requested range is not satisfiable", http.StatusRequestedRangeNotSatisfiable) 2112 } 2113 2114 // TestObjectMultipartAbort - Test validates abortion of a multipart upload after uploading 2 parts. 2115 func (s *TestSuiteCommon) TestObjectMultipartAbort(c *check) { 2116 // generate a random bucket name. 2117 bucketName := getRandomBucketName() 2118 // HTTP request to create the bucket. 2119 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 2120 0, nil, s.accessKey, s.secretKey, s.signer) 2121 c.Assert(err, nil) 2122 2123 // execute the HTTP request to create bucket. 2124 response, err := s.client.Do(request) 2125 c.Assert(err, nil) 2126 c.Assert(response.StatusCode, http.StatusOK) 2127 2128 objectName := "test-multipart-object" 2129 2130 // 1. Initiate 2 uploads for the same object 2131 // 2. Upload 2 parts for the second upload 2132 // 3. Abort the second upload. 2133 // 4. Abort the first upload. 2134 // This will test abort upload when there are more than one upload IDs 2135 // and the case where there is only one upload ID. 2136 2137 // construct HTTP request to initiate a NewMultipart upload. 2138 request, err = newTestSignedRequest(http.MethodPost, getNewMultipartURL(s.endPoint, bucketName, objectName), 2139 0, nil, s.accessKey, s.secretKey, s.signer) 2140 c.Assert(err, nil) 2141 2142 // execute the HTTP request initiating the new multipart upload. 2143 response, err = s.client.Do(request) 2144 c.Assert(err, nil) 2145 c.Assert(response.StatusCode, http.StatusOK) 2146 2147 // parse the response body and obtain the new upload ID. 2148 decoder := xml.NewDecoder(response.Body) 2149 newResponse := &InitiateMultipartUploadResponse{} 2150 2151 err = decoder.Decode(newResponse) 2152 c.Assert(err, nil) 2153 c.Assert(len(newResponse.UploadID) > 0, true) 2154 2155 // construct HTTP request to initiate a NewMultipart upload. 2156 request, err = newTestSignedRequest(http.MethodPost, getNewMultipartURL(s.endPoint, bucketName, objectName), 2157 0, nil, s.accessKey, s.secretKey, s.signer) 2158 c.Assert(err, nil) 2159 2160 // execute the HTTP request initiating the new multipart upload. 2161 response, err = s.client.Do(request) 2162 c.Assert(err, nil) 2163 c.Assert(response.StatusCode, http.StatusOK) 2164 2165 // parse the response body and obtain the new upload ID. 2166 decoder = xml.NewDecoder(response.Body) 2167 newResponse = &InitiateMultipartUploadResponse{} 2168 2169 err = decoder.Decode(newResponse) 2170 c.Assert(err, nil) 2171 c.Assert(len(newResponse.UploadID) > 0, true) 2172 // uploadID to be used for rest of the multipart operations on the object. 2173 uploadID := newResponse.UploadID 2174 2175 // content for the part to be uploaded. 2176 buffer1 := bytes.NewReader([]byte("hello world")) 2177 // HTTP request for the part to be uploaded. 2178 request, err = newTestSignedRequest(http.MethodPut, getPartUploadURL(s.endPoint, bucketName, objectName, uploadID, "1"), 2179 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 2180 c.Assert(err, nil) 2181 // execute the HTTP request to upload the first part. 2182 response1, err := s.client.Do(request) 2183 c.Assert(err, nil) 2184 c.Assert(response1.StatusCode, http.StatusOK) 2185 2186 // content for the second part to be uploaded. 2187 buffer2 := bytes.NewReader([]byte("hello world")) 2188 // HTTP request for the second part to be uploaded. 2189 request, err = newTestSignedRequest(http.MethodPut, getPartUploadURL(s.endPoint, bucketName, objectName, uploadID, "2"), 2190 int64(buffer2.Len()), buffer2, s.accessKey, s.secretKey, s.signer) 2191 c.Assert(err, nil) 2192 // execute the HTTP request to upload the second part. 2193 response2, err := s.client.Do(request) 2194 c.Assert(err, nil) 2195 c.Assert(response2.StatusCode, http.StatusOK) 2196 // HTTP request for aborting the multipart upload. 2197 request, err = newTestSignedRequest(http.MethodDelete, getAbortMultipartUploadURL(s.endPoint, bucketName, objectName, uploadID), 2198 0, nil, s.accessKey, s.secretKey, s.signer) 2199 c.Assert(err, nil) 2200 // execute the HTTP request to abort the multipart upload. 2201 response3, err := s.client.Do(request) 2202 c.Assert(err, nil) 2203 // expecting the response status code to be http.StatusNoContent. 2204 // The assertion validates the success of Abort Multipart operation. 2205 c.Assert(response3.StatusCode, http.StatusNoContent) 2206 } 2207 2208 // TestBucketMultipartList - Initiates a NewMultipart upload, uploads parts and validates listing of the parts. 2209 func (s *TestSuiteCommon) TestBucketMultipartList(c *check) { 2210 // generate a random bucket name. 2211 bucketName := getRandomBucketName() 2212 // HTTP request to create the bucket. 2213 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 0, 2214 nil, s.accessKey, s.secretKey, s.signer) 2215 c.Assert(err, nil) 2216 2217 // execute the HTTP request to create bucket. 2218 response, err := s.client.Do(request) 2219 c.Assert(err, nil) 2220 c.Assert(response.StatusCode, 200) 2221 2222 objectName := "test-multipart-object" 2223 // construct HTTP request to initiate a NewMultipart upload. 2224 request, err = newTestSignedRequest(http.MethodPost, getNewMultipartURL(s.endPoint, bucketName, objectName), 2225 0, nil, s.accessKey, s.secretKey, s.signer) 2226 c.Assert(err, nil) 2227 // execute the HTTP request initiating the new multipart upload. 2228 response, err = s.client.Do(request) 2229 c.Assert(err, nil) 2230 // expecting the response status code to be http.StatusOK(200 OK) . 2231 c.Assert(response.StatusCode, http.StatusOK) 2232 2233 // parse the response body and obtain the new upload ID. 2234 decoder := xml.NewDecoder(response.Body) 2235 newResponse := &InitiateMultipartUploadResponse{} 2236 2237 err = decoder.Decode(newResponse) 2238 c.Assert(err, nil) 2239 c.Assert(len(newResponse.UploadID) > 0, true) 2240 // uploadID to be used for rest of the multipart operations on the object. 2241 uploadID := newResponse.UploadID 2242 2243 // content for the part to be uploaded. 2244 buffer1 := bytes.NewReader([]byte("hello world")) 2245 // HTTP request for the part to be uploaded. 2246 request, err = newTestSignedRequest(http.MethodPut, getPartUploadURL(s.endPoint, bucketName, objectName, uploadID, "1"), 2247 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 2248 c.Assert(err, nil) 2249 // execute the HTTP request to upload the first part. 2250 response1, err := s.client.Do(request) 2251 c.Assert(err, nil) 2252 c.Assert(response1.StatusCode, http.StatusOK) 2253 2254 // content for the second part to be uploaded. 2255 buffer2 := bytes.NewReader([]byte("hello world")) 2256 // HTTP request for the second part to be uploaded. 2257 request, err = newTestSignedRequest(http.MethodPut, getPartUploadURL(s.endPoint, bucketName, objectName, uploadID, "2"), 2258 int64(buffer2.Len()), buffer2, s.accessKey, s.secretKey, s.signer) 2259 c.Assert(err, nil) 2260 // execute the HTTP request to upload the second part. 2261 response2, err := s.client.Do(request) 2262 c.Assert(err, nil) 2263 c.Assert(response2.StatusCode, http.StatusOK) 2264 2265 // HTTP request to ListMultipart Uploads. 2266 request, err = newTestSignedRequest(http.MethodGet, getListMultipartURL(s.endPoint, bucketName), 2267 0, nil, s.accessKey, s.secretKey, s.signer) 2268 c.Assert(err, nil) 2269 // execute the HTTP request. 2270 response3, err := s.client.Do(request) 2271 c.Assert(err, nil) 2272 c.Assert(response3.StatusCode, http.StatusOK) 2273 2274 // The reason to duplicate this structure here is to verify if the 2275 // unmarshalling works from a client perspective, specifically 2276 // while unmarshalling time.Time type for 'Initiated' field. 2277 // time.Time does not honor xml marshaller, it means that we need 2278 // to encode/format it before giving it to xml marshaling. 2279 2280 // This below check adds client side verification to see if its 2281 // truly parsable. 2282 2283 // listMultipartUploadsResponse - format for list multipart uploads response. 2284 type listMultipartUploadsResponse struct { 2285 XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListMultipartUploadsResult" json:"-"` 2286 2287 Bucket string 2288 KeyMarker string 2289 UploadIDMarker string `xml:"UploadIdMarker"` 2290 NextKeyMarker string 2291 NextUploadIDMarker string `xml:"NextUploadIdMarker"` 2292 EncodingType string 2293 MaxUploads int 2294 IsTruncated bool 2295 // All the in progress multipart uploads. 2296 Uploads []struct { 2297 Key string 2298 UploadID string `xml:"UploadId"` 2299 Initiator Initiator 2300 Owner Owner 2301 StorageClass string 2302 Initiated time.Time // Keep this native to be able to parse properly. 2303 } 2304 Prefix string 2305 Delimiter string 2306 CommonPrefixes []CommonPrefix 2307 } 2308 2309 // parse the response body. 2310 decoder = xml.NewDecoder(response3.Body) 2311 newResponse3 := &listMultipartUploadsResponse{} 2312 err = decoder.Decode(newResponse3) 2313 c.Assert(err, nil) 2314 // Assert the bucket name in the response with the expected bucketName. 2315 c.Assert(newResponse3.Bucket, bucketName) 2316 // Assert the bucket name in the response with the expected bucketName. 2317 c.Assert(newResponse3.IsTruncated, false) 2318 } 2319 2320 // TestValidateObjectMultipartUploadID - Test Initiates a new multipart upload and validates the uploadID. 2321 func (s *TestSuiteCommon) TestValidateObjectMultipartUploadID(c *check) { 2322 // generate a random bucket name. 2323 bucketName := getRandomBucketName() 2324 // HTTP request to create the bucket. 2325 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 2326 0, nil, s.accessKey, s.secretKey, s.signer) 2327 c.Assert(err, nil) 2328 2329 // execute the HTTP request to create bucket. 2330 response, err := s.client.Do(request) 2331 c.Assert(err, nil) 2332 c.Assert(response.StatusCode, 200) 2333 2334 objectName := "directory1/directory2/object" 2335 // construct HTTP request to initiate a NewMultipart upload. 2336 request, err = newTestSignedRequest(http.MethodPost, getNewMultipartURL(s.endPoint, bucketName, objectName), 2337 0, nil, s.accessKey, s.secretKey, s.signer) 2338 c.Assert(err, nil) 2339 // execute the HTTP request initiating the new multipart upload. 2340 response, err = s.client.Do(request) 2341 c.Assert(err, nil) 2342 c.Assert(response.StatusCode, http.StatusOK) 2343 2344 // parse the response body and obtain the new upload ID. 2345 decoder := xml.NewDecoder(response.Body) 2346 newResponse := &InitiateMultipartUploadResponse{} 2347 err = decoder.Decode(newResponse) 2348 // expecting the decoding error to be nil. 2349 c.Assert(err, nil) 2350 // Verifying for Upload ID value to be greater than 0. 2351 c.Assert(len(newResponse.UploadID) > 0, true) 2352 } 2353 2354 // TestObjectMultipartListError - Initiates a NewMultipart upload, uploads parts and validates 2355 // error response for an incorrect max-parts parameter . 2356 func (s *TestSuiteCommon) TestObjectMultipartListError(c *check) { 2357 // generate a random bucket name. 2358 bucketName := getRandomBucketName() 2359 // HTTP request to create the bucket. 2360 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 2361 0, nil, s.accessKey, s.secretKey, s.signer) 2362 c.Assert(err, nil) 2363 2364 // execute the HTTP request to create bucket. 2365 response, err := s.client.Do(request) 2366 c.Assert(err, nil) 2367 c.Assert(response.StatusCode, 200) 2368 2369 objectName := "test-multipart-object" 2370 // construct HTTP request to initiate a NewMultipart upload. 2371 request, err = newTestSignedRequest(http.MethodPost, getNewMultipartURL(s.endPoint, bucketName, objectName), 2372 0, nil, s.accessKey, s.secretKey, s.signer) 2373 c.Assert(err, nil) 2374 // execute the HTTP request initiating the new multipart upload. 2375 response, err = s.client.Do(request) 2376 c.Assert(err, nil) 2377 c.Assert(response.StatusCode, http.StatusOK) 2378 // parse the response body and obtain the new upload ID. 2379 decoder := xml.NewDecoder(response.Body) 2380 newResponse := &InitiateMultipartUploadResponse{} 2381 2382 err = decoder.Decode(newResponse) 2383 c.Assert(err, nil) 2384 c.Assert(len(newResponse.UploadID) > 0, true) 2385 // uploadID to be used for rest of the multipart operations on the object. 2386 uploadID := newResponse.UploadID 2387 2388 // content for the part to be uploaded. 2389 buffer1 := bytes.NewReader([]byte("hello world")) 2390 // HTTP request for the part to be uploaded. 2391 request, err = newTestSignedRequest(http.MethodPut, getPartUploadURL(s.endPoint, bucketName, objectName, uploadID, "1"), 2392 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 2393 c.Assert(err, nil) 2394 // execute the HTTP request to upload the first part. 2395 response1, err := s.client.Do(request) 2396 c.Assert(err, nil) 2397 c.Assert(response1.StatusCode, http.StatusOK) 2398 2399 // content for the second part to be uploaded. 2400 buffer2 := bytes.NewReader([]byte("hello world")) 2401 // HTTP request for the second part to be uploaded. 2402 request, err = newTestSignedRequest(http.MethodPut, getPartUploadURL(s.endPoint, bucketName, objectName, uploadID, "2"), 2403 int64(buffer2.Len()), buffer2, s.accessKey, s.secretKey, s.signer) 2404 c.Assert(err, nil) 2405 2406 // execute the HTTP request to upload the second part. 2407 response2, err := s.client.Do(request) 2408 c.Assert(err, nil) 2409 c.Assert(response2.StatusCode, http.StatusOK) 2410 2411 // HTTP request to ListMultipart Uploads. 2412 // max-keys is set to valid value of 1 2413 request, err = newTestSignedRequest(http.MethodGet, getListMultipartURLWithParams(s.endPoint, bucketName, objectName, uploadID, "1", "", ""), 2414 0, nil, s.accessKey, s.secretKey, s.signer) 2415 c.Assert(err, nil) 2416 // execute the HTTP request. 2417 response3, err := s.client.Do(request) 2418 c.Assert(err, nil) 2419 c.Assert(response3.StatusCode, http.StatusOK) 2420 2421 // HTTP request to ListMultipart Uploads. 2422 // max-keys is set to invalid value of -2. 2423 request, err = newTestSignedRequest(http.MethodGet, getListMultipartURLWithParams(s.endPoint, bucketName, objectName, uploadID, "-2", "", ""), 2424 0, nil, s.accessKey, s.secretKey, s.signer) 2425 c.Assert(err, nil) 2426 // execute the HTTP request. 2427 response4, err := s.client.Do(request) 2428 c.Assert(err, nil) 2429 // Since max-keys parameter in the ListMultipart request set to invalid value of -2, 2430 // its expected to fail with error message "InvalidArgument". 2431 verifyError(c, response4, "InvalidArgument", "Argument max-parts must be an integer between 0 and 2147483647", http.StatusBadRequest) 2432 } 2433 2434 // TestObjectValidMD5 - First uploads an object with a valid Content-Md5 header and verifies the status, 2435 // then upload an object in a wrong Content-Md5 and validate the error response. 2436 func (s *TestSuiteCommon) TestObjectValidMD5(c *check) { 2437 // generate a random bucket name. 2438 bucketName := getRandomBucketName() 2439 // HTTP request to create the bucket. 2440 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 2441 0, nil, s.accessKey, s.secretKey, s.signer) 2442 c.Assert(err, nil) 2443 2444 // execute the HTTP request to create bucket. 2445 response, err := s.client.Do(request) 2446 c.Assert(err, nil) 2447 c.Assert(response.StatusCode, 200) 2448 2449 // Create a byte array of 5MB. 2450 // content for the object to be uploaded. 2451 data := bytes.Repeat([]byte("0123456789abcdef"), 5*humanize.MiByte/16) 2452 // calculate etag of the data. 2453 etagBase64 := getMD5HashBase64(data) 2454 2455 buffer1 := bytes.NewReader(data) 2456 objectName := "test-1-object" 2457 // HTTP request for the object to be uploaded. 2458 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 2459 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 2460 c.Assert(err, nil) 2461 // set the Content-Md5 to be the hash to content. 2462 request.Header.Set("Content-Md5", etagBase64) 2463 response, err = s.client.Do(request) 2464 c.Assert(err, nil) 2465 // expecting a successful upload. 2466 c.Assert(response.StatusCode, http.StatusOK) 2467 objectName = "test-2-object" 2468 buffer1 = bytes.NewReader(data) 2469 // HTTP request for the object to be uploaded. 2470 request, err = newTestSignedRequest(http.MethodPut, getPutObjectURL(s.endPoint, bucketName, objectName), 2471 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 2472 c.Assert(err, nil) 2473 // set Content-Md5 to invalid value. 2474 request.Header.Set("Content-Md5", "kvLTlMrX9NpYDQlEIFlnDA==") 2475 // expecting a failure during upload. 2476 response, err = s.client.Do(request) 2477 c.Assert(err, nil) 2478 // Since Content-Md5 header was wrong, expecting to fail with "SignatureDoesNotMatch" error. 2479 verifyError(c, response, "SignatureDoesNotMatch", "The request signature we calculated does not match the signature you provided. Check your key and signing method.", http.StatusForbidden) 2480 } 2481 2482 // TestObjectMultipart - Initiates a NewMultipart upload, uploads 2 parts, 2483 // completes the multipart upload and validates the status of the operation. 2484 func (s *TestSuiteCommon) TestObjectMultipart(c *check) { 2485 // generate a random bucket name. 2486 bucketName := getRandomBucketName() 2487 // HTTP request to create the bucket. 2488 request, err := newTestSignedRequest(http.MethodPut, getMakeBucketURL(s.endPoint, bucketName), 2489 0, nil, s.accessKey, s.secretKey, s.signer) 2490 c.Assert(err, nil) 2491 2492 // execute the HTTP request to create bucket. 2493 response, err := s.client.Do(request) 2494 c.Assert(err, nil) 2495 c.Assert(response.StatusCode, 200) 2496 2497 objectName := "test-multipart-object" 2498 // construct HTTP request to initiate a NewMultipart upload. 2499 request, err = newTestSignedRequest(http.MethodPost, getNewMultipartURL(s.endPoint, bucketName, objectName), 2500 0, nil, s.accessKey, s.secretKey, s.signer) 2501 c.Assert(err, nil) 2502 2503 // execute the HTTP request initiating the new multipart upload. 2504 response, err = s.client.Do(request) 2505 c.Assert(err, nil) 2506 // expecting the response status code to be http.StatusOK(200 OK). 2507 c.Assert(response.StatusCode, http.StatusOK) 2508 // parse the response body and obtain the new upload ID. 2509 decoder := xml.NewDecoder(response.Body) 2510 newResponse := &InitiateMultipartUploadResponse{} 2511 2512 err = decoder.Decode(newResponse) 2513 c.Assert(err, nil) 2514 c.Assert(len(newResponse.UploadID) > 0, true) 2515 // uploadID to be used for rest of the multipart operations on the object. 2516 uploadID := newResponse.UploadID 2517 2518 // content for the part to be uploaded. 2519 // Create a byte array of 5MB. 2520 data := bytes.Repeat([]byte("0123456789abcdef"), 5*humanize.MiByte/16) 2521 // calculate etag of the data. 2522 md5SumBase64 := getMD5HashBase64(data) 2523 2524 buffer1 := bytes.NewReader(data) 2525 // HTTP request for the part to be uploaded. 2526 request, err = newTestSignedRequest(http.MethodPut, getPartUploadURL(s.endPoint, bucketName, objectName, uploadID, "1"), 2527 int64(buffer1.Len()), buffer1, s.accessKey, s.secretKey, s.signer) 2528 // set the Content-Md5 header to the base64 encoding the etag of the content. 2529 request.Header.Set("Content-Md5", md5SumBase64) 2530 c.Assert(err, nil) 2531 2532 // execute the HTTP request to upload the first part. 2533 response1, err := s.client.Do(request) 2534 c.Assert(err, nil) 2535 c.Assert(response1.StatusCode, http.StatusOK) 2536 2537 // content for the second part to be uploaded. 2538 // Create a byte array of 1 byte. 2539 data = []byte("0") 2540 2541 // calculate etag of the data. 2542 md5SumBase64 = getMD5HashBase64(data) 2543 2544 buffer2 := bytes.NewReader(data) 2545 // HTTP request for the second part to be uploaded. 2546 request, err = newTestSignedRequest(http.MethodPut, getPartUploadURL(s.endPoint, bucketName, objectName, uploadID, "2"), 2547 int64(buffer2.Len()), buffer2, s.accessKey, s.secretKey, s.signer) 2548 // set the Content-Md5 header to the base64 encoding the etag of the content. 2549 request.Header.Set("Content-Md5", md5SumBase64) 2550 c.Assert(err, nil) 2551 2552 // execute the HTTP request to upload the second part. 2553 response2, err := s.client.Do(request) 2554 c.Assert(err, nil) 2555 c.Assert(response2.StatusCode, http.StatusOK) 2556 2557 // Complete multipart upload 2558 completeUploads := &CompleteMultipartUpload{ 2559 Parts: []CompletePart{ 2560 { 2561 PartNumber: 1, 2562 ETag: response1.Header.Get("ETag"), 2563 }, 2564 { 2565 PartNumber: 2, 2566 ETag: response2.Header.Get("ETag"), 2567 }, 2568 }, 2569 } 2570 2571 completeBytes, err := xml.Marshal(completeUploads) 2572 c.Assert(err, nil) 2573 // Indicating that all parts are uploaded and initiating CompleteMultipartUpload. 2574 request, err = newTestSignedRequest(http.MethodPost, getCompleteMultipartUploadURL(s.endPoint, bucketName, objectName, uploadID), 2575 int64(len(completeBytes)), bytes.NewReader(completeBytes), s.accessKey, s.secretKey, s.signer) 2576 c.Assert(err, nil) 2577 // Execute the complete multipart request. 2578 response, err = s.client.Do(request) 2579 c.Assert(err, nil) 2580 // verify whether complete multipart was successful. 2581 c.Assert(response.StatusCode, http.StatusOK) 2582 var parts []CompletePart 2583 for _, part := range completeUploads.Parts { 2584 part.ETag = canonicalizeETag(part.ETag) 2585 parts = append(parts, part) 2586 } 2587 etag := getCompleteMultipartMD5(parts) 2588 c.Assert(canonicalizeETag(response.Header.Get(xhttp.ETag)), etag) 2589 }