@@ -30,6 +30,8 @@ import (
30
30
"github.com/aws/aws-sdk-go/service/s3/s3manager"
31
31
)
32
32
33
+ var etag string = "myetag"
34
+
33
35
func dlLoggingSvc (data []byte ) (* s3.S3 , * []string , * []string ) {
34
36
var m sync.Mutex
35
37
names := []string {}
@@ -203,6 +205,93 @@ func dlLoggingSvcWithErrReader(cases []testErrReader) (*s3.S3, *[]string) {
203
205
return svc , & names
204
206
}
205
207
208
+ func dlLoggingSvcWithVersions (data []byte ) (* s3.S3 , * []string , * []string , * []string ) {
209
+ var m sync.Mutex
210
+ versions := []string {}
211
+ etags := []string {}
212
+ names := []string {}
213
+
214
+ svc := s3 .New (unit .Session )
215
+ svc .Handlers .Send .Clear ()
216
+ svc .Handlers .Send .PushBack (func (r * request.Request ) {
217
+ m .Lock ()
218
+ defer m .Unlock ()
219
+
220
+ names = append (names , r .Operation .Name )
221
+ versions = append (versions , aws .StringValue (r .Params .(* s3.GetObjectInput ).VersionId ))
222
+ etags = append (etags , aws .StringValue (r .Params .(* s3.GetObjectInput ).IfMatch ))
223
+
224
+ rerng := regexp .MustCompile (`bytes=(\d+)-(\d+)` )
225
+ rng := rerng .FindStringSubmatch (r .HTTPRequest .Header .Get ("Range" ))
226
+ start , _ := strconv .ParseInt (rng [1 ], 10 , 64 )
227
+ fin , _ := strconv .ParseInt (rng [2 ], 10 , 64 )
228
+ fin ++
229
+
230
+ if fin > int64 (len (data )) {
231
+ fin = int64 (len (data ))
232
+ }
233
+
234
+ bodyBytes := data [start :fin ]
235
+ r .HTTPResponse = & http.Response {
236
+ StatusCode : 200 ,
237
+ Body : ioutil .NopCloser (bytes .NewReader (bodyBytes )),
238
+ Header : http.Header {},
239
+ }
240
+ r .HTTPResponse .Header .Set ("Content-Range" , fmt .Sprintf ("bytes %d-%d/%d" ,
241
+ start , fin - 1 , len (data )))
242
+ r .HTTPResponse .Header .Set ("Content-Length" , fmt .Sprintf ("%d" , len (bodyBytes )))
243
+ r .HTTPResponse .Header .Set ("Etag" , etag )
244
+ })
245
+
246
+ return svc , & names , & versions , & etags
247
+ }
248
+
249
+ func dlLoggingSvcWithVersionMismatch (t * testing.T ) * s3.S3 {
250
+ var m sync.Mutex
251
+ reqCount := int64 (0 )
252
+ body := bytes .NewReader (make ([]byte , s3manager .DefaultDownloadPartSize ))
253
+ svc := s3 .New (unit .Session )
254
+ svc .Handlers .Send .Clear ()
255
+ svc .Handlers .Send .PushBack (func (r * request.Request ) {
256
+ m .Lock ()
257
+ defer m .Unlock ()
258
+
259
+ statusCode := http .StatusOK
260
+ var eTag * string
261
+ switch atomic .LoadInt64 (& reqCount ) {
262
+ case 0 :
263
+ if a := r .Params .(* s3.GetObjectInput ).IfMatch ; a != nil {
264
+ t .Errorf ("expect no Etag in first request, got %s" , aws .StringValue (a ))
265
+ statusCode = http .StatusBadRequest
266
+ } else {
267
+ eTag = aws .String (etag )
268
+ }
269
+ case 1 :
270
+ // Give a chance for the multipart chunks to be queued up
271
+ time .Sleep (1 * time .Second )
272
+ // mock the precondition error when object is synchronously updated
273
+ statusCode = http .StatusPreconditionFailed
274
+ default :
275
+ if a := aws .StringValue (r .Params .(* s3.GetObjectInput ).IfMatch ); a != etag {
276
+ t .Errorf ("expect subrequests' IfMatch header to be %s, got %s" , etag , a )
277
+ statusCode = http .StatusBadRequest
278
+ }
279
+ }
280
+ r .HTTPResponse = & http.Response {
281
+ StatusCode : statusCode ,
282
+ Body : ioutil .NopCloser (body ),
283
+ Header : http.Header {},
284
+ }
285
+ r .HTTPResponse .Header .Set ("Content-Range" , fmt .Sprintf ("bytes 0-%d/%d" ,
286
+ body .Len ()- 1 , body .Len ()* 10 ))
287
+ r .HTTPResponse .Header .Set ("Content-Length" , fmt .Sprintf ("%d" , body .Len ()))
288
+ r .HTTPResponse .Header .Set ("Etag" , aws .StringValue (eTag ))
289
+ atomic .AddInt64 (& reqCount , 1 )
290
+ })
291
+
292
+ return svc
293
+ }
294
+
206
295
func TestDownloadOrder (t * testing.T ) {
207
296
s , names , ranges := dlLoggingSvc (buf12MB )
208
297
@@ -526,6 +615,92 @@ func TestDownloadPartBodyRetry_FailRetry(t *testing.T) {
526
615
}
527
616
}
528
617
618
+ func TestDownloadWithVersionID (t * testing.T ) {
619
+ s , names , versions , etags := dlLoggingSvcWithVersions (buf12MB )
620
+ d := s3manager .NewDownloaderWithClient (s )
621
+
622
+ w := & aws.WriteAtBuffer {}
623
+ n , err := d .Download (w , & s3.GetObjectInput {
624
+ Bucket : aws .String ("bucket" ),
625
+ Key : aws .String ("key" ),
626
+ VersionId : aws .String ("vid" ),
627
+ })
628
+
629
+ if err != nil {
630
+ t .Fatalf ("expect no error, got %v" , err )
631
+ }
632
+
633
+ if e , a := int64 (len (buf12MB )), n ; e != a {
634
+ t .Errorf ("expect %d buffer length, got %d" , e , a )
635
+ }
636
+
637
+ expectCalls := []string {"GetObject" , "GetObject" , "GetObject" }
638
+ if e , a := expectCalls , * names ; ! reflect .DeepEqual (e , a ) {
639
+ t .Errorf ("expect %v API calls, got %v" , e , a )
640
+ }
641
+
642
+ expectVersions := []string {"vid" , "vid" , "vid" }
643
+ if e , a := expectVersions , * versions ; ! reflect .DeepEqual (e , a ) {
644
+ t .Errorf ("expect %v version ids, got %v" , e , a )
645
+ }
646
+
647
+ expectETags := []string {"" , "" , "" }
648
+ if e , a := expectETags , * etags ; ! reflect .DeepEqual (e , a ) {
649
+ t .Errorf ("expect %v etags, got %v" , e , a )
650
+ }
651
+ }
652
+
653
+ func TestDownloadWithETags (t * testing.T ) {
654
+ s , names , versions , etags := dlLoggingSvcWithVersions (buf12MB )
655
+ d := s3manager .NewDownloaderWithClient (s )
656
+
657
+ w := & aws.WriteAtBuffer {}
658
+ n , err := d .Download (w , & s3.GetObjectInput {
659
+ Bucket : aws .String ("bucket" ),
660
+ Key : aws .String ("key" ),
661
+ })
662
+
663
+ if err != nil {
664
+ t .Fatalf ("expect no error, got %v" , err )
665
+ }
666
+
667
+ if e , a := int64 (len (buf12MB )), n ; e != a {
668
+ t .Errorf ("expect %d buffer length, got %d" , e , a )
669
+ }
670
+
671
+ expectCalls := []string {"GetObject" , "GetObject" , "GetObject" }
672
+ if e , a := expectCalls , * names ; ! reflect .DeepEqual (e , a ) {
673
+ t .Errorf ("expect %v API calls, got %v" , e , a )
674
+ }
675
+
676
+ expectVersions := []string {"" , "" , "" }
677
+ if e , a := expectVersions , * versions ; ! reflect .DeepEqual (e , a ) {
678
+ t .Errorf ("expect %v version ids, got %v" , e , a )
679
+ }
680
+
681
+ expectETags := []string {"" , etag , etag }
682
+ if e , a := expectETags , * etags ; ! reflect .DeepEqual (e , a ) {
683
+ t .Errorf ("expect %v etags, got %v" , e , a )
684
+ }
685
+ }
686
+
687
+ func TestDownloadWithVersionMismatch (t * testing.T ) {
688
+ s := dlLoggingSvcWithVersionMismatch (t )
689
+ d := s3manager .NewDownloaderWithClient (s )
690
+
691
+ w := & aws.WriteAtBuffer {}
692
+ _ , err := d .Download (w , & s3.GetObjectInput {
693
+ Bucket : aws .String ("bucket" ),
694
+ Key : aws .String ("key" ),
695
+ })
696
+
697
+ if err == nil {
698
+ t .Fatalf ("expect error, got none" )
699
+ } else if e , a := "PreconditionFailed" , err .Error (); ! strings .Contains (a , e ) {
700
+ t .Fatalf ("expect error message to contain %s, but did not %s" , e , a )
701
+ }
702
+ }
703
+
529
704
func TestDownloadWithContextCanceled (t * testing.T ) {
530
705
d := s3manager .NewDownloader (unit .Session )
531
706
0 commit comments