Skip to content

Commit 160c5ea

Browse files
harshavardhanaminio-trusted
authored andcommitted
api: use simple streaming upload for GetObject() reader.
This change is needed to avoid the offset based ReadAt() calls which can occur if using io.SectionReader(). Optimizing this part re-implementing a sectionReader() based on Seeker() wasn't worth the complexity. This PR also fixes the bug where the streaming signature was interpreting the Read() error return in a wrong manner.
1 parent 129fe89 commit 160c5ea

2 files changed

Lines changed: 33 additions & 28 deletions

File tree

api-put-object-streaming.go

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (c Client) putObjectMultipartStream(bucketName, objectName string,
4949
// while it is going to be duck typed similar to io.ReaderAt.
5050
// It is to indicate that *minio.Object implements io.ReaderAt.
5151
// and such a functionality is used in the subsequent code path.
52-
if isObject(reader) || isReadAt(reader) || isFile(reader) {
52+
if isFile(reader) || !isObject(reader) && isReadAt(reader) {
5353
n, err = c.putObjectMultipartStreamFromReadAt(bucketName, objectName, reader.(io.ReaderAt), size, metadata, progress)
5454
} else {
5555
n, err = c.putObjectMultipartStreamNoChecksum(bucketName, objectName, reader, size, metadata, progress)
@@ -270,14 +270,8 @@ func (c Client) putObjectMultipartStreamNoChecksum(bucketName, objectName string
270270

271271
var objPart ObjectPart
272272
objPart, err = c.uploadPart(bucketName, objectName, uploadID,
273-
io.LimitReader(hookReader, partSize), partNumber,
274-
nil, nil, partSize, metadata)
275-
// For unknown size, Read EOF we break away.
276-
// We do not have to upload till totalPartsCount.
277-
if err == io.EOF && size < 0 {
278-
break
279-
}
280-
273+
io.LimitReader(hookReader, partSize),
274+
partNumber, nil, nil, partSize, metadata)
281275
if err != nil {
282276
return totalUploadedSize, err
283277
}
@@ -340,9 +334,8 @@ func (c Client) putObjectNoChecksum(bucketName, objectName string, reader io.Rea
340334
return 0, ErrEntityTooSmall(size, bucketName, objectName)
341335
}
342336
if size > 0 {
343-
readerAt, ok := reader.(io.ReaderAt)
344-
if ok {
345-
reader = io.NewSectionReader(readerAt, 0, size)
337+
if isReadAt(reader) && !isObject(reader) {
338+
reader = io.NewSectionReader(reader.(io.ReaderAt), 0, size)
346339
}
347340
}
348341

pkg/s3signer/request-signature-streaming.go

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,18 @@ func (s *StreamingReader) Read(buf []byte) (int, error) {
254254
s.chunkBufLen = 0
255255
for {
256256
n1, err := s.baseReadCloser.Read(s.chunkBuf[s.chunkBufLen:])
257-
if err == nil || err == io.ErrUnexpectedEOF {
257+
// Usually we validate `err` first, but in this case
258+
// we are validating n > 0 for the following reasons.
259+
//
260+
// 1. n > 0, err is one of io.EOF, nil (near end of stream)
261+
// A Reader returning a non-zero number of bytes at the end
262+
// of the input stream may return either err == EOF or err == nil
263+
//
264+
// 2. n == 0, err is io.EOF (actual end of stream)
265+
//
266+
// Callers should always process the n > 0 bytes returned
267+
// before considering the error err.
268+
if n1 > 0 {
258269
s.chunkBufLen += n1
259270
s.bytesRead += int64(n1)
260271

@@ -265,25 +276,26 @@ func (s *StreamingReader) Read(buf []byte) (int, error) {
265276
s.signChunk(s.chunkBufLen)
266277
break
267278
}
279+
}
280+
if err != nil {
281+
if err == io.EOF {
282+
// No more data left in baseReader - last chunk.
283+
// Done reading the last chunk from baseReader.
284+
s.done = true
285+
286+
// bytes read from baseReader different than
287+
// content length provided.
288+
if s.bytesRead != s.contentLen {
289+
return 0, fmt.Errorf("Expected %d, got %d", s.bytesRead, s.contentLen)
290+
}
268291

269-
} else if err == io.EOF {
270-
// No more data left in baseReader - last chunk.
271-
// Done reading the last chunk from baseReader.
272-
s.done = true
273-
274-
// bytes read from baseReader different than
275-
// content length provided.
276-
if s.bytesRead != s.contentLen {
277-
return 0, io.ErrUnexpectedEOF
292+
// Sign the chunk and write it to s.buf.
293+
s.signChunk(0)
294+
break
278295
}
279-
280-
// Sign the chunk and write it to s.buf.
281-
s.signChunk(0)
282-
break
283-
284-
} else {
285296
return 0, err
286297
}
298+
287299
}
288300
}
289301
return s.buf.Read(buf)

0 commit comments

Comments
 (0)