Skip to content

Commit 2010860

Browse files
skotambkarjasdel
authored andcommitted
service/s3: Fixes incorrect EOF error by s3manager (#386)
Fixes incorrectly thrown EOF error by s3manager, when upload is performed. Fixes #316
1 parent 2cdb4e5 commit 2010860

File tree

3 files changed

+63
-18
lines changed

3 files changed

+63
-18
lines changed

CHANGELOG_PENDING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,8 @@
2222
* `aws` : Adds missing sdk error checking when seeking readers ([#379](https://github.com/aws/aws-sdk-go-v2/pull/379))
2323
* Adds support for nonseekable io.Reader. Adds support for streamed payloads for unsigned body request.
2424
* Fixes [#371](https://github.com/aws/aws-sdk-go-v2/issues/371)
25+
* `service/s3` : Fixes unexpected EOF error by s3manager ([#386](https://github.com/aws/aws-sdk-go-v2/pull/386))
26+
* Fixes bug which threw unexpected EOF error when s3 upload is performed for a file of maximum allowed size
27+
* Fixes [#316](https://github.com/aws/aws-sdk-go-v2/issues/316)
28+
2529

service/s3/s3manager/upload.go

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,13 @@ type Uploader struct {
144144
// MaxUploadParts is the max number of parts which will be uploaded to S3.
145145
// Will be used to calculate the partsize of the object to be uploaded.
146146
// E.g: 5GB file, with MaxUploadParts set to 100, will upload the file
147-
// as 100, 50MB parts.
148-
// With a limited of s3.MaxUploadParts (10,000 parts).
147+
// as 100, 50MB parts. With a limited of s3.MaxUploadParts (10,000 parts).
148+
//
149+
// MaxUploadParts must not be used to limit the total number of bytes uploaded.
150+
// Use a type like to io.LimitReader (https://golang.org/pkg/io/#LimitedReader)
151+
// instead. An io.LimitReader is helpful when uploading an unbounded reader
152+
// to S3, and you know its maximum size. Otherwise the reader's io.EOF returned
153+
// error must be used to signal end of stream.
149154
MaxUploadParts int
150155

151156
// The client to use when uploading to S3.
@@ -532,21 +537,6 @@ func (u *multiuploader) upload(firstBuf io.ReadSeeker) (*UploadOutput, error) {
532537

533538
// Read and queue the rest of the parts
534539
for u.geterr() == nil && err == nil {
535-
num++
536-
// This upload exceeded maximum number of supported parts, error now.
537-
if num > int64(u.cfg.MaxUploadParts) || num > int64(MaxUploadParts) {
538-
var msg string
539-
if num > int64(u.cfg.MaxUploadParts) {
540-
msg = fmt.Sprintf("exceeded total allowed configured MaxUploadParts (%d). Adjust PartSize to fit in this limit",
541-
u.cfg.MaxUploadParts)
542-
} else {
543-
msg = fmt.Sprintf("exceeded total allowed S3 limit MaxUploadParts (%d). Adjust PartSize to fit in this limit",
544-
MaxUploadParts)
545-
}
546-
u.seterr(awserr.New("TotalPartsExceeded", msg, nil))
547-
break
548-
}
549-
550540
var reader io.ReadSeeker
551541
var nextChunkLen int
552542
reader, nextChunkLen, err = u.nextReader()
@@ -566,6 +556,21 @@ func (u *multiuploader) upload(firstBuf io.ReadSeeker) (*UploadOutput, error) {
566556
break
567557
}
568558

559+
num++
560+
// This upload exceeded maximum number of supported parts, error now.
561+
if num > int64(u.cfg.MaxUploadParts) || num > int64(MaxUploadParts) {
562+
var msg string
563+
if num > int64(u.cfg.MaxUploadParts) {
564+
msg = fmt.Sprintf("exceeded total allowed configured MaxUploadParts (%d). Adjust PartSize to fit in this limit",
565+
u.cfg.MaxUploadParts)
566+
} else {
567+
msg = fmt.Sprintf("exceeded total allowed S3 limit MaxUploadParts (%d). Adjust PartSize to fit in this limit",
568+
MaxUploadParts)
569+
}
570+
u.seterr(awserr.New("TotalPartsExceeded", msg, nil))
571+
break
572+
}
573+
569574
ch <- chunk{buf: reader, num: num}
570575
}
571576

service/s3/s3manager/upload_test.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
"github.com/aws/aws-sdk-go-v2/service/s3/s3manager"
2525
)
2626

27-
var emptyList = []string{}
27+
var emptyList []string
2828

2929
func val(i interface{}, s string) interface{} {
3030
v, err := awsutil.ValuesAtPath(i, s)
@@ -1005,3 +1005,39 @@ func TestUploadWithContextCanceled(t *testing.T) {
10051005
t.Errorf("expected error message to contain %q, but did not %q", e, a)
10061006
}
10071007
}
1008+
1009+
// S3 Uploader incorrectly fails an upload if the content being uploaded
1010+
// has a size of MinPartSize * MaxUploadParts.
1011+
func TestUploadMaxPartsEOF(t *testing.T) {
1012+
s, ops, _ := loggingSvc(emptyList)
1013+
mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) {
1014+
u.Concurrency = 1
1015+
u.PartSize = s3manager.DefaultUploadPartSize
1016+
u.MaxUploadParts = 2
1017+
})
1018+
f := bytes.NewReader(make([]byte, int(mgr.PartSize)*mgr.MaxUploadParts))
1019+
1020+
r1 := io.NewSectionReader(f, 0, s3manager.DefaultUploadPartSize)
1021+
r2 := io.NewSectionReader(f, s3manager.DefaultUploadPartSize, 2*s3manager.DefaultUploadPartSize)
1022+
body := io.MultiReader(r1, r2)
1023+
1024+
_, err := mgr.Upload(&s3manager.UploadInput{
1025+
Bucket: aws.String("Bucket"),
1026+
Key: aws.String("Key"),
1027+
Body: body,
1028+
})
1029+
1030+
if err != nil {
1031+
t.Fatalf("expect no error, got %v", err)
1032+
}
1033+
1034+
expectOps := []string{
1035+
"CreateMultipartUpload",
1036+
"UploadPart",
1037+
"UploadPart",
1038+
"CompleteMultipartUpload",
1039+
}
1040+
if e, a := expectOps, *ops; !reflect.DeepEqual(e, a) {
1041+
t.Errorf("expect %v ops, got %v", e, a)
1042+
}
1043+
}

0 commit comments

Comments
 (0)