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.

939 lines
24 KiB

4 years ago
6 years ago
4 years ago
  1. package cos
  2. import (
  3. "bytes"
  4. "context"
  5. "crypto/rand"
  6. "encoding/hex"
  7. "encoding/xml"
  8. "errors"
  9. "fmt"
  10. "hash/crc64"
  11. "io"
  12. "io/ioutil"
  13. math_rand "math/rand"
  14. "net/http"
  15. "net/url"
  16. "os"
  17. "reflect"
  18. "strconv"
  19. "strings"
  20. "testing"
  21. "time"
  22. )
  23. func TestObjectService_Get(t *testing.T) {
  24. setup()
  25. defer teardown()
  26. name := "test/hello.txt"
  27. contentLength := 1024 * 1024 * 10
  28. data := make([]byte, contentLength)
  29. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  30. testMethod(t, r, "GET")
  31. vs := values{
  32. "response-content-type": "text/html",
  33. }
  34. testFormValues(t, r, vs)
  35. strRange := r.Header.Get("Range")
  36. slice1 := strings.Split(strRange, "=")
  37. slice2 := strings.Split(slice1[1], "-")
  38. start, _ := strconv.ParseInt(slice2[0], 10, 64)
  39. end, _ := strconv.ParseInt(slice2[1], 10, 64)
  40. io.Copy(w, bytes.NewBuffer(data[start:end+1]))
  41. })
  42. for i := 0; i < 3; i++ {
  43. math_rand.Seed(time.Now().UnixNano())
  44. rangeStart := math_rand.Intn(contentLength)
  45. rangeEnd := rangeStart + math_rand.Intn(contentLength-rangeStart)
  46. if rangeEnd == rangeStart || rangeStart >= contentLength-1 {
  47. continue
  48. }
  49. opt := &ObjectGetOptions{
  50. ResponseContentType: "text/html",
  51. Range: fmt.Sprintf("bytes=%v-%v", rangeStart, rangeEnd),
  52. }
  53. resp, err := client.Object.Get(context.Background(), name, opt)
  54. if err != nil {
  55. t.Fatalf("Object.Get returned error: %v", err)
  56. }
  57. b, _ := ioutil.ReadAll(resp.Body)
  58. if bytes.Compare(b, data[rangeStart:rangeEnd+1]) != 0 {
  59. t.Errorf("Object.Get Failed")
  60. }
  61. }
  62. }
  63. func TestObjectService_GetToFile(t *testing.T) {
  64. setup()
  65. defer teardown()
  66. name := "test/hello.txt"
  67. data := make([]byte, 1024*1024*10)
  68. rand.Read(data)
  69. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  70. testMethod(t, r, "GET")
  71. vs := values{
  72. "response-content-type": "text/html",
  73. }
  74. testFormValues(t, r, vs)
  75. testHeader(t, r, "Range", "bytes=0-3")
  76. io.Copy(w, bytes.NewReader(data))
  77. })
  78. opt := &ObjectGetOptions{
  79. ResponseContentType: "text/html",
  80. Range: "bytes=0-3",
  81. }
  82. filePath := "test.file" + time.Now().Format(time.RFC3339)
  83. _, err := client.Object.GetToFile(context.Background(), name, filePath, opt)
  84. if err != nil {
  85. t.Fatalf("Object.Get returned error: %v", err)
  86. }
  87. defer os.Remove(filePath)
  88. fd, err := os.Open(filePath)
  89. if err != nil {
  90. }
  91. defer fd.Close()
  92. bs, _ := ioutil.ReadAll(fd)
  93. if bytes.Compare(bs, data) != 0 {
  94. t.Errorf("Object.GetToFile data isn't consistent")
  95. }
  96. }
  97. func TestObjectService_GetPresignedURL(t *testing.T) {
  98. setup()
  99. defer teardown()
  100. 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"
  101. exceptURL := &url.URL{
  102. Scheme: "http",
  103. Host: client.Host,
  104. Path: "/test.jpg",
  105. RawQuery: exceptSign,
  106. }
  107. c := context.Background()
  108. name := "test.jpg"
  109. ak := "QmFzZTY0IGlzIGEgZ*******"
  110. sk := "ZfbOA78asKUYBcXFrJD0a1I*******"
  111. startTime := time.Unix(int64(1622702557), 0)
  112. endTime := time.Unix(int64(1622706157), 0)
  113. opt := presignedURLTestingOptions{
  114. authTime: &AuthTime{
  115. SignStartTime: startTime,
  116. SignEndTime: endTime,
  117. KeyStartTime: startTime,
  118. KeyEndTime: endTime,
  119. },
  120. }
  121. presignedURL, err := client.Object.GetPresignedURL(c, http.MethodPut, name, ak, sk, time.Hour, opt)
  122. if err != nil {
  123. t.Fatal(err)
  124. }
  125. if reflect.DeepEqual(exceptURL, presignedURL) {
  126. t.Fatalf("Wrong PreSignedURL!")
  127. }
  128. }
  129. func TestObjectService_Put(t *testing.T) {
  130. setup()
  131. defer teardown()
  132. opt := &ObjectPutOptions{
  133. ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  134. ContentType: "text/html",
  135. },
  136. ACLHeaderOptions: &ACLHeaderOptions{
  137. XCosACL: "private",
  138. },
  139. }
  140. name := "test/hello.txt"
  141. retry := 0
  142. final := 10
  143. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  144. testMethod(t, r, http.MethodPut)
  145. testHeader(t, r, "x-cos-acl", "private")
  146. testHeader(t, r, "Content-Type", "text/html")
  147. if retry%2 == 0 {
  148. b, _ := ioutil.ReadAll(r.Body)
  149. tb := crc64.MakeTable(crc64.ECMA)
  150. crc := crc64.Update(0, tb, b)
  151. v := string(b)
  152. want := "hello"
  153. if !reflect.DeepEqual(v, want) {
  154. t.Errorf("Object.Put request body: %#v, want %#v", v, want)
  155. }
  156. realcrc := crc64.Update(0, tb, []byte("hello"))
  157. if !reflect.DeepEqual(crc, realcrc) {
  158. t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc)
  159. }
  160. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  161. if retry != final {
  162. w.WriteHeader(http.StatusGatewayTimeout)
  163. }
  164. } else {
  165. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  166. }
  167. })
  168. for retry <= final {
  169. r := bytes.NewReader([]byte("hello"))
  170. _, err := client.Object.Put(context.Background(), name, r, opt)
  171. if retry < final && err == nil {
  172. t.Fatalf("Error must not nil when retry < final")
  173. }
  174. if retry == final && err != nil {
  175. t.Fatalf("Put Error: %v", err)
  176. }
  177. retry++
  178. }
  179. }
  180. func TestObjectService_PutFromFile(t *testing.T) {
  181. setup()
  182. defer teardown()
  183. filePath := "tmpfile" + time.Now().Format(time.RFC3339)
  184. newfile, err := os.Create(filePath)
  185. if err != nil {
  186. t.Fatalf("create tmp file failed")
  187. }
  188. defer os.Remove(filePath)
  189. // 源文件内容
  190. b := make([]byte, 1024*1024*3)
  191. _, err = rand.Read(b)
  192. newfile.Write(b)
  193. newfile.Close()
  194. tb := crc64.MakeTable(crc64.ECMA)
  195. realcrc := crc64.Update(0, tb, b)
  196. opt := &ObjectPutOptions{
  197. ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  198. ContentType: "text/html",
  199. Listener: &DefaultProgressListener{},
  200. },
  201. ACLHeaderOptions: &ACLHeaderOptions{
  202. XCosACL: "private",
  203. },
  204. }
  205. name := "test/hello.txt"
  206. retry := 0
  207. final := 4
  208. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  209. testMethod(t, r, http.MethodPut)
  210. testHeader(t, r, "x-cos-acl", "private")
  211. testHeader(t, r, "Content-Type", "text/html")
  212. if retry%2 == 0 {
  213. bs, _ := ioutil.ReadAll(r.Body)
  214. crc := crc64.Update(0, tb, bs)
  215. if !reflect.DeepEqual(bs, b) {
  216. t.Errorf("Object.Put request body Error")
  217. }
  218. if !reflect.DeepEqual(crc, realcrc) {
  219. t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc)
  220. }
  221. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  222. if retry != final {
  223. w.WriteHeader(http.StatusGatewayTimeout)
  224. }
  225. } else {
  226. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  227. }
  228. })
  229. for retry <= final {
  230. _, err := client.Object.PutFromFile(context.Background(), name, filePath, opt)
  231. if retry < final && err == nil {
  232. t.Fatalf("Error must not nil when retry < final")
  233. }
  234. if retry == final && err != nil {
  235. t.Fatalf("Put Error: %v", err)
  236. }
  237. retry++
  238. }
  239. }
  240. func TestObjectService_Delete(t *testing.T) {
  241. setup()
  242. defer teardown()
  243. name := "test/hello.txt"
  244. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  245. testMethod(t, r, http.MethodDelete)
  246. w.WriteHeader(http.StatusNoContent)
  247. })
  248. _, err := client.Object.Delete(context.Background(), name)
  249. if err != nil {
  250. t.Fatalf("Object.Delete returned error: %v", err)
  251. }
  252. }
  253. func TestObjectService_Head(t *testing.T) {
  254. setup()
  255. defer teardown()
  256. name := "test/hello.txt"
  257. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  258. testMethod(t, r, "HEAD")
  259. testHeader(t, r, "If-Modified-Since", "Mon, 12 Jun 2017 05:36:19 GMT")
  260. })
  261. opt := &ObjectHeadOptions{
  262. IfModifiedSince: "Mon, 12 Jun 2017 05:36:19 GMT",
  263. }
  264. _, err := client.Object.Head(context.Background(), name, opt)
  265. if err != nil {
  266. t.Fatalf("Object.Head returned error: %v", err)
  267. }
  268. }
  269. func TestObjectService_Options(t *testing.T) {
  270. setup()
  271. defer teardown()
  272. name := "test/hello.txt"
  273. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  274. testMethod(t, r, http.MethodOptions)
  275. testHeader(t, r, "Access-Control-Request-Method", "PUT")
  276. testHeader(t, r, "Origin", "www.qq.com")
  277. })
  278. opt := &ObjectOptionsOptions{
  279. Origin: "www.qq.com",
  280. AccessControlRequestMethod: "PUT",
  281. }
  282. _, err := client.Object.Options(context.Background(), name, opt)
  283. if err != nil {
  284. t.Fatalf("Object.Options returned error: %v", err)
  285. }
  286. }
  287. func TestObjectService_PostRestore(t *testing.T) {
  288. setup()
  289. defer teardown()
  290. name := "test/hello.txt"
  291. wantBody := "<RestoreRequest><Days>3</Days><CASJobParameters><Tier>Expedited</Tier></CASJobParameters></RestoreRequest>"
  292. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  293. testMethod(t, r, http.MethodPost)
  294. testHeader(t, r, "Content-Type", "application/xml")
  295. testHeader(t, r, "Content-Length", "106")
  296. //b, _ := ioutil.ReadAll(r.Body)
  297. //fmt.Printf("%s", string(b))
  298. testBody(t, r, wantBody)
  299. })
  300. opt := &ObjectRestoreOptions{
  301. Days: 3,
  302. Tier: &CASJobParameters{
  303. Tier: "Expedited",
  304. },
  305. }
  306. _, err := client.Object.PostRestore(context.Background(), name, opt)
  307. if err != nil {
  308. t.Fatalf("Object.PostRestore returned error: %v", err)
  309. }
  310. }
  311. // func TestObjectService_Append(t *testing.T) {
  312. // setup()
  313. // defer teardown()
  314. // opt := &ObjectPutOptions{
  315. // ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  316. // ContentType: "text/html",
  317. // },
  318. // ACLHeaderOptions: &ACLHeaderOptions{
  319. // XCosACL: "private",
  320. // },
  321. // }
  322. // name := "test/hello.txt"
  323. // position := 0
  324. // mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  325. // vs := values{
  326. // "append": "",
  327. // "position": "0",
  328. // }
  329. // testFormValues(t, r, vs)
  330. // testMethod(t, r, http.MethodPost)
  331. // testHeader(t, r, "x-cos-acl", "private")
  332. // testHeader(t, r, "Content-Type", "text/html")
  333. // b, _ := ioutil.ReadAll(r.Body)
  334. // v := string(b)
  335. // want := "hello"
  336. // if !reflect.DeepEqual(v, want) {
  337. // t.Errorf("Object.Append request body: %#v, want %#v", v, want)
  338. // }
  339. // })
  340. // r := bytes.NewReader([]byte("hello"))
  341. // _, err := client.Object.Append(context.Background(), name, position, r, opt)
  342. // if err != nil {
  343. // t.Fatalf("Object.Append returned error: %v", err)
  344. // }
  345. // }
  346. func TestObjectService_DeleteMulti(t *testing.T) {
  347. setup()
  348. defer teardown()
  349. mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  350. testMethod(t, r, http.MethodPost)
  351. vs := values{
  352. "delete": "",
  353. }
  354. testFormValues(t, r, vs)
  355. fmt.Fprint(w, `<DeleteResult>
  356. <Deleted>
  357. <Key>test1</Key>
  358. </Deleted>
  359. <Deleted>
  360. <Key>test3</Key>
  361. </Deleted>
  362. <Deleted>
  363. <Key>test2</Key>
  364. </Deleted>
  365. </DeleteResult>`)
  366. })
  367. opt := &ObjectDeleteMultiOptions{
  368. Objects: []Object{
  369. {
  370. Key: "test1",
  371. },
  372. {
  373. Key: "test3",
  374. },
  375. {
  376. Key: "test2",
  377. },
  378. },
  379. }
  380. ref, _, err := client.Object.DeleteMulti(context.Background(), opt)
  381. if err != nil {
  382. t.Fatalf("Object.DeleteMulti returned error: %v", err)
  383. }
  384. want := &ObjectDeleteMultiResult{
  385. XMLName: xml.Name{Local: "DeleteResult"},
  386. DeletedObjects: []Object{
  387. {
  388. Key: "test1",
  389. },
  390. {
  391. Key: "test3",
  392. },
  393. {
  394. Key: "test2",
  395. },
  396. },
  397. }
  398. if !reflect.DeepEqual(ref, want) {
  399. t.Errorf("Object.DeleteMulti returned %+v, want %+v", ref, want)
  400. }
  401. }
  402. func TestObiectService_Read_and_Close(t *testing.T) {
  403. data := make([]byte, 1024*10)
  404. rand.Read(data)
  405. body := bytes.NewReader(data)
  406. r, _ := http.NewRequest(http.MethodGet, "test", body)
  407. drc := DiscardReadCloser{
  408. RC: r.Body,
  409. Discard: 10,
  410. }
  411. res := make([]byte, 1024*10)
  412. readLen, err := drc.Read(res)
  413. if err != nil {
  414. t.Fatalf("Object.Read returned %v", err)
  415. }
  416. if readLen != 10230 {
  417. t.Fatalf("Object.Read returned %#v, excepted %#v", readLen, 10230)
  418. }
  419. if drc.Discard != 0 {
  420. t.Fatalf("Object.Read: drc.Discard = %v, excepted %v", drc.Discard, 0)
  421. }
  422. if !reflect.DeepEqual(res[:10230], data[10:]) {
  423. t.Fatalf("Object.Read: Wrong data!")
  424. }
  425. err = drc.Close()
  426. if err != nil {
  427. t.Fatal(err)
  428. }
  429. }
  430. func TestObjectService_Copy(t *testing.T) {
  431. setup()
  432. defer teardown()
  433. mux.HandleFunc("/test.go.copy", func(w http.ResponseWriter, r *http.Request) {
  434. testMethod(t, r, http.MethodPut)
  435. fmt.Fprint(w, `<CopyObjectResult>
  436. <ETag>"098f6bcd4621d373cade4e832627b4f6"</ETag>
  437. <LastModified>2017-12-13T14:53:12</LastModified>
  438. </CopyObjectResult>`)
  439. })
  440. wrongURL := "wrongURL"
  441. _, _, err := client.Object.Copy(context.Background(), "test.go.copy", wrongURL, nil)
  442. exceptedErr := errors.New(fmt.Sprintf("x-cos-copy-source format error: %s", wrongURL))
  443. if !reflect.DeepEqual(err, exceptedErr) {
  444. t.Fatalf("Object.Copy returned %#v, excepted %#v", err, exceptedErr)
  445. }
  446. sourceURL := "test-1253846586.cos.ap-guangzhou.myqcloud.com/test.source"
  447. ref, _, err := client.Object.Copy(context.Background(), "test.go.copy", sourceURL, nil)
  448. if err != nil {
  449. t.Fatalf("Object.Copy returned error: %v", err)
  450. }
  451. want := &ObjectCopyResult{
  452. XMLName: xml.Name{Local: "CopyObjectResult"},
  453. ETag: `"098f6bcd4621d373cade4e832627b4f6"`,
  454. LastModified: "2017-12-13T14:53:12",
  455. }
  456. if !reflect.DeepEqual(ref, want) {
  457. t.Errorf("Object.Copy returned %+v, want %+v", ref, want)
  458. }
  459. }
  460. func TestObjectService_Upload(t *testing.T) {
  461. setup()
  462. defer teardown()
  463. filePath := "tmpfile" + time.Now().Format(time.RFC3339)
  464. newfile, err := os.Create(filePath)
  465. if err != nil {
  466. t.Fatalf("create tmp file failed")
  467. }
  468. defer os.Remove(filePath)
  469. // 源文件内容
  470. b := make([]byte, 1024*1024*33)
  471. _, err = rand.Read(b)
  472. newfile.Write(b)
  473. newfile.Close()
  474. // 已上传内容, 10个分块
  475. rb := make([][]byte, 33)
  476. uploadid := "test-cos-multiupload-uploadid"
  477. partmap := make(map[int64]int)
  478. mux.HandleFunc("/test.go.upload", func(w http.ResponseWriter, r *http.Request) {
  479. if r.Method == http.MethodPut { // 分块上传
  480. r.ParseForm()
  481. part, _ := strconv.ParseInt(r.Form.Get("partNumber"), 10, 64)
  482. if partmap[part] == 0 {
  483. // 重试检验1
  484. partmap[part]++
  485. ioutil.ReadAll(r.Body)
  486. w.WriteHeader(http.StatusGatewayTimeout)
  487. } else if partmap[part] == 1 {
  488. // 重试校验2
  489. partmap[part]++
  490. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  491. } else { // 正确上传
  492. bs, _ := ioutil.ReadAll(r.Body)
  493. rb[part-1] = bs
  494. md := hex.EncodeToString(calMD5Digest(bs))
  495. crc := crc64.Update(0, crc64.MakeTable(crc64.ECMA), bs)
  496. w.Header().Add("ETag", md)
  497. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  498. }
  499. } else {
  500. testMethod(t, r, http.MethodPost)
  501. initreq := url.Values{}
  502. initreq.Set("uploads", "")
  503. compreq := url.Values{}
  504. compreq.Set("uploadId", uploadid)
  505. r.ParseForm()
  506. if reflect.DeepEqual(r.Form, initreq) {
  507. // 初始化分块上传
  508. fmt.Fprintf(w, `<InitiateMultipartUploadResult>
  509. <Bucket></Bucket>
  510. <Key>%v</Key>
  511. <UploadId>%v</UploadId>
  512. </InitiateMultipartUploadResult>`, "test.go.upload", uploadid)
  513. } else if reflect.DeepEqual(r.Form, compreq) {
  514. // 完成分块上传
  515. tb := crc64.MakeTable(crc64.ECMA)
  516. crc := uint64(0)
  517. for _, v := range rb {
  518. crc = crc64.Update(crc, tb, v)
  519. }
  520. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  521. fmt.Fprintf(w, `<CompleteMultipartUploadResult>
  522. <Location>/test.go.upload</Location>
  523. <Bucket></Bucket>
  524. <Key>test.go.upload</Key>
  525. <ETag>&quot;%v&quot;</ETag>
  526. </CompleteMultipartUploadResult>`, hex.EncodeToString(calMD5Digest(b)))
  527. } else {
  528. t.Errorf("TestObjectService_Upload Unknown Request")
  529. }
  530. }
  531. })
  532. opt := &MultiUploadOptions{
  533. ThreadPoolSize: 3,
  534. PartSize: 1,
  535. }
  536. _, _, err = client.Object.Upload(context.Background(), "test.go.upload", filePath, opt)
  537. if err != nil {
  538. t.Fatalf("Object.Upload returned error: %v", err)
  539. }
  540. }
  541. func TestObjectService_Upload2(t *testing.T) {
  542. setup()
  543. defer teardown()
  544. filePath := "tmpfile" + time.Now().Format(time.RFC3339)
  545. newfile, err := os.Create(filePath)
  546. if err != nil {
  547. t.Fatalf("create tmp file failed")
  548. }
  549. defer os.Remove(filePath)
  550. // 源文件内容
  551. b := make([]byte, 1024*1024*3)
  552. _, err = rand.Read(b)
  553. newfile.Write(b)
  554. newfile.Close()
  555. tb := crc64.MakeTable(crc64.ECMA)
  556. realcrc := crc64.Update(0, tb, b)
  557. name := "test/hello.txt"
  558. retry := 0
  559. final := 4
  560. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  561. testMethod(t, r, http.MethodPut)
  562. testHeader(t, r, "x-cos-acl", "private")
  563. testHeader(t, r, "Content-Type", "text/html")
  564. if retry%2 == 0 {
  565. bs, _ := ioutil.ReadAll(r.Body)
  566. crc := crc64.Update(0, tb, bs)
  567. if !reflect.DeepEqual(bs, b) {
  568. t.Errorf("Object.Put request body Error")
  569. }
  570. if !reflect.DeepEqual(crc, realcrc) {
  571. t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc)
  572. }
  573. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  574. if retry != final {
  575. w.WriteHeader(http.StatusGatewayTimeout)
  576. }
  577. } else {
  578. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  579. }
  580. })
  581. mopt := &MultiUploadOptions{
  582. OptIni: &InitiateMultipartUploadOptions{
  583. ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  584. ContentType: "text/html",
  585. },
  586. ACLHeaderOptions: &ACLHeaderOptions{
  587. XCosACL: "private",
  588. },
  589. },
  590. }
  591. for retry <= final {
  592. _, _, err := client.Object.Upload(context.Background(), name, filePath, mopt)
  593. if retry < final && err == nil {
  594. t.Fatalf("Error must not nil when retry < final")
  595. }
  596. if retry == final && err != nil {
  597. t.Fatalf("Put Error: %v", err)
  598. }
  599. retry++
  600. }
  601. }
  602. func TestObjectService_Download(t *testing.T) {
  603. setup()
  604. defer teardown()
  605. filePath := "rsp.file" + time.Now().Format(time.RFC3339)
  606. newfile, err := os.Create(filePath)
  607. if err != nil {
  608. t.Fatalf("create tmp file failed")
  609. }
  610. defer os.Remove(filePath)
  611. // 源文件内容
  612. totalBytes := int64(1024*1024*9 + 123)
  613. b := make([]byte, totalBytes)
  614. _, err = rand.Read(b)
  615. newfile.Write(b)
  616. newfile.Close()
  617. tb := crc64.MakeTable(crc64.ECMA)
  618. localcrc := strconv.FormatUint(crc64.Update(0, tb, b), 10)
  619. retryMap := make(map[int64]int)
  620. mux.HandleFunc("/test.go.download", func(w http.ResponseWriter, r *http.Request) {
  621. if r.Method == http.MethodHead {
  622. w.Header().Add("Content-Length", strconv.FormatInt(totalBytes, 10))
  623. w.Header().Add("x-cos-hash-crc64ecma", localcrc)
  624. return
  625. }
  626. strRange := r.Header.Get("Range")
  627. slice1 := strings.Split(strRange, "=")
  628. slice2 := strings.Split(slice1[1], "-")
  629. start, _ := strconv.ParseInt(slice2[0], 10, 64)
  630. end, _ := strconv.ParseInt(slice2[1], 10, 64)
  631. if retryMap[start] == 0 {
  632. // 重试校验1
  633. retryMap[start]++
  634. w.WriteHeader(http.StatusGatewayTimeout)
  635. } else if retryMap[start] == 1 {
  636. // 重试检验2
  637. retryMap[start]++
  638. io.Copy(w, bytes.NewBuffer(b[start:end]))
  639. } else if retryMap[start] == 2 {
  640. // 重试检验3
  641. retryMap[start]++
  642. st := math_rand.Int63n(totalBytes - 1024*1024)
  643. et := st + end - start
  644. io.Copy(w, bytes.NewBuffer(b[st:et+1]))
  645. } else {
  646. io.Copy(w, bytes.NewBuffer(b[start:end+1]))
  647. }
  648. })
  649. opt := &MultiDownloadOptions{
  650. ThreadPoolSize: 3,
  651. PartSize: 1,
  652. }
  653. downPath := "down.file" + time.Now().Format(time.RFC3339)
  654. defer os.Remove(downPath)
  655. _, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
  656. if err == nil {
  657. // 长度不一致 Failed
  658. t.Fatalf("Object.Upload returned error: %v", err)
  659. }
  660. _, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
  661. if err == nil {
  662. // CRC不一致
  663. t.Fatalf("Object.Upload returned error: %v", err)
  664. }
  665. _, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
  666. if err != nil {
  667. // 正确
  668. t.Fatalf("Object.Upload returned error: %v", err)
  669. }
  670. }
  671. func TestObjectService_DownloadWithCheckPoint(t *testing.T) {
  672. setup()
  673. defer teardown()
  674. filePath := "rsp.file" + time.Now().Format(time.RFC3339)
  675. newfile, err := os.Create(filePath)
  676. if err != nil {
  677. t.Fatalf("create tmp file failed")
  678. }
  679. defer os.Remove(filePath)
  680. // 源文件内容
  681. totalBytes := int64(1024*1024*9 + 123)
  682. partSize := 1024 * 1024
  683. b := make([]byte, totalBytes)
  684. _, err = rand.Read(b)
  685. newfile.Write(b)
  686. newfile.Close()
  687. tb := crc64.MakeTable(crc64.ECMA)
  688. localcrc := strconv.FormatUint(crc64.Update(0, tb, b), 10)
  689. oddok := false
  690. var oddcount, evencount int
  691. mux.HandleFunc("/test.go.download", func(w http.ResponseWriter, r *http.Request) {
  692. if r.Method == http.MethodHead {
  693. w.Header().Add("Content-Length", strconv.FormatInt(totalBytes, 10))
  694. w.Header().Add("x-cos-hash-crc64ecma", localcrc)
  695. return
  696. }
  697. strRange := r.Header.Get("Range")
  698. slice1 := strings.Split(strRange, "=")
  699. slice2 := strings.Split(slice1[1], "-")
  700. start, _ := strconv.ParseInt(slice2[0], 10, 64)
  701. end, _ := strconv.ParseInt(slice2[1], 10, 64)
  702. if (start/int64(partSize))%2 == 1 {
  703. if oddok {
  704. io.Copy(w, bytes.NewBuffer(b[start:end+1]))
  705. } else {
  706. // 数据校验失败, Download不会做重试
  707. io.Copy(w, bytes.NewBuffer(b[start:end]))
  708. }
  709. oddcount++
  710. } else {
  711. io.Copy(w, bytes.NewBuffer(b[start:end+1]))
  712. evencount++
  713. }
  714. })
  715. opt := &MultiDownloadOptions{
  716. ThreadPoolSize: 3,
  717. PartSize: 1,
  718. CheckPoint: true,
  719. }
  720. downPath := "down.file" + time.Now().Format(time.RFC3339)
  721. defer os.Remove(downPath)
  722. _, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
  723. if err == nil {
  724. // 偶数块下载完成,奇数块下载失败
  725. t.Fatalf("Object.Download returned error: %v", err)
  726. }
  727. fd, err := os.Open(downPath)
  728. if err != nil {
  729. t.Fatalf("Object Download Open File Failed:%v", err)
  730. }
  731. offset := 0
  732. for i := 0; i < 10; i++ {
  733. bs, _ := ioutil.ReadAll(io.LimitReader(fd, int64(partSize)))
  734. offset += len(bs)
  735. if i%2 == 1 {
  736. bs[len(bs)-1] = b[offset-1]
  737. }
  738. if bytes.Compare(bs, b[i*partSize:offset]) != 0 {
  739. t.Fatalf("Compare Error, index:%v, len:%v, offset:%v", i, len(bs), offset)
  740. }
  741. }
  742. fd.Close()
  743. if oddcount != 5 || evencount != 5 {
  744. t.Fatalf("Object.Download failed, odd:%v, even:%v", oddcount, evencount)
  745. }
  746. // 设置奇数块OK
  747. oddok = true
  748. _, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
  749. if err != nil {
  750. // 下载成功
  751. t.Fatalf("Object.Download returned error: %v", err)
  752. }
  753. if oddcount != 10 || evencount != 5 {
  754. t.Fatalf("Object.Download failed, odd:%v, even:%v", oddcount, evencount)
  755. }
  756. }
  757. func TestObjectService_GetTagging(t *testing.T) {
  758. setup()
  759. defer teardown()
  760. mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
  761. testMethod(t, r, "GET")
  762. vs := values{
  763. "tagging": "",
  764. }
  765. testFormValues(t, r, vs)
  766. fmt.Fprint(w, `<Tagging>
  767. <TagSet>
  768. <Tag>
  769. <Key>test_k2</Key>
  770. <Value>test_v2</Value>
  771. </Tag>
  772. <Tag>
  773. <Key>test_k3</Key>
  774. <Value>test_vv</Value>
  775. </Tag>
  776. </TagSet>
  777. </Tagging>`)
  778. })
  779. res, _, err := client.Object.GetTagging(context.Background(), "test")
  780. if err != nil {
  781. t.Fatalf("Object.GetTagging returned error %v", err)
  782. }
  783. want := &ObjectGetTaggingResult{
  784. XMLName: xml.Name{Local: "Tagging"},
  785. TagSet: []ObjectTaggingTag{
  786. {"test_k2", "test_v2"},
  787. {"test_k3", "test_vv"},
  788. },
  789. }
  790. if !reflect.DeepEqual(res, want) {
  791. t.Errorf("Object.GetTagging returned %+v, want %+v", res, want)
  792. }
  793. }
  794. func TestObjectService_PutTagging(t *testing.T) {
  795. setup()
  796. defer teardown()
  797. opt := &ObjectPutTaggingOptions{
  798. TagSet: []ObjectTaggingTag{
  799. {
  800. Key: "test_k2",
  801. Value: "test_v2",
  802. },
  803. {
  804. Key: "test_k3",
  805. Value: "test_v3",
  806. },
  807. },
  808. }
  809. mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
  810. v := new(ObjectPutTaggingOptions)
  811. xml.NewDecoder(r.Body).Decode(v)
  812. testMethod(t, r, "PUT")
  813. vs := values{
  814. "tagging": "",
  815. }
  816. testFormValues(t, r, vs)
  817. want := opt
  818. want.XMLName = xml.Name{Local: "Tagging"}
  819. if !reflect.DeepEqual(v, want) {
  820. t.Errorf("Object.PutTagging request body: %+v, want %+v", v, want)
  821. }
  822. })
  823. _, err := client.Object.PutTagging(context.Background(), "test", opt)
  824. if err != nil {
  825. t.Fatalf("Object.PutTagging returned error: %v", err)
  826. }
  827. }
  828. func TestObjectService_DeleteTagging(t *testing.T) {
  829. setup()
  830. defer teardown()
  831. mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {
  832. testMethod(t, r, http.MethodDelete)
  833. vs := values{
  834. "tagging": "",
  835. }
  836. testFormValues(t, r, vs)
  837. w.WriteHeader(http.StatusNoContent)
  838. })
  839. _, err := client.Object.DeleteTagging(context.Background(), "test")
  840. if err != nil {
  841. t.Fatalf("Object.DeleteTagging returned error: %v", err)
  842. }
  843. }