|
|
@ -3,12 +3,19 @@ package cos |
|
|
|
import ( |
|
|
|
"bytes" |
|
|
|
"context" |
|
|
|
"crypto/rand" |
|
|
|
"encoding/hex" |
|
|
|
"encoding/xml" |
|
|
|
"fmt" |
|
|
|
"hash/crc64" |
|
|
|
"io/ioutil" |
|
|
|
"net/http" |
|
|
|
"net/url" |
|
|
|
"os" |
|
|
|
"reflect" |
|
|
|
"strconv" |
|
|
|
"testing" |
|
|
|
"time" |
|
|
|
) |
|
|
|
|
|
|
|
func TestObjectService_Get(t *testing.T) { |
|
|
@ -59,25 +66,110 @@ func TestObjectService_Put(t *testing.T) { |
|
|
|
} |
|
|
|
name := "test/hello.txt" |
|
|
|
|
|
|
|
retry := 0 |
|
|
|
final := 10 |
|
|
|
mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) { |
|
|
|
testMethod(t, r, http.MethodPut) |
|
|
|
testHeader(t, r, "x-cos-acl", "private") |
|
|
|
testHeader(t, r, "Content-Type", "text/html") |
|
|
|
|
|
|
|
b, _ := ioutil.ReadAll(r.Body) |
|
|
|
v := string(b) |
|
|
|
want := "hello" |
|
|
|
if !reflect.DeepEqual(v, want) { |
|
|
|
t.Errorf("Object.Put request body: %#v, want %#v", v, want) |
|
|
|
if retry%2 == 0 { |
|
|
|
b, _ := ioutil.ReadAll(r.Body) |
|
|
|
tb := crc64.MakeTable(crc64.ECMA) |
|
|
|
crc := crc64.Update(0, tb, b) |
|
|
|
v := string(b) |
|
|
|
want := "hello" |
|
|
|
if !reflect.DeepEqual(v, want) { |
|
|
|
t.Errorf("Object.Put request body: %#v, want %#v", v, want) |
|
|
|
} |
|
|
|
realcrc := crc64.Update(0, tb, []byte("hello")) |
|
|
|
if !reflect.DeepEqual(crc, realcrc) { |
|
|
|
t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc) |
|
|
|
} |
|
|
|
w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10)) |
|
|
|
if retry != final { |
|
|
|
w.WriteHeader(http.StatusGatewayTimeout) |
|
|
|
} |
|
|
|
} else { |
|
|
|
w.Header().Add("x-cos-hash-crc64ecma", "123456789") |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
r := bytes.NewReader([]byte("hello")) |
|
|
|
_, err := client.Object.Put(context.Background(), name, r, opt) |
|
|
|
for retry <= final { |
|
|
|
r := bytes.NewReader([]byte("hello")) |
|
|
|
_, err := client.Object.Put(context.Background(), name, r, opt) |
|
|
|
if retry < final && err == nil { |
|
|
|
t.Fatalf("Error must not nil when retry < final") |
|
|
|
} |
|
|
|
if retry == final && err != nil { |
|
|
|
t.Fatalf("Put Error: %v", err) |
|
|
|
} |
|
|
|
retry++ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestObjectService_PutFromFile(t *testing.T) { |
|
|
|
setup() |
|
|
|
defer teardown() |
|
|
|
|
|
|
|
filePath := "tmpfile" + time.Now().Format(time.RFC3339) |
|
|
|
newfile, err := os.Create(filePath) |
|
|
|
if err != nil { |
|
|
|
t.Fatalf("Object.Put returned error: %v", err) |
|
|
|
t.Fatalf("create tmp file failed") |
|
|
|
} |
|
|
|
defer os.Remove(filePath) |
|
|
|
// 源文件内容
|
|
|
|
b := make([]byte, 1024*1024*3) |
|
|
|
_, err = rand.Read(b) |
|
|
|
newfile.Write(b) |
|
|
|
newfile.Close() |
|
|
|
|
|
|
|
tb := crc64.MakeTable(crc64.ECMA) |
|
|
|
realcrc := crc64.Update(0, tb, b) |
|
|
|
opt := &ObjectPutOptions{ |
|
|
|
ObjectPutHeaderOptions: &ObjectPutHeaderOptions{ |
|
|
|
ContentType: "text/html", |
|
|
|
}, |
|
|
|
ACLHeaderOptions: &ACLHeaderOptions{ |
|
|
|
XCosACL: "private", |
|
|
|
}, |
|
|
|
} |
|
|
|
name := "test/hello.txt" |
|
|
|
retry := 0 |
|
|
|
final := 4 |
|
|
|
mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) { |
|
|
|
testMethod(t, r, http.MethodPut) |
|
|
|
testHeader(t, r, "x-cos-acl", "private") |
|
|
|
testHeader(t, r, "Content-Type", "text/html") |
|
|
|
|
|
|
|
if retry%2 == 0 { |
|
|
|
bs, _ := ioutil.ReadAll(r.Body) |
|
|
|
crc := crc64.Update(0, tb, bs) |
|
|
|
if !reflect.DeepEqual(bs, b) { |
|
|
|
t.Errorf("Object.Put request body Error") |
|
|
|
} |
|
|
|
if !reflect.DeepEqual(crc, realcrc) { |
|
|
|
t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc) |
|
|
|
} |
|
|
|
w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10)) |
|
|
|
if retry != final { |
|
|
|
w.WriteHeader(http.StatusGatewayTimeout) |
|
|
|
} |
|
|
|
} else { |
|
|
|
w.Header().Add("x-cos-hash-crc64ecma", "123456789") |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
for retry <= final { |
|
|
|
_, err := client.Object.PutFromFile(context.Background(), name, filePath, opt) |
|
|
|
if retry < final && err == nil { |
|
|
|
t.Fatalf("Error must not nil when retry < final") |
|
|
|
} |
|
|
|
if retry == final && err != nil { |
|
|
|
t.Fatalf("Put Error: %v", err) |
|
|
|
} |
|
|
|
retry++ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestObjectService_Delete(t *testing.T) { |
|
|
@ -114,7 +206,6 @@ func TestObjectService_Head(t *testing.T) { |
|
|
|
if err != nil { |
|
|
|
t.Fatalf("Object.Head returned error: %v", err) |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
func TestObjectService_Options(t *testing.T) { |
|
|
@ -272,3 +363,90 @@ func TestObjectService_Copy(t *testing.T) { |
|
|
|
t.Errorf("Object.Copy returned %+v, want %+v", ref, want) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func TestObjectService_Upload(t *testing.T) { |
|
|
|
setup() |
|
|
|
defer teardown() |
|
|
|
|
|
|
|
filePath := "tmpfile" + time.Now().Format(time.RFC3339) |
|
|
|
newfile, err := os.Create(filePath) |
|
|
|
if err != nil { |
|
|
|
t.Fatalf("create tmp file failed") |
|
|
|
} |
|
|
|
defer os.Remove(filePath) |
|
|
|
// 源文件内容
|
|
|
|
b := make([]byte, 1024*1024*10) |
|
|
|
_, err = rand.Read(b) |
|
|
|
newfile.Write(b) |
|
|
|
newfile.Close() |
|
|
|
|
|
|
|
// 已上传内容, 10个分块
|
|
|
|
rb := make([][]byte, 10) |
|
|
|
uploadid := "test-cos-multiupload-uploadid" |
|
|
|
partmap := make(map[int64]int) |
|
|
|
mux.HandleFunc("/test.go.upload", func(w http.ResponseWriter, r *http.Request) { |
|
|
|
if r.Method == http.MethodPut { // 分块上传
|
|
|
|
r.ParseForm() |
|
|
|
part, _ := strconv.ParseInt(r.Form.Get("partNumber"), 10, 64) |
|
|
|
if partmap[part] == 0 { |
|
|
|
// 重试检验1
|
|
|
|
partmap[part]++ |
|
|
|
ioutil.ReadAll(r.Body) |
|
|
|
w.WriteHeader(http.StatusGatewayTimeout) |
|
|
|
} else if partmap[part] == 1 { |
|
|
|
// 重试校验2
|
|
|
|
partmap[part]++ |
|
|
|
w.Header().Add("x-cos-hash-crc64ecma", "123456789") |
|
|
|
} else { // 正确上传
|
|
|
|
bs, _ := ioutil.ReadAll(r.Body) |
|
|
|
rb[part-1] = bs |
|
|
|
md := hex.EncodeToString(calMD5Digest(bs)) |
|
|
|
crc := crc64.Update(0, crc64.MakeTable(crc64.ECMA), bs) |
|
|
|
w.Header().Add("ETag", md) |
|
|
|
w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10)) |
|
|
|
} |
|
|
|
} else { |
|
|
|
testMethod(t, r, http.MethodPost) |
|
|
|
initreq := url.Values{} |
|
|
|
initreq.Set("uploads", "") |
|
|
|
compreq := url.Values{} |
|
|
|
compreq.Set("uploadId", uploadid) |
|
|
|
r.ParseForm() |
|
|
|
if reflect.DeepEqual(r.Form, initreq) { |
|
|
|
// 初始化分块上传
|
|
|
|
fmt.Fprintf(w, `<InitiateMultipartUploadResult> |
|
|
|
<Bucket></Bucket> |
|
|
|
<Key>%v</Key> |
|
|
|
<UploadId>%v</UploadId> |
|
|
|
</InitiateMultipartUploadResult>`, "test.go.upload", uploadid) |
|
|
|
} else if reflect.DeepEqual(r.Form, compreq) { |
|
|
|
// 完成分块上传
|
|
|
|
tb := crc64.MakeTable(crc64.ECMA) |
|
|
|
crc := uint64(0) |
|
|
|
ccv := make([]uint64, 10) |
|
|
|
for i, v := range rb { |
|
|
|
ccv[i] = crc64.Update(0, crc64.MakeTable(crc64.ECMA), v) |
|
|
|
crc = crc64.Update(crc, tb, v) |
|
|
|
} |
|
|
|
w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10)) |
|
|
|
fmt.Fprintf(w, `<CompleteMultipartUploadResult> |
|
|
|
<Location>/test.go.upload</Location> |
|
|
|
<Bucket></Bucket> |
|
|
|
<Key>test.go.upload</Key> |
|
|
|
<ETag>"%v"</ETag> |
|
|
|
</CompleteMultipartUploadResult>`, hex.EncodeToString(calMD5Digest(b))) |
|
|
|
} else { |
|
|
|
t.Errorf("TestObjectService_Upload Unknown Request") |
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
|
|
|
|
opt := &MultiUploadOptions{ |
|
|
|
ThreadPoolSize: 3, |
|
|
|
PartSize: 1, |
|
|
|
} |
|
|
|
_, _, err = client.Object.Upload(context.Background(), "test.go.upload", filePath, opt) |
|
|
|
if err != nil { |
|
|
|
t.Fatalf("Object.Upload returned error: %v", err) |
|
|
|
} |
|
|
|
} |