github.com/aavshr/aws-sdk-go@v1.41.3/service/s3/s3manager/upload_test.go (about) 1 //go:build go1.8 2 // +build go1.8 3 4 package s3manager_test 5 6 import ( 7 "bytes" 8 "fmt" 9 "io" 10 "io/ioutil" 11 random "math/rand" 12 "net/http" 13 "net/http/httptest" 14 "os" 15 "reflect" 16 "regexp" 17 "sort" 18 "strconv" 19 "strings" 20 "sync" 21 "testing" 22 "time" 23 24 "github.com/aavshr/aws-sdk-go/aws" 25 "github.com/aavshr/aws-sdk-go/aws/awserr" 26 "github.com/aavshr/aws-sdk-go/aws/awsutil" 27 "github.com/aavshr/aws-sdk-go/aws/request" 28 "github.com/aavshr/aws-sdk-go/awstesting" 29 "github.com/aavshr/aws-sdk-go/awstesting/unit" 30 "github.com/aavshr/aws-sdk-go/service/s3" 31 "github.com/aavshr/aws-sdk-go/service/s3/internal/s3testing" 32 "github.com/aavshr/aws-sdk-go/service/s3/s3manager" 33 ) 34 35 var emptyList = []string{} 36 37 const respMsg = `<?xml version="1.0" encoding="UTF-8"?> 38 <CompleteMultipartUploadOutput> 39 <Location>mockValue</Location> 40 <Bucket>mockValue</Bucket> 41 <Key>mockValue</Key> 42 <ETag>mockValue</ETag> 43 </CompleteMultipartUploadOutput>` 44 45 func val(i interface{}, s string) interface{} { 46 v, err := awsutil.ValuesAtPath(i, s) 47 if err != nil || len(v) == 0 { 48 return nil 49 } 50 if _, ok := v[0].(io.Reader); ok { 51 return v[0] 52 } 53 54 if rv := reflect.ValueOf(v[0]); rv.Kind() == reflect.Ptr { 55 return rv.Elem().Interface() 56 } 57 58 return v[0] 59 } 60 61 func contains(src []string, s string) bool { 62 for _, v := range src { 63 if s == v { 64 return true 65 } 66 } 67 return false 68 } 69 70 func loggingSvc(ignoreOps []string) (*s3.S3, *[]string, *[]interface{}) { 71 var m sync.Mutex 72 partNum := 0 73 names := []string{} 74 params := []interface{}{} 75 svc := s3.New(unit.Session) 76 svc.Handlers.Unmarshal.Clear() 77 svc.Handlers.UnmarshalMeta.Clear() 78 svc.Handlers.UnmarshalError.Clear() 79 svc.Handlers.Send.Clear() 80 svc.Handlers.Send.PushBack(func(r *request.Request) { 81 m.Lock() 82 defer m.Unlock() 83 84 if !contains(ignoreOps, r.Operation.Name) { 85 names = append(names, r.Operation.Name) 86 params = append(params, r.Params) 87 } 88 89 r.HTTPResponse = &http.Response{ 90 StatusCode: 200, 91 Body: ioutil.NopCloser(bytes.NewReader([]byte(respMsg))), 92 } 93 94 switch data := r.Data.(type) { 95 case *s3.CreateMultipartUploadOutput: 96 data.UploadId = aws.String("UPLOAD-ID") 97 case *s3.UploadPartOutput: 98 partNum++ 99 data.ETag = aws.String(fmt.Sprintf("ETAG%d", partNum)) 100 case *s3.CompleteMultipartUploadOutput: 101 data.Location = aws.String("https://location") 102 data.VersionId = aws.String("VERSION-ID") 103 data.ETag = aws.String("ETAG") 104 case *s3.PutObjectOutput: 105 data.VersionId = aws.String("VERSION-ID") 106 data.ETag = aws.String("ETAG") 107 } 108 }) 109 110 return svc, &names, ¶ms 111 } 112 113 func buflen(i interface{}) int { 114 r := i.(io.Reader) 115 b, _ := ioutil.ReadAll(r) 116 return len(b) 117 } 118 119 func TestUploadOrderMulti(t *testing.T) { 120 s, ops, args := loggingSvc(emptyList) 121 u := s3manager.NewUploaderWithClient(s) 122 123 resp, err := u.Upload(&s3manager.UploadInput{ 124 Bucket: aws.String("Bucket"), 125 Key: aws.String("Key - value"), 126 Body: bytes.NewReader(buf12MB), 127 ServerSideEncryption: aws.String("aws:kms"), 128 SSEKMSKeyId: aws.String("KmsId"), 129 ContentType: aws.String("content/type"), 130 }) 131 132 if err != nil { 133 t.Errorf("Expected no error but received %v", err) 134 } 135 136 expected := []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "UploadPart", "CompleteMultipartUpload"} 137 if !reflect.DeepEqual(expected, *ops) { 138 t.Errorf("Expected %v, but received %v", expected, *ops) 139 } 140 141 if e, a := `https://s3.mock-region.amazonaws.com/Bucket/Key%20-%20value`, resp.Location; e != a { 142 t.Errorf("Expected %q, but received %q", e, a) 143 } 144 145 if "UPLOAD-ID" != resp.UploadID { 146 t.Errorf("Expected %q, but received %q", "UPLOAD-ID", resp.UploadID) 147 } 148 149 if "VERSION-ID" != *resp.VersionID { 150 t.Errorf("Expected %q, but received %q", "VERSION-ID", *resp.VersionID) 151 } 152 153 if "ETAG" != *resp.ETag { 154 t.Errorf("Expected %q, but received %q", "ETAG", *resp.ETag) 155 } 156 157 // Validate input values 158 159 // UploadPart 160 for i := 1; i < 5; i++ { 161 v := val((*args)[i], "UploadId") 162 if "UPLOAD-ID" != v { 163 t.Errorf("Expected %q, but received %q", "UPLOAD-ID", v) 164 } 165 } 166 167 // CompleteMultipartUpload 168 v := val((*args)[4], "UploadId") 169 if "UPLOAD-ID" != v { 170 t.Errorf("Expected %q, but received %q", "UPLOAD-ID", v) 171 } 172 173 for i := 0; i < 3; i++ { 174 e := val((*args)[4], fmt.Sprintf("MultipartUpload.Parts[%d].PartNumber", i)) 175 if int64(i+1) != e.(int64) { 176 t.Errorf("Expected %d, but received %d", i+1, e) 177 } 178 } 179 180 vals := []string{ 181 val((*args)[4], "MultipartUpload.Parts[0].ETag").(string), 182 val((*args)[4], "MultipartUpload.Parts[1].ETag").(string), 183 val((*args)[4], "MultipartUpload.Parts[2].ETag").(string), 184 } 185 186 for _, a := range vals { 187 if matched, err := regexp.MatchString(`^ETAG\d+$`, a); !matched || err != nil { 188 t.Errorf("Failed regexp expression `^ETAG\\d+$`") 189 } 190 } 191 192 // Custom headers 193 e := val((*args)[0], "ServerSideEncryption") 194 if e != "aws:kms" { 195 t.Errorf("Expected %q, but received %q", "aws:kms", e) 196 } 197 198 e = val((*args)[0], "SSEKMSKeyId") 199 if e != "KmsId" { 200 t.Errorf("Expected %q, but received %q", "KmsId", e) 201 } 202 203 e = val((*args)[0], "ContentType") 204 if e != "content/type" { 205 t.Errorf("Expected %q, but received %q", "content/type", e) 206 } 207 } 208 209 func TestUploadOrderMultiDifferentPartSize(t *testing.T) { 210 s, ops, args := loggingSvc(emptyList) 211 mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) { 212 u.PartSize = 1024 * 1024 * 7 213 u.Concurrency = 1 214 }) 215 _, err := mgr.Upload(&s3manager.UploadInput{ 216 Bucket: aws.String("Bucket"), 217 Key: aws.String("Key"), 218 Body: bytes.NewReader(buf12MB), 219 }) 220 221 if err != nil { 222 t.Errorf("Expected no error but received %v", err) 223 } 224 225 vals := []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "CompleteMultipartUpload"} 226 if !reflect.DeepEqual(vals, *ops) { 227 t.Errorf("Expected %v, but received %v", vals, *ops) 228 } 229 230 // Part lengths 231 if len := buflen(val((*args)[1], "Body")); 1024*1024*7 != len { 232 t.Errorf("Expected %d, but received %d", 1024*1024*7, len) 233 } 234 if len := buflen(val((*args)[2], "Body")); 1024*1024*5 != len { 235 t.Errorf("Expected %d, but received %d", 1024*1024*5, len) 236 } 237 } 238 239 func TestUploadIncreasePartSize(t *testing.T) { 240 s, ops, args := loggingSvc(emptyList) 241 mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) { 242 u.Concurrency = 1 243 u.MaxUploadParts = 2 244 }) 245 _, err := mgr.Upload(&s3manager.UploadInput{ 246 Bucket: aws.String("Bucket"), 247 Key: aws.String("Key"), 248 Body: bytes.NewReader(buf12MB), 249 }) 250 251 if err != nil { 252 t.Errorf("Expected no error but received %v", err) 253 } 254 255 if int64(s3manager.DefaultDownloadPartSize) != mgr.PartSize { 256 t.Errorf("Expected %d, but received %d", s3manager.DefaultDownloadPartSize, mgr.PartSize) 257 } 258 259 vals := []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "CompleteMultipartUpload"} 260 if !reflect.DeepEqual(vals, *ops) { 261 t.Errorf("Expected %v, but received %v", vals, *ops) 262 } 263 264 // Part lengths 265 if len := buflen(val((*args)[1], "Body")); (1024*1024*6)+1 != len { 266 t.Errorf("Expected %d, but received %d", (1024*1024*6)+1, len) 267 } 268 269 if len := buflen(val((*args)[2], "Body")); (1024*1024*6)-1 != len { 270 t.Errorf("Expected %d, but received %d", (1024*1024*6)-1, len) 271 } 272 } 273 274 func TestUploadFailIfPartSizeTooSmall(t *testing.T) { 275 mgr := s3manager.NewUploader(unit.Session, func(u *s3manager.Uploader) { 276 u.PartSize = 5 277 }) 278 resp, err := mgr.Upload(&s3manager.UploadInput{ 279 Bucket: aws.String("Bucket"), 280 Key: aws.String("Key"), 281 Body: bytes.NewReader(buf12MB), 282 }) 283 284 if resp != nil { 285 t.Errorf("Expected response to be nil, but received %v", resp) 286 } 287 288 if err == nil { 289 t.Errorf("Expected error, but received nil") 290 } 291 292 aerr := err.(awserr.Error) 293 if e, a := "ConfigError", aerr.Code(); e != a { 294 t.Errorf("Expected %q, but received %q", e, a) 295 } 296 297 if e, a := "part size must be at least", aerr.Message(); !strings.Contains(a, e) { 298 t.Errorf("expect %v to be in %v", e, a) 299 } 300 } 301 302 func TestUploadOrderSingle(t *testing.T) { 303 s, ops, args := loggingSvc(emptyList) 304 mgr := s3manager.NewUploaderWithClient(s) 305 resp, err := mgr.Upload(&s3manager.UploadInput{ 306 Bucket: aws.String("Bucket"), 307 Key: aws.String("Key - value"), 308 Body: bytes.NewReader(buf2MB), 309 ServerSideEncryption: aws.String("aws:kms"), 310 SSEKMSKeyId: aws.String("KmsId"), 311 ContentType: aws.String("content/type"), 312 }) 313 314 if err != nil { 315 t.Errorf("Expected no error but received %v", err) 316 } 317 318 if vals := []string{"PutObject"}; !reflect.DeepEqual(vals, *ops) { 319 t.Errorf("Expected %v, but received %v", vals, *ops) 320 } 321 322 if e, a := `https://s3.mock-region.amazonaws.com/Bucket/Key%20-%20value`, resp.Location; e != a { 323 t.Errorf("Expected %q, but received %q", e, a) 324 } 325 326 if e := "VERSION-ID"; e != *resp.VersionID { 327 t.Errorf("Expected %q, but received %q", e, *resp.VersionID) 328 } 329 330 if "ETAG" != *resp.ETag { 331 t.Errorf("Expected %q, but received %q", "ETAG", *resp.ETag) 332 } 333 334 if len(resp.UploadID) > 0 { 335 t.Errorf("Expected empty string, but received %q", resp.UploadID) 336 } 337 338 if e, a := "aws:kms", val((*args)[0], "ServerSideEncryption").(string); e != a { 339 t.Errorf("Expected %q, but received %q", e, a) 340 } 341 342 if e, a := "KmsId", val((*args)[0], "SSEKMSKeyId").(string); e != a { 343 t.Errorf("Expected %q, but received %q", e, a) 344 } 345 346 if e, a := "content/type", val((*args)[0], "ContentType").(string); e != a { 347 t.Errorf("Expected %q, but received %q", e, a) 348 } 349 } 350 351 func TestUploadOrderSingleFailure(t *testing.T) { 352 s, ops, _ := loggingSvc(emptyList) 353 s.Handlers.Send.PushBack(func(r *request.Request) { 354 r.HTTPResponse.StatusCode = 400 355 }) 356 mgr := s3manager.NewUploaderWithClient(s) 357 resp, err := mgr.Upload(&s3manager.UploadInput{ 358 Bucket: aws.String("Bucket"), 359 Key: aws.String("Key"), 360 Body: bytes.NewReader(buf2MB), 361 }) 362 363 if err == nil { 364 t.Error("Expected error, but receievd nil") 365 } 366 367 if vals := []string{"PutObject"}; !reflect.DeepEqual(vals, *ops) { 368 t.Errorf("Expected %v, but received %v", vals, *ops) 369 } 370 371 if resp != nil { 372 t.Errorf("Expected response to be nil, but received %v", resp) 373 } 374 } 375 376 func TestUploadOrderZero(t *testing.T) { 377 s, ops, args := loggingSvc(emptyList) 378 mgr := s3manager.NewUploaderWithClient(s) 379 resp, err := mgr.Upload(&s3manager.UploadInput{ 380 Bucket: aws.String("Bucket"), 381 Key: aws.String("Key"), 382 Body: bytes.NewReader(make([]byte, 0)), 383 }) 384 385 if err != nil { 386 t.Errorf("Expected no error but received %v", err) 387 } 388 389 if vals := []string{"PutObject"}; !reflect.DeepEqual(vals, *ops) { 390 t.Errorf("Expected %v, but received %v", vals, *ops) 391 } 392 393 if len(resp.Location) == 0 { 394 t.Error("Expected Location to not be empty") 395 } 396 397 if len(resp.UploadID) > 0 { 398 t.Errorf("Expected empty string, but received %q", resp.UploadID) 399 } 400 401 if e, a := 0, buflen(val((*args)[0], "Body")); e != a { 402 t.Errorf("Expected %d, but received %d", e, a) 403 } 404 } 405 406 func TestUploadOrderMultiFailure(t *testing.T) { 407 s, ops, _ := loggingSvc(emptyList) 408 s.Handlers.Send.PushBack(func(r *request.Request) { 409 switch t := r.Data.(type) { 410 case *s3.UploadPartOutput: 411 if *t.ETag == "ETAG2" { 412 r.HTTPResponse.StatusCode = 400 413 } 414 } 415 }) 416 417 mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) { 418 u.Concurrency = 1 419 }) 420 _, err := mgr.Upload(&s3manager.UploadInput{ 421 Bucket: aws.String("Bucket"), 422 Key: aws.String("Key"), 423 Body: bytes.NewReader(buf12MB), 424 }) 425 426 if err == nil { 427 t.Error("Expected error, but receievd nil") 428 } 429 430 if e, a := []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "AbortMultipartUpload"}, *ops; !reflect.DeepEqual(e, a) { 431 t.Errorf("Expected %v, but received %v", e, a) 432 } 433 } 434 435 func TestUploadOrderMultiFailureOnComplete(t *testing.T) { 436 s, ops, _ := loggingSvc(emptyList) 437 s.Handlers.Send.PushBack(func(r *request.Request) { 438 switch r.Data.(type) { 439 case *s3.CompleteMultipartUploadOutput: 440 r.HTTPResponse.StatusCode = 400 441 } 442 }) 443 444 mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) { 445 u.Concurrency = 1 446 }) 447 _, err := mgr.Upload(&s3manager.UploadInput{ 448 Bucket: aws.String("Bucket"), 449 Key: aws.String("Key"), 450 Body: bytes.NewReader(buf12MB), 451 }) 452 453 if err == nil { 454 t.Error("Expected error, but receievd nil") 455 } 456 457 if e, a := []string{"CreateMultipartUpload", "UploadPart", "UploadPart", 458 "UploadPart", "CompleteMultipartUpload", "AbortMultipartUpload"}, *ops; !reflect.DeepEqual(e, a) { 459 t.Errorf("Expected %v, but received %v", e, a) 460 } 461 } 462 463 func TestUploadOrderMultiFailureOnCreate(t *testing.T) { 464 s, ops, _ := loggingSvc(emptyList) 465 s.Handlers.Send.PushBack(func(r *request.Request) { 466 switch r.Data.(type) { 467 case *s3.CreateMultipartUploadOutput: 468 r.HTTPResponse.StatusCode = 400 469 } 470 }) 471 472 mgr := s3manager.NewUploaderWithClient(s) 473 _, err := mgr.Upload(&s3manager.UploadInput{ 474 Bucket: aws.String("Bucket"), 475 Key: aws.String("Key"), 476 Body: bytes.NewReader(make([]byte, 1024*1024*12)), 477 }) 478 479 if err == nil { 480 t.Error("Expected error, but receievd nil") 481 } 482 483 if e, a := []string{"CreateMultipartUpload"}, *ops; !reflect.DeepEqual(e, a) { 484 t.Errorf("Expected %v, but received %v", e, a) 485 } 486 } 487 488 func TestUploadOrderMultiFailureLeaveParts(t *testing.T) { 489 s, ops, _ := loggingSvc(emptyList) 490 s.Handlers.Send.PushBack(func(r *request.Request) { 491 switch data := r.Data.(type) { 492 case *s3.UploadPartOutput: 493 if *data.ETag == "ETAG2" { 494 r.HTTPResponse.StatusCode = 400 495 } 496 } 497 }) 498 499 mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) { 500 u.Concurrency = 1 501 u.LeavePartsOnError = true 502 }) 503 _, err := mgr.Upload(&s3manager.UploadInput{ 504 Bucket: aws.String("Bucket"), 505 Key: aws.String("Key"), 506 Body: bytes.NewReader(make([]byte, 1024*1024*12)), 507 }) 508 509 if err == nil { 510 t.Error("Expected error, but receievd nil") 511 } 512 513 if e, a := []string{"CreateMultipartUpload", "UploadPart", "UploadPart"}, *ops; !reflect.DeepEqual(e, a) { 514 t.Errorf("Expected %v, but received %v", e, a) 515 } 516 } 517 518 type failreader struct { 519 times int 520 failCount int 521 } 522 523 func (f *failreader) Read(b []byte) (int, error) { 524 f.failCount++ 525 if f.failCount >= f.times { 526 return 0, fmt.Errorf("random failure") 527 } 528 return len(b), nil 529 } 530 531 func TestUploadOrderReadFail1(t *testing.T) { 532 s, ops, _ := loggingSvc(emptyList) 533 mgr := s3manager.NewUploaderWithClient(s) 534 _, err := mgr.Upload(&s3manager.UploadInput{ 535 Bucket: aws.String("Bucket"), 536 Key: aws.String("Key"), 537 Body: &failreader{times: 1}, 538 }) 539 540 if e, a := "ReadRequestBody", err.(awserr.Error).Code(); e != a { 541 t.Errorf("Expected %q, but received %q", e, a) 542 } 543 544 if e, a := err.(awserr.Error).OrigErr().Error(), "random failure"; e != a { 545 t.Errorf("Expected %q, but received %q", e, a) 546 } 547 548 if e, a := []string{}, *ops; !reflect.DeepEqual(e, a) { 549 t.Errorf("Expected %v, but received %v", e, a) 550 } 551 } 552 553 func TestUploadOrderReadFail2(t *testing.T) { 554 s, ops, _ := loggingSvc([]string{"UploadPart"}) 555 mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) { 556 u.Concurrency = 1 557 }) 558 _, err := mgr.Upload(&s3manager.UploadInput{ 559 Bucket: aws.String("Bucket"), 560 Key: aws.String("Key"), 561 Body: &failreader{times: 2}, 562 }) 563 564 if e, a := "MultipartUpload", err.(awserr.Error).Code(); e != a { 565 t.Errorf("Expected %q, but received %q", e, a) 566 } 567 568 if e, a := "ReadRequestBody", err.(awserr.Error).OrigErr().(awserr.Error).Code(); e != a { 569 t.Errorf("Expected %q, but received %q", e, a) 570 } 571 572 if errStr := err.(awserr.Error).OrigErr().Error(); !strings.Contains(errStr, "random failure") { 573 t.Errorf("Expected error to contains 'random failure', but was %q", errStr) 574 } 575 576 if e, a := []string{"CreateMultipartUpload", "AbortMultipartUpload"}, *ops; !reflect.DeepEqual(e, a) { 577 t.Errorf("Expected %v, but receievd %v", e, a) 578 } 579 } 580 581 type sizedReader struct { 582 size int 583 cur int 584 err error 585 } 586 587 func (s *sizedReader) Read(p []byte) (n int, err error) { 588 if s.cur >= s.size { 589 if s.err == nil { 590 s.err = io.EOF 591 } 592 return 0, s.err 593 } 594 595 n = len(p) 596 s.cur += len(p) 597 if s.cur > s.size { 598 n -= s.cur - s.size 599 } 600 601 return n, err 602 } 603 604 func TestUploadOrderMultiBufferedReader(t *testing.T) { 605 s, ops, args := loggingSvc(emptyList) 606 mgr := s3manager.NewUploaderWithClient(s) 607 _, err := mgr.Upload(&s3manager.UploadInput{ 608 Bucket: aws.String("Bucket"), 609 Key: aws.String("Key"), 610 Body: &sizedReader{size: 1024 * 1024 * 12}, 611 }) 612 613 if err != nil { 614 t.Errorf("Expected no error but received %v", err) 615 } 616 617 if e, a := []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops; !reflect.DeepEqual(e, a) { 618 t.Errorf("Expected %v, but receievd %v", e, a) 619 } 620 621 // Part lengths 622 parts := []int{ 623 buflen(val((*args)[1], "Body")), 624 buflen(val((*args)[2], "Body")), 625 buflen(val((*args)[3], "Body")), 626 } 627 sort.Ints(parts) 628 629 if e, a := []int{1024 * 1024 * 2, 1024 * 1024 * 5, 1024 * 1024 * 5}, parts; !reflect.DeepEqual(e, a) { 630 t.Errorf("Expected %v, but receievd %v", e, a) 631 } 632 } 633 634 func TestUploadOrderMultiBufferedReaderPartial(t *testing.T) { 635 s, ops, args := loggingSvc(emptyList) 636 mgr := s3manager.NewUploaderWithClient(s) 637 _, err := mgr.Upload(&s3manager.UploadInput{ 638 Bucket: aws.String("Bucket"), 639 Key: aws.String("Key"), 640 Body: &sizedReader{size: 1024 * 1024 * 12, err: io.EOF}, 641 }) 642 643 if err != nil { 644 t.Errorf("Expected no error but received %v", err) 645 } 646 647 if e, a := []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops; !reflect.DeepEqual(e, a) { 648 t.Errorf("Expected %v, but receievd %v", e, a) 649 } 650 651 // Part lengths 652 parts := []int{ 653 buflen(val((*args)[1], "Body")), 654 buflen(val((*args)[2], "Body")), 655 buflen(val((*args)[3], "Body")), 656 } 657 sort.Ints(parts) 658 659 if e, a := []int{1024 * 1024 * 2, 1024 * 1024 * 5, 1024 * 1024 * 5}, parts; !reflect.DeepEqual(e, a) { 660 t.Errorf("Expected %v, but receievd %v", e, a) 661 } 662 } 663 664 // TestUploadOrderMultiBufferedReaderEOF tests the edge case where the 665 // file size is the same as part size. 666 func TestUploadOrderMultiBufferedReaderEOF(t *testing.T) { 667 s, ops, args := loggingSvc(emptyList) 668 mgr := s3manager.NewUploaderWithClient(s) 669 _, err := mgr.Upload(&s3manager.UploadInput{ 670 Bucket: aws.String("Bucket"), 671 Key: aws.String("Key"), 672 Body: &sizedReader{size: 1024 * 1024 * 10, err: io.EOF}, 673 }) 674 675 if err != nil { 676 t.Errorf("Expected no error but received %v", err) 677 } 678 679 if e, a := []string{"CreateMultipartUpload", "UploadPart", "UploadPart", "CompleteMultipartUpload"}, *ops; !reflect.DeepEqual(e, a) { 680 t.Errorf("Expected %v, but receievd %v", e, a) 681 } 682 683 // Part lengths 684 parts := []int{ 685 buflen(val((*args)[1], "Body")), 686 buflen(val((*args)[2], "Body")), 687 } 688 sort.Ints(parts) 689 690 if e, a := []int{1024 * 1024 * 5, 1024 * 1024 * 5}, parts; !reflect.DeepEqual(e, a) { 691 t.Errorf("Expected %v, but receievd %v", e, a) 692 } 693 } 694 695 func TestUploadOrderMultiBufferedReaderExceedTotalParts(t *testing.T) { 696 s, ops, _ := loggingSvc([]string{"UploadPart"}) 697 mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) { 698 u.Concurrency = 1 699 u.MaxUploadParts = 2 700 }) 701 resp, err := mgr.Upload(&s3manager.UploadInput{ 702 Bucket: aws.String("Bucket"), 703 Key: aws.String("Key"), 704 Body: &sizedReader{size: 1024 * 1024 * 12}, 705 }) 706 707 if err == nil { 708 t.Error("Expected an error, but received nil") 709 } 710 711 if resp != nil { 712 t.Errorf("Expected nil, but receievd %v", resp) 713 } 714 715 if e, a := []string{"CreateMultipartUpload", "AbortMultipartUpload"}, *ops; !reflect.DeepEqual(e, a) { 716 t.Errorf("Expected %v, but receievd %v", e, a) 717 } 718 719 aerr := err.(awserr.Error) 720 if e, a := "MultipartUpload", aerr.Code(); e != a { 721 t.Errorf("Expected %q, but received %q", e, a) 722 } 723 724 if e, a := "TotalPartsExceeded", aerr.OrigErr().(awserr.Error).Code(); e != a { 725 t.Errorf("Expected %q, but received %q", e, a) 726 } 727 728 if !strings.Contains(aerr.Error(), "configured MaxUploadParts (2)") { 729 t.Errorf("Expected error to contain 'configured MaxUploadParts (2)', but receievd %q", aerr.Error()) 730 } 731 } 732 733 func TestUploadOrderSingleBufferedReader(t *testing.T) { 734 s, ops, _ := loggingSvc(emptyList) 735 mgr := s3manager.NewUploaderWithClient(s) 736 resp, err := mgr.Upload(&s3manager.UploadInput{ 737 Bucket: aws.String("Bucket"), 738 Key: aws.String("Key"), 739 Body: &sizedReader{size: 1024 * 1024 * 2}, 740 }) 741 742 if err != nil { 743 t.Errorf("Expected no error but received %v", err) 744 } 745 746 if e, a := []string{"PutObject"}, *ops; !reflect.DeepEqual(e, a) { 747 t.Errorf("Expected %v, but received %v", e, a) 748 } 749 750 if len(resp.Location) == 0 { 751 t.Error("Expected a value in Location but received empty string") 752 } 753 754 if len(resp.UploadID) > 0 { 755 t.Errorf("Expected empty string but received %q", resp.UploadID) 756 } 757 } 758 759 func TestUploadZeroLenObject(t *testing.T) { 760 requestMade := false 761 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 762 requestMade = true 763 w.WriteHeader(http.StatusOK) 764 })) 765 defer server.Close() 766 mgr := s3manager.NewUploaderWithClient(s3.New(unit.Session, &aws.Config{ 767 Endpoint: aws.String(server.URL), 768 })) 769 resp, err := mgr.Upload(&s3manager.UploadInput{ 770 Bucket: aws.String("Bucket"), 771 Key: aws.String("Key"), 772 Body: strings.NewReader(""), 773 }) 774 775 if err != nil { 776 t.Errorf("Expected no error but received %v", err) 777 } 778 if !requestMade { 779 t.Error("Expected request to have been made, but was not") 780 } 781 782 if len(resp.Location) == 0 { 783 t.Error("Expected a non-empty string value for Location") 784 } 785 786 if len(resp.UploadID) > 0 { 787 t.Errorf("Expected empty string, but received %q", resp.UploadID) 788 } 789 } 790 791 func TestUploadInputS3PutObjectInputPairity(t *testing.T) { 792 matchings := compareStructType(reflect.TypeOf(s3.PutObjectInput{}), 793 reflect.TypeOf(s3manager.UploadInput{})) 794 aOnly := []string{} 795 bOnly := []string{} 796 797 for k, c := range matchings { 798 if c == 1 && k != "ContentLength" { 799 aOnly = append(aOnly, k) 800 } else if c == 2 { 801 bOnly = append(bOnly, k) 802 } 803 } 804 805 if len(aOnly) > 0 { 806 t.Errorf("Expected empty array, but received %v", aOnly) 807 } 808 809 if len(bOnly) > 0 { 810 t.Errorf("Expected empty array, but received %v", bOnly) 811 } 812 } 813 814 type testIncompleteReader struct { 815 Size int64 816 read int64 817 } 818 819 func (r *testIncompleteReader) Read(p []byte) (n int, err error) { 820 r.read += int64(len(p)) 821 if r.read >= r.Size { 822 return int(r.read - r.Size), io.ErrUnexpectedEOF 823 } 824 return len(p), nil 825 } 826 827 func TestUploadUnexpectedEOF(t *testing.T) { 828 s, ops, _ := loggingSvc(emptyList) 829 mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) { 830 u.Concurrency = 1 831 u.PartSize = s3manager.MinUploadPartSize 832 }) 833 _, err := mgr.Upload(&s3manager.UploadInput{ 834 Bucket: aws.String("Bucket"), 835 Key: aws.String("Key"), 836 Body: &testIncompleteReader{ 837 Size: int64(s3manager.MinUploadPartSize + 1), 838 }, 839 }) 840 if err == nil { 841 t.Error("Expected error, but received none") 842 } 843 844 // Ensure upload started. 845 if e, a := "CreateMultipartUpload", (*ops)[0]; e != a { 846 t.Errorf("Expected %q, but received %q", e, a) 847 } 848 849 // Part may or may not be sent because of timing of sending parts and 850 // reading next part in upload manager. Just check for the last abort. 851 if e, a := "AbortMultipartUpload", (*ops)[len(*ops)-1]; e != a { 852 t.Errorf("Expected %q, but received %q", e, a) 853 } 854 } 855 856 func compareStructType(a, b reflect.Type) map[string]int { 857 if a.Kind() != reflect.Struct || b.Kind() != reflect.Struct { 858 panic(fmt.Sprintf("types must both be structs, got %v and %v", a.Kind(), b.Kind())) 859 } 860 861 aFields := enumFields(a) 862 bFields := enumFields(b) 863 864 matchings := map[string]int{} 865 866 for i := 0; i < len(aFields) || i < len(bFields); i++ { 867 if i < len(aFields) { 868 c := matchings[aFields[i].Name] 869 matchings[aFields[i].Name] = c + 1 870 } 871 if i < len(bFields) { 872 c := matchings[bFields[i].Name] 873 matchings[bFields[i].Name] = c + 2 874 } 875 } 876 877 return matchings 878 } 879 880 func enumFields(v reflect.Type) []reflect.StructField { 881 fields := []reflect.StructField{} 882 883 for i := 0; i < v.NumField(); i++ { 884 field := v.Field(i) 885 // Ignoreing anon fields 886 if field.PkgPath != "" { 887 // Ignore unexported fields 888 continue 889 } 890 891 fields = append(fields, field) 892 } 893 894 return fields 895 } 896 897 type fooReaderAt struct{} 898 899 func (r *fooReaderAt) Read(p []byte) (n int, err error) { 900 return 12, io.EOF 901 } 902 903 func (r *fooReaderAt) ReadAt(p []byte, off int64) (n int, err error) { 904 return 12, io.EOF 905 } 906 907 func TestReaderAt(t *testing.T) { 908 svc := s3.New(unit.Session) 909 svc.Handlers.Unmarshal.Clear() 910 svc.Handlers.UnmarshalMeta.Clear() 911 svc.Handlers.UnmarshalError.Clear() 912 svc.Handlers.Send.Clear() 913 914 contentLen := "" 915 svc.Handlers.Send.PushBack(func(r *request.Request) { 916 contentLen = r.HTTPRequest.Header.Get("Content-Length") 917 r.HTTPResponse = &http.Response{ 918 StatusCode: 200, 919 Body: ioutil.NopCloser(bytes.NewReader([]byte{})), 920 } 921 }) 922 923 mgr := s3manager.NewUploaderWithClient(svc, func(u *s3manager.Uploader) { 924 u.Concurrency = 1 925 }) 926 927 _, err := mgr.Upload(&s3manager.UploadInput{ 928 Bucket: aws.String("Bucket"), 929 Key: aws.String("Key"), 930 Body: &fooReaderAt{}, 931 }) 932 933 if err != nil { 934 t.Errorf("Expected no error but received %v", err) 935 } 936 937 if e, a := "12", contentLen; e != a { 938 t.Errorf("Expected %q, but received %q", e, a) 939 } 940 } 941 942 func TestSSE(t *testing.T) { 943 svc := s3.New(unit.Session) 944 svc.Handlers.Unmarshal.Clear() 945 svc.Handlers.UnmarshalMeta.Clear() 946 svc.Handlers.UnmarshalError.Clear() 947 svc.Handlers.ValidateResponse.Clear() 948 svc.Handlers.Send.Clear() 949 partNum := 0 950 mutex := &sync.Mutex{} 951 952 svc.Handlers.Send.PushBack(func(r *request.Request) { 953 mutex.Lock() 954 defer mutex.Unlock() 955 r.HTTPResponse = &http.Response{ 956 StatusCode: 200, 957 Body: ioutil.NopCloser(bytes.NewReader([]byte(respMsg))), 958 } 959 switch data := r.Data.(type) { 960 case *s3.CreateMultipartUploadOutput: 961 data.UploadId = aws.String("UPLOAD-ID") 962 case *s3.UploadPartOutput: 963 input := r.Params.(*s3.UploadPartInput) 964 if input.SSECustomerAlgorithm == nil { 965 t.Fatal("SSECustomerAlgoritm should not be nil") 966 } 967 if input.SSECustomerKey == nil { 968 t.Fatal("SSECustomerKey should not be nil") 969 } 970 partNum++ 971 data.ETag = aws.String(fmt.Sprintf("ETAG%d", partNum)) 972 case *s3.CompleteMultipartUploadOutput: 973 data.Location = aws.String("https://location") 974 data.VersionId = aws.String("VERSION-ID") 975 case *s3.PutObjectOutput: 976 data.VersionId = aws.String("VERSION-ID") 977 } 978 979 }) 980 981 mgr := s3manager.NewUploaderWithClient(svc, func(u *s3manager.Uploader) { 982 u.Concurrency = 5 983 }) 984 985 _, err := mgr.Upload(&s3manager.UploadInput{ 986 Bucket: aws.String("Bucket"), 987 Key: aws.String("Key"), 988 SSECustomerAlgorithm: aws.String("AES256"), 989 SSECustomerKey: aws.String("foo"), 990 Body: bytes.NewBuffer(make([]byte, 1024*1024*10)), 991 }) 992 993 if err != nil { 994 t.Fatal("Expected no error, but received" + err.Error()) 995 } 996 } 997 998 func TestUploadWithContextCanceled(t *testing.T) { 999 u := s3manager.NewUploader(unit.Session) 1000 1001 params := s3manager.UploadInput{ 1002 Bucket: aws.String("Bucket"), 1003 Key: aws.String("Key"), 1004 Body: bytes.NewReader(make([]byte, 0)), 1005 } 1006 1007 ctx := &awstesting.FakeContext{DoneCh: make(chan struct{})} 1008 ctx.Error = fmt.Errorf("context canceled") 1009 close(ctx.DoneCh) 1010 1011 _, err := u.UploadWithContext(ctx, ¶ms) 1012 if err == nil { 1013 t.Fatalf("expected error, did not get one") 1014 } 1015 aerr := err.(awserr.Error) 1016 if e, a := request.CanceledErrorCode, aerr.Code(); e != a { 1017 t.Errorf("expected error code %q, got %q", e, a) 1018 } 1019 if e, a := "canceled", aerr.Message(); !strings.Contains(a, e) { 1020 t.Errorf("expected error message to contain %q, but did not %q", e, a) 1021 } 1022 } 1023 1024 // S3 Uploader incorrectly fails an upload if the content being uploaded 1025 // has a size of MinPartSize * MaxUploadParts. 1026 // Github: aws/aws-sdk-go#2557 1027 func TestUploadMaxPartsEOF(t *testing.T) { 1028 s, ops, _ := loggingSvc(emptyList) 1029 mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) { 1030 u.Concurrency = 1 1031 u.PartSize = s3manager.DefaultUploadPartSize 1032 u.MaxUploadParts = 2 1033 }) 1034 f := bytes.NewReader(make([]byte, int(mgr.PartSize)*mgr.MaxUploadParts)) 1035 1036 r1 := io.NewSectionReader(f, 0, s3manager.DefaultUploadPartSize) 1037 r2 := io.NewSectionReader(f, s3manager.DefaultUploadPartSize, 2*s3manager.DefaultUploadPartSize) 1038 body := io.MultiReader(r1, r2) 1039 1040 _, err := mgr.Upload(&s3manager.UploadInput{ 1041 Bucket: aws.String("Bucket"), 1042 Key: aws.String("Key"), 1043 Body: body, 1044 }) 1045 1046 if err != nil { 1047 t.Fatalf("expect no error, got %v", err) 1048 } 1049 1050 expectOps := []string{ 1051 "CreateMultipartUpload", 1052 "UploadPart", 1053 "UploadPart", 1054 "CompleteMultipartUpload", 1055 } 1056 if e, a := expectOps, *ops; !reflect.DeepEqual(e, a) { 1057 t.Errorf("expect %v ops, got %v", e, a) 1058 } 1059 } 1060 1061 func createTempFile(t *testing.T, size int64) (*os.File, func(*testing.T), error) { 1062 file, err := ioutil.TempFile(os.TempDir(), aws.SDKName+t.Name()) 1063 if err != nil { 1064 return nil, nil, err 1065 } 1066 filename := file.Name() 1067 if err := file.Truncate(size); err != nil { 1068 return nil, nil, err 1069 } 1070 1071 return file, 1072 func(t *testing.T) { 1073 if err := file.Close(); err != nil { 1074 t.Errorf("failed to close temp file, %s, %v", filename, err) 1075 } 1076 if err := os.Remove(filename); err != nil { 1077 t.Errorf("failed to remove temp file, %s, %v", filename, err) 1078 } 1079 }, 1080 nil 1081 } 1082 1083 func buildFailHandlers(tb testing.TB, parts, retry int) []http.Handler { 1084 handlers := make([]http.Handler, parts) 1085 for i := 0; i < len(handlers); i++ { 1086 handlers[i] = &failPartHandler{ 1087 tb: tb, 1088 failsRemaining: retry, 1089 successHandler: successPartHandler{tb: tb}, 1090 } 1091 } 1092 1093 return handlers 1094 } 1095 1096 func TestUploadRetry(t *testing.T) { 1097 const numParts, retries = 3, 10 1098 1099 testFile, testFileCleanup, err := createTempFile(t, s3manager.DefaultUploadPartSize*numParts) 1100 if err != nil { 1101 t.Fatalf("failed to create test file, %v", err) 1102 } 1103 defer testFileCleanup(t) 1104 1105 cases := map[string]struct { 1106 Body io.Reader 1107 PartHandlers func(testing.TB) []http.Handler 1108 }{ 1109 "bytes.Buffer": { 1110 Body: bytes.NewBuffer(make([]byte, s3manager.DefaultUploadPartSize*numParts)), 1111 PartHandlers: func(tb testing.TB) []http.Handler { 1112 return buildFailHandlers(tb, numParts, retries) 1113 }, 1114 }, 1115 "bytes.Reader": { 1116 Body: bytes.NewReader(make([]byte, s3manager.DefaultUploadPartSize*numParts)), 1117 PartHandlers: func(tb testing.TB) []http.Handler { 1118 return buildFailHandlers(tb, numParts, retries) 1119 }, 1120 }, 1121 "os.File": { 1122 Body: testFile, 1123 PartHandlers: func(tb testing.TB) []http.Handler { 1124 return buildFailHandlers(tb, numParts, retries) 1125 }, 1126 }, 1127 } 1128 1129 for name, c := range cases { 1130 t.Run(name, func(t *testing.T) { 1131 mux := newMockS3UploadServer(t, c.PartHandlers(t)) 1132 server := httptest.NewServer(mux) 1133 defer server.Close() 1134 1135 var logger aws.Logger 1136 var logLevel *aws.LogLevelType 1137 if v := os.Getenv("DEBUG_BODY"); len(v) != 0 { 1138 logger = t 1139 logLevel = aws.LogLevel( 1140 aws.LogDebugWithRequestErrors | aws.LogDebugWithRequestRetries, 1141 ) 1142 } 1143 sess := unit.Session.Copy(&aws.Config{ 1144 Endpoint: aws.String(server.URL), 1145 S3ForcePathStyle: aws.Bool(true), 1146 DisableSSL: aws.Bool(true), 1147 MaxRetries: aws.Int(retries + 1), 1148 SleepDelay: func(time.Duration) {}, 1149 1150 Logger: logger, 1151 LogLevel: logLevel, 1152 //Credentials: credentials.AnonymousCredentials, 1153 }) 1154 1155 uploader := s3manager.NewUploader(sess, func(u *s3manager.Uploader) { 1156 // u.Concurrency = 1 1157 }) 1158 _, err := uploader.Upload(&s3manager.UploadInput{ 1159 Bucket: aws.String("bucket"), 1160 Key: aws.String("key"), 1161 Body: c.Body, 1162 }) 1163 1164 if err != nil { 1165 t.Fatalf("expect no error, got %v", err) 1166 } 1167 }) 1168 } 1169 } 1170 1171 func TestUploadBufferStrategy(t *testing.T) { 1172 cases := map[string]struct { 1173 PartSize int64 1174 Size int64 1175 Strategy s3manager.ReadSeekerWriteToProvider 1176 callbacks int 1177 }{ 1178 "NoBuffer": { 1179 PartSize: s3manager.DefaultUploadPartSize, 1180 Strategy: nil, 1181 }, 1182 "SinglePart": { 1183 PartSize: s3manager.DefaultUploadPartSize, 1184 Size: s3manager.DefaultUploadPartSize, 1185 Strategy: &recordedBufferProvider{size: int(s3manager.DefaultUploadPartSize)}, 1186 callbacks: 1, 1187 }, 1188 "MultiPart": { 1189 PartSize: s3manager.DefaultUploadPartSize, 1190 Size: s3manager.DefaultUploadPartSize * 2, 1191 Strategy: &recordedBufferProvider{size: int(s3manager.DefaultUploadPartSize)}, 1192 callbacks: 2, 1193 }, 1194 } 1195 1196 for name, tCase := range cases { 1197 t.Run(name, func(t *testing.T) { 1198 _ = tCase 1199 sess := unit.Session.Copy() 1200 svc := s3.New(sess) 1201 svc.Handlers.Unmarshal.Clear() 1202 svc.Handlers.UnmarshalMeta.Clear() 1203 svc.Handlers.UnmarshalError.Clear() 1204 svc.Handlers.Send.Clear() 1205 svc.Handlers.Send.PushBack(func(r *request.Request) { 1206 if r.Body != nil { 1207 io.Copy(ioutil.Discard, r.Body) 1208 } 1209 1210 r.HTTPResponse = &http.Response{ 1211 StatusCode: 200, 1212 Body: ioutil.NopCloser(bytes.NewReader([]byte(respMsg))), 1213 } 1214 1215 switch data := r.Data.(type) { 1216 case *s3.CreateMultipartUploadOutput: 1217 data.UploadId = aws.String("UPLOAD-ID") 1218 case *s3.UploadPartOutput: 1219 data.ETag = aws.String(fmt.Sprintf("ETAG%d", random.Int())) 1220 case *s3.CompleteMultipartUploadOutput: 1221 data.Location = aws.String("https://location") 1222 data.VersionId = aws.String("VERSION-ID") 1223 case *s3.PutObjectOutput: 1224 data.VersionId = aws.String("VERSION-ID") 1225 } 1226 }) 1227 1228 uploader := s3manager.NewUploaderWithClient(svc, func(u *s3manager.Uploader) { 1229 u.PartSize = tCase.PartSize 1230 u.BufferProvider = tCase.Strategy 1231 u.Concurrency = 1 1232 }) 1233 1234 expected := s3testing.GetTestBytes(int(tCase.Size)) 1235 _, err := uploader.Upload(&s3manager.UploadInput{ 1236 Bucket: aws.String("bucket"), 1237 Key: aws.String("key"), 1238 Body: bytes.NewReader(expected), 1239 }) 1240 if err != nil { 1241 t.Fatalf("failed to upload file: %v", err) 1242 } 1243 1244 switch strat := tCase.Strategy.(type) { 1245 case *recordedBufferProvider: 1246 if !bytes.Equal(expected, strat.content) { 1247 t.Errorf("content buffered did not match expected") 1248 } 1249 if tCase.callbacks != strat.callbackCount { 1250 t.Errorf("expected %v, got %v callbacks", tCase.callbacks, strat.callbackCount) 1251 } 1252 } 1253 }) 1254 } 1255 } 1256 1257 func TestUploaderValidARN(t *testing.T) { 1258 cases := map[string]struct { 1259 input s3manager.UploadInput 1260 wantErr bool 1261 }{ 1262 "standard bucket": { 1263 input: s3manager.UploadInput{ 1264 Bucket: aws.String("test-bucket"), 1265 Key: aws.String("test-key"), 1266 Body: bytes.NewReader([]byte("test body content")), 1267 }, 1268 }, 1269 "accesspoint": { 1270 input: s3manager.UploadInput{ 1271 Bucket: aws.String("arn:aws:s3:us-west-2:123456789012:accesspoint/myap"), 1272 Key: aws.String("test-key"), 1273 Body: bytes.NewReader([]byte("test body content")), 1274 }, 1275 }, 1276 "outpost accesspoint": { 1277 input: s3manager.UploadInput{ 1278 Bucket: aws.String("arn:aws:s3-outposts:us-west-2:012345678901:outpost/op-1234567890123456/accesspoint/myaccesspoint"), 1279 Key: aws.String("test-key"), 1280 Body: bytes.NewReader([]byte("test body content")), 1281 }, 1282 }, 1283 "s3-object-lambda accesspoint": { 1284 input: s3manager.UploadInput{ 1285 Bucket: aws.String("arn:aws:s3-object-lambda:us-west-2:123456789012:accesspoint/myap"), 1286 Key: aws.String("test-key"), 1287 Body: bytes.NewReader([]byte("test body content")), 1288 }, 1289 wantErr: true, 1290 }, 1291 } 1292 1293 for name, tt := range cases { 1294 t.Run(name, func(t *testing.T) { 1295 client, _, _ := loggingSvc(nil) 1296 client.Config.Region = aws.String("us-west-2") 1297 client.ClientInfo.SigningRegion = "us-west-2" 1298 1299 uploader := s3manager.NewUploaderWithClient(client) 1300 1301 _, err := uploader.Upload(&tt.input) 1302 if (err != nil) != tt.wantErr { 1303 t.Errorf("err: %v, wantErr: %v", err, tt.wantErr) 1304 } 1305 }) 1306 } 1307 } 1308 1309 type mockS3UploadServer struct { 1310 *http.ServeMux 1311 1312 tb testing.TB 1313 partHandler []http.Handler 1314 } 1315 1316 func newMockS3UploadServer(tb testing.TB, partHandler []http.Handler) *mockS3UploadServer { 1317 s := &mockS3UploadServer{ 1318 ServeMux: http.NewServeMux(), 1319 partHandler: partHandler, 1320 tb: tb, 1321 } 1322 1323 s.HandleFunc("/", s.handleRequest) 1324 1325 return s 1326 } 1327 1328 func (s mockS3UploadServer) handleRequest(w http.ResponseWriter, r *http.Request) { 1329 defer r.Body.Close() 1330 1331 _, hasUploads := r.URL.Query()["uploads"] 1332 1333 switch { 1334 case r.Method == "POST" && hasUploads: 1335 // CreateMultipartUpload 1336 w.Header().Set("Content-Length", strconv.Itoa(len(createUploadResp))) 1337 w.Write([]byte(createUploadResp)) 1338 1339 case r.Method == "PUT": 1340 // UploadPart 1341 partNumStr := r.URL.Query().Get("partNumber") 1342 id, err := strconv.Atoi(partNumStr) 1343 if err != nil { 1344 failRequest(w, 400, "BadRequest", 1345 fmt.Sprintf("unable to parse partNumber, %q, %v", 1346 partNumStr, err)) 1347 return 1348 } 1349 id-- 1350 if id < 0 || id >= len(s.partHandler) { 1351 failRequest(w, 400, "BadRequest", 1352 fmt.Sprintf("invalid partNumber %v", id)) 1353 return 1354 } 1355 s.partHandler[id].ServeHTTP(w, r) 1356 1357 case r.Method == "POST": 1358 // CompleteMultipartUpload 1359 w.Header().Set("Content-Length", strconv.Itoa(len(completeUploadResp))) 1360 w.Write([]byte(completeUploadResp)) 1361 1362 case r.Method == "DELETE": 1363 // AbortMultipartUpload 1364 w.Header().Set("Content-Length", strconv.Itoa(len(abortUploadResp))) 1365 w.WriteHeader(200) 1366 w.Write([]byte(abortUploadResp)) 1367 1368 default: 1369 failRequest(w, 400, "BadRequest", 1370 fmt.Sprintf("invalid request %v %v", r.Method, r.URL)) 1371 } 1372 } 1373 1374 func failRequest(w http.ResponseWriter, status int, code, msg string) { 1375 msg = fmt.Sprintf(baseRequestErrorResp, code, msg) 1376 w.Header().Set("Content-Length", strconv.Itoa(len(msg))) 1377 w.WriteHeader(status) 1378 w.Write([]byte(msg)) 1379 } 1380 1381 type successPartHandler struct { 1382 tb testing.TB 1383 } 1384 1385 func (h successPartHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 1386 defer r.Body.Close() 1387 1388 n, err := io.Copy(ioutil.Discard, r.Body) 1389 if err != nil { 1390 failRequest(w, 400, "BadRequest", 1391 fmt.Sprintf("failed to read body, %v", err)) 1392 return 1393 } 1394 1395 contLenStr := r.Header.Get("Content-Length") 1396 expectLen, err := strconv.ParseInt(contLenStr, 10, 64) 1397 if err != nil { 1398 h.tb.Logf("expect content-length, got %q, %v", contLenStr, err) 1399 failRequest(w, 400, "BadRequest", 1400 fmt.Sprintf("unable to get content-length %v", err)) 1401 return 1402 } 1403 if e, a := expectLen, n; e != a { 1404 h.tb.Logf("expect %v read, got %v", e, a) 1405 failRequest(w, 400, "BadRequest", 1406 fmt.Sprintf( 1407 "content-length and body do not match, %v, %v", e, a)) 1408 return 1409 } 1410 1411 w.Header().Set("Content-Length", strconv.Itoa(len(uploadPartResp))) 1412 w.Write([]byte(uploadPartResp)) 1413 } 1414 1415 type failPartHandler struct { 1416 tb testing.TB 1417 1418 failsRemaining int 1419 successHandler http.Handler 1420 } 1421 1422 func (h *failPartHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 1423 defer r.Body.Close() 1424 1425 if h.failsRemaining == 0 && h.successHandler != nil { 1426 h.successHandler.ServeHTTP(w, r) 1427 return 1428 } 1429 1430 io.Copy(ioutil.Discard, r.Body) 1431 1432 failRequest(w, 500, "InternalException", 1433 fmt.Sprintf("mock error, partNumber %v", r.URL.Query().Get("partNumber"))) 1434 1435 h.failsRemaining-- 1436 } 1437 1438 type recordedBufferProvider struct { 1439 content []byte 1440 size int 1441 callbackCount int 1442 } 1443 1444 func (r *recordedBufferProvider) GetWriteTo(seeker io.ReadSeeker) (s3manager.ReadSeekerWriteTo, func()) { 1445 b := make([]byte, r.size) 1446 w := &s3manager.BufferedReadSeekerWriteTo{BufferedReadSeeker: s3manager.NewBufferedReadSeeker(seeker, b)} 1447 1448 return w, func() { 1449 r.content = append(r.content, b...) 1450 r.callbackCount++ 1451 } 1452 } 1453 1454 const createUploadResp = ` 1455 <CreateMultipartUploadResponse> 1456 <Bucket>bucket</Bucket> 1457 <Key>key</Key> 1458 <UploadId>abc123</UploadId> 1459 </CreateMultipartUploadResponse> 1460 ` 1461 const uploadPartResp = ` 1462 <UploadPartResponse> 1463 <ETag>key</ETag> 1464 </UploadPartResponse> 1465 ` 1466 const baseRequestErrorResp = ` 1467 <Error> 1468 <Code>%s</Code> 1469 <Message>%s</Message> 1470 <RequestId>request-id</RequestId> 1471 <HostId>host-id</HostId> 1472 </Error> 1473 ` 1474 const completeUploadResp = ` 1475 <CompleteMultipartUploadResponse> 1476 <Bucket>bucket</Bucket> 1477 <Key>key</Key> 1478 <ETag>key</ETag> 1479 <Location>https://bucket.us-west-2.amazonaws.com/key</Location> 1480 <UploadId>abc123</UploadId> 1481 </CompleteMultipartUploadResponse> 1482 ` 1483 1484 const abortUploadResp = ` 1485 <AbortMultipartUploadResponse> 1486 </AbortMultipartUploadResponse> 1487 `