@@ -81,6 +81,8 @@ func (c *Client) GetObject(ctx context.Context, bucketName, objectName string, o
8181 reqCh := make (chan getRequest )
8282 // Create response channel.
8383 resCh := make (chan getResponse )
84+ // record original range header for stat operation.
85+ originalRangeHeader := opts .Header ().Get ("Range" )
8486
8587 // This routine feeds partial object data as and when the caller reads.
8688 go func () {
@@ -147,19 +149,35 @@ func (c *Client) GetObject(ctx context.Context, bucketName, objectName string, o
147149 // it to io.EOF - return unexpected EOF.
148150 err = io .ErrUnexpectedEOF
149151 }
152+ // when doing a readAt, we can't reuse the httpReader for next read action.
153+ if req .isReadAt {
154+ httpReader .Close ()
155+ httpReader = nil
156+ }
157+
158+ objectSize := int64 (0 )
159+ // case for ReadFull with range request
160+ if ! req .isReadAt && req .Offset == 0 {
161+ objectSize = objectInfo .Size
162+ }
150163 // Send back the first response.
151164 resCh <- getResponse {
165+ ObjectSize : objectSize ,
152166 objectInfo : objectInfo ,
153167 Size : size ,
154168 Error : err ,
155169 didRead : true ,
156170 }
157171 } else {
158- // First request is a Stat or Seek call.
159- // Only need to run a StatObject until an actual Read or ReadAt request comes through.
172+ if originalRangeHeader == "" {
173+ // First request is a Seek call.
174+ // Only need to run a StatObject until an actual Read request comes through.
160175
161- // Remove range header if already set, for stat Operations to get original file size.
162- delete (opts .headers , "Range" )
176+ // Remove range header if already set, for stat Operations to get original file size.
177+ delete (opts .headers , "Range" )
178+ } else {
179+ opts .headers ["Range" ] = originalRangeHeader
180+ }
163181 objectInfo , err = c .StatObject (gctx , bucketName , objectName , StatObjectOptions (opts ))
164182 if err != nil {
165183 resCh <- getResponse {
@@ -171,12 +189,17 @@ func (c *Client) GetObject(ctx context.Context, bucketName, objectName string, o
171189 etag = objectInfo .ETag
172190 // Send back the first response.
173191 resCh <- getResponse {
192+ ObjectSize : objectInfo .Size ,
174193 objectInfo : objectInfo ,
175194 }
176195 }
177196 } else if req .settingObjectInfo { // Request is just to get objectInfo.
178197 // Remove range header if already set, for stat Operations to get original file size.
179- delete (opts .headers , "Range" )
198+ if originalRangeHeader == "" {
199+ delete (opts .headers , "Range" )
200+ } else {
201+ opts .headers ["Range" ] = originalRangeHeader
202+ }
180203 // Check whether this is snowball
181204 // if yes do not use If-Match feature
182205 // it doesn't work.
@@ -193,9 +216,12 @@ func (c *Client) GetObject(ctx context.Context, bucketName, objectName string, o
193216 }
194217 // Send back the objectInfo.
195218 resCh <- getResponse {
219+ ObjectSize : objectInfo .Size ,
196220 objectInfo : objectInfo ,
197221 }
198222 } else {
223+ objectSize := int64 (0 )
224+ renewReader := false
199225 // Offset changes fetch the new object at an Offset.
200226 // Because the httpReader may not be set by the first
201227 // request if it was a stat or seek it must be checked
@@ -223,8 +249,11 @@ func (c *Client) GetObject(ctx context.Context, bucketName, objectName string, o
223249 } else if req .Offset > 0 { // Range is set with respect to the offset.
224250 opts .SetRange (req .Offset , 0 )
225251 } else {
226- // Remove range header if already set
227- delete (opts .headers , "Range" )
252+ if originalRangeHeader == "" {
253+ delete (opts .headers , "Range" )
254+ } else {
255+ opts .headers ["Range" ] = originalRangeHeader
256+ }
228257 }
229258 httpReader , objectInfo , _ , err = c .getObject (gctx , bucketName , objectName , opts )
230259 if err != nil {
@@ -239,6 +268,11 @@ func (c *Client) GetObject(ctx context.Context, bucketName, objectName string, o
239268 }
240269 return
241270 }
271+ // case for ReadFull with range request
272+ if ! req .isReadAt && req .Offset == 0 {
273+ objectSize = objectInfo .Size
274+ }
275+ renewReader = true
242276 totalRead = 0
243277 }
244278
@@ -265,9 +299,13 @@ func (c *Client) GetObject(ctx context.Context, bucketName, objectName string, o
265299 // it to io.EOF - return unexpected EOF.
266300 err = io .ErrUnexpectedEOF
267301 }
268-
302+ if renewReader && req .isReadAt {
303+ httpReader .Close ()
304+ httpReader = nil
305+ }
269306 // Reply back how much was read.
270307 resCh <- getResponse {
308+ ObjectSize : objectSize ,
271309 Size : size ,
272310 Error : err ,
273311 didRead : true ,
@@ -296,6 +334,7 @@ type getRequest struct {
296334
297335// get response message container to reply back for the request.
298336type getResponse struct {
337+ ObjectSize int64
299338 Size int
300339 Error error
301340 didRead bool // Lets subsequent calls know whether or not httpReader has been initiated.
@@ -314,6 +353,7 @@ type Object struct {
314353 ctx context.Context
315354 cancel context.CancelFunc
316355 currOffset int64
356+ totalSize int64
317357 objectInfo ObjectInfo
318358
319359 // Ask lower level to initiate data fetching based on currOffset
@@ -371,6 +411,10 @@ func (o *Object) doGetRequest(request getRequest) (getResponse, error) {
371411 // Data are ready on the wire, no need to reinitiate connection in lower level
372412 o .seekData = false
373413
414+ if response .ObjectSize != 0 {
415+ o .totalSize = response .ObjectSize
416+ }
417+
374418 return response , response .Error
375419}
376420
@@ -380,7 +424,7 @@ func (o *Object) setOffset(bytesRead int64) error {
380424 // Update the currentOffset.
381425 o .currOffset += bytesRead
382426
383- if o .objectInfo . Size > - 1 && o .currOffset >= o .objectInfo . Size {
427+ if o .totalSize > - 1 && o .currOffset >= o .totalSize {
384428 return io .EOF
385429 }
386430 return nil
@@ -458,6 +502,8 @@ func (o *Object) Read(b []byte) (n int, err error) {
458502}
459503
460504// Stat returns the ObjectInfo structure describing Object.
505+ // When requesting a partial object or reading has started,
506+ // the size returned will reflect the remaining size.
461507func (o * Object ) Stat () (ObjectInfo , error ) {
462508 if o == nil {
463509 return ObjectInfo {}, errInvalidArgument ("Object is nil" )
@@ -474,6 +520,12 @@ func (o *Object) Stat() (ObjectInfo, error) {
474520 // payload is delivered out-of-band so the object is created closed, or a
475521 // previously completed request) report it, even for a closed object.
476522 if o .objectInfoSet {
523+ if o .currOffset > o .totalSize {
524+ return ObjectInfo {}, io .EOF
525+ }
526+ if o .currOffset <= o .totalSize {
527+ o .objectInfo .Size = o .totalSize - o .currOffset
528+ }
477529 return o .objectInfo , nil
478530 }
479531
@@ -493,7 +545,12 @@ func (o *Object) Stat() (ObjectInfo, error) {
493545 return ObjectInfo {}, err
494546 }
495547 }
496-
548+ if o .currOffset > o .totalSize {
549+ return ObjectInfo {}, io .EOF
550+ }
551+ if o .currOffset <= o .totalSize {
552+ o .objectInfo .Size = o .totalSize - o .currOffset
553+ }
497554 return o .objectInfo , nil
498555}
499556
@@ -529,7 +586,7 @@ func (o *Object) ReadAt(b []byte, offset int64) (n int, err error) {
529586 if o .objectInfoSet {
530587 // If offset is negative than we return io.EOF.
531588 // If offset is greater than or equal to object size we return io.EOF.
532- if (o .objectInfo . Size > - 1 && offset >= o .objectInfo . Size ) || offset < 0 {
589+ if (o .totalSize > - 1 && offset >= o .totalSize ) || offset < 0 {
533590 return 0 , io .EOF
534591 }
535592 }
@@ -643,10 +700,10 @@ func (o *Object) Seek(offset int64, whence int) (n int64, err error) {
643700 newOffset += offset
644701 case 2 :
645702 // If we don't know the object size return an error for io.SeekEnd
646- if o .objectInfo . Size < 0 {
703+ if o .totalSize < 0 {
647704 return 0 , errInvalidArgument ("Whence END is not supported when the object size is unknown" )
648705 }
649- newOffset = o .objectInfo . Size + offset
706+ newOffset = o .totalSize + offset
650707 }
651708 // Seeking to a position before the start of the object is not allowed for
652709 // any whence.
0 commit comments