mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-05-30 04:20:53 +00:00
Update Go AWS SDK to the latest version
This commit is contained in:
committed by
Andrey Smirnov
parent
d08be990ef
commit
94a72b23ff
+4089
-384
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -80,7 +80,8 @@ func buildGetBucketLocation(r *request.Request) {
|
||||
out := r.Data.(*GetBucketLocationOutput)
|
||||
b, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||
if err != nil {
|
||||
r.Error = awserr.New("SerializationError", "failed reading response body", err)
|
||||
r.Error = awserr.New(request.ErrCodeSerialization,
|
||||
"failed reading response body", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@ func TestPopulateLocationConstraint(t *testing.T) {
|
||||
func TestNoPopulateLocationConstraintIfProvided(t *testing.T) {
|
||||
s := s3.New(unit.Session)
|
||||
req, _ := s.CreateBucketRequest(&s3.CreateBucketInput{
|
||||
Bucket: aws.String("bucket"),
|
||||
Bucket: aws.String("bucket"),
|
||||
CreateBucketConfiguration: &s3.CreateBucketConfiguration{},
|
||||
})
|
||||
if err := req.Build(); err != nil {
|
||||
|
||||
+188
@@ -0,0 +1,188 @@
|
||||
// +build integration
|
||||
|
||||
package s3_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
)
|
||||
|
||||
func TestInteg_SelectObjectContent(t *testing.T) {
|
||||
keyName := "selectObject.csv"
|
||||
putTestFile(t, filepath.Join("testdata", "positive_select.csv"), keyName)
|
||||
|
||||
resp, err := svc.SelectObjectContent(&s3.SelectObjectContentInput{
|
||||
Bucket: bucketName,
|
||||
Key: &keyName,
|
||||
Expression: aws.String("Select * from S3Object"),
|
||||
ExpressionType: aws.String(s3.ExpressionTypeSql),
|
||||
InputSerialization: &s3.InputSerialization{
|
||||
CSV: &s3.CSVInput{
|
||||
FieldDelimiter: aws.String(","),
|
||||
FileHeaderInfo: aws.String(s3.FileHeaderInfoIgnore),
|
||||
},
|
||||
},
|
||||
OutputSerialization: &s3.OutputSerialization{
|
||||
CSV: &s3.CSVOutput{
|
||||
FieldDelimiter: aws.String(","),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, %v", err)
|
||||
}
|
||||
defer resp.EventStream.Close()
|
||||
|
||||
var sum int64
|
||||
var processed int64
|
||||
for event := range resp.EventStream.Events() {
|
||||
switch tv := event.(type) {
|
||||
case *s3.RecordsEvent:
|
||||
sum += int64(len(tv.Payload))
|
||||
case *s3.StatsEvent:
|
||||
processed = *tv.Details.BytesProcessed
|
||||
}
|
||||
}
|
||||
|
||||
if sum == 0 {
|
||||
t.Errorf("expect selected content, got none")
|
||||
}
|
||||
|
||||
if processed == 0 {
|
||||
t.Errorf("expect selected status bytes processed, got none")
|
||||
}
|
||||
|
||||
if err := resp.EventStream.Err(); err != nil {
|
||||
t.Fatalf("exect no error, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInteg_SelectObjectContent_Error(t *testing.T) {
|
||||
keyName := "negativeSelect.csv"
|
||||
|
||||
buf := make([]byte, 0, 1024*1024*6)
|
||||
buf = append(buf, []byte("name,number\n")...)
|
||||
line := []byte("jj,0\n")
|
||||
for i := 0; i < (cap(buf)/len(line))-2; i++ {
|
||||
buf = append(buf, line...)
|
||||
}
|
||||
buf = append(buf, []byte("gg,NaN\n")...)
|
||||
|
||||
putTestContent(t, bytes.NewReader(buf), keyName)
|
||||
|
||||
resp, err := svc.SelectObjectContent(&s3.SelectObjectContentInput{
|
||||
Bucket: bucketName,
|
||||
Key: &keyName,
|
||||
Expression: aws.String("SELECT name FROM S3Object WHERE cast(number as int) < 1"),
|
||||
ExpressionType: aws.String(s3.ExpressionTypeSql),
|
||||
InputSerialization: &s3.InputSerialization{
|
||||
CSV: &s3.CSVInput{
|
||||
FileHeaderInfo: aws.String(s3.FileHeaderInfoUse),
|
||||
},
|
||||
},
|
||||
OutputSerialization: &s3.OutputSerialization{
|
||||
CSV: &s3.CSVOutput{
|
||||
FieldDelimiter: aws.String(","),
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, %v", err)
|
||||
}
|
||||
defer resp.EventStream.Close()
|
||||
|
||||
var sum int64
|
||||
for event := range resp.EventStream.Events() {
|
||||
switch tv := event.(type) {
|
||||
case *s3.RecordsEvent:
|
||||
sum += int64(len(tv.Payload))
|
||||
}
|
||||
}
|
||||
|
||||
if sum == 0 {
|
||||
t.Errorf("expect selected content")
|
||||
}
|
||||
|
||||
err = resp.EventStream.Err()
|
||||
if err == nil {
|
||||
t.Fatalf("exepct error")
|
||||
}
|
||||
|
||||
aerr := err.(awserr.Error)
|
||||
if a := aerr.Code(); len(a) == 0 {
|
||||
t.Errorf("expect, error code")
|
||||
}
|
||||
if a := aerr.Message(); len(a) == 0 {
|
||||
t.Errorf("expect, error message")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInteg_SelectObjectContent_Stream(t *testing.T) {
|
||||
keyName := "selectGopher.csv"
|
||||
|
||||
buf := `name,number
|
||||
gopher,0
|
||||
ᵷodɥǝɹ,1
|
||||
`
|
||||
// Put a mock CSV file to the S3 bucket so that its contents can be
|
||||
// selected.
|
||||
putTestContent(t, strings.NewReader(buf), keyName)
|
||||
|
||||
// Make the Select Object Content API request using the object uploaded.
|
||||
resp, err := svc.SelectObjectContent(&s3.SelectObjectContentInput{
|
||||
Bucket: bucketName,
|
||||
Key: &keyName,
|
||||
Expression: aws.String("SELECT name FROM S3Object WHERE cast(number as int) < 1"),
|
||||
ExpressionType: aws.String(s3.ExpressionTypeSql),
|
||||
InputSerialization: &s3.InputSerialization{
|
||||
CSV: &s3.CSVInput{
|
||||
FileHeaderInfo: aws.String(s3.FileHeaderInfoUse),
|
||||
},
|
||||
},
|
||||
OutputSerialization: &s3.OutputSerialization{
|
||||
CSV: &s3.CSVOutput{},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed making API request, %v\n", err)
|
||||
return
|
||||
}
|
||||
defer resp.EventStream.Close()
|
||||
|
||||
results, resultWriter := io.Pipe()
|
||||
go func() {
|
||||
defer resultWriter.Close()
|
||||
for event := range resp.EventStream.Events() {
|
||||
switch e := event.(type) {
|
||||
case *s3.RecordsEvent:
|
||||
resultWriter.Write(e.Payload)
|
||||
case *s3.StatsEvent:
|
||||
fmt.Printf("Processed %d bytes\n", *e.Details.BytesProcessed)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Printout the results
|
||||
resReader := csv.NewReader(results)
|
||||
for {
|
||||
record, err := resReader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
fmt.Println(record)
|
||||
}
|
||||
|
||||
if err := resp.EventStream.Err(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "reading from event stream failed, %v\n", err)
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
// +build integration
|
||||
|
||||
package s3_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
)
|
||||
|
||||
func base64Sum(content []byte) string {
|
||||
sum := md5.Sum(content)
|
||||
return base64.StdEncoding.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func SkipTestContentMD5Validate(t *testing.T) {
|
||||
body := []byte("really cool body content")
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
Body []byte
|
||||
Sum64 string
|
||||
RangeGet []int64
|
||||
}{
|
||||
{
|
||||
Body: body,
|
||||
Sum64: base64Sum(body),
|
||||
Name: "contentMD5validation.pop",
|
||||
},
|
||||
{
|
||||
Body: []byte{},
|
||||
Sum64: base64Sum([]byte{}),
|
||||
Name: "contentMD5validation.empty",
|
||||
},
|
||||
{
|
||||
Body: body,
|
||||
Sum64: base64Sum(body),
|
||||
RangeGet: []int64{0, 9},
|
||||
Name: "contentMD5validation.range",
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
keyName := aws.String(c.Name)
|
||||
req, _ := svc.PutObjectRequest(&s3.PutObjectInput{
|
||||
Bucket: bucketName,
|
||||
Key: keyName,
|
||||
Body: bytes.NewReader(c.Body),
|
||||
})
|
||||
|
||||
req.Build()
|
||||
if e, a := c.Sum64, req.HTTPRequest.Header.Get("Content-Md5"); e != a {
|
||||
t.Errorf("%d, expect %v sum, got %v", i, e, a)
|
||||
}
|
||||
|
||||
if err := req.Send(); err != nil {
|
||||
t.Fatalf("%d, expect no error, got %v", i, err)
|
||||
}
|
||||
|
||||
getObjIn := &s3.GetObjectInput{
|
||||
Bucket: bucketName,
|
||||
Key: keyName,
|
||||
}
|
||||
|
||||
expectBody := c.Body
|
||||
if c.RangeGet != nil {
|
||||
getObjIn.Range = aws.String(fmt.Sprintf("bytes=%d-%d", c.RangeGet[0], c.RangeGet[1]-1))
|
||||
expectBody = c.Body[c.RangeGet[0]:c.RangeGet[1]]
|
||||
}
|
||||
|
||||
getReq, getOut := svc.GetObjectRequest(getObjIn)
|
||||
|
||||
getReq.Build()
|
||||
if e, a := "append-md5", getReq.HTTPRequest.Header.Get("X-Amz-Te"); e != a {
|
||||
t.Errorf("%d, expect %v encoding, got %v", i, e, a)
|
||||
}
|
||||
if err := getReq.Send(); err != nil {
|
||||
t.Fatalf("%d, expect no error, got %v", i, err)
|
||||
}
|
||||
defer getOut.Body.Close()
|
||||
|
||||
if e, a := "append-md5", getReq.HTTPResponse.Header.Get("X-Amz-Transfer-Encoding"); e != a {
|
||||
t.Fatalf("%d, expect response tx encoding header %v, got %v", i, e, a)
|
||||
}
|
||||
|
||||
var readBody bytes.Buffer
|
||||
_, err := io.Copy(&readBody, getOut.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("%d, expect no error, got %v", i, err)
|
||||
}
|
||||
|
||||
if e, a := expectBody, readBody.Bytes(); !bytes.Equal(e, a) {
|
||||
t.Errorf("%d, expect %v body, got %v", i, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
// +build integration
|
||||
|
||||
package s3_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/awstesting/integration"
|
||||
"github.com/aws/aws-sdk-go/awstesting/integration/s3integ"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
)
|
||||
|
||||
const integBucketPrefix = "aws-sdk-go-integration"
|
||||
|
||||
var bucketName *string
|
||||
var svc *s3.S3
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
sess := integration.SessionWithDefaultRegion("us-west-2")
|
||||
svc = s3.New(sess)
|
||||
bucketName = aws.String(s3integ.GenerateBucketName())
|
||||
if err := s3integ.SetupTest(svc, *bucketName); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var result int
|
||||
defer func() {
|
||||
if err := s3integ.CleanupTest(svc, *bucketName); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
if r := recover(); r != nil {
|
||||
fmt.Fprintln(os.Stderr, "S3 integrationt tests paniced,", r)
|
||||
result = 1
|
||||
}
|
||||
os.Exit(result)
|
||||
}()
|
||||
|
||||
result = m.Run()
|
||||
}
|
||||
|
||||
func putTestFile(t *testing.T, filename, key string) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open testfile, %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
putTestContent(t, f, key)
|
||||
}
|
||||
|
||||
func putTestContent(t *testing.T, reader io.ReadSeeker, key string) {
|
||||
fmt.Println(bucketName, key, svc)
|
||||
_, err := svc.PutObject(&s3.PutObjectInput{
|
||||
Bucket: bucketName,
|
||||
Key: aws.String(key),
|
||||
Body: reader,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("expect no error, got %v", err)
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
// +build integration
|
||||
|
||||
package s3_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
)
|
||||
|
||||
func TestInteg_WriteToObject(t *testing.T) {
|
||||
_, err := svc.PutObject(&s3.PutObjectInput{
|
||||
Bucket: bucketName,
|
||||
Key: aws.String("key name"),
|
||||
Body: bytes.NewReader([]byte("hello world")),
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("expect no error, got %v", err)
|
||||
}
|
||||
|
||||
resp, err := svc.GetObject(&s3.GetObjectInput{
|
||||
Bucket: bucketName,
|
||||
Key: aws.String("key name"),
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("expect no error, got %v", err)
|
||||
}
|
||||
|
||||
b, _ := ioutil.ReadAll(resp.Body)
|
||||
if e, a := []byte("hello world"), b; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expect %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInteg_PresignedGetPut(t *testing.T) {
|
||||
putreq, _ := svc.PutObjectRequest(&s3.PutObjectInput{
|
||||
Bucket: bucketName,
|
||||
Key: aws.String("presigned-key"),
|
||||
})
|
||||
var err error
|
||||
|
||||
// Presign a PUT request
|
||||
var puturl string
|
||||
puturl, err = putreq.Presign(300 * time.Second)
|
||||
if err != nil {
|
||||
t.Errorf("expect no error, got %v", err)
|
||||
}
|
||||
|
||||
// PUT to the presigned URL with a body
|
||||
var puthttpreq *http.Request
|
||||
buf := bytes.NewReader([]byte("hello world"))
|
||||
puthttpreq, err = http.NewRequest("PUT", puturl, buf)
|
||||
if err != nil {
|
||||
t.Errorf("expect no error, got %v", err)
|
||||
}
|
||||
|
||||
var putresp *http.Response
|
||||
putresp, err = http.DefaultClient.Do(puthttpreq)
|
||||
if err != nil {
|
||||
t.Errorf("expect put with presign url no error, got %v", err)
|
||||
}
|
||||
if e, a := 200, putresp.StatusCode; e != a {
|
||||
t.Errorf("expect %v, got %v", e, a)
|
||||
}
|
||||
|
||||
// Presign a GET on the same URL
|
||||
getreq, _ := svc.GetObjectRequest(&s3.GetObjectInput{
|
||||
Bucket: bucketName,
|
||||
Key: aws.String("presigned-key"),
|
||||
})
|
||||
|
||||
var geturl string
|
||||
geturl, err = getreq.Presign(300 * time.Second)
|
||||
if err != nil {
|
||||
t.Errorf("expect no error, got %v", err)
|
||||
}
|
||||
|
||||
// Get the body
|
||||
var getresp *http.Response
|
||||
getresp, err = http.Get(geturl)
|
||||
if err != nil {
|
||||
t.Errorf("expect no error, got %v", err)
|
||||
}
|
||||
|
||||
var b []byte
|
||||
defer getresp.Body.Close()
|
||||
b, err = ioutil.ReadAll(getresp.Body)
|
||||
if e, a := "hello world", string(b); e != a {
|
||||
t.Errorf("expect %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -3,6 +3,7 @@ package s3
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/s3err"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -16,11 +17,13 @@ func defaultInitClientFn(c *client.Client) {
|
||||
|
||||
// Require SSL when using SSE keys
|
||||
c.Handlers.Validate.PushBack(validateSSERequiresSSL)
|
||||
c.Handlers.Build.PushBack(computeSSEKeys)
|
||||
c.Handlers.Build.PushBack(computeSSEKeyMD5)
|
||||
c.Handlers.Build.PushBack(computeCopySourceSSEKeyMD5)
|
||||
|
||||
// S3 uses custom error unmarshaling logic
|
||||
c.Handlers.UnmarshalError.Clear()
|
||||
c.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||
c.Handlers.UnmarshalError.PushBackNamed(s3err.RequestFailureWrapperHandler())
|
||||
}
|
||||
|
||||
func defaultInitRequestFn(r *request.Request) {
|
||||
@@ -31,6 +34,7 @@ func defaultInitRequestFn(r *request.Request) {
|
||||
switch r.Operation.Name {
|
||||
case opPutBucketCors, opPutBucketLifecycle, opPutBucketPolicy,
|
||||
opPutBucketTagging, opDeleteObjects, opPutBucketLifecycleConfiguration,
|
||||
opPutObjectLegalHold, opPutObjectRetention, opPutObjectLockConfiguration,
|
||||
opPutBucketReplication:
|
||||
// These S3 operations require Content-MD5 to be set
|
||||
r.Handlers.Build.PushBack(contentMD5)
|
||||
@@ -42,6 +46,7 @@ func defaultInitRequestFn(r *request.Request) {
|
||||
r.Handlers.Validate.PushFront(populateLocationConstraint)
|
||||
case opCopyObject, opUploadPartCopy, opCompleteMultipartUpload:
|
||||
r.Handlers.Unmarshal.PushFront(copyMultipartStatusOKUnmarhsalError)
|
||||
r.Handlers.Unmarshal.PushBackNamed(s3err.RequestFailureWrapperHandler())
|
||||
case opPutObject, opUploadPart:
|
||||
r.Handlers.Build.PushBack(computeBodyHashes)
|
||||
// Disabled until #1837 root issue is resolved.
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
)
|
||||
|
||||
func ExampleS3_SelectObjectContent() {
|
||||
sess := session.Must(session.NewSession())
|
||||
svc := New(sess)
|
||||
|
||||
/*
|
||||
Example myObjectKey CSV content:
|
||||
|
||||
name,number
|
||||
gopher,0
|
||||
ᵷodɥǝɹ,1
|
||||
*/
|
||||
|
||||
// Make the Select Object Content API request using the object uploaded.
|
||||
resp, err := svc.SelectObjectContent(&SelectObjectContentInput{
|
||||
Bucket: aws.String("myBucket"),
|
||||
Key: aws.String("myObjectKey"),
|
||||
Expression: aws.String("SELECT name FROM S3Object WHERE cast(number as int) < 1"),
|
||||
ExpressionType: aws.String(ExpressionTypeSql),
|
||||
InputSerialization: &InputSerialization{
|
||||
CSV: &CSVInput{
|
||||
FileHeaderInfo: aws.String(FileHeaderInfoUse),
|
||||
},
|
||||
},
|
||||
OutputSerialization: &OutputSerialization{
|
||||
CSV: &CSVOutput{},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed making API request, %v\n", err)
|
||||
return
|
||||
}
|
||||
defer resp.EventStream.Close()
|
||||
|
||||
results, resultWriter := io.Pipe()
|
||||
go func() {
|
||||
defer resultWriter.Close()
|
||||
for event := range resp.EventStream.Events() {
|
||||
switch e := event.(type) {
|
||||
case *RecordsEvent:
|
||||
resultWriter.Write(e.Payload)
|
||||
case *StatsEvent:
|
||||
fmt.Printf("Processed %d bytes\n", *e.Details.BytesProcessed)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Printout the results
|
||||
resReader := csv.NewReader(results)
|
||||
for {
|
||||
record, err := resReader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
fmt.Println(record)
|
||||
}
|
||||
|
||||
if err := resp.EventStream.Err(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "reading from event stream failed, %v\n", err)
|
||||
}
|
||||
}
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
|
||||
|
||||
// +build go1.6
|
||||
|
||||
package s3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/corehandlers"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/awstesting/unit"
|
||||
"github.com/aws/aws-sdk-go/private/protocol"
|
||||
"github.com/aws/aws-sdk-go/private/protocol/eventstream"
|
||||
"github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi"
|
||||
"github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamtest"
|
||||
"github.com/aws/aws-sdk-go/private/protocol/restxml"
|
||||
)
|
||||
|
||||
var _ time.Time
|
||||
var _ awserr.Error
|
||||
|
||||
func TestSelectObjectContent_Read(t *testing.T) {
|
||||
expectEvents, eventMsgs := mockSelectObjectContentReadEvents()
|
||||
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
|
||||
eventstreamtest.ServeEventStream{
|
||||
T: t,
|
||||
Events: eventMsgs,
|
||||
},
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, %v", err)
|
||||
}
|
||||
defer cleanupFn()
|
||||
|
||||
svc := New(sess)
|
||||
resp, err := svc.SelectObjectContent(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error got, %v", err)
|
||||
}
|
||||
defer resp.EventStream.Close()
|
||||
|
||||
var i int
|
||||
for event := range resp.EventStream.Events() {
|
||||
if event == nil {
|
||||
t.Errorf("%d, expect event, got nil", i)
|
||||
}
|
||||
if e, a := expectEvents[i], event; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("%d, expect %T %v, got %T %v", i, e, e, a, a)
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
if err := resp.EventStream.Err(); err != nil {
|
||||
t.Errorf("expect no error, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectObjectContent_ReadClose(t *testing.T) {
|
||||
_, eventMsgs := mockSelectObjectContentReadEvents()
|
||||
sess, cleanupFn, err := eventstreamtest.SetupEventStreamSession(t,
|
||||
eventstreamtest.ServeEventStream{
|
||||
T: t,
|
||||
Events: eventMsgs,
|
||||
},
|
||||
true,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, %v", err)
|
||||
}
|
||||
defer cleanupFn()
|
||||
|
||||
svc := New(sess)
|
||||
resp, err := svc.SelectObjectContent(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error got, %v", err)
|
||||
}
|
||||
|
||||
resp.EventStream.Close()
|
||||
<-resp.EventStream.Events()
|
||||
|
||||
if err := resp.EventStream.Err(); err != nil {
|
||||
t.Errorf("expect no error, %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSelectObjectContent_Read(b *testing.B) {
|
||||
_, eventMsgs := mockSelectObjectContentReadEvents()
|
||||
var buf bytes.Buffer
|
||||
encoder := eventstream.NewEncoder(&buf)
|
||||
for _, msg := range eventMsgs {
|
||||
if err := encoder.Encode(msg); err != nil {
|
||||
b.Fatalf("failed to encode message, %v", err)
|
||||
}
|
||||
}
|
||||
stream := &loopReader{source: bytes.NewReader(buf.Bytes())}
|
||||
|
||||
sess := unit.Session
|
||||
svc := New(sess, &aws.Config{
|
||||
Endpoint: aws.String("https://example.com"),
|
||||
DisableParamValidation: aws.Bool(true),
|
||||
})
|
||||
svc.Handlers.Send.Swap(corehandlers.SendHandler.Name,
|
||||
request.NamedHandler{Name: "mockSend",
|
||||
Fn: func(r *request.Request) {
|
||||
r.HTTPResponse = &http.Response{
|
||||
Status: "200 OK",
|
||||
StatusCode: 200,
|
||||
Header: http.Header{},
|
||||
Body: ioutil.NopCloser(stream),
|
||||
}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
resp, err := svc.SelectObjectContent(nil)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create request, %v", err)
|
||||
}
|
||||
defer resp.EventStream.Close()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err = resp.EventStream.Err(); err != nil {
|
||||
b.Fatalf("expect no error, got %v", err)
|
||||
}
|
||||
event := <-resp.EventStream.Events()
|
||||
if event == nil {
|
||||
b.Fatalf("expect event, got nil, %v, %d", resp.EventStream.Err(), i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func mockSelectObjectContentReadEvents() (
|
||||
[]SelectObjectContentEventStreamEvent,
|
||||
[]eventstream.Message,
|
||||
) {
|
||||
expectEvents := []SelectObjectContentEventStreamEvent{
|
||||
&ContinuationEvent{},
|
||||
&EndEvent{},
|
||||
&ProgressEvent{
|
||||
Details: &Progress{
|
||||
BytesProcessed: aws.Int64(1234),
|
||||
BytesReturned: aws.Int64(1234),
|
||||
BytesScanned: aws.Int64(1234),
|
||||
},
|
||||
},
|
||||
&RecordsEvent{
|
||||
Payload: []byte("blob value goes here"),
|
||||
},
|
||||
&StatsEvent{
|
||||
Details: &Stats{
|
||||
BytesProcessed: aws.Int64(1234),
|
||||
BytesReturned: aws.Int64(1234),
|
||||
BytesScanned: aws.Int64(1234),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var marshalers request.HandlerList
|
||||
marshalers.PushBackNamed(restxml.BuildHandler)
|
||||
payloadMarshaler := protocol.HandlerPayloadMarshal{
|
||||
Marshalers: marshalers,
|
||||
}
|
||||
_ = payloadMarshaler
|
||||
|
||||
eventMsgs := []eventstream.Message{
|
||||
{
|
||||
Headers: eventstream.Headers{
|
||||
eventstreamtest.EventMessageTypeHeader,
|
||||
{
|
||||
Name: eventstreamapi.EventTypeHeader,
|
||||
Value: eventstream.StringValue("Cont"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Headers: eventstream.Headers{
|
||||
eventstreamtest.EventMessageTypeHeader,
|
||||
{
|
||||
Name: eventstreamapi.EventTypeHeader,
|
||||
Value: eventstream.StringValue("End"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Headers: eventstream.Headers{
|
||||
eventstreamtest.EventMessageTypeHeader,
|
||||
{
|
||||
Name: eventstreamapi.EventTypeHeader,
|
||||
Value: eventstream.StringValue("Progress"),
|
||||
},
|
||||
},
|
||||
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[2]),
|
||||
},
|
||||
{
|
||||
Headers: eventstream.Headers{
|
||||
eventstreamtest.EventMessageTypeHeader,
|
||||
{
|
||||
Name: eventstreamapi.EventTypeHeader,
|
||||
Value: eventstream.StringValue("Records"),
|
||||
},
|
||||
},
|
||||
Payload: expectEvents[3].(*RecordsEvent).Payload,
|
||||
},
|
||||
{
|
||||
Headers: eventstream.Headers{
|
||||
eventstreamtest.EventMessageTypeHeader,
|
||||
{
|
||||
Name: eventstreamapi.EventTypeHeader,
|
||||
Value: eventstream.StringValue("Stats"),
|
||||
},
|
||||
},
|
||||
Payload: eventstreamtest.MarshalEventPayload(payloadMarshaler, expectEvents[4]),
|
||||
},
|
||||
}
|
||||
|
||||
return expectEvents, eventMsgs
|
||||
}
|
||||
|
||||
type loopReader struct {
|
||||
source *bytes.Reader
|
||||
}
|
||||
|
||||
func (c *loopReader) Read(p []byte) (int, error) {
|
||||
if c.source.Len() == 0 {
|
||||
c.source.Seek(0, 0)
|
||||
}
|
||||
|
||||
return c.source.Read(p)
|
||||
}
|
||||
+162
-162
@@ -467,16 +467,16 @@ func ExampleS3_DeleteObject_shared01() {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To remove tag set from an object version
|
||||
// To remove tag set from an object
|
||||
//
|
||||
// The following example removes tag set associated with the specified object version.
|
||||
// The request specifies both the object key and object version.
|
||||
// The following example removes tag set associated with the specified object. If the
|
||||
// bucket is versioning enabled, the operation removes tag set from the latest object
|
||||
// version.
|
||||
func ExampleS3_DeleteObjectTagging_shared00() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.DeleteObjectTaggingInput{
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("HappyFace.jpg"),
|
||||
VersionId: aws.String("ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI"),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("HappyFace.jpg"),
|
||||
}
|
||||
|
||||
result, err := svc.DeleteObjectTagging(input)
|
||||
@@ -497,16 +497,16 @@ func ExampleS3_DeleteObjectTagging_shared00() {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To remove tag set from an object
|
||||
// To remove tag set from an object version
|
||||
//
|
||||
// The following example removes tag set associated with the specified object. If the
|
||||
// bucket is versioning enabled, the operation removes tag set from the latest object
|
||||
// version.
|
||||
// The following example removes tag set associated with the specified object version.
|
||||
// The request specifies both the object key and object version.
|
||||
func ExampleS3_DeleteObjectTagging_shared01() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.DeleteObjectTaggingInput{
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("HappyFace.jpg"),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("HappyFace.jpg"),
|
||||
VersionId: aws.String("ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI"),
|
||||
}
|
||||
|
||||
result, err := svc.DeleteObjectTagging(input)
|
||||
@@ -1026,14 +1026,16 @@ func ExampleS3_GetObjectAcl_shared00() {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To retrieve tag set of an object
|
||||
// To retrieve tag set of a specific object version
|
||||
//
|
||||
// The following example retrieves tag set of an object.
|
||||
// The following example retrieves tag set of an object. The request specifies object
|
||||
// version.
|
||||
func ExampleS3_GetObjectTagging_shared00() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.GetObjectTaggingInput{
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("HappyFace.jpg"),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("exampleobject"),
|
||||
VersionId: aws.String("ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI"),
|
||||
}
|
||||
|
||||
result, err := svc.GetObjectTagging(input)
|
||||
@@ -1054,16 +1056,14 @@ func ExampleS3_GetObjectTagging_shared00() {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To retrieve tag set of a specific object version
|
||||
// To retrieve tag set of an object
|
||||
//
|
||||
// The following example retrieves tag set of an object. The request specifies object
|
||||
// version.
|
||||
// The following example retrieves tag set of an object.
|
||||
func ExampleS3_GetObjectTagging_shared01() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.GetObjectTaggingInput{
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("exampleobject"),
|
||||
VersionId: aws.String("ydlaNkwWm0SfKJR.T1b1fIdPRbldTYRI"),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("HappyFace.jpg"),
|
||||
}
|
||||
|
||||
result, err := svc.GetObjectTagging(input)
|
||||
@@ -1197,13 +1197,17 @@ func ExampleS3_ListBuckets_shared00() {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To list in-progress multipart uploads on a bucket
|
||||
// List next set of multipart uploads when previous result is truncated
|
||||
//
|
||||
// The following example lists in-progress multipart uploads on a specific bucket.
|
||||
// The following example specifies the upload-id-marker and key-marker from previous
|
||||
// truncated response to retrieve next setup of multipart uploads.
|
||||
func ExampleS3_ListMultipartUploads_shared00() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.ListMultipartUploadsInput{
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
KeyMarker: aws.String("nextkeyfrompreviousresponse"),
|
||||
MaxUploads: aws.Int64(2),
|
||||
UploadIdMarker: aws.String("valuefrompreviousresponse"),
|
||||
}
|
||||
|
||||
result, err := svc.ListMultipartUploads(input)
|
||||
@@ -1224,17 +1228,13 @@ func ExampleS3_ListMultipartUploads_shared00() {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// List next set of multipart uploads when previous result is truncated
|
||||
// To list in-progress multipart uploads on a bucket
|
||||
//
|
||||
// The following example specifies the upload-id-marker and key-marker from previous
|
||||
// truncated response to retrieve next setup of multipart uploads.
|
||||
// The following example lists in-progress multipart uploads on a specific bucket.
|
||||
func ExampleS3_ListMultipartUploads_shared01() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.ListMultipartUploadsInput{
|
||||
Bucket: aws.String("examplebucket"),
|
||||
KeyMarker: aws.String("nextkeyfrompreviousresponse"),
|
||||
MaxUploads: aws.Int64(2),
|
||||
UploadIdMarker: aws.String("valuefrompreviousresponse"),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
}
|
||||
|
||||
result, err := svc.ListMultipartUploads(input)
|
||||
@@ -1804,45 +1804,12 @@ func ExampleS3_PutBucketWebsite_shared00() {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To upload an object and specify server-side encryption and object tags
|
||||
//
|
||||
// The following example uploads and object. The request specifies the optional server-side
|
||||
// encryption option. The request also specifies optional object tags. If the bucket
|
||||
// is versioning enabled, S3 returns version ID in response.
|
||||
func ExampleS3_PutObject_shared00() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.PutObjectInput{
|
||||
Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("exampleobject"),
|
||||
ServerSideEncryption: aws.String("AES256"),
|
||||
Tagging: aws.String("key1=value1&key2=value2"),
|
||||
}
|
||||
|
||||
result, err := svc.PutObject(input)
|
||||
if err != nil {
|
||||
if aerr, ok := err.(awserr.Error); ok {
|
||||
switch aerr.Code() {
|
||||
default:
|
||||
fmt.Println(aerr.Error())
|
||||
}
|
||||
} else {
|
||||
// Print the error, cast err to awserr.Error to get the Code and
|
||||
// Message from an error.
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To upload an object and specify canned ACL.
|
||||
//
|
||||
// The following example uploads and object. The request specifies optional canned ACL
|
||||
// (access control list) to all READ access to authenticated users. If the bucket is
|
||||
// versioning enabled, S3 returns version ID in response.
|
||||
func ExampleS3_PutObject_shared01() {
|
||||
func ExampleS3_PutObject_shared00() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.PutObjectInput{
|
||||
ACL: aws.String("authenticated-read"),
|
||||
@@ -1869,72 +1836,11 @@ func ExampleS3_PutObject_shared01() {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To upload an object
|
||||
//
|
||||
// The following example uploads an object to a versioning-enabled bucket. The source
|
||||
// file is specified using Windows file syntax. S3 returns VersionId of the newly created
|
||||
// object.
|
||||
func ExampleS3_PutObject_shared02() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.PutObjectInput{
|
||||
Body: aws.ReadSeekCloser(strings.NewReader("HappyFace.jpg")),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("HappyFace.jpg"),
|
||||
}
|
||||
|
||||
result, err := svc.PutObject(input)
|
||||
if err != nil {
|
||||
if aerr, ok := err.(awserr.Error); ok {
|
||||
switch aerr.Code() {
|
||||
default:
|
||||
fmt.Println(aerr.Error())
|
||||
}
|
||||
} else {
|
||||
// Print the error, cast err to awserr.Error to get the Code and
|
||||
// Message from an error.
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To create an object.
|
||||
//
|
||||
// The following example creates an object. If the bucket is versioning enabled, S3
|
||||
// returns version ID in response.
|
||||
func ExampleS3_PutObject_shared03() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.PutObjectInput{
|
||||
Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("objectkey"),
|
||||
}
|
||||
|
||||
result, err := svc.PutObject(input)
|
||||
if err != nil {
|
||||
if aerr, ok := err.(awserr.Error); ok {
|
||||
switch aerr.Code() {
|
||||
default:
|
||||
fmt.Println(aerr.Error())
|
||||
}
|
||||
} else {
|
||||
// Print the error, cast err to awserr.Error to get the Code and
|
||||
// Message from an error.
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To upload an object and specify optional tags
|
||||
//
|
||||
// The following example uploads an object. The request specifies optional object tags.
|
||||
// The bucket is versioned, therefore S3 returns version ID of the newly created object.
|
||||
func ExampleS3_PutObject_shared04() {
|
||||
func ExampleS3_PutObject_shared01() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.PutObjectInput{
|
||||
Body: aws.ReadSeekCloser(strings.NewReader("c:\\HappyFace.jpg")),
|
||||
@@ -1965,7 +1871,7 @@ func ExampleS3_PutObject_shared04() {
|
||||
//
|
||||
// The following example creates an object. The request also specifies optional metadata.
|
||||
// If the bucket is versioning enabled, S3 returns version ID in response.
|
||||
func ExampleS3_PutObject_shared05() {
|
||||
func ExampleS3_PutObject_shared02() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.PutObjectInput{
|
||||
Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")),
|
||||
@@ -1995,6 +1901,100 @@ func ExampleS3_PutObject_shared05() {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To upload an object and specify server-side encryption and object tags
|
||||
//
|
||||
// The following example uploads and object. The request specifies the optional server-side
|
||||
// encryption option. The request also specifies optional object tags. If the bucket
|
||||
// is versioning enabled, S3 returns version ID in response.
|
||||
func ExampleS3_PutObject_shared03() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.PutObjectInput{
|
||||
Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("exampleobject"),
|
||||
ServerSideEncryption: aws.String("AES256"),
|
||||
Tagging: aws.String("key1=value1&key2=value2"),
|
||||
}
|
||||
|
||||
result, err := svc.PutObject(input)
|
||||
if err != nil {
|
||||
if aerr, ok := err.(awserr.Error); ok {
|
||||
switch aerr.Code() {
|
||||
default:
|
||||
fmt.Println(aerr.Error())
|
||||
}
|
||||
} else {
|
||||
// Print the error, cast err to awserr.Error to get the Code and
|
||||
// Message from an error.
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To create an object.
|
||||
//
|
||||
// The following example creates an object. If the bucket is versioning enabled, S3
|
||||
// returns version ID in response.
|
||||
func ExampleS3_PutObject_shared04() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.PutObjectInput{
|
||||
Body: aws.ReadSeekCloser(strings.NewReader("filetoupload")),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("objectkey"),
|
||||
}
|
||||
|
||||
result, err := svc.PutObject(input)
|
||||
if err != nil {
|
||||
if aerr, ok := err.(awserr.Error); ok {
|
||||
switch aerr.Code() {
|
||||
default:
|
||||
fmt.Println(aerr.Error())
|
||||
}
|
||||
} else {
|
||||
// Print the error, cast err to awserr.Error to get the Code and
|
||||
// Message from an error.
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To upload an object
|
||||
//
|
||||
// The following example uploads an object to a versioning-enabled bucket. The source
|
||||
// file is specified using Windows file syntax. S3 returns VersionId of the newly created
|
||||
// object.
|
||||
func ExampleS3_PutObject_shared05() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.PutObjectInput{
|
||||
Body: aws.ReadSeekCloser(strings.NewReader("HappyFace.jpg")),
|
||||
Bucket: aws.String("examplebucket"),
|
||||
Key: aws.String("HappyFace.jpg"),
|
||||
}
|
||||
|
||||
result, err := svc.PutObject(input)
|
||||
if err != nil {
|
||||
if aerr, ok := err.(awserr.Error); ok {
|
||||
switch aerr.Code() {
|
||||
default:
|
||||
fmt.Println(aerr.Error())
|
||||
}
|
||||
} else {
|
||||
// Print the error, cast err to awserr.Error to get the Code and
|
||||
// Message from an error.
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To upload an object (specify optional headers)
|
||||
//
|
||||
// The following example uploads an object. The request specifies optional request headers
|
||||
@@ -2171,11 +2171,43 @@ func ExampleS3_UploadPart_shared00() {
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To upload a part by copying data from an existing object as data source
|
||||
//
|
||||
// The following example uploads a part of a multipart upload by copying data from an
|
||||
// existing object as data source.
|
||||
func ExampleS3_UploadPartCopy_shared00() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.UploadPartCopyInput{
|
||||
Bucket: aws.String("examplebucket"),
|
||||
CopySource: aws.String("/bucketname/sourceobjectkey"),
|
||||
Key: aws.String("examplelargeobject"),
|
||||
PartNumber: aws.Int64(1),
|
||||
UploadId: aws.String("exampleuoh_10OhKhT7YukE9bjzTPRiuaCotmZM_pFngJFir9OZNrSr5cWa3cq3LZSUsfjI4FI7PkP91We7Nrw--"),
|
||||
}
|
||||
|
||||
result, err := svc.UploadPartCopy(input)
|
||||
if err != nil {
|
||||
if aerr, ok := err.(awserr.Error); ok {
|
||||
switch aerr.Code() {
|
||||
default:
|
||||
fmt.Println(aerr.Error())
|
||||
}
|
||||
} else {
|
||||
// Print the error, cast err to awserr.Error to get the Code and
|
||||
// Message from an error.
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To upload a part by copying byte range from an existing object as data source
|
||||
//
|
||||
// The following example uploads a part of a multipart upload by copying a specified
|
||||
// byte range from an existing object as data source.
|
||||
func ExampleS3_UploadPartCopy_shared00() {
|
||||
func ExampleS3_UploadPartCopy_shared01() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.UploadPartCopyInput{
|
||||
Bucket: aws.String("examplebucket"),
|
||||
@@ -2203,35 +2235,3 @@ func ExampleS3_UploadPartCopy_shared00() {
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// To upload a part by copying data from an existing object as data source
|
||||
//
|
||||
// The following example uploads a part of a multipart upload by copying data from an
|
||||
// existing object as data source.
|
||||
func ExampleS3_UploadPartCopy_shared01() {
|
||||
svc := s3.New(session.New())
|
||||
input := &s3.UploadPartCopyInput{
|
||||
Bucket: aws.String("examplebucket"),
|
||||
CopySource: aws.String("/bucketname/sourceobjectkey"),
|
||||
Key: aws.String("examplelargeobject"),
|
||||
PartNumber: aws.Int64(1),
|
||||
UploadId: aws.String("exampleuoh_10OhKhT7YukE9bjzTPRiuaCotmZM_pFngJFir9OZNrSr5cWa3cq3LZSUsfjI4FI7PkP91We7Nrw--"),
|
||||
}
|
||||
|
||||
result, err := svc.UploadPartCopy(input)
|
||||
if err != nil {
|
||||
if aerr, ok := err.(awserr.Error); ok {
|
||||
switch aerr.Code() {
|
||||
default:
|
||||
fmt.Println(aerr.Error())
|
||||
}
|
||||
} else {
|
||||
// Print the error, cast err to awserr.Error to get the Code and
|
||||
// Message from an error.
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
+52
@@ -1,7 +1,10 @@
|
||||
package s3_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -125,3 +128,52 @@ func TestHostStyleBucketGetBucketLocation(t *testing.T) {
|
||||
t.Errorf("expect %s to be in %s", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVirtualHostStyleSuite(t *testing.T) {
|
||||
f, err := os.Open(filepath.Join("testdata", "virtual_host.json"))
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, %v", err)
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
Bucket string
|
||||
Region string
|
||||
UseDualStack bool
|
||||
UseS3Accelerate bool
|
||||
ConfiguredAddressingStyle string
|
||||
|
||||
ExpectedURI string
|
||||
}{}
|
||||
|
||||
decoder := json.NewDecoder(f)
|
||||
if err := decoder.Decode(&cases); err != nil {
|
||||
t.Fatalf("expect no error, %v", err)
|
||||
}
|
||||
|
||||
const testPathStyle = "path"
|
||||
for i, c := range cases {
|
||||
svc := s3.New(unit.Session, &aws.Config{
|
||||
Region: &c.Region,
|
||||
UseDualStack: &c.UseDualStack,
|
||||
S3UseAccelerate: &c.UseS3Accelerate,
|
||||
S3ForcePathStyle: aws.Bool(c.ConfiguredAddressingStyle == testPathStyle),
|
||||
})
|
||||
|
||||
req, _ := svc.HeadBucketRequest(&s3.HeadBucketInput{
|
||||
Bucket: &c.Bucket,
|
||||
})
|
||||
req.Build()
|
||||
if req.Error != nil {
|
||||
t.Fatalf("expect no error, got %v", req.Error)
|
||||
}
|
||||
|
||||
// Trim trailing '/' that are added by the SDK but not in the tests.
|
||||
actualURI := strings.TrimRightFunc(
|
||||
req.HTTPRequest.URL.String(),
|
||||
func(r rune) bool { return r == '/' },
|
||||
)
|
||||
if e, a := c.ExpectedURI, actualURI; e != a {
|
||||
t.Errorf("%d, expect\n%s\nurl to be\n%s", i, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
|
||||
|
||||
// +build go1.10,integration
|
||||
|
||||
package s3_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/awstesting/integration"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
)
|
||||
|
||||
var _ aws.Config
|
||||
var _ awserr.Error
|
||||
var _ request.Request
|
||||
|
||||
func TestInteg_00_ListBuckets(t *testing.T) {
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancelFn()
|
||||
|
||||
sess := integration.SessionWithDefaultRegion("us-west-2")
|
||||
svc := s3.New(sess)
|
||||
params := &s3.ListBucketsInput{}
|
||||
_, err := svc.ListBucketsWithContext(ctx, params)
|
||||
if err != nil {
|
||||
t.Errorf("expect no error, got %v", err)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -7,7 +7,7 @@ const (
|
||||
var aescbcPadding = aescbcPadder{pkcs7Padder{16}}
|
||||
|
||||
// AESCBCPadder is used to pad AES encrypted and decrypted data.
|
||||
// Altough it uses the pkcs5Padder, it isn't following the RFC
|
||||
// Although it uses the pkcs5Padder, it isn't following the RFC
|
||||
// for PKCS5. The only reason why it is called pkcs5Padder is
|
||||
// due to the Name returning PKCS5Padding.
|
||||
var AESCBCPadder = Padder(aescbcPadding)
|
||||
|
||||
+2
-2
@@ -58,7 +58,7 @@ func NewDecryptionClient(prov client.ConfigProvider, options ...func(*Decryption
|
||||
}).decryptHandler,
|
||||
},
|
||||
CEKRegistry: map[string]CEKEntry{
|
||||
AESGCMNoPadding: newAESGCMContentCipher,
|
||||
AESGCMNoPadding: newAESGCMContentCipher,
|
||||
strings.Join([]string{AESCBC, AESCBCPadder.Name()}, "/"): newAESCBCContentCipher,
|
||||
},
|
||||
PadderRegistry: map[string]Padder{
|
||||
@@ -125,7 +125,7 @@ func (c *DecryptionClient) GetObject(input *s3.GetObjectInput) (*s3.GetObjectOut
|
||||
//
|
||||
// GetObjectWithContext is the same as GetObject with the additional support for
|
||||
// Context input parameters. The Context must not be nil. A nil Context will
|
||||
// cause a panic. Use the Context to add deadlining, timeouts, ect. In the future
|
||||
// cause a panic. Use the Context to add deadlining, timeouts, etc. In the future
|
||||
// this may create sub-contexts for individual underlying requests.
|
||||
func (c *DecryptionClient) GetObjectWithContext(ctx aws.Context, input *s3.GetObjectInput, opts ...request.Option) (*s3.GetObjectOutput, error) {
|
||||
req, out := c.GetObjectRequest(input)
|
||||
|
||||
-1
@@ -204,7 +204,6 @@ func TestGetObjectCBC2(t *testing.T) {
|
||||
},
|
||||
Body: ioutil.NopCloser(bytes.NewBuffer(b)),
|
||||
}
|
||||
fmt.Println("HEADER", r.HTTPResponse.Header)
|
||||
out.Metadata = make(map[string]*string)
|
||||
out.Metadata["x-amz-wrap-alg"] = aws.String(s3crypto.KMSWrap)
|
||||
})
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ func (c *EncryptionClient) PutObject(input *s3.PutObjectInput) (*s3.PutObjectOut
|
||||
//
|
||||
// PutObjectWithContext is the same as PutObject with the additional support for
|
||||
// Context input parameters. The Context must not be nil. A nil Context will
|
||||
// cause a panic. Use the Context to add deadlining, timeouts, ect. In the future
|
||||
// cause a panic. Use the Context to add deadlining, timeouts, etc. In the future
|
||||
// this may create sub-contexts for individual underlying requests.
|
||||
func (c *EncryptionClient) PutObjectWithContext(ctx aws.Context, input *s3.PutObjectInput, opts ...request.Option) (*s3.PutObjectOutput, error) {
|
||||
req, out := c.PutObjectRequest(input)
|
||||
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
// +build go1.9,s3crypto_integ
|
||||
|
||||
package s3crypto_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/awstesting/integration"
|
||||
"github.com/aws/aws-sdk-go/service/kms"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/aws/aws-sdk-go/service/s3/s3crypto"
|
||||
)
|
||||
|
||||
func TestInteg_EncryptFixtures(t *testing.T) {
|
||||
sess := integration.SessionWithDefaultRegion("us-west-2")
|
||||
|
||||
const bucket = "aws-s3-shared-tests"
|
||||
const version = "version_2"
|
||||
|
||||
cases := []struct {
|
||||
CEKAlg string
|
||||
KEK, V1, V2, CEK string
|
||||
}{
|
||||
{
|
||||
CEKAlg: "aes_gcm",
|
||||
KEK: "kms", V1: "AWS_SDK_TEST_ALIAS", V2: "us-west-2", CEK: "aes_gcm",
|
||||
},
|
||||
{
|
||||
CEKAlg: "aes_cbc",
|
||||
KEK: "kms", V1: "AWS_SDK_TEST_ALIAS", V2: "us-west-2", CEK: "aes_cbc",
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.CEKAlg, func(t *testing.T) {
|
||||
s3Client := s3.New(sess)
|
||||
|
||||
fixtures := getFixtures(t, s3Client, c.CEKAlg, bucket)
|
||||
builder, masterKey := getEncryptFixtureBuilder(t, c.KEK, c.V1, c.V2, c.CEK)
|
||||
|
||||
encClient := s3crypto.NewEncryptionClient(sess, builder)
|
||||
|
||||
for caseKey, plaintext := range fixtures.Plaintexts {
|
||||
_, err := encClient.PutObject(&s3.PutObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: aws.String(
|
||||
fmt.Sprintf("%s/%s/language_Go/ciphertext_test_case_%s",
|
||||
fixtures.BaseFolder, version, caseKey),
|
||||
),
|
||||
Body: bytes.NewReader(plaintext),
|
||||
Metadata: map[string]*string{
|
||||
"Masterkey": &masterKey,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to upload encrypted fixture, %v", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInteg_DecryptFixtures(t *testing.T) {
|
||||
sess := integration.SessionWithDefaultRegion("us-west-2")
|
||||
|
||||
const bucket = "aws-s3-shared-tests"
|
||||
const version = "version_2"
|
||||
|
||||
cases := []struct {
|
||||
CEKAlg string
|
||||
Lang string
|
||||
}{
|
||||
{CEKAlg: "aes_cbc", Lang: "Go"},
|
||||
{CEKAlg: "aes_gcm", Lang: "Go"},
|
||||
{CEKAlg: "aes_cbc", Lang: "Java"},
|
||||
{CEKAlg: "aes_gcm", Lang: "Java"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.CEKAlg+"-"+c.Lang, func(t *testing.T) {
|
||||
decClient := s3crypto.NewDecryptionClient(sess)
|
||||
s3Client := s3.New(sess)
|
||||
|
||||
fixtures := getFixtures(t, s3Client, c.CEKAlg, bucket)
|
||||
ciphertexts := decryptFixtures(t, decClient, s3Client, fixtures, bucket, c.Lang, version)
|
||||
|
||||
for caseKey, ciphertext := range ciphertexts {
|
||||
if e, a := len(fixtures.Plaintexts[caseKey]), len(ciphertext); e != a {
|
||||
t.Errorf("expect %v text len, got %v", e, a)
|
||||
}
|
||||
if e, a := fixtures.Plaintexts[caseKey], ciphertext; !bytes.Equal(e, a) {
|
||||
t.Errorf("expect %v text, got %v", e, a)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testFixtures struct {
|
||||
BaseFolder string
|
||||
Plaintexts map[string][]byte
|
||||
}
|
||||
|
||||
func getFixtures(t *testing.T, s3Client *s3.S3, cekAlg, bucket string) testFixtures {
|
||||
t.Helper()
|
||||
|
||||
prefix := "plaintext_test_case_"
|
||||
baseFolder := "crypto_tests/" + cekAlg
|
||||
|
||||
out, err := s3Client.ListObjects(&s3.ListObjectsInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Prefix: aws.String(baseFolder + "/" + prefix),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to list fixtures %v", err)
|
||||
}
|
||||
|
||||
plaintexts := map[string][]byte{}
|
||||
for _, obj := range out.Contents {
|
||||
ptObj, err := s3Client.GetObject(&s3.GetObjectInput{
|
||||
Bucket: aws.String(bucket),
|
||||
Key: obj.Key,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get fixture object %s, %v", *obj.Key, err)
|
||||
}
|
||||
caseKey := strings.TrimPrefix(*obj.Key, baseFolder+"/"+prefix)
|
||||
plaintext, err := ioutil.ReadAll(ptObj.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to read fixture object %s, %v", *obj.Key, err)
|
||||
}
|
||||
|
||||
plaintexts[caseKey] = plaintext
|
||||
}
|
||||
|
||||
return testFixtures{
|
||||
BaseFolder: baseFolder,
|
||||
Plaintexts: plaintexts,
|
||||
}
|
||||
}
|
||||
|
||||
func getEncryptFixtureBuilder(t *testing.T, kek, v1, v2, cek string,
|
||||
) (builder s3crypto.ContentCipherBuilder, masterKey string) {
|
||||
t.Helper()
|
||||
|
||||
var handler s3crypto.CipherDataGenerator
|
||||
switch kek {
|
||||
case "kms":
|
||||
arn, err := getAliasInformation(v1, v2)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get fixture alias info for %s, %v", v1, err)
|
||||
}
|
||||
|
||||
masterKey = base64.StdEncoding.EncodeToString([]byte(arn))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to encode alias's arn %v", err)
|
||||
}
|
||||
|
||||
kmsSvc := kms.New(integration.Session, &aws.Config{
|
||||
Region: &v2,
|
||||
})
|
||||
handler = s3crypto.NewKMSKeyGenerator(kmsSvc, arn)
|
||||
default:
|
||||
t.Fatalf("unknown fixture KEK, %v", kek)
|
||||
}
|
||||
|
||||
switch cek {
|
||||
case "aes_gcm":
|
||||
builder = s3crypto.AESGCMContentCipherBuilder(handler)
|
||||
case "aes_cbc":
|
||||
builder = s3crypto.AESCBCContentCipherBuilder(handler, s3crypto.AESCBCPadder)
|
||||
default:
|
||||
t.Fatalf("unknown fixture CEK, %v", cek)
|
||||
}
|
||||
|
||||
return builder, masterKey
|
||||
}
|
||||
|
||||
func getAliasInformation(alias, region string) (string, error) {
|
||||
arn := ""
|
||||
svc := kms.New(integration.Session, &aws.Config{
|
||||
Region: ®ion,
|
||||
})
|
||||
|
||||
truncated := true
|
||||
var marker *string
|
||||
for truncated {
|
||||
out, err := svc.ListAliases(&kms.ListAliasesInput{
|
||||
Marker: marker,
|
||||
})
|
||||
if err != nil {
|
||||
return arn, err
|
||||
}
|
||||
for _, aliasEntry := range out.Aliases {
|
||||
if *aliasEntry.AliasName == "alias/"+alias {
|
||||
return *aliasEntry.AliasArn, nil
|
||||
}
|
||||
}
|
||||
truncated = *out.Truncated
|
||||
marker = out.NextMarker
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("kms alias %s does not exist", alias)
|
||||
}
|
||||
|
||||
func decryptFixtures(t *testing.T, decClient *s3crypto.DecryptionClient, s3Client *s3.S3,
|
||||
fixtures testFixtures, bucket, lang, version string,
|
||||
) map[string][]byte {
|
||||
t.Helper()
|
||||
|
||||
prefix := "ciphertext_test_case_"
|
||||
lang = "language_" + lang
|
||||
|
||||
ciphertexts := map[string][]byte{}
|
||||
for caseKey := range fixtures.Plaintexts {
|
||||
cipherKey := fixtures.BaseFolder + "/" + version + "/" + lang + "/" + prefix + caseKey
|
||||
|
||||
// To get metadata for encryption key
|
||||
ctObj, err := s3Client.GetObject(&s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &cipherKey,
|
||||
})
|
||||
if err != nil {
|
||||
// TODO error?
|
||||
continue
|
||||
}
|
||||
|
||||
// We don't support wrap, so skip it
|
||||
if ctObj.Metadata["X-Amz-Wrap-Alg"] == nil || *ctObj.Metadata["X-Amz-Wrap-Alg"] != "kms" {
|
||||
continue
|
||||
}
|
||||
|
||||
ctObj, err = decClient.GetObject(&s3.GetObjectInput{
|
||||
Bucket: &bucket,
|
||||
Key: &cipherKey,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get encrypted object %v", err)
|
||||
}
|
||||
|
||||
ciphertext, err := ioutil.ReadAll(ctObj.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read object data %v", err)
|
||||
}
|
||||
ciphertexts[caseKey] = ciphertext
|
||||
}
|
||||
|
||||
return ciphertexts
|
||||
}
|
||||
+20
-1
@@ -39,7 +39,7 @@ func NewKMSKeyGenerator(kmsClient kmsiface.KMSAPI, cmkID string) CipherDataGener
|
||||
// sess := session.New(&aws.Config{})
|
||||
// cmkID := "arn to key"
|
||||
// matdesc := s3crypto.MaterialDescription{}
|
||||
// handler, err := s3crypto.NewKMSKeyGeneratorWithMatDesc(kms.New(sess), cmkID, matdesc)
|
||||
// handler := s3crypto.NewKMSKeyGeneratorWithMatDesc(kms.New(sess), cmkID, matdesc)
|
||||
func NewKMSKeyGeneratorWithMatDesc(kmsClient kmsiface.KMSAPI, cmkID string, matdesc MaterialDescription) CipherDataGenerator {
|
||||
if matdesc == nil {
|
||||
matdesc = MaterialDescription{}
|
||||
@@ -57,6 +57,25 @@ func NewKMSKeyGeneratorWithMatDesc(kmsClient kmsiface.KMSAPI, cmkID string, matd
|
||||
return kp
|
||||
}
|
||||
|
||||
// NewKMSWrapEntry builds returns a new KMS key provider and its decrypt handler.
|
||||
//
|
||||
// Example:
|
||||
// sess := session.New(&aws.Config{})
|
||||
// customKMSClient := kms.New(sess)
|
||||
// decryptHandler := s3crypto.NewKMSWrapEntry(customKMSClient)
|
||||
//
|
||||
// svc := s3crypto.NewDecryptionClient(sess, func(svc *s3crypto.DecryptionClient) {
|
||||
// svc.WrapRegistry[s3crypto.KMSWrap] = decryptHandler
|
||||
// }))
|
||||
func NewKMSWrapEntry(kmsClient kmsiface.KMSAPI) WrapEntry {
|
||||
// These values are read only making them thread safe
|
||||
kp := &kmsKeyHandler{
|
||||
kms: kmsClient,
|
||||
}
|
||||
|
||||
return kp.decryptHandler
|
||||
}
|
||||
|
||||
// decryptHandler initializes a KMS keyprovider with a material description. This
|
||||
// is used with Decrypting kms content, due to the cmkID being in the material description.
|
||||
func (kp kmsKeyHandler) decryptHandler(env Envelope) (CipherDataDecrypter, error) {
|
||||
|
||||
+44
@@ -136,6 +136,10 @@ type S3API interface {
|
||||
DeleteObjectsWithContext(aws.Context, *s3.DeleteObjectsInput, ...request.Option) (*s3.DeleteObjectsOutput, error)
|
||||
DeleteObjectsRequest(*s3.DeleteObjectsInput) (*request.Request, *s3.DeleteObjectsOutput)
|
||||
|
||||
DeletePublicAccessBlock(*s3.DeletePublicAccessBlockInput) (*s3.DeletePublicAccessBlockOutput, error)
|
||||
DeletePublicAccessBlockWithContext(aws.Context, *s3.DeletePublicAccessBlockInput, ...request.Option) (*s3.DeletePublicAccessBlockOutput, error)
|
||||
DeletePublicAccessBlockRequest(*s3.DeletePublicAccessBlockInput) (*request.Request, *s3.DeletePublicAccessBlockOutput)
|
||||
|
||||
GetBucketAccelerateConfiguration(*s3.GetBucketAccelerateConfigurationInput) (*s3.GetBucketAccelerateConfigurationOutput, error)
|
||||
GetBucketAccelerateConfigurationWithContext(aws.Context, *s3.GetBucketAccelerateConfigurationInput, ...request.Option) (*s3.GetBucketAccelerateConfigurationOutput, error)
|
||||
GetBucketAccelerateConfigurationRequest(*s3.GetBucketAccelerateConfigurationInput) (*request.Request, *s3.GetBucketAccelerateConfigurationOutput)
|
||||
@@ -192,6 +196,10 @@ type S3API interface {
|
||||
GetBucketPolicyWithContext(aws.Context, *s3.GetBucketPolicyInput, ...request.Option) (*s3.GetBucketPolicyOutput, error)
|
||||
GetBucketPolicyRequest(*s3.GetBucketPolicyInput) (*request.Request, *s3.GetBucketPolicyOutput)
|
||||
|
||||
GetBucketPolicyStatus(*s3.GetBucketPolicyStatusInput) (*s3.GetBucketPolicyStatusOutput, error)
|
||||
GetBucketPolicyStatusWithContext(aws.Context, *s3.GetBucketPolicyStatusInput, ...request.Option) (*s3.GetBucketPolicyStatusOutput, error)
|
||||
GetBucketPolicyStatusRequest(*s3.GetBucketPolicyStatusInput) (*request.Request, *s3.GetBucketPolicyStatusOutput)
|
||||
|
||||
GetBucketReplication(*s3.GetBucketReplicationInput) (*s3.GetBucketReplicationOutput, error)
|
||||
GetBucketReplicationWithContext(aws.Context, *s3.GetBucketReplicationInput, ...request.Option) (*s3.GetBucketReplicationOutput, error)
|
||||
GetBucketReplicationRequest(*s3.GetBucketReplicationInput) (*request.Request, *s3.GetBucketReplicationOutput)
|
||||
@@ -220,6 +228,18 @@ type S3API interface {
|
||||
GetObjectAclWithContext(aws.Context, *s3.GetObjectAclInput, ...request.Option) (*s3.GetObjectAclOutput, error)
|
||||
GetObjectAclRequest(*s3.GetObjectAclInput) (*request.Request, *s3.GetObjectAclOutput)
|
||||
|
||||
GetObjectLegalHold(*s3.GetObjectLegalHoldInput) (*s3.GetObjectLegalHoldOutput, error)
|
||||
GetObjectLegalHoldWithContext(aws.Context, *s3.GetObjectLegalHoldInput, ...request.Option) (*s3.GetObjectLegalHoldOutput, error)
|
||||
GetObjectLegalHoldRequest(*s3.GetObjectLegalHoldInput) (*request.Request, *s3.GetObjectLegalHoldOutput)
|
||||
|
||||
GetObjectLockConfiguration(*s3.GetObjectLockConfigurationInput) (*s3.GetObjectLockConfigurationOutput, error)
|
||||
GetObjectLockConfigurationWithContext(aws.Context, *s3.GetObjectLockConfigurationInput, ...request.Option) (*s3.GetObjectLockConfigurationOutput, error)
|
||||
GetObjectLockConfigurationRequest(*s3.GetObjectLockConfigurationInput) (*request.Request, *s3.GetObjectLockConfigurationOutput)
|
||||
|
||||
GetObjectRetention(*s3.GetObjectRetentionInput) (*s3.GetObjectRetentionOutput, error)
|
||||
GetObjectRetentionWithContext(aws.Context, *s3.GetObjectRetentionInput, ...request.Option) (*s3.GetObjectRetentionOutput, error)
|
||||
GetObjectRetentionRequest(*s3.GetObjectRetentionInput) (*request.Request, *s3.GetObjectRetentionOutput)
|
||||
|
||||
GetObjectTagging(*s3.GetObjectTaggingInput) (*s3.GetObjectTaggingOutput, error)
|
||||
GetObjectTaggingWithContext(aws.Context, *s3.GetObjectTaggingInput, ...request.Option) (*s3.GetObjectTaggingOutput, error)
|
||||
GetObjectTaggingRequest(*s3.GetObjectTaggingInput) (*request.Request, *s3.GetObjectTaggingOutput)
|
||||
@@ -228,6 +248,10 @@ type S3API interface {
|
||||
GetObjectTorrentWithContext(aws.Context, *s3.GetObjectTorrentInput, ...request.Option) (*s3.GetObjectTorrentOutput, error)
|
||||
GetObjectTorrentRequest(*s3.GetObjectTorrentInput) (*request.Request, *s3.GetObjectTorrentOutput)
|
||||
|
||||
GetPublicAccessBlock(*s3.GetPublicAccessBlockInput) (*s3.GetPublicAccessBlockOutput, error)
|
||||
GetPublicAccessBlockWithContext(aws.Context, *s3.GetPublicAccessBlockInput, ...request.Option) (*s3.GetPublicAccessBlockOutput, error)
|
||||
GetPublicAccessBlockRequest(*s3.GetPublicAccessBlockInput) (*request.Request, *s3.GetPublicAccessBlockOutput)
|
||||
|
||||
HeadBucket(*s3.HeadBucketInput) (*s3.HeadBucketOutput, error)
|
||||
HeadBucketWithContext(aws.Context, *s3.HeadBucketInput, ...request.Option) (*s3.HeadBucketOutput, error)
|
||||
HeadBucketRequest(*s3.HeadBucketInput) (*request.Request, *s3.HeadBucketOutput)
|
||||
@@ -367,14 +391,34 @@ type S3API interface {
|
||||
PutObjectAclWithContext(aws.Context, *s3.PutObjectAclInput, ...request.Option) (*s3.PutObjectAclOutput, error)
|
||||
PutObjectAclRequest(*s3.PutObjectAclInput) (*request.Request, *s3.PutObjectAclOutput)
|
||||
|
||||
PutObjectLegalHold(*s3.PutObjectLegalHoldInput) (*s3.PutObjectLegalHoldOutput, error)
|
||||
PutObjectLegalHoldWithContext(aws.Context, *s3.PutObjectLegalHoldInput, ...request.Option) (*s3.PutObjectLegalHoldOutput, error)
|
||||
PutObjectLegalHoldRequest(*s3.PutObjectLegalHoldInput) (*request.Request, *s3.PutObjectLegalHoldOutput)
|
||||
|
||||
PutObjectLockConfiguration(*s3.PutObjectLockConfigurationInput) (*s3.PutObjectLockConfigurationOutput, error)
|
||||
PutObjectLockConfigurationWithContext(aws.Context, *s3.PutObjectLockConfigurationInput, ...request.Option) (*s3.PutObjectLockConfigurationOutput, error)
|
||||
PutObjectLockConfigurationRequest(*s3.PutObjectLockConfigurationInput) (*request.Request, *s3.PutObjectLockConfigurationOutput)
|
||||
|
||||
PutObjectRetention(*s3.PutObjectRetentionInput) (*s3.PutObjectRetentionOutput, error)
|
||||
PutObjectRetentionWithContext(aws.Context, *s3.PutObjectRetentionInput, ...request.Option) (*s3.PutObjectRetentionOutput, error)
|
||||
PutObjectRetentionRequest(*s3.PutObjectRetentionInput) (*request.Request, *s3.PutObjectRetentionOutput)
|
||||
|
||||
PutObjectTagging(*s3.PutObjectTaggingInput) (*s3.PutObjectTaggingOutput, error)
|
||||
PutObjectTaggingWithContext(aws.Context, *s3.PutObjectTaggingInput, ...request.Option) (*s3.PutObjectTaggingOutput, error)
|
||||
PutObjectTaggingRequest(*s3.PutObjectTaggingInput) (*request.Request, *s3.PutObjectTaggingOutput)
|
||||
|
||||
PutPublicAccessBlock(*s3.PutPublicAccessBlockInput) (*s3.PutPublicAccessBlockOutput, error)
|
||||
PutPublicAccessBlockWithContext(aws.Context, *s3.PutPublicAccessBlockInput, ...request.Option) (*s3.PutPublicAccessBlockOutput, error)
|
||||
PutPublicAccessBlockRequest(*s3.PutPublicAccessBlockInput) (*request.Request, *s3.PutPublicAccessBlockOutput)
|
||||
|
||||
RestoreObject(*s3.RestoreObjectInput) (*s3.RestoreObjectOutput, error)
|
||||
RestoreObjectWithContext(aws.Context, *s3.RestoreObjectInput, ...request.Option) (*s3.RestoreObjectOutput, error)
|
||||
RestoreObjectRequest(*s3.RestoreObjectInput) (*request.Request, *s3.RestoreObjectOutput)
|
||||
|
||||
SelectObjectContent(*s3.SelectObjectContentInput) (*s3.SelectObjectContentOutput, error)
|
||||
SelectObjectContentWithContext(aws.Context, *s3.SelectObjectContentInput, ...request.Option) (*s3.SelectObjectContentOutput, error)
|
||||
SelectObjectContentRequest(*s3.SelectObjectContentInput) (*request.Request, *s3.SelectObjectContentOutput)
|
||||
|
||||
UploadPart(*s3.UploadPartInput) (*s3.UploadPartOutput, error)
|
||||
UploadPartWithContext(aws.Context, *s3.UploadPartInput, ...request.Option) (*s3.UploadPartOutput, error)
|
||||
UploadPartRequest(*s3.UploadPartInput) (*request.Request, *s3.UploadPartOutput)
|
||||
|
||||
+8
-3
@@ -273,7 +273,7 @@ type DeleteObjectsIterator struct {
|
||||
inc bool
|
||||
}
|
||||
|
||||
// Next will increment the default iterator's index and and ensure that there
|
||||
// Next will increment the default iterator's index and ensure that there
|
||||
// is another object to iterator to.
|
||||
func (iter *DeleteObjectsIterator) Next() bool {
|
||||
if iter.inc {
|
||||
@@ -338,6 +338,11 @@ func (d *BatchDelete) Delete(ctx aws.Context, iter BatchDeleteIterator) error {
|
||||
}
|
||||
}
|
||||
|
||||
// iter.Next() could return false (above) plus populate iter.Err()
|
||||
if iter.Err() != nil {
|
||||
errs = append(errs, newError(iter.Err(), nil, nil))
|
||||
}
|
||||
|
||||
if input != nil && len(input.Delete.Objects) > 0 {
|
||||
if err := deleteBatch(ctx, d, input, objects); err != nil {
|
||||
errs = append(errs, err...)
|
||||
@@ -453,7 +458,7 @@ type DownloadObjectsIterator struct {
|
||||
inc bool
|
||||
}
|
||||
|
||||
// Next will increment the default iterator's index and and ensure that there
|
||||
// Next will increment the default iterator's index and ensure that there
|
||||
// is another object to iterator to.
|
||||
func (batcher *DownloadObjectsIterator) Next() bool {
|
||||
if batcher.inc {
|
||||
@@ -492,7 +497,7 @@ type UploadObjectsIterator struct {
|
||||
inc bool
|
||||
}
|
||||
|
||||
// Next will increment the default iterator's index and and ensure that there
|
||||
// Next will increment the default iterator's index and ensure that there
|
||||
// is another object to iterator to.
|
||||
func (batcher *UploadObjectsIterator) Next() bool {
|
||||
if batcher.inc {
|
||||
|
||||
+15
-5
@@ -39,6 +39,8 @@ type Downloader struct {
|
||||
// The number of goroutines to spin up in parallel when sending parts.
|
||||
// If this is set to zero, the DefaultDownloadConcurrency value will be used.
|
||||
//
|
||||
// Concurrency of 1 will download the parts sequentially.
|
||||
//
|
||||
// Concurrency is ignored if the Range input parameter is provided.
|
||||
Concurrency int
|
||||
|
||||
@@ -124,7 +126,8 @@ type maxRetrier interface {
|
||||
}
|
||||
|
||||
// Download downloads an object in S3 and writes the payload into w using
|
||||
// concurrent GET requests.
|
||||
// concurrent GET requests. The n int64 returned is the size of the object downloaded
|
||||
// in bytes.
|
||||
//
|
||||
// Additional functional options can be provided to configure the individual
|
||||
// download. These options are copies of the Downloader instance Download is called from.
|
||||
@@ -135,6 +138,9 @@ type maxRetrier interface {
|
||||
// The w io.WriterAt can be satisfied by an os.File to do multipart concurrent
|
||||
// downloads, or in memory []byte wrapper using aws.WriteAtBuffer.
|
||||
//
|
||||
// Specifying a Downloader.Concurrency of 1 will cause the Downloader to
|
||||
// download the parts from S3 sequentially.
|
||||
//
|
||||
// If the GetObjectInput's Range value is provided that will cause the downloader
|
||||
// to perform a single GetObjectInput request for that object's range. This will
|
||||
// caused the part size, and concurrency configurations to be ignored.
|
||||
@@ -143,11 +149,12 @@ func (d Downloader) Download(w io.WriterAt, input *s3.GetObjectInput, options ..
|
||||
}
|
||||
|
||||
// DownloadWithContext downloads an object in S3 and writes the payload into w
|
||||
// using concurrent GET requests.
|
||||
// using concurrent GET requests. The n int64 returned is the size of the object downloaded
|
||||
// in bytes.
|
||||
//
|
||||
// DownloadWithContext is the same as Download with the additional support for
|
||||
// Context input parameters. The Context must not be nil. A nil Context will
|
||||
// cause a panic. Use the Context to add deadlining, timeouts, ect. The
|
||||
// cause a panic. Use the Context to add deadlining, timeouts, etc. The
|
||||
// DownloadWithContext may create sub-contexts for individual underlying
|
||||
// requests.
|
||||
//
|
||||
@@ -160,6 +167,9 @@ func (d Downloader) Download(w io.WriterAt, input *s3.GetObjectInput, options ..
|
||||
// The w io.WriterAt can be satisfied by an os.File to do multipart concurrent
|
||||
// downloads, or in memory []byte wrapper using aws.WriteAtBuffer.
|
||||
//
|
||||
// Specifying a Downloader.Concurrency of 1 will cause the Downloader to
|
||||
// download the parts from S3 sequentially.
|
||||
//
|
||||
// It is safe to call this method concurrently across goroutines.
|
||||
//
|
||||
// If the GetObjectInput's Range value is provided that will cause the downloader
|
||||
@@ -207,14 +217,14 @@ func (d Downloader) DownloadWithContext(ctx aws.Context, w io.WriterAt, input *s
|
||||
//
|
||||
// objects := []s3manager.BatchDownloadObject {
|
||||
// {
|
||||
// Input: &s3.GetObjectInput {
|
||||
// Object: &s3.GetObjectInput {
|
||||
// Bucket: aws.String("bucket"),
|
||||
// Key: aws.String("foo"),
|
||||
// },
|
||||
// Writer: fooFile,
|
||||
// },
|
||||
// {
|
||||
// Input: &s3.GetObjectInput {
|
||||
// Object: &s3.GetObjectInput {
|
||||
// Bucket: aws.String("bucket"),
|
||||
// Key: aws.String("bar"),
|
||||
// },
|
||||
|
||||
+26
-19
@@ -11,6 +11,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -582,10 +583,22 @@ func TestDownload_WithFailure(t *testing.T) {
|
||||
svc := s3.New(unit.Session)
|
||||
svc.Handlers.Send.Clear()
|
||||
|
||||
first := true
|
||||
reqCount := int64(0)
|
||||
startingByte := 0
|
||||
svc.Handlers.Send.PushBack(func(r *request.Request) {
|
||||
if first {
|
||||
first = false
|
||||
switch atomic.LoadInt64(&reqCount) {
|
||||
case 1:
|
||||
// Give a chance for the multipart chunks to be queued up
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
r.HTTPResponse = &http.Response{
|
||||
Header: http.Header{},
|
||||
Body: ioutil.NopCloser(&bytes.Buffer{}),
|
||||
}
|
||||
r.Error = awserr.New("ConnectionError", "some connection error", nil)
|
||||
r.Retryable = aws.Bool(false)
|
||||
|
||||
default:
|
||||
body := bytes.NewReader(make([]byte, s3manager.DefaultDownloadPartSize))
|
||||
r.HTTPResponse = &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
@@ -596,22 +609,18 @@ func TestDownload_WithFailure(t *testing.T) {
|
||||
}
|
||||
r.HTTPResponse.Header.Set("Content-Length", strconv.Itoa(body.Len()))
|
||||
r.HTTPResponse.Header.Set("Content-Range",
|
||||
fmt.Sprintf("bytes 0-%d/%d", body.Len()-1, body.Len()*10))
|
||||
return
|
||||
fmt.Sprintf("bytes %d-%d/%d", startingByte, body.Len()-1, body.Len()*10))
|
||||
|
||||
startingByte += body.Len()
|
||||
if reqCount > 0 {
|
||||
// sleep here to ensure context switching between goroutines
|
||||
time.Sleep(25 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// Give a chance for the multipart chunks to be queued up
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
r.HTTPResponse = &http.Response{
|
||||
Header: http.Header{},
|
||||
Body: ioutil.NopCloser(&bytes.Buffer{}),
|
||||
}
|
||||
r.Error = awserr.New("ConnectionError", "some connection error", nil)
|
||||
r.Retryable = aws.Bool(false)
|
||||
atomic.AddInt64(&reqCount, 1)
|
||||
})
|
||||
|
||||
start := time.Now()
|
||||
d := s3manager.NewDownloaderWithClient(svc, func(d *s3manager.Downloader) {
|
||||
d.Concurrency = 2
|
||||
})
|
||||
@@ -628,10 +637,8 @@ func TestDownload_WithFailure(t *testing.T) {
|
||||
t.Fatalf("expect error, got none")
|
||||
}
|
||||
|
||||
limit := start.Add(5 * time.Second)
|
||||
dur := time.Now().Sub(start)
|
||||
if time.Now().After(limit) {
|
||||
t.Errorf("expect time to be less than %v, took %v", limit, dur)
|
||||
if atomic.LoadInt64(&reqCount) > 3 {
|
||||
t.Errorf("expect no more than 3 requests, but received %d", reqCount)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
// +build integration
|
||||
|
||||
package s3manager_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||
)
|
||||
|
||||
func TestGetBucketRegion(t *testing.T) {
|
||||
expectRegion := aws.StringValue(integSess.Config.Region)
|
||||
|
||||
ctx := aws.BackgroundContext()
|
||||
region, err := s3manager.GetBucketRegion(ctx, integSess,
|
||||
aws.StringValue(bucketName), expectRegion)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, got %v", err)
|
||||
}
|
||||
|
||||
if e, a := expectRegion, region; e != a {
|
||||
t.Errorf("expect %s bucket region, got %s", e, a)
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// +build integration
|
||||
|
||||
package s3manager_test
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/awstesting/integration"
|
||||
"github.com/aws/aws-sdk-go/awstesting/integration/s3integ"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||
)
|
||||
|
||||
func init() {
|
||||
integSess = integration.SessionWithDefaultRegion("us-west-2")
|
||||
}
|
||||
|
||||
var integSess *session.Session
|
||||
var bucketName *string
|
||||
var svc *s3.S3
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
svc = s3.New(integSess)
|
||||
bucketName = aws.String(s3integ.GenerateBucketName())
|
||||
if err := s3integ.SetupTest(svc, *bucketName); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var result int
|
||||
defer func() {
|
||||
if err := s3integ.CleanupTest(svc, *bucketName); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
}
|
||||
if r := recover(); r != nil {
|
||||
fmt.Fprintln(os.Stderr, "S3 integrationt tests paniced,", r)
|
||||
result = 1
|
||||
}
|
||||
os.Exit(result)
|
||||
}()
|
||||
|
||||
result = m.Run()
|
||||
}
|
||||
|
||||
type dlwriter struct {
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func newDLWriter(size int) *dlwriter {
|
||||
return &dlwriter{buf: make([]byte, size)}
|
||||
}
|
||||
|
||||
func (d dlwriter) WriteAt(p []byte, pos int64) (n int, err error) {
|
||||
if pos > int64(len(d.buf)) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
written := 0
|
||||
for i, b := range p {
|
||||
if i >= len(d.buf) {
|
||||
break
|
||||
}
|
||||
d.buf[pos+int64(i)] = b
|
||||
written++
|
||||
}
|
||||
return written, nil
|
||||
}
|
||||
|
||||
func validate(t *testing.T, key string, md5value string) {
|
||||
mgr := s3manager.NewDownloader(integSess)
|
||||
params := &s3.GetObjectInput{Bucket: bucketName, Key: &key}
|
||||
|
||||
w := newDLWriter(1024 * 1024 * 20)
|
||||
n, err := mgr.Download(w, params)
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, got %v", err)
|
||||
}
|
||||
if e, a := md5value, fmt.Sprintf("%x", md5.Sum(w.buf[0:n])); e != a {
|
||||
t.Errorf("expect %s md5 value, got %s", e, a)
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
// +build integration
|
||||
|
||||
package s3manager_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||
)
|
||||
|
||||
var integBuf12MB = make([]byte, 1024*1024*12)
|
||||
var integMD512MB = fmt.Sprintf("%x", md5.Sum(integBuf12MB))
|
||||
|
||||
func TestUploadConcurrently(t *testing.T) {
|
||||
key := "12mb-1"
|
||||
mgr := s3manager.NewUploader(integSess)
|
||||
out, err := mgr.Upload(&s3manager.UploadInput{
|
||||
Bucket: bucketName,
|
||||
Key: &key,
|
||||
Body: bytes.NewReader(integBuf12MB),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, got %v", err)
|
||||
}
|
||||
if len(out.UploadID) == 0 {
|
||||
t.Errorf("expect upload ID but was empty")
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`^https?://.+/` + key + `$`)
|
||||
if e, a := re.String(), out.Location; !re.MatchString(a) {
|
||||
t.Errorf("expect %s to match URL regexp %q, did not", e, a)
|
||||
}
|
||||
|
||||
validate(t, key, integMD512MB)
|
||||
}
|
||||
|
||||
func TestUploadFailCleanup(t *testing.T) {
|
||||
// Break checksum on 2nd part so it fails
|
||||
part := 0
|
||||
svc.Handlers.Build.PushBack(func(r *request.Request) {
|
||||
if r.Operation.Name == "UploadPart" {
|
||||
if part == 1 {
|
||||
r.HTTPRequest.Header.Set("X-Amz-Content-Sha256", "000")
|
||||
}
|
||||
part++
|
||||
}
|
||||
})
|
||||
|
||||
key := "12mb-leave"
|
||||
mgr := s3manager.NewUploaderWithClient(svc, func(u *s3manager.Uploader) {
|
||||
u.LeavePartsOnError = false
|
||||
})
|
||||
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||
Bucket: bucketName,
|
||||
Key: &key,
|
||||
Body: bytes.NewReader(integBuf12MB),
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatalf("expect error, but did not get one")
|
||||
}
|
||||
|
||||
aerr := err.(awserr.Error)
|
||||
if e, a := "MissingRegion", aerr.Code(); strings.Contains(a, e) {
|
||||
t.Errorf("expect %q to not be in error code %q", e, a)
|
||||
}
|
||||
|
||||
uploadID := ""
|
||||
merr := err.(s3manager.MultiUploadFailure)
|
||||
if uploadID = merr.UploadID(); len(uploadID) == 0 {
|
||||
t.Errorf("expect upload ID to not be empty, but was")
|
||||
}
|
||||
|
||||
_, err = svc.ListParts(&s3.ListPartsInput{
|
||||
Bucket: bucketName, Key: &key, UploadId: &uploadID,
|
||||
})
|
||||
if err == nil {
|
||||
t.Errorf("expect error for list parts, but got none")
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+22
-2
@@ -9,13 +9,21 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||
)
|
||||
|
||||
var _ DownloaderAPI = (*s3manager.Downloader)(nil)
|
||||
|
||||
// DownloaderAPI is the interface type for s3manager.Downloader.
|
||||
type DownloaderAPI interface {
|
||||
Download(io.WriterAt, *s3.GetObjectInput, ...func(*s3manager.Downloader)) (int64, error)
|
||||
DownloadWithContext(aws.Context, io.WriterAt, *s3.GetObjectInput, ...func(*s3manager.Downloader)) (int64, error)
|
||||
}
|
||||
|
||||
var _ DownloaderAPI = (*s3manager.Downloader)(nil)
|
||||
// DownloadWithIterator is the interface type for the contained method of the same name.
|
||||
type DownloadWithIterator interface {
|
||||
DownloadWithIterator(aws.Context, s3manager.BatchDownloadIterator, ...func(*s3manager.Downloader)) error
|
||||
}
|
||||
|
||||
var _ UploaderAPI = (*s3manager.Uploader)(nil)
|
||||
var _ UploadWithIterator = (*s3manager.Uploader)(nil)
|
||||
|
||||
// UploaderAPI is the interface type for s3manager.Uploader.
|
||||
type UploaderAPI interface {
|
||||
@@ -23,4 +31,16 @@ type UploaderAPI interface {
|
||||
UploadWithContext(aws.Context, *s3manager.UploadInput, ...func(*s3manager.Uploader)) (*s3manager.UploadOutput, error)
|
||||
}
|
||||
|
||||
var _ UploaderAPI = (*s3manager.Uploader)(nil)
|
||||
// UploadWithIterator is the interface for uploading objects to S3 using the S3
|
||||
// upload manager.
|
||||
type UploadWithIterator interface {
|
||||
UploadWithIterator(aws.Context, s3manager.BatchUploadIterator, ...func(*s3manager.Uploader)) error
|
||||
}
|
||||
|
||||
var _ BatchDelete = (*s3manager.BatchDelete)(nil)
|
||||
|
||||
// BatchDelete is the interface type for batch deleting objects from S3 using
|
||||
// the S3 manager. (separated for user to compose).
|
||||
type BatchDelete interface {
|
||||
Delete(aws.Context, s3manager.BatchDeleteIterator) error
|
||||
}
|
||||
|
||||
+41
-115
@@ -6,12 +6,12 @@ import (
|
||||
"io"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/service/s3"
|
||||
"github.com/aws/aws-sdk-go/service/s3/s3iface"
|
||||
@@ -96,100 +96,6 @@ func (m multiUploadError) UploadID() string {
|
||||
return m.uploadID
|
||||
}
|
||||
|
||||
// UploadInput contains all input for upload requests to Amazon S3.
|
||||
type UploadInput struct {
|
||||
// The canned ACL to apply to the object.
|
||||
ACL *string `location:"header" locationName:"x-amz-acl" type:"string"`
|
||||
|
||||
Bucket *string `location:"uri" locationName:"Bucket" type:"string" required:"true"`
|
||||
|
||||
// Specifies caching behavior along the request/reply chain.
|
||||
CacheControl *string `location:"header" locationName:"Cache-Control" type:"string"`
|
||||
|
||||
// Specifies presentational information for the object.
|
||||
ContentDisposition *string `location:"header" locationName:"Content-Disposition" type:"string"`
|
||||
|
||||
// Specifies what content encodings have been applied to the object and thus
|
||||
// what decoding mechanisms must be applied to obtain the media-type referenced
|
||||
// by the Content-Type header field.
|
||||
ContentEncoding *string `location:"header" locationName:"Content-Encoding" type:"string"`
|
||||
|
||||
// The language the content is in.
|
||||
ContentLanguage *string `location:"header" locationName:"Content-Language" type:"string"`
|
||||
|
||||
// The base64-encoded 128-bit MD5 digest of the part data.
|
||||
ContentMD5 *string `location:"header" locationName:"Content-MD5" type:"string"`
|
||||
|
||||
// A standard MIME type describing the format of the object data.
|
||||
ContentType *string `location:"header" locationName:"Content-Type" type:"string"`
|
||||
|
||||
// The date and time at which the object is no longer cacheable.
|
||||
Expires *time.Time `location:"header" locationName:"Expires" type:"timestamp" timestampFormat:"rfc822"`
|
||||
|
||||
// Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.
|
||||
GrantFullControl *string `location:"header" locationName:"x-amz-grant-full-control" type:"string"`
|
||||
|
||||
// Allows grantee to read the object data and its metadata.
|
||||
GrantRead *string `location:"header" locationName:"x-amz-grant-read" type:"string"`
|
||||
|
||||
// Allows grantee to read the object ACL.
|
||||
GrantReadACP *string `location:"header" locationName:"x-amz-grant-read-acp" type:"string"`
|
||||
|
||||
// Allows grantee to write the ACL for the applicable object.
|
||||
GrantWriteACP *string `location:"header" locationName:"x-amz-grant-write-acp" type:"string"`
|
||||
|
||||
Key *string `location:"uri" locationName:"Key" type:"string" required:"true"`
|
||||
|
||||
// A map of metadata to store with the object in S3.
|
||||
Metadata map[string]*string `location:"headers" locationName:"x-amz-meta-" type:"map"`
|
||||
|
||||
// Confirms that the requester knows that she or he will be charged for the
|
||||
// request. Bucket owners need not specify this parameter in their requests.
|
||||
// Documentation on downloading objects from requester pays buckets can be found
|
||||
// at http://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
|
||||
RequestPayer *string `location:"header" locationName:"x-amz-request-payer" type:"string"`
|
||||
|
||||
// Specifies the algorithm to use to when encrypting the object (e.g., AES256,
|
||||
// aws:kms).
|
||||
SSECustomerAlgorithm *string `location:"header" locationName:"x-amz-server-side-encryption-customer-algorithm" type:"string"`
|
||||
|
||||
// Specifies the customer-provided encryption key for Amazon S3 to use in encrypting
|
||||
// data. This value is used to store the object and then it is discarded; Amazon
|
||||
// does not store the encryption key. The key must be appropriate for use with
|
||||
// the algorithm specified in the x-amz-server-side-encryption-customer-algorithm
|
||||
// header.
|
||||
SSECustomerKey *string `location:"header" locationName:"x-amz-server-side-encryption-customer-key" type:"string"`
|
||||
|
||||
// Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321.
|
||||
// Amazon S3 uses this header for a message integrity check to ensure the encryption
|
||||
// key was transmitted without error.
|
||||
SSECustomerKeyMD5 *string `location:"header" locationName:"x-amz-server-side-encryption-customer-key-MD5" type:"string"`
|
||||
|
||||
// Specifies the AWS KMS key ID to use for object encryption. All GET and PUT
|
||||
// requests for an object protected by AWS KMS will fail if not made via SSL
|
||||
// or using SigV4. Documentation on configuring any of the officially supported
|
||||
// AWS SDKs and CLI can be found at http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify-signature-version
|
||||
SSEKMSKeyId *string `location:"header" locationName:"x-amz-server-side-encryption-aws-kms-key-id" type:"string"`
|
||||
|
||||
// The Server-side encryption algorithm used when storing this object in S3
|
||||
// (e.g., AES256, aws:kms).
|
||||
ServerSideEncryption *string `location:"header" locationName:"x-amz-server-side-encryption" type:"string"`
|
||||
|
||||
// The type of storage to use for the object. Defaults to 'STANDARD'.
|
||||
StorageClass *string `location:"header" locationName:"x-amz-storage-class" type:"string"`
|
||||
|
||||
// The tag-set for the object. The tag-set must be encoded as URL Query parameters
|
||||
Tagging *string `location:"header" locationName:"x-amz-tagging" type:"string"`
|
||||
|
||||
// If the bucket is configured as a website, redirects requests for this object
|
||||
// to another object in the same bucket or to an external URL. Amazon S3 stores
|
||||
// the value of this header in the object metadata.
|
||||
WebsiteRedirectLocation *string `location:"header" locationName:"x-amz-website-redirect-location" type:"string"`
|
||||
|
||||
// The readable body payload to send to S3.
|
||||
Body io.Reader
|
||||
}
|
||||
|
||||
// UploadOutput represents a response from the Upload() call.
|
||||
type UploadOutput struct {
|
||||
// The URL where the object was uploaded to.
|
||||
@@ -239,8 +145,15 @@ type Uploader struct {
|
||||
// MaxUploadParts is the max number of parts which will be uploaded to S3.
|
||||
// Will be used to calculate the partsize of the object to be uploaded.
|
||||
// E.g: 5GB file, with MaxUploadParts set to 100, will upload the file
|
||||
// as 100, 50MB parts.
|
||||
// With a limited of s3.MaxUploadParts (10,000 parts).
|
||||
// as 100, 50MB parts. With a limited of s3.MaxUploadParts (10,000 parts).
|
||||
//
|
||||
// MaxUploadParts must not be used to limit the total number of bytes uploaded.
|
||||
// Use a type like to io.LimitReader (https://golang.org/pkg/io/#LimitedReader)
|
||||
// instead. An io.LimitReader is helpful when uploading an unbounded reader
|
||||
// to S3, and you know its maximum size. Otherwise the reader's io.EOF returned
|
||||
// error must be used to signal end of stream.
|
||||
//
|
||||
// Defaults to package const's MaxUploadParts value.
|
||||
MaxUploadParts int
|
||||
|
||||
// The client to use when uploading to S3.
|
||||
@@ -357,7 +270,7 @@ func (u Uploader) Upload(input *UploadInput, options ...func(*Uploader)) (*Uploa
|
||||
//
|
||||
// UploadWithContext is the same as Upload with the additional support for
|
||||
// Context input parameters. The Context must not be nil. A nil Context will
|
||||
// cause a panic. Use the context to add deadlining, timeouts, ect. The
|
||||
// cause a panic. Use the context to add deadlining, timeouts, etc. The
|
||||
// UploadWithContext may create sub-contexts for individual underlying requests.
|
||||
//
|
||||
// Additional functional options can be provided to configure the individual
|
||||
@@ -395,7 +308,7 @@ func (u Uploader) UploadWithContext(ctx aws.Context, input *UploadInput, opts ..
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// iter := &s3managee.UploadObjectsIterator{Objects: objects}
|
||||
// iter := &s3manager.UploadObjectsIterator{Objects: objects}
|
||||
// if err := svc.UploadWithIterator(aws.BackgroundContext(), iter); err != nil {
|
||||
// return err
|
||||
// }
|
||||
@@ -477,6 +390,9 @@ func (u *uploader) init() {
|
||||
if u.cfg.PartSize == 0 {
|
||||
u.cfg.PartSize = DefaultUploadPartSize
|
||||
}
|
||||
if u.cfg.MaxUploadParts == 0 {
|
||||
u.cfg.MaxUploadParts = MaxUploadParts
|
||||
}
|
||||
|
||||
u.bufferPool = sync.Pool{
|
||||
New: func() interface{} { return make([]byte, u.cfg.PartSize) },
|
||||
@@ -631,21 +547,6 @@ func (u *multiuploader) upload(firstBuf io.ReadSeeker, firstPart []byte) (*Uploa
|
||||
|
||||
// Read and queue the rest of the parts
|
||||
for u.geterr() == nil && err == nil {
|
||||
num++
|
||||
// This upload exceeded maximum number of supported parts, error now.
|
||||
if num > int64(u.cfg.MaxUploadParts) || num > int64(MaxUploadParts) {
|
||||
var msg string
|
||||
if num > int64(u.cfg.MaxUploadParts) {
|
||||
msg = fmt.Sprintf("exceeded total allowed configured MaxUploadParts (%d). Adjust PartSize to fit in this limit",
|
||||
u.cfg.MaxUploadParts)
|
||||
} else {
|
||||
msg = fmt.Sprintf("exceeded total allowed S3 limit MaxUploadParts (%d). Adjust PartSize to fit in this limit",
|
||||
MaxUploadParts)
|
||||
}
|
||||
u.seterr(awserr.New("TotalPartsExceeded", msg, nil))
|
||||
break
|
||||
}
|
||||
|
||||
var reader io.ReadSeeker
|
||||
var nextChunkLen int
|
||||
var part []byte
|
||||
@@ -666,6 +567,21 @@ func (u *multiuploader) upload(firstBuf io.ReadSeeker, firstPart []byte) (*Uploa
|
||||
break
|
||||
}
|
||||
|
||||
num++
|
||||
// This upload exceeded maximum number of supported parts, error now.
|
||||
if num > int64(u.cfg.MaxUploadParts) || num > int64(MaxUploadParts) {
|
||||
var msg string
|
||||
if num > int64(u.cfg.MaxUploadParts) {
|
||||
msg = fmt.Sprintf("exceeded total allowed configured MaxUploadParts (%d). Adjust PartSize to fit in this limit",
|
||||
u.cfg.MaxUploadParts)
|
||||
} else {
|
||||
msg = fmt.Sprintf("exceeded total allowed S3 limit MaxUploadParts (%d). Adjust PartSize to fit in this limit",
|
||||
MaxUploadParts)
|
||||
}
|
||||
u.seterr(awserr.New("TotalPartsExceeded", msg, nil))
|
||||
break
|
||||
}
|
||||
|
||||
ch <- chunk{buf: reader, part: part, num: num}
|
||||
}
|
||||
|
||||
@@ -683,8 +599,18 @@ func (u *multiuploader) upload(firstBuf io.ReadSeeker, firstPart []byte) (*Uploa
|
||||
uploadID: u.uploadID,
|
||||
}
|
||||
}
|
||||
|
||||
// Create a presigned URL of the S3 Get Object in order to have parity with
|
||||
// single part upload.
|
||||
getReq, _ := u.cfg.S3.GetObjectRequest(&s3.GetObjectInput{
|
||||
Bucket: u.in.Bucket,
|
||||
Key: u.in.Key,
|
||||
})
|
||||
getReq.Config.Credentials = credentials.AnonymousCredentials
|
||||
uploadLocation, _, _ := getReq.PresignRequest(1)
|
||||
|
||||
return &UploadOutput{
|
||||
Location: aws.StringValue(complete.Location),
|
||||
Location: uploadLocation,
|
||||
VersionID: complete.VersionId,
|
||||
UploadID: u.uploadID,
|
||||
}, nil
|
||||
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
|
||||
|
||||
package s3manager
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UploadInput provides the input parameters for uploading a stream or buffer
|
||||
// to an object in an Amazon S3 bucket. This type is similar to the s3
|
||||
// package's PutObjectInput with the exception that the Body member is an
|
||||
// io.Reader instead of an io.ReadSeeker.
|
||||
type UploadInput struct {
|
||||
_ struct{} `type:"structure" payload:"Body"`
|
||||
|
||||
// The canned ACL to apply to the object.
|
||||
ACL *string `location:"header" locationName:"x-amz-acl" type:"string" enum:"ObjectCannedACL"`
|
||||
|
||||
// The readable body payload to send to S3.
|
||||
Body io.Reader
|
||||
|
||||
// Name of the bucket to which the PUT operation was initiated.
|
||||
//
|
||||
// Bucket is a required field
|
||||
Bucket *string `location:"uri" locationName:"Bucket" type:"string" required:"true"`
|
||||
|
||||
// Specifies caching behavior along the request/reply chain.
|
||||
CacheControl *string `location:"header" locationName:"Cache-Control" type:"string"`
|
||||
|
||||
// Specifies presentational information for the object.
|
||||
ContentDisposition *string `location:"header" locationName:"Content-Disposition" type:"string"`
|
||||
|
||||
// Specifies what content encodings have been applied to the object and thus
|
||||
// what decoding mechanisms must be applied to obtain the media-type referenced
|
||||
// by the Content-Type header field.
|
||||
ContentEncoding *string `location:"header" locationName:"Content-Encoding" type:"string"`
|
||||
|
||||
// The language the content is in.
|
||||
ContentLanguage *string `location:"header" locationName:"Content-Language" type:"string"`
|
||||
|
||||
// The base64-encoded 128-bit MD5 digest of the part data. This parameter is
|
||||
// auto-populated when using the command from the CLI. This parameted is required
|
||||
// if object lock parameters are specified.
|
||||
ContentMD5 *string `location:"header" locationName:"Content-MD5" type:"string"`
|
||||
|
||||
// A standard MIME type describing the format of the object data.
|
||||
ContentType *string `location:"header" locationName:"Content-Type" type:"string"`
|
||||
|
||||
// The date and time at which the object is no longer cacheable.
|
||||
Expires *time.Time `location:"header" locationName:"Expires" type:"timestamp"`
|
||||
|
||||
// Gives the grantee READ, READ_ACP, and WRITE_ACP permissions on the object.
|
||||
GrantFullControl *string `location:"header" locationName:"x-amz-grant-full-control" type:"string"`
|
||||
|
||||
// Allows grantee to read the object data and its metadata.
|
||||
GrantRead *string `location:"header" locationName:"x-amz-grant-read" type:"string"`
|
||||
|
||||
// Allows grantee to read the object ACL.
|
||||
GrantReadACP *string `location:"header" locationName:"x-amz-grant-read-acp" type:"string"`
|
||||
|
||||
// Allows grantee to write the ACL for the applicable object.
|
||||
GrantWriteACP *string `location:"header" locationName:"x-amz-grant-write-acp" type:"string"`
|
||||
|
||||
// Object key for which the PUT operation was initiated.
|
||||
//
|
||||
// Key is a required field
|
||||
Key *string `location:"uri" locationName:"Key" min:"1" type:"string" required:"true"`
|
||||
|
||||
// A map of metadata to store with the object in S3.
|
||||
Metadata map[string]*string `location:"headers" locationName:"x-amz-meta-" type:"map"`
|
||||
|
||||
// The Legal Hold status that you want to apply to the specified object.
|
||||
ObjectLockLegalHoldStatus *string `location:"header" locationName:"x-amz-object-lock-legal-hold" type:"string" enum:"ObjectLockLegalHoldStatus"`
|
||||
|
||||
// The object lock mode that you want to apply to this object.
|
||||
ObjectLockMode *string `location:"header" locationName:"x-amz-object-lock-mode" type:"string" enum:"ObjectLockMode"`
|
||||
|
||||
// The date and time when you want this object's object lock to expire.
|
||||
ObjectLockRetainUntilDate *time.Time `location:"header" locationName:"x-amz-object-lock-retain-until-date" type:"timestamp" timestampFormat:"iso8601"`
|
||||
|
||||
// Confirms that the requester knows that she or he will be charged for the
|
||||
// request. Bucket owners need not specify this parameter in their requests.
|
||||
// Documentation on downloading objects from requester pays buckets can be found
|
||||
// at http://docs.aws.amazon.com/AmazonS3/latest/dev/ObjectsinRequesterPaysBuckets.html
|
||||
RequestPayer *string `location:"header" locationName:"x-amz-request-payer" type:"string" enum:"RequestPayer"`
|
||||
|
||||
// Specifies the algorithm to use to when encrypting the object (e.g., AES256).
|
||||
SSECustomerAlgorithm *string `location:"header" locationName:"x-amz-server-side-encryption-customer-algorithm" type:"string"`
|
||||
|
||||
// Specifies the customer-provided encryption key for Amazon S3 to use in encrypting
|
||||
// data. This value is used to store the object and then it is discarded; Amazon
|
||||
// does not store the encryption key. The key must be appropriate for use with
|
||||
// the algorithm specified in the x-amz-server-side-encryption-customer-algorithm
|
||||
// header.
|
||||
SSECustomerKey *string `marshal-as:"blob" location:"header" locationName:"x-amz-server-side-encryption-customer-key" type:"string" sensitive:"true"`
|
||||
|
||||
// Specifies the 128-bit MD5 digest of the encryption key according to RFC 1321.
|
||||
// Amazon S3 uses this header for a message integrity check to ensure the encryption
|
||||
// key was transmitted without error.
|
||||
SSECustomerKeyMD5 *string `location:"header" locationName:"x-amz-server-side-encryption-customer-key-MD5" type:"string"`
|
||||
|
||||
// Specifies the AWS KMS Encryption Context to use for object encryption. The
|
||||
// value of this header is a base64-encoded UTF-8 string holding JSON with the
|
||||
// encryption context key-value pairs.
|
||||
SSEKMSEncryptionContext *string `location:"header" locationName:"x-amz-server-side-encryption-context" type:"string" sensitive:"true"`
|
||||
|
||||
// Specifies the AWS KMS key ID to use for object encryption. All GET and PUT
|
||||
// requests for an object protected by AWS KMS will fail if not made via SSL
|
||||
// or using SigV4. Documentation on configuring any of the officially supported
|
||||
// AWS SDKs and CLI can be found at http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingAWSSDK.html#specify-signature-version
|
||||
SSEKMSKeyId *string `location:"header" locationName:"x-amz-server-side-encryption-aws-kms-key-id" type:"string" sensitive:"true"`
|
||||
|
||||
// The Server-side encryption algorithm used when storing this object in S3
|
||||
// (e.g., AES256, aws:kms).
|
||||
ServerSideEncryption *string `location:"header" locationName:"x-amz-server-side-encryption" type:"string" enum:"ServerSideEncryption"`
|
||||
|
||||
// The type of storage to use for the object. Defaults to 'STANDARD'.
|
||||
StorageClass *string `location:"header" locationName:"x-amz-storage-class" type:"string" enum:"StorageClass"`
|
||||
|
||||
// The tag-set for the object. The tag-set must be encoded as URL Query parameters.
|
||||
// (For example, "Key1=Value1")
|
||||
Tagging *string `location:"header" locationName:"x-amz-tagging" type:"string"`
|
||||
|
||||
// If the bucket is configured as a website, redirects requests for this object
|
||||
// to another object in the same bucket or to an external URL. Amazon S3 stores
|
||||
// the value of this header in the object metadata.
|
||||
WebsiteRedirectLocation *string `location:"header" locationName:"x-amz-website-redirect-location" type:"string"`
|
||||
}
|
||||
+334
-32
@@ -1,3 +1,5 @@
|
||||
// +build go1.8
|
||||
|
||||
package s3manager_test
|
||||
|
||||
import (
|
||||
@@ -7,12 +9,15 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
@@ -104,7 +109,7 @@ func TestUploadOrderMulti(t *testing.T) {
|
||||
|
||||
resp, err := u.Upload(&s3manager.UploadInput{
|
||||
Bucket: aws.String("Bucket"),
|
||||
Key: aws.String("Key"),
|
||||
Key: aws.String("Key - value"),
|
||||
Body: bytes.NewReader(buf12MB),
|
||||
ServerSideEncryption: aws.String("aws:kms"),
|
||||
SSEKMSKeyId: aws.String("KmsId"),
|
||||
@@ -120,8 +125,8 @@ func TestUploadOrderMulti(t *testing.T) {
|
||||
t.Errorf("Expected %v, but received %v", expected, *ops)
|
||||
}
|
||||
|
||||
if "https://location" != resp.Location {
|
||||
t.Errorf("Expected %q, but received %q", "https://location", resp.Location)
|
||||
if e, a := `https://s3.mock-region.amazonaws.com/Bucket/Key%20-%20value`, resp.Location; e != a {
|
||||
t.Errorf("Expected %q, but received %q", e, a)
|
||||
}
|
||||
|
||||
if "UPLOAD-ID" != resp.UploadID {
|
||||
@@ -129,7 +134,7 @@ func TestUploadOrderMulti(t *testing.T) {
|
||||
}
|
||||
|
||||
if "VERSION-ID" != *resp.VersionID {
|
||||
t.Errorf("Expected %q, but received %q", "VERSION-ID", resp.VersionID)
|
||||
t.Errorf("Expected %q, but received %q", "VERSION-ID", *resp.VersionID)
|
||||
}
|
||||
|
||||
// Validate input values
|
||||
@@ -268,12 +273,12 @@ func TestUploadFailIfPartSizeTooSmall(t *testing.T) {
|
||||
}
|
||||
|
||||
aerr := err.(awserr.Error)
|
||||
if "ConfigError" != aerr.Code() {
|
||||
t.Errorf("Expected %q, but received %q", "ConfigError", aerr.Code())
|
||||
if e, a := "ConfigError", aerr.Code(); e != a {
|
||||
t.Errorf("Expected %q, but received %q", e, a)
|
||||
}
|
||||
|
||||
if strings.Contains("part size must be at least", aerr.Message()) {
|
||||
t.Errorf("Expected string to contain %q, but received %q", "part size must be at least", aerr.Message())
|
||||
if e, a := "part size must be at least", aerr.Message(); !strings.Contains(a, e) {
|
||||
t.Errorf("expect %v to be in %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +287,7 @@ func TestUploadOrderSingle(t *testing.T) {
|
||||
mgr := s3manager.NewUploaderWithClient(s)
|
||||
resp, err := mgr.Upload(&s3manager.UploadInput{
|
||||
Bucket: aws.String("Bucket"),
|
||||
Key: aws.String("Key"),
|
||||
Key: aws.String("Key - value"),
|
||||
Body: bytes.NewReader(buf2MB),
|
||||
ServerSideEncryption: aws.String("aws:kms"),
|
||||
SSEKMSKeyId: aws.String("KmsId"),
|
||||
@@ -297,12 +302,12 @@ func TestUploadOrderSingle(t *testing.T) {
|
||||
t.Errorf("Expected %v, but received %v", vals, *ops)
|
||||
}
|
||||
|
||||
if len(resp.Location) == 0 {
|
||||
t.Error("Expected Location to not be empty")
|
||||
if e, a := `https://s3.mock-region.amazonaws.com/Bucket/Key%20-%20value`, resp.Location; e != a {
|
||||
t.Errorf("Expected %q, but received %q", e, a)
|
||||
}
|
||||
|
||||
if e := "VERSION-ID"; e != *resp.VersionID {
|
||||
t.Errorf("Expected %q, but received %q", e, resp.VersionID)
|
||||
t.Errorf("Expected %q, but received %q", e, *resp.VersionID)
|
||||
}
|
||||
|
||||
if len(resp.UploadID) > 0 {
|
||||
@@ -785,53 +790,45 @@ func TestUploadInputS3PutObjectInputPairity(t *testing.T) {
|
||||
}
|
||||
|
||||
type testIncompleteReader struct {
|
||||
Buf []byte
|
||||
Count int
|
||||
Size int64
|
||||
read int64
|
||||
}
|
||||
|
||||
func (r *testIncompleteReader) Read(p []byte) (n int, err error) {
|
||||
if r.Count < 0 {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
r.read += int64(len(p))
|
||||
if r.read >= r.Size {
|
||||
return int(r.read - r.Size), io.ErrUnexpectedEOF
|
||||
}
|
||||
|
||||
r.Count--
|
||||
return copy(p, r.Buf), nil
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func TestUploadUnexpectedEOF(t *testing.T) {
|
||||
s, ops, args := loggingSvc(emptyList)
|
||||
s, ops, _ := loggingSvc(emptyList)
|
||||
mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) {
|
||||
u.Concurrency = 1
|
||||
u.PartSize = s3manager.MinUploadPartSize
|
||||
})
|
||||
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||
Bucket: aws.String("Bucket"),
|
||||
Key: aws.String("Key"),
|
||||
Body: &testIncompleteReader{
|
||||
Buf: make([]byte, 1024*1024*5),
|
||||
Count: 1,
|
||||
Size: int64(s3manager.MinUploadPartSize + 1),
|
||||
},
|
||||
})
|
||||
|
||||
if err == nil {
|
||||
t.Error("Expected error, but received none")
|
||||
}
|
||||
|
||||
// Ensure upload started.
|
||||
if e, a := "CreateMultipartUpload", (*ops)[0]; e != a {
|
||||
t.Errorf("Expected %q, but received %q", e, a)
|
||||
}
|
||||
|
||||
if e, a := "UploadPart", (*ops)[1]; e != a {
|
||||
t.Errorf("Expected %q, but received %q", e, a)
|
||||
}
|
||||
|
||||
// Part may or may not be sent because of timing of sending parts and
|
||||
// reading next part in upload manager. Just check for the last abort.
|
||||
if e, a := "AbortMultipartUpload", (*ops)[len(*ops)-1]; e != a {
|
||||
t.Errorf("Expected %q, but received %q", e, a)
|
||||
}
|
||||
|
||||
// Part lengths
|
||||
if e, a := 1024*1024*5, buflen(val((*args)[1], "Body")); e != a {
|
||||
t.Errorf("Expected %d, but received %d", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func compareStructType(a, b reflect.Type) map[string]int {
|
||||
@@ -1001,3 +998,308 @@ func TestUploadWithContextCanceled(t *testing.T) {
|
||||
t.Errorf("expected error message to contain %q, but did not %q", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
// S3 Uploader incorrectly fails an upload if the content being uploaded
|
||||
// has a size of MinPartSize * MaxUploadParts.
|
||||
// Github: aws/aws-sdk-go#2557
|
||||
func TestUploadMaxPartsEOF(t *testing.T) {
|
||||
s, ops, _ := loggingSvc(emptyList)
|
||||
mgr := s3manager.NewUploaderWithClient(s, func(u *s3manager.Uploader) {
|
||||
u.Concurrency = 1
|
||||
u.PartSize = s3manager.DefaultUploadPartSize
|
||||
u.MaxUploadParts = 2
|
||||
})
|
||||
f := bytes.NewReader(make([]byte, int(mgr.PartSize)*mgr.MaxUploadParts))
|
||||
|
||||
r1 := io.NewSectionReader(f, 0, s3manager.DefaultUploadPartSize)
|
||||
r2 := io.NewSectionReader(f, s3manager.DefaultUploadPartSize, 2*s3manager.DefaultUploadPartSize)
|
||||
body := io.MultiReader(r1, r2)
|
||||
|
||||
_, err := mgr.Upload(&s3manager.UploadInput{
|
||||
Bucket: aws.String("Bucket"),
|
||||
Key: aws.String("Key"),
|
||||
Body: body,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, got %v", err)
|
||||
}
|
||||
|
||||
expectOps := []string{
|
||||
"CreateMultipartUpload",
|
||||
"UploadPart",
|
||||
"UploadPart",
|
||||
"CompleteMultipartUpload",
|
||||
}
|
||||
if e, a := expectOps, *ops; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expect %v ops, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func createTempFile(t *testing.T, size int64) (*os.File, func(*testing.T), error) {
|
||||
file, err := ioutil.TempFile(os.TempDir(), aws.SDKName+t.Name())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
filename := file.Name()
|
||||
if err := file.Truncate(size); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return file,
|
||||
func(t *testing.T) {
|
||||
if err := file.Close(); err != nil {
|
||||
t.Errorf("failed to close temp file, %s, %v", filename, err)
|
||||
}
|
||||
if err := os.Remove(filename); err != nil {
|
||||
t.Errorf("failed to remove temp file, %s, %v", filename, err)
|
||||
}
|
||||
},
|
||||
nil
|
||||
}
|
||||
|
||||
func buildFailHandlers(tb testing.TB, parts, retry int) []http.Handler {
|
||||
handlers := make([]http.Handler, parts)
|
||||
for i := 0; i < len(handlers); i++ {
|
||||
handlers[i] = &failPartHandler{
|
||||
tb: tb,
|
||||
failsRemaining: retry,
|
||||
successHandler: successPartHandler{tb: tb},
|
||||
}
|
||||
}
|
||||
|
||||
return handlers
|
||||
}
|
||||
|
||||
func TestUploadRetry(t *testing.T) {
|
||||
const numParts, retries = 3, 10
|
||||
|
||||
testFile, testFileCleanup, err := createTempFile(t, s3manager.DefaultUploadPartSize*numParts)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create test file, %v", err)
|
||||
}
|
||||
defer testFileCleanup(t)
|
||||
|
||||
cases := map[string]struct {
|
||||
Body io.Reader
|
||||
PartHandlers func(testing.TB) []http.Handler
|
||||
}{
|
||||
"bytes.Buffer": {
|
||||
Body: bytes.NewBuffer(make([]byte, s3manager.DefaultUploadPartSize*numParts)),
|
||||
PartHandlers: func(tb testing.TB) []http.Handler {
|
||||
return buildFailHandlers(tb, numParts, retries)
|
||||
},
|
||||
},
|
||||
"bytes.Reader": {
|
||||
Body: bytes.NewReader(make([]byte, s3manager.DefaultUploadPartSize*numParts)),
|
||||
PartHandlers: func(tb testing.TB) []http.Handler {
|
||||
return buildFailHandlers(tb, numParts, retries)
|
||||
},
|
||||
},
|
||||
"os.File": {
|
||||
Body: testFile,
|
||||
PartHandlers: func(tb testing.TB) []http.Handler {
|
||||
return buildFailHandlers(tb, numParts, retries)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, c := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
mux := newMockS3UploadServer(t, c.PartHandlers(t))
|
||||
server := httptest.NewServer(mux)
|
||||
defer server.Close()
|
||||
|
||||
sess := unit.Session.Copy(&aws.Config{
|
||||
Endpoint: aws.String(server.URL),
|
||||
S3ForcePathStyle: aws.Bool(true),
|
||||
DisableSSL: aws.Bool(true),
|
||||
Logger: t,
|
||||
MaxRetries: aws.Int(retries + 1),
|
||||
SleepDelay: func(time.Duration) {},
|
||||
|
||||
LogLevel: aws.LogLevel(
|
||||
aws.LogDebugWithRequestErrors | aws.LogDebugWithRequestRetries,
|
||||
),
|
||||
//Credentials: credentials.AnonymousCredentials,
|
||||
})
|
||||
|
||||
uploader := s3manager.NewUploader(sess, func(u *s3manager.Uploader) {
|
||||
// u.Concurrency = 1
|
||||
})
|
||||
_, err := uploader.Upload(&s3manager.UploadInput{
|
||||
Bucket: aws.String("bucket"),
|
||||
Key: aws.String("key"),
|
||||
Body: c.Body,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("expect no error, got %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockS3UploadServer struct {
|
||||
*http.ServeMux
|
||||
|
||||
tb testing.TB
|
||||
partHandler []http.Handler
|
||||
}
|
||||
|
||||
func newMockS3UploadServer(tb testing.TB, partHandler []http.Handler) *mockS3UploadServer {
|
||||
s := &mockS3UploadServer{
|
||||
ServeMux: http.NewServeMux(),
|
||||
partHandler: partHandler,
|
||||
tb: tb,
|
||||
}
|
||||
|
||||
s.HandleFunc("/", s.handleRequest)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s mockS3UploadServer) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
_, hasUploads := r.URL.Query()["uploads"]
|
||||
|
||||
switch {
|
||||
case r.Method == "POST" && hasUploads:
|
||||
// CreateMultipartUpload
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(createUploadResp)))
|
||||
w.Write([]byte(createUploadResp))
|
||||
|
||||
case r.Method == "PUT":
|
||||
// UploadPart
|
||||
partNumStr := r.URL.Query().Get("partNumber")
|
||||
id, err := strconv.Atoi(partNumStr)
|
||||
if err != nil {
|
||||
failRequest(w, 400, "BadRequest",
|
||||
fmt.Sprintf("unable to parse partNumber, %q, %v",
|
||||
partNumStr, err))
|
||||
return
|
||||
}
|
||||
id--
|
||||
if id < 0 || id >= len(s.partHandler) {
|
||||
failRequest(w, 400, "BadRequest",
|
||||
fmt.Sprintf("invalid partNumber %v", id))
|
||||
return
|
||||
}
|
||||
s.partHandler[id].ServeHTTP(w, r)
|
||||
|
||||
case r.Method == "POST":
|
||||
// CompleteMultipartUpload
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(completeUploadResp)))
|
||||
w.Write([]byte(completeUploadResp))
|
||||
|
||||
case r.Method == "DELETE":
|
||||
// AbortMultipartUpload
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(abortUploadResp)))
|
||||
w.WriteHeader(200)
|
||||
w.Write([]byte(abortUploadResp))
|
||||
|
||||
default:
|
||||
failRequest(w, 400, "BadRequest",
|
||||
fmt.Sprintf("invalid request %v %v", r.Method, r.URL))
|
||||
}
|
||||
}
|
||||
|
||||
func failRequest(w http.ResponseWriter, status int, code, msg string) {
|
||||
msg = fmt.Sprintf(baseRequestErrorResp, code, msg)
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(msg)))
|
||||
w.WriteHeader(status)
|
||||
w.Write([]byte(msg))
|
||||
}
|
||||
|
||||
type successPartHandler struct {
|
||||
tb testing.TB
|
||||
}
|
||||
|
||||
func (h successPartHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
n, err := io.Copy(ioutil.Discard, r.Body)
|
||||
if err != nil {
|
||||
failRequest(w, 400, "BadRequest",
|
||||
fmt.Sprintf("failed to read body, %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
contLenStr := r.Header.Get("Content-Length")
|
||||
expectLen, err := strconv.ParseInt(contLenStr, 10, 64)
|
||||
if err != nil {
|
||||
h.tb.Logf("expect content-length, got %q, %v", contLenStr, err)
|
||||
failRequest(w, 400, "BadRequest",
|
||||
fmt.Sprintf("unable to get content-length %v", err))
|
||||
return
|
||||
}
|
||||
if e, a := expectLen, n; e != a {
|
||||
h.tb.Logf("expect %v read, got %v", e, a)
|
||||
failRequest(w, 400, "BadRequest",
|
||||
fmt.Sprintf(
|
||||
"content-length and body do not match, %v, %v", e, a))
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Length", strconv.Itoa(len(uploadPartResp)))
|
||||
w.Write([]byte(uploadPartResp))
|
||||
}
|
||||
|
||||
type failPartHandler struct {
|
||||
tb testing.TB
|
||||
|
||||
failsRemaining int
|
||||
successHandler http.Handler
|
||||
}
|
||||
|
||||
func (h *failPartHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
if h.failsRemaining == 0 && h.successHandler != nil {
|
||||
h.successHandler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
io.Copy(ioutil.Discard, r.Body)
|
||||
|
||||
failRequest(w, 500, "InternalException",
|
||||
fmt.Sprintf("mock error, partNumber %v", r.URL.Query().Get("partNumber")))
|
||||
|
||||
h.failsRemaining--
|
||||
}
|
||||
|
||||
const createUploadResp = `
|
||||
<CreateMultipartUploadResponse>
|
||||
<Bucket>bucket</Bucket>
|
||||
<Key>key</Key>
|
||||
<UploadId>abc123</UploadId>
|
||||
</CreateMultipartUploadResponse>
|
||||
`
|
||||
const uploadPartResp = `
|
||||
<UploadPartResponse>
|
||||
<ETag>key</ETag>
|
||||
</UploadPartResponse>
|
||||
`
|
||||
const baseRequestErrorResp = `
|
||||
<Error>
|
||||
<Code>%s</Code>
|
||||
<Message>%s</Message>
|
||||
<RequestId>request-id</RequestId>
|
||||
<HostId>host-id</HostId>
|
||||
</Error>
|
||||
`
|
||||
const completeUploadResp = `
|
||||
<CompleteMultipartUploadResponse>
|
||||
<Bucket>bucket</Bucket>
|
||||
<Key>key</Key>
|
||||
<ETag>key</ETag>
|
||||
<Location>https://bucket.us-west-2.amazonaws.com/key</Location>
|
||||
<UploadId>abc123</UploadId>
|
||||
</CompleteMultipartUploadResponse>
|
||||
`
|
||||
|
||||
const abortUploadResp = `
|
||||
<AbortMultipartUploadResponse>
|
||||
</AbortMultipartUploadResponse>
|
||||
`
|
||||
|
||||
+9
-3
@@ -29,8 +29,9 @@ var initRequest func(*request.Request)
|
||||
|
||||
// Service information constants
|
||||
const (
|
||||
ServiceName = "s3" // Service endpoint prefix API calls made to.
|
||||
EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata.
|
||||
ServiceName = "s3" // Name of service.
|
||||
EndpointsID = ServiceName // ID to lookup a service endpoint with.
|
||||
ServiceID = "S3" // ServiceID is a unique identifer of a specific service.
|
||||
)
|
||||
|
||||
// New creates a new instance of the S3 client with a session.
|
||||
@@ -55,6 +56,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
|
||||
cfg,
|
||||
metadata.ClientInfo{
|
||||
ServiceName: ServiceName,
|
||||
ServiceID: ServiceID,
|
||||
SigningName: signingName,
|
||||
SigningRegion: signingRegion,
|
||||
Endpoint: endpoint,
|
||||
@@ -65,12 +67,16 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
|
||||
}
|
||||
|
||||
// Handlers
|
||||
svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler)
|
||||
svc.Handlers.Sign.PushBackNamed(v4.BuildNamedHandler(v4.SignRequestHandler.Name, func(s *v4.Signer) {
|
||||
s.DisableURIPathEscaping = true
|
||||
}))
|
||||
svc.Handlers.Build.PushBackNamed(restxml.BuildHandler)
|
||||
svc.Handlers.Unmarshal.PushBackNamed(restxml.UnmarshalHandler)
|
||||
svc.Handlers.UnmarshalMeta.PushBackNamed(restxml.UnmarshalMetaHandler)
|
||||
svc.Handlers.UnmarshalError.PushBackNamed(restxml.UnmarshalErrorHandler)
|
||||
|
||||
svc.Handlers.UnmarshalStream.PushBackNamed(restxml.UnmarshalHandler)
|
||||
|
||||
// Run custom client initialization if present
|
||||
if initClient != nil {
|
||||
initClient(svc.Client)
|
||||
|
||||
+46
-16
@@ -3,6 +3,7 @@ package s3
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
@@ -30,25 +31,54 @@ func validateSSERequiresSSL(r *request.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func computeSSEKeys(r *request.Request) {
|
||||
headers := []string{
|
||||
"x-amz-server-side-encryption-customer-key",
|
||||
"x-amz-copy-source-server-side-encryption-customer-key",
|
||||
const (
|
||||
sseKeyHeader = "x-amz-server-side-encryption-customer-key"
|
||||
sseKeyMD5Header = sseKeyHeader + "-md5"
|
||||
)
|
||||
|
||||
func computeSSEKeyMD5(r *request.Request) {
|
||||
var key string
|
||||
if g, ok := r.Params.(sseCustomerKeyGetter); ok {
|
||||
key = g.getSSECustomerKey()
|
||||
}
|
||||
|
||||
for _, h := range headers {
|
||||
md5h := h + "-md5"
|
||||
if key := r.HTTPRequest.Header.Get(h); key != "" {
|
||||
// Base64-encode the value
|
||||
b64v := base64.StdEncoding.EncodeToString([]byte(key))
|
||||
r.HTTPRequest.Header.Set(h, b64v)
|
||||
computeKeyMD5(sseKeyHeader, sseKeyMD5Header, key, r.HTTPRequest)
|
||||
}
|
||||
|
||||
// Add MD5 if it wasn't computed
|
||||
if r.HTTPRequest.Header.Get(md5h) == "" {
|
||||
sum := md5.Sum([]byte(key))
|
||||
b64sum := base64.StdEncoding.EncodeToString(sum[:])
|
||||
r.HTTPRequest.Header.Set(md5h, b64sum)
|
||||
}
|
||||
const (
|
||||
copySrcSSEKeyHeader = "x-amz-copy-source-server-side-encryption-customer-key"
|
||||
copySrcSSEKeyMD5Header = copySrcSSEKeyHeader + "-md5"
|
||||
)
|
||||
|
||||
func computeCopySourceSSEKeyMD5(r *request.Request) {
|
||||
var key string
|
||||
if g, ok := r.Params.(copySourceSSECustomerKeyGetter); ok {
|
||||
key = g.getCopySourceSSECustomerKey()
|
||||
}
|
||||
|
||||
computeKeyMD5(copySrcSSEKeyHeader, copySrcSSEKeyMD5Header, key, r.HTTPRequest)
|
||||
}
|
||||
|
||||
func computeKeyMD5(keyHeader, keyMD5Header, key string, r *http.Request) {
|
||||
if len(key) == 0 {
|
||||
// Backwards compatiablity where user just set the header value instead
|
||||
// of using the API parameter, or setting the header value for an
|
||||
// operation without the parameters modeled.
|
||||
key = r.Header.Get(keyHeader)
|
||||
if len(key) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// In backwards compatiable, the header's value is not base64 encoded,
|
||||
// and needs to be encoded and updated by the SDK's customizations.
|
||||
b64Key := base64.StdEncoding.EncodeToString([]byte(key))
|
||||
r.Header.Set(keyHeader, b64Key)
|
||||
}
|
||||
|
||||
// Only update Key's MD5 if not already set.
|
||||
if len(r.Header.Get(keyMD5Header)) == 0 {
|
||||
sum := md5.Sum([]byte(key))
|
||||
keyMD5 := base64.StdEncoding.EncodeToString(sum[:])
|
||||
r.Header.Set(keyMD5Header, keyMD5)
|
||||
}
|
||||
}
|
||||
|
||||
+30
-3
@@ -34,9 +34,9 @@ func TestSSECustomerKeyOverHTTPError(t *testing.T) {
|
||||
func TestCopySourceSSECustomerKeyOverHTTPError(t *testing.T) {
|
||||
s := s3.New(unit.Session, &aws.Config{DisableSSL: aws.Bool(true)})
|
||||
req, _ := s.CopyObjectRequest(&s3.CopyObjectInput{
|
||||
Bucket: aws.String("bucket"),
|
||||
CopySource: aws.String("bucket/source"),
|
||||
Key: aws.String("dest"),
|
||||
Bucket: aws.String("bucket"),
|
||||
CopySource: aws.String("bucket/source"),
|
||||
Key: aws.String("dest"),
|
||||
CopySourceSSECustomerKey: aws.String("key"),
|
||||
})
|
||||
err := req.Build()
|
||||
@@ -109,3 +109,30 @@ func TestComputeSSEKeysShortcircuit(t *testing.T) {
|
||||
t.Errorf("expected %s, but received %s", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSECustomerKeysWithSpaces(t *testing.T) {
|
||||
s := s3.New(unit.Session)
|
||||
req, _ := s.CopyObjectRequest(&s3.CopyObjectInput{
|
||||
Bucket: aws.String("bucket"),
|
||||
CopySource: aws.String("bucket/source"),
|
||||
Key: aws.String("dest"),
|
||||
SSECustomerKey: aws.String(" key "),
|
||||
CopySourceSSECustomerKey: aws.String(" copykey "),
|
||||
})
|
||||
err := req.Build()
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, but received %v", err)
|
||||
}
|
||||
if e, a := "ICAga2V5ICAg", req.HTTPRequest.Header.Get("x-amz-server-side-encryption-customer-key"); e != a {
|
||||
t.Errorf("expected %s, but received %s", e, a)
|
||||
}
|
||||
if e, a := "ICAgY29weWtleSAgIA==", req.HTTPRequest.Header.Get("x-amz-copy-source-server-side-encryption-customer-key"); e != a {
|
||||
t.Errorf("expected %s, but received %s", e, a)
|
||||
}
|
||||
if e, a := "13XiUSCa6ReZ3CHtCLiJLg==", req.HTTPRequest.Header.Get("x-amz-server-side-encryption-customer-key-md5"); e != a {
|
||||
t.Errorf("expected %s, but received %s", e, a)
|
||||
}
|
||||
if e, a := "MHVtfmuml539o1871Vsc6w==", req.HTTPRequest.Header.Get("x-amz-copy-source-server-side-encryption-customer-key-md5"); e != a {
|
||||
t.Errorf("expected %s, but received %s", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -13,7 +13,11 @@ import (
|
||||
func copyMultipartStatusOKUnmarhsalError(r *request.Request) {
|
||||
b, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||
if err != nil {
|
||||
r.Error = awserr.New("SerializationError", "unable to read response body", err)
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New(request.ErrCodeSerialization, "unable to read response body", err),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
return
|
||||
}
|
||||
body := bytes.NewReader(b)
|
||||
@@ -27,7 +31,7 @@ func copyMultipartStatusOKUnmarhsalError(r *request.Request) {
|
||||
|
||||
unmarshalError(r)
|
||||
if err, ok := r.Error.(awserr.Error); ok && err != nil {
|
||||
if err.Code() == "SerializationError" {
|
||||
if err.Code() == request.ErrCodeSerialization {
|
||||
r.Error = nil
|
||||
return
|
||||
}
|
||||
|
||||
+556
@@ -0,0 +1,556 @@
|
||||
A,B,C,D,E,F,G,H,I,J
|
||||
0,0,0.5,217.371,217.658,218.002,269.445,487.447,2.106,489.554
|
||||
0,0,490.077,0.657,0.84,1.588,121.092,122.681,2.185,124.867
|
||||
0,9,490.077,1.602,1.676,1.977,184.155,186.132,1.198,187.331
|
||||
0,6,490.384,1.787,1.962,2.451,270.592,273.044,4.158,277.202
|
||||
0,5,491.125,0.693,0.877,1.9,295.589,297.49,19.456,316.946
|
||||
0,8,491.348,1.833,1.924,66.432,250.035,316.467,2.379,318.847
|
||||
0,0,614.955,0.455,0.507,8.554,229.261,237.815,15.761,253.577
|
||||
0,9,677.418,0.493,0.546,14.288,176.628,190.917,6.522,197.44
|
||||
0,3,491.864,1.034,1.109,250.552,132.254,382.806,2.485,385.291
|
||||
0,2,491.351,1.763,1.807,276.239,122.283,398.523,26.436,424.96
|
||||
0,4,490.154,1.867,1.935,341.544,86.243,427.787,15.659,443.447
|
||||
0,8,810.203,0.381,0.453,21.4,101.836,123.236,9.01,132.246
|
||||
0,6,767.595,0.54,0.592,68.928,105.705,174.633,0.93,175.564
|
||||
0,0,868.54,0.384,0.483,8.721,82.83,91.552,37.154,128.706
|
||||
0,9,874.866,0.472,0.574,41.728,80.617,122.345,11.14,133.486
|
||||
0,1,490.757,2.284,2.336,426.145,91.084,517.229,0.507,517.736
|
||||
0,7,490.755,1.652,1.816,426.521,90.913,517.435,2.244,519.679
|
||||
0,6,943.165,0.324,0.391,2.676,116.421,119.098,0.452,119.551
|
||||
0,2,916.32,0.609,0.884,81.097,83.55,164.647,1.342,165.989
|
||||
0,8,942.462,0.374,0.464,55.682,84.915,140.597,46.844,187.442
|
||||
0,4,933.61,0.377,0.451,64.784,131.147,195.932,9.997,205.929
|
||||
0,5,808.083,0.565,0.641,190.27,140.808,331.078,0.465,331.543
|
||||
0,3,877.165,0.47,0.546,121.153,141.078,262.231,2.558,264.789
|
||||
0,7,1010.442,0.372,0.441,2.201,129.116,131.318,7.148,138.467
|
||||
0,6,1062.725,0.483,0.581,6.402,80.848,87.251,45.416,132.668
|
||||
0,1,1008.502,1.579,1.706,65.348,121.324,186.672,1.251,187.924
|
||||
0,0,997.256,0.402,0.832,76.472,121.367,197.839,18.513,216.353
|
||||
0,9,1008.364,1.428,1.582,70.936,133.941,204.878,2.255,207.133
|
||||
0,8,1129.916,0.458,0.551,19.07,113.216,132.287,0.38,132.667
|
||||
0,4,1139.547,0.388,0.457,9.775,113.036,122.812,1.531,124.343
|
||||
0,2,1082.318,0.352,0.43,67.775,112.768,180.543,22.578,203.122
|
||||
0,5,1139.629,0.416,0.466,10.429,135.269,145.699,2.425,148.124
|
||||
0,3,1141.962,0.394,0.472,53.375,92.295,145.67,0.212,145.883
|
||||
0,0,1213.618,0.408,0.481,2.655,112.525,115.18,0.547,115.728
|
||||
0,9,1215.504,0.427,0.672,2.233,111.382,113.615,1.145,114.761
|
||||
0,6,1195.403,0.437,0.511,25.098,109.564,134.662,22.852,157.515
|
||||
0,7,1148.919,0.486,1.021,138.981,91.884,230.865,16.504,247.369
|
||||
0,4,1263.894,0.711,0.788,33.761,98.396,132.158,0.671,132.83
|
||||
0,1,1196.433,0.301,0.364,100.757,99.252,200.01,1.116,201.126
|
||||
0,8,1262.594,0.964,1.248,66.394,96.747,163.141,1.173,164.315
|
||||
0,2,1285.445,0.209,0.254,43.808,97.338,141.146,0.548,141.694
|
||||
0,9,1330.271,0.288,0.355,35.329,84.511,119.84,16.98,136.821
|
||||
0,6,1352.928,0.474,0.579,12.904,100.981,113.886,0.517,114.403
|
||||
0,0,1329.353,0.294,0.366,36.781,100.857,137.638,0.532,138.171
|
||||
0,2,1427.143,0.532,0.58,9.336,79.756,89.093,1.239,90.332
|
||||
0,4,1396.736,0.503,0.592,44.925,80.622,125.548,10.972,136.52
|
||||
0,8,1426.921,0.631,0.71,15.906,90.068,105.975,1.38,107.356
|
||||
0,5,1287.759,0.278,0.316,181.454,80.529,261.984,10.774,272.759
|
||||
0,1,1397.566,0.268,0.324,99.635,82.55,182.186,8.445,190.632
|
||||
0,0,1467.53,1.354,1.536,33.298,87.09,120.388,0.517,120.905
|
||||
0,7,1396.3,0.593,0.684,104.729,87.108,191.838,1.133,192.971
|
||||
0,9,1467.103,1.422,1.546,42.719,82.771,125.491,9.398,134.89
|
||||
0,3,1287.85,0.245,0.272,248.437,79.849,328.286,9.318,337.605
|
||||
0,6,1467.339,0.933,1.132,69.074,88.797,157.871,0.891,158.762
|
||||
0,4,1533.267,0.449,0.548,42.003,81.379,123.383,9.108,132.491
|
||||
0,2,1517.483,0.32,0.399,56.455,91.585,148.04,0.44,148.48
|
||||
0,8,1534.284,0.425,0.48,39.738,91.427,131.166,1.247,132.413
|
||||
0,5,1560.528,0.396,0.499,19.318,86.69,106.008,1.54,107.548
|
||||
0,6,1626.109,0.34,0.43,6.88,82.575,89.455,0.568,90.023
|
||||
0,1,1588.231,0.451,0.541,44.902,84.635,129.537,24.285,153.823
|
||||
0,7,1589.279,0.288,0.352,57.597,94.892,152.489,0.555,153.044
|
||||
0,9,1602.002,0.424,0.68,45.095,94.862,139.957,0.797,140.755
|
||||
0,5,1668.084,0.29,0.351,29.143,84.196,113.34,26.201,139.542
|
||||
0,4,1665.766,0.373,0.457,34.457,107.029,141.486,0.715,142.201
|
||||
0,2,1665.971,0.303,0.375,47.175,94.572,141.748,0.861,142.609
|
||||
0,0,1588.445,0.517,0.625,154.081,80.415,234.496,7.112,241.608
|
||||
0,3,1625.463,0.647,0.751,143.666,83.075,226.742,21.951,248.694
|
||||
0,7,1742.325,0.53,0.624,25.438,84.393,109.831,22.061,131.893
|
||||
0,1,1742.079,0.555,0.659,34.769,97.152,131.921,0.364,132.286
|
||||
0,8,1666.705,0.883,1.046,113.237,94.162,207.4,1.118,208.519
|
||||
0,2,1808.586,0.3,0.362,21.568,78.537,100.106,1.979,102.085
|
||||
0,0,1830.064,0.405,0.481,7.114,81.532,88.647,2.392,91.04
|
||||
0,6,1716.143,0.49,0.557,121.371,87.089,208.46,14.395,222.856
|
||||
0,5,1807.634,0.442,0.527,36.463,94.602,131.065,1.369,132.434
|
||||
0,8,1875.228,0.164,0.198,3.582,79.597,83.179,1.504,84.684
|
||||
0,1,1874.367,0.562,0.645,28.304,79.054,107.358,0.456,107.815
|
||||
0,4,1807.975,0.4,0.482,99.4,81.109,180.509,1.133,181.643
|
||||
0,9,1742.773,0.339,0.39,168.948,80.077,249.025,2.367,251.393
|
||||
0,3,1874.166,0.495,0.593,38.988,84.602,123.59,7.982,131.572
|
||||
0,8,1959.921,0.415,0.494,9.861,82.855,92.717,2.765,95.483
|
||||
0,2,1910.682,0.995,1.109,69.161,85.346,154.507,1.625,156.133
|
||||
0,0,1921.114,0.394,0.473,92.984,80.599,173.584,1.153,174.738
|
||||
0,3,2005.748,0.476,0.556,7.441,83.841,91.283,1.422,92.705
|
||||
0,5,1940.076,0.325,0.401,73.91,84.342,158.253,1.718,159.972
|
||||
0,7,1874.22,0.557,0.642,140.027,91.031,231.059,1.754,232.814
|
||||
0,6,1939.008,0.377,0.455,95.473,81.569,177.042,1.072,178.115
|
||||
0,4,1989.626,0.314,0.388,51.23,81.724,132.955,11.694,144.65
|
||||
0,9,1994.183,0.321,0.394,57.158,82.743,139.902,0.758,140.66
|
||||
0,2,2066.819,0.204,0.259,13.808,84.448,98.256,1.366,99.623
|
||||
0,1,1982.189,0.295,0.368,99.448,85.395,184.843,1.56,186.403
|
||||
0,8,2055.408,0.203,0.253,51.567,82.101,133.668,1.073,134.741
|
||||
0,9,2134.846,0.25,0.306,16.106,85.649,101.755,0.176,101.931
|
||||
0,1,2168.598,0.229,0.302,6.826,81.481,88.307,1.143,89.45
|
||||
0,4,2134.304,0.373,0.492,45.962,81.137,127.099,0.549,127.648
|
||||
0,7,2107.039,0.483,0.555,73.813,81.641,155.455,1.562,157.017
|
||||
0,6,2117.128,0.208,0.255,63.776,83.028,146.805,1.656,148.461
|
||||
0,8,2190.154,0.225,0.285,29.098,80.996,110.094,1.344,111.439
|
||||
0,9,2236.784,0.256,0.32,4.01,82.88,86.89,2.12,89.011
|
||||
0,2,2166.449,0.317,0.395,80.763,84.208,164.971,2.32,167.291
|
||||
0,5,2100.052,0.294,0.365,146.743,86.973,233.716,0.672,234.389
|
||||
0,3,2098.458,0.241,0.3,150.5,84.733,235.234,1.292,236.526
|
||||
0,0,2095.857,0.215,0.271,153.005,85.917,238.923,0.534,239.458
|
||||
0,6,2265.593,0.182,0.218,20.159,80.738,100.897,1.449,102.347
|
||||
0,4,2261.957,0.207,0.256,42.386,82.309,124.696,1.433,126.13
|
||||
0,7,2264.061,0.243,0.288,51.339,80.631,131.97,0.973,132.943
|
||||
0,8,2301.604,0.391,0.474,24.05,81.886,105.937,1.805,107.743
|
||||
0,1,2258.053,0.206,0.26,93.644,81.876,175.52,1.331,176.852
|
||||
0,0,2335.321,0.204,0.245,21.603,81.849,103.452,0.941,104.394
|
||||
0,6,2367.949,0.434,0.515,6.274,83.161,89.435,4.495,93.931
|
||||
0,3,2334.991,0.332,0.403,58.507,88.463,146.971,1.116,148.088
|
||||
0,8,2409.356,0.385,0.463,13.78,83.24,97.02,0.344,97.364
|
||||
0,5,2334.448,0.364,0.451,106.034,82.488,188.523,1.39,189.914
|
||||
0,9,2325.809,0.429,0.506,114.736,84.279,199.015,1.209,200.225
|
||||
0,2,2333.745,0.423,0.517,106.853,85.698,192.551,1.745,194.296
|
||||
0,4,2388.097,0.399,0.498,67.532,84.096,151.628,0.599,152.228
|
||||
0,3,2483.086,0.35,0.427,19.21,81.612,100.822,3.51,104.333
|
||||
0,1,2434.913,0.435,0.577,86.727,83.002,169.729,1.902,171.632
|
||||
0,7,2397.012,0.331,0.416,142.874,80.866,223.74,1.672,225.413
|
||||
0,6,2461.891,0.36,0.441,78.194,82.238,160.433,0.613,161.046
|
||||
0,9,2526.038,0.665,0.74,32.614,86.809,119.423,1.275,120.699
|
||||
0,4,2540.332,0.326,0.387,42.093,80.618,122.711,2.268,124.979
|
||||
0,8,2506.727,0.378,0.456,99.838,79.225,179.064,0.294,179.358
|
||||
0,6,2622.939,0.33,0.385,1.186,81.73,82.917,2.248,85.165
|
||||
0,3,2587.429,0.61,0.72,59.939,82.437,142.376,0.97,143.346
|
||||
0,1,2606.549,0.391,0.459,40.636,83.436,124.072,2.096,126.169
|
||||
0,7,2622.432,0.383,0.463,30.735,80.765,111.501,0.733,112.234
|
||||
0,2,2528.046,0.199,0.244,128.905,85.696,214.602,0.334,214.936
|
||||
0,4,2665.318,0.312,0.399,26.866,81.414,108.281,0.222,108.504
|
||||
0,5,2524.369,0.329,0.413,167.907,84.934,252.841,1.305,254.147
|
||||
0,8,2686.096,0.401,0.494,7.747,85.181,92.928,2.125,95.053
|
||||
0,0,2439.722,0.357,0.696,254.259,89.099,343.358,2.809,346.167
|
||||
0,9,2646.75,0.681,0.799,73.064,84.639,157.704,3.532,161.236
|
||||
0,6,2708.115,0.4,0.481,14.501,86.758,101.259,0.934,102.194
|
||||
0,3,2730.783,0.303,0.377,35.013,88.845,123.858,1.666,125.524
|
||||
0,1,2732.726,0.318,0.414,53.138,78.873,132.011,0.237,132.249
|
||||
0,0,2785.893,0.375,0.447,25.451,83.295,108.746,4.165,112.911
|
||||
0,9,2807.993,0.31,0.384,35.981,91.657,127.639,0.466,128.106
|
||||
0,2,2742.992,0.403,0.56,101.119,91.707,192.827,5.458,198.285
|
||||
0,8,2781.157,0.365,0.446,70.781,90.886,161.667,0.817,162.484
|
||||
0,1,2864.982,0.311,0.402,19.474,86.691,106.165,3.435,109.601
|
||||
0,3,2856.319,0.429,0.493,54.672,82.88,137.553,0.33,137.884
|
||||
0,5,2778.523,0.309,0.392,132.818,84.58,217.399,1.527,218.927
|
||||
0,0,2898.815,0.362,0.463,12.416,86.002,98.418,1.107,99.525
|
||||
0,7,2734.674,0.744,0.873,195.477,83.728,279.205,7.848,287.053
|
||||
0,4,2773.831,0.339,0.428,156.128,91.457,247.585,1.311,248.897
|
||||
0,6,2810.317,0.339,0.432,125.657,102.335,227.993,2.034,230.027
|
||||
0,2,2941.285,0.294,0.367,38.02,79.84,117.86,1.696,119.556
|
||||
0,8,2943.648,0.293,0.373,38.288,79.728,118.016,2.042,120.058
|
||||
0,9,2936.108,0.466,0.563,63.933,82.084,146.017,1.602,147.619
|
||||
0,4,3022.735,0.269,0.339,3.697,87.616,91.313,0.516,91.83
|
||||
0,3,2994.213,0.418,0.495,42.946,81.806,124.752,0.29,125.043
|
||||
0,1,2974.591,0.641,0.762,72.809,81.187,153.997,1.512,155.51
|
||||
0,9,3083.737,0.352,0.425,15.144,84.807,99.951,1.383,101.335
|
||||
0,6,3040.353,0.399,0.48,61.605,83.294,144.899,9.906,154.806
|
||||
0,2,3060.852,0.407,0.487,40.928,92.521,133.449,0.893,134.342
|
||||
0,0,2998.348,0.336,0.417,115.561,82.329,197.89,2.808,200.698
|
||||
0,8,3063.714,0.314,0.391,50.53,84.619,135.15,28.56,163.71
|
||||
0,1,3130.111,0.381,0.484,36.604,82.182,118.787,1.306,120.094
|
||||
0,5,2997.458,0.349,0.427,169.477,83.501,252.978,2.447,255.425
|
||||
0,7,3021.738,0.425,0.518,148.774,83.974,232.748,0.411,233.16
|
||||
0,3,3119.263,0.315,0.392,50.462,85,135.463,4.92,140.383
|
||||
0,4,3114.576,0.397,0.465,66.492,81.543,148.035,1.216,149.251
|
||||
0,9,3185.086,0.49,0.563,0.843,79.106,79.95,28.271,108.222
|
||||
0,6,3195.164,0.659,0.878,41.861,81.999,123.86,0.305,124.166
|
||||
0,8,3227.436,0.588,0.685,13.471,80.559,94.03,0.675,94.705
|
||||
0,0,3199.056,0.344,0.417,55.856,81.147,137.003,2.313,139.317
|
||||
0,2,3195.197,0.89,0.993,59.866,83.95,143.817,2.518,146.336
|
||||
0,1,3250.212,0.555,0.641,53.457,80.43,133.887,1.541,135.428
|
||||
0,5,3252.89,0.347,0.424,55.768,81.876,137.644,2.326,139.971
|
||||
0,9,3293.317,0.516,0.622,39.115,78.826,117.941,1.674,119.615
|
||||
0,2,3341.541,0.379,0.456,26.056,81.181,107.238,1.453,108.691
|
||||
0,4,3263.836,0.304,0.385,109.176,79.223,188.399,1.336,189.736
|
||||
0,6,3319.341,0.424,0.509,52.086,83.572,135.658,1.93,137.589
|
||||
0,3,3259.654,0.318,0.4,115.781,84.483,200.264,2.851,203.116
|
||||
0,9,3412.942,0.36,0.432,19.904,83.186,103.091,0.294,103.386
|
||||
0,5,3392.869,0.364,0.438,46.674,81.708,128.382,2.336,130.718
|
||||
0,7,3254.902,0.434,0.504,184.8,83.725,268.526,1.536,270.063
|
||||
0,0,3338.38,0.334,0.412,104.769,84.635,189.405,0.579,189.984
|
||||
0,8,3322.15,0.363,0.429,120.337,85.709,206.047,1.064,207.111
|
||||
0,3,3462.777,0.285,0.363,32.857,78.802,111.659,3.064,114.724
|
||||
0,2,3450.24,0.329,0.416,53.507,82.338,135.845,0.291,136.137
|
||||
0,1,3385.654,0.404,0.479,125.574,82.017,207.591,1.116,208.708
|
||||
0,6,3456.937,0.306,0.374,58.496,80.921,139.418,1.87,141.288
|
||||
0,4,3453.579,0.31,0.387,61.685,82.969,144.655,1.418,146.073
|
||||
0,8,3529.268,0.324,0.408,31.325,78.86,110.186,1.213,111.4
|
||||
0,5,3523.596,0.334,0.417,39.494,83.382,122.877,0.347,123.225
|
||||
0,7,3524.971,0.36,0.472,47.432,80.801,128.234,0.953,129.187
|
||||
0,4,3599.659,0.319,0.398,27.195,80.69,107.885,1.895,109.781
|
||||
0,3,3577.512,0.571,0.652,51.889,82.948,134.837,1.141,135.979
|
||||
0,1,3594.371,0.341,0.422,42.685,81.099,123.785,1.473,125.259
|
||||
0,7,3654.167,0.306,0.383,15.528,81.986,97.515,2.405,99.92
|
||||
0,9,3516.338,0.397,0.472,178.897,79.745,258.642,2.238,260.881
|
||||
0,2,3586.389,1.185,1.333,109.11,81.551,190.661,2.03,192.692
|
||||
0,5,3646.833,0.424,0.488,56.484,81.305,137.789,1.209,138.999
|
||||
0,0,3528.372,0.397,0.487,176.378,80.819,257.198,0.746,257.944
|
||||
0,6,3598.234,0.336,0.428,102.676,85.142,187.818,1.845,189.664
|
||||
0,8,3640.677,0.476,0.58,83.915,81.6,165.515,12.681,178.196
|
||||
0,4,3709.449,0.415,0.495,25.988,83.141,109.13,1.996,111.126
|
||||
0,3,3713.499,0.322,0.402,55.534,81.807,137.341,0.906,138.248
|
||||
0,0,3786.324,0.919,1.147,3.983,80.348,84.331,1.885,86.217
|
||||
0,7,3754.097,0.438,0.543,36.421,81.782,118.204,1.217,119.421
|
||||
0,9,3777.227,0.339,0.419,18.041,81.599,99.641,2.512,102.154
|
||||
0,1,3719.638,0.353,0.419,112.793,82.398,195.191,1.433,196.624
|
||||
0,4,3820.583,0.299,0.38,14.112,83.485,97.598,1.551,99.149
|
||||
0,6,3787.905,0.358,0.44,49.391,82.265,131.656,1.218,132.874
|
||||
0,2,3779.087,0.323,0.402,81.512,79.373,160.885,3.793,164.679
|
||||
0,5,3785.843,1.116,1.253,82.986,77.901,160.888,1.176,162.064
|
||||
0,8,3818.882,0.383,0.46,80.581,80.539,161.121,2.24,163.361
|
||||
0,9,3879.424,0.314,0.394,21.002,81.687,102.689,1.579,104.268
|
||||
0,6,3920.787,0.287,0.38,3.32,80.808,84.129,2.54,86.669
|
||||
0,7,3873.527,0.371,0.436,60.962,79.343,140.305,1.693,141.998
|
||||
0,5,3947.917,0.324,0.401,18.55,86.417,104.968,1.132,106.101
|
||||
0,3,3851.755,0.345,0.42,114.969,87.007,201.977,0.317,202.294
|
||||
0,9,3983.709,0.467,0.534,6.466,81.73,88.196,0.443,88.64
|
||||
0,8,3982.255,0.767,0.998,14.279,81.449,95.729,1.705,97.435
|
||||
0,4,3919.74,0.346,0.424,85.31,79.932,165.243,0.644,165.887
|
||||
0,7,4015.534,0.333,0.409,17.541,80.366,97.907,1.668,99.575
|
||||
0,1,3916.272,0.432,0.512,128.903,84.8,213.703,2.03,215.733
|
||||
0,0,3872.552,0.463,0.605,190.345,81.085,271.43,2.323,273.754
|
||||
0,2,3943.776,0.456,0.565,124.062,79.417,203.479,2.947,206.427
|
||||
0,5,4054.025,0.473,0.519,16.707,81.618,98.325,2.546,100.872
|
||||
0,4,4085.637,0.444,0.528,14.533,83.168,97.701,1.309,99.01
|
||||
0,7,4115.136,0.466,0.563,10.979,80.789,91.768,1.994,93.762
|
||||
0,9,4072.356,0.332,0.411,61.405,81.35,142.756,1.96,144.716
|
||||
0,6,4007.465,0.323,0.404,173.194,81.587,254.782,1.562,256.344
|
||||
0,0,4146.315,0.415,0.495,47.446,82.791,130.237,1.332,131.569
|
||||
0,3,4054.052,0.334,0.407,140.693,83.369,224.063,5.103,229.166
|
||||
0,8,4079.697,0.352,0.431,114.177,84.118,198.295,7.426,205.722
|
||||
0,4,4184.657,0.346,0.42,13.748,86.813,100.561,0.308,100.869
|
||||
0,2,4150.211,0.297,0.391,50.058,85.067,135.125,2.111,137.237
|
||||
0,9,4217.079,0.289,0.372,15.913,78.546,94.459,1.492,95.952
|
||||
0,7,4208.903,0.592,0.799,46.416,79.377,125.794,1.363,127.157
|
||||
0,5,4154.904,0.378,0.458,105.65,86.733,192.384,0.306,192.69
|
||||
0,1,4132.012,0.351,0.423,128.658,87.255,215.914,1.377,217.291
|
||||
0,6,4263.817,0.316,0.392,7.054,80.022,87.076,2.867,89.944
|
||||
0,2,4287.456,0.418,0.519,13.658,77.869,91.528,1.837,93.365
|
||||
0,8,4285.427,0.371,0.448,46.607,81.282,127.89,1.193,129.083
|
||||
0,6,4353.769,0.428,0.512,12.728,83.385,96.114,1.372,97.486
|
||||
0,9,4313.041,0.452,0.544,65.025,81.466,146.492,1.454,147.947
|
||||
0,7,4336.069,0.547,0.631,62.669,80.678,143.347,1.741,145.089
|
||||
0,4,4285.532,0.421,0.489,126.035,80.128,206.164,1.865,208.029
|
||||
0,1,4349.311,0.344,0.419,83.199,81.257,164.457,2.457,166.915
|
||||
0,5,4347.602,0.336,0.415,84.785,84.577,169.362,0.205,169.568
|
||||
0,3,4283.225,0.311,0.39,165.412,81.631,247.043,2.736,249.779
|
||||
0,6,4451.266,0.349,0.435,16.483,81.492,97.976,1.693,99.669
|
||||
0,2,4380.832,0.957,1.096,87.309,82.649,169.959,1.588,171.547
|
||||
0,8,4414.518,0.362,0.479,53.482,84.438,137.92,1.534,139.454
|
||||
0,0,4277.9,0.615,0.698,190.489,85.361,275.85,1.139,276.99
|
||||
0,4,4493.572,0.353,0.433,5.668,79.869,85.538,1.985,87.523
|
||||
0,9,4460.995,0.297,0.379,72.698,82.185,154.884,1.312,156.196
|
||||
0,7,4481.166,0.353,0.43,52.934,82.767,135.702,0.9,136.602
|
||||
0,1,4516.236,0.426,0.513,21.016,82.575,103.591,1.242,104.834
|
||||
0,0,4554.897,0.284,0.36,14.035,80.027,94.063,0.644,94.708
|
||||
0,2,4552.387,0.34,0.416,49.053,82.256,131.309,1.498,132.807
|
||||
0,6,4550.944,0.374,0.452,58.083,82.241,140.324,0.226,140.55
|
||||
0,5,4517.178,0.287,0.348,92.038,83.638,175.677,2.136,177.813
|
||||
0,3,4533.015,0.387,0.482,81.677,80.321,161.999,0.881,162.88
|
||||
0,8,4553.982,0.403,0.5,92.788,79.698,172.487,2.855,175.343
|
||||
0,0,4649.615,0.455,0.528,19.45,84.334,103.785,1.69,105.475
|
||||
0,4,4581.108,0.727,0.888,88.144,85.538,173.683,0.515,174.198
|
||||
0,7,4617.775,0.309,0.38,57.266,80.933,138.2,1.523,139.723
|
||||
0,9,4617.201,0.408,0.513,79.382,81.334,160.716,0.872,161.589
|
||||
0,1,4621.077,0.313,0.394,79.38,80.484,159.864,1.538,161.403
|
||||
0,5,4695,0.323,0.398,26.916,80.04,106.957,1.254,108.212
|
||||
0,8,4729.336,0.417,0.504,8.58,81.443,90.024,1.481,91.506
|
||||
0,6,4691.503,0.315,0.393,52.131,81.54,133.672,1.764,135.436
|
||||
0,7,4757.506,0.336,0.402,8.604,82.634,91.239,2.208,93.447
|
||||
0,3,4695.901,0.364,0.612,110.355,79.703,190.059,2.086,192.145
|
||||
0,4,4755.316,0.387,0.444,71.444,80.424,151.868,1.02,152.889
|
||||
0,1,4782.487,0.804,0.913,71.209,80.168,151.377,1.373,152.751
|
||||
0,2,4685.202,0.318,0.395,168.695,81.247,249.943,1.572,251.515
|
||||
0,0,4755.102,0.475,0.548,109.227,80.705,189.933,0.478,190.411
|
||||
0,7,4850.962,0.47,0.583,25.01,82.997,108.007,1.157,109.164
|
||||
0,5,4803.219,0.334,0.445,72.976,85.095,158.071,1.135,159.207
|
||||
0,9,4778.799,0.486,0.589,120.634,80.375,201.009,1.331,202.341
|
||||
0,4,4908.214,0.415,0.488,15.698,82.28,97.979,1.139,99.118
|
||||
0,6,4826.949,0.393,0.479,97.059,83.146,180.206,1.672,181.879
|
||||
0,8,4820.854,0.484,0.577,109.039,81.594,190.634,0.318,190.953
|
||||
0,2,4936.725,0.354,0.431,6.303,83.521,89.824,1.867,91.692
|
||||
0,3,4888.057,0.498,0.577,71.038,81.397,152.435,0.342,152.777
|
||||
0,5,4962.434,0.279,0.331,14.723,82.679,97.402,0.279,97.682
|
||||
0,7,4960.133,0.327,0.397,44.946,83.613,128.56,0.245,128.805
|
||||
0,3,5040.845,0.439,0.546,14.402,85.033,99.435,2.166,101.602
|
||||
0,8,5011.814,0.411,0.481,59.326,83.914,143.24,1.398,144.639
|
||||
0,2,5028.425,0.366,0.446,42.929,84.889,127.819,0.305,128.124
|
||||
0,0,4945.521,0.346,0.418,150.986,80.727,231.713,2.023,233.737
|
||||
0,9,4981.151,0.45,0.557,115.5,82.904,198.405,2.823,201.228
|
||||
0,5,5060.124,0.398,0.501,64.278,83.236,147.514,2.355,149.869
|
||||
0,6,5008.836,0.28,0.353,115.728,85.538,201.267,0.603,201.871
|
||||
0,1,4935.247,0.379,0.458,189.72,85.303,275.023,0.465,275.488
|
||||
0,4,5007.34,0.542,0.741,117.463,85.645,203.108,2.146,205.255
|
||||
0,7,5088.946,0.313,0.394,49.444,81.597,131.042,1.646,132.688
|
||||
0,3,5142.457,0.399,0.478,5.147,81.287,86.435,0.881,87.317
|
||||
0,2,5156.557,0.291,0.362,5.203,81.382,86.585,1.92,88.505
|
||||
0,8,5156.462,0.521,0.69,19.219,80.308,99.528,1.821,101.349
|
||||
0,9,5182.387,0.32,0.399,23.899,80.953,104.852,1.121,105.973
|
||||
0,7,5221.642,0.307,0.391,5.745,81.815,87.561,1.817,89.378
|
||||
0,0,5179.266,0.363,0.443,49.741,84.892,134.633,2.054,136.688
|
||||
0,5,5210.018,0.568,0.643,61.334,81.577,142.911,1.418,144.33
|
||||
0,3,5229.782,0.339,0.44,49.632,85.029,134.661,1.34,136.002
|
||||
0,1,5210.738,0.792,1.471,86.688,81.747,168.435,0.87,169.305
|
||||
0,9,5288.37,0.428,0.512,5.724,86.497,92.221,2.111,94.333
|
||||
0,2,5245.07,0.351,0.431,61.536,81.418,142.954,1.728,144.682
|
||||
0,0,5315.961,0.382,0.447,20.382,79.869,100.252,1.409,101.661
|
||||
0,8,5257.815,0.423,0.49,92.566,80.892,173.459,1.496,174.955
|
||||
0,7,5311.029,0.435,0.516,52.528,81.313,133.841,1.413,135.255
|
||||
0,6,5210.712,1.532,1.63,151.833,85.155,236.989,1.118,238.107
|
||||
0,3,5365.791,0.299,0.377,50.784,81.585,132.369,1.452,133.822
|
||||
0,2,5389.757,0.474,0.554,26.667,82.959,109.627,1.814,111.441
|
||||
0,0,5417.631,0.784,0.907,8.897,81.026,89.924,1.412,91.336
|
||||
0,4,5212.602,0.386,0.447,217.003,82.217,299.221,1.166,300.387
|
||||
0,9,5382.712,0.311,0.389,75.127,83.211,158.339,1.983,160.323
|
||||
0,5,5354.356,0.305,0.39,112.1,80.95,193.05,1.376,194.427
|
||||
0,7,5446.296,0.356,0.432,36.105,80.157,116.262,0.92,117.183
|
||||
0,1,5380.053,0.405,0.494,111.048,85.12,196.168,1.345,197.514
|
||||
0,6,5448.827,0.315,0.388,42.469,86.109,128.578,0.571,129.149
|
||||
0,8,5432.778,0.329,0.409,64.109,83.938,148.048,0.344,148.393
|
||||
0,4,5512.997,0.298,0.374,12.336,81.353,93.69,2.014,95.704
|
||||
0,3,5499.624,0.486,0.595,59.894,82.285,142.179,1.619,143.799
|
||||
0,5,5548.792,0.428,0.535,10.625,83.853,94.479,1.992,96.472
|
||||
0,2,5501.208,0.575,0.684,90.412,78.346,168.759,0.685,169.444
|
||||
0,4,5608.705,0.558,0.815,8.557,78.683,87.24,1.117,88.358
|
||||
0,1,5577.576,0.426,0.513,55.869,79.618,135.487,1.607,137.095
|
||||
0,0,5508.975,0.328,0.404,126.867,81.307,208.175,1.943,210.118
|
||||
0,9,5543.047,0.36,0.436,90.905,85.163,176.069,2.65,178.719
|
||||
0,2,5670.662,0.41,0.496,11.867,80.168,92.035,2.243,94.278
|
||||
0,7,5563.487,0.303,0.384,127.359,84.02,211.379,1.073,212.452
|
||||
0,6,5577.982,0.339,0.414,117.634,83.729,201.363,0.301,201.665
|
||||
0,5,5645.292,0.373,0.455,50.568,84.436,135.005,1.355,136.36
|
||||
0,4,5697.074,0.417,0.514,14.751,79.581,94.332,2.424,96.757
|
||||
0,3,5643.43,0.367,0.462,102.833,81.493,184.327,2.916,187.244
|
||||
0,0,5719.101,0.348,0.427,42.935,78.555,121.491,1.886,123.377
|
||||
0,8,5581.178,0.307,0.381,181.001,83.057,264.059,1.295,265.354
|
||||
0,9,5721.773,0.324,0.406,46.37,81.578,127.949,1.29,129.239
|
||||
0,2,5764.95,0.399,0.477,28.761,79.071,107.832,2.316,110.149
|
||||
0,1,5714.679,0.318,0.404,98.197,82.491,180.689,1.885,182.574
|
||||
0,4,5793.839,0.314,0.382,30.597,81.38,111.978,1.234,113.212
|
||||
0,7,5775.948,0.332,0.413,51.577,84.088,135.665,0.377,136.043
|
||||
0,3,5830.686,0.955,1.072,11.735,81.606,93.341,1.701,95.043
|
||||
0,9,5851.02,0.333,0.414,18.739,81.701,100.441,1.33,101.771
|
||||
0,2,5875.109,0.396,0.478,15.164,81.893,97.058,1.217,98.275
|
||||
0,5,5781.659,0.318,0.386,119.27,83.15,202.42,0.376,202.797
|
||||
0,7,5911.998,0.339,0.418,11.726,82.093,93.82,1.495,95.315
|
||||
0,3,5925.738,0.447,0.516,21.925,79.87,101.795,0.344,102.14
|
||||
0,0,5842.488,0.338,0.409,124.073,81.734,205.807,2.047,207.855
|
||||
0,6,5779.654,0.326,0.406,188.882,82.945,271.828,0.958,272.786
|
||||
0,1,5897.261,0.301,0.377,74.047,81.4,155.448,0.967,156.415
|
||||
0,8,5846.539,0.337,0.429,140.2,79.976,220.176,2.328,222.505
|
||||
0,9,5952.801,0.344,0.421,54.536,79.755,134.291,1.41,135.702
|
||||
0,7,6007.318,0.522,0.618,14.816,79.956,94.772,16.419,111.192
|
||||
0,2,5973.394,0.518,0.592,60.307,84.753,145.06,0.75,145.81
|
||||
0,5,5984.463,0.449,0.524,50.467,84.478,134.945,1.93,136.876
|
||||
0,4,5907.056,0.402,0.484,129.229,84.9,214.129,1.722,215.851
|
||||
0,3,6027.888,0.42,0.494,39.048,80.015,119.064,1.978,121.042
|
||||
0,0,6050.355,1.316,1.485,29.107,82.111,111.218,1.742,112.96
|
||||
0,1,6053.684,0.311,0.379,47.288,81.844,129.132,1.182,130.315
|
||||
0,6,6052.444,0.503,0.615,67.027,81.05,148.077,1.152,149.229
|
||||
0,5,6121.346,0.337,0.446,23.684,78.068,101.752,2.019,103.772
|
||||
0,8,6069.053,0.397,0.478,79.682,79.537,159.219,1.582,160.802
|
||||
0,9,6088.518,0.344,0.419,65.681,81.3,146.982,13.495,160.477
|
||||
0,7,6118.543,0.547,0.608,50.691,81.862,132.554,0.377,132.931
|
||||
0,3,6148.938,0.351,0.416,38.998,82.443,121.442,10.62,132.062
|
||||
0,1,6184.009,0.625,0.847,30.097,79.57,109.667,2.015,111.682
|
||||
0,4,6122.915,0.478,0.554,133.957,92.839,226.796,1.7,228.497
|
||||
0,2,6119.207,0.42,0.468,136.749,95.595,232.345,3.165,235.51
|
||||
0,5,6225.127,0.307,0.389,30.625,98.714,129.339,0.471,129.811
|
||||
0,8,6229.86,0.669,0.746,51.831,97.397,149.228,1.67,150.898
|
||||
0,6,6201.683,0.393,0.475,85.686,96.242,181.928,5.483,187.412
|
||||
0,9,6249.004,0.394,0.467,68.533,82.42,150.954,0.531,151.485
|
||||
0,0,6163.324,0.47,0.577,154.586,83.082,237.668,12.538,250.207
|
||||
0,7,6251.483,0.329,0.409,71.341,90.405,161.747,0.44,162.188
|
||||
0,3,6281.008,0.413,0.572,45.589,86.798,132.388,1.088,133.476
|
||||
0,1,6295.702,0.418,0.499,51.953,79.591,131.545,1.322,132.867
|
||||
0,4,6351.453,0.526,0.622,1.002,79.648,80.651,2.773,83.424
|
||||
0,2,6354.728,0.554,0.647,34.466,80.677,115.143,0.939,116.083
|
||||
0,5,6354.95,0.507,0.659,65.208,80.513,145.721,4.54,150.262
|
||||
0,6,6389.103,0.371,0.453,31.17,84.714,115.884,0.492,116.376
|
||||
0,0,6413.542,0.446,0.573,10.238,84.236,94.475,0.223,94.699
|
||||
0,1,6428.577,0.312,0.401,27.804,81.855,109.659,1.676,111.336
|
||||
0,7,6413.677,0.485,0.6,45.942,80.918,126.86,2.002,128.863
|
||||
0,2,6470.818,0.322,0.4,18.925,81.521,100.446,0.952,101.399
|
||||
0,4,6434.885,0.367,0.43,63.441,84.026,147.468,2.273,149.741
|
||||
0,0,6508.249,0.291,0.367,14.539,80.234,94.774,2.012,96.787
|
||||
0,9,6400.496,0.321,0.422,124.092,86.419,210.512,2.121,212.633
|
||||
0,6,6505.483,0.332,0.395,50.891,82.623,133.515,2.367,135.883
|
||||
0,5,6505.215,0.47,0.546,66.876,80.667,147.543,1.659,149.202
|
||||
0,2,6572.224,0.316,0.376,17.54,84.437,101.978,0.329,102.308
|
||||
0,4,6584.634,0.383,0.482,15.541,81.204,96.746,1.192,97.939
|
||||
0,9,6613.139,0.441,0.554,10.492,79.826,90.318,1.596,91.915
|
||||
0,7,6542.547,0.337,0.417,113.17,80.125,193.295,1.239,194.535
|
||||
0,0,6605.043,0.334,0.415,57.477,82.496,139.973,1.355,141.329
|
||||
0,3,6414.491,0.775,0.919,247.767,84.12,331.888,2.017,333.905
|
||||
0,8,6380.768,0.437,0.526,281.598,85.832,367.431,0.649,368.081
|
||||
0,5,6654.422,0.74,0.895,7.715,86.517,94.233,1.298,95.531
|
||||
0,1,6539.923,0.391,0.483,149.274,83.239,232.513,2.996,235.509
|
||||
0,4,6682.579,0.336,0.416,6.481,86.19,92.672,10.253,102.925
|
||||
0,6,6641.381,0.317,0.393,59.657,84.226,143.884,0.521,144.405
|
||||
0,9,6705.064,0.411,0.478,19.864,80.4,100.264,1.428,101.693
|
||||
0,2,6674.542,0.419,0.494,53.331,81.556,134.888,1.243,136.131
|
||||
0,7,6737.093,0.433,0.512,18.865,80.421,99.287,0.362,99.649
|
||||
0,8,6748.856,0.377,0.434,17.681,84.385,102.067,0.776,102.843
|
||||
0,6,6785.795,0.516,0.607,9.629,79.503,89.133,2.355,91.488
|
||||
0,5,6749.962,0.412,0.48,73.922,79.8,153.722,1.568,155.291
|
||||
0,0,6746.38,0.315,0.39,110.351,80.636,190.987,1.112,192.1
|
||||
0,7,6836.754,0.55,0.613,24.537,80.93,105.467,2.694,108.162
|
||||
0,8,6851.707,0.33,0.393,20.472,81.812,102.285,1.242,103.527
|
||||
0,6,6877.293,0.396,0.464,12.523,81.162,93.686,1.659,95.345
|
||||
0,4,6785.508,0.589,0.692,115.734,86.656,202.391,0.733,203.124
|
||||
0,1,6775.441,0.33,0.414,130.532,83.319,213.851,1.687,215.538
|
||||
0,9,6806.765,0.34,0.423,99.015,85.013,184.029,3.078,187.108
|
||||
0,3,6748.406,0.486,0.618,174.309,81.219,255.529,4.272,259.801
|
||||
0,5,6905.264,0.568,0.647,23.753,80.364,104.118,0.631,104.749
|
||||
0,8,6955.243,0.326,0.406,2.524,80.645,83.169,0.755,83.925
|
||||
0,0,6938.49,0.461,0.541,27.562,81.761,109.323,1.228,110.552
|
||||
0,2,6810.68,0.93,1.1,197.267,80.838,278.105,1.271,279.376
|
||||
0,7,6944.924,0.37,0.455,63.266,82.346,145.613,1.637,147.251
|
||||
0,4,6988.643,0.466,0.606,34.89,83.461,118.352,3.998,122.35
|
||||
0,3,7008.212,0.533,0.621,66.77,79.833,146.603,1.377,147.98
|
||||
0,0,7049.053,0.377,0.459,25.731,82.163,107.895,1.657,109.553
|
||||
0,6,6972.667,0.354,0.428,119.457,80.472,199.93,0.336,200.266
|
||||
0,7,7092.184,0.337,0.406,4.213,82.36,86.573,1.179,87.752
|
||||
0,9,6993.88,0.439,0.511,110.675,82.611,193.287,1.277,194.564
|
||||
0,1,6990.987,0.309,0.389,131.279,80.628,211.907,1.199,213.107
|
||||
0,8,7039.181,1.011,1.109,91.879,83.571,175.451,1.855,177.306
|
||||
0,2,7090.066,0.356,0.455,51.305,82.594,133.899,1.56,135.459
|
||||
0,4,7111.004,0.415,0.497,30.584,84.877,115.461,1.764,117.225
|
||||
0,5,7010.022,0.464,0.541,141.61,82.79,224.4,2.016,226.417
|
||||
0,0,7158.615,0.364,0.442,4.359,80.082,84.441,1.28,85.722
|
||||
0,6,7172.942,0.521,0.598,26.196,79.661,105.858,1.066,106.924
|
||||
0,3,7156.204,0.42,0.502,53.726,79.747,133.473,1.584,135.058
|
||||
0,1,7204.101,0.331,0.416,26.093,81.632,107.725,2,109.726
|
||||
0,0,7244.345,0.32,0.398,21.466,80.972,102.438,1.166,103.605
|
||||
0,5,7236.446,0.493,0.55,41.039,79.52,120.56,2.102,122.662
|
||||
0,9,7188.454,0.601,0.747,110.165,80.817,190.983,0.996,191.979
|
||||
0,8,7216.494,0.341,0.427,81.98,80.888,162.869,2.197,165.067
|
||||
0,7,7179.945,0.403,0.485,119.842,81.613,201.455,0.476,201.931
|
||||
0,2,7225.536,0.484,0.582,74.136,81.65,155.786,1.136,156.923
|
||||
0,1,7313.837,0.452,0.567,18.595,83.189,101.784,2.503,104.288
|
||||
0,6,7279.875,0.328,0.411,61.184,81.339,142.524,2.233,144.757
|
||||
0,5,7359.116,0.325,0.402,6.764,82.524,89.288,1.53,90.819
|
||||
0,4,7228.237,0.716,0.876,138.937,84.177,223.115,0.301,223.416
|
||||
0,7,7381.883,0.288,0.383,22.346,80.881,103.228,1.238,104.466
|
||||
0,3,7291.272,0.443,0.52,113.137,81.756,194.893,1.745,196.639
|
||||
0,8,7381.568,0.403,0.472,54.796,80.499,135.296,0.451,135.748
|
||||
0,0,7347.958,0.324,0.405,118.366,81.119,199.485,1.507,200.992
|
||||
0,9,7380.444,0.554,0.68,93.055,79.533,172.588,1.514,174.103
|
||||
0,4,7451.66,0.316,0.384,22.329,80.797,103.127,2.351,105.478
|
||||
0,3,7487.919,0.325,0.403,15.077,82.38,97.457,17.729,115.187
|
||||
0,1,7418.133,0.432,0.498,97.07,87.573,184.644,0.619,185.263
|
||||
0,8,7517.326,0.441,0.512,15.017,86.247,101.265,0.225,101.49
|
||||
0,6,7424.648,0.311,0.394,113.912,82.157,196.07,0.29,196.361
|
||||
0,5,7449.946,0.966,1.145,91.384,80.285,171.67,1.546,173.216
|
||||
0,2,7382.465,0.311,0.368,155.965,84.581,240.547,6.505,247.052
|
||||
0,7,7486.358,0.32,0.403,62.472,82.007,144.479,5.539,150.018
|
||||
0,4,7557.146,0.366,0.452,15.698,80.87,96.568,13.833,110.402
|
||||
0,0,7548.957,0.315,0.408,53.927,80.333,134.26,1.998,136.259
|
||||
0,9,7554.554,0.424,0.512,48.698,81.789,130.488,2.084,132.572
|
||||
0,3,7603.119,0.564,0.632,66.287,80.673,146.961,1.472,148.433
|
||||
0,6,7621.016,0.287,0.361,50.611,79.755,130.366,2.06,132.427
|
||||
0,1,7603.404,0.35,0.44,71.549,81.794,153.343,1.394,154.737
|
||||
0,7,7636.385,0.328,0.393,38.377,83.192,121.569,7.821,129.391
|
||||
0,8,7618.824,0.315,0.391,58.371,88.394,146.766,0.268,147.034
|
||||
0,4,7667.559,0.458,0.544,41.885,80.925,122.811,2.257,125.068
|
||||
0,0,7685.224,0.379,0.462,51.5,85.236,136.737,1.835,138.572
|
||||
0,5,7623.17,0.345,0.429,113.716,86.773,200.489,2.099,202.589
|
||||
0,9,7687.135,0.365,0.44,55.114,83.348,138.463,1.93,140.394
|
||||
0,2,7629.525,0.75,0.881,112.85,84.984,197.834,1.481,199.316
|
||||
0,6,7753.451,0.348,0.443,19.425,81.92,101.345,0.235,101.581
|
||||
0,8,7765.861,0.295,0.382,42.989,83.301,126.291,0.309,126.6
|
||||
0,3,7751.56,0.371,0.447,57.895,83.908,141.804,1.772,143.577
|
||||
0,1,7758.148,0.305,0.375,61.756,83.904,145.661,0.303,145.964
|
||||
0,4,7792.635,0.385,0.467,48.907,82.184,131.091,1.903,132.995
|
||||
0,7,7765.787,0.508,0.598,82.233,80.202,162.436,0.937,163.373
|
||||
0,5,7825.768,0.352,0.433,48.817,83.915,132.733,1.856,134.59
|
||||
0,0,7823.804,0.347,0.42,81.14,78.737,159.878,2.87,162.748
|
||||
0,3,7895.144,0.324,0.404,19.162,79.062,98.225,1.353,99.579
|
||||
0,1,7904.124,0.531,0.75,44.278,80.547,124.825,2.869,127.694
|
||||
0,7,7929.169,0.305,0.4,22.345,80.727,103.073,0.274,103.347
|
||||
0,9,7827.535,0.315,0.398,125.914,79.985,205.899,1.407,207.306
|
||||
0,4,7925.637,0.295,0.37,55.306,79.662,134.969,1.413,136.382
|
||||
0,6,7855.04,0.348,0.424,129.077,82.84,211.917,1.083,213
|
||||
0,2,7828.844,0.332,0.377,154.911,84.522,239.433,0.778,240.211
|
||||
0,8,7892.473,0.449,0.525,91.578,85.603,177.182,9.727,186.909
|
||||
0,5,7960.366,0.389,0.468,55.094,82.706,137.801,1.851,139.652
|
||||
0,3,7994.732,0.337,0.41,22.79,83.043,105.834,1.32,107.154
|
||||
0,7,8032.525,0.351,0.425,16.842,80.964,97.806,3.531,101.338
|
||||
0,0,7986.561,0.295,0.37,65.496,81.656,147.152,1.49,148.643
|
||||
0,9,8034.848,0.327,0.409,49.351,81.008,130.359,9.179,139.539
|
||||
0,4,8062.031,0.837,1.09,46.288,82.287,128.575,3.955,132.531
|
||||
0,5,8100.03,0.389,0.486,18.157,82.12,100.278,2.442,102.72
|
||||
0,1,8031.827,0.478,0.608,96.665,82.449,179.114,1.973,181.087
|
||||
0,2,8069.062,0.342,0.407,85.309,81.496,166.805,0.329,167.135
|
||||
0,9,8174.399,0.474,0.569,11.749,83.111,94.861,1.975,96.836
|
||||
0,3,8101.893,0.317,0.394,85.135,84.047,169.183,2.215,171.398
|
||||
0,8,8079.389,0.343,0.409,114.95,81.574,196.524,0.902,197.427
|
||||
0,1,8212.925,0.393,0.494,5.508,81.466,86.974,2.467,89.442
|
||||
0,6,8068.049,0.4,0.476,153.286,83.743,237.03,1.172,238.202
|
||||
0,4,8194.572,0.388,0.455,46.45,82.449,128.899,3.487,132.387
|
||||
0,7,8133.87,0.316,0.402,127.895,81.295,209.19,0.378,209.569
|
||||
0,0,8135.211,0.37,0.49,149.827,95.401,245.228,0.525,245.753
|
||||
0,5,8202.758,0.299,0.378,83.997,93.829,177.826,0.433,178.259
|
||||
0,2,8236.206,0.419,0.527,47.407,97.117,144.525,0.519,145.044
|
||||
0,6,8306.259,0.336,0.413,3.622,81.754,85.377,3.954,89.331
|
||||
0,9,8271.247,0.468,1.138,48.801,82.285,131.086,5.775,136.861
|
||||
0,7,8343.45,0.445,0.55,7.533,83.535,91.069,12.373,103.442
|
||||
0,1,8302.377,0.352,0.429,54.306,89.95,144.257,1.044,145.301
|
||||
0,8,8276.825,0.397,0.494,104.45,87.657,192.107,1.003,193.111
|
||||
0,3,8273.295,0.582,0.657,107.827,88.457,196.284,0.432,196.717
|
||||
0,4,8326.967,0.382,0.454,55.066,88.862,143.928,3.529,147.457
|
||||
0,9,8408.118,0.387,0.47,10.387,81.323,91.711,2.096,93.808
|
||||
0,5,8381.02,0.737,0.809,72.154,82.173,154.327,2.08,156.408
|
||||
0,6,8395.599,0.31,0.381,60.746,88.193,148.94,0.223,149.163
|
||||
0,1,8447.686,0.287,0.348,15.042,82.895,97.937,3.174,101.111
|
||||
0,0,8380.969,0.55,0.685,84.005,88.633,172.638,1.193,173.831
|
||||
0,4,8474.432,0.295,0.375,10.992,87.808,98.8,15.063,113.864
|
||||
0,8,8469.946,0.996,1.127,55.763,87.871,143.635,0.35,143.985
|
||||
0,0,8554.808,0.306,0.389,4.062,97.274,101.336,1.629,102.966
|
||||
0,9,8501.936,0.364,0.441,57.994,97.65,155.644,0.333,155.977
|
||||
0,5,8537.438,0.409,0.487,24.686,95.581,120.268,0.765,121.034
|
||||
0,7,8446.903,0.418,0.538,166.93,82.688,249.618,12.816,262.434
|
||||
0,3,8470.014,1.107,1.204,144.07,95.186,239.257,0.458,239.715
|
||||
0,1,8548.805,0.291,0.366,65.599,95.008,160.607,1.759,162.367
|
||||
0,2,8381.252,0.64,0.703,232.706,95.11,327.816,2.124,329.941
|
||||
0,4,8588.308,0.451,0.53,47.676,81.686,129.363,19.95,149.313
|
||||
0,6,8544.77,0.425,0.514,94.256,98.361,192.617,1.175,193.793
|
||||
0,8,8613.935,0.482,0.648,66.74,79.822,146.562,13.637,160.199
|
||||
0,5,8658.478,0.364,0.461,23.727,91.555,115.283,0.678,115.961
|
||||
0,0,8657.783,0.359,0.437,29.469,86.909,116.378,1.615,117.994
|
||||
0,9,8657.92,0.434,0.494,52.377,80.121,132.499,2.162,134.661
|
||||
0,7,8709.341,1.143,1.286,2.176,84.412,86.589,18.531,105.121
|
||||
0,0,8775.784,0.34,0.447,2.582,677.582,680.165,1.972,682.138
|
||||
0,4,8737.632,0.477,0.553,40.936,679.191,720.127,46.877,767.005
|
||||
0,1,8711.177,0.458,0.532,46.203,746.914,793.118,0.676,793.795
|
||||
0,2,8711.197,0.549,0.614,36.637,756.865,793.503,0.62,794.123
|
||||
0,3,8709.732,0.968,1.401,729.574,148.167,877.741,1.855,879.597
|
||||
0,9,8792.592,0.382,0.461,647.063,212.999,860.063,2.327,862.391
|
||||
0,5,8774.449,0.442,0.497,665.322,278.888,944.21,1.71,945.921
|
||||
0,8,8774.139,0.542,0.675,666.023,348.013,1014.037,3.225,1017.262
|
||||
0,7,8814.472,0.42,0.499,625.747,350.894,976.641,1.077,977.719
|
||||
0,3,9589.338,0.74,0.84,52.156,212.561,264.717,1.988,266.706
|
||||
0,1,9504.98,0.395,0.478,199.746,152.4,352.146,1.436,353.583
|
||||
0,2,9505.326,0.369,0.408,200.027,212.787,412.815,0.346,413.162
|
||||
0,5,9720.381,0.408,0.468,50.974,150.027,201.002,0.889,201.892
|
||||
0,9,9654.994,0.436,0.496,119.325,206.595,325.92,1.9,327.82
|
||||
0,7,9792.196,0.259,0.853,45.479,147.983,193.463,1.292,194.756
|
||||
0,8,9791.412,0.416,0.474,47.556,209.663,257.22,1.399,258.62
|
||||
0,1,9858.571,0.332,0.388,47.169,146.521,193.691,1.358,195.049
|
||||
0,6,8738.566,0.293,0.349,1167.354,156.58,1323.935,0.934,1324.869
|
||||
0,5,9922.281,0.382,0.438,49.221,145.247,194.469,1.392,195.862
|
||||
0,7,9986.962,0.364,0.42,47.021,150.528,197.549,1.334,198.884
|
||||
0,0,9457.939,0.316,0.395,578.457,150.278,728.735,1.254,729.99
|
||||
0,9,9982.825,0.43,0.5,64.168,199.563,263.731,0.345,264.077
|
||||
0,3,9856.054,0.416,0.472,247.039,147.388,394.428,1.72,396.148
|
||||
0,4,9504.641,0.548,0.619,664.554,141.019,805.573,1.417,806.991
|
||||
0,2,9918.499,0.404,0.459,250.86,142.241,393.101,1.155,394.257
|
||||
|
+178
@@ -0,0 +1,178 @@
|
||||
[
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://bucket-name.s3.amazonaws.com",
|
||||
"Region": "us-east-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://bucket-name.s3.us-west-1.amazonaws.com",
|
||||
"Region": "us-west-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-with-number-1",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://bucket-with-number-1.s3.us-west-1.amazonaws.com",
|
||||
"Region": "us-west-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://bucket-name.s3.cn-north-1.amazonaws.com.cn",
|
||||
"Region": "cn-north-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "BucketName",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://s3.amazonaws.com/BucketName",
|
||||
"Region": "us-east-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "BucketName",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://s3.amazonaws.com/BucketName",
|
||||
"Region": "us-east-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket_name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://s3.us-west-1.amazonaws.com/bucket_name",
|
||||
"Region": "us-west-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket.name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://s3.us-west-1.amazonaws.com/bucket.name",
|
||||
"Region": "us-west-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "-bucket-name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://s3.us-west-1.amazonaws.com/-bucket-name",
|
||||
"Region": "us-west-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name-",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://s3.us-west-1.amazonaws.com/bucket-name-",
|
||||
"Region": "us-west-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "aa",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://s3.us-west-1.amazonaws.com/aa",
|
||||
"Region": "us-west-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://s3.us-west-1.amazonaws.com/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
||||
"Region": "us-west-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://bucket-name.s3-accelerate.amazonaws.com",
|
||||
"Region": "us-east-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": true
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://bucket-name.s3-accelerate.amazonaws.com",
|
||||
"Region": "us-west-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": true
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://bucket-name.s3.dualstack.us-east-1.amazonaws.com",
|
||||
"Region": "us-east-1",
|
||||
"UseDualstack": true,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://bucket-name.s3.dualstack.us-west-2.amazonaws.com",
|
||||
"Region": "us-west-2",
|
||||
"UseDualstack": true,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket.name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://s3.dualstack.us-west-2.amazonaws.com/bucket.name",
|
||||
"Region": "us-west-2",
|
||||
"UseDualstack": true,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "default",
|
||||
"ExpectedUri": "https://bucket-name.s3-accelerate.dualstack.amazonaws.com",
|
||||
"Region": "us-east-1",
|
||||
"UseDualstack": true,
|
||||
"UseS3Accelerate": true
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "path",
|
||||
"ExpectedUri": "https://s3.amazonaws.com/bucket-name",
|
||||
"Region": "us-east-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "path",
|
||||
"ExpectedUri": "https://bucket-name.s3-accelerate.amazonaws.com",
|
||||
"Region": "us-east-1",
|
||||
"UseDualstack": false,
|
||||
"UseS3Accelerate": true
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "path",
|
||||
"ExpectedUri": "https://s3.dualstack.us-east-1.amazonaws.com/bucket-name",
|
||||
"Region": "us-east-1",
|
||||
"UseDualstack": true,
|
||||
"UseS3Accelerate": false
|
||||
},
|
||||
{
|
||||
"Bucket": "bucket-name",
|
||||
"ConfiguredAddressingStyle": "path",
|
||||
"ExpectedUri": "https://bucket-name.s3-accelerate.dualstack.amazonaws.com",
|
||||
"Region": "us-east-1",
|
||||
"UseDualstack": true,
|
||||
"UseS3Accelerate": true
|
||||
}
|
||||
]
|
||||
+36
-51
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
|
||||
)
|
||||
|
||||
type xmlErrorResponse struct {
|
||||
@@ -23,54 +24,56 @@ func unmarshalError(r *request.Request) {
|
||||
defer r.HTTPResponse.Body.Close()
|
||||
defer io.Copy(ioutil.Discard, r.HTTPResponse.Body)
|
||||
|
||||
hostID := r.HTTPResponse.Header.Get("X-Amz-Id-2")
|
||||
|
||||
// Bucket exists in a different region, and request needs
|
||||
// to be made to the correct region.
|
||||
if r.HTTPResponse.StatusCode == http.StatusMovedPermanently {
|
||||
r.Error = requestFailure{
|
||||
RequestFailure: awserr.NewRequestFailure(
|
||||
awserr.New("BucketRegionError",
|
||||
fmt.Sprintf("incorrect region, the bucket is not in '%s' region",
|
||||
aws.StringValue(r.Config.Region)),
|
||||
nil),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
),
|
||||
hostID: hostID,
|
||||
msg := fmt.Sprintf(
|
||||
"incorrect region, the bucket is not in '%s' region at endpoint '%s'",
|
||||
aws.StringValue(r.Config.Region),
|
||||
aws.StringValue(r.Config.Endpoint),
|
||||
)
|
||||
if v := r.HTTPResponse.Header.Get("x-amz-bucket-region"); len(v) != 0 {
|
||||
msg += fmt.Sprintf(", bucket is in '%s' region", v)
|
||||
}
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New("BucketRegionError", msg, nil),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
var errCode, errMsg string
|
||||
|
||||
// Attempt to parse error from body if it is known
|
||||
resp := &xmlErrorResponse{}
|
||||
err := xml.NewDecoder(r.HTTPResponse.Body).Decode(resp)
|
||||
if err != nil && err != io.EOF {
|
||||
errCode = "SerializationError"
|
||||
errMsg = "failed to decode S3 XML error response"
|
||||
} else {
|
||||
errCode = resp.Code
|
||||
errMsg = resp.Message
|
||||
var errResp xmlErrorResponse
|
||||
err := xmlutil.UnmarshalXMLError(&errResp, r.HTTPResponse.Body)
|
||||
if err == io.EOF {
|
||||
// Only capture the error if an unmarshal error occurs that is not EOF,
|
||||
// because S3 might send an error without a error message which causes
|
||||
// the XML unmarshal to fail with EOF.
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New(request.ErrCodeSerialization,
|
||||
"failed to unmarshal error message", err),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Fallback to status code converted to message if still no error code
|
||||
if len(errCode) == 0 {
|
||||
if len(errResp.Code) == 0 {
|
||||
statusText := http.StatusText(r.HTTPResponse.StatusCode)
|
||||
errCode = strings.Replace(statusText, " ", "", -1)
|
||||
errMsg = statusText
|
||||
errResp.Code = strings.Replace(statusText, " ", "", -1)
|
||||
errResp.Message = statusText
|
||||
}
|
||||
|
||||
r.Error = requestFailure{
|
||||
RequestFailure: awserr.NewRequestFailure(
|
||||
awserr.New(errCode, errMsg, err),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
),
|
||||
hostID: hostID,
|
||||
}
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New(errResp.Code, errResp.Message, err),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
}
|
||||
|
||||
// A RequestFailure provides access to the S3 Request ID and Host ID values
|
||||
@@ -83,21 +86,3 @@ type RequestFailure interface {
|
||||
// Host ID is the S3 Host ID needed for debug, and contacting support
|
||||
HostID() string
|
||||
}
|
||||
|
||||
type requestFailure struct {
|
||||
awserr.RequestFailure
|
||||
|
||||
hostID string
|
||||
}
|
||||
|
||||
func (r requestFailure) Error() string {
|
||||
extra := fmt.Sprintf("status code: %d, request id: %s, host id: %s",
|
||||
r.StatusCode(), r.RequestID(), r.hostID)
|
||||
return awserr.SprintError(r.Code(), r.Message(), extra, r.OrigErr())
|
||||
}
|
||||
func (r requestFailure) String() string {
|
||||
return r.Error()
|
||||
}
|
||||
func (r requestFailure) HostID() string {
|
||||
return r.hostID
|
||||
}
|
||||
|
||||
+1
-1
@@ -162,7 +162,7 @@ func TestUnmarshalError(t *testing.T) {
|
||||
if e, a := c.Code, err.(awserr.Error).Code(); e != a {
|
||||
t.Errorf("%d, Code: expect %s, got %s", i, e, a)
|
||||
}
|
||||
if e, a := c.Msg, err.(awserr.Error).Message(); e != a {
|
||||
if e, a := c.Msg, err.(awserr.Error).Message(); !strings.Contains(a, e) {
|
||||
t.Errorf("%d, Message: expect %s, got %s", i, e, a)
|
||||
}
|
||||
if e, a := c.ReqID, err.(awserr.RequestFailure).RequestID(); e != a {
|
||||
|
||||
Reference in New Issue
Block a user