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.

592 lines
15 KiB

4 years ago
6 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. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  27. testMethod(t, r, "GET")
  28. vs := values{
  29. "response-content-type": "text/html",
  30. }
  31. testFormValues(t, r, vs)
  32. testHeader(t, r, "Range", "bytes=0-3")
  33. fmt.Fprint(w, `hello`)
  34. })
  35. opt := &ObjectGetOptions{
  36. ResponseContentType: "text/html",
  37. Range: "bytes=0-3",
  38. }
  39. resp, err := client.Object.Get(context.Background(), name, opt)
  40. if err != nil {
  41. t.Fatalf("Object.Get returned error: %v", err)
  42. }
  43. b, _ := ioutil.ReadAll(resp.Body)
  44. ref := string(b)
  45. want := "hello"
  46. if !reflect.DeepEqual(ref, want) {
  47. t.Errorf("Object.Get returned %+v, want %+v", ref, want)
  48. }
  49. }
  50. func TestObjectService_Put(t *testing.T) {
  51. setup()
  52. defer teardown()
  53. opt := &ObjectPutOptions{
  54. ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  55. ContentType: "text/html",
  56. },
  57. ACLHeaderOptions: &ACLHeaderOptions{
  58. XCosACL: "private",
  59. },
  60. }
  61. name := "test/hello.txt"
  62. retry := 0
  63. final := 10
  64. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  65. testMethod(t, r, http.MethodPut)
  66. testHeader(t, r, "x-cos-acl", "private")
  67. testHeader(t, r, "Content-Type", "text/html")
  68. if retry%2 == 0 {
  69. b, _ := ioutil.ReadAll(r.Body)
  70. tb := crc64.MakeTable(crc64.ECMA)
  71. crc := crc64.Update(0, tb, b)
  72. v := string(b)
  73. want := "hello"
  74. if !reflect.DeepEqual(v, want) {
  75. t.Errorf("Object.Put request body: %#v, want %#v", v, want)
  76. }
  77. realcrc := crc64.Update(0, tb, []byte("hello"))
  78. if !reflect.DeepEqual(crc, realcrc) {
  79. t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc)
  80. }
  81. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  82. if retry != final {
  83. w.WriteHeader(http.StatusGatewayTimeout)
  84. }
  85. } else {
  86. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  87. }
  88. })
  89. for retry <= final {
  90. r := bytes.NewReader([]byte("hello"))
  91. _, err := client.Object.Put(context.Background(), name, r, opt)
  92. if retry < final && err == nil {
  93. t.Fatalf("Error must not nil when retry < final")
  94. }
  95. if retry == final && err != nil {
  96. t.Fatalf("Put Error: %v", err)
  97. }
  98. retry++
  99. }
  100. }
  101. func TestObjectService_PutFromFile(t *testing.T) {
  102. setup()
  103. defer teardown()
  104. filePath := "tmpfile" + time.Now().Format(time.RFC3339)
  105. newfile, err := os.Create(filePath)
  106. if err != nil {
  107. t.Fatalf("create tmp file failed")
  108. }
  109. defer os.Remove(filePath)
  110. // 源文件内容
  111. b := make([]byte, 1024*1024*3)
  112. _, err = rand.Read(b)
  113. newfile.Write(b)
  114. newfile.Close()
  115. tb := crc64.MakeTable(crc64.ECMA)
  116. realcrc := crc64.Update(0, tb, b)
  117. opt := &ObjectPutOptions{
  118. ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  119. ContentType: "text/html",
  120. },
  121. ACLHeaderOptions: &ACLHeaderOptions{
  122. XCosACL: "private",
  123. },
  124. }
  125. name := "test/hello.txt"
  126. retry := 0
  127. final := 4
  128. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  129. testMethod(t, r, http.MethodPut)
  130. testHeader(t, r, "x-cos-acl", "private")
  131. testHeader(t, r, "Content-Type", "text/html")
  132. if retry%2 == 0 {
  133. bs, _ := ioutil.ReadAll(r.Body)
  134. crc := crc64.Update(0, tb, bs)
  135. if !reflect.DeepEqual(bs, b) {
  136. t.Errorf("Object.Put request body Error")
  137. }
  138. if !reflect.DeepEqual(crc, realcrc) {
  139. t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc)
  140. }
  141. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  142. if retry != final {
  143. w.WriteHeader(http.StatusGatewayTimeout)
  144. }
  145. } else {
  146. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  147. }
  148. })
  149. for retry <= final {
  150. _, err := client.Object.PutFromFile(context.Background(), name, filePath, opt)
  151. if retry < final && err == nil {
  152. t.Fatalf("Error must not nil when retry < final")
  153. }
  154. if retry == final && err != nil {
  155. t.Fatalf("Put Error: %v", err)
  156. }
  157. retry++
  158. }
  159. }
  160. func TestObjectService_Delete(t *testing.T) {
  161. setup()
  162. defer teardown()
  163. name := "test/hello.txt"
  164. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  165. testMethod(t, r, http.MethodDelete)
  166. w.WriteHeader(http.StatusNoContent)
  167. })
  168. _, err := client.Object.Delete(context.Background(), name)
  169. if err != nil {
  170. t.Fatalf("Object.Delete returned error: %v", err)
  171. }
  172. }
  173. func TestObjectService_Head(t *testing.T) {
  174. setup()
  175. defer teardown()
  176. name := "test/hello.txt"
  177. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  178. testMethod(t, r, "HEAD")
  179. testHeader(t, r, "If-Modified-Since", "Mon, 12 Jun 2017 05:36:19 GMT")
  180. })
  181. opt := &ObjectHeadOptions{
  182. IfModifiedSince: "Mon, 12 Jun 2017 05:36:19 GMT",
  183. }
  184. _, err := client.Object.Head(context.Background(), name, opt)
  185. if err != nil {
  186. t.Fatalf("Object.Head returned error: %v", err)
  187. }
  188. }
  189. func TestObjectService_Options(t *testing.T) {
  190. setup()
  191. defer teardown()
  192. name := "test/hello.txt"
  193. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  194. testMethod(t, r, http.MethodOptions)
  195. testHeader(t, r, "Access-Control-Request-Method", "PUT")
  196. testHeader(t, r, "Origin", "www.qq.com")
  197. })
  198. opt := &ObjectOptionsOptions{
  199. Origin: "www.qq.com",
  200. AccessControlRequestMethod: "PUT",
  201. }
  202. _, err := client.Object.Options(context.Background(), name, opt)
  203. if err != nil {
  204. t.Fatalf("Object.Options returned error: %v", err)
  205. }
  206. }
  207. // func TestObjectService_Append(t *testing.T) {
  208. // setup()
  209. // defer teardown()
  210. // opt := &ObjectPutOptions{
  211. // ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  212. // ContentType: "text/html",
  213. // },
  214. // ACLHeaderOptions: &ACLHeaderOptions{
  215. // XCosACL: "private",
  216. // },
  217. // }
  218. // name := "test/hello.txt"
  219. // position := 0
  220. // mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  221. // vs := values{
  222. // "append": "",
  223. // "position": "0",
  224. // }
  225. // testFormValues(t, r, vs)
  226. // testMethod(t, r, http.MethodPost)
  227. // testHeader(t, r, "x-cos-acl", "private")
  228. // testHeader(t, r, "Content-Type", "text/html")
  229. // b, _ := ioutil.ReadAll(r.Body)
  230. // v := string(b)
  231. // want := "hello"
  232. // if !reflect.DeepEqual(v, want) {
  233. // t.Errorf("Object.Append request body: %#v, want %#v", v, want)
  234. // }
  235. // })
  236. // r := bytes.NewReader([]byte("hello"))
  237. // _, err := client.Object.Append(context.Background(), name, position, r, opt)
  238. // if err != nil {
  239. // t.Fatalf("Object.Append returned error: %v", err)
  240. // }
  241. // }
  242. func TestObjectService_DeleteMulti(t *testing.T) {
  243. setup()
  244. defer teardown()
  245. mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  246. testMethod(t, r, http.MethodPost)
  247. vs := values{
  248. "delete": "",
  249. }
  250. testFormValues(t, r, vs)
  251. fmt.Fprint(w, `<DeleteResult>
  252. <Deleted>
  253. <Key>test1</Key>
  254. </Deleted>
  255. <Deleted>
  256. <Key>test3</Key>
  257. </Deleted>
  258. <Deleted>
  259. <Key>test2</Key>
  260. </Deleted>
  261. </DeleteResult>`)
  262. })
  263. opt := &ObjectDeleteMultiOptions{
  264. Objects: []Object{
  265. {
  266. Key: "test1",
  267. },
  268. {
  269. Key: "test3",
  270. },
  271. {
  272. Key: "test2",
  273. },
  274. },
  275. }
  276. ref, _, err := client.Object.DeleteMulti(context.Background(), opt)
  277. if err != nil {
  278. t.Fatalf("Object.DeleteMulti returned error: %v", err)
  279. }
  280. want := &ObjectDeleteMultiResult{
  281. XMLName: xml.Name{Local: "DeleteResult"},
  282. DeletedObjects: []Object{
  283. {
  284. Key: "test1",
  285. },
  286. {
  287. Key: "test3",
  288. },
  289. {
  290. Key: "test2",
  291. },
  292. },
  293. }
  294. if !reflect.DeepEqual(ref, want) {
  295. t.Errorf("Object.DeleteMulti returned %+v, want %+v", ref, want)
  296. }
  297. }
  298. func TestObjectService_Copy(t *testing.T) {
  299. setup()
  300. defer teardown()
  301. mux.HandleFunc("/test.go.copy", func(w http.ResponseWriter, r *http.Request) {
  302. testMethod(t, r, http.MethodPut)
  303. fmt.Fprint(w, `<CopyObjectResult>
  304. <ETag>"098f6bcd4621d373cade4e832627b4f6"</ETag>
  305. <LastModified>2017-12-13T14:53:12</LastModified>
  306. </CopyObjectResult>`)
  307. })
  308. sourceURL := "test-1253846586.cos.ap-guangzhou.myqcloud.com/test.source"
  309. ref, _, err := client.Object.Copy(context.Background(), "test.go.copy", sourceURL, nil)
  310. if err != nil {
  311. t.Fatalf("Object.Copy returned error: %v", err)
  312. }
  313. want := &ObjectCopyResult{
  314. XMLName: xml.Name{Local: "CopyObjectResult"},
  315. ETag: `"098f6bcd4621d373cade4e832627b4f6"`,
  316. LastModified: "2017-12-13T14:53:12",
  317. }
  318. if !reflect.DeepEqual(ref, want) {
  319. t.Errorf("Object.Copy returned %+v, want %+v", ref, want)
  320. }
  321. }
  322. func TestObjectService_Upload(t *testing.T) {
  323. setup()
  324. defer teardown()
  325. filePath := "tmpfile" + time.Now().Format(time.RFC3339)
  326. newfile, err := os.Create(filePath)
  327. if err != nil {
  328. t.Fatalf("create tmp file failed")
  329. }
  330. defer os.Remove(filePath)
  331. // 源文件内容
  332. b := make([]byte, 1024*1024*33)
  333. _, err = rand.Read(b)
  334. newfile.Write(b)
  335. newfile.Close()
  336. // 已上传内容, 10个分块
  337. rb := make([][]byte, 33)
  338. uploadid := "test-cos-multiupload-uploadid"
  339. partmap := make(map[int64]int)
  340. mux.HandleFunc("/test.go.upload", func(w http.ResponseWriter, r *http.Request) {
  341. if r.Method == http.MethodPut { // 分块上传
  342. r.ParseForm()
  343. part, _ := strconv.ParseInt(r.Form.Get("partNumber"), 10, 64)
  344. if partmap[part] == 0 {
  345. // 重试检验1
  346. partmap[part]++
  347. ioutil.ReadAll(r.Body)
  348. w.WriteHeader(http.StatusGatewayTimeout)
  349. } else if partmap[part] == 1 {
  350. // 重试校验2
  351. partmap[part]++
  352. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  353. } else { // 正确上传
  354. bs, _ := ioutil.ReadAll(r.Body)
  355. rb[part-1] = bs
  356. md := hex.EncodeToString(calMD5Digest(bs))
  357. crc := crc64.Update(0, crc64.MakeTable(crc64.ECMA), bs)
  358. w.Header().Add("ETag", md)
  359. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  360. }
  361. } else {
  362. testMethod(t, r, http.MethodPost)
  363. initreq := url.Values{}
  364. initreq.Set("uploads", "")
  365. compreq := url.Values{}
  366. compreq.Set("uploadId", uploadid)
  367. r.ParseForm()
  368. if reflect.DeepEqual(r.Form, initreq) {
  369. // 初始化分块上传
  370. fmt.Fprintf(w, `<InitiateMultipartUploadResult>
  371. <Bucket></Bucket>
  372. <Key>%v</Key>
  373. <UploadId>%v</UploadId>
  374. </InitiateMultipartUploadResult>`, "test.go.upload", uploadid)
  375. } else if reflect.DeepEqual(r.Form, compreq) {
  376. // 完成分块上传
  377. tb := crc64.MakeTable(crc64.ECMA)
  378. crc := uint64(0)
  379. for _, v := range rb {
  380. crc = crc64.Update(crc, tb, v)
  381. }
  382. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  383. fmt.Fprintf(w, `<CompleteMultipartUploadResult>
  384. <Location>/test.go.upload</Location>
  385. <Bucket></Bucket>
  386. <Key>test.go.upload</Key>
  387. <ETag>&quot;%v&quot;</ETag>
  388. </CompleteMultipartUploadResult>`, hex.EncodeToString(calMD5Digest(b)))
  389. } else {
  390. t.Errorf("TestObjectService_Upload Unknown Request")
  391. }
  392. }
  393. })
  394. opt := &MultiUploadOptions{
  395. ThreadPoolSize: 3,
  396. PartSize: 1,
  397. }
  398. _, _, err = client.Object.Upload(context.Background(), "test.go.upload", filePath, opt)
  399. if err != nil {
  400. t.Fatalf("Object.Upload returned error: %v", err)
  401. }
  402. }
  403. func TestObjectService_Upload2(t *testing.T) {
  404. setup()
  405. defer teardown()
  406. filePath := "tmpfile" + time.Now().Format(time.RFC3339)
  407. newfile, err := os.Create(filePath)
  408. if err != nil {
  409. t.Fatalf("create tmp file failed")
  410. }
  411. defer os.Remove(filePath)
  412. // 源文件内容
  413. b := make([]byte, 1024*1024*3)
  414. _, err = rand.Read(b)
  415. newfile.Write(b)
  416. newfile.Close()
  417. tb := crc64.MakeTable(crc64.ECMA)
  418. realcrc := crc64.Update(0, tb, b)
  419. name := "test/hello.txt"
  420. retry := 0
  421. final := 4
  422. mux.HandleFunc("/test/hello.txt", func(w http.ResponseWriter, r *http.Request) {
  423. testMethod(t, r, http.MethodPut)
  424. testHeader(t, r, "x-cos-acl", "private")
  425. testHeader(t, r, "Content-Type", "text/html")
  426. if retry%2 == 0 {
  427. bs, _ := ioutil.ReadAll(r.Body)
  428. crc := crc64.Update(0, tb, bs)
  429. if !reflect.DeepEqual(bs, b) {
  430. t.Errorf("Object.Put request body Error")
  431. }
  432. if !reflect.DeepEqual(crc, realcrc) {
  433. t.Errorf("Object.Put crc: %v, want: %v", crc, realcrc)
  434. }
  435. w.Header().Add("x-cos-hash-crc64ecma", strconv.FormatUint(crc, 10))
  436. if retry != final {
  437. w.WriteHeader(http.StatusGatewayTimeout)
  438. }
  439. } else {
  440. w.Header().Add("x-cos-hash-crc64ecma", "123456789")
  441. }
  442. })
  443. mopt := &MultiUploadOptions{
  444. OptIni: &InitiateMultipartUploadOptions{
  445. ObjectPutHeaderOptions: &ObjectPutHeaderOptions{
  446. ContentType: "text/html",
  447. },
  448. ACLHeaderOptions: &ACLHeaderOptions{
  449. XCosACL: "private",
  450. },
  451. },
  452. }
  453. for retry <= final {
  454. _, _, err := client.Object.Upload(context.Background(), name, filePath, mopt)
  455. if retry < final && err == nil {
  456. t.Fatalf("Error must not nil when retry < final")
  457. }
  458. if retry == final && err != nil {
  459. t.Fatalf("Put Error: %v", err)
  460. }
  461. retry++
  462. }
  463. }
  464. func TestObjectService_Download(t *testing.T) {
  465. setup()
  466. defer teardown()
  467. filePath := "rsp.file" + time.Now().Format(time.RFC3339)
  468. newfile, err := os.Create(filePath)
  469. if err != nil {
  470. t.Fatalf("create tmp file failed")
  471. }
  472. defer os.Remove(filePath)
  473. // 源文件内容
  474. totalBytes := int64(1024*1024*9 + 123)
  475. b := make([]byte, totalBytes)
  476. _, err = rand.Read(b)
  477. newfile.Write(b)
  478. newfile.Close()
  479. tb := crc64.MakeTable(crc64.ECMA)
  480. localcrc := strconv.FormatUint(crc64.Update(0, tb, b), 10)
  481. retryMap := make(map[int64]int)
  482. mux.HandleFunc("/test.go.download", func(w http.ResponseWriter, r *http.Request) {
  483. if r.Method == http.MethodHead {
  484. w.Header().Add("Content-Length", strconv.FormatInt(totalBytes, 10))
  485. w.Header().Add("x-cos-hash-crc64ecma", localcrc)
  486. return
  487. }
  488. strRange := r.Header.Get("Range")
  489. slice1 := strings.Split(strRange, "=")
  490. slice2 := strings.Split(slice1[1], "-")
  491. start, _ := strconv.ParseInt(slice2[0], 10, 64)
  492. end, _ := strconv.ParseInt(slice2[1], 10, 64)
  493. if retryMap[start] == 0 {
  494. // 重试校验1
  495. retryMap[start]++
  496. w.WriteHeader(http.StatusGatewayTimeout)
  497. } else if retryMap[start] == 1 {
  498. // 重试检验2
  499. retryMap[start]++
  500. io.Copy(w, bytes.NewBuffer(b[start:end]))
  501. } else if retryMap[start] == 2 {
  502. // 重试检验3
  503. retryMap[start]++
  504. st := math_rand.Int63n(totalBytes - 1024*1024)
  505. et := st + end - start
  506. io.Copy(w, bytes.NewBuffer(b[st:et+1]))
  507. } else {
  508. io.Copy(w, bytes.NewBuffer(b[start:end+1]))
  509. }
  510. })
  511. opt := &MultiDownloadOptions{
  512. ThreadPoolSize: 3,
  513. PartSize: 1,
  514. }
  515. downPath := "down.file" + time.Now().Format(time.RFC3339)
  516. defer os.Remove(downPath)
  517. _, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
  518. if err == nil {
  519. // 长度不一致 Failed
  520. t.Fatalf("Object.Upload returned error: %v", err)
  521. }
  522. _, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
  523. if err == nil {
  524. // CRC不一致
  525. t.Fatalf("Object.Upload returned error: %v", err)
  526. }
  527. _, err = client.Object.Download(context.Background(), "test.go.download", downPath, opt)
  528. if err != nil {
  529. // 正确
  530. t.Fatalf("Object.Upload returned error: %v", err)
  531. }
  532. }