You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1172 lines
						
					
					
						
							31 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							1172 lines
						
					
					
						
							31 KiB
						
					
					
				
								package cos
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"bytes"
							 | 
						|
									"context"
							 | 
						|
									"crypto/rand"
							 | 
						|
									"encoding/hex"
							 | 
						|
									"encoding/json"
							 | 
						|
									"encoding/xml"
							 | 
						|
									"errors"
							 | 
						|
									"fmt"
							 | 
						|
									"hash/crc64"
							 | 
						|
									"io"
							 | 
						|
									"io/ioutil"
							 | 
						|
									math_rand "math/rand"
							 | 
						|
									"net"
							 | 
						|
									"net/http"
							 | 
						|
									"net/url"
							 | 
						|
									"os"
							 | 
						|
									"reflect"
							 | 
						|
									"strconv"
							 | 
						|
									"strings"
							 | 
						|
									"testing"
							 | 
						|
									"time"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								func TestObjectService_Get(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
									name := "test/hello.txt"
							 | 
						|
									contentLength := 1024 * 1024 * 10
							 | 
						|
									data := make([]byte, contentLength)
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, "GET")
							 | 
						|
										vs := values{
							 | 
						|
											"response-content-type": "text/html",
							 | 
						|
										}
							 | 
						|
										testFormValues(t, r, vs)
							 | 
						|
										strRange := r.Header.Get("Range")
							 | 
						|
										slice1 := strings.Split(strRange, "=")
							 | 
						|
										slice2 := strings.Split(slice1[1], "-")
							 | 
						|
										start, _ := strconv.ParseInt(slice2[0], 10, 64)
							 | 
						|
										end, _ := strconv.ParseInt(slice2[1], 10, 64)
							 | 
						|
										io.Copy(w, bytes.NewBuffer(data[start:end+1]))
							 | 
						|
									})
							 | 
						|
									for i := 0; i < 3; i++ {
							 | 
						|
										math_rand.Seed(time.Now().UnixNano())
							 | 
						|
										rangeStart := math_rand.Intn(contentLength)
							 | 
						|
										rangeEnd := rangeStart + math_rand.Intn(contentLength-rangeStart)
							 | 
						|
										if rangeEnd == rangeStart || rangeStart >= contentLength-1 {
							 | 
						|
											continue
							 | 
						|
										}
							 | 
						|
										opt := &ObjectGetOptions{
							 | 
						|
											ResponseContentType: "text/html",
							 | 
						|
											Range:               fmt.Sprintf("bytes=%v-%v", rangeStart, rangeEnd),
							 | 
						|
										}
							 | 
						|
										resp, err := client.Object.Get(context.Background(), name, opt)
							 | 
						|
										if err != nil {
							 | 
						|
											t.Fatalf("Object.Get returned error: %v", err)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										b, _ := ioutil.ReadAll(resp.Body)
							 | 
						|
										if bytes.Compare(b, data[rangeStart:rangeEnd+1]) != 0 {
							 | 
						|
											t.Errorf("Object.Get Failed")
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_GetToFile(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
									name := "test/hello.txt"
							 | 
						|
									data := make([]byte, 1024*1024*10)
							 | 
						|
									rand.Read(data)
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, "GET")
							 | 
						|
										vs := values{
							 | 
						|
											"response-content-type": "text/html",
							 | 
						|
										}
							 | 
						|
										testFormValues(t, r, vs)
							 | 
						|
										testHeader(t, r, "Range", "bytes=0-3")
							 | 
						|
										io.Copy(w, bytes.NewReader(data))
							 | 
						|
									})
							 | 
						|
									opt := &ObjectGetOptions{
							 | 
						|
										ResponseContentType: "text/html",
							 | 
						|
										Range:               "bytes=0-3",
							 | 
						|
									}
							 | 
						|
									filePath := "test.file" + time.Now().Format(time.RFC3339)
							 | 
						|
									_, err := client.Object.GetToFile(context.Background(), name, filePath, opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Get returned error: %v", err)
							 | 
						|
									}
							 | 
						|
									defer os.Remove(filePath)
							 | 
						|
									fd, err := os.Open(filePath)
							 | 
						|
									if err != nil {
							 | 
						|
									}
							 | 
						|
									defer fd.Close()
							 | 
						|
									bs, _ := ioutil.ReadAll(fd)
							 | 
						|
									if bytes.Compare(bs, data) != 0 {
							 | 
						|
										t.Errorf("Object.GetToFile data isn't consistent")
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_GetRetry(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
									u, _ := url.Parse(server.URL)
							 | 
						|
									client := NewClient(&BaseURL{u, u, u, u, u}, &http.Client{
							 | 
						|
										Transport: &http.Transport{
							 | 
						|
											Proxy: http.ProxyFromEnvironment,
							 | 
						|
											DialContext: (&net.Dialer{
							 | 
						|
												Timeout:   30 * time.Second,
							 | 
						|
												KeepAlive: 30 * time.Second,
							 | 
						|
												DualStack: true,
							 | 
						|
											}).DialContext,
							 | 
						|
											IdleConnTimeout:       90 * time.Second,
							 | 
						|
											TLSHandshakeTimeout:   10 * time.Second,
							 | 
						|
											ExpectContinueTimeout: 1 * time.Second,
							 | 
						|
											ResponseHeaderTimeout: 1 * time.Second,
							 | 
						|
										},
							 | 
						|
									})
							 | 
						|
									client.Conf.RetryOpt.StatusCode = []int{499}
							 | 
						|
									name := "test/hello.txt"
							 | 
						|
									contentLength := 1024 * 1024 * 10
							 | 
						|
									data := make([]byte, contentLength)
							 | 
						|
									index := 0
							 | 
						|
									mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, "GET")
							 | 
						|
										vs := values{
							 | 
						|
											"response-content-type": "text/html",
							 | 
						|
										}
							 | 
						|
										index++
							 | 
						|
										if index%3 != 0 {
							 | 
						|
											if index > 6 {
							 | 
						|
												w.WriteHeader(499)
							 | 
						|
												return
							 | 
						|
											}
							 | 
						|
											time.Sleep(time.Second * 2)
							 | 
						|
										}
							 | 
						|
										testFormValues(t, r, vs)
							 | 
						|
										strRange := r.Header.Get("Range")
							 | 
						|
										slice1 := strings.Split(strRange, "=")
							 | 
						|
										slice2 := strings.Split(slice1[1], "-")
							 | 
						|
										start, _ := strconv.ParseInt(slice2[0], 10, 64)
							 | 
						|
										end, _ := strconv.ParseInt(slice2[1], 10, 64)
							 | 
						|
										io.Copy(w, bytes.NewBuffer(data[start:end+1]))
							 | 
						|
									})
							 | 
						|
									for i := 0; i < 3; i++ {
							 | 
						|
										math_rand.Seed(time.Now().UnixNano())
							 | 
						|
										rangeStart := math_rand.Intn(contentLength)
							 | 
						|
										rangeEnd := rangeStart + math_rand.Intn(contentLength-rangeStart)
							 | 
						|
										if rangeEnd == rangeStart || rangeStart >= contentLength-1 {
							 | 
						|
											continue
							 | 
						|
										}
							 | 
						|
										opt := &ObjectGetOptions{
							 | 
						|
											ResponseContentType: "text/html",
							 | 
						|
											Range:               fmt.Sprintf("bytes=%v-%v", rangeStart, rangeEnd),
							 | 
						|
										}
							 | 
						|
										resp, err := client.Object.Get(context.Background(), name, opt)
							 | 
						|
										if err != nil {
							 | 
						|
											t.Fatalf("Object.Get returned error: %v", err)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										b, _ := ioutil.ReadAll(resp.Body)
							 | 
						|
										if bytes.Compare(b, data[rangeStart:rangeEnd+1]) != 0 {
							 | 
						|
											t.Errorf("Object.Get Failed")
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									if index != 9 {
							 | 
						|
										t.Errorf("retry time error, retry count: %v\n", index)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_GetPresignedURL(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									exceptSign := "q-sign-algorithm=sha1&q-ak=QmFzZTY0IGlzIGEgZ*******&q-sign-time=1622702557;1622706157&q-key-time=1622702557;1622706157&q-header-list=&q-url-param-list=&q-signature=0f359fe9d29e7fa0c738ce6c8feaf4ed1e84f287"
							 | 
						|
									exceptURL := &url.URL{
							 | 
						|
										Scheme:   "http",
							 | 
						|
										Host:     client.Host,
							 | 
						|
										Path:     "/test.jpg",
							 | 
						|
										RawQuery: exceptSign,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									c := context.Background()
							 | 
						|
									name := "test.jpg"
							 | 
						|
									ak := "QmFzZTY0IGlzIGEgZ*******"
							 | 
						|
									sk := "ZfbOA78asKUYBcXFrJD0a1I*******"
							 | 
						|
									startTime := time.Unix(int64(1622702557), 0)
							 | 
						|
									endTime := time.Unix(int64(1622706157), 0)
							 | 
						|
									opt := presignedURLTestingOptions{
							 | 
						|
										authTime: &AuthTime{
							 | 
						|
											SignStartTime: startTime,
							 | 
						|
											SignEndTime:   endTime,
							 | 
						|
											KeyStartTime:  startTime,
							 | 
						|
											KeyEndTime:    endTime,
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									presignedURL, err := client.Object.GetPresignedURL(c, http.MethodPut, name, ak, sk, time.Hour, opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatal(err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if reflect.DeepEqual(exceptURL, presignedURL) {
							 | 
						|
										t.Fatalf("Wrong PreSignedURL!")
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_Put(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									opt := &ObjectPutOptions{
							 | 
						|
										ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
							 | 
						|
											ContentType: "text/html",
							 | 
						|
										},
							 | 
						|
										ACLHeaderOptions: &ACLHeaderOptions{
							 | 
						|
											XCosACL: "private",
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
									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")
							 | 
						|
								
							 | 
						|
										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")
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									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("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",
							 | 
						|
											Listener:    &DefaultProgressListener{},
							 | 
						|
										},
							 | 
						|
										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) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
									name := "test/hello.txt"
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, http.MethodDelete)
							 | 
						|
										w.WriteHeader(http.StatusNoContent)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									_, err := client.Object.Delete(context.Background(), name)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Delete returned error: %v", err)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_Head(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
									name := "test/hello.txt"
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, "HEAD")
							 | 
						|
										testHeader(t, r, "If-Modified-Since", "Mon, 12 Jun 2017 05:36:19 GMT")
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									opt := &ObjectHeadOptions{
							 | 
						|
										IfModifiedSince: "Mon, 12 Jun 2017 05:36:19 GMT",
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									_, err := client.Object.Head(context.Background(), name, opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Head returned error: %v", err)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_Options(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
									name := "test/hello.txt"
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, http.MethodOptions)
							 | 
						|
										testHeader(t, r, "Access-Control-Request-Method", "PUT")
							 | 
						|
										testHeader(t, r, "Origin", "www.qq.com")
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									opt := &ObjectOptionsOptions{
							 | 
						|
										Origin:                     "www.qq.com",
							 | 
						|
										AccessControlRequestMethod: "PUT",
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									_, err := client.Object.Options(context.Background(), name, opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Options returned error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_PostRestore(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
									name := "test/hello.txt"
							 | 
						|
									wantBody := "<RestoreRequest><Days>3</Days><CASJobParameters><Tier>Expedited</Tier></CASJobParameters></RestoreRequest>"
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, http.MethodPost)
							 | 
						|
										testHeader(t, r, "Content-Type", "application/xml")
							 | 
						|
										testHeader(t, r, "Content-Length", "106")
							 | 
						|
										//b, _ := ioutil.ReadAll(r.Body)
							 | 
						|
										//fmt.Printf("%s", string(b))
							 | 
						|
										testBody(t, r, wantBody)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									opt := &ObjectRestoreOptions{
							 | 
						|
										Days: 3,
							 | 
						|
										Tier: &CASJobParameters{
							 | 
						|
											Tier: "Expedited",
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									_, err := client.Object.PostRestore(context.Background(), name, opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.PostRestore returned error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_Append_Simple(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									opt := &ObjectPutOptions{
							 | 
						|
										ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
							 | 
						|
											ContentType: "text/html",
							 | 
						|
										},
							 | 
						|
										ACLHeaderOptions: &ACLHeaderOptions{
							 | 
						|
											XCosACL: "private",
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
									name := "test/hello.txt"
							 | 
						|
									position := 0
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										vs := values{
							 | 
						|
											"append":   "",
							 | 
						|
											"position": "0",
							 | 
						|
										}
							 | 
						|
										testFormValues(t, r, vs)
							 | 
						|
								
							 | 
						|
										testMethod(t, r, http.MethodPost)
							 | 
						|
										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.Append request body: %#v, want %#v", v, want)
							 | 
						|
										}
							 | 
						|
										w.Header().Add("x-cos-content-sha1", hex.EncodeToString(calMD5Digest(b)))
							 | 
						|
										w.Header().Add("x-cos-next-append-position", strconv.FormatInt(int64(len(b)), 10))
							 | 
						|
								
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									r := bytes.NewReader([]byte("hello"))
							 | 
						|
									p, _, err := client.Object.Append(context.Background(), name, position, r, opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Append returned error: %v", err)
							 | 
						|
									}
							 | 
						|
									if p != len("hello") {
							 | 
						|
										t.Fatalf("Object.Append position error, want: %v, return: %v", len("hello"), p)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_DeleteMulti(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, http.MethodPost)
							 | 
						|
										vs := values{
							 | 
						|
											"delete": "",
							 | 
						|
										}
							 | 
						|
										testFormValues(t, r, vs)
							 | 
						|
										fmt.Fprint(w, `<DeleteResult>
							 | 
						|
									<Deleted>
							 | 
						|
										<Key>test1</Key>
							 | 
						|
									</Deleted>
							 | 
						|
									<Deleted>
							 | 
						|
										<Key>test3</Key>
							 | 
						|
									</Deleted>
							 | 
						|
									<Deleted>
							 | 
						|
										<Key>test2</Key>
							 | 
						|
									</Deleted>
							 | 
						|
								</DeleteResult>`)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									opt := &ObjectDeleteMultiOptions{
							 | 
						|
										Objects: []Object{
							 | 
						|
											{
							 | 
						|
												Key: "test1",
							 | 
						|
											},
							 | 
						|
											{
							 | 
						|
												Key: "test3",
							 | 
						|
											},
							 | 
						|
											{
							 | 
						|
												Key: "test2",
							 | 
						|
											},
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									ref, _, err := client.Object.DeleteMulti(context.Background(), opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.DeleteMulti returned error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									want := &ObjectDeleteMultiResult{
							 | 
						|
										XMLName: xml.Name{Local: "DeleteResult"},
							 | 
						|
										DeletedObjects: []Object{
							 | 
						|
											{
							 | 
						|
												Key: "test1",
							 | 
						|
											},
							 | 
						|
											{
							 | 
						|
												Key: "test3",
							 | 
						|
											},
							 | 
						|
											{
							 | 
						|
												Key: "test2",
							 | 
						|
											},
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if !reflect.DeepEqual(ref, want) {
							 | 
						|
										t.Errorf("Object.DeleteMulti returned %+v, want %+v", ref, want)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObiectService_Read_and_Close(t *testing.T) {
							 | 
						|
									data := make([]byte, 1024*10)
							 | 
						|
									rand.Read(data)
							 | 
						|
									body := bytes.NewReader(data)
							 | 
						|
									r, _ := http.NewRequest(http.MethodGet, "test", body)
							 | 
						|
								
							 | 
						|
									drc := DiscardReadCloser{
							 | 
						|
										RC:      r.Body,
							 | 
						|
										Discard: 10,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									res := make([]byte, 1024*10)
							 | 
						|
									readLen, err := drc.Read(res)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Read returned %v", err)
							 | 
						|
									}
							 | 
						|
									if readLen != 10230 {
							 | 
						|
										t.Fatalf("Object.Read returned %#v, excepted %#v", readLen, 10230)
							 | 
						|
									}
							 | 
						|
									if drc.Discard != 0 {
							 | 
						|
										t.Fatalf("Object.Read: drc.Discard = %v, excepted %v", drc.Discard, 0)
							 | 
						|
									}
							 | 
						|
									if !reflect.DeepEqual(res[:10230], data[10:]) {
							 | 
						|
										t.Fatalf("Object.Read: Wrong data!")
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									err = drc.Close()
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatal(err)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_Copy(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test.go.copy", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, http.MethodPut)
							 | 
						|
										fmt.Fprint(w, `<CopyObjectResult>
							 | 
						|
										<ETag>"098f6bcd4621d373cade4e832627b4f6"</ETag>
							 | 
						|
										<LastModified>2017-12-13T14:53:12</LastModified>
							 | 
						|
									</CopyObjectResult>`)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									wrongURL := "wrongURL"
							 | 
						|
									_, _, err := client.Object.Copy(context.Background(), "test.go.copy", wrongURL, nil)
							 | 
						|
									exceptedErr := errors.New(fmt.Sprintf("x-cos-copy-source format error: %s", wrongURL))
							 | 
						|
									if !reflect.DeepEqual(err, exceptedErr) {
							 | 
						|
										t.Fatalf("Object.Copy returned %#v, excepted %#v", err, exceptedErr)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									sourceURL := "test-1253846586.cos.ap-guangzhou.myqcloud.com/test.source"
							 | 
						|
									ref, _, err := client.Object.Copy(context.Background(), "test.go.copy", sourceURL, nil)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Copy returned error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									want := &ObjectCopyResult{
							 | 
						|
										XMLName:      xml.Name{Local: "CopyObjectResult"},
							 | 
						|
										ETag:         `"098f6bcd4621d373cade4e832627b4f6"`,
							 | 
						|
										LastModified: "2017-12-13T14:53:12",
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if !reflect.DeepEqual(ref, want) {
							 | 
						|
										t.Errorf("Object.Copy returned %+v, want %+v", ref, want)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_Append(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
									size := 1111 * 1111 * 63
							 | 
						|
									b := make([]byte, size)
							 | 
						|
									p := int(math_rand.Int31n(int32(size)))
							 | 
						|
									var buf bytes.Buffer
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test.append", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, "POST")
							 | 
						|
										bs, _ := ioutil.ReadAll(r.Body)
							 | 
						|
										buf.Write(bs)
							 | 
						|
										w.Header().Add("x-cos-content-sha1", hex.EncodeToString(calMD5Digest(bs)))
							 | 
						|
										w.Header().Add("x-cos-next-append-position", strconv.FormatInt(int64(buf.Len()), 10))
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									pos, _, err := client.Object.Append(context.Background(), "test.append", 0, bytes.NewReader(b[:p]), nil)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Append return error %v", err)
							 | 
						|
									}
							 | 
						|
									if pos != p {
							 | 
						|
										t.Fatalf("Object.Append pos error, returned:%v, wanted:%v", pos, p)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									opt := &ObjectPutOptions{
							 | 
						|
										ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
							 | 
						|
											ContentType: "text/html",
							 | 
						|
											Listener:    &DefaultProgressListener{},
							 | 
						|
										},
							 | 
						|
										ACLHeaderOptions: &ACLHeaderOptions{
							 | 
						|
											XCosACL: "private",
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									pos, _, err = client.Object.Append(context.Background(), "test.append", pos, bytes.NewReader(b[p:]), opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Append return error %v", err)
							 | 
						|
									}
							 | 
						|
									if pos != size {
							 | 
						|
										t.Fatalf("Object.Append pos error, returned:%v, wanted:%v", pos, size)
							 | 
						|
									}
							 | 
						|
									if bytes.Compare(b, buf.Bytes()) != 0 {
							 | 
						|
										t.Fatalf("Object.Append Compare failed")
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								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*33)
							 | 
						|
									_, err = rand.Read(b)
							 | 
						|
									newfile.Write(b)
							 | 
						|
									newfile.Close()
							 | 
						|
								
							 | 
						|
									// 已上传内容, 10个分块
							 | 
						|
									rb := make([][]byte, 33)
							 | 
						|
									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)
							 | 
						|
												for _, v := range rb {
							 | 
						|
													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)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_Upload2(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*3)
							 | 
						|
									_, err = rand.Read(b)
							 | 
						|
									newfile.Write(b)
							 | 
						|
									newfile.Close()
							 | 
						|
								
							 | 
						|
									tb := crc64.MakeTable(crc64.ECMA)
							 | 
						|
									realcrc := crc64.Update(0, tb, b)
							 | 
						|
									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")
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									mopt := &MultiUploadOptions{
							 | 
						|
										OptIni: &InitiateMultipartUploadOptions{
							 | 
						|
											ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
							 | 
						|
												ContentType: "text/html",
							 | 
						|
											},
							 | 
						|
											ACLHeaderOptions: &ACLHeaderOptions{
							 | 
						|
												XCosACL: "private",
							 | 
						|
											},
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
									for retry <= final {
							 | 
						|
										_, _, err := client.Object.Upload(context.Background(), name, filePath, mopt)
							 | 
						|
										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_Download(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									filePath := "rsp.file" + time.Now().Format(time.RFC3339)
							 | 
						|
									newfile, err := os.Create(filePath)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("create tmp file failed")
							 | 
						|
									}
							 | 
						|
									defer os.Remove(filePath)
							 | 
						|
									// 源文件内容
							 | 
						|
									totalBytes := int64(1024*1024*9 + 1230)
							 | 
						|
									b := make([]byte, totalBytes)
							 | 
						|
									_, err = rand.Read(b)
							 | 
						|
									newfile.Write(b)
							 | 
						|
									newfile.Close()
							 | 
						|
									tb := crc64.MakeTable(crc64.ECMA)
							 | 
						|
									localcrc := strconv.FormatUint(crc64.Update(0, tb, b), 10)
							 | 
						|
								
							 | 
						|
									retryMap := make(map[int64]int)
							 | 
						|
									mux.HandleFunc("/test.go.download", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										if r.Method == http.MethodHead {
							 | 
						|
											w.Header().Add("Content-Length", strconv.FormatInt(totalBytes, 10))
							 | 
						|
											w.Header().Add("x-cos-hash-crc64ecma", localcrc)
							 | 
						|
											return
							 | 
						|
										}
							 | 
						|
										strRange := r.Header.Get("Range")
							 | 
						|
										slice1 := strings.Split(strRange, "=")
							 | 
						|
										slice2 := strings.Split(slice1[1], "-")
							 | 
						|
										start, _ := strconv.ParseInt(slice2[0], 10, 64)
							 | 
						|
										end, _ := strconv.ParseInt(slice2[1], 10, 64)
							 | 
						|
										if retryMap[start] == 0 {
							 | 
						|
											// SDK 内部重试
							 | 
						|
											retryMap[start]++
							 | 
						|
											w.WriteHeader(http.StatusGatewayTimeout)
							 | 
						|
										} else if retryMap[start] == 1 {
							 | 
						|
											// SDK Download 做重试
							 | 
						|
											retryMap[start]++
							 | 
						|
											io.Copy(w, bytes.NewBuffer(b[start:end]))
							 | 
						|
										} else if retryMap[start] == 2 {
							 | 
						|
											// SDK Download 做重试
							 | 
						|
											retryMap[start]++
							 | 
						|
											st := start
							 | 
						|
											et := st + math_rand.Int63n(1024)
							 | 
						|
											io.Copy(w, bytes.NewBuffer(b[st:et+1]))
							 | 
						|
										} else {
							 | 
						|
											// SDK Download 成功
							 | 
						|
											io.Copy(w, bytes.NewBuffer(b[start:end+1]))
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									opt := &MultiDownloadOptions{
							 | 
						|
										ThreadPoolSize: 3,
							 | 
						|
										PartSize:       1,
							 | 
						|
									}
							 | 
						|
									downPath := "down.file" + time.Now().Format(time.RFC3339)
							 | 
						|
									defer os.Remove(downPath)
							 | 
						|
									_, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Upload returned error: %v", err)
							 | 
						|
									}
							 | 
						|
									_, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Upload returned error: %v", err)
							 | 
						|
									}
							 | 
						|
									_, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.Upload returned error: %v", err)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_DownloadWithCheckPoint(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									filePath := "rsp.file" + time.Now().Format(time.RFC3339)
							 | 
						|
									newfile, err := os.Create(filePath)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("create tmp file failed")
							 | 
						|
									}
							 | 
						|
									defer os.Remove(filePath)
							 | 
						|
									// 源文件内容
							 | 
						|
									totalBytes := int64(1024*1024*9 + 123)
							 | 
						|
									partSize := 1024 * 1024
							 | 
						|
									b := make([]byte, totalBytes)
							 | 
						|
									_, err = rand.Read(b)
							 | 
						|
									newfile.Write(b)
							 | 
						|
									newfile.Close()
							 | 
						|
									tb := crc64.MakeTable(crc64.ECMA)
							 | 
						|
									localcrc := strconv.FormatUint(crc64.Update(0, tb, b), 10)
							 | 
						|
								
							 | 
						|
									oddok := false
							 | 
						|
									var oddcount, evencount int
							 | 
						|
									mux.HandleFunc("/test.go.download", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										if r.Method == http.MethodHead {
							 | 
						|
											w.Header().Add("Content-Length", strconv.FormatInt(totalBytes, 10))
							 | 
						|
											w.Header().Add("x-cos-hash-crc64ecma", localcrc)
							 | 
						|
											return
							 | 
						|
										}
							 | 
						|
										strRange := r.Header.Get("Range")
							 | 
						|
										slice1 := strings.Split(strRange, "=")
							 | 
						|
										slice2 := strings.Split(slice1[1], "-")
							 | 
						|
										start, _ := strconv.ParseInt(slice2[0], 10, 64)
							 | 
						|
										end, _ := strconv.ParseInt(slice2[1], 10, 64)
							 | 
						|
										if (start/int64(partSize))%2 == 1 {
							 | 
						|
											if oddok {
							 | 
						|
												io.Copy(w, bytes.NewBuffer(b[start:end+1]))
							 | 
						|
											} else {
							 | 
						|
												// 数据校验失败, Download做3次重试
							 | 
						|
												io.Copy(w, bytes.NewBuffer(b[start:end]))
							 | 
						|
											}
							 | 
						|
											oddcount++
							 | 
						|
										} else {
							 | 
						|
											io.Copy(w, bytes.NewBuffer(b[start:end+1]))
							 | 
						|
											evencount++
							 | 
						|
										}
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									opt := &MultiDownloadOptions{
							 | 
						|
										ThreadPoolSize: 3,
							 | 
						|
										PartSize:       1,
							 | 
						|
										CheckPoint:     true,
							 | 
						|
									}
							 | 
						|
									downPath := "down.file" + time.Now().Format(time.RFC3339)
							 | 
						|
									defer os.Remove(downPath)
							 | 
						|
									_, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
							 | 
						|
									if err == nil {
							 | 
						|
										// 偶数块下载完成,奇数块下载失败
							 | 
						|
										t.Fatalf("Object.Download returned error: %v", err)
							 | 
						|
									}
							 | 
						|
									fd, err := os.Open(downPath)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object Download Open File Failed:%v", err)
							 | 
						|
									}
							 | 
						|
									offset := 0
							 | 
						|
									for i := 0; i < 10; i++ {
							 | 
						|
										bs, _ := ioutil.ReadAll(io.LimitReader(fd, int64(partSize)))
							 | 
						|
										offset += len(bs)
							 | 
						|
										if i%2 == 1 {
							 | 
						|
											bs[len(bs)-1] = b[offset-1]
							 | 
						|
										}
							 | 
						|
										if bytes.Compare(bs, b[i*partSize:offset]) != 0 {
							 | 
						|
											t.Fatalf("Compare Error, index:%v, len:%v, offset:%v", i, len(bs), offset)
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									fd.Close()
							 | 
						|
								
							 | 
						|
									if oddcount != 15 || evencount != 5 {
							 | 
						|
										t.Fatalf("Object.Download failed, odd:%v, even:%v", oddcount, evencount)
							 | 
						|
									}
							 | 
						|
									// 设置奇数块OK
							 | 
						|
									oddok = true
							 | 
						|
									_, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
							 | 
						|
									if err != nil {
							 | 
						|
										// 下载成功
							 | 
						|
										t.Fatalf("Object.Download returned error: %v", err)
							 | 
						|
									}
							 | 
						|
									if oddcount != 20 || evencount != 5 {
							 | 
						|
										t.Fatalf("Object.Download failed, odd:%v, even:%v", oddcount, evencount)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_GetTagging(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, "GET")
							 | 
						|
										vs := values{
							 | 
						|
											"tagging": "",
							 | 
						|
										}
							 | 
						|
										testFormValues(t, r, vs)
							 | 
						|
										fmt.Fprint(w, `<Tagging>
							 | 
						|
									<TagSet>
							 | 
						|
										<Tag>
							 | 
						|
											<Key>test_k2</Key>
							 | 
						|
											<Value>test_v2</Value>
							 | 
						|
										</Tag>
							 | 
						|
										<Tag>
							 | 
						|
											<Key>test_k3</Key>
							 | 
						|
											<Value>test_vv</Value>
							 | 
						|
										</Tag>
							 | 
						|
									</TagSet>
							 | 
						|
								</Tagging>`)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									res, _, err := client.Object.GetTagging(context.Background(), "test")
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.GetTagging returned error %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									want := &ObjectGetTaggingResult{
							 | 
						|
										XMLName: xml.Name{Local: "Tagging"},
							 | 
						|
										TagSet: []ObjectTaggingTag{
							 | 
						|
											{"test_k2", "test_v2"},
							 | 
						|
											{"test_k3", "test_vv"},
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if !reflect.DeepEqual(res, want) {
							 | 
						|
										t.Errorf("Object.GetTagging returned %+v, want %+v", res, want)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_PutTagging(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									opt := &ObjectPutTaggingOptions{
							 | 
						|
										TagSet: []ObjectTaggingTag{
							 | 
						|
											{
							 | 
						|
												Key:   "test_k2",
							 | 
						|
												Value: "test_v2",
							 | 
						|
											},
							 | 
						|
											{
							 | 
						|
												Key:   "test_k3",
							 | 
						|
												Value: "test_v3",
							 | 
						|
											},
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										v := new(ObjectPutTaggingOptions)
							 | 
						|
										xml.NewDecoder(r.Body).Decode(v)
							 | 
						|
								
							 | 
						|
										testMethod(t, r, "PUT")
							 | 
						|
										vs := values{
							 | 
						|
											"tagging": "",
							 | 
						|
										}
							 | 
						|
										testFormValues(t, r, vs)
							 | 
						|
								
							 | 
						|
										want := opt
							 | 
						|
										want.XMLName = xml.Name{Local: "Tagging"}
							 | 
						|
										if !reflect.DeepEqual(v, want) {
							 | 
						|
											t.Errorf("Object.PutTagging request body: %+v, want %+v", v, want)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									_, err := client.Object.PutTagging(context.Background(), "test", opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.PutTagging returned error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_DeleteTagging(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, http.MethodDelete)
							 | 
						|
										vs := values{
							 | 
						|
											"tagging": "",
							 | 
						|
										}
							 | 
						|
										testFormValues(t, r, vs)
							 | 
						|
								
							 | 
						|
										w.WriteHeader(http.StatusNoContent)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									_, err := client.Object.DeleteTagging(context.Background(), "test")
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.DeleteTagging returned error: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_PutFetchTask(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									opt := &PutFetchTaskOptions{
							 | 
						|
										Url:                "http://examplebucket-1250000000.cos.ap-guangzhou.myqcloud.com/exampleobject",
							 | 
						|
										Key:                "exampleobject",
							 | 
						|
										MD5:                "MD5",
							 | 
						|
										OnKeyExist:         "OnKeyExist",
							 | 
						|
										IgnoreSameKey:      true,
							 | 
						|
										SuccessCallbackUrl: "SuccessCallbackUrl",
							 | 
						|
										FailureCallbackUrl: "FailureCallbackUrl",
							 | 
						|
										XOptionHeader:      &http.Header{},
							 | 
						|
									}
							 | 
						|
									opt.XOptionHeader.Add("Content-Type", "application/json")
							 | 
						|
									opt.XOptionHeader.Add("Content-Type", "application/xml")
							 | 
						|
									opt.XOptionHeader.Add("Cache-Control", "max-age=10")
							 | 
						|
									opt.XOptionHeader.Add("Cache-Control", "max-stale=10")
							 | 
						|
									res := &PutFetchTaskResult{
							 | 
						|
										Code:      0,
							 | 
						|
										Message:   "SUCCESS",
							 | 
						|
										RequestId: "NjE0ZGMxMDhfMmZjMjNiMGFfNWY2N18yOTRjYWE=",
							 | 
						|
										Data: struct {
							 | 
						|
											TaskId string `json:"taskId,omitempty"`
							 | 
						|
										}{
							 | 
						|
											TaskId: "NjE0ZGMxMDhfMmZjMjNiMGFfNWY2N18yOTRjYWE=",
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
									mux.HandleFunc("/examplebucket-1250000000/", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, http.MethodPost)
							 | 
						|
										opt.XOptionHeader.Set("Content-Type", "application/json")
							 | 
						|
										for k, v := range *opt.XOptionHeader {
							 | 
						|
											if k != "Content-Type" {
							 | 
						|
												if !reflect.DeepEqual(r.Header[k], v) {
							 | 
						|
													t.Errorf("Object.PutFetchTask request header: %+v, want %+v", r.Header[k], v)
							 | 
						|
												}
							 | 
						|
												continue
							 | 
						|
											}
							 | 
						|
											if r.Header.Get(k) != "application/json" || len(r.Header[k]) != 1 {
							 | 
						|
												t.Errorf("Object.PutFetchTask request header: %+v, want %+v", r.Header[k], v)
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
										v := new(PutFetchTaskOptions)
							 | 
						|
										json.NewDecoder(r.Body).Decode(v)
							 | 
						|
										want := opt
							 | 
						|
										v.XOptionHeader = opt.XOptionHeader
							 | 
						|
										if !reflect.DeepEqual(v, want) {
							 | 
						|
											t.Errorf("Object.PutFetchTask request body: %+v, want %+v", v, want)
							 | 
						|
										}
							 | 
						|
										fmt.Fprint(w, `{
							 | 
						|
								            "code":0,
							 | 
						|
								            "message":"SUCCESS",
							 | 
						|
								            "request_id":"NjE0ZGMxMDhfMmZjMjNiMGFfNWY2N18yOTRjYWE=",
							 | 
						|
								            "data":{"taskid":"NjE0ZGMxMDhfMmZjMjNiMGFfNWY2N18yOTRjYWE="}
							 | 
						|
								        }`)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									r, _, err := client.Object.PutFetchTask(context.Background(), "examplebucket-1250000000", opt)
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.PutFetchTask returned error: %v", err)
							 | 
						|
									}
							 | 
						|
									if !reflect.DeepEqual(r, res) {
							 | 
						|
										t.Errorf("object.PutFetchTask res: %+v, want: %+v", r, res)
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func TestObjectService_GetFetchTask(t *testing.T) {
							 | 
						|
									setup()
							 | 
						|
									defer teardown()
							 | 
						|
								
							 | 
						|
									res := &GetFetchTaskResult{
							 | 
						|
										Code:      0,
							 | 
						|
										Message:   "SUCCESS",
							 | 
						|
										RequestId: "NjE0ZGNiMDVfMmZjMjNiMGFfNWY2N18yOTRjYWM=",
							 | 
						|
										Data: struct {
							 | 
						|
											Code    string `json:"code,omitempty"`
							 | 
						|
											Message string `json:"msg,omitempty"`
							 | 
						|
											Percent int    `json:"percent,omitempty"`
							 | 
						|
											Status  string `json:"status,omitempty"`
							 | 
						|
										}{
							 | 
						|
											Code:    "Forbidden",
							 | 
						|
											Message: "The specified download can not be allowed.",
							 | 
						|
											Percent: 0,
							 | 
						|
											Status:  "TASK_FAILED",
							 | 
						|
										},
							 | 
						|
									}
							 | 
						|
									mux.HandleFunc("/examplebucket-1250000000/NjE0ZGMxMDhfMmZjMjNiMGFfNWY2N18yOTRjYWE=", func(w http.ResponseWriter, r *http.Request) {
							 | 
						|
										testMethod(t, r, http.MethodGet)
							 | 
						|
										fmt.Fprint(w, `{
							 | 
						|
								            "code":0,
							 | 
						|
								            "message":"SUCCESS",
							 | 
						|
								            "request_id":"NjE0ZGNiMDVfMmZjMjNiMGFfNWY2N18yOTRjYWM=",
							 | 
						|
								            "data": {
							 | 
						|
								                "code":"Forbidden",
							 | 
						|
								                "msg":"The specified download can not be allowed.",
							 | 
						|
								                "percent":0,
							 | 
						|
								                "status":"TASK_FAILED"
							 | 
						|
								            }
							 | 
						|
								        }`)
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									r, _, err := client.Object.GetFetchTask(context.Background(), "examplebucket-1250000000", "NjE0ZGMxMDhfMmZjMjNiMGFfNWY2N18yOTRjYWE=")
							 | 
						|
									if err != nil {
							 | 
						|
										t.Fatalf("Object.GetFetchTask returned error: %v", err)
							 | 
						|
									}
							 | 
						|
									if !reflect.DeepEqual(r, res) {
							 | 
						|
										t.Errorf("object.GetFetchTask res: %+v, want: %+v", r, res)
							 | 
						|
									}
							 | 
						|
								}
							 |