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.

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